├── .gitignore ├── Cargo.toml ├── disasm ├── Cargo.toml ├── src │ ├── iter.rs │ ├── formatter.rs │ └── lib.rs └── tests │ └── test_disasm.rs ├── fuzz ├── Cargo.toml └── src │ └── main.rs ├── dol ├── Cargo.toml └── src │ └── lib.rs ├── rand ├── Cargo.toml └── src │ └── main.rs ├── genisa ├── Cargo.toml └── src │ └── main.rs ├── flow-graph ├── Cargo.toml └── src │ ├── main.rs │ ├── slices.rs │ └── flow.rs ├── disasm-py ├── Cargo.toml └── src │ └── lib.rs ├── README.md ├── .github └── workflows │ └── test.yml ├── analysis └── cfa.md ├── Cargo.lock └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .idea/ 3 | .DS_Store 4 | *.profraw 5 | env/ 6 | lib/ 7 | *.bin 8 | *.png 9 | *.dol 10 | *.rel 11 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "disasm", 4 | "disasm-py", 5 | "dol", 6 | "fuzz", 7 | "genisa", 8 | "flow-graph", 9 | "rand", 10 | ] 11 | -------------------------------------------------------------------------------- /disasm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ppc750cl" 3 | version = "0.2.0" 4 | edition = "2021" 5 | authors = ["Richard Patel "] 6 | license = "GPL-3.0-or-later" 7 | description = "Disassembler for PowerPC 750CL" 8 | keywords = ["powerpc", "wii", "gamecube"] 9 | repository = "https://github.com/terorie/ppc750cl" 10 | 11 | [dependencies] 12 | num-traits = "0.2" 13 | serde = "1.0" 14 | -------------------------------------------------------------------------------- /fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ppc750cl-fuzz" 3 | version = "0.2.0" 4 | edition = "2021" 5 | authors = ["Richard Patel "] 6 | license = "GPL-3.0-or-later" 7 | description = "Complete fuzzer for ppc750cl" 8 | repository = "https://github.com/terorie/ppc750cl" 9 | 10 | [dependencies] 11 | clap = "3" 12 | num_cpus = "1.13" 13 | ppc750cl = { path = "../disasm", version = "0.2.0" } 14 | -------------------------------------------------------------------------------- /dol/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dol" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Richard Patel "] 6 | license = "GPL-3.0-or-later" 7 | description = "Deserializer for the DOL executable format" 8 | repository = "https://github.com/terorie/ppc750cl" 9 | 10 | [dependencies] 11 | bincode = "1.3" 12 | serde = { version = "1.0", features = ["derive"] } 13 | thiserror = "1.0" 14 | -------------------------------------------------------------------------------- /rand/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ppc750cl-rand" 3 | version = "0.2.0" 4 | edition = "2021" 5 | authors = ["Richard Patel "] 6 | license = "GPL-3.0-or-later" 7 | description = "Generate random PowerPC 750CL instructions" 8 | repository = "https://github.com/terorie/ppc750cl" 9 | 10 | [dependencies] 11 | ppc750cl = { path = "../disasm", version = "0.2.0" } 12 | rand_core = "0.6" 13 | sfmt = "0.7" 14 | -------------------------------------------------------------------------------- /rand/src/main.rs: -------------------------------------------------------------------------------- 1 | use rand_core::{RngCore, SeedableRng}; 2 | use sfmt::SFMT; 3 | 4 | use ppc750cl::formatter::FormattedIns; 5 | use ppc750cl::{Ins, Opcode}; 6 | 7 | fn main() { 8 | let mut rng = SFMT::seed_from_u64(42); 9 | loop { 10 | let ins = Ins::new(rng.next_u32(), 0); 11 | if ins.op == Opcode::Illegal { 12 | continue; 13 | } 14 | println!("{}", FormattedIns(ins)); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /genisa/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ppc750cl-genisa" 3 | version = "0.2.0" 4 | edition = "2021" 5 | authors = ["Richard Patel "] 6 | license = "GPL-3.0-or-later" 7 | description = "Rust code generator for ppc750cl" 8 | repository = "https://github.com/terorie/ppc750cl" 9 | 10 | 11 | [dependencies] 12 | itertools = "0.10" 13 | proc-macro2 = "1.0" 14 | quote = "1.0" 15 | syn = "1.0" 16 | serde = { version = "1.0", features = ["derive"] } 17 | serde_yaml = "0.8" 18 | -------------------------------------------------------------------------------- /flow-graph/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ppc750cl-flow-graph" 3 | version = "0.2.0" 4 | edition = "2021" 5 | authors = ["riidefi ", "Richard Patel "] 6 | license = "GPL-3.0-or-later" 7 | description = "Control flow graph analysis for PowerPC 750CL" 8 | repository = "https://github.com/terorie/ppc750cl" 9 | 10 | [dependencies] 11 | clap = "3" 12 | dol = { version = "0.1.0", path = "../dol" } 13 | itertools = "0.10" 14 | parse_int = "0.6" 15 | petgraph = "0.6" 16 | ppc750cl = { version = "0.2.0", path = "../disasm" } 17 | -------------------------------------------------------------------------------- /disasm-py/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ppc750cl-py" 3 | version = "0.2.0" 4 | edition = "2021" 5 | authors = ["Richard Patel "] 6 | license = "GPL-3.0-or-later" 7 | description = "Python bindings for PowerPC 750CL Disassembler" 8 | repository = "https://github.com/terorie/ppc750cl" 9 | 10 | [lib] 11 | name = "ppc750cl" 12 | crate-type = ["cdylib"] 13 | 14 | [features] 15 | extension-module = ["pyo3/extension-module"] 16 | default = ["extension-module"] 17 | 18 | [dependencies] 19 | pyo3 = { version = "0.16", features = ["multiple-pymethods"] } 20 | ppc750cl = { version = "0.2.0", path = "../disasm" } 21 | -------------------------------------------------------------------------------- /disasm/src/iter.rs: -------------------------------------------------------------------------------- 1 | use crate::Ins; 2 | 3 | /// Returns an iterator of instructions in the given byte slice. 4 | pub fn disasm_iter(code: &[u8], addr: u32) -> DisasmIterator { 5 | DisasmIterator { code, addr } 6 | } 7 | 8 | pub struct DisasmIterator<'a> { 9 | code: &'a [u8], 10 | addr: u32, 11 | } 12 | 13 | impl<'a> Iterator for DisasmIterator<'a> { 14 | type Item = Ins; 15 | 16 | fn next(&mut self) -> Option { 17 | if self.code.len() < 4 { 18 | return None; 19 | } 20 | let code = ((self.code[0] as u32) << 24) 21 | | ((self.code[1] as u32) << 16) 22 | | ((self.code[2] as u32) << 8) 23 | | (self.code[3] as u32); 24 | self.code = &self.code[4..]; 25 | let addr = self.addr; 26 | self.addr += 4; 27 | Some(Ins::new(code, addr)) 28 | } 29 | 30 | fn size_hint(&self) -> (usize, Option) { 31 | let count = self.code.len() / 4; 32 | (count, Some(count)) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /disasm/src/formatter.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Display, Formatter}; 2 | 3 | use crate::prelude::*; 4 | 5 | pub struct FormattedIns(pub Ins); 6 | 7 | impl Display for FormattedIns { 8 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 9 | let simple = self.0.clone().simplified(); 10 | write!(f, "{}{}", simple.mnemonic, simple.suffix)?; 11 | let mut writing_offset = false; 12 | for (i, arg) in simple.args.iter().enumerate() { 13 | if i == 0 { 14 | write!(f, " ")?; 15 | } 16 | if i > 0 && !writing_offset { 17 | write!(f, ", ")?; 18 | } 19 | if let Argument::Offset(val) = arg { 20 | write!(f, "{}(", val)?; 21 | writing_offset = true; 22 | continue; 23 | } else { 24 | write!(f, "{}", arg)?; 25 | } 26 | if writing_offset { 27 | write!(f, ")")?; 28 | writing_offset = false; 29 | } 30 | } 31 | Ok(()) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ppc750cl 2 | 3 | Rust tools for working with the PowerPC 750CL family of processors. 4 | 5 | ### Rust crates 6 | 7 | ```shell 8 | rustup components add rustfmt 9 | cargo run --package ppc750cl-genisa 10 | cargo build --release 11 | ``` 12 | 13 | ### Python module 14 | 15 | ```shell 16 | python -m venv env 17 | source ./env/bin/activate 18 | pip install maturin 19 | maturin build -m ./disasm-py/Cargo.toml 20 | ``` 21 | 22 | Install module in dev env 23 | 24 | ``` 25 | maturin develop -m ./disasm-py/Cargo.toml 26 | python 27 | >>> import ppc750cl 28 | >>> ins = ppc750cl.Ins(addr=0x80006969, code=0x10400420) 29 | >>> str(ins) 30 | 'ps_merge00 f2, f0, f0' 31 | >>> ins.fields() 32 | [('frD', 2), ('frA', 0), ('frB', 0)] 33 | >>> ins.frD 34 | 2 35 | ``` 36 | 37 | ### Instruction Set 38 | 39 | For those unfamiliar with PowerPC, here are some basics. 40 | - PowerPC 7xx is a family of RISC CPUs produced from 1997 to 2012. 41 | - They operate with 32-bit words and every instruction is 32-bits wide. 42 | - This project focuses (only) on compatibility with the PowerPC 750CL. 43 | - This chip is famously packaged as codename "Broadway" for the Nintendo Wii. 44 | - Its predecessor PowerPC 750CXe is used in the Nintendo GameCube. 45 | - It adds a "paired-singles" SIMD unit and a bunch of other instructions. 46 | 47 | ### isa.yaml 48 | 49 | The file [isa.yaml](./isa.yaml) contains a full definition of the PowerPC 750CL instruction set. 50 | 51 | It powers the disassembler, assembler, and Rust/Python bindings code analysis tools. 52 | 53 | Similarly to LLVM TableGen, the program `ppc750cl-genisa` generates a Rust file implementing an instruction decoder. 54 | 55 | ### Safety & Correctness 56 | 57 | - This project does not use `unsafe` Rust code outside of testing utils. 58 | - The disassembler has been fuzzed over all ~4.29 billion possible instructions (via `ppc750cl-fuzz`). 59 | - It is safe to run the disassembler over untrusted byte arrays. 60 | - However no guarantees on correctness are made (yet). Expect bugs. 61 | 62 | ### Performance 63 | 64 | - Performance isn't great but acceptable. 65 | - Disassembling & printing: 600k insn/s (2.4 MB/s) 66 | - Disassembling only: 6M insn/s (24 MB/s) 67 | -------------------------------------------------------------------------------- /flow-graph/src/main.rs: -------------------------------------------------------------------------------- 1 | use petgraph::dot::{Config as DotConfig, Dot}; 2 | 3 | use ppc750cl::{disasm_iter, Ins}; 4 | 5 | pub mod flow; 6 | pub mod slices; 7 | 8 | use crate::flow::FlowGraph; 9 | use crate::slices::BasicSlices; 10 | use dol::Dol; 11 | 12 | fn main() { 13 | let matches = clap::Command::new("ppc750cl-flow-graph") 14 | .version("0.2.0") 15 | .about("Control flow graph analysis for PowerPC 750CL") 16 | .arg( 17 | clap::Arg::new("START") 18 | .long("--start") 19 | .required(true) 20 | .takes_value(true) 21 | .help("Start address"), 22 | ) 23 | .arg( 24 | clap::Arg::new("STOP") 25 | .long("--stop") 26 | .required(true) 27 | .takes_value(true) 28 | .help("Stop address"), 29 | ) 30 | .arg( 31 | clap::Arg::new("INPUT") 32 | .required(true) 33 | .help("Binary input file"), 34 | ) 35 | .get_matches(); 36 | 37 | let start_addr = matches.value_of("START").unwrap(); 38 | let start_addr: u32 = ::parse_int::parse(start_addr).expect("Invalid address flag"); 39 | let stop_addr = matches.value_of("STOP").unwrap(); 40 | let stop_addr: u32 = ::parse_int::parse(stop_addr).expect("Invalid address flag"); 41 | 42 | let file_path = matches.value_of("INPUT").unwrap(); 43 | let dol_file = std::fs::File::open(file_path).expect("Failed to read file"); 44 | let dol = Dol::read_from(&dol_file).expect("Invalid DOL file"); 45 | drop(dol_file); 46 | let mut bytes = vec![0u8; (stop_addr - start_addr) as usize]; 47 | dol.virtual_read(&mut bytes, start_addr) 48 | .expect("Invalid address range"); 49 | 50 | // Create control flow graph. 51 | let ins_list: Vec = disasm_iter(&bytes, start_addr).collect(); 52 | let basic_slices = BasicSlices::from_code(&ins_list); 53 | let graph = FlowGraph::from_basic_slices(&basic_slices, &ins_list); 54 | 55 | // Output graphviz. 56 | let graphviz = Dot::with_config( 57 | &graph.graph, 58 | &[DotConfig::EdgeNoLabel, DotConfig::GraphContentOnly], 59 | ); 60 | println!( 61 | concat!( 62 | "digraph func {{\n", 63 | "node [shape=record fontname=Arial];\n", 64 | "{:?}\n", 65 | "}}" 66 | ), 67 | graphviz 68 | ); 69 | } 70 | -------------------------------------------------------------------------------- /flow-graph/src/slices.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{BTreeSet, HashSet}; 2 | 3 | use ppc750cl::{Ins, Opcode}; 4 | 5 | /// The instruction address divided by four. 6 | pub type CodeIdx = u32; 7 | 8 | pub struct BasicSlices { 9 | /// The indexes separating instructions into basic blocks. 10 | /// Used to create a list of consecutive basic blocks. 11 | pub cuts: BTreeSet, 12 | /// The possible branches from one instruction to another. 13 | /// Used to link together basic blocks into a directed graph. 14 | pub branches: HashSet<(CodeIdx, CodeIdx)>, 15 | } 16 | 17 | impl BasicSlices { 18 | /// Computes basic slices from instructions. 19 | pub fn from_code(code: &[Ins]) -> Self { 20 | let mut cuts = BTreeSet::::new(); 21 | let mut branches = HashSet::<(CodeIdx, CodeIdx)>::new(); 22 | for ins in code { 23 | let cur_index = ins.addr / 4; 24 | let is_control_flow_ins = match ins.op { 25 | // Direct branches are control flow instructions if they don't save the link register. 26 | // If they do, we encountered a function call. 27 | Opcode::B | Opcode::Bc => !ins.field_LK(), 28 | // Switch table 29 | Opcode::Bcctr => panic!("jump tables not supported yet"), 30 | _ => false, 31 | }; 32 | if !is_control_flow_ins { 33 | continue; 34 | } 35 | // We encountered some kind of control flow instruction. 36 | if ins.field_BO() == 20 && ins.field_BI() == 0 { 37 | // There's a possibility that branch can be taken. 38 | // Branch destinations are always the first instruction of a block. 39 | // Thus, we also found the end of another block. 40 | let new_index = ins.branch_dest().unwrap() / 4; 41 | cuts.insert(new_index); 42 | branches.insert((cur_index, new_index)); 43 | } 44 | if is_conditional_branch(ins) { 45 | // There's a possibility that branch is not taken. 46 | // End block anyways. 47 | cuts.insert(cur_index + 1); 48 | branches.insert((cur_index, cur_index + 1)); 49 | } 50 | } 51 | Self { cuts, branches } 52 | } 53 | } 54 | 55 | fn is_conditional_branch(ins: &Ins) -> bool { 56 | match ins.op { 57 | Opcode::Bc | Opcode::Bcctr | Opcode::Bclr => (), 58 | _ => return false, 59 | }; 60 | // Check whether bits "branch always". 61 | ins.field_BO() & 0b10100 != 0b10100 62 | } 63 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | # https://www.infinyon.com/blog/2021/04/github-actions-best-practices/ 2 | 3 | name: Test 4 | 5 | on: 6 | push: 7 | workflow_dispatch: 8 | 9 | jobs: 10 | tests: 11 | name: Test (${{ matrix.os }}) 12 | runs-on: ${{ matrix.os }} 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | os: [ubuntu-latest, macos-latest] 17 | rust: [stable] 18 | include: 19 | - os: ubuntu-latest 20 | sccache-path: /home/runner/.cache/sccache 21 | - os: macos-latest 22 | sccache-path: /Users/runner/Library/Caches/Mozilla.sccache 23 | env: 24 | RUST_BACKTRACE: full 25 | RUSTC_WRAPPER: sccache 26 | RUSTV: ${{ matrix.rust }} 27 | SCCACHE_CACHE_SIZE: 2G 28 | SCCACHE_DIR: ${{ matrix.sccache-path }} 29 | # SCCACHE_RECACHE: 1 # Uncomment this to clear cache, then comment it back out 30 | steps: 31 | - uses: actions/checkout@v2 32 | - name: Install sccache (ubuntu-latest) 33 | if: matrix.os == 'ubuntu-latest' 34 | env: 35 | LINK: https://github.com/mozilla/sccache/releases/download 36 | SCCACHE_VERSION: v0.2.15 37 | run: | 38 | SCCACHE_FILE=sccache-$SCCACHE_VERSION-x86_64-unknown-linux-musl 39 | mkdir -p $HOME/.local/bin 40 | curl -L "$LINK/$SCCACHE_VERSION/$SCCACHE_FILE.tar.gz" | tar xz 41 | mv -f $SCCACHE_FILE/sccache $HOME/.local/bin/sccache 42 | chmod +x $HOME/.local/bin/sccache 43 | echo "$HOME/.local/bin" >> $GITHUB_PATH 44 | - name: Install sccache (macos-latest) 45 | if: matrix.os == 'macos-latest' 46 | run: | 47 | brew update 48 | brew install sccache 49 | - name: Install Rust ${{ matrix.rust }} 50 | uses: actions-rs/toolchain@v1 51 | with: 52 | toolchain: ${{ matrix.rust }} 53 | profile: minimal 54 | override: true 55 | components: rustfmt, clippy 56 | - name: Cache cargo registry 57 | uses: actions/cache@v2 58 | continue-on-error: false 59 | with: 60 | path: | 61 | ~/.cargo/registry 62 | ~/.cargo/git 63 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 64 | restore-keys: | 65 | ${{ runner.os }}-cargo- 66 | - name: Save sccache 67 | uses: actions/cache@v2 68 | continue-on-error: false 69 | with: 70 | path: ${{ matrix.sccache-path }} 71 | key: ${{ runner.os }}-sccache-${{ hashFiles('**/Cargo.lock') }} 72 | restore-keys: | 73 | ${{ runner.os }}-sccache- 74 | - name: Start sccache server 75 | run: sccache --start-server 76 | - name: cargo check 77 | run: cargo check --all --all-features --tests 78 | - name: cargo test 79 | run: cargo test --no-default-features 80 | - name: cargo clippy 81 | run: cargo clippy --all --all-features --tests 82 | - name: cargo fmt 83 | run: cargo fmt -- --check 84 | - name: Print sccache stats 85 | run: sccache --show-stats 86 | - name: Stop sccache server 87 | run: sccache --stop-server || true 88 | -------------------------------------------------------------------------------- /fuzz/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | use std::ops::Range; 3 | use std::str::FromStr; 4 | use std::sync::atomic::{AtomicU32, Ordering}; 5 | use std::sync::Arc; 6 | use std::time::{Duration, Instant}; 7 | 8 | use ppc750cl::formatter::FormattedIns; 9 | use ppc750cl::Ins; 10 | 11 | fn main() { 12 | let matches = clap::Command::new("ppc750cl-fuzz") 13 | .version("0.2.0") 14 | .about("Complete \"fuzzer\" for ppc750cl disassembler") 15 | .arg( 16 | clap::Arg::new("threads") 17 | .short('t') 18 | .long("--threads") 19 | .takes_value(true) 20 | .help("Number of threads to use (default num CPUs)"), 21 | ) 22 | .get_matches(); 23 | 24 | let threads = match matches.value_of("threads") { 25 | Some(t) => u32::from_str(t).expect("invalid threads flag"), 26 | None => num_cpus::get() as u32, 27 | }; 28 | let start = Instant::now(); 29 | let fuzzer = MultiFuzzer::new(threads); 30 | fuzzer.run(); 31 | println!("Finished in {:.2}s", start.elapsed().as_secs_f32()); 32 | } 33 | 34 | #[derive(Clone)] 35 | struct MultiFuzzer { 36 | threads: Vec, 37 | } 38 | 39 | impl MultiFuzzer { 40 | fn new(num_threads: u32) -> Self { 41 | assert_ne!(num_threads, 0); 42 | let mut threads = Vec::::with_capacity(num_threads as usize); 43 | let part_size = 0xFFFF_FFFF / num_threads; 44 | let mut offset = 0u32; 45 | loop { 46 | let next_offset = match offset.checked_add(part_size) { 47 | None => break, 48 | Some(v) => v, 49 | }; 50 | threads.push(Fuzzer::new(offset..next_offset)); 51 | offset = next_offset; 52 | } 53 | threads.last_mut().unwrap().range.end = 0xFFFF_FFFF; 54 | Self { threads } 55 | } 56 | 57 | fn dispatch_progress_monitor(&self) { 58 | let this = self.clone(); 59 | std::thread::spawn(move || { 60 | let mut last = 0u32; 61 | loop { 62 | std::thread::sleep(Duration::from_secs(1)); 63 | let mut now = 0u32; 64 | for thread in &this.threads { 65 | now += thread.counter.load(Ordering::Relaxed) - thread.range.start; 66 | } 67 | let per_second = now - last; 68 | last = now; 69 | let progress = 100f32 * ((now as f32) / (0x1_0000_0000u64 as f32)); 70 | println!("{}/s\t{:05.2}%\tn=0x{:08x}", per_second, progress, now); 71 | } 72 | }); 73 | } 74 | 75 | fn run(&self) { 76 | self.dispatch_progress_monitor(); 77 | let handles: Vec<_> = self.threads.iter().map(|t| t.dispatch()).collect(); 78 | for handle in handles { 79 | // TODO This doesn't panic immediately, since we'll block on thread zero 80 | // for most of the time. 81 | handle.join().expect("thread panicked"); 82 | } 83 | } 84 | } 85 | 86 | #[derive(Clone)] 87 | struct Fuzzer { 88 | range: Range, 89 | counter: Arc, 90 | } 91 | 92 | impl Fuzzer { 93 | fn new(range: Range) -> Self { 94 | Self { 95 | range, 96 | counter: Arc::new(AtomicU32::new(0)), 97 | } 98 | } 99 | 100 | fn dispatch(&self) -> std::thread::JoinHandle<()> { 101 | let mut devnull = DevNull; 102 | 103 | let counter = Arc::clone(&self.counter); 104 | let range = self.range.clone(); 105 | std::thread::spawn(move || { 106 | for x in range.clone() { 107 | let ins = Ins::new(x, 0x8000_0000); 108 | writeln!(&mut devnull, "{}", FormattedIns(ins)).unwrap(); 109 | if x % (1 << 19) == 0 { 110 | counter.store(x, Ordering::Relaxed); 111 | } 112 | } 113 | counter.store(range.end, Ordering::Relaxed); 114 | }) 115 | } 116 | } 117 | 118 | struct DevNull; 119 | 120 | impl std::io::Write for DevNull { 121 | fn write(&mut self, buf: &[u8]) -> std::io::Result { 122 | buf.iter().for_each(|b| unsafe { 123 | std::ptr::read_volatile(b); 124 | }); 125 | Ok(buf.len()) 126 | } 127 | fn flush(&mut self) -> std::io::Result<()> { 128 | Ok(()) 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /analysis/cfa.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2022-04-10 3 | status: draft 4 | --- 5 | 6 | # Control-flow analysis 7 | 8 | CFA analyses the entire `.text` section with linear complexity. 9 | 10 | It has the following goals: 11 | - Detect function boundaries 12 | - Create the control-flow graph of each function 13 | 14 | ## Pass 1: Find control-flow intrinsics 15 | 16 | Input: 17 | - `.text` section 18 | 19 | Output: 20 | - Locations of compiler intrinsics 21 | 22 | Rules 23 | 24 | *savegpr/restgpr detection* (CodeWarrior only) 25 | 26 | Find the following instructions in `.text` 27 | 28 | ```asm 29 | _save_gpr: 30 | stw r14, -0x48(r11) 31 | stw r15, -0x44(r11) 32 | stw r16, -0x40(r11) 33 | stw r17, -0x3c(r11) 34 | stw r18, -0x38(r11) 35 | stw r19, -0x34(r11) 36 | stw r20, -0x30(r11) 37 | stw r21, -0x2c(r11) 38 | stw r22, -0x28(r11) 39 | stw r23, -0x24(r11) 40 | stw r24, -0x20(r11) 41 | stw r25, -0x1c(r11) 42 | stw r26, -0x18(r11) 43 | stw r27, -0x14(r11) 44 | stw r28, -0x10(r11) 45 | stw r29, -0xc(r11) 46 | stw r30, -0x8(r11) 47 | stw r31, -0x4(r11) 48 | blr 49 | 50 | _load_gpr: 51 | lwz r14, -0x48(r11) 52 | lwz r15, -0x44(r11) 53 | lwz r16, -0x40(r11) 54 | lwz r17, -0x3c(r11) 55 | lwz r18, -0x38(r11) 56 | lwz r19, -0x34(r11) 57 | lwz r20, -0x30(r11) 58 | lwz r21, -0x2c(r11) 59 | lwz r22, -0x28(r11) 60 | lwz r23, -0x24(r11) 61 | lwz r24, -0x20(r11) 62 | lwz r25, -0x1c(r11) 63 | lwz r26, -0x18(r11) 64 | lwz r27, -0x14(r11) 65 | lwz r28, -0x10(r11) 66 | lwz r29, -0xc(r11) 67 | lwz r30, -0x8(r11) 68 | lwz r31, -0x4(r11) 69 | blr 70 | ``` 71 | 72 | ## Pass 2: Branch analysis 73 | 74 | Input: 75 | - `.text` section 76 | 77 | Output: 78 | - Slices (cuts) from which basic blocks can be derived 79 | - Forward edges between basic blocks 80 | - Initial set of function boundaries 81 | 82 | #### Branch instruction hints 83 | 84 | - Iterate over all branch opcodes (b, bc, bcctr, bclr) 85 | - Assume that branches with link register save … 86 | - point to the start of another function; 87 | - eventually return back to the call site. 88 | - Assume that branches without link register save are … 89 | - tail calls if their target precedes the function start of the call site; 90 | - probably jumps to basic blocks within the function otherwise (might be wrong) 91 | - Skip indirect local branches (bcctr, bclr) for now. 92 | 93 | #### Stack frame detection (CodeWarrior only) 94 | 95 | Detect patterns matching stack frames. They might be reordered due to instruction scheduling. 96 | 97 | Since CodeWarrior will only emit one function epilogue and prologue per function, 98 | we can use this hint (if present) to reliably detect function bounds. 99 | 100 | - On function entry point: 101 | - Execute instructions up to next branch 102 | - Derive stack frame from changes in machine state 103 | - If the stack changed, we found an epilog 104 | - When a `blr` (function return) is encountered: 105 | - Execute instructions in basic block of `blr` 106 | - If stack-related machine state got reverted, assume this BB to contain the prolog 107 | 108 | Example 0x80045de0 from RMCP01 (savegpr/restgpr sled): 109 | 110 | ```asm 111 | # Stack 112 | # +0x04..+0x08: ret addr to caller 113 | # 0x00..+0x04: caller backchain <- caller r1 114 | # -0x30.. 0x00: callee saved gprs <- callee r11 115 | # -0x68..-0x30: callee stack 116 | # -0x6c..-0x68: ret addr to callee 117 | # -0x70..-0x6c: callee backchain <- callee r1 118 | 119 | _alloc_stack_frame: 120 | stwu r1, -0x70(r1) # store callee backchain & alloc stack frame 121 | mflr r0 122 | … 123 | stw r0, 0x74(r1) # store ret addr to caller 124 | … 125 | la r11, 0x40(r1) # load ptr to callee saved gprs 126 | bl _savegpr_22 # save gprs 127 | mr r31, r1 128 | 129 | … 130 | 131 | _free_stack_frame: 132 | mr r10, r31 133 | la r11, 0x40(r10) # load ptr to callee saved gprs 134 | bl _restgpr_22 # restore gprs 135 | lwz r10, 0x0(r1) # load ptr to caller stack frame 136 | lwz r0, 0x4(r10) # load ret addr to caller 137 | mr r1, r10 # free stack frame 138 | mtlr r0 139 | blr # return to caller 140 | ``` 141 | 142 | Example 0x80228490 from RMCP01 (stmw/lmw): 143 | 144 | ```asm 145 | # Stack 146 | # +0x04..+0x08: ret addr to caller 147 | # 0x00..+0x04: caller backchain <- caller r1 148 | # -0x30.. 0x00: callee saved gprs <- callee r11 149 | # -0x38..-0x30: callee stack 150 | # -0x3c..-0x38: ret addr to callee 151 | # -0x40..-0x3c: callee backchain <- callee r1 152 | 153 | _alloc_stack_frame: 154 | stwu r1, -0x40(r1) # store callee backchain & alloc stack frame 155 | mflr r0 156 | stw r0, 0x44(r1) # store ret addr to caller 157 | stmw r20, -0x30(r1) # save gprs 158 | 159 | … 160 | 161 | _free_stack_frame: 162 | lmw r20, -0x30(r1) # restore gprs 163 | lwz r0, 0x44(r1) # load ret addr to caller 164 | mtlr r0 165 | la r1, 0x40(r1) # free stack frame 166 | blr # return to caller 167 | ``` 168 | 169 | ## TODO 170 | 171 | Add the following rules: 172 | - follow indirect local branches 173 | - destructor detection 174 | - vtable detection 175 | -------------------------------------------------------------------------------- /disasm-py/src/lib.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | use pyo3::types::PyBytes; 3 | 4 | use ppc750cl::formatter::FormattedIns; 5 | 6 | #[pyclass] 7 | struct Ins(ppc750cl::Ins); 8 | 9 | #[pymethods] 10 | impl Ins { 11 | #[new] 12 | fn new(code: u32, addr: u32) -> Self { 13 | Ins(ppc750cl::Ins::new(code, addr)) 14 | } 15 | 16 | #[getter] 17 | fn code(&self) -> u32 { 18 | self.0.code 19 | } 20 | 21 | #[getter] 22 | fn addr(&self) -> u32 { 23 | self.0.addr 24 | } 25 | 26 | #[getter] 27 | fn opcode(&self) -> &'static str { 28 | self.0.op.mnemonic() 29 | } 30 | 31 | fn __str__(&self) -> String { 32 | FormattedIns(self.0.clone()).to_string() 33 | } 34 | 35 | fn fields(&self) -> Vec<(&'static str, i64)> { 36 | self.0 37 | .fields() 38 | .iter() 39 | .flat_map(|field| field.argument().map(|arg| (field.name(), arg.into()))) 40 | .collect() 41 | } 42 | } 43 | 44 | #[allow(non_snake_case)] 45 | #[pymethods] 46 | impl Ins { 47 | #[getter] 48 | fn simm(&self) -> i64 { 49 | self.0.field_simm() as i64 50 | } 51 | #[getter] 52 | fn uimm(&self) -> i64 { 53 | self.0.field_uimm() as i64 54 | } 55 | #[getter] 56 | fn offset(&self) -> i64 { 57 | self.0.field_offset() as i64 58 | } 59 | #[getter] 60 | fn ps_offset(&self) -> i64 { 61 | self.0.field_ps_offset() as i64 62 | } 63 | #[getter] 64 | fn BO(&self) -> i64 { 65 | self.0.field_BO() as i64 66 | } 67 | #[getter] 68 | fn BI(&self) -> i64 { 69 | self.0.field_BI() as i64 70 | } 71 | #[getter] 72 | fn BD(&self) -> i64 { 73 | self.0.field_BD() as i64 74 | } 75 | #[getter] 76 | fn LI(&self) -> i64 { 77 | self.0.field_LI() as i64 78 | } 79 | #[getter] 80 | fn SH(&self) -> i64 { 81 | self.0.field_SH() as i64 82 | } 83 | #[getter] 84 | fn MB(&self) -> i64 { 85 | self.0.field_SH() as i64 86 | } 87 | #[getter] 88 | fn ME(&self) -> i64 { 89 | self.0.field_SH() as i64 90 | } 91 | #[getter] 92 | fn rS(&self) -> i64 { 93 | self.0.field_rS() as i64 94 | } 95 | #[getter] 96 | fn rD(&self) -> i64 { 97 | self.0.field_rD() as i64 98 | } 99 | #[getter] 100 | fn rA(&self) -> i64 { 101 | self.0.field_rA() as i64 102 | } 103 | #[getter] 104 | fn rB(&self) -> i64 { 105 | self.0.field_rB() as i64 106 | } 107 | #[getter] 108 | fn rC(&self) -> i64 { 109 | self.0.field_rC() as i64 110 | } 111 | #[getter] 112 | fn sr(&self) -> i64 { 113 | self.0.field_sr() as i64 114 | } 115 | #[getter] 116 | fn spr(&self) -> i64 { 117 | self.0.field_spr() as i64 118 | } 119 | #[getter] 120 | fn frS(&self) -> i64 { 121 | self.0.field_frS() as i64 122 | } 123 | #[getter] 124 | fn frD(&self) -> i64 { 125 | self.0.field_frD() as i64 126 | } 127 | #[getter] 128 | fn frA(&self) -> i64 { 129 | self.0.field_frA() as i64 130 | } 131 | #[getter] 132 | fn frB(&self) -> i64 { 133 | self.0.field_frB() as i64 134 | } 135 | #[getter] 136 | fn frC(&self) -> i64 { 137 | self.0.field_frC() as i64 138 | } 139 | #[getter] 140 | fn crbD(&self) -> i64 { 141 | self.0.field_crbD() as i64 142 | } 143 | #[getter] 144 | fn crbA(&self) -> i64 { 145 | self.0.field_crbA() as i64 146 | } 147 | #[getter] 148 | fn crbB(&self) -> i64 { 149 | self.0.field_crbB() as i64 150 | } 151 | #[getter] 152 | fn crfD(&self) -> i64 { 153 | self.0.field_crfD() as i64 154 | } 155 | #[getter] 156 | fn crfS(&self) -> i64 { 157 | self.0.field_crfS() as i64 158 | } 159 | #[getter] 160 | fn crm(&self) -> i64 { 161 | self.0.field_crm() as i64 162 | } 163 | #[getter] 164 | fn ps_I(&self) -> i64 { 165 | self.0.field_ps_I() as i64 166 | } 167 | #[getter] 168 | fn ps_IX(&self) -> i64 { 169 | self.0.field_ps_IX() as i64 170 | } 171 | #[getter] 172 | fn ps_W(&self) -> i64 { 173 | self.0.field_ps_W() as i64 174 | } 175 | #[getter] 176 | fn ps_WX(&self) -> i64 { 177 | self.0.field_ps_WX() as i64 178 | } 179 | #[getter] 180 | fn ps_NB(&self) -> i64 { 181 | self.0.field_NB() as i64 182 | } 183 | #[getter] 184 | fn tbr(&self) -> i64 { 185 | self.0.field_tbr() as i64 186 | } 187 | #[getter] 188 | fn mtfsf_FM(&self) -> i64 { 189 | self.0.field_mtfsf_FM() as i64 190 | } 191 | #[getter] 192 | fn mtfsf_IMM(&self) -> i64 { 193 | self.0.field_mtfsf_IMM() as i64 194 | } 195 | #[getter] 196 | fn TO(&self) -> i64 { 197 | self.0.field_TO() as i64 198 | } 199 | } 200 | 201 | impl From for Ins { 202 | fn from(ins: ppc750cl::Ins) -> Self { 203 | Self(ins) 204 | } 205 | } 206 | 207 | #[pyclass] 208 | struct DisasmIterator { 209 | bytes: Py, 210 | addr: u32, 211 | offset: u32, 212 | left: usize, 213 | } 214 | 215 | #[pymethods] 216 | impl DisasmIterator { 217 | fn __iter__(slf: PyRef) -> PyRef { 218 | slf 219 | } 220 | fn __next__(mut slf: PyRefMut) -> PyResult> { 221 | if slf.left < 4 { 222 | return Ok(None); 223 | } 224 | let bytes = slf.bytes.as_ref(slf.py()); 225 | let code = ((bytes[(slf.offset) as usize] as u32) << 24) 226 | | ((bytes[(slf.offset + 1) as usize] as u32) << 16) 227 | | ((bytes[(slf.offset + 2) as usize] as u32) << 8) 228 | | (bytes[(slf.offset + 3) as usize] as u32); 229 | slf.offset += 4; 230 | slf.left -= 4; 231 | let ins = Ins::new(code, slf.addr); 232 | slf.addr += 4; 233 | Ok(Some(ins)) 234 | } 235 | } 236 | 237 | #[pyfunction(code, addr, offset = "0", size = "None")] 238 | fn disasm_iter( 239 | code: &PyBytes, 240 | addr: u32, 241 | offset: u32, 242 | size: Option, 243 | ) -> PyResult { 244 | let left = match size { 245 | None => code.as_bytes().len().saturating_sub(offset as usize), 246 | Some(v) => v as usize, 247 | }; 248 | Ok(DisasmIterator { 249 | bytes: code.into(), 250 | addr, 251 | offset, 252 | left, 253 | }) 254 | } 255 | 256 | #[pymodule] 257 | fn ppc750cl(_: Python, m: &PyModule) -> PyResult<()> { 258 | m.add_class::()?; 259 | m.add_wrapped(wrap_pyfunction!(disasm_iter))?; 260 | Ok(()) 261 | } 262 | -------------------------------------------------------------------------------- /flow-graph/src/flow.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{BTreeMap, HashMap}; 2 | use std::fmt::{Debug, Display, Formatter}; 3 | use std::hash::{Hash, Hasher}; 4 | use std::ops::{Index, Range}; 5 | 6 | use itertools::Itertools; 7 | use petgraph::algo::dominators::Dominators; 8 | use petgraph::graph::{DefaultIx, NodeIndex}; 9 | use petgraph::Graph; 10 | 11 | use ppc750cl::formatter::FormattedIns; 12 | use ppc750cl::{Ins, Opcode}; 13 | 14 | use crate::slices::{BasicSlices, CodeIdx}; 15 | 16 | #[derive(Default)] 17 | pub struct BasicBlock<'a> { 18 | pub range: Range, 19 | pub code: &'a [Ins], 20 | pub data_refs: HashMap, 21 | } 22 | 23 | impl<'a> PartialEq for BasicBlock<'a> { 24 | fn eq(&self, other: &Self) -> bool { 25 | self.range == other.range 26 | } 27 | } 28 | 29 | impl<'a> Eq for BasicBlock<'a> {} 30 | 31 | impl<'a> Hash for BasicBlock<'a> { 32 | fn hash(&self, state: &mut H) { 33 | self.range.hash(state) 34 | } 35 | } 36 | 37 | impl<'a> BasicBlock<'a> { 38 | pub fn from_code_slice(range: Range, complete_code: &'a [Ins]) -> BasicBlock { 39 | let start_idx = complete_code.first().unwrap().addr / 4; 40 | assert!(start_idx <= range.start); 41 | let offset = (range.start - start_idx) as usize; 42 | let code = &complete_code[offset..(offset + (range.len() as usize))]; 43 | BasicBlock { 44 | range, 45 | code, 46 | data_refs: Self::detect_data_refs(code), 47 | } 48 | } 49 | 50 | /// Very simple algorithm to detect data references. 51 | fn detect_data_refs(code: &[Ins]) -> HashMap { 52 | let mut defs = HashMap::::new(); 53 | let mut data_refs = HashMap::::new(); 54 | for ins in code { 55 | match ins.op { 56 | Opcode::Addis => { 57 | if ins.field_rA() == 0 { 58 | // lis 59 | defs.insert(ins.field_rD() as u8, ins.field_uimm() as u16); 60 | } else { 61 | defs.remove(&(ins.field_rD() as u8)); 62 | } 63 | } 64 | Opcode::Addi => { 65 | if let Some(hi) = defs.get(&(ins.field_rA() as u8)) { 66 | data_refs.insert( 67 | ins.addr / 4, 68 | ((*hi as u32) << 16) + (ins.field_uimm() as u32), 69 | ); 70 | } 71 | defs.remove(&(ins.field_rD() as u8)); 72 | } 73 | _ => (), 74 | } 75 | } 76 | data_refs 77 | } 78 | } 79 | 80 | impl<'a> Display for BasicBlock<'a> { 81 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 82 | write!(f, "{:0>#8x}", self.range.start * 4) 83 | } 84 | } 85 | 86 | impl<'a> Debug for BasicBlock<'a> { 87 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 88 | writeln!( 89 | f, 90 | "// {:0>#8x}..{:0>#8x}", 91 | self.range.start * 4, 92 | self.range.end * 4 93 | )?; 94 | for ins in self.code { 95 | writeln!(f, "{}", FormattedIns(ins.clone()))?; 96 | if let Some(addr) = self.data_refs.get(&(ins.addr / 4)) { 97 | writeln!(f, " ref: {:0>#8x}", addr)?; 98 | } 99 | } 100 | Ok(()) 101 | } 102 | } 103 | 104 | /// A control-flow graph of a function. 105 | pub struct FlowGraph<'a> { 106 | pub graph: Graph, ()>, 107 | pub root_idx: NodeIndex, 108 | } 109 | 110 | impl<'a> FlowGraph<'a> { 111 | /// Creates a control-flow graph from basic slices. 112 | pub fn from_basic_slices(slices: &BasicSlices, code: &'a [Ins]) -> Self { 113 | assert!(!code.is_empty(), "Attempt to create empty flow graph"); 114 | // Walk set cuts and create basic blocks. 115 | let mut graph = Graph::new(); 116 | let mut node_by_addr = BTreeMap::>::new(); 117 | let mut block_start: CodeIdx = code[0].addr / 4; 118 | for cut in &slices.cuts { 119 | if *cut > block_start { 120 | node_by_addr.insert( 121 | block_start, 122 | graph.add_node(BasicBlock::from_code_slice(block_start..*cut, code)), 123 | ); 124 | } 125 | block_start = *cut; 126 | } 127 | // Last block. 128 | let func_end: CodeIdx = (code.last().unwrap().addr / 4) + 1; 129 | if func_end > block_start { 130 | node_by_addr.insert( 131 | block_start, 132 | graph.add_node(BasicBlock::from_code_slice(block_start..func_end, code)), 133 | ); 134 | } 135 | // Walk set of branches and connect graph. 136 | for branch in &slices.branches { 137 | let src_node_idx = match node_by_addr.range(..branch.0 + 1).last() { 138 | None => continue, 139 | Some(idx) => *idx.1, 140 | }; 141 | debug_assert!(graph[src_node_idx].range.contains(&branch.0)); 142 | let dst_node_idx = match node_by_addr.range(..branch.1 + 1).last() { 143 | None => continue, 144 | Some(idx) => *idx.1, 145 | }; 146 | debug_assert!(graph[dst_node_idx].range.contains(&branch.1)); 147 | graph.add_edge(src_node_idx, dst_node_idx, ()); 148 | } 149 | // Walk blocks and re-connect nodes that were split off. 150 | for (src_node_idx, dst_node_idx) in node_by_addr.values().tuple_windows::<(_, _)>() { 151 | // Get pairs of two blocks as a sliding window. 152 | let src_block: &BasicBlock = &graph[*src_node_idx]; 153 | let dst_block: &BasicBlock = &graph[*dst_node_idx]; 154 | assert_eq!(src_block.range.end, dst_block.range.start); 155 | // Get last instruction of left block. 156 | // Unless it's an unconditional branch, we can connect the blocks. 157 | let last_ins = &src_block.code.last().unwrap(); 158 | if last_ins.code == 0x4E800020 159 | || (last_ins.op == Opcode::B && last_ins.field_BO() == 0b10100) 160 | { 161 | continue; 162 | } 163 | // Execution can continue past the last instruction of a block, 164 | // so re-connect two blocks that were split off. 165 | if !graph.contains_edge(*src_node_idx, *dst_node_idx) { 166 | graph.add_edge(*src_node_idx, *dst_node_idx, ()); 167 | } 168 | } 169 | Self { 170 | graph, 171 | root_idx: *node_by_addr.index(node_by_addr.keys().next().unwrap()), 172 | } 173 | } 174 | 175 | pub fn dominators(&self) -> Dominators { 176 | petgraph::algo::dominators::simple_fast(&self.graph, self.root_idx) 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /dol/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::io::{Read, Seek, SeekFrom}; 2 | 3 | use bincode::Options; 4 | use serde::{Deserialize, Serialize}; 5 | use thiserror::Error; 6 | 7 | /// A loaded DOL executable. 8 | pub struct Dol { 9 | pub header: DolHeader, 10 | pub memory: Vec, 11 | pub memory_offset: u32, 12 | } 13 | 14 | /// An error that can be raised during DOL parsing. 15 | #[derive(Error, Debug)] 16 | pub enum Error { 17 | #[error("{0}")] 18 | BincodeError(bincode::Error), 19 | #[error("{0}")] 20 | IOError(std::io::Error), 21 | #[error("No sections in DOL")] 22 | NoSections, 23 | #[error("Overlapping sections: {0:8>X} {1:8>X}")] 24 | OverlappingSections(u32, u32), 25 | #[error("Section sizes too large")] 26 | SectionsTooLarge, 27 | #[error("Attempted to access {0:08X} past DOL bounds")] 28 | OutOfBounds(u32), 29 | } 30 | 31 | impl From for Error { 32 | fn from(e: bincode::Error) -> Self { 33 | Self::BincodeError(e) 34 | } 35 | } 36 | 37 | impl From for Error { 38 | fn from(e: std::io::Error) -> Self { 39 | Self::IOError(e) 40 | } 41 | } 42 | 43 | /// The result of a DOL parsing. 44 | pub type Result = std::result::Result; 45 | 46 | impl Dol { 47 | /// Reads a DOL executable from a `Reader`. 48 | pub fn read_from(mut r: R) -> Result 49 | where 50 | R: Read + Seek, 51 | { 52 | // Read header. 53 | let header_data = DolHeaderData::read_from(&mut r)?; 54 | let header: DolHeader = (&header_data).into(); 55 | Dol::read_with_header(r, header) 56 | } 57 | 58 | /// Reads a DOL body from a `Reader` given a header. 59 | pub fn read_with_header(mut r: R, header: DolHeader) -> Result 60 | where 61 | R: Read + Seek, 62 | { 63 | let dol_start = r.stream_position()? - DolHeaderData::SERIALIZED_SIZE; 64 | if header.sections.is_empty() { 65 | return Err(Error::NoSections); 66 | } 67 | /* 68 | // Verify that sections are not overlapping. 69 | let mut end_target_addr = 0u32; 70 | for section in &header.sections { 71 | if section.target < end_target_addr { 72 | return Err(Error::OverlappingSections(end_target_addr, section.target)); 73 | } 74 | end_target_addr = section.target + section.size 75 | } 76 | */ 77 | // Remember the memory offset of the first section. 78 | // Then shift all sections to the beginning of memory. 79 | let memory_offset = header.sections[0].target; 80 | // Get the size of all sections combined. 81 | let mut total_size = 0usize; 82 | for section in &header.sections { 83 | if let Some(sum) = total_size.checked_add(section.size as usize) { 84 | total_size = sum; 85 | } else { 86 | return Err(Error::SectionsTooLarge); 87 | } 88 | } 89 | // Cannot be larger than 24 MiB. 90 | if total_size > 0x180_0000 { 91 | return Err(Error::SectionsTooLarge); 92 | } 93 | // Create memory. 94 | let mut memory = vec![0u8; total_size]; 95 | // Read sections into memory. 96 | for section in &header.sections { 97 | if section.kind == DolSectionType::Bss { 98 | continue; 99 | } 100 | r.seek(SeekFrom::Start(dol_start + section.offset as u64))?; 101 | let mem_start = (section.target - memory_offset) as usize; 102 | let mem_end = mem_start + section.size as usize; 103 | r.read_exact(&mut memory[mem_start..mem_end])?; 104 | } 105 | Ok(Self { 106 | header, 107 | memory, 108 | memory_offset, 109 | }) 110 | } 111 | 112 | pub fn section_data(&self, section: &DolSection) -> &[u8] { 113 | self.virtual_data_at(section.target, section.size).unwrap() 114 | } 115 | 116 | /// Returns a slice of DOL data. Does not support bss. 117 | pub fn virtual_data_at(&self, virtual_addr: u32, read_len: u32) -> Result<&[u8]> { 118 | if virtual_addr < self.memory_offset { 119 | return Err(Error::OutOfBounds(virtual_addr)); 120 | } 121 | 122 | let offset = (virtual_addr - self.memory_offset) as usize; 123 | if offset + (read_len as usize) < self.memory.len() { 124 | Ok(&self.memory[offset..offset + (read_len as usize)]) 125 | } else { 126 | Err(Error::OutOfBounds(virtual_addr + read_len)) 127 | } 128 | } 129 | 130 | /// Reads bytes into a destination buffer given a virtual address. 131 | pub fn virtual_read(&self, data: &mut [u8], virtual_addr: u32) -> Result<()> { 132 | if virtual_addr < self.memory_offset { 133 | return Err(Error::OutOfBounds(virtual_addr)); 134 | } 135 | 136 | let offset = (virtual_addr - self.memory_offset) as usize; 137 | let read_len = data.len(); 138 | if offset + read_len < self.memory.len() { 139 | data.copy_from_slice(&self.memory[offset..offset + data.len()]); 140 | Ok(()) 141 | } else { 142 | Err(Error::OutOfBounds(virtual_addr + (read_len as u32))) 143 | } 144 | } 145 | } 146 | 147 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] 148 | pub enum DolSectionType { 149 | Text, 150 | Data, 151 | Bss, 152 | } 153 | 154 | #[derive(Debug, Clone)] 155 | pub struct DolSection { 156 | pub kind: DolSectionType, 157 | pub index: usize, 158 | pub offset: u32, 159 | pub target: u32, 160 | pub size: u32, 161 | } 162 | 163 | pub struct DolHeader { 164 | pub sections: Vec, 165 | pub entry_point: u32, 166 | } 167 | 168 | impl From<&DolHeaderData> for DolHeader { 169 | fn from(header: &DolHeaderData) -> Self { 170 | let mut sections = Vec::with_capacity(DolHeaderData::SECTION_COUNT); 171 | for i in 0..DolHeaderData::SECTION_COUNT { 172 | let kind = if i < 7 { 173 | DolSectionType::Text 174 | } else { 175 | DolSectionType::Data 176 | }; 177 | 178 | if header.section_sizes[i] > 0 { 179 | sections.push(DolSection { 180 | kind, 181 | index: i, 182 | offset: header.section_offsets[i], 183 | target: header.section_targets[i], 184 | size: header.section_sizes[i], 185 | }); 186 | } 187 | } 188 | if header.bss_target > 0 { 189 | sections.push(DolSection { 190 | kind: DolSectionType::Bss, 191 | index: 0, 192 | offset: 0, 193 | target: header.bss_target, 194 | size: header.bss_size, 195 | }) 196 | } 197 | // Sort sections by target address to prepare them for mapping. 198 | sections.sort_by_key(|s| s.target); 199 | Self { 200 | sections, 201 | entry_point: header.entry_point, 202 | } 203 | } 204 | } 205 | 206 | impl DolHeader { 207 | pub fn section_at(&self, addr: u32) -> Option<&DolSection> { 208 | self.sections 209 | .iter() 210 | .find(|§ion| (section.target..(section.target + section.size)).contains(&addr)) 211 | } 212 | } 213 | 214 | #[derive(Debug, Serialize, Deserialize)] 215 | pub struct DolHeaderData { 216 | pub section_offsets: [u32; Self::SECTION_COUNT], 217 | pub section_targets: [u32; Self::SECTION_COUNT], 218 | pub section_sizes: [u32; Self::SECTION_COUNT], 219 | pub bss_target: u32, 220 | pub bss_size: u32, 221 | pub entry_point: u32, 222 | pub padding: [u8; 0x1c], 223 | } 224 | 225 | impl DolHeaderData { 226 | const SECTION_COUNT: usize = 18; 227 | const SERIALIZED_SIZE: u64 = 0x100; 228 | 229 | /// Reads the DOL header from a `Reader`. 230 | pub fn read_from(mut r: R) -> bincode::Result { 231 | bincode::DefaultOptions::new() 232 | .with_big_endian() 233 | .allow_trailing_bytes() 234 | .with_fixint_encoding() 235 | .deserialize_from(&mut r) 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /disasm/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Display, Formatter, LowerHex, UpperHex, Write}; 2 | use std::ops::Range; 3 | 4 | use num_traits::{AsPrimitive, PrimInt}; 5 | 6 | pub use crate::iter::{disasm_iter, DisasmIterator}; 7 | 8 | pub mod formatter; 9 | mod generated; 10 | mod iter; 11 | pub use generated::*; 12 | 13 | pub mod prelude { 14 | pub use crate::formatter::FormattedIns; 15 | pub use crate::Argument; 16 | pub use crate::Field::*; 17 | pub use crate::Ins; 18 | pub use crate::Opcode::*; 19 | pub use crate::SimplifiedIns; 20 | pub use crate::{ 21 | Bit, BranchDest, CRBit, CRField, Offset, OpaqueU, Simm, Uimm, FPR, GPR, GQR, SPR, SR, 22 | }; 23 | } 24 | 25 | macro_rules! field_arg_no_display { 26 | ($name:ident, $typ:ident) => { 27 | #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] 28 | pub struct $name(pub $typ); 29 | impl std::convert::From<$name> for Argument { 30 | fn from(x: $name) -> Argument { 31 | Argument::$name(x) 32 | } 33 | } 34 | }; 35 | } 36 | 37 | macro_rules! field_arg { 38 | ($name:ident, $typ:ident) => { 39 | field_arg_no_display!($name, $typ); 40 | impl Display for $name { 41 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 42 | write!(f, "{}", self.0) 43 | } 44 | } 45 | }; 46 | ($name:ident, $typ:ident, $format:literal) => { 47 | field_arg_no_display!($name, $typ); 48 | impl Display for $name { 49 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 50 | write!(f, $format, self.0) 51 | } 52 | } 53 | }; 54 | ($name:ident, $typ:ident, $format:literal, $format_arg:expr) => { 55 | field_arg_no_display!($name, $typ); 56 | impl Display for $name { 57 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 58 | write!(f, $format, $format_arg(self.0)) 59 | } 60 | } 61 | }; 62 | } 63 | 64 | #[inline(always)] 65 | fn bit(x: u32, idx: usize) -> bool { 66 | ((x >> (32 - idx - 1)) & 1) == 1 67 | } 68 | 69 | #[inline(always)] 70 | fn bits(x: u32, range: Range) -> F 71 | where 72 | F: 'static + std::marker::Copy, 73 | u32: AsPrimitive, 74 | { 75 | let masked: u32 = (x >> (32 - range.end)) & ((1 << range.len()) - 1); 76 | masked.as_() 77 | } 78 | 79 | // https://stackoverflow.com/questions/44711012/how-do-i-format-a-signed-integer-to-a-sign-aware-hexadecimal-representation 80 | struct ReallySigned(N); 81 | 82 | impl LowerHex for ReallySigned { 83 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 84 | let num = self.0.to_i32().unwrap(); 85 | let prefix = if f.alternate() { "0x" } else { "" }; 86 | let bare_hex = format!("{:x}", num.abs()); 87 | f.pad_integral(num >= 0, prefix, &bare_hex) 88 | } 89 | } 90 | 91 | impl UpperHex for ReallySigned { 92 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 93 | let num = self.0.to_i32().unwrap(); 94 | let prefix = if f.alternate() { "0x" } else { "" }; 95 | let bare_hex = format!("{:X}", num.abs()); 96 | f.pad_integral(num >= 0, prefix, &bare_hex) 97 | } 98 | } 99 | 100 | // General-purpose register. 101 | field_arg!(GPR, u8, "r{}"); 102 | // Floating-point register (direct or paired-singles mode). 103 | field_arg!(FPR, u8, "f{}"); 104 | // Segment register. 105 | field_arg!(SR, u8); 106 | // Special-purpose register. 107 | field_arg_no_display!(SPR, u16); 108 | impl Display for SPR { 109 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 110 | f.write_str(match self.0 { 111 | 1 => "XER", 112 | 8 => "LR", 113 | 9 => "CTR", 114 | 18 => "DSISR", 115 | 19 => "DAR", 116 | 22 => "DEC", 117 | 25 => "SDR1", 118 | 26 => "SRR0", 119 | 27 => "SRR1", 120 | 272 => "SPRG0", 121 | 273 => "SPRG1", 122 | 274 => "SPRG2", 123 | 275 => "SPRG3", 124 | 282 => "EAR", 125 | 287 => "PVR", 126 | 528 => "IBAT0U", 127 | 529 => "IBAT0L", 128 | 530 => "IBAT1U", 129 | 531 => "IBAT1L", 130 | 532 => "IBAT2U", 131 | 533 => "IBAT2L", 132 | 534 => "IBAT3U", 133 | 535 => "IBAT3L", 134 | 536 => "DBAT0U", 135 | 537 => "DBAT0L", 136 | 538 => "DBAT1U", 137 | 539 => "DBAT1L", 138 | 540 => "DBAT2U", 139 | 541 => "DBAT2L", 140 | 542 => "DBAT3U", 141 | 543 => "DBAT3L", 142 | 912 => "GQR0", 143 | 913 => "GQR1", 144 | 914 => "GQR2", 145 | 915 => "GQR3", 146 | 916 => "GQR4", 147 | 917 => "GQR5", 148 | 918 => "GQR6", 149 | 919 => "GQR7", 150 | 920 => "HID2", 151 | 921 => "WPAR", 152 | 922 => "DMA_U", 153 | 923 => "DMA_L", 154 | 936 => "UMMCR0", 155 | 937 => "UPMC1", 156 | 938 => "UPMC2", 157 | 939 => "USIA", 158 | 940 => "UMMCR1", 159 | 941 => "UPMC3", 160 | 942 => "UPMC4", 161 | 943 => "USDA", 162 | 952 => "MMCR0", 163 | 953 => "PMC1", 164 | 954 => "PMC2", 165 | 955 => "SIA", 166 | 956 => "MMCR1", 167 | 957 => "PMC3", 168 | 958 => "PMC4", 169 | 959 => "SDA", 170 | 1008 => "HID0", 171 | 1009 => "HID1", 172 | 1010 => "IABR", 173 | 1013 => "DABR", 174 | 1017 => "L2CR", 175 | 1019 => "ICTC", 176 | 1020 => "THRM1", 177 | 1021 => "THRM2", 178 | 1022 => "THRM3", 179 | _ => return write!(f, "{}", self.0), 180 | }) 181 | } 182 | } 183 | // Condition register field. 184 | field_arg!(CRField, u8, "cr{}"); 185 | // Condition register bit (index + condition case). 186 | field_arg_no_display!(CRBit, u8); 187 | impl Display for CRBit { 188 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 189 | let cr = self.0 >> 2; 190 | let cc = self.0 & 3; 191 | if cr != 0 { 192 | write!(f, "4*{}+", CRField(cr))?; 193 | } 194 | const CR_NAMES: [&str; 4] = ["lt", "gt", "eq", "so"]; 195 | f.write_str(CR_NAMES[cc as usize]) 196 | } 197 | } 198 | // Paired-single graphics quantization register 199 | field_arg!(GQR, u8, "qr{}"); 200 | // Unsigned immediate. 201 | field_arg!(Uimm, u16, "{:#x}"); 202 | // Signed immediate. 203 | field_arg!(Simm, i16, "{:#x}", ReallySigned); 204 | // Offset for indirect memory reference. 205 | field_arg!(Offset, i16, "{:#x}", ReallySigned); 206 | // Branch destination. 207 | field_arg!(BranchDest, i32, "{:#x}", ReallySigned); 208 | // Opaque zero or one argument. 209 | field_arg_no_display!(Bit, bool); 210 | impl Display for Bit { 211 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 212 | f.write_char(if self.0 { '1' } else { '0' }) 213 | } 214 | } 215 | // Unsigned opaque argument. 216 | field_arg!(OpaqueU, u32); 217 | 218 | #[derive(Debug, Clone)] 219 | pub enum Argument { 220 | GPR(GPR), 221 | FPR(FPR), 222 | SR(SR), 223 | SPR(SPR), 224 | CRField(CRField), 225 | CRBit(CRBit), 226 | GQR(GQR), 227 | Uimm(Uimm), 228 | Simm(Simm), 229 | Offset(Offset), 230 | BranchDest(BranchDest), 231 | Bit(Bit), 232 | OpaqueU(OpaqueU), 233 | } 234 | 235 | impl Display for Argument { 236 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 237 | match self { 238 | Argument::GPR(x) => x.fmt(f), 239 | Argument::FPR(x) => x.fmt(f), 240 | Argument::SR(x) => x.fmt(f), 241 | Argument::SPR(x) => x.fmt(f), 242 | Argument::CRField(x) => x.fmt(f), 243 | Argument::CRBit(x) => x.fmt(f), 244 | Argument::GQR(x) => x.fmt(f), 245 | Argument::Uimm(x) => x.fmt(f), 246 | Argument::Simm(x) => x.fmt(f), 247 | Argument::Offset(x) => x.fmt(f), 248 | Argument::BranchDest(x) => x.fmt(f), 249 | Argument::Bit(x) => x.fmt(f), 250 | Argument::OpaqueU(x) => x.fmt(f), 251 | } 252 | } 253 | } 254 | 255 | impl From for i64 { 256 | fn from(arg: Argument) -> Self { 257 | match arg { 258 | Argument::GPR(x) => x.0 as i64, 259 | Argument::FPR(x) => x.0 as i64, 260 | Argument::SR(x) => x.0 as i64, 261 | Argument::SPR(x) => x.0 as i64, 262 | Argument::CRField(x) => x.0 as i64, 263 | Argument::CRBit(x) => x.0 as i64, 264 | Argument::GQR(x) => x.0 as i64, 265 | Argument::Uimm(x) => x.0 as i64, 266 | Argument::Simm(x) => x.0 as i64, 267 | Argument::Offset(x) => x.0 as i64, 268 | Argument::BranchDest(x) => x.0 as i64, 269 | Argument::Bit(x) => x.0 as i64, 270 | Argument::OpaqueU(x) => x.0 as i64, 271 | } 272 | } 273 | } 274 | 275 | impl TryInto for &Field { 276 | type Error = (); 277 | fn try_into(self) -> Result { 278 | self.argument().ok_or(()) 279 | } 280 | } 281 | 282 | impl Opcode { 283 | /// Detects the opcode of a machine code instruction. 284 | pub fn detect(code: u32) -> Self { 285 | Self::_detect(code) // auto-generated 286 | } 287 | 288 | /// Prints the basic mnemonic of an opcode. 289 | pub fn mnemonic(self) -> &'static str { 290 | self._mnemonic() // auto-generated 291 | } 292 | } 293 | 294 | impl Default for Opcode { 295 | fn default() -> Self { 296 | Opcode::Illegal 297 | } 298 | } 299 | 300 | impl std::string::ToString for Opcode { 301 | fn to_string(&self) -> String { 302 | let mnemonic = self.mnemonic(); 303 | mnemonic.to_owned() 304 | } 305 | } 306 | 307 | /// A PowerPC 750CL instruction. 308 | #[derive(Default, Clone, Debug, Eq, PartialEq)] 309 | pub struct Ins { 310 | pub code: u32, 311 | pub addr: u32, 312 | pub op: Opcode, 313 | } 314 | 315 | impl Ins { 316 | const BLR: u32 = 0x4e800020; 317 | 318 | /// Constructs an instruction from the given machine code and address. 319 | pub fn new(code: u32, addr: u32) -> Self { 320 | Self { 321 | code, 322 | addr, 323 | op: Opcode::detect(code), 324 | } 325 | } 326 | 327 | /// Returns the simplified representation of an instruction. 328 | pub fn simplified(self) -> SimplifiedIns { 329 | self._simplified() // auto-generated 330 | } 331 | 332 | /// Gets the fields of an instruction. 333 | pub fn fields(&self) -> Vec { 334 | self._fields() // auto-generated 335 | } 336 | 337 | /// Gets the suffix of an instruction mnemonic. 338 | pub fn suffix(&self) -> String { 339 | self._suffix() // auto-generated 340 | } 341 | 342 | /// Gets the defs of an instruction. 343 | pub fn defs(&self) -> Vec { 344 | self._defs() // auto-generated 345 | } 346 | 347 | /// Gets the uses of an instruction. 348 | pub fn uses(&self) -> Vec { 349 | self._uses() // auto-generated 350 | } 351 | 352 | /// Gets the given bit from the machine code instruction. 353 | pub fn bit(&self, idx: usize) -> bool { 354 | bit(self.code, idx) 355 | } 356 | 357 | /// Gets the given range of btis from the machine code instruction. 358 | pub fn bits(&self, range: Range) -> u32 { 359 | bits(self.code, range) 360 | } 361 | 362 | pub fn branch_offset(&self) -> Option { 363 | match self.op { 364 | Opcode::B => Some(self.field_LI() as i32), 365 | Opcode::Bc => Some(self.field_BD() as i32), 366 | _ => None, 367 | } 368 | } 369 | 370 | pub fn branch_dest(&self) -> Option { 371 | self.branch_offset().and_then(|offset| { 372 | if self.field_AA() { 373 | Some(offset as u32) 374 | } else if offset < 0 { 375 | self.addr.checked_sub((-offset) as u32) 376 | } else { 377 | self.addr.checked_add(offset as u32) 378 | } 379 | }) 380 | } 381 | 382 | pub fn is_branch(&self) -> bool { 383 | matches!( 384 | self.op, 385 | Opcode::B | Opcode::Bc | Opcode::Bcctr | Opcode::Bclr 386 | ) 387 | } 388 | 389 | pub fn is_direct_branch(&self) -> bool { 390 | matches!(self.op, Opcode::B | Opcode::Bc) 391 | } 392 | 393 | pub fn is_unconditional_branch(&self) -> bool { 394 | match self.op { 395 | Opcode::B => true, 396 | Opcode::Bc | Opcode::Bcctr | Opcode::Bclr => { 397 | self.field_BO() == 20 && self.field_BI() == 0 398 | } 399 | _ => false, 400 | } 401 | } 402 | 403 | pub fn is_conditional_branch(&self) -> bool { 404 | self.is_branch() && !self.is_unconditional_branch() 405 | } 406 | 407 | #[inline] 408 | pub fn is_blr(&self) -> bool { 409 | // self.op == Opcode::Bclr && self.is_unconditional_branch() && !self.field_LK() 410 | self.code == Ins::BLR 411 | } 412 | } 413 | 414 | /// A simplified PowerPC 750CL instruction. 415 | pub struct SimplifiedIns { 416 | pub ins: Ins, 417 | pub mnemonic: &'static str, 418 | pub suffix: String, 419 | pub args: Vec, 420 | } 421 | 422 | impl Display for SimplifiedIns { 423 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 424 | write!(f, "{}{} ", self.mnemonic, self.suffix)?; 425 | let mut writing_offset = false; 426 | for (i, argument) in self.args.iter().enumerate() { 427 | write!(f, "{}", argument)?; 428 | if let Argument::Offset(_) = argument { 429 | write!(f, "(")?; 430 | writing_offset = true; 431 | continue; 432 | } 433 | if writing_offset { 434 | write!(f, ")")?; 435 | writing_offset = false; 436 | } 437 | if i != self.args.len() - 1 { 438 | write!(f, ", ")?; 439 | } 440 | } 441 | Ok(()) 442 | } 443 | } 444 | 445 | impl SimplifiedIns { 446 | pub(crate) fn basic_form(ins: Ins) -> Self { 447 | Self { 448 | mnemonic: ins.op.mnemonic(), 449 | suffix: ins.suffix(), 450 | args: ins 451 | .fields() 452 | .iter() 453 | .flat_map(|field| field.argument()) 454 | .collect(), 455 | ins, 456 | } 457 | } 458 | } 459 | -------------------------------------------------------------------------------- /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 = "atty" 7 | version = "0.2.14" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 10 | dependencies = [ 11 | "hermit-abi", 12 | "libc", 13 | "winapi", 14 | ] 15 | 16 | [[package]] 17 | name = "autocfg" 18 | version = "1.1.0" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 21 | 22 | [[package]] 23 | name = "bincode" 24 | version = "1.3.3" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" 27 | dependencies = [ 28 | "serde", 29 | ] 30 | 31 | [[package]] 32 | name = "bitflags" 33 | version = "1.3.2" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 36 | 37 | [[package]] 38 | name = "cfg-if" 39 | version = "1.0.0" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 42 | 43 | [[package]] 44 | name = "clap" 45 | version = "3.1.8" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "71c47df61d9e16dc010b55dba1952a57d8c215dbb533fd13cdd13369aac73b1c" 48 | dependencies = [ 49 | "atty", 50 | "bitflags", 51 | "indexmap", 52 | "os_str_bytes", 53 | "strsim", 54 | "termcolor", 55 | "textwrap", 56 | ] 57 | 58 | [[package]] 59 | name = "ctor" 60 | version = "0.1.22" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c" 63 | dependencies = [ 64 | "quote", 65 | "syn", 66 | ] 67 | 68 | [[package]] 69 | name = "dol" 70 | version = "0.1.0" 71 | dependencies = [ 72 | "bincode", 73 | "serde", 74 | "thiserror", 75 | ] 76 | 77 | [[package]] 78 | name = "either" 79 | version = "1.6.1" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" 82 | 83 | [[package]] 84 | name = "fixedbitset" 85 | version = "0.4.1" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "279fb028e20b3c4c320317955b77c5e0c9701f05a1d309905d6fc702cdc5053e" 88 | 89 | [[package]] 90 | name = "getrandom" 91 | version = "0.2.6" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" 94 | dependencies = [ 95 | "cfg-if", 96 | "libc", 97 | "wasi", 98 | ] 99 | 100 | [[package]] 101 | name = "ghost" 102 | version = "0.1.2" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "1a5bcf1bbeab73aa4cf2fde60a846858dc036163c7c33bec309f8d17de785479" 105 | dependencies = [ 106 | "proc-macro2", 107 | "quote", 108 | "syn", 109 | ] 110 | 111 | [[package]] 112 | name = "hashbrown" 113 | version = "0.11.2" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" 116 | 117 | [[package]] 118 | name = "hermit-abi" 119 | version = "0.1.19" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 122 | dependencies = [ 123 | "libc", 124 | ] 125 | 126 | [[package]] 127 | name = "indexmap" 128 | version = "1.8.1" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" 131 | dependencies = [ 132 | "autocfg", 133 | "hashbrown", 134 | ] 135 | 136 | [[package]] 137 | name = "indoc" 138 | version = "1.0.4" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "e7906a9fababaeacb774f72410e497a1d18de916322e33797bb2cd29baa23c9e" 141 | dependencies = [ 142 | "unindent", 143 | ] 144 | 145 | [[package]] 146 | name = "instant" 147 | version = "0.1.12" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 150 | dependencies = [ 151 | "cfg-if", 152 | ] 153 | 154 | [[package]] 155 | name = "inventory" 156 | version = "0.2.2" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "ce6b5d8c669bfbad811d95ddd7a1c6cf9cfdbf2777e59928b6f3fa8ff54f72a0" 159 | dependencies = [ 160 | "ctor", 161 | "ghost", 162 | ] 163 | 164 | [[package]] 165 | name = "itertools" 166 | version = "0.10.3" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" 169 | dependencies = [ 170 | "either", 171 | ] 172 | 173 | [[package]] 174 | name = "libc" 175 | version = "0.2.122" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "ec647867e2bf0772e28c8bcde4f0d19a9216916e890543b5a03ed8ef27b8f259" 178 | 179 | [[package]] 180 | name = "linked-hash-map" 181 | version = "0.5.4" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" 184 | 185 | [[package]] 186 | name = "lock_api" 187 | version = "0.4.7" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" 190 | dependencies = [ 191 | "autocfg", 192 | "scopeguard", 193 | ] 194 | 195 | [[package]] 196 | name = "memchr" 197 | version = "2.4.1" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 200 | 201 | [[package]] 202 | name = "num-traits" 203 | version = "0.2.14" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 206 | dependencies = [ 207 | "autocfg", 208 | ] 209 | 210 | [[package]] 211 | name = "num_cpus" 212 | version = "1.13.1" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" 215 | dependencies = [ 216 | "hermit-abi", 217 | "libc", 218 | ] 219 | 220 | [[package]] 221 | name = "once_cell" 222 | version = "1.10.0" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" 225 | 226 | [[package]] 227 | name = "os_str_bytes" 228 | version = "6.0.0" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" 231 | dependencies = [ 232 | "memchr", 233 | ] 234 | 235 | [[package]] 236 | name = "parking_lot" 237 | version = "0.11.2" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" 240 | dependencies = [ 241 | "instant", 242 | "lock_api", 243 | "parking_lot_core", 244 | ] 245 | 246 | [[package]] 247 | name = "parking_lot_core" 248 | version = "0.8.5" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" 251 | dependencies = [ 252 | "cfg-if", 253 | "instant", 254 | "libc", 255 | "redox_syscall", 256 | "smallvec", 257 | "winapi", 258 | ] 259 | 260 | [[package]] 261 | name = "parse_int" 262 | version = "0.6.0" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "2d695b79916a2c08bcff7be7647ab60d1402885265005a6658ffe6d763553c5a" 265 | dependencies = [ 266 | "num-traits", 267 | ] 268 | 269 | [[package]] 270 | name = "petgraph" 271 | version = "0.6.0" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "4a13a2fa9d0b63e5f22328828741e523766fff0ee9e779316902290dff3f824f" 274 | dependencies = [ 275 | "fixedbitset", 276 | "indexmap", 277 | ] 278 | 279 | [[package]] 280 | name = "ppc750cl" 281 | version = "0.2.0" 282 | dependencies = [ 283 | "num-traits", 284 | "serde", 285 | ] 286 | 287 | [[package]] 288 | name = "ppc750cl-flow-graph" 289 | version = "0.2.0" 290 | dependencies = [ 291 | "clap", 292 | "dol", 293 | "itertools", 294 | "parse_int", 295 | "petgraph", 296 | "ppc750cl", 297 | ] 298 | 299 | [[package]] 300 | name = "ppc750cl-fuzz" 301 | version = "0.2.0" 302 | dependencies = [ 303 | "clap", 304 | "num_cpus", 305 | "ppc750cl", 306 | ] 307 | 308 | [[package]] 309 | name = "ppc750cl-genisa" 310 | version = "0.2.0" 311 | dependencies = [ 312 | "itertools", 313 | "proc-macro2", 314 | "quote", 315 | "serde", 316 | "serde_yaml", 317 | "syn", 318 | ] 319 | 320 | [[package]] 321 | name = "ppc750cl-py" 322 | version = "0.2.0" 323 | dependencies = [ 324 | "ppc750cl", 325 | "pyo3", 326 | ] 327 | 328 | [[package]] 329 | name = "ppc750cl-rand" 330 | version = "0.2.0" 331 | dependencies = [ 332 | "ppc750cl", 333 | "rand_core", 334 | "sfmt", 335 | ] 336 | 337 | [[package]] 338 | name = "ppv-lite86" 339 | version = "0.2.16" 340 | source = "registry+https://github.com/rust-lang/crates.io-index" 341 | checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" 342 | 343 | [[package]] 344 | name = "proc-macro2" 345 | version = "1.0.37" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" 348 | dependencies = [ 349 | "unicode-xid", 350 | ] 351 | 352 | [[package]] 353 | name = "pyo3" 354 | version = "0.16.3" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "6b3e99c4c3e790e4fc365b42b70c1f7801f42eadc4ea648fa327e6f5ca29f215" 357 | dependencies = [ 358 | "cfg-if", 359 | "indoc", 360 | "inventory", 361 | "libc", 362 | "parking_lot", 363 | "pyo3-build-config", 364 | "pyo3-ffi", 365 | "pyo3-macros", 366 | "unindent", 367 | ] 368 | 369 | [[package]] 370 | name = "pyo3-build-config" 371 | version = "0.16.3" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | checksum = "2486b96281859ff0a3929ba6467b13751627b974f7137362db38e2bed14b2094" 374 | dependencies = [ 375 | "once_cell", 376 | "target-lexicon", 377 | ] 378 | 379 | [[package]] 380 | name = "pyo3-ffi" 381 | version = "0.16.3" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "dd9de1d94557751599f8bd321f10e6c1ef2801067acb58c91138deef2ae83a17" 384 | dependencies = [ 385 | "libc", 386 | "pyo3-build-config", 387 | ] 388 | 389 | [[package]] 390 | name = "pyo3-macros" 391 | version = "0.16.3" 392 | source = "registry+https://github.com/rust-lang/crates.io-index" 393 | checksum = "0b9584049129b1cfb615243391a6345c726690271ae195ffd6aa3766177296aa" 394 | dependencies = [ 395 | "proc-macro2", 396 | "pyo3-macros-backend", 397 | "quote", 398 | "syn", 399 | ] 400 | 401 | [[package]] 402 | name = "pyo3-macros-backend" 403 | version = "0.16.3" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | checksum = "b6c4717e6a55c51a9958eda1f5481ff7f62cccd21f45309c10e4731cb7198dbc" 406 | dependencies = [ 407 | "proc-macro2", 408 | "quote", 409 | "syn", 410 | ] 411 | 412 | [[package]] 413 | name = "quote" 414 | version = "1.0.17" 415 | source = "registry+https://github.com/rust-lang/crates.io-index" 416 | checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58" 417 | dependencies = [ 418 | "proc-macro2", 419 | ] 420 | 421 | [[package]] 422 | name = "rand" 423 | version = "0.8.5" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 426 | dependencies = [ 427 | "libc", 428 | "rand_chacha", 429 | "rand_core", 430 | ] 431 | 432 | [[package]] 433 | name = "rand_chacha" 434 | version = "0.3.1" 435 | source = "registry+https://github.com/rust-lang/crates.io-index" 436 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 437 | dependencies = [ 438 | "ppv-lite86", 439 | "rand_core", 440 | ] 441 | 442 | [[package]] 443 | name = "rand_core" 444 | version = "0.6.3" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" 447 | dependencies = [ 448 | "getrandom", 449 | ] 450 | 451 | [[package]] 452 | name = "redox_syscall" 453 | version = "0.2.13" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" 456 | dependencies = [ 457 | "bitflags", 458 | ] 459 | 460 | [[package]] 461 | name = "ryu" 462 | version = "1.0.9" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" 465 | 466 | [[package]] 467 | name = "scopeguard" 468 | version = "1.1.0" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 471 | 472 | [[package]] 473 | name = "serde" 474 | version = "1.0.136" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" 477 | dependencies = [ 478 | "serde_derive", 479 | ] 480 | 481 | [[package]] 482 | name = "serde_derive" 483 | version = "1.0.136" 484 | source = "registry+https://github.com/rust-lang/crates.io-index" 485 | checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" 486 | dependencies = [ 487 | "proc-macro2", 488 | "quote", 489 | "syn", 490 | ] 491 | 492 | [[package]] 493 | name = "serde_yaml" 494 | version = "0.8.23" 495 | source = "registry+https://github.com/rust-lang/crates.io-index" 496 | checksum = "a4a521f2940385c165a24ee286aa8599633d162077a54bdcae2a6fd5a7bfa7a0" 497 | dependencies = [ 498 | "indexmap", 499 | "ryu", 500 | "serde", 501 | "yaml-rust", 502 | ] 503 | 504 | [[package]] 505 | name = "sfmt" 506 | version = "0.7.0" 507 | source = "registry+https://github.com/rust-lang/crates.io-index" 508 | checksum = "e389a3c1536438f85612667cd992dfb7169f60784252342278458e90d37c7565" 509 | dependencies = [ 510 | "rand", 511 | "rand_core", 512 | ] 513 | 514 | [[package]] 515 | name = "smallvec" 516 | version = "1.8.0" 517 | source = "registry+https://github.com/rust-lang/crates.io-index" 518 | checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" 519 | 520 | [[package]] 521 | name = "strsim" 522 | version = "0.10.0" 523 | source = "registry+https://github.com/rust-lang/crates.io-index" 524 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 525 | 526 | [[package]] 527 | name = "syn" 528 | version = "1.0.91" 529 | source = "registry+https://github.com/rust-lang/crates.io-index" 530 | checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" 531 | dependencies = [ 532 | "proc-macro2", 533 | "quote", 534 | "unicode-xid", 535 | ] 536 | 537 | [[package]] 538 | name = "target-lexicon" 539 | version = "0.12.3" 540 | source = "registry+https://github.com/rust-lang/crates.io-index" 541 | checksum = "d7fa7e55043acb85fca6b3c01485a2eeb6b69c5d21002e273c79e465f43b7ac1" 542 | 543 | [[package]] 544 | name = "termcolor" 545 | version = "1.1.3" 546 | source = "registry+https://github.com/rust-lang/crates.io-index" 547 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" 548 | dependencies = [ 549 | "winapi-util", 550 | ] 551 | 552 | [[package]] 553 | name = "textwrap" 554 | version = "0.15.0" 555 | source = "registry+https://github.com/rust-lang/crates.io-index" 556 | checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" 557 | 558 | [[package]] 559 | name = "thiserror" 560 | version = "1.0.30" 561 | source = "registry+https://github.com/rust-lang/crates.io-index" 562 | checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" 563 | dependencies = [ 564 | "thiserror-impl", 565 | ] 566 | 567 | [[package]] 568 | name = "thiserror-impl" 569 | version = "1.0.30" 570 | source = "registry+https://github.com/rust-lang/crates.io-index" 571 | checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" 572 | dependencies = [ 573 | "proc-macro2", 574 | "quote", 575 | "syn", 576 | ] 577 | 578 | [[package]] 579 | name = "unicode-xid" 580 | version = "0.2.2" 581 | source = "registry+https://github.com/rust-lang/crates.io-index" 582 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 583 | 584 | [[package]] 585 | name = "unindent" 586 | version = "0.1.8" 587 | source = "registry+https://github.com/rust-lang/crates.io-index" 588 | checksum = "514672a55d7380da379785a4d70ca8386c8883ff7eaae877be4d2081cebe73d8" 589 | 590 | [[package]] 591 | name = "wasi" 592 | version = "0.10.2+wasi-snapshot-preview1" 593 | source = "registry+https://github.com/rust-lang/crates.io-index" 594 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 595 | 596 | [[package]] 597 | name = "winapi" 598 | version = "0.3.9" 599 | source = "registry+https://github.com/rust-lang/crates.io-index" 600 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 601 | dependencies = [ 602 | "winapi-i686-pc-windows-gnu", 603 | "winapi-x86_64-pc-windows-gnu", 604 | ] 605 | 606 | [[package]] 607 | name = "winapi-i686-pc-windows-gnu" 608 | version = "0.4.0" 609 | source = "registry+https://github.com/rust-lang/crates.io-index" 610 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 611 | 612 | [[package]] 613 | name = "winapi-util" 614 | version = "0.1.5" 615 | source = "registry+https://github.com/rust-lang/crates.io-index" 616 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 617 | dependencies = [ 618 | "winapi", 619 | ] 620 | 621 | [[package]] 622 | name = "winapi-x86_64-pc-windows-gnu" 623 | version = "0.4.0" 624 | source = "registry+https://github.com/rust-lang/crates.io-index" 625 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 626 | 627 | [[package]] 628 | name = "yaml-rust" 629 | version = "0.4.5" 630 | source = "registry+https://github.com/rust-lang/crates.io-index" 631 | checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" 632 | dependencies = [ 633 | "linked-hash-map", 634 | ] 635 | -------------------------------------------------------------------------------- /genisa/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::fs::File; 3 | use std::io::Write; 4 | use std::ops::Range; 5 | use std::process::{Command, Stdio}; 6 | use std::str::FromStr; 7 | 8 | use itertools::Itertools; 9 | use proc_macro2::{Group, Ident, Literal, Span, TokenStream, TokenTree}; 10 | use quote::quote; 11 | use serde::{Deserialize, Deserializer}; 12 | use syn::{LitChar, LitInt, LitStr}; 13 | 14 | macro_rules! token_stream { 15 | ($stream:ident) => { 16 | TokenStream::from_iter($stream.into_iter()) 17 | }; 18 | } 19 | 20 | fn main() { 21 | if let Err(err) = _main() { 22 | eprintln!("{}", err); 23 | std::process::exit(1); 24 | } 25 | } 26 | 27 | type Error = Box; 28 | type Result = std::result::Result; 29 | 30 | fn _main() -> Result<()> { 31 | let isa = load_isa()?; 32 | 33 | let mut unformatted_code = Vec::::new(); 34 | writeln!( 35 | &mut unformatted_code, 36 | "{}", 37 | quote! { 38 | use crate::prelude::*; 39 | } 40 | )?; 41 | writeln!(&mut unformatted_code, "{}", isa.gen_opcode_enum()?)?; 42 | writeln!(&mut unformatted_code, "{}", isa.gen_field_enum()?)?; 43 | writeln!(&mut unformatted_code, "{}", isa.gen_field_impl()?)?; 44 | writeln!(&mut unformatted_code, "{}", isa.gen_ins_impl()?)?; 45 | 46 | let formatted_code = rustfmt(unformatted_code); 47 | File::create("./disasm/src/generated.rs")?.write_all(&formatted_code)?; 48 | 49 | Ok(()) 50 | } 51 | 52 | fn rustfmt(code: Vec) -> Vec { 53 | let mut rustfmt = Command::new("rustfmt") 54 | .stdin(Stdio::piped()) 55 | .stdout(Stdio::piped()) 56 | .spawn() 57 | .expect("failed to spawn rustfmt"); 58 | 59 | let mut stdin = rustfmt.stdin.take().unwrap(); 60 | std::thread::spawn(move || { 61 | let _ = stdin.write_all(&code); 62 | }); 63 | 64 | let rustfmt_res = rustfmt.wait_with_output().expect("failed to run rustfmt"); 65 | if !rustfmt_res.status.success() { 66 | panic!("rustfmt failed"); 67 | } 68 | 69 | rustfmt_res.stdout 70 | } 71 | 72 | #[derive(Default)] 73 | pub(crate) struct BitRange(Range); 74 | 75 | impl<'de> Deserialize<'de> for BitRange { 76 | fn deserialize(deserializer: D) -> std::result::Result 77 | where 78 | D: Deserializer<'de>, 79 | { 80 | let range_str: String = Deserialize::deserialize(deserializer)?; 81 | if let Some((start_str, stop_str)) = range_str.split_once("..") { 82 | let start = start_str.parse::().map_err(serde::de::Error::custom)?; 83 | let stop = stop_str.parse::().map_err(serde::de::Error::custom)?; 84 | Ok(Self(start..stop)) 85 | } else { 86 | let bit_idx = range_str.parse::().map_err(serde::de::Error::custom)?; 87 | Ok(Self(bit_idx..bit_idx)) 88 | } 89 | } 90 | } 91 | 92 | #[derive(Deserialize, Default)] 93 | #[serde(default)] 94 | pub(crate) struct Field { 95 | name: String, 96 | desc: String, 97 | bits: BitRange, 98 | signed: bool, 99 | split: bool, 100 | arg: Option, 101 | shift_left: u8, 102 | } 103 | 104 | impl Field { 105 | fn variant_identifier(&self) -> Option { 106 | self.identifier("") 107 | } 108 | 109 | fn identifier(&self, prefix: &str) -> Option { 110 | if self.name.strip_suffix(".nz").is_none() { 111 | Some(to_rust_ident(prefix, &self.name)) 112 | } else { 113 | None 114 | } 115 | } 116 | 117 | fn express_value(&self, code: TokenStream) -> TokenStream { 118 | let mut val = quote!(#code); 119 | 120 | let shift = 32 - self.bits.0.end; 121 | if shift > 0 { 122 | val = quote!((#val >> #shift)); 123 | } 124 | 125 | let mask = (1u32 << self.bits.0.len()) - 1; 126 | if mask != 0xFFFF_FFFF { 127 | let mask = LitInt::new(&format!("0x{:x}", mask), Span::call_site()); 128 | val = quote!((#val & #mask)); 129 | } 130 | 131 | if self.split { 132 | val = quote!((((#val & 0b11111_00000u32) >> 5u32) | ((#val & 0b00000_11111u32) << 5u32)) as u32); 133 | } 134 | 135 | // https://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend 136 | if self.signed { 137 | let mask2 = 1u32 << (self.bits.0.len() - 1); 138 | let mask2 = LitInt::new(&format!("0x{:x}", mask2), Span::call_site()); 139 | val = quote!((((#val ^ #mask2).wrapping_sub(#mask2)) as i32)) 140 | } 141 | 142 | let val_shift = self.shift_left; 143 | if val_shift > 0 { 144 | val = quote!((#val << #val_shift)); 145 | } 146 | 147 | val 148 | } 149 | 150 | fn express_value_self(&self) -> TokenStream { 151 | self.express_value(quote!(self.code)) 152 | } 153 | 154 | fn enum_variant_definition(&self) -> Option { 155 | let ident = self.variant_identifier()?; 156 | Some(if let Some(arg) = &self.arg { 157 | let arg = TokenTree::Ident(Ident::new(arg, Span::call_site())); 158 | quote! { 159 | #ident(#arg), 160 | } 161 | } else { 162 | quote! { 163 | #ident, 164 | } 165 | }) 166 | } 167 | 168 | pub(crate) fn construct_variant(&self, code: TokenStream) -> TokenStream { 169 | let field_variant = self.variant_identifier(); 170 | if let Some(arg) = &self.arg { 171 | let field_arg = TokenTree::Ident(Ident::new(arg, Span::call_site())); 172 | let value = self.express_value(code); 173 | quote! { 174 | Field::#field_variant(#field_arg(#value as _)) 175 | } 176 | } else { 177 | quote! { 178 | Field::#field_variant 179 | } 180 | } 181 | } 182 | 183 | fn construct_variant_self(&self) -> TokenStream { 184 | self.construct_variant(quote!(self.code)) 185 | } 186 | 187 | fn construct_accessor(&self) -> TokenStream { 188 | let field_variant = match self.identifier("field_") { 189 | Some(v) => v, 190 | None => return TokenStream::new(), 191 | }; 192 | if self.arg.is_none() { 193 | return TokenStream::new(); 194 | } 195 | let value = self.express_value_self(); 196 | let ret_type = if self.signed { 197 | quote!(isize) 198 | } else { 199 | quote!(usize) 200 | }; 201 | quote! { 202 | #[inline(always)] 203 | pub fn #field_variant(&self) -> #ret_type { 204 | #value as _ 205 | } 206 | } 207 | } 208 | } 209 | 210 | #[derive(Deserialize, Default)] 211 | #[serde(default)] 212 | pub(crate) struct Opcode { 213 | name: String, 214 | desc: String, 215 | bitmask: u32, 216 | pattern: u32, 217 | modifiers: Vec, 218 | side_effects: Vec, 219 | args: Vec, 220 | defs: Vec, 221 | uses: Vec, 222 | } 223 | 224 | impl Opcode { 225 | fn variant_identifier(&self) -> Result { 226 | to_rust_variant(&self.name) 227 | } 228 | } 229 | 230 | #[derive(Deserialize, Default)] 231 | #[serde(default)] 232 | pub(crate) struct Mnemonic { 233 | name: String, 234 | opcode: String, 235 | // Overrides modifier list from opcode 236 | modifiers: Option>, 237 | args: Vec, 238 | condition: String, 239 | } 240 | 241 | #[derive(Deserialize, Default)] 242 | #[serde(default)] 243 | pub(crate) struct Modifier { 244 | name: String, 245 | suffix: char, 246 | bit: u8, 247 | condition: String, 248 | } 249 | 250 | impl Modifier { 251 | fn express_value_self(&self, field_by_name: &HashMap) -> Result { 252 | if self.condition.is_empty() { 253 | let modifier_bit = self.bit as usize; 254 | Ok(quote!(self.bit(#modifier_bit))) 255 | } else { 256 | compile_mnemonic_condition(field_by_name, &self.condition) 257 | } 258 | } 259 | 260 | fn construct_accessor(&self, field_by_name: &HashMap) -> Result { 261 | let field_variant = to_rust_ident("field_", &self.name); 262 | let value = self.express_value_self(field_by_name)?; 263 | Ok(quote! { 264 | #[inline(always)] 265 | pub fn #field_variant(&self) -> bool { 266 | #value 267 | } 268 | }) 269 | } 270 | } 271 | 272 | #[derive(Deserialize, Default)] 273 | #[serde(default)] 274 | pub(crate) struct Isa { 275 | fields: Vec, 276 | modifiers: Vec, 277 | opcodes: Vec, 278 | mnemonics: Vec, 279 | } 280 | 281 | fn load_isa() -> Result { 282 | let yaml_file = File::open("isa.yaml")?; 283 | let isa: Isa = serde_yaml::from_reader(yaml_file)?; 284 | Ok(isa) 285 | } 286 | 287 | impl Isa { 288 | fn gen_opcode_enum(&self) -> Result { 289 | // Create enum variants. 290 | let enum_variants = self 291 | .opcodes 292 | .iter() 293 | .map(|opcode| -> Result { 294 | let ident = opcode.variant_identifier()?; 295 | Ok(quote! { 296 | #ident, 297 | }) 298 | }) 299 | .try_collect::, Error>()?; 300 | let enum_variants = token_stream!(enum_variants); 301 | 302 | // Create functions. 303 | let mnemonic_fn = self.gen_mnemonic_fn()?; 304 | let detect_fn = self.gen_opcode_detect()?; 305 | 306 | // Create final enum. 307 | let opcode_enum = quote! { 308 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] 309 | pub enum Opcode { 310 | Illegal = -1, 311 | #enum_variants 312 | } 313 | #[allow(clippy::all)] 314 | impl Opcode { 315 | #mnemonic_fn 316 | #detect_fn 317 | } 318 | }; 319 | Ok(opcode_enum) 320 | } 321 | 322 | fn gen_mnemonic_fn(&self) -> Result { 323 | // Create match arms. 324 | let match_arms = self 325 | .opcodes 326 | .iter() 327 | .map(|opcode| { 328 | let variant = opcode.variant_identifier()?; 329 | let literal = Literal::string(&opcode.name); 330 | Ok(quote! { 331 | Opcode::#variant => #literal, 332 | }) 333 | }) 334 | .try_collect::, Error>()?; 335 | let match_arms = token_stream!(match_arms); 336 | // Create final function. 337 | let mnemonic_fn = quote! { 338 | pub(crate) fn _mnemonic(self) -> &'static str { 339 | match self { 340 | Opcode::Illegal => "", 341 | #match_arms 342 | } 343 | } 344 | }; 345 | Ok(mnemonic_fn) 346 | } 347 | 348 | fn gen_opcode_detect(&self) -> Result { 349 | // Generate if chain. 350 | let if_chain = self 351 | .opcodes 352 | .iter() 353 | .map(|opcode| { 354 | let bitmask_str = format!("{:>#8x}", opcode.bitmask); 355 | let bitmask = LitInt::new(&bitmask_str, Span::call_site()); 356 | let pattern_str = format!("{:>#8x}", opcode.pattern); 357 | let pattern = LitInt::new(&pattern_str, Span::call_site()); 358 | let identifier = opcode.variant_identifier()?; 359 | Ok(quote! { 360 | if code & #bitmask == #pattern { 361 | return Opcode::#identifier; 362 | } 363 | }) 364 | }) 365 | .try_collect::, Error>()?; 366 | let if_chain = token_stream!(if_chain); 367 | // Generate function. 368 | let func = quote! { 369 | pub(crate) fn _detect(code: u32) -> Self { 370 | #if_chain 371 | Opcode::Illegal 372 | } 373 | }; 374 | Ok(func) 375 | } 376 | 377 | fn gen_field_enum(&self) -> Result { 378 | // Create enum variants. 379 | let mut enum_variants = Vec::new(); 380 | for field in &self.fields { 381 | if let Some(field) = field.enum_variant_definition() { 382 | enum_variants.push(field); 383 | } 384 | } 385 | let enum_variants = token_stream!(enum_variants); 386 | 387 | // Create final enum. 388 | let field_enum = quote! { 389 | #[allow(non_camel_case_types)] 390 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] 391 | pub enum Field { 392 | #enum_variants 393 | } 394 | }; 395 | Ok(field_enum) 396 | } 397 | 398 | fn gen_field_argument(&self) -> Result { 399 | let mut match_arms = Vec::new(); 400 | for field in &self.fields { 401 | if let Some(variant) = field.variant_identifier() { 402 | if let Some(arg_str) = field.arg.as_ref() { 403 | let arg = Ident::new(arg_str, Span::call_site()); 404 | match_arms.push(quote! { Field::#variant(x) => Some(Argument::#arg(*x)), }); 405 | } 406 | } 407 | } 408 | let match_arms = token_stream!(match_arms); 409 | Ok(quote! { 410 | pub fn argument(&self) -> Option { 411 | match self { 412 | #match_arms 413 | _ => None, 414 | } 415 | } 416 | }) 417 | } 418 | 419 | fn gen_field_name(&self) -> Result { 420 | let mut match_arms = Vec::new(); 421 | for field in &self.fields { 422 | if let Some(variant) = field.variant_identifier() { 423 | let name = LitStr::new(&variant.to_string(), Span::call_site()); 424 | let arg = field.arg.as_ref().map(|_| quote!((_))); 425 | match_arms.push(quote! { Field::#variant #arg => #name, }); 426 | } 427 | } 428 | let match_arms = token_stream!(match_arms); 429 | Ok(quote! { 430 | pub fn name(&self) -> &'static str { 431 | match self { 432 | #match_arms 433 | } 434 | } 435 | }) 436 | } 437 | 438 | fn gen_field_impl(&self) -> Result { 439 | let field_argument = self.gen_field_argument()?; 440 | let field_name = self.gen_field_name()?; 441 | Ok(quote! { 442 | impl Field { 443 | #field_argument 444 | #field_name 445 | } 446 | }) 447 | } 448 | 449 | fn gen_ins_impl(&self) -> Result { 450 | // Map fields by name. 451 | let mut field_by_name = HashMap::::new(); 452 | for field in &self.fields { 453 | field_by_name.insert(field.name.clone(), field); 454 | } 455 | let mut modifier_by_name = HashMap::::new(); 456 | for modifier in &self.modifiers { 457 | modifier_by_name.insert(modifier.name.clone(), modifier); 458 | } 459 | // Map mnemonics by opcode. 460 | let mut mnemonics_by_opcode = HashMap::<&String, Vec<&Mnemonic>>::new(); 461 | for simple in &self.mnemonics { 462 | mnemonics_by_opcode 463 | .entry(&simple.opcode) 464 | .or_insert_with(Vec::new) 465 | .push(simple) 466 | } 467 | // Generate match arms for each opcode. 468 | let mut field_match_arms = Vec::new(); 469 | let mut def_match_arms = Vec::new(); 470 | let mut use_match_arms = Vec::new(); 471 | let mut suffix_match_arms = Vec::new(); 472 | let mut simplified_ins_match_arms = Vec::new(); 473 | for opcode in &self.opcodes { 474 | // Generate fields of opcode. 475 | let mut fields = Vec::new(); 476 | for arg in &opcode.args { 477 | let field: &Field = field_by_name 478 | .get(arg) 479 | .ok_or_else(|| Error::from(format!("undefined field {}", arg)))?; 480 | let variant = field.construct_variant_self(); 481 | fields.extend(quote! { #variant, }) 482 | } 483 | let fields = token_stream!(fields); 484 | // Emit match arm. 485 | let ident = opcode.variant_identifier()?; 486 | field_match_arms.push(quote! { 487 | Opcode::#ident => vec![#fields], 488 | }); 489 | 490 | // Generate modifiers. 491 | let suffix = express_suffix(&modifier_by_name, &field_by_name, &opcode.modifiers)?; 492 | suffix_match_arms.push(quote! { 493 | Opcode::#ident => #suffix, 494 | }); 495 | 496 | // Generate defs. 497 | let mut defs = Vec::new(); 498 | for arg in &opcode.defs { 499 | let field: &Field = field_by_name.get(arg).ok_or_else(|| { 500 | syn::Error::new(Span::call_site(), format!("undefined field {}", arg)) 501 | })?; 502 | let variant = field.construct_variant_self(); 503 | defs.extend(quote! { #variant, }) 504 | } 505 | let defs = token_stream!(defs); 506 | let ident = opcode.variant_identifier()?; 507 | def_match_arms.push(quote! { 508 | Opcode::#ident => vec![#defs], 509 | }); 510 | 511 | // Generate uses. 512 | let mut uses = Vec::new(); 513 | let mut special_uses = Vec::new(); 514 | for arg in &opcode.uses { 515 | // Detect non-zero modifier. 516 | let mut arg = arg.as_str(); 517 | let mut non_zero = false; 518 | if let Some(substr) = arg.strip_suffix(".nz") { 519 | non_zero = true; 520 | arg = substr; 521 | } 522 | // Get underlying field. 523 | let field: &Field = field_by_name.get(arg).ok_or_else(|| { 524 | syn::Error::new(Span::call_site(), format!("undefined field {}", arg)) 525 | })?; 526 | let variant = field.construct_variant_self(); 527 | if non_zero { 528 | let value = field.express_value_self(); 529 | special_uses.extend(quote! { 530 | if (#value) != 0 { 531 | uses.push(#variant); 532 | } 533 | }) 534 | } else { 535 | uses.extend(quote! { 536 | #variant, 537 | }) 538 | } 539 | } 540 | let uses = token_stream!(uses); 541 | let ident = opcode.variant_identifier()?; 542 | let special_uses = token_stream!(special_uses); 543 | use_match_arms.push(quote! { 544 | Opcode::#ident => { 545 | let mut uses = vec![#uses]; 546 | #special_uses 547 | uses 548 | }, 549 | }); 550 | 551 | // Generate instruction simplification. 552 | if let Some(mnemonics) = mnemonics_by_opcode.get(&opcode.name) { 553 | let mut simplified_conditions = Vec::new(); 554 | for mnemonic in mnemonics { 555 | if mnemonic.condition.is_empty() { 556 | continue; // TODO else branch 557 | } 558 | simplified_conditions.push(quote!(if)); 559 | simplified_conditions.push(compile_mnemonic_condition( 560 | &field_by_name, 561 | &mnemonic.condition, 562 | )?); 563 | // Emit branch. 564 | let mnemonic_lit = LitStr::new(&mnemonic.name, Span::call_site()); 565 | // Emit suffix. 566 | let modifiers = mnemonic.modifiers.as_ref().unwrap_or(&opcode.modifiers); 567 | let suffix = express_suffix(&modifier_by_name, &field_by_name, modifiers)?; 568 | // Extract arguments. 569 | let mut args = Vec::new(); 570 | for arg in &mnemonic.args { 571 | let (field_name, expression) = arg.split_once('=').unwrap_or((arg, arg)); 572 | let field = field_by_name 573 | .get(field_name) 574 | .unwrap_or_else(|| panic!("field not found: {}", arg)); 575 | let variant = Ident::new(field.arg.as_ref().unwrap(), Span::call_site()); 576 | let value = compile_mnemonic_condition(&field_by_name, expression)?; 577 | args.push(quote!(Argument::#variant(#variant((#value) as _)),)); 578 | } 579 | let args = token_stream!(args); 580 | simplified_conditions.push(quote! { 581 | { 582 | return SimplifiedIns { 583 | mnemonic: #mnemonic_lit, 584 | suffix: #suffix, 585 | args: vec![#args], 586 | ins: self, 587 | }; 588 | } 589 | }); 590 | } 591 | let simplified_conditions = token_stream!(simplified_conditions); 592 | simplified_ins_match_arms.push(quote! { 593 | Opcode::#ident => { 594 | #simplified_conditions 595 | }, 596 | }); 597 | } 598 | } 599 | let field_match_arms = token_stream!(field_match_arms); 600 | let def_match_arms = token_stream!(def_match_arms); 601 | let use_match_arms = token_stream!(use_match_arms); 602 | let suffix_match_arms = token_stream!(suffix_match_arms); 603 | let simplified_ins_match_arms = token_stream!(simplified_ins_match_arms); 604 | let field_accessors = 605 | TokenStream::from_iter(self.fields.iter().map(|field| field.construct_accessor())); 606 | let modifiers: Vec = self 607 | .modifiers 608 | .iter() 609 | .map(|modifier| modifier.construct_accessor(&field_by_name)) 610 | .try_collect()?; 611 | let modifier_accessors = TokenStream::from_iter(modifiers); 612 | // Generate final fields function. 613 | let ins_impl = quote! { 614 | #[allow(clippy::all, unused_mut)] 615 | impl Ins { 616 | pub(crate) fn _fields(&self) -> Vec { 617 | match self.op { 618 | Opcode::Illegal => vec![], 619 | #field_match_arms 620 | } 621 | } 622 | 623 | pub(crate) fn _defs(&self) -> Vec { 624 | match self.op { 625 | Opcode::Illegal => vec![], 626 | #def_match_arms 627 | } 628 | } 629 | 630 | pub(crate) fn _uses(&self) -> Vec { 631 | match self.op { 632 | Opcode::Illegal => vec![], 633 | #use_match_arms 634 | } 635 | } 636 | 637 | pub(crate) fn _suffix(&self) -> String { 638 | match self.op { 639 | Opcode::Illegal => String::new(), 640 | #suffix_match_arms 641 | } 642 | } 643 | 644 | pub(crate) fn _simplified(self) -> SimplifiedIns { 645 | match self.op { 646 | #simplified_ins_match_arms 647 | _ => {} 648 | } 649 | SimplifiedIns::basic_form(self) 650 | } 651 | } 652 | #[allow(clippy::all, non_snake_case)] 653 | impl Ins { 654 | #field_accessors 655 | #modifier_accessors 656 | } 657 | }; 658 | Ok(ins_impl) 659 | } 660 | } 661 | 662 | /// Converts the given key into an identifier. 663 | fn to_rust_ident(prefix: &str, key: &str) -> TokenTree { 664 | TokenTree::Ident(Ident::new( 665 | &(prefix.to_owned() + &key.replace('.', "_")), 666 | Span::call_site(), 667 | )) 668 | } 669 | 670 | /// Converts the given key into an enum variant key. 671 | fn to_rust_variant(key: &str) -> Result { 672 | Ok(TokenTree::Ident(Ident::new( 673 | &to_rust_variant_str(key)?, 674 | Span::call_site(), 675 | ))) 676 | } 677 | 678 | fn to_rust_variant_str(key: &str) -> Result { 679 | let mut s = String::new(); 680 | let mut chars = key.chars(); 681 | loop { 682 | // Make first char uppercase. 683 | let c = match chars.next() { 684 | None => return Ok(s), 685 | Some(c) => c, 686 | }; 687 | s.push(match c { 688 | 'a'..='z' => c.to_ascii_uppercase(), 689 | 'A'..='Z' => c, 690 | _ => return Err(format!("invalid identifier: {}", key).into()), 691 | }); 692 | loop { 693 | let c = match chars.next() { 694 | None => return Ok(s), 695 | Some(c) => c, 696 | }; 697 | match c.to_ascii_lowercase() { 698 | '0'..='9' | 'a'..='z' => s.push(c), 699 | '_' => break, 700 | '.' => { 701 | s.push('_'); 702 | break; 703 | } 704 | _ => return Err(format!("invalid character in opcode name: {}", key).into()), 705 | } 706 | } 707 | } 708 | } 709 | 710 | /// Compiles conditions such as `S == B` into valid Rust expressions on a PowerPC instruction. 711 | fn compile_mnemonic_condition( 712 | field_by_name: &HashMap, 713 | code: &str, 714 | ) -> Result { 715 | let src_stream = TokenStream::from_str(code)?; 716 | fn map_ident(field_by_name: &HashMap, token: TokenTree) -> TokenStream { 717 | match token { 718 | TokenTree::Ident(ref ident) => { 719 | if let Some(field) = field_by_name.get(&ident.to_string()) { 720 | return field.express_value_self(); 721 | } 722 | } 723 | TokenTree::Group(ref group) => { 724 | let iter = group 725 | .stream() 726 | .into_iter() 727 | .flat_map(|token| map_ident(field_by_name, token)); 728 | let stream = TokenStream::from_iter(iter); 729 | return TokenStream::from(TokenTree::Group(Group::new(group.delimiter(), stream))); 730 | } 731 | _ => {} 732 | } 733 | token.into() 734 | } 735 | let token_iter = src_stream 736 | .into_iter() 737 | .flat_map(|token| map_ident(field_by_name, token)); 738 | Ok(TokenStream::from_iter(token_iter)) 739 | } 740 | 741 | fn express_suffix( 742 | modifier_by_name: &HashMap, 743 | field_by_name: &HashMap, 744 | modifiers: &[String], 745 | ) -> Result { 746 | Ok(if modifiers.is_empty() { 747 | quote!(String::new()) 748 | } else { 749 | let mut chars = Vec::new(); 750 | for mod_name in modifiers { 751 | let modifier: &Modifier = modifier_by_name 752 | .get(mod_name) 753 | .ok_or_else(|| Error::from(format!("undefined modifier {}", mod_name)))?; 754 | let lit_char = LitChar::new(modifier.suffix, Span::call_site()); 755 | let modifier_bit = modifier.express_value_self(field_by_name)?; 756 | chars.push(quote! { 757 | if #modifier_bit { 758 | s.push(#lit_char); 759 | } 760 | }); 761 | } 762 | let chars = token_stream!(chars); 763 | quote!({ 764 | { 765 | let mut s = String::with_capacity(4); 766 | #chars 767 | s 768 | } 769 | }) 770 | }) 771 | } 772 | -------------------------------------------------------------------------------- /disasm/tests/test_disasm.rs: -------------------------------------------------------------------------------- 1 | use ppc750cl::prelude::*; 2 | 3 | macro_rules! assert_asm { 4 | ($ins:ident, $disasm:literal) => {{ 5 | assert_eq!(format!("{}", FormattedIns($ins)), $disasm) 6 | }}; 7 | ($code:literal, $disasm:literal) => {{ 8 | let ins = Ins::new($code, 0x8000_0000); 9 | assert_eq!(format!("{}", FormattedIns(ins)), $disasm) 10 | }}; 11 | } 12 | 13 | #[test] 14 | fn test_ins_addc() { 15 | let ins = Ins::new(0x7c002014, 0x8000_0000u32); 16 | assert_eq!(ins.op, Addc); 17 | assert_eq!(ins.fields(), vec![rD(GPR(0)), rA(GPR(0)), rB(GPR(4))]); 18 | assert_asm!(ins, "addc r0, r0, r4"); 19 | } 20 | 21 | #[test] 22 | fn test_ins_addi() { 23 | let ins = Ins::new(0x38010140, 0x8000_0000u32); 24 | assert_eq!(ins.op, Addi); 25 | assert_eq!( 26 | ins.fields(), 27 | vec![rD(GPR(0)), rA(GPR(1)), simm(Simm(0x140))] 28 | ); 29 | assert_eq!(ins.defs(), vec![rD(GPR(0))]); 30 | assert_eq!(ins.uses(), vec![rA(GPR(1))]); 31 | assert_asm!(ins, "addi r0, r1, 0x140"); 32 | 33 | assert_asm!(0x38010008, "addi r0, r1, 0x8"); 34 | assert_asm!(0x38010010, "addi r0, r1, 0x10"); 35 | assert_asm!(0x38010018, "addi r0, r1, 0x18"); 36 | assert_asm!(0x38010140, "addi r0, r1, 0x140"); 37 | assert_asm!(0x38049000, "addi r0, r4, -0x7000"); 38 | assert_asm!(0x38a00000, "li r5, 0x0"); 39 | } 40 | 41 | #[test] 42 | fn test_ins_adde() { 43 | assert_asm!(0x7c006114, "adde r0, r0, r12"); 44 | } 45 | 46 | #[test] 47 | fn test_ins_addic() { 48 | assert_asm!(0x3060ffff, "addic r3, r0, -0x1"); 49 | assert_asm!(0x30840800, "addic r4, r4, 0x800"); 50 | assert_asm!(0x30a50008, "addic r5, r5, 0x8"); 51 | assert_asm!(0x37DF001C, "addic. r30, r31, 0x1c"); 52 | assert_asm!(0x37E06278, "addic. r31, r0, 0x6278"); 53 | assert_asm!(0x37E3FFFF, "addic. r31, r3, -0x1"); 54 | } 55 | 56 | #[test] 57 | fn test_ins_addis() { 58 | assert_asm!(0x3C030000, "addis r0, r3, 0x0"); 59 | assert_asm!(0x3C038000, "addis r0, r3, 0x8000"); 60 | assert_asm!(0x3D00EFCE, "lis r8, 0xefce"); 61 | } 62 | 63 | #[test] 64 | fn test_ins_addze() { 65 | assert_asm!(0x7C000194, "addze r0, r0"); 66 | } 67 | 68 | #[test] 69 | fn test_ins_and() { 70 | assert_asm!(0x7C001838, "and r0, r0, r3"); 71 | assert_asm!(0x7C001839, "and. r0, r0, r3"); 72 | } 73 | 74 | #[test] 75 | fn test_ins_andc() { 76 | assert_asm!(0x7C001878, "andc r0, r0, r3"); 77 | } 78 | 79 | #[test] 80 | fn test_ins_andi_() { 81 | assert_asm!(0x70000009, "andi. r0, r0, 0x9"); 82 | } 83 | 84 | #[test] 85 | fn test_ins_andis_() { 86 | assert_asm!(0x77c802ff, "andis. r8, r30, 0x2ff"); 87 | } 88 | 89 | #[test] 90 | fn test_ins_b() { 91 | assert_asm!(0x48000000, "b 0x0"); 92 | assert_asm!(0x48000004, "b 0x4"); 93 | assert_asm!(0x4800A5C9, "bl 0xa5c8"); 94 | assert_asm!(0x4823B4D9, "bl 0x23b4d8"); 95 | assert_asm!(0x4BE03C99, "bl -0x1fc368"); 96 | assert_asm!(0x4BDC1A59, "bl -0x23e5a8"); 97 | assert_asm!(0x48000063, "bla 0x60"); 98 | assert_asm!(0x48000002, "ba 0x0"); 99 | } 100 | 101 | #[test] 102 | fn test_ins_bc() { 103 | assert_asm!(0x40800008, "bge 0x8"); 104 | assert_asm!(0x40802350, "bge 0x2350"); 105 | assert_asm!(0x4080FC7C, "bge -0x384"); 106 | assert_asm!(0x408100AC, "ble 0xac"); 107 | assert_asm!(0x4081F788, "ble -0x878"); 108 | assert_asm!(0x40821BA0, "bne 0x1ba0"); 109 | assert_asm!(0x4082E3C4, "bne -0x1c3c"); 110 | assert_asm!(0x408600D8, "bne cr1, 0xd8"); 111 | assert_asm!(0x4086FECC, "bne cr1, -0x134"); 112 | assert_asm!(0x409C000C, "bge cr7, 0xc"); 113 | assert_asm!(0x4180000C, "blt 0xc"); 114 | assert_asm!(0x4180F9C0, "blt -0x640"); 115 | assert_asm!(0x4181021C, "bgt 0x21c"); 116 | assert_asm!(0x4181FD80, "bgt -0x280"); 117 | assert_asm!(0x41822304, "beq 0x2304"); 118 | assert_asm!(0x4182FE3C, "beq -0x1c4"); 119 | assert_asm!(0x418401AC, "blt cr1, 0x1ac"); 120 | assert_asm!(0x4184FCE4, "blt cr1, -0x31c"); 121 | assert_asm!(0x418500C0, "bgt cr1, 0xc0"); 122 | assert_asm!(0x418502E4, "bgt cr1, 0x2e4"); 123 | assert_asm!(0x419A0138, "beq cr6, 0x138"); 124 | assert_asm!(0x419C0008, "blt cr7, 0x8"); 125 | assert_asm!(0x4240FFF0, "bdz -0x10"); 126 | assert_asm!(0x4200F560, "bdnz -0xaa0"); 127 | assert_asm!(0x40010014, "bdnzf gt, 0x14"); 128 | assert_asm!(0x40410035, "bdzfl gt, 0x34"); 129 | assert_asm!(0x41430023, "bdztla so, 0x20"); 130 | assert_asm!(0x4108FFE3, "bdnztla 4*cr2+lt, -0x20"); 131 | assert_asm!(0x40A20008, "bne+ 0x8"); 132 | } 133 | 134 | #[test] 135 | fn test_ins_bcctr() { 136 | assert_asm!(0x4E800420, "bctr"); 137 | assert_asm!(0x4E800421, "bctrl"); 138 | assert_asm!(0x4D820420, "beqctr"); 139 | assert_asm!(0x4D8D0421, "bgtctrl cr3"); 140 | assert_asm!(0x4DA20420, "beqctr+"); 141 | assert_asm!(0x4DB90421, "bgtctrl+ cr6"); 142 | } 143 | 144 | #[test] 145 | fn test_ins_bclr() { 146 | assert_asm!(0x4C800020, "bgelr"); 147 | assert_asm!(0x4CA00020, "bgelr+"); 148 | assert_asm!(0x4C810020, "blelr"); 149 | assert_asm!(0x4C820020, "bnelr"); 150 | assert_asm!(0x4C9E0020, "bnelr cr7"); 151 | assert_asm!(0x4D800020, "bltlr"); 152 | assert_asm!(0x4D810020, "bgtlr"); 153 | assert_asm!(0x4D820020, "beqlr"); 154 | assert_asm!(0x4D860020, "beqlr cr1"); 155 | assert_asm!(0x4E800020, "blr"); 156 | assert_asm!(0x4E800021, "blrl"); 157 | assert_asm!(0x4D000020, "bdnztlr lt"); 158 | assert_asm!(0x4C1F0021, "bdnzflrl 4*cr7+so"); 159 | } 160 | 161 | #[test] 162 | fn test_ins_cmp() { 163 | assert_asm!(0x7C030000, "cmpw r3, r0"); 164 | } 165 | 166 | #[test] 167 | fn test_ins_cmpi() { 168 | assert_asm!(0x2C050D00, "cmpwi r5, 0xd00"); 169 | assert_asm!(0x2F1F0000, "cmpwi cr6, r31, 0x0"); 170 | } 171 | 172 | #[test] 173 | fn test_ins_cmpl() { 174 | assert_asm!(0x7C9A2040, "cmplw cr1, r26, r4"); 175 | } 176 | 177 | #[test] 178 | fn test_ins_cmpli() { 179 | assert_asm!(0x2803FFF3, "cmplwi r3, 0xfff3"); 180 | assert_asm!(0x2884F8F0, "cmplwi cr1, r4, 0xf8f0"); 181 | } 182 | 183 | #[test] 184 | fn test_ins_cntlzw() { 185 | assert_asm!(0x7C030034, "cntlzw r3, r0"); 186 | } 187 | 188 | #[test] 189 | fn test_ins_crand() { 190 | assert_asm!(0x4C853202, "crand 4*cr1+lt, 4*cr1+gt, 4*cr1+eq"); 191 | } 192 | 193 | #[test] 194 | fn test_ins_crandc() { 195 | assert_asm!(0x4C642902, "crandc so, 4*cr1+lt, 4*cr1+gt"); 196 | } 197 | 198 | #[test] 199 | fn test_ins_creqv() { 200 | assert_asm!(0x4CE00A42, "creqv 4*cr1+so, lt, gt"); 201 | } 202 | 203 | #[test] 204 | fn test_ins_crnand() { 205 | assert_asm!(0x4C2219C2, "crnand gt, eq, so"); 206 | } 207 | 208 | #[test] 209 | fn test_ins_cror() { 210 | assert_asm!(0x4C411382, "cror eq, gt, eq"); 211 | assert_asm!(0x4CA63B82, "cror 4*cr1+gt, 4*cr1+eq, 4*cr1+so"); 212 | } 213 | 214 | #[test] 215 | fn test_ins_crorc() { 216 | assert_asm!(0x4C432342, "crorc eq, so, 4*cr1+lt"); 217 | } 218 | 219 | #[test] 220 | fn test_ins_crnor() { 221 | assert_asm!(0x4C011042, "crnor lt, gt, eq"); 222 | assert_asm!(0x4CA63042, "crnot 4*cr1+gt, 4*cr1+eq"); 223 | } 224 | 225 | #[test] 226 | fn test_ins_crxor() { 227 | assert_asm!(0x4CC70182, "crxor 4*cr1+eq, 4*cr1+so, lt"); 228 | } 229 | 230 | #[test] 231 | fn test_ins_dcbf() { 232 | assert_asm!(0x7C0028AC, "dcbf r0, r5"); 233 | } 234 | 235 | #[test] 236 | fn test_ins_dcbi() { 237 | assert_asm!(0x7C001BAC, "dcbi r0, r3"); 238 | } 239 | 240 | #[test] 241 | fn test_ins_dcbst() { 242 | assert_asm!(0x7C00286C, "dcbst r0, r5"); 243 | } 244 | 245 | #[test] 246 | fn test_ins_dcbt() { 247 | assert_asm!(0x7C001A2C, "dcbt r0, r3"); 248 | } 249 | 250 | #[test] 251 | fn test_ins_dcbz() { 252 | assert_asm!(0x7C001FEC, "dcbz r0, r3"); 253 | } 254 | 255 | #[test] 256 | fn test_ins_dcbz_l() { 257 | assert_asm!(0x10061FEC, "dcbz_l r6, r3"); 258 | } 259 | 260 | #[test] 261 | fn test_ins_divw() { 262 | assert_asm!(0x7C8073D6, "divw r4, r0, r14"); 263 | } 264 | 265 | #[test] 266 | fn test_ins_divwu() { 267 | assert_asm!(0x7C69E396, "divwu r3, r9, r28"); 268 | } 269 | 270 | #[test] 271 | fn test_ins_extsb() { 272 | assert_asm!(0x7C650774, "extsb r5, r3"); 273 | assert_asm!(0x7C650775, "extsb. r5, r3"); 274 | } 275 | 276 | #[test] 277 | fn test_ins_extsh() { 278 | assert_asm!(0x7C000734, "extsh r0, r0"); 279 | assert_asm!(0x7C000735, "extsh. r0, r0"); 280 | } 281 | 282 | #[test] 283 | fn test_ins_fabs() { 284 | assert_asm!(0xFC000A10, "fabs f0, f1"); 285 | } 286 | 287 | #[test] 288 | fn test_ins_fadd() { 289 | assert_asm!(0xFC00282A, "fadd f0, f0, f5"); 290 | } 291 | 292 | #[test] 293 | fn test_ins_fadds() { 294 | assert_asm!(0xEC41602A, "fadds f2, f1, f12"); 295 | } 296 | 297 | #[test] 298 | fn test_ins_fcmpo() { 299 | assert_asm!(0xFC00C840, "fcmpo cr0, f0, f25"); 300 | } 301 | 302 | #[test] 303 | fn test_ins_fcmpu() { 304 | assert_asm!(0xFC00D000, "fcmpu cr0, f0, f26"); 305 | } 306 | 307 | #[test] 308 | fn test_ins_fctiwz() { 309 | assert_asm!(0xFC20001E, "fctiwz f1, f0"); 310 | } 311 | 312 | #[test] 313 | fn test_ins_fdiv() { 314 | assert_asm!(0xFC200024, "fdiv f1, f0, f0"); 315 | } 316 | 317 | #[test] 318 | fn test_ins_fdivs() { 319 | assert_asm!(0xEC01F824, "fdivs f0, f1, f31"); 320 | } 321 | 322 | #[test] 323 | fn test_ins_fmadds() { 324 | assert_asm!(0xEC0200FA, "fmadds f0, f2, f3, f0"); 325 | } 326 | 327 | #[test] 328 | fn test_ins_fmsub() { 329 | assert_asm!(0xFC000028, "fsub f0, f0, f0"); 330 | } 331 | 332 | #[test] 333 | fn test_ins_fmsubs() { 334 | assert_asm!(0xEC00B828, "fsubs f0, f0, f23"); 335 | } 336 | 337 | #[test] 338 | fn test_ins_fmul() { 339 | assert_asm!(0xFC0000B2, "fmul f0, f0, f2"); 340 | assert_asm!(0xFC0000F2, "fmul f0, f0, f3"); 341 | } 342 | 343 | #[test] 344 | fn test_ins_fmuls() { 345 | assert_asm!(0xEC0007B2, "fmuls f0, f0, f30"); 346 | } 347 | 348 | #[test] 349 | fn test_ins_fneg() { 350 | assert_asm!(0xFCE00050, "fneg f7, f0"); 351 | } 352 | 353 | #[test] 354 | fn test_ins_fnmsub() { 355 | assert_asm!(0xFCC640BC, "fnmsub f6, f6, f2, f8"); 356 | } 357 | 358 | #[test] 359 | fn test_ins_fnmsubs() { 360 | assert_asm!(0xEC022B3C, "fnmsubs f0, f2, f12, f5"); 361 | } 362 | 363 | #[test] 364 | fn test_ins_fres() { 365 | assert_asm!(0xEC000830, "fres f0, f1"); 366 | } 367 | 368 | #[test] 369 | fn test_ins_frsp() { 370 | assert_asm!(0xFC000018, "frsp f0, f0"); 371 | } 372 | 373 | #[test] 374 | fn test_ins_frsqrte() { 375 | assert_asm!(0xFC000834, "frsqrte f0, f1"); 376 | } 377 | 378 | #[test] 379 | fn test_ins_fsel() { 380 | assert_asm!(0xFC01F82E, "fsel f0, f1, f0, f31"); 381 | } 382 | 383 | #[test] 384 | fn test_ins_fsub() { 385 | assert_asm!(0xFC000828, "fsub f0, f0, f1"); 386 | } 387 | 388 | #[test] 389 | fn test_ins_fsubs() { 390 | assert_asm!(0xEC000828, "fsubs f0, f0, f1"); 391 | } 392 | 393 | #[test] 394 | fn test_ins_icbi() { 395 | assert_asm!(0x7C001FAC, "icbi r0, r3"); 396 | } 397 | 398 | #[test] 399 | fn test_ins_isync() { 400 | assert_asm!(0x4C00012C, "isync"); 401 | } 402 | 403 | #[test] 404 | fn test_ins_lbz() { 405 | assert_asm!(0x880104CC, "lbz r0, 0x4cc(r1)"); 406 | assert_asm!(0x8802801B, "lbz r0, -0x7fe5(r2)"); 407 | } 408 | 409 | #[test] 410 | fn test_ins_lbzu() { 411 | assert_asm!(0x8D9DCA10, "lbzu r12, -0x35f0(r29)"); 412 | assert_asm!(0x8E3053EC, "lbzu r17, 0x53ec(r16)"); 413 | } 414 | 415 | #[test] 416 | fn test_ins_lbzux() { 417 | assert_asm!(0x7C0400EE, "lbzux r0, r4, r0"); 418 | } 419 | 420 | #[test] 421 | fn test_ins_lbzx() { 422 | assert_asm!(0x7C0300AE, "lbzx r0, r3, r0"); 423 | } 424 | 425 | #[test] 426 | fn test_ins_lfd() { 427 | assert_asm!(0xC80140C8, "lfd f0, 0x40c8(r1)"); 428 | assert_asm!(0xC8028090, "lfd f0, -0x7f70(r2)"); 429 | } 430 | 431 | #[test] 432 | fn test_ins_lfdu() { 433 | assert_asm!(0xCC03FFC0, "lfdu f0, -0x40(r3)"); 434 | } 435 | 436 | #[test] 437 | fn test_ins_lfdx() { 438 | assert_asm!(0x7C0404AE, "lfdx f0, r4, r0"); 439 | } 440 | 441 | #[test] 442 | fn test_ins_lfs() { 443 | assert_asm!(0xC001027C, "lfs f0, 0x27c(r1)"); 444 | assert_asm!(0xC0028000, "lfs f0, -0x8000(r2)"); 445 | } 446 | 447 | #[test] 448 | fn test_ins_lfsu() { 449 | assert_asm!(0xC404FFF4, "lfsu f0, -0xc(r4)"); 450 | assert_asm!(0xC4170084, "lfsu f0, 0x84(r23)"); 451 | } 452 | 453 | #[test] 454 | fn test_ins_lfsux() { 455 | assert_asm!(0x7C03846E, "lfsux f0, r3, r16"); 456 | } 457 | 458 | #[test] 459 | fn test_ins_lfsx() { 460 | assert_asm!(0x7C03042E, "lfsx f0, r3, r0"); 461 | } 462 | 463 | #[test] 464 | fn test_ins_lha() { 465 | assert_asm!(0xA861000E, "lha r3, 0xe(r1)"); 466 | assert_asm!(0xA80D9F64, "lha r0, -0x609c(r13)"); 467 | } 468 | 469 | #[test] 470 | fn test_ins_lhau() { 471 | assert_asm!(0xAC060006, "lhau r0, 0x6(r6)"); 472 | assert_asm!(0xAC06FFFA, "lhau r0, -0x6(r6)"); 473 | } 474 | 475 | #[test] 476 | fn test_ins_lhax() { 477 | assert_asm!(0x7C0402AE, "lhax r0, r4, r0"); 478 | } 479 | 480 | #[test] 481 | fn test_ins_lhz() { 482 | assert_asm!(0xA00104D6, "lhz r0, 0x4d6(r1)"); 483 | assert_asm!(0xA00296DA, "lhz r0, -0x6926(r2)"); 484 | } 485 | 486 | #[test] 487 | fn test_ins_lhzu() { 488 | assert_asm!(0xA40A0004, "lhzu r0, 0x4(r10)"); 489 | } 490 | 491 | #[test] 492 | fn test_ins_lhzux() { 493 | assert_asm!(0x7C04026E, "lhzux r0, r4, r0"); 494 | } 495 | 496 | #[test] 497 | fn test_ins_lhzx() { 498 | assert_asm!(0x7C03022E, "lhzx r0, r3, r0"); 499 | } 500 | 501 | #[test] 502 | fn test_ins_lmw() { 503 | assert_asm!(0xBB210444, "lmw r25, 0x444(r1)"); 504 | } 505 | 506 | #[test] 507 | fn test_ins_lwbrx() { 508 | assert_asm!(0x7D80242C, "lwbrx r12, r0, r4"); 509 | } 510 | 511 | #[test] 512 | fn test_ins_lwz() { 513 | assert_asm!(0x800294F4, "lwz r0, -0x6b0c(r2)"); 514 | assert_asm!(0x80011254, "lwz r0, 0x1254(r1)"); 515 | } 516 | 517 | #[test] 518 | fn test_ins_lwzu() { 519 | assert_asm!(0x84038608, "lwzu r0, -0x79f8(r3)"); 520 | assert_asm!(0x873E5058, "lwzu r25, 0x5058(r30)"); 521 | } 522 | 523 | #[test] 524 | fn test_ins_lwzux() { 525 | assert_asm!(0x7C03006E, "lwzux r0, r3, r0"); 526 | } 527 | 528 | #[test] 529 | fn test_ins_lwzx() { 530 | assert_asm!(0x7C03002E, "lwzx r0, r3, r0"); 531 | } 532 | 533 | #[test] 534 | fn test_ins_mcrf() { 535 | assert_asm!(0x4E1C0000, "mcrf cr4, cr7"); 536 | } 537 | 538 | #[test] 539 | fn test_ins_mcrfs() { 540 | assert_asm!(0xFE1C0080, "mcrfs cr4, cr7"); 541 | } 542 | 543 | #[test] 544 | fn test_ins_mcrxr() { 545 | assert_asm!(0x7F800400, "mcrxr cr7"); 546 | } 547 | 548 | #[test] 549 | fn test_ins_mfcr() { 550 | assert_asm!(0x7C000026, "mfcr r0"); 551 | } 552 | 553 | #[test] 554 | fn test_ins_mffs() { 555 | assert_asm!(0xFC00048E, "mffs f0"); 556 | } 557 | 558 | #[test] 559 | fn test_ins_mfmsr() { 560 | assert_asm!(0x7C0000A6, "mfmsr r0"); 561 | } 562 | 563 | #[test] 564 | fn test_ins_mfspr() { 565 | assert_asm!(0x7E1A02A6, "mfsrr0 r16"); 566 | assert_asm!(0x7C70FAA6, "mfspr r3, HID0"); 567 | assert_asm!(0x7C7482A6, "mfibatu r3, 2"); 568 | assert_asm!(0x7C7782A6, "mfibatl r3, 3"); 569 | } 570 | 571 | #[test] 572 | fn test_ins_mfsr() { 573 | assert_asm!(0x7E0004A6, "mfsr r16, 0"); 574 | } 575 | 576 | #[test] 577 | fn test_ins_mftb() { 578 | assert_asm!(0x7C8C42E6, "mftb r4, 268"); 579 | } 580 | 581 | #[test] 582 | fn test_ins_mtcrf() { 583 | assert_asm!(0x7C6FF120, "mtcrf 255, r3"); 584 | } 585 | 586 | #[test] 587 | fn test_ins_mtfsb0() { 588 | assert_asm!(0xFFA0008C, "mtfsb0 4*cr7+gt") 589 | } 590 | 591 | #[test] 592 | fn test_ins_mtfsb1() { 593 | assert_asm!(0xFFA0004C, "mtfsb1 4*cr7+gt"); 594 | } 595 | 596 | #[test] 597 | fn test_ins_mtfsf() { 598 | assert_asm!(0xFDFE058E, "mtfsf 255, f0"); 599 | assert_asm!(0xFDFEFD8E, "mtfsf 255, f31"); 600 | } 601 | 602 | #[test] 603 | fn test_ins_mtmsr() { 604 | assert_asm!(0x7C000124, "mtmsr r0"); 605 | } 606 | 607 | #[test] 608 | fn test_ins_mtspr() { 609 | assert_asm!(0x7E75FBA6, "mtspr DABR, r19"); 610 | assert_asm!(0x7C70FBA6, "mtspr HID0, r3"); 611 | assert_asm!(0x7C7603A6, "mtdec r3"); 612 | assert_asm!(0x7C7043A6, "mtsprg 0, r3"); 613 | assert_asm!(0x7C7143A6, "mtsprg 1, r3"); 614 | assert_asm!(0x7C7343A6, "mtsprg 3, r3"); 615 | assert_asm!(0x7C7083A6, "mtibatu 0, r3"); 616 | assert_asm!(0x7C7483A6, "mtibatu 2, r3"); 617 | assert_asm!(0x7C7783A6, "mtibatl 3, r3"); 618 | assert_asm!(0x7C7D83A6, "mtdbatl 2, r3"); 619 | } 620 | 621 | #[test] 622 | fn test_ins_mtsr() { 623 | assert_asm!(0x7E0001A4, "mtsr 0, r16"); 624 | } 625 | 626 | #[test] 627 | fn test_ins_mulhw() { 628 | assert_asm!(0x7C7F2096, "mulhw r3, r31, r4"); 629 | } 630 | 631 | #[test] 632 | fn test_ins_mulhwu() { 633 | assert_asm!(0x7C7D0016, "mulhwu r3, r29, r0"); 634 | } 635 | 636 | #[test] 637 | fn test_ins_mulli() { 638 | assert_asm!(0x1C001880, "mulli r0, r0, 0x1880"); 639 | assert_asm!(0x1FBD0030, "mulli r29, r29, 0x30"); 640 | } 641 | 642 | #[test] 643 | fn test_ins_mullw() { 644 | assert_asm!(0x7C7D01D6, "mullw r3, r29, r0"); 645 | } 646 | 647 | #[test] 648 | fn test_ins_nand() { 649 | assert_asm!(0x7C7D03B8, "nand r29, r3, r0"); 650 | } 651 | 652 | #[test] 653 | fn test_ins_neg() { 654 | assert_asm!(0x7C0600D0, "neg r0, r6"); 655 | } 656 | 657 | #[test] 658 | fn test_ins_nor() { 659 | assert_asm!(0x7C0500F8, "nor r5, r0, r0"); 660 | } 661 | 662 | #[test] 663 | fn test_ins_or() { 664 | assert_asm!(0x7C04DB78, "or r4, r0, r27"); 665 | } 666 | 667 | #[test] 668 | fn test_ins_orc() { 669 | assert_asm!(0x7C042338, "orc r4, r0, r4"); 670 | } 671 | 672 | #[test] 673 | fn test_ins_ori() { 674 | assert_asm!(0x60002204, "ori r0, r0, 0x2204"); 675 | } 676 | 677 | #[test] 678 | fn test_ins_oris() { 679 | assert_asm!(0x67A06800, "oris r0, r29, 0x6800"); 680 | } 681 | 682 | #[test] 683 | fn test_ins_psq_l() { 684 | assert_asm!(0xE02500AC, "psq_l f1, 0xac(r5), 0, qr0"); 685 | } 686 | 687 | #[test] 688 | fn test_ins_psq_lu() { 689 | assert_asm!(0xE5435010, "psq_lu f10, 0x10(r3), 0, qr5"); 690 | } 691 | 692 | #[test] 693 | fn test_ins_psq_lx() { 694 | let ins = Ins::new(0x1000000C, 0x8000_0000u32); 695 | assert_eq!(ins.op, PsqLx); 696 | assert_eq!( 697 | ins.fields(), 698 | vec![ 699 | frD(FPR(0)), 700 | rA(GPR(0)), 701 | rB(GPR(0)), 702 | ps_WX(OpaqueU(0)), 703 | ps_IX(GQR(0)), 704 | ] 705 | ); 706 | assert_eq!(ins.defs(), vec![frD(FPR(0))]); 707 | assert_eq!(ins.uses(), vec![rB(GPR(0))]); 708 | 709 | assert_asm!(0x1000000C, "psq_lx f0, r0, r0, 0, qr0"); 710 | } 711 | 712 | #[test] 713 | fn test_ins_psq_st() { 714 | assert_asm!(0xF1230210, "psq_st f9, 0x210(r3), 0, qr0"); 715 | assert_asm!(0xF1238008, "psq_st f9, 0x8(r3), 1, qr0"); 716 | } 717 | 718 | #[test] 719 | fn test_ins_psq_stu() { 720 | assert_asm!(0xF40A0020, "psq_stu f0, 0x20(r10), 0, qr0"); 721 | } 722 | 723 | #[test] 724 | fn test_ins_psq_stx() { 725 | assert_asm!(0x13E1000E, "psq_stx f31, r1, r0, 0, qr0"); 726 | } 727 | 728 | #[test] 729 | fn test_ins_ps_abs() { 730 | assert_asm!(0x10A03210, "ps_abs f5, f6"); 731 | } 732 | 733 | #[test] 734 | fn test_ins_ps_add() { 735 | assert_asm!(0x1006382A, "ps_add f0, f6, f7"); 736 | } 737 | 738 | #[test] 739 | fn test_ins_ps_cmpo0() { 740 | assert_asm!(0x10070840, "ps_cmpo0 cr0, f7, f1"); 741 | } 742 | 743 | #[test] 744 | fn test_ins_ps_cmpu0() { 745 | assert_asm!(0x10003000, "ps_cmpu0 cr0, f0, f6"); 746 | } 747 | 748 | #[test] 749 | fn test_ins_ps_cmpu1() { 750 | assert_asm!(0x10003080, "ps_cmpu1 cr0, f0, f6"); 751 | } 752 | 753 | #[test] 754 | fn test_ins_ps_madd() { 755 | assert_asm!(0x112141FA, "ps_madd f9, f1, f7, f8"); 756 | } 757 | 758 | #[test] 759 | fn test_ins_ps_madds0() { 760 | assert_asm!(0x10AC299C, "ps_madds0 f5, f12, f6, f5"); 761 | } 762 | 763 | #[test] 764 | fn test_ins_ps_madds1() { 765 | assert_asm!(0x110640DE, "ps_madds1 f8, f6, f3, f8"); 766 | } 767 | 768 | #[test] 769 | fn test_ins_ps_merge00() { 770 | assert_asm!(0x10400420, "ps_merge00 f2, f0, f0"); 771 | } 772 | 773 | #[test] 774 | fn test_ins_ps_merge01() { 775 | assert_asm!(0x10400C60, "ps_merge01 f2, f0, f1"); 776 | } 777 | 778 | #[test] 779 | fn test_ins_ps_merge10() { 780 | assert_asm!(0x104004A0, "ps_merge10 f2, f0, f0"); 781 | } 782 | 783 | #[test] 784 | fn test_ins_ps_merge11() { 785 | assert_asm!(0x10AA14E0, "ps_merge11 f5, f10, f2"); 786 | } 787 | 788 | #[test] 789 | fn test_ins_ps_mr() { 790 | assert_asm!(0x10200090, "ps_mr f1, f0"); 791 | } 792 | 793 | #[test] 794 | fn test_ins_ps_msub() { 795 | assert_asm!(0x10A53778, "ps_msub f5, f5, f29, f6"); 796 | } 797 | 798 | #[test] 799 | fn test_ins_ps_mul() { 800 | assert_asm!(0x10000032, "ps_mul f0, f0, f0"); 801 | } 802 | 803 | #[test] 804 | fn test_ins_ps_muls0() { 805 | assert_asm!(0x100002D8, "ps_muls0 f0, f0, f11"); 806 | } 807 | 808 | #[test] 809 | fn test_ins_ps_muls1() { 810 | assert_asm!(0x10A2005A, "ps_muls1 f5, f2, f1"); 811 | } 812 | 813 | #[test] 814 | fn test_ins_ps_nabs() { 815 | assert_asm!(0x10803210, "ps_abs f4, f6"); 816 | } 817 | 818 | #[test] 819 | fn test_ins_ps_neg() { 820 | assert_asm!(0x10E03850, "ps_neg f7, f7"); 821 | } 822 | 823 | #[test] 824 | fn test_ins_ps_nmadd() { 825 | assert_asm!(0x10CB30FE, "ps_nmadd f6, f11, f3, f6"); 826 | } 827 | 828 | #[test] 829 | fn test_ins_ps_nmsub() { 830 | assert_asm!(0x107E083C, "ps_nmsub f3, f30, f0, f1"); 831 | } 832 | 833 | #[test] 834 | fn test_ins_ps_sel() { 835 | assert_asm!(0x106428EE, "ps_sel f3, f4, f3, f5"); 836 | } 837 | 838 | #[test] 839 | fn test_ins_ps_sub() { 840 | assert_asm!(0x10A92828, "ps_sub f5, f9, f5"); 841 | } 842 | 843 | #[test] 844 | fn test_ins_ps_sum0() { 845 | assert_asm!(0x10230854, "ps_sum0 f1, f3, f1, f1"); 846 | } 847 | 848 | #[test] 849 | fn test_ins_ps_sum1() { 850 | assert_asm!(0x10A12956, "ps_sum1 f5, f1, f5, f5"); 851 | } 852 | 853 | #[test] 854 | fn test_ins_rfi() { 855 | assert_asm!(0x4C000064, "rfi"); 856 | } 857 | 858 | #[test] 859 | fn test_ins_rlwimi() { 860 | assert_asm!(0x500306FE, "rlwimi r3, r0, 0, 27, 31"); 861 | assert_asm!(0x50032D74, "rlwimi r3, r0, 5, 21, 26"); 862 | assert_asm!(0x5400003F, "clrrwi. r0, r0, 0"); 863 | } 864 | 865 | #[test] 866 | fn test_ins_rlwinm() { 867 | assert_asm!(0x54000423, "rlwinm. r0, r0, 0, 16, 17"); 868 | assert_asm!(0x54000432, "rlwinm r0, r0, 0, 16, 25"); 869 | 870 | // mnemonics 871 | assert_asm!(0x57E5103A, "slwi r5, r31, 2"); 872 | assert_asm!(0x54832026, "extlwi r3, r4, 20, 4"); 873 | assert_asm!(0x5483AB3E, "extrwi r3, r4, 20, 1"); 874 | assert_asm!(0x540027BE, "extrwi r0, r0, 2, 2"); 875 | assert_asm!(0x54839B3E, "rlwinm r3, r4, 19, 12, 31"); 876 | assert_asm!(0x5483203E, "rotlwi r3, r4, 4"); 877 | assert_asm!(0x5483E03E, "rotrwi r3, r4, 4"); 878 | assert_asm!(0x5464043E, "clrlwi r4, r3, 16"); 879 | assert_asm!(0x54830036, "clrrwi r3, r4, 4"); 880 | assert_asm!(0x54640FBC, "clrlslwi r4, r3, 31, 1"); 881 | assert_asm!(0x54092DB4, "clrlslwi r9, r0, 27, 5"); 882 | assert_asm!(0x54096226, "clrlslwi r9, r0, 20, 12"); 883 | } 884 | 885 | #[test] 886 | fn test_ins_rlwnm() { 887 | assert_asm!(0x5D6A67FE, "rlwnm r10, r11, r12, 31, 31"); 888 | assert_asm!(0x5FC52EFE, "rlwnm r5, r30, r5, 27, 31"); 889 | assert_asm!(0x5FC5283F, "rotlw. r5, r30, r5"); 890 | } 891 | 892 | #[test] 893 | fn test_ins_sc() { 894 | assert_asm!(0x44000002, "sc"); 895 | } 896 | 897 | #[test] 898 | fn test_ins_slw() { 899 | assert_asm!(0x7C042830, "slw r4, r0, r5"); 900 | } 901 | 902 | #[test] 903 | fn test_ins_sraw() { 904 | assert_asm!(0x7C043E30, "sraw r4, r0, r7"); 905 | } 906 | 907 | #[test] 908 | fn test_ins_srawi() { 909 | assert_asm!(0x7C000E70, "srawi r0, r0, 1"); 910 | assert_asm!(0x7C001670, "srawi r0, r0, 2"); 911 | } 912 | 913 | #[test] 914 | fn test_ins_srw() { 915 | assert_asm!(0x7C001C30, "srw r0, r0, r3"); 916 | assert_asm!(0x7C600430, "srw r0, r3, r0"); 917 | } 918 | 919 | #[test] 920 | fn test_ins_stb() { 921 | assert_asm!(0x980105EC, "stb r0, 0x5ec(r1)"); 922 | assert_asm!(0x98030000, "stb r0, 0x0(r3)"); 923 | } 924 | 925 | #[test] 926 | fn test_ins_stbu() { 927 | assert_asm!(0x9D2A7428, "stbu r9, 0x7428(r10)"); 928 | assert_asm!(0x9D66FFFF, "stbu r11, -0x1(r6)"); 929 | } 930 | 931 | #[test] 932 | fn test_ins_stbux() { 933 | assert_asm!(0x7C08F9EE, "stbux r0, r8, r31"); 934 | } 935 | 936 | #[test] 937 | fn test_ins_stbx() { 938 | assert_asm!(0x7C03F9AE, "stbx r0, r3, r31"); 939 | } 940 | 941 | #[test] 942 | fn test_ins_stfd() { 943 | assert_asm!(0xD80D97B0, "stfd f0, -0x6850(r13)"); 944 | assert_asm!(0xD8050090, "stfd f0, 0x90(r5)"); 945 | } 946 | 947 | #[test] 948 | fn test_ins_stfdu() { 949 | assert_asm!(0xDC24FFC0, "stfdu f1, -0x40(r4)"); 950 | } 951 | 952 | #[test] 953 | fn test_ins_stfdx() { 954 | assert_asm!(0x7C4405AE, "stfdx f2, r4, r0"); 955 | } 956 | 957 | #[test] 958 | fn test_ins_stfs() { 959 | assert_asm!(0xD003086C, "stfs f0, 0x86c(r3)"); 960 | assert_asm!(0xD0038000, "stfs f0, -0x8000(r3)"); 961 | } 962 | 963 | #[test] 964 | fn test_ins_stfsx() { 965 | assert_asm!(0x7C465D2E, "stfsx f2, r6, r11"); 966 | } 967 | 968 | #[test] 969 | fn test_ins_sth() { 970 | assert_asm!(0xB0038A7C, "sth r0, -0x7584(r3)"); 971 | assert_asm!(0xB0035036, "sth r0, 0x5036(r3)"); 972 | } 973 | 974 | #[test] 975 | fn test_ins_sthbrx() { 976 | assert_asm!(0x7C60072C, "sthbrx r3, r0, r0"); 977 | } 978 | 979 | #[test] 980 | fn test_ins_sthu() { 981 | assert_asm!(0xB4055B88, "sthu r0, 0x5b88(r5)"); 982 | } 983 | 984 | #[test] 985 | fn test_ins_sthux() { 986 | assert_asm!(0x7C03236E, "sthux r0, r3, r4"); 987 | } 988 | 989 | #[test] 990 | fn test_ins_sthx() { 991 | assert_asm!(0x7C1C2B2E, "sthx r0, r28, r5"); 992 | } 993 | 994 | #[test] 995 | fn test_ins_stmw() { 996 | assert_asm!(0xBFA202A4, "stmw r29, 0x2a4(r2)"); 997 | } 998 | 999 | #[test] 1000 | fn test_ins_stw() { 1001 | assert_asm!(0x900140CC, "stw r0, 0x40cc(r1)"); 1002 | assert_asm!(0x9003FFBC, "stw r0, -0x44(r3)"); 1003 | } 1004 | 1005 | #[test] 1006 | fn test_ins_stwbrx() { 1007 | assert_asm!(0x7C00FD2C, "stwbrx r0, r0, r31"); 1008 | } 1009 | 1010 | #[test] 1011 | fn test_ins_stwu() { 1012 | assert_asm!(0x9421EBC0, "stwu r1, -0x1440(r1)"); 1013 | } 1014 | 1015 | #[test] 1016 | fn test_ins_stwux() { 1017 | assert_asm!(0x7C01B96E, "stwux r0, r1, r23"); 1018 | } 1019 | 1020 | #[test] 1021 | fn test_ins_stwx() { 1022 | assert_asm!(0x7C03212E, "stwx r0, r3, r4"); 1023 | } 1024 | 1025 | #[test] 1026 | fn test_ins_subf() { 1027 | assert_asm!(0x7C051850, "subf r0, r5, r3"); 1028 | assert_asm!(0x7C051851, "subf. r0, r5, r3"); 1029 | } 1030 | 1031 | #[test] 1032 | fn test_ins_subfc() { 1033 | assert_asm!(0x7C040010, "subfc r0, r4, r0"); 1034 | } 1035 | 1036 | #[test] 1037 | fn test_ins_subfe() { 1038 | assert_asm!(0x7C030110, "subfe r0, r3, r0"); 1039 | } 1040 | 1041 | #[test] 1042 | fn test_ins_subfic() { 1043 | assert_asm!(0x200602FF, "subfic r0, r6, 0x2ff"); 1044 | } 1045 | 1046 | #[test] 1047 | fn test_ins_subfze() { 1048 | assert_asm!(0x7C000190, "subfze r0, r0"); 1049 | } 1050 | 1051 | #[test] 1052 | fn test_ins_sync() { 1053 | assert_asm!(0x7C0004AC, "sync"); 1054 | } 1055 | 1056 | #[test] 1057 | fn test_tlbie() { 1058 | assert_asm!(0x7C001A64, "tlbie r3"); 1059 | } 1060 | 1061 | #[test] 1062 | fn test_tlbsync() { 1063 | assert_asm!(0x7C00046C, "tlbsync"); 1064 | } 1065 | 1066 | #[test] 1067 | fn test_tw() { 1068 | assert_asm!(0x7C063808, "tw 0, r6, r7"); 1069 | assert_asm!(0x7C842808, "tweq r4, r5"); 1070 | assert_asm!(0x7CA42808, "twlge r4, r5"); 1071 | assert_asm!(0x7FE00008, "trap"); 1072 | } 1073 | 1074 | #[test] 1075 | fn test_twi() { 1076 | assert_asm!(0x0C000000, "twi 0, r0, 0x0"); 1077 | assert_asm!(0x0D07FFFF, "twgti r7, -0x1"); 1078 | assert_asm!(0x0CC4FF01, "twllei r4, -0xff"); 1079 | assert_asm!(0x0FE40003, "twui r4, 0x3"); 1080 | } 1081 | 1082 | #[test] 1083 | fn test_ins_xor() { 1084 | assert_asm!(0x7C052A78, "xor r5, r0, r5"); 1085 | assert_asm!(0x7D275279, "xor. r7, r9, r10"); 1086 | } 1087 | 1088 | #[test] 1089 | fn test_ins_xori() { 1090 | assert_asm!(0x68E71021, "xori r7, r7, 0x1021"); 1091 | } 1092 | 1093 | #[test] 1094 | fn test_ins_xoris() { 1095 | assert_asm!(0x6E3D8000, "xoris r29, r17, 0x8000"); 1096 | } 1097 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | --------------------------------------------------------------------------------