mirror of
https://github.com/encounter/ppc750cl.git
synced 2025-10-04 17:29:41 +00:00
Rename crate to powerpc; support extensions
This commit is contained in:
parent
2204612dfb
commit
c75d090d82
29
Cargo.lock
generated
29
Cargo.lock
generated
@ -123,9 +123,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.14.5"
|
version = "0.15.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
@ -135,12 +135,13 @@ checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.5.0"
|
version = "2.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
|
checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -252,33 +253,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppc750cl"
|
name = "powerpc"
|
||||||
version = "0.3.3"
|
version = "0.4.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppc750cl-asm"
|
name = "powerpc-asm"
|
||||||
version = "0.3.3"
|
version = "0.4.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"phf",
|
"phf",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppc750cl-fuzz"
|
name = "powerpc-fuzz"
|
||||||
version = "0.3.3"
|
version = "0.4.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
"ppc750cl",
|
"powerpc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppc750cl-genisa"
|
name = "powerpc-genisa"
|
||||||
version = "0.3.3"
|
version = "0.4.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"indexmap",
|
||||||
"log",
|
"log",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"phf",
|
|
||||||
"phf_codegen",
|
"phf_codegen",
|
||||||
"prettyplease",
|
"prettyplease",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
13
Cargo.toml
13
Cargo.toml
@ -2,18 +2,11 @@
|
|||||||
members = ["asm", "disasm", "fuzz", "genisa"]
|
members = ["asm", "disasm", "fuzz", "genisa"]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
panic = "abort"
|
|
||||||
|
|
||||||
[profile.release-lto]
|
|
||||||
inherits = "release"
|
|
||||||
lto = true
|
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.3.3"
|
version = "0.4.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["Luke Street <luke@street.dev>"]
|
authors = ["Luke Street <luke@street.dev>"]
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
keywords = ["powerpc", "wii", "gamecube", "xbox"]
|
keywords = ["powerpc", "disassembler", "assembler", "ppc"]
|
||||||
repository = "https://github.com/rjkiv/ppc750cl.git"
|
repository = "https://github.com/encounter/powerpc-rs.git"
|
||||||
rust-version = "1.74"
|
rust-version = "1.74"
|
||||||
|
101
README.md
101
README.md
@ -1,47 +1,96 @@
|
|||||||
# ppc750cl [![Build Status]][actions] [![Latest Version]][crates.io] [![Api Rustdoc]][rustdoc] ![Rust Version]
|
# powerpc [![Build Status]][actions] [![Latest Version]][crates.io] [![Api Rustdoc]][rustdoc] ![Rust Version]
|
||||||
|
|
||||||
[Build Status]: https://github.com/encounter/ppc750cl/actions/workflows/test.yml/badge.svg
|
[Build Status]: https://github.com/encounter/powerpc-rs/actions/workflows/test.yml/badge.svg
|
||||||
[actions]: https://github.com/encounter/ppc750cl/actions
|
[actions]: https://github.com/encounter/powerpc-rs/actions
|
||||||
[Latest Version]: https://img.shields.io/crates/v/ppc750cl.svg
|
[Latest Version]: https://img.shields.io/crates/v/powerpc.svg
|
||||||
[crates.io]: https://crates.io/crates/ppc750cl
|
[crates.io]: https://crates.io/crates/powerpc
|
||||||
[Api Rustdoc]: https://img.shields.io/badge/api-rustdoc-blue.svg
|
[Api Rustdoc]: https://img.shields.io/badge/api-rustdoc-blue.svg
|
||||||
[rustdoc]: https://docs.rs/ppc750cl
|
[rustdoc]: https://docs.rs/powerpc
|
||||||
[Rust Version]: https://img.shields.io/badge/rust-1.74+-blue.svg?maxAge=3600
|
[Rust Version]: https://img.shields.io/badge/rust-1.74+-blue.svg?maxAge=3600
|
||||||
|
|
||||||
Rust tools for working with the PowerPC 750CL / 750CXe family of processors.
|
(Previously `ppc750cl`)
|
||||||
|
|
||||||
### Building
|
Rust disassembler and assembler for the PowerPC ISA.
|
||||||
|
|
||||||
```shell
|
### Supported Extensions
|
||||||
cargo run --package ppc750cl-genisa
|
|
||||||
cargo build --release
|
- PowerPC 64-bit
|
||||||
|
- Paired Singles
|
||||||
|
- PowerPC 750CXe "Gekko" (Nintendo GameCube)
|
||||||
|
- PowerPC 750CL "Broadway" (Nintendo Wii)
|
||||||
|
- AltiVec
|
||||||
|
- VMX128
|
||||||
|
- PowerPC "Xenon" (Xbox 360)
|
||||||
|
|
||||||
|
If you need support for other extensions, please open an issue.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
In Cargo.toml:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
powerpc = "0.4" # disassembler
|
||||||
|
powerpc-asm = "0.4" # assembler
|
||||||
```
|
```
|
||||||
|
|
||||||
### Instruction Set
|
Disassembling and printing instructions:
|
||||||
|
|
||||||
For those unfamiliar with PowerPC, here are some basics.
|
```rust
|
||||||
- PowerPC 7xx is a family of RISC CPUs produced from 1997 to 2012.
|
use powerpc::{Argument, Extensions, Ins, Opcode, Simm, GPR};
|
||||||
- They operate with 32-bit words and every instruction is 32-bits wide.
|
|
||||||
- This project focuses (only) on compatibility with the PowerPC 750CL and 750CXe.
|
|
||||||
- PowerPC 750CL ("Broadway") is used in the Nintendo Wii.
|
|
||||||
- PowerPC 750CXe ("Gekko") is used in the Nintendo GameCube.
|
|
||||||
- They add a "paired-singles" SIMD unit and a bunch of other instructions.
|
|
||||||
|
|
||||||
### isa.yaml
|
let ins = Ins::new(0x38A00000, Extensions::none());
|
||||||
|
assert_eq!(ins.op, Opcode::Addi);
|
||||||
|
|
||||||
The file [isa.yaml](./isa.yaml) contains a full definition of the PowerPC 750CL / 750CXe instruction set.
|
// Basic form
|
||||||
|
let parsed = ins.basic();
|
||||||
|
assert_eq!(parsed.args[0], Argument::GPR(GPR(5)));
|
||||||
|
assert_eq!(parsed.args[1], Argument::GPR(GPR(0)));
|
||||||
|
assert_eq!(parsed.args[2], Argument::Simm(Simm(0)));
|
||||||
|
assert_eq!(parsed.to_string(), "addi r5, r0, 0x0");
|
||||||
|
|
||||||
It powers the disassembler and assembler.
|
// Simplified form
|
||||||
|
let parsed = ins.simplified();
|
||||||
|
assert_eq!(parsed.to_string(), "li r5, 0x0");
|
||||||
|
```
|
||||||
|
|
||||||
Similarly to LLVM TableGen, the program `ppc750cl-genisa` generates a Rust file implementing an instruction decoder.
|
Assembling instructions:
|
||||||
|
|
||||||
### Safety & Correctness
|
```rust
|
||||||
|
use powerpc_asm::{assemble, Argument, Arguments};
|
||||||
|
|
||||||
- The disassembler has been fuzzed over all ~4.29 billion possible instructions (via `ppc750cl-fuzz`).
|
let args: Arguments = [
|
||||||
|
Argument::Unsigned(5),
|
||||||
|
Argument::Unsigned(0),
|
||||||
|
Argument::Signed(0),
|
||||||
|
Argument::None,
|
||||||
|
Argument::None,
|
||||||
|
];
|
||||||
|
let code = assemble("addi", &args).expect("Invalid arguments");
|
||||||
|
assert_eq!(code, 0x38A00000); // addi r5, r0, 0x0
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
```
|
||||||
|
cargo run --package powerpc-genisa
|
||||||
|
cargo test
|
||||||
|
```
|
||||||
|
|
||||||
|
## isa.yaml
|
||||||
|
|
||||||
|
The file [isa.yaml](./isa.yaml) contains a definition of the PowerPC instruction set.
|
||||||
|
|
||||||
|
Similarly to LLVM TableGen, the program `powerpc-genisa` generates Rust files implementing core functionality
|
||||||
|
for the disassembler and assembler.
|
||||||
|
|
||||||
|
## Safety & Correctness
|
||||||
|
|
||||||
|
- The disassembler has been fuzzed over all ~4.29 billion possible instructions (via `powerpc-fuzz`).
|
||||||
- It is safe to run the disassembler over untrusted byte arrays.
|
- It is safe to run the disassembler over untrusted byte arrays.
|
||||||
- However, no guarantees on correctness are made (yet). Expect bugs.
|
- However, no guarantees on correctness are made (yet). Expect bugs.
|
||||||
|
|
||||||
### Performance
|
## Performance
|
||||||
|
|
||||||
With a single thread on Ryzen 9 3900X:
|
With a single thread on Ryzen 9 3900X:
|
||||||
|
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ppc750cl-asm"
|
name = "powerpc-asm"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
description = "Assembler for PowerPC 750CL"
|
description = "PowerPC assembler"
|
||||||
readme = "../README.md"
|
readme = "../README.md"
|
||||||
keywords.workspace = true
|
keywords.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
documentation = "https://docs.rs/ppc750cl-asm"
|
documentation = "https://docs.rs/powerpc-asm"
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
3230
asm/src/generated.rs
3230
asm/src/generated.rs
File diff suppressed because it is too large
Load Diff
@ -1,24 +1,24 @@
|
|||||||
use ppc750cl_asm::*;
|
use powerpc_asm::*;
|
||||||
use Argument::{None, Signed as S, Unsigned as U};
|
use Argument::{None as N, Signed as S, Unsigned as U};
|
||||||
|
|
||||||
macro_rules! assert_asm {
|
macro_rules! assert_asm {
|
||||||
($mnemonic:literal, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $arg5: expr, $code:literal) => {{
|
($mnemonic:literal, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $arg5: expr, $code:literal) => {{
|
||||||
assert_eq!(assemble($mnemonic, &[$arg1, $arg2, $arg3, $arg4, $arg5]).unwrap(), $code)
|
assert_eq!(assemble($mnemonic, &[$arg1, $arg2, $arg3, $arg4, $arg5]).unwrap(), $code)
|
||||||
}};
|
}};
|
||||||
($mnemonic:literal, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $code:literal) => {{
|
($mnemonic:literal, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $code:literal) => {{
|
||||||
assert_eq!(assemble($mnemonic, &[$arg1, $arg2, $arg3, $arg4, None]).unwrap(), $code)
|
assert_eq!(assemble($mnemonic, &[$arg1, $arg2, $arg3, $arg4, N]).unwrap(), $code)
|
||||||
}};
|
}};
|
||||||
($mnemonic:literal, $arg1:expr, $arg2:expr, $arg3:expr, $code:literal) => {{
|
($mnemonic:literal, $arg1:expr, $arg2:expr, $arg3:expr, $code:literal) => {{
|
||||||
assert_eq!(assemble($mnemonic, &[$arg1, $arg2, $arg3, None, None]).unwrap(), $code)
|
assert_eq!(assemble($mnemonic, &[$arg1, $arg2, $arg3, N, N]).unwrap(), $code)
|
||||||
}};
|
}};
|
||||||
($mnemonic:literal, $arg1:expr, $arg2:expr, $code:literal) => {{
|
($mnemonic:literal, $arg1:expr, $arg2:expr, $code:literal) => {{
|
||||||
assert_eq!(assemble($mnemonic, &[$arg1, $arg2, None, None, None]).unwrap(), $code)
|
assert_eq!(assemble($mnemonic, &[$arg1, $arg2, N, N, N]).unwrap(), $code)
|
||||||
}};
|
}};
|
||||||
($mnemonic:literal, $arg1:expr, $code:literal) => {{
|
($mnemonic:literal, $arg1:expr, $code:literal) => {{
|
||||||
assert_eq!(assemble($mnemonic, &[$arg1, None, None, None, None]).unwrap(), $code)
|
assert_eq!(assemble($mnemonic, &[$arg1, N, N, N, N]).unwrap(), $code)
|
||||||
}};
|
}};
|
||||||
($mnemonic:literal, $code:literal) => {{
|
($mnemonic:literal, $code:literal) => {{
|
||||||
assert_eq!(assemble($mnemonic, &[None, None, None, None, None]).unwrap(), $code)
|
assert_eq!(assemble($mnemonic, &[N, N, N, N, N]).unwrap(), $code)
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ppc750cl"
|
name = "powerpc"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
description = "Disassembler for PowerPC 750CL"
|
description = "PowerPC disassembler"
|
||||||
readme = "../README.md"
|
readme = "../README.md"
|
||||||
keywords.workspace = true
|
keywords.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
documentation = "https://docs.rs/ppc750cl"
|
documentation = "https://docs.rs/powerpc"
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
use core::{
|
|
||||||
fmt,
|
|
||||||
fmt::{Display, Formatter, LowerHex},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::generated::{
|
use crate::generated::{
|
||||||
parse_basic, parse_defs, parse_simplified, parse_uses, Arguments, Opcode, EMPTY_ARGS,
|
parse_basic, parse_defs, parse_simplified, parse_uses, Arguments, Extension, Opcode, EMPTY_ARGS,
|
||||||
|
};
|
||||||
|
use core::{
|
||||||
|
fmt::{self, Display, Formatter, LowerHex},
|
||||||
|
hash::{Hash, Hasher},
|
||||||
|
ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Not},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A PowerPC 750CL instruction.
|
/// A PowerPC instruction.
|
||||||
#[derive(Default, Copy, Clone, Debug, Eq, PartialEq)]
|
#[derive(Default, Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct Ins {
|
pub struct Ins {
|
||||||
pub code: u32,
|
pub code: u32,
|
||||||
@ -16,8 +16,9 @@ pub struct Ins {
|
|||||||
|
|
||||||
impl Ins {
|
impl Ins {
|
||||||
/// Create a new instruction from its raw code.
|
/// Create a new instruction from its raw code.
|
||||||
pub fn new(code: u32) -> Self {
|
#[inline]
|
||||||
Self { code, op: Opcode::detect(code) }
|
pub fn new(code: u32, extensions: Extensions) -> Self {
|
||||||
|
Self { code, op: Opcode::detect(code, extensions) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse the instruction into a simplified mnemonic, if any match.
|
/// Parse the instruction into a simplified mnemonic, if any match.
|
||||||
@ -129,6 +130,22 @@ impl Ins {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Hash for Ins {
|
||||||
|
#[inline]
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.code.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for Opcode {
|
||||||
|
/// Opcode enum discriminants are not stable.
|
||||||
|
/// Instead, hash the mnemonic string.
|
||||||
|
#[inline]
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.mnemonic().hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! field_arg_no_display {
|
macro_rules! field_arg_no_display {
|
||||||
($name:ident, $typ:ident) => {
|
($name:ident, $typ:ident) => {
|
||||||
#[derive(Debug, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
|
||||||
@ -336,7 +353,7 @@ impl Display for Argument {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A parsed PowerPC 750CL instruction.
|
/// A parsed PowerPC instruction.
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct ParsedIns {
|
pub struct ParsedIns {
|
||||||
pub mnemonic: &'static str,
|
pub mnemonic: &'static str,
|
||||||
@ -344,6 +361,7 @@ pub struct ParsedIns {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ParsedIns {
|
impl Default for ParsedIns {
|
||||||
|
#[inline]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
@ -351,6 +369,7 @@ impl Default for ParsedIns {
|
|||||||
|
|
||||||
impl ParsedIns {
|
impl ParsedIns {
|
||||||
/// An empty parsed instruction.
|
/// An empty parsed instruction.
|
||||||
|
#[inline]
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self { mnemonic: "<illegal>", args: EMPTY_ARGS }
|
Self { mnemonic: "<illegal>", args: EMPTY_ARGS }
|
||||||
}
|
}
|
||||||
@ -412,21 +431,30 @@ impl LowerHex for SignedHexLiteral<i32> {
|
|||||||
|
|
||||||
pub struct InsIter<'a> {
|
pub struct InsIter<'a> {
|
||||||
address: u32,
|
address: u32,
|
||||||
|
extensions: Extensions,
|
||||||
data: &'a [u8],
|
data: &'a [u8],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> InsIter<'a> {
|
impl<'a> InsIter<'a> {
|
||||||
pub fn new(data: &'a [u8], address: u32) -> Self {
|
#[inline]
|
||||||
Self { address, data }
|
pub fn new(data: &'a [u8], address: u32, extensions: Extensions) -> Self {
|
||||||
|
Self { address, extensions, data }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn address(&self) -> u32 {
|
pub fn address(&self) -> u32 {
|
||||||
self.address
|
self.address
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn data(&self) -> &'a [u8] {
|
pub fn data(&self) -> &'a [u8] {
|
||||||
self.data
|
self.data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn extensions(&self) -> Extensions {
|
||||||
|
self.extensions
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Iterator for InsIter<'_> {
|
impl Iterator for InsIter<'_> {
|
||||||
@ -439,10 +467,194 @@ impl Iterator for InsIter<'_> {
|
|||||||
|
|
||||||
// SAFETY: The slice is guaranteed to be at least 4 bytes long.
|
// SAFETY: The slice is guaranteed to be at least 4 bytes long.
|
||||||
let chunk = unsafe { *(self.data.as_ptr() as *const [u8; 4]) };
|
let chunk = unsafe { *(self.data.as_ptr() as *const [u8; 4]) };
|
||||||
let ins = Ins::new(u32::from_be_bytes(chunk));
|
let ins = Ins::new(u32::from_be_bytes(chunk), self.extensions);
|
||||||
let addr = self.address;
|
let addr = self.address;
|
||||||
self.address += 4;
|
self.address += 4;
|
||||||
self.data = &self.data[4..];
|
self.data = &self.data[4..];
|
||||||
Some((addr, ins))
|
Some((addr, ins))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct Extensions(u32);
|
||||||
|
|
||||||
|
impl Extensions {
|
||||||
|
/// Creates an empty set of extensions.
|
||||||
|
#[inline]
|
||||||
|
pub const fn none() -> Self {
|
||||||
|
Self(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The set of extensions used by the PowerPC 750CXe (Gekko) / 750CL (Broadway) CPUs
|
||||||
|
/// used in the GameCube and Wii respectively.
|
||||||
|
#[inline]
|
||||||
|
pub const fn gekko_broadway() -> Self {
|
||||||
|
Self::from_bitmask(Extension::PairedSingles.bitmask())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The set of extensions used by the PowerPC Xenon CPU used in the Xbox 360.
|
||||||
|
#[inline]
|
||||||
|
pub const fn xenon() -> Self {
|
||||||
|
Self::from_bitmask(
|
||||||
|
Extension::Ppc64.bitmask() | Extension::AltiVec.bitmask() | Extension::Vmx128.bitmask(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the given extension (and all required extensions) are enabled.
|
||||||
|
#[inline]
|
||||||
|
pub const fn contains(&self, ext: Extension) -> bool {
|
||||||
|
let bitmask = ext.bitmask();
|
||||||
|
(self.0 & bitmask) == bitmask
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the given set of extensions are enabled.
|
||||||
|
#[inline]
|
||||||
|
pub const fn contains_all(&self, other: Extensions) -> bool {
|
||||||
|
(self.0 & other.0) == other.0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enables the given extension. Implicitly enables all required extensions.
|
||||||
|
#[inline]
|
||||||
|
pub const fn insert(&mut self, ext: Extension) {
|
||||||
|
self.0 |= ext.bitmask();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disables the given extension.
|
||||||
|
#[inline]
|
||||||
|
pub const fn remove(&mut self, ext: Extension) {
|
||||||
|
// Instead of using bitmask, which includes required extensions,
|
||||||
|
// we only clear the bit for the specific extension.
|
||||||
|
self.0 &= !(1 << (ext as u32));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enables or disables the given extension.
|
||||||
|
#[inline]
|
||||||
|
pub const fn set(&mut self, ext: Extension, value: bool) {
|
||||||
|
if value {
|
||||||
|
self.insert(ext);
|
||||||
|
} else {
|
||||||
|
self.remove(ext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether the extensions set is empty.
|
||||||
|
#[inline]
|
||||||
|
pub const fn is_empty(&self) -> bool {
|
||||||
|
self.0 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the raw bitmask of the extensions.
|
||||||
|
#[inline]
|
||||||
|
pub const fn bitmask(&self) -> u32 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a set of extensions from a raw bitmask.
|
||||||
|
#[inline]
|
||||||
|
pub const fn from_bitmask(bitmask: u32) -> Self {
|
||||||
|
Self(bitmask)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a set of extensions from a single extension (and its required extensions).
|
||||||
|
#[inline]
|
||||||
|
pub const fn from_extension(ext: Extension) -> Self {
|
||||||
|
Self(ext.bitmask())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Extensions {
|
||||||
|
#[inline]
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::none()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Extension> for Extensions {
|
||||||
|
#[inline]
|
||||||
|
fn from(ext: Extension) -> Self {
|
||||||
|
Self::from_extension(ext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitOr for Extensions {
|
||||||
|
type Output = Extensions;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn bitor(self, rhs: Self) -> Self::Output {
|
||||||
|
Self(self.0 | rhs.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitOr<Extension> for Extensions {
|
||||||
|
type Output = Extensions;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn bitor(self, rhs: Extension) -> Self::Output {
|
||||||
|
Self(self.0 | rhs.bitmask())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitOrAssign<Extension> for Extensions {
|
||||||
|
#[inline]
|
||||||
|
fn bitor_assign(&mut self, rhs: Extension) {
|
||||||
|
self.0 |= rhs.bitmask();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitAnd for Extensions {
|
||||||
|
type Output = Extensions;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn bitand(self, rhs: Self) -> Self::Output {
|
||||||
|
Self(self.0 & rhs.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitAnd<Extension> for Extensions {
|
||||||
|
type Output = Extensions;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn bitand(self, rhs: Extension) -> Self::Output {
|
||||||
|
Self(self.0 & rhs.bitmask())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitAndAssign<Extension> for Extensions {
|
||||||
|
#[inline]
|
||||||
|
fn bitand_assign(&mut self, rhs: Extension) {
|
||||||
|
self.0 &= rhs.bitmask();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Not for Extensions {
|
||||||
|
type Output = Extensions;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn not(self) -> Self::Output {
|
||||||
|
Self(!self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitOr for Extension {
|
||||||
|
type Output = Extensions;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn bitor(self, rhs: Self) -> Self::Output {
|
||||||
|
Extensions(self.bitmask() | rhs.bitmask())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for Extension {
|
||||||
|
#[inline]
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.bitmask().hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Extension {
|
||||||
|
#[inline]
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str(self.name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
13903
disasm/src/generated.rs
13903
disasm/src/generated.rs
File diff suppressed because it is too large
Load Diff
@ -3,7 +3,7 @@ mod disasm;
|
|||||||
mod generated;
|
mod generated;
|
||||||
|
|
||||||
pub use disasm::{
|
pub use disasm::{
|
||||||
Argument, BranchDest, CRBit, CRField, Ins, InsIter, Offset, OpaqueU, ParsedIns, Simm, Uimm,
|
Argument, BranchDest, CRBit, CRField, Extensions, Ins, InsIter, Offset, OpaqueU, ParsedIns,
|
||||||
FPR, GPR, GQR, SPR, SR,
|
Simm, Uimm, FPR, GPR, GQR, SPR, SR,
|
||||||
};
|
};
|
||||||
pub use generated::{Arguments, Opcode};
|
pub use generated::{Arguments, Extension, Opcode};
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
use ppc750cl::Ins;
|
use powerpc::{Extension, Extensions, Ins};
|
||||||
|
|
||||||
|
const EXTENSIONS: Extensions = Extensions::from_extension(Extension::AltiVec);
|
||||||
|
|
||||||
macro_rules! assert_asm {
|
macro_rules! assert_asm {
|
||||||
($ins:ident, $disasm:literal) => {{
|
($ins:ident, $disasm:literal) => {{
|
||||||
assert_eq!(format!("{}", $ins.simplified()), $disasm)
|
assert_eq!(format!("{}", $ins.simplified()), $disasm)
|
||||||
}};
|
}};
|
||||||
($code:literal, $disasm:literal) => {{
|
($code:literal, $disasm:literal) => {{
|
||||||
let ins = Ins::new($code);
|
let ins = Ins::new($code, EXTENSIONS);
|
||||||
assert_eq!(format!("{}", ins.simplified()), $disasm)
|
assert_eq!(format!("{}", ins.simplified()), $disasm)
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
use ppc750cl::{Argument, Ins, InsIter, Opcode, GPR};
|
use powerpc::{Argument, Extensions, Ins, InsIter, Opcode, GPR};
|
||||||
|
|
||||||
|
const EXTENSIONS: Extensions = Extensions::none();
|
||||||
|
|
||||||
macro_rules! assert_asm {
|
macro_rules! assert_asm {
|
||||||
($ins:ident, $disasm:literal) => {{
|
($ins:ident, $disasm:literal) => {{
|
||||||
assert_eq!(format!("{}", $ins.simplified()), $disasm)
|
assert_eq!(format!("{}", $ins.simplified()), $disasm)
|
||||||
}};
|
}};
|
||||||
($code:literal, $disasm:literal) => {{
|
($code:literal, $disasm:literal) => {{
|
||||||
let ins = Ins::new($code);
|
let ins = Ins::new($code, EXTENSIONS);
|
||||||
assert_eq!(format!("{}", ins.simplified()), $disasm)
|
assert_eq!(format!("{}", ins.simplified()), $disasm)
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
@ -15,7 +17,7 @@ macro_rules! assert_basic {
|
|||||||
assert_eq!(format!("{}", $ins.basic_form()), $disasm)
|
assert_eq!(format!("{}", $ins.basic_form()), $disasm)
|
||||||
}};
|
}};
|
||||||
($code:literal, $disasm:literal) => {{
|
($code:literal, $disasm:literal) => {{
|
||||||
let ins = Ins::new($code);
|
let ins = Ins::new($code, EXTENSIONS);
|
||||||
assert_eq!(format!("{}", ins.basic()), $disasm)
|
assert_eq!(format!("{}", ins.basic()), $disasm)
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
@ -30,7 +32,7 @@ fn test_ins_add() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ins_addc() {
|
fn test_ins_addc() {
|
||||||
let ins = Ins::new(0x7c002014);
|
let ins = Ins::new(0x7c002014, EXTENSIONS);
|
||||||
assert_eq!(ins.op, Opcode::Addc);
|
assert_eq!(ins.op, Opcode::Addc);
|
||||||
// assert_eq!(ins.fields(), vec![rD(GPR(0)), rA(GPR(0)), rB(GPR(4))]);
|
// assert_eq!(ins.fields(), vec![rD(GPR(0)), rA(GPR(0)), rB(GPR(4))]);
|
||||||
assert_asm!(ins, "addc r0, r0, r4");
|
assert_asm!(ins, "addc r0, r0, r4");
|
||||||
@ -42,7 +44,7 @@ fn test_ins_addc() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ins_addi() {
|
fn test_ins_addi() {
|
||||||
let ins = Ins::new(0x38010140);
|
let ins = Ins::new(0x38010140, EXTENSIONS);
|
||||||
assert_eq!(ins.op, Opcode::Addi);
|
assert_eq!(ins.op, Opcode::Addi);
|
||||||
// assert_eq!(
|
// assert_eq!(
|
||||||
// ins.fields(),
|
// ins.fields(),
|
||||||
@ -214,11 +216,6 @@ fn test_ins_cmpli() {
|
|||||||
assert_asm!(0x2884F8F0, "cmplwi cr1, r4, 0xf8f0");
|
assert_asm!(0x2884F8F0, "cmplwi cr1, r4, 0xf8f0");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_cntlzd() {
|
|
||||||
assert_asm!(0x7CA30074, "cntlzd r3, r5");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ins_cntlzw() {
|
fn test_ins_cntlzw() {
|
||||||
assert_asm!(0x7C030034, "cntlzw r3, r0");
|
assert_asm!(0x7C030034, "cntlzw r3, r0");
|
||||||
@ -291,21 +288,6 @@ fn test_ins_dcbz() {
|
|||||||
assert_asm!(0x7C001FEC, "dcbz r0, r3");
|
assert_asm!(0x7C001FEC, "dcbz r0, r3");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_dcbz_l() {
|
|
||||||
assert_asm!(0x10061FEC, "dcbz_l r6, r3");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_divd() {
|
|
||||||
assert_asm!(0x7CA63BD2, "divd r5, r6, r7");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_divdu() {
|
|
||||||
assert_asm!(0x7C839392, "divdu r4, r3, r18");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ins_divw() {
|
fn test_ins_divw() {
|
||||||
assert_asm!(0x7C8073D6, "divw r4, r0, r14");
|
assert_asm!(0x7C8073D6, "divw r4, r0, r14");
|
||||||
@ -328,12 +310,6 @@ fn test_ins_extsh() {
|
|||||||
assert_asm!(0x7C000735, "extsh. r0, r0");
|
assert_asm!(0x7C000735, "extsh. r0, r0");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_extsw() {
|
|
||||||
assert_asm!(0x7CC307B4, "extsw r3, r6");
|
|
||||||
assert_asm!(0x7CC307B5, "extsw. r3, r6");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ins_fabs() {
|
fn test_ins_fabs() {
|
||||||
assert_asm!(0xFC000A10, "fabs f0, f1");
|
assert_asm!(0xFC000A10, "fabs f0, f1");
|
||||||
@ -349,11 +325,6 @@ fn test_ins_fadds() {
|
|||||||
assert_asm!(0xEC41602A, "fadds f2, f1, f12");
|
assert_asm!(0xEC41602A, "fadds f2, f1, f12");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_fcfid() {
|
|
||||||
assert_asm!(0xFC602E9C, "fcfid f3, f5");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ins_fcmpo() {
|
fn test_ins_fcmpo() {
|
||||||
assert_asm!(0xFC00C840, "fcmpo cr0, f0, f25");
|
assert_asm!(0xFC00C840, "fcmpo cr0, f0, f25");
|
||||||
@ -364,16 +335,6 @@ fn test_ins_fcmpu() {
|
|||||||
assert_asm!(0xFC00D000, "fcmpu cr0, f0, f26");
|
assert_asm!(0xFC00D000, "fcmpu cr0, f0, f26");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_fctid() {
|
|
||||||
assert_asm!(0xFC60065C, "fctid f3, f0");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_fctidz() {
|
|
||||||
assert_asm!(0xFC60065E, "fctidz f3, f0");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ins_fctiwz() {
|
fn test_ins_fctiwz() {
|
||||||
assert_asm!(0xFC20001E, "fctiwz f1, f0");
|
assert_asm!(0xFC20001E, "fctiwz f1, f0");
|
||||||
@ -492,33 +453,6 @@ fn test_ins_lbzx() {
|
|||||||
assert_asm!(0x7C0300AE, "lbzx r0, r3, r0");
|
assert_asm!(0x7C0300AE, "lbzx r0, r3, r0");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_ld() {
|
|
||||||
assert_asm!(0xebe10058, "ld r31, 0x58(r1)");
|
|
||||||
assert_asm!(0xe9790010, "ld r11, 0x10(r25)");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_ldarx() {
|
|
||||||
assert_asm!(0x7C6538A8, "ldarx r3, r5, r7");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_ldu() {
|
|
||||||
assert_asm!(0xe97cfff9, "ldu r11, -0x8(r28)");
|
|
||||||
assert_asm!(0xe8deffe9, "ldu r6, -0x18(r30)");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_ldux() {
|
|
||||||
assert_asm!(0x7C60286A, "ldux r3, r0, r5");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_ldx() {
|
|
||||||
assert_asm!(0x7C60282A, "ldx r3, r0, r5");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ins_lfd() {
|
fn test_ins_lfd() {
|
||||||
assert_asm!(0xC80140C8, "lfd f0, 0x40c8(r1)");
|
assert_asm!(0xC80140C8, "lfd f0, 0x40c8(r1)");
|
||||||
@ -600,21 +534,6 @@ fn test_ins_lmw() {
|
|||||||
assert_asm!(0xBB210444, "lmw r25, 0x444(r1)");
|
assert_asm!(0xBB210444, "lmw r25, 0x444(r1)");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_lwa() {
|
|
||||||
assert_asm!(0xe97fffea, "lwa r11, -0x18(r31)");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_lwaux() {
|
|
||||||
assert_asm!(0x7C8532EA, "lwaux r4, r5, r6");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_lwax() {
|
|
||||||
assert_asm!(0x7CA63AAA, "lwax r5, r6, r7");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ins_lwbrx() {
|
fn test_ins_lwbrx() {
|
||||||
assert_asm!(0x7D80242C, "lwbrx r12, r0, r4");
|
assert_asm!(0x7D80242C, "lwbrx r12, r0, r4");
|
||||||
@ -716,12 +635,6 @@ fn test_ins_mtmsr() {
|
|||||||
assert_asm!(0x7C000124, "mtmsr r0");
|
assert_asm!(0x7C000124, "mtmsr r0");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_mtmsrd() {
|
|
||||||
assert_asm!(0x7C000164, "mtmsrd r0, 0");
|
|
||||||
assert_asm!(0x7D210164, "mtmsrd r9, 1");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ins_mtspr() {
|
fn test_ins_mtspr() {
|
||||||
assert_asm!(0x7E75FBA6, "mtspr DABR, r19");
|
assert_asm!(0x7E75FBA6, "mtspr DABR, r19");
|
||||||
@ -741,26 +654,6 @@ fn test_ins_mtsr() {
|
|||||||
assert_asm!(0x7E0001A4, "mtsr 0, r16");
|
assert_asm!(0x7E0001A4, "mtsr 0, r16");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_mtsrd() {
|
|
||||||
assert_asm!(0x7E0000A4, "mtsrd 0, r16");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_mtsrdin() {
|
|
||||||
assert_asm!(0x7C8040E4, "mtsrdin r4, r8");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_mulhd() {
|
|
||||||
assert_asm!(0x7C7CF892, "mulhd r3, r28, r31");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_mulhdu() {
|
|
||||||
assert_asm!(0x7CBCF812, "mulhdu r5, r28, r31");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ins_mulhw() {
|
fn test_ins_mulhw() {
|
||||||
assert_asm!(0x7C7F2096, "mulhw r3, r31, r4");
|
assert_asm!(0x7C7F2096, "mulhw r3, r31, r4");
|
||||||
@ -771,12 +664,6 @@ fn test_ins_mulhwu() {
|
|||||||
assert_asm!(0x7C7D0016, "mulhwu r3, r29, r0");
|
assert_asm!(0x7C7D0016, "mulhwu r3, r29, r0");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_mulld() {
|
|
||||||
assert_asm!(0x7C6419D2, "mulld r3, r4, r3");
|
|
||||||
assert_asm!(0x7d6b49d2, "mulld r11, r11, r9");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ins_mulli() {
|
fn test_ins_mulli() {
|
||||||
assert_asm!(0x1C001880, "mulli r0, r0, 0x1880");
|
assert_asm!(0x1C001880, "mulli r0, r0, 0x1880");
|
||||||
@ -826,230 +713,11 @@ fn test_ins_oris() {
|
|||||||
assert_asm!(0x67A06800, "oris r0, r29, 0x6800");
|
assert_asm!(0x67A06800, "oris r0, r29, 0x6800");
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_ins_psq_l() {
|
|
||||||
// assert_asm!(0xE02500AC, "psq_l f1, 0xac(r5), 0, qr0");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_ins_psq_lu() {
|
|
||||||
// assert_asm!(0xE5435010, "psq_lu f10, 0x10(r3), 0, qr5");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_ins_psq_lx() {
|
|
||||||
// let ins = Ins::new(0x1000000C);
|
|
||||||
// assert_eq!(ins.op, Opcode::PsqLx);
|
|
||||||
// // assert_eq!(
|
|
||||||
// // ins.fields(),
|
|
||||||
// // vec![
|
|
||||||
// // frD(FPR(0)),
|
|
||||||
// // rA(GPR(0)),
|
|
||||||
// // rB(GPR(0)),
|
|
||||||
// // ps_WX(OpaqueU(0)),
|
|
||||||
// // ps_IX(GQR(0)),
|
|
||||||
// // ]
|
|
||||||
// // );
|
|
||||||
// assert_eq!(
|
|
||||||
// ins.defs(),
|
|
||||||
// [Argument::FPR(FPR(0)), Argument::None, Argument::None, Argument::None, Argument::None]
|
|
||||||
// );
|
|
||||||
// assert_eq!(
|
|
||||||
// ins.uses(),
|
|
||||||
// [Argument::None, Argument::GPR(GPR(0)), Argument::None, Argument::None, Argument::None]
|
|
||||||
// );
|
|
||||||
|
|
||||||
// assert_asm!(0x1000000C, "psq_lx f0, r0, r0, 0, qr0");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_ins_psq_st() {
|
|
||||||
// assert_asm!(0xF1230210, "psq_st f9, 0x210(r3), 0, qr0");
|
|
||||||
// assert_asm!(0xF1238008, "psq_st f9, 0x8(r3), 1, qr0");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_ins_psq_stu() {
|
|
||||||
// assert_asm!(0xF40A0020, "psq_stu f0, 0x20(r10), 0, qr0");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_ins_psq_stx() {
|
|
||||||
// assert_asm!(0x13E1000E, "psq_stx f31, r1, r0, 0, qr0");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_ins_ps_abs() {
|
|
||||||
// assert_asm!(0x10A03210, "ps_abs f5, f6");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_ins_ps_add() {
|
|
||||||
// assert_asm!(0x1006382A, "ps_add f0, f6, f7");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_ins_ps_cmpo0() {
|
|
||||||
// assert_asm!(0x10070840, "ps_cmpo0 cr0, f7, f1");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_ins_ps_cmpu0() {
|
|
||||||
// assert_asm!(0x10003000, "ps_cmpu0 cr0, f0, f6");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_ins_ps_cmpu1() {
|
|
||||||
// assert_asm!(0x10003080, "ps_cmpu1 cr0, f0, f6");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_ins_ps_madd() {
|
|
||||||
// assert_asm!(0x112141FA, "ps_madd f9, f1, f7, f8");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_ins_ps_madds0() {
|
|
||||||
// assert_asm!(0x10AC299C, "ps_madds0 f5, f12, f6, f5");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_ins_ps_madds1() {
|
|
||||||
// assert_asm!(0x110640DE, "ps_madds1 f8, f6, f3, f8");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_ins_ps_merge00() {
|
|
||||||
// assert_asm!(0x10400420, "ps_merge00 f2, f0, f0");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_ins_ps_merge01() {
|
|
||||||
// assert_asm!(0x10400C60, "ps_merge01 f2, f0, f1");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_ins_ps_merge10() {
|
|
||||||
// assert_asm!(0x104004A0, "ps_merge10 f2, f0, f0");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_ins_ps_merge11() {
|
|
||||||
// assert_asm!(0x10AA14E0, "ps_merge11 f5, f10, f2");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_ins_ps_mr() {
|
|
||||||
// assert_asm!(0x10200090, "ps_mr f1, f0");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_ins_ps_msub() {
|
|
||||||
// assert_asm!(0x10A53778, "ps_msub f5, f5, f29, f6");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_ins_ps_mul() {
|
|
||||||
// assert_asm!(0x10000032, "ps_mul f0, f0, f0");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_ins_ps_muls0() {
|
|
||||||
// assert_asm!(0x100002D8, "ps_muls0 f0, f0, f11");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_ins_ps_muls1() {
|
|
||||||
// assert_asm!(0x10A2005A, "ps_muls1 f5, f2, f1");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_ins_ps_nabs() {
|
|
||||||
// assert_asm!(0x10803210, "ps_abs f4, f6");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_ins_ps_neg() {
|
|
||||||
// assert_asm!(0x10E03850, "ps_neg f7, f7");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_ins_ps_nmadd() {
|
|
||||||
// assert_asm!(0x10CB30FE, "ps_nmadd f6, f11, f3, f6");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_ins_ps_nmsub() {
|
|
||||||
// assert_asm!(0x107E083C, "ps_nmsub f3, f30, f0, f1");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_ins_ps_sel() {
|
|
||||||
// assert_asm!(0x106428EE, "ps_sel f3, f4, f3, f5");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_ins_ps_sub() {
|
|
||||||
// assert_asm!(0x10A92828, "ps_sub f5, f9, f5");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_ins_ps_sum0() {
|
|
||||||
// assert_asm!(0x10230854, "ps_sum0 f1, f3, f1, f1");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_ins_ps_sum1() {
|
|
||||||
// assert_asm!(0x10A12956, "ps_sum1 f5, f1, f5, f5");
|
|
||||||
// }
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ins_rfi() {
|
fn test_ins_rfi() {
|
||||||
assert_asm!(0x4C000064, "rfi");
|
assert_asm!(0x4C000064, "rfi");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_rfid() {
|
|
||||||
assert_asm!(0x4c000024, "rfid");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_rldcl() {
|
|
||||||
assert_asm!(0x780336D0, "rldcl r3, r0, r6, 27");
|
|
||||||
assert_asm!(0x78033010, "rotld r3, r0, r6");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_rldcr() {
|
|
||||||
assert_asm!(0x78A345D2, "rldcr r3, r5, r8, 23");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_rldic() {
|
|
||||||
assert_asm!(0x78C51928, "rldic r5, r6, 3, 36");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_rldicl() {
|
|
||||||
assert_asm!(0x78c50020, "rldicl r5, r6, 0, 32");
|
|
||||||
assert_asm!(0x7bab07a0, "rldicl r11, r29, 0, 62");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_rldicr() {
|
|
||||||
assert_asm!(0x7883ffe6, "rldicr r3, r4, 63, 63");
|
|
||||||
assert_asm!(0x798c37e4, "rldicr r12, r12, 6, 63");
|
|
||||||
assert_asm!(0x798c07c6, "rldicr r12, r12, 32, 31");
|
|
||||||
assert_asm!(0x798ccfe6, "rldicr r12, r12, 57, 63");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_rldimi() {
|
|
||||||
assert_asm!(0x78a3a04e, "rldimi r3, r5, 52, 1");
|
|
||||||
assert_asm!(0x794b000e, "rldimi r11, r10, 32, 0");
|
|
||||||
assert_asm!(0x780331CC, "rldimi r3, r0, 6, 7");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ins_rlwimi() {
|
fn test_ins_rlwimi() {
|
||||||
assert_asm!(0x500306FE, "rlwimi r3, r0, 0, 27, 31");
|
assert_asm!(0x500306FE, "rlwimi r3, r0, 0, 27, 31");
|
||||||
@ -1090,39 +758,11 @@ fn test_ins_sc() {
|
|||||||
assert_asm!(0x44000002, "sc");
|
assert_asm!(0x44000002, "sc");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_slbia() {
|
|
||||||
assert_asm!(0x7c0003e4, "slbia");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_slbie() {
|
|
||||||
assert_asm!(0x7C002B64, "slbie r5");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_sld() {
|
|
||||||
assert_asm!(0x7d6a5036, "sld r10, r11, r10");
|
|
||||||
assert_asm!(0x7D034836, "sld r3, r8, r9");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ins_slw() {
|
fn test_ins_slw() {
|
||||||
assert_asm!(0x7C042830, "slw r4, r0, r5");
|
assert_asm!(0x7C042830, "slw r4, r0, r5");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_srad() {
|
|
||||||
assert_asm!(0x7d0b5e34, "srad r11, r8, r11");
|
|
||||||
assert_asm!(0x7C033634, "srad r3, r0, r6");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_sradi() {
|
|
||||||
assert_asm!(0x7cc4a674, "sradi r4, r6, 20");
|
|
||||||
assert_asm!(0x7d6b0676, "sradi r11, r11, 32");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ins_sraw() {
|
fn test_ins_sraw() {
|
||||||
assert_asm!(0x7C043E30, "sraw r4, r0, r7");
|
assert_asm!(0x7C043E30, "sraw r4, r0, r7");
|
||||||
@ -1134,14 +774,6 @@ fn test_ins_srawi() {
|
|||||||
assert_asm!(0x7C001670, "srawi r0, r0, 2");
|
assert_asm!(0x7C001670, "srawi r0, r0, 2");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_srd() {
|
|
||||||
assert_asm!(0x7d0a4c36, "srd r10, r8, r9");
|
|
||||||
assert_asm!(0x7d675436, "srd r7, r11, r10");
|
|
||||||
assert_asm!(0x7C001C36, "srd r0, r0, r3");
|
|
||||||
assert_asm!(0x7C600436, "srd r0, r3, r0");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ins_srw() {
|
fn test_ins_srw() {
|
||||||
assert_asm!(0x7C001C30, "srw r0, r0, r3");
|
assert_asm!(0x7C001C30, "srw r0, r0, r3");
|
||||||
@ -1170,35 +802,6 @@ fn test_ins_stbx() {
|
|||||||
assert_asm!(0x7C03F9AE, "stbx r0, r3, r31");
|
assert_asm!(0x7C03F9AE, "stbx r0, r3, r31");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_std() {
|
|
||||||
assert_asm!(0xfbe1fff0, "std r31, -0x10(r1)");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_stdcx() {
|
|
||||||
assert_asm!(0x7CA749AD, "stdcx. r5, r7, r9");
|
|
||||||
assert_asm!(0x7fc0e9ad, "stdcx. r30, r0, r29");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_stdu() {
|
|
||||||
assert_asm!(0xf9690009, "stdu r11, 0x8(r9)");
|
|
||||||
assert_asm!(0xf97ffff9, "stdu r11, -0x8(r31)");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_stdux() {
|
|
||||||
assert_asm!(0x7C03316A, "stdux r0, r3, r6");
|
|
||||||
assert_asm!(0x7d5cc96a, "stdux r10, r28, r25");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_stdx() {
|
|
||||||
assert_asm!(0x7CA7F92A, "stdx r5, r7, r31");
|
|
||||||
assert_asm!(0x7cc3212a, "stdx r6, r3, r4");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ins_stfd() {
|
fn test_ins_stfd() {
|
||||||
assert_asm!(0xD80D97B0, "stfd f0, -0x6850(r13)");
|
assert_asm!(0xD80D97B0, "stfd f0, -0x6850(r13)");
|
||||||
@ -1319,16 +922,6 @@ fn test_ins_sync() {
|
|||||||
assert_asm!(0x7c4004Ac, "ptesync");
|
assert_asm!(0x7c4004Ac, "ptesync");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_td() {
|
|
||||||
assert_asm!(0x7DC30088, "td 14, r3, r0");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ins_tdi() {
|
|
||||||
assert_asm!(0x09830058, "tdi 12, r3, 0x58");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tlbie() {
|
fn test_tlbie() {
|
||||||
assert_asm!(0x7C001A64, "tlbie r3");
|
assert_asm!(0x7C001A64, "tlbie r3");
|
||||||
@ -1373,8 +966,9 @@ fn test_ins_xoris() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ins_iter() {
|
fn test_ins_iter() {
|
||||||
let mut iter = InsIter::new(&[0x7C, 0x43, 0x22, 0x14, 0x7E, 0x1A, 0x02, 0xA6, 0xFF], 0);
|
let mut iter =
|
||||||
assert_eq!(iter.next(), Some((0, Ins::new(0x7C432214))));
|
InsIter::new(&[0x7C, 0x43, 0x22, 0x14, 0x7E, 0x1A, 0x02, 0xA6, 0xFF], 0, EXTENSIONS);
|
||||||
assert_eq!(iter.next(), Some((4, Ins::new(0x7E1A02A6))));
|
assert_eq!(iter.next(), Some((0, Ins::new(0x7C432214, EXTENSIONS))));
|
||||||
|
assert_eq!(iter.next(), Some((4, Ins::new(0x7E1A02A6, EXTENSIONS))));
|
||||||
assert_eq!(iter.next(), None);
|
assert_eq!(iter.next(), None);
|
||||||
}
|
}
|
||||||
|
245
disasm/tests/test_ppc64.rs
Normal file
245
disasm/tests/test_ppc64.rs
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
use powerpc::{Extension, Extensions, Ins};
|
||||||
|
|
||||||
|
const EXTENSIONS: Extensions = Extensions::from_extension(Extension::Ppc64);
|
||||||
|
|
||||||
|
macro_rules! assert_asm {
|
||||||
|
($ins:ident, $disasm:literal) => {{
|
||||||
|
assert_eq!(format!("{}", $ins.simplified()), $disasm)
|
||||||
|
}};
|
||||||
|
($code:literal, $disasm:literal) => {{
|
||||||
|
let ins = Ins::new($code, EXTENSIONS);
|
||||||
|
assert_eq!(format!("{}", ins.simplified()), $disasm)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_cntlzd() {
|
||||||
|
assert_asm!(0x7CA30074, "cntlzd r3, r5");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_divd() {
|
||||||
|
assert_asm!(0x7CA63BD2, "divd r5, r6, r7");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_divdu() {
|
||||||
|
assert_asm!(0x7C839392, "divdu r4, r3, r18");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_extsw() {
|
||||||
|
assert_asm!(0x7CC307B4, "extsw r3, r6");
|
||||||
|
assert_asm!(0x7CC307B5, "extsw. r3, r6");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_fcfid() {
|
||||||
|
assert_asm!(0xFC602E9C, "fcfid f3, f5");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_fctid() {
|
||||||
|
assert_asm!(0xFC60065C, "fctid f3, f0");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_fctidz() {
|
||||||
|
assert_asm!(0xFC60065E, "fctidz f3, f0");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_ld() {
|
||||||
|
assert_asm!(0xebe10058, "ld r31, 0x58(r1)");
|
||||||
|
assert_asm!(0xe9790010, "ld r11, 0x10(r25)");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_ldarx() {
|
||||||
|
assert_asm!(0x7C6538A8, "ldarx r3, r5, r7");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_ldu() {
|
||||||
|
assert_asm!(0xe97cfff9, "ldu r11, -0x8(r28)");
|
||||||
|
assert_asm!(0xe8deffe9, "ldu r6, -0x18(r30)");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_ldux() {
|
||||||
|
assert_asm!(0x7C60286A, "ldux r3, r0, r5");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_ldx() {
|
||||||
|
assert_asm!(0x7C60282A, "ldx r3, r0, r5");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_lwa() {
|
||||||
|
assert_asm!(0xe97fffea, "lwa r11, -0x18(r31)");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_lwaux() {
|
||||||
|
assert_asm!(0x7C8532EA, "lwaux r4, r5, r6");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_lwax() {
|
||||||
|
assert_asm!(0x7CA63AAA, "lwax r5, r6, r7");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_mtmsrd() {
|
||||||
|
assert_asm!(0x7C000164, "mtmsrd r0, 0");
|
||||||
|
assert_asm!(0x7D210164, "mtmsrd r9, 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_mtsrd() {
|
||||||
|
assert_asm!(0x7E0000A4, "mtsrd 0, r16");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_mtsrdin() {
|
||||||
|
assert_asm!(0x7C8040E4, "mtsrdin r4, r8");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_mulhd() {
|
||||||
|
assert_asm!(0x7C7CF892, "mulhd r3, r28, r31");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_mulhdu() {
|
||||||
|
assert_asm!(0x7CBCF812, "mulhdu r5, r28, r31");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_mulld() {
|
||||||
|
assert_asm!(0x7C6419D2, "mulld r3, r4, r3");
|
||||||
|
assert_asm!(0x7d6b49d2, "mulld r11, r11, r9");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_rfid() {
|
||||||
|
assert_asm!(0x4c000024, "rfid");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_rldcl() {
|
||||||
|
assert_asm!(0x780336D0, "rldcl r3, r0, r6, 27");
|
||||||
|
assert_asm!(0x78033010, "rotld r3, r0, r6");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_rldcr() {
|
||||||
|
assert_asm!(0x78A345D2, "rldcr r3, r5, r8, 23");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_rldic() {
|
||||||
|
assert_asm!(0x78C51928, "rldic r5, r6, 3, 36");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_rldicl() {
|
||||||
|
assert_asm!(0x78c50020, "rldicl r5, r6, 0, 32");
|
||||||
|
assert_asm!(0x7bab07a0, "rldicl r11, r29, 0, 62");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_rldicr() {
|
||||||
|
assert_asm!(0x7883ffe6, "rldicr r3, r4, 63, 63");
|
||||||
|
assert_asm!(0x798c37e4, "rldicr r12, r12, 6, 63");
|
||||||
|
assert_asm!(0x798c07c6, "rldicr r12, r12, 32, 31");
|
||||||
|
assert_asm!(0x798ccfe6, "rldicr r12, r12, 57, 63");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_rldimi() {
|
||||||
|
assert_asm!(0x78a3a04e, "rldimi r3, r5, 52, 1");
|
||||||
|
assert_asm!(0x794b000e, "rldimi r11, r10, 32, 0");
|
||||||
|
assert_asm!(0x780331CC, "rldimi r3, r0, 6, 7");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_slbia() {
|
||||||
|
assert_asm!(0x7c0003e4, "slbia");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_slbie() {
|
||||||
|
assert_asm!(0x7C002B64, "slbie r5");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_sld() {
|
||||||
|
assert_asm!(0x7d6a5036, "sld r10, r11, r10");
|
||||||
|
assert_asm!(0x7D034836, "sld r3, r8, r9");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_srad() {
|
||||||
|
assert_asm!(0x7d0b5e34, "srad r11, r8, r11");
|
||||||
|
assert_asm!(0x7C033634, "srad r3, r0, r6");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_sradi() {
|
||||||
|
assert_asm!(0x7cc4a674, "sradi r4, r6, 20");
|
||||||
|
assert_asm!(0x7d6b0676, "sradi r11, r11, 32");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_srd() {
|
||||||
|
assert_asm!(0x7d0a4c36, "srd r10, r8, r9");
|
||||||
|
assert_asm!(0x7d675436, "srd r7, r11, r10");
|
||||||
|
assert_asm!(0x7C001C36, "srd r0, r0, r3");
|
||||||
|
assert_asm!(0x7C600436, "srd r0, r3, r0");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_std() {
|
||||||
|
assert_asm!(0xfbe1fff0, "std r31, -0x10(r1)");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_stdcx() {
|
||||||
|
assert_asm!(0x7CA749AD, "stdcx. r5, r7, r9");
|
||||||
|
assert_asm!(0x7fc0e9ad, "stdcx. r30, r0, r29");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_stdu() {
|
||||||
|
assert_asm!(0xf9690009, "stdu r11, 0x8(r9)");
|
||||||
|
assert_asm!(0xf97ffff9, "stdu r11, -0x8(r31)");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_stdux() {
|
||||||
|
assert_asm!(0x7C03316A, "stdux r0, r3, r6");
|
||||||
|
assert_asm!(0x7d5cc96a, "stdux r10, r28, r25");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_stdx() {
|
||||||
|
assert_asm!(0x7CA7F92A, "stdx r5, r7, r31");
|
||||||
|
assert_asm!(0x7cc3212a, "stdx r6, r3, r4");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_td() {
|
||||||
|
assert_asm!(0x7DC30088, "td 14, r3, r0");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_tdi() {
|
||||||
|
assert_asm!(0x09830058, "tdi 12, r3, 0x58");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_vmx_dcbzl() {
|
||||||
|
assert_asm!(0x7c2327ec, "dcbzl r3, r4");
|
||||||
|
}
|
195
disasm/tests/test_ps.rs
Normal file
195
disasm/tests/test_ps.rs
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
use powerpc::{Argument, Extension, Extensions, Ins, Opcode, FPR, GPR};
|
||||||
|
|
||||||
|
const EXTENSIONS: Extensions = Extensions::from_extension(Extension::PairedSingles);
|
||||||
|
|
||||||
|
macro_rules! assert_asm {
|
||||||
|
($ins:ident, $disasm:literal) => {{
|
||||||
|
assert_eq!(format!("{}", $ins.simplified()), $disasm)
|
||||||
|
}};
|
||||||
|
($code:literal, $disasm:literal) => {{
|
||||||
|
let ins = Ins::new($code, EXTENSIONS);
|
||||||
|
assert_eq!(format!("{}", ins.simplified()), $disasm)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_dcbz_l() {
|
||||||
|
assert_asm!(0x10061FEC, "dcbz_l r6, r3");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_psq_l() {
|
||||||
|
assert_asm!(0xE02500AC, "psq_l f1, 0xac(r5), 0, qr0");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_psq_lu() {
|
||||||
|
assert_asm!(0xE5435010, "psq_lu f10, 0x10(r3), 0, qr5");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_psq_lx() {
|
||||||
|
let ins = Ins::new(0x1000000C, EXTENSIONS);
|
||||||
|
assert_eq!(ins.op, Opcode::PsqLx);
|
||||||
|
// assert_eq!(
|
||||||
|
// ins.fields(),
|
||||||
|
// vec![
|
||||||
|
// frD(FPR(0)),
|
||||||
|
// rA(GPR(0)),
|
||||||
|
// rB(GPR(0)),
|
||||||
|
// ps_WX(OpaqueU(0)),
|
||||||
|
// ps_IX(GQR(0)),
|
||||||
|
// ]
|
||||||
|
// );
|
||||||
|
assert_eq!(
|
||||||
|
ins.defs(),
|
||||||
|
[Argument::FPR(FPR(0)), Argument::None, Argument::None, Argument::None, Argument::None]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
ins.uses(),
|
||||||
|
[Argument::None, Argument::GPR(GPR(0)), Argument::None, Argument::None, Argument::None]
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_asm!(0x1000000C, "psq_lx f0, r0, r0, 0, qr0");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_psq_st() {
|
||||||
|
assert_asm!(0xF1230210, "psq_st f9, 0x210(r3), 0, qr0");
|
||||||
|
assert_asm!(0xF1238008, "psq_st f9, 0x8(r3), 1, qr0");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_psq_stu() {
|
||||||
|
assert_asm!(0xF40A0020, "psq_stu f0, 0x20(r10), 0, qr0");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_psq_stx() {
|
||||||
|
assert_asm!(0x13E1000E, "psq_stx f31, r1, r0, 0, qr0");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_ps_abs() {
|
||||||
|
assert_asm!(0x10A03210, "ps_abs f5, f6");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_ps_add() {
|
||||||
|
assert_asm!(0x1006382A, "ps_add f0, f6, f7");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_ps_cmpo0() {
|
||||||
|
assert_asm!(0x10070840, "ps_cmpo0 cr0, f7, f1");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_ps_cmpu0() {
|
||||||
|
assert_asm!(0x10003000, "ps_cmpu0 cr0, f0, f6");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_ps_cmpu1() {
|
||||||
|
assert_asm!(0x10003080, "ps_cmpu1 cr0, f0, f6");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_ps_madd() {
|
||||||
|
assert_asm!(0x112141FA, "ps_madd f9, f1, f7, f8");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_ps_madds0() {
|
||||||
|
assert_asm!(0x10AC299C, "ps_madds0 f5, f12, f6, f5");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_ps_madds1() {
|
||||||
|
assert_asm!(0x110640DE, "ps_madds1 f8, f6, f3, f8");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_ps_merge00() {
|
||||||
|
assert_asm!(0x10400420, "ps_merge00 f2, f0, f0");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_ps_merge01() {
|
||||||
|
assert_asm!(0x10400C60, "ps_merge01 f2, f0, f1");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_ps_merge10() {
|
||||||
|
assert_asm!(0x104004A0, "ps_merge10 f2, f0, f0");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_ps_merge11() {
|
||||||
|
assert_asm!(0x10AA14E0, "ps_merge11 f5, f10, f2");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_ps_mr() {
|
||||||
|
assert_asm!(0x10200090, "ps_mr f1, f0");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_ps_msub() {
|
||||||
|
assert_asm!(0x10A53778, "ps_msub f5, f5, f29, f6");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_ps_mul() {
|
||||||
|
assert_asm!(0x10000032, "ps_mul f0, f0, f0");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_ps_muls0() {
|
||||||
|
assert_asm!(0x100002D8, "ps_muls0 f0, f0, f11");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_ps_muls1() {
|
||||||
|
assert_asm!(0x10A2005A, "ps_muls1 f5, f2, f1");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_ps_nabs() {
|
||||||
|
assert_asm!(0x10803210, "ps_abs f4, f6");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_ps_neg() {
|
||||||
|
assert_asm!(0x10E03850, "ps_neg f7, f7");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_ps_nmadd() {
|
||||||
|
assert_asm!(0x10CB30FE, "ps_nmadd f6, f11, f3, f6");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_ps_nmsub() {
|
||||||
|
assert_asm!(0x107E083C, "ps_nmsub f3, f30, f0, f1");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_ps_sel() {
|
||||||
|
assert_asm!(0x106428EE, "ps_sel f3, f4, f3, f5");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_ps_sub() {
|
||||||
|
assert_asm!(0x10A92828, "ps_sub f5, f9, f5");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_ps_sum0() {
|
||||||
|
assert_asm!(0x10230854, "ps_sum0 f1, f3, f1, f1");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ins_ps_sum1() {
|
||||||
|
assert_asm!(0x10A12956, "ps_sum1 f5, f1, f5, f5");
|
||||||
|
}
|
@ -1,15 +1,41 @@
|
|||||||
use ppc750cl::Ins;
|
use powerpc::{Extension, Extensions, Ins};
|
||||||
|
|
||||||
|
const EXTENSIONS: Extensions = Extensions::from_extension(Extension::Vmx128);
|
||||||
|
|
||||||
macro_rules! assert_asm {
|
macro_rules! assert_asm {
|
||||||
($ins:ident, $disasm:literal) => {{
|
($ins:ident, $disasm:literal) => {{
|
||||||
assert_eq!(format!("{}", $ins.simplified()), $disasm)
|
assert_eq!(format!("{}", $ins.simplified()), $disasm)
|
||||||
}};
|
}};
|
||||||
($code:literal, $disasm:literal) => {{
|
($code:literal, $disasm:literal) => {{
|
||||||
let ins = Ins::new($code);
|
let ins = Ins::new($code, EXTENSIONS);
|
||||||
assert_eq!(format!("{}", ins.simplified()), $disasm)
|
assert_eq!(format!("{}", ins.simplified()), $disasm)
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_vmx_enables_altivec() {
|
||||||
|
let extensions = Extensions::from_extension(Extension::Vmx128);
|
||||||
|
assert!(extensions.contains(Extension::AltiVec));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_extensions_remove_vmx() {
|
||||||
|
let mut extensions = Extensions::from_extension(Extension::Vmx128);
|
||||||
|
extensions.remove(Extension::Vmx128);
|
||||||
|
assert!(!extensions.contains(Extension::Vmx128));
|
||||||
|
// Ensure AltiVec is still enabled
|
||||||
|
assert!(extensions.contains(Extension::AltiVec));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_extensions_remove_altivec() {
|
||||||
|
let mut extensions = Extensions::from_extension(Extension::Vmx128);
|
||||||
|
extensions.remove(Extension::AltiVec);
|
||||||
|
assert!(!extensions.contains(Extension::AltiVec));
|
||||||
|
// Ensure Vmx128 is disabled (AltiVec required)
|
||||||
|
assert!(!extensions.contains(Extension::Vmx128));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_vmx_lvewx128() {
|
fn test_vmx_lvewx128() {
|
||||||
assert_asm!(0x1243388F, "lvewx128 v114, r3, r7");
|
assert_asm!(0x1243388F, "lvewx128 v114, r3, r7");
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ppc750cl-fuzz"
|
name = "powerpc-fuzz"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
authors = ["Richard Patel <me@terorie.dev>"]
|
authors = ["Richard Patel <me@terorie.dev>"]
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
description = "Complete fuzzer for ppc750cl"
|
description = "Complete fuzzer for powerpc"
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
publish = false
|
publish = false
|
||||||
@ -12,4 +12,4 @@ publish = false
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "4"
|
clap = "4"
|
||||||
num_cpus = "1.16"
|
num_cpus = "1.16"
|
||||||
ppc750cl = { path = "../disasm" }
|
powerpc = { path = "../disasm" }
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use powerpc::Extensions;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::sync::atomic::{AtomicU32, Ordering};
|
use std::sync::atomic::{AtomicU32, Ordering};
|
||||||
@ -5,9 +6,9 @@ use std::sync::Arc;
|
|||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let matches = clap::Command::new("ppc750cl-fuzz")
|
let matches = clap::Command::new("powerpc-fuzz")
|
||||||
.version("0.2.0")
|
.version(env!("CARGO_PKG_VERSION"))
|
||||||
.about("Complete \"fuzzer\" for ppc750cl disassembler")
|
.about("Complete \"fuzzer\" for powerpc disassembler")
|
||||||
.arg(
|
.arg(
|
||||||
clap::Arg::new("threads")
|
clap::Arg::new("threads")
|
||||||
.short('t')
|
.short('t')
|
||||||
@ -101,9 +102,10 @@ impl Fuzzer {
|
|||||||
let counter = Arc::clone(&self.counter);
|
let counter = Arc::clone(&self.counter);
|
||||||
let range = self.range.clone();
|
let range = self.range.clone();
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
let mut parsed = ppc750cl::ParsedIns::default();
|
let mut parsed = powerpc::ParsedIns::default();
|
||||||
for x in range.clone() {
|
for x in range.clone() {
|
||||||
ppc750cl::Ins::new(x).parse_simplified(&mut parsed);
|
powerpc::Ins::new(x, Extensions::from_bitmask(u32::MAX))
|
||||||
|
.parse_simplified(&mut parsed);
|
||||||
writeln!(&mut devnull, "{parsed}").unwrap();
|
writeln!(&mut devnull, "{parsed}").unwrap();
|
||||||
if x % (1 << 19) == 0 {
|
if x % (1 << 19) == 0 {
|
||||||
counter.store(x, Ordering::Relaxed);
|
counter.store(x, Ordering::Relaxed);
|
||||||
|
@ -1,18 +1,20 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ppc750cl-genisa"
|
name = "powerpc-genisa"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
description = "Rust code generator for ppc750cl"
|
description = "Rust code generator for powerpc"
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
|
indexmap = { version = "2.10", features = ["serde"] }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
|
phf_codegen = "0.11"
|
||||||
prettyplease = "0.2"
|
prettyplease = "0.2"
|
||||||
proc-macro2 = "1.0"
|
proc-macro2 = "1.0"
|
||||||
quote = "1.0"
|
quote = "1.0"
|
||||||
@ -20,5 +22,3 @@ serde = { version = "1.0", features = ["derive"] }
|
|||||||
serde_yaml = "0.9"
|
serde_yaml = "0.9"
|
||||||
simple_logger = "5.0"
|
simple_logger = "5.0"
|
||||||
syn = { version = "2", default-features = false, features = ["full", "parsing"] }
|
syn = { version = "2", default-features = false, features = ["full", "parsing"] }
|
||||||
phf = "0.11"
|
|
||||||
phf_codegen = "0.11"
|
|
||||||
|
@ -102,7 +102,7 @@ pub fn gen_asm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
|||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
#![cfg_attr(rustfmt, rustfmt_skip)]
|
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
#[comment = " Code generated by ppc750-genisa. DO NOT EDIT."]
|
#[comment = " Code generated by powerpc-genisa. DO NOT EDIT."]
|
||||||
use crate::types::*;
|
use crate::types::*;
|
||||||
pub type Arguments = [Argument; #max_args];
|
pub type Arguments = [Argument; #max_args];
|
||||||
#functions
|
#functions
|
||||||
|
@ -1,10 +1,15 @@
|
|||||||
use crate::condition::{parse_conditions, replace_fields};
|
use crate::{
|
||||||
use crate::ident;
|
condition::{parse_conditions, replace_fields},
|
||||||
use crate::isa::{modifiers_iter, modifiers_valid, HexLiteral, Isa, Opcode};
|
ident,
|
||||||
|
isa::{modifiers_iter, modifiers_valid, HexLiteral, Isa, Opcode},
|
||||||
|
};
|
||||||
use anyhow::{bail, ensure, Result};
|
use anyhow::{bail, ensure, Result};
|
||||||
use proc_macro2::{Literal, TokenStream};
|
use proc_macro2::{Ident, Literal, TokenStream};
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
use std::collections::HashMap;
|
use std::{
|
||||||
|
collections::{btree_map, BTreeMap, HashMap},
|
||||||
|
hash::{DefaultHasher, Hash, Hasher},
|
||||||
|
};
|
||||||
|
|
||||||
pub fn gen_disasm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
pub fn gen_disasm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
||||||
// The entry table allows us to quickly find the range of possible opcodes
|
// The entry table allows us to quickly find the range of possible opcodes
|
||||||
@ -59,7 +64,19 @@ pub fn gen_disasm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
|||||||
let pattern = HexLiteral(opcode.pattern);
|
let pattern = HexLiteral(opcode.pattern);
|
||||||
let enum_idx = Literal::u16_unsuffixed(idx as u16);
|
let enum_idx = Literal::u16_unsuffixed(idx as u16);
|
||||||
let name = &opcode.name;
|
let name = &opcode.name;
|
||||||
opcode_patterns.extend(quote! { (#bitmask, #pattern), });
|
let comment = format!(" {}", name);
|
||||||
|
let extension =
|
||||||
|
isa.extensions.iter().find(|(_, e)| e.opcodes.iter().any(|o| o.name == opcode.name));
|
||||||
|
let initializer = if let Some((id, _)) = extension {
|
||||||
|
let ident = format_ident!("{id}");
|
||||||
|
quote! { OpcodePattern::extension(#bitmask, #pattern, Extension::#ident) }
|
||||||
|
} else {
|
||||||
|
quote! { OpcodePattern::base(#bitmask, #pattern) }
|
||||||
|
};
|
||||||
|
opcode_patterns.extend(quote! {
|
||||||
|
#[comment = #comment]
|
||||||
|
#initializer,
|
||||||
|
});
|
||||||
opcode_names.extend(quote! { #name, });
|
opcode_names.extend(quote! { #name, });
|
||||||
let doc = opcode.doc();
|
let doc = opcode.doc();
|
||||||
let variant = opcode.variant();
|
let variant = opcode.variant();
|
||||||
@ -244,9 +261,12 @@ pub fn gen_disasm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
|||||||
let mut defs_uses_functions = TokenStream::new();
|
let mut defs_uses_functions = TokenStream::new();
|
||||||
let mut defs_refs = TokenStream::new();
|
let mut defs_refs = TokenStream::new();
|
||||||
let mut uses_refs = TokenStream::new();
|
let mut uses_refs = TokenStream::new();
|
||||||
|
|
||||||
|
// Deduplicate equivalent functions
|
||||||
|
let mut hash_to_fn = BTreeMap::<u64, Ident>::new();
|
||||||
|
|
||||||
for opcode in &sorted_ops {
|
for opcode in &sorted_ops {
|
||||||
let mut defs = TokenStream::new();
|
let mut defs = TokenStream::new();
|
||||||
let mut uses = TokenStream::new();
|
|
||||||
let mut defs_count = 0;
|
let mut defs_count = 0;
|
||||||
for def in &opcode.defs {
|
for def in &opcode.defs {
|
||||||
if isa.find_field(def).is_some_and(|f| f.arg.is_none()) {
|
if isa.find_field(def).is_some_and(|f| f.arg.is_none()) {
|
||||||
@ -256,6 +276,8 @@ pub fn gen_disasm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
|||||||
defs.extend(quote! { #arg, });
|
defs.extend(quote! { #arg, });
|
||||||
defs_count += 1;
|
defs_count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut uses = TokenStream::new();
|
||||||
let mut use_count = 0;
|
let mut use_count = 0;
|
||||||
for use_ in &opcode.uses {
|
for use_ in &opcode.uses {
|
||||||
if let Some(use_) = use_.strip_suffix(".nz") {
|
if let Some(use_) = use_.strip_suffix(".nz") {
|
||||||
@ -278,10 +300,23 @@ pub fn gen_disasm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
|||||||
defs.extend(quote! { Argument::None, });
|
defs.extend(quote! { Argument::None, });
|
||||||
}
|
}
|
||||||
let defs_name = format_ident!("defs_{}", opcode.ident());
|
let defs_name = format_ident!("defs_{}", opcode.ident());
|
||||||
defs_uses_functions.extend(quote! {
|
|
||||||
fn #defs_name(out: &mut Arguments, ins: Ins) { *out = [#defs]; }
|
let mut hasher = DefaultHasher::default();
|
||||||
});
|
opcode.defs.hash(&mut hasher);
|
||||||
defs_refs.extend(quote! { #defs_name, });
|
let defs_hash = hasher.finish();
|
||||||
|
match hash_to_fn.entry(defs_hash) {
|
||||||
|
btree_map::Entry::Vacant(e) => {
|
||||||
|
e.insert(defs_name.clone());
|
||||||
|
defs_uses_functions.extend(quote! {
|
||||||
|
fn #defs_name(out: &mut Arguments, ins: Ins) { *out = [#defs]; }
|
||||||
|
});
|
||||||
|
defs_refs.extend(quote! { #defs_name, });
|
||||||
|
}
|
||||||
|
btree_map::Entry::Occupied(e) => {
|
||||||
|
let ident = e.get();
|
||||||
|
defs_refs.extend(quote! { #ident, });
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
defs_refs.extend(quote! { defs_uses_empty, });
|
defs_refs.extend(quote! { defs_uses_empty, });
|
||||||
}
|
}
|
||||||
@ -291,10 +326,23 @@ pub fn gen_disasm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
|||||||
uses.extend(quote! { Argument::None, });
|
uses.extend(quote! { Argument::None, });
|
||||||
}
|
}
|
||||||
let uses_name = format_ident!("uses_{}", opcode.ident());
|
let uses_name = format_ident!("uses_{}", opcode.ident());
|
||||||
defs_uses_functions.extend(quote! {
|
|
||||||
fn #uses_name(out: &mut Arguments, ins: Ins) { *out = [#uses]; }
|
let mut hasher = DefaultHasher::default();
|
||||||
});
|
opcode.uses.hash(&mut hasher);
|
||||||
uses_refs.extend(quote! { #uses_name, });
|
let uses_hash = hasher.finish();
|
||||||
|
match hash_to_fn.entry(uses_hash) {
|
||||||
|
btree_map::Entry::Vacant(e) => {
|
||||||
|
e.insert(uses_name.clone());
|
||||||
|
defs_uses_functions.extend(quote! {
|
||||||
|
fn #uses_name(out: &mut Arguments, ins: Ins) { *out = [#uses]; }
|
||||||
|
});
|
||||||
|
uses_refs.extend(quote! { #uses_name, });
|
||||||
|
}
|
||||||
|
btree_map::Entry::Occupied(e) => {
|
||||||
|
let ident = e.get();
|
||||||
|
uses_refs.extend(quote! { #ident, });
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
uses_refs.extend(quote! { defs_uses_empty, });
|
uses_refs.extend(quote! { defs_uses_empty, });
|
||||||
}
|
}
|
||||||
@ -308,19 +356,96 @@ pub fn gen_disasm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
|||||||
none_args.extend(quote! { Argument::None, });
|
none_args.extend(quote! { Argument::None, });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let extension_variants = isa
|
||||||
|
.extensions
|
||||||
|
.iter()
|
||||||
|
.map(|(id, ext)| {
|
||||||
|
let ident = format_ident!("{id}");
|
||||||
|
let desc = format!(" {}", ext.name);
|
||||||
|
quote! {
|
||||||
|
#[doc = #desc]
|
||||||
|
#ident,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let extension_requires = isa
|
||||||
|
.extensions
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(id, ext)| {
|
||||||
|
if ext.requires.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let requires = ext
|
||||||
|
.requires
|
||||||
|
.iter()
|
||||||
|
.map(|parent| {
|
||||||
|
let ident = format_ident!("{parent}");
|
||||||
|
quote! { Extension::#ident.bitmask() }
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let ident = format_ident!("{id}");
|
||||||
|
Some(quote! { Extension::#ident => #(#requires)|*, })
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let extension_names = isa
|
||||||
|
.extensions
|
||||||
|
.iter()
|
||||||
|
.map(|(id, ext)| {
|
||||||
|
let ident = format_ident!("{id}");
|
||||||
|
let name = &ext.name;
|
||||||
|
Some(quote! { Extension::#ident => #name, })
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let extensions = quote! {
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum Extension {
|
||||||
|
#(#extension_variants)*
|
||||||
|
}
|
||||||
|
impl Extension {
|
||||||
|
#[inline]
|
||||||
|
pub const fn bitmask(self) -> u32 {
|
||||||
|
(1 << (self as u32)) | match self {
|
||||||
|
#(#extension_requires)*
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub const fn name(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
#(#extension_names)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let entries_count = Literal::usize_unsuffixed(entries.len());
|
let entries_count = Literal::usize_unsuffixed(entries.len());
|
||||||
let opcode_count = Literal::usize_unsuffixed(sorted_ops.len());
|
let opcode_count = Literal::usize_unsuffixed(sorted_ops.len());
|
||||||
let max_args = Literal::usize_unsuffixed(max_args);
|
let max_args = Literal::usize_unsuffixed(max_args);
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
#![cfg_attr(rustfmt, rustfmt_skip)]
|
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
#[comment = " Code generated by ppc750-genisa. DO NOT EDIT."]
|
#[comment = " Code generated by powerpc-genisa. DO NOT EDIT."]
|
||||||
use crate::disasm::*;
|
use crate::disasm::*;
|
||||||
|
#extensions
|
||||||
#[doc = " The entry table allows us to quickly find the range of possible opcodes for a"]
|
#[doc = " The entry table allows us to quickly find the range of possible opcodes for a"]
|
||||||
#[doc = " given 6-bit prefix. 2*64 bytes should fit in a cache line (or two)."]
|
#[doc = " given 6-bit prefix. 2*64 bytes should fit in a cache line (or two)."]
|
||||||
static OPCODE_ENTRIES: [(u16, u16); #entries_count] = [#opcode_entries];
|
static OPCODE_ENTRIES: [(u16, u16); #entries_count] = [#opcode_entries];
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
struct OpcodePattern {
|
||||||
|
bitmask: u32,
|
||||||
|
pattern: u32,
|
||||||
|
extensions: Extensions,
|
||||||
|
}
|
||||||
|
impl OpcodePattern {
|
||||||
|
const fn base(bitmask: u32, pattern: u32) -> Self {
|
||||||
|
Self { bitmask, pattern, extensions: Extensions::none() }
|
||||||
|
}
|
||||||
|
const fn extension(bitmask: u32, pattern: u32, extension: Extension) -> Self {
|
||||||
|
Self { bitmask, pattern, extensions: Extensions::from_extension(extension) }
|
||||||
|
}
|
||||||
|
}
|
||||||
#[doc = " The bitmask and pattern for each opcode."]
|
#[doc = " The bitmask and pattern for each opcode."]
|
||||||
static OPCODE_PATTERNS: [(u32, u32); #opcode_count] = [#opcode_patterns];
|
static OPCODE_PATTERNS: [OpcodePattern; #opcode_count] = [#opcode_patterns];
|
||||||
#[doc = " The name of each opcode."]
|
#[doc = " The name of each opcode."]
|
||||||
static OPCODE_NAMES: [&str; #opcode_count] = [#opcode_names];
|
static OPCODE_NAMES: [&str; #opcode_count] = [#opcode_names];
|
||||||
|
|
||||||
@ -334,17 +459,15 @@ pub fn gen_disasm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
|||||||
#opcode_enum
|
#opcode_enum
|
||||||
}
|
}
|
||||||
impl Opcode {
|
impl Opcode {
|
||||||
#[inline]
|
|
||||||
pub fn mnemonic(self) -> &'static str {
|
pub fn mnemonic(self) -> &'static str {
|
||||||
OPCODE_NAMES.get(self as usize).copied().unwrap_or("<illegal>")
|
OPCODE_NAMES.get(self as usize).copied().unwrap_or("<illegal>")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
pub fn detect(code: u32, extensions: Extensions) -> Self {
|
||||||
pub fn detect(code: u32) -> Self {
|
|
||||||
let entry = OPCODE_ENTRIES[(code >> 26) as usize];
|
let entry = OPCODE_ENTRIES[(code >> 26) as usize];
|
||||||
for i in entry.0..entry.1 {
|
for i in entry.0..entry.1 {
|
||||||
let pattern = OPCODE_PATTERNS[i as usize];
|
let op = OPCODE_PATTERNS[i as usize];
|
||||||
if (code & pattern.0) == pattern.1 {
|
if extensions.contains_all(op.extensions) && (code & op.bitmask) == op.pattern {
|
||||||
#[comment = " Safety: The enum is repr(u16) and the value is within the enum's range"]
|
#[comment = " Safety: The enum is repr(u16) and the value is within the enum's range"]
|
||||||
return unsafe { core::mem::transmute::<u16, Opcode>(i) };
|
return unsafe { core::mem::transmute::<u16, Opcode>(i) };
|
||||||
}
|
}
|
||||||
@ -380,16 +503,14 @@ pub fn gen_disasm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
|||||||
type MnemonicFunction = fn(&mut ParsedIns, Ins);
|
type MnemonicFunction = fn(&mut ParsedIns, Ins);
|
||||||
#mnemonic_functions
|
#mnemonic_functions
|
||||||
static BASIC_MNEMONICS: [MnemonicFunction; #opcode_count] = [#basic_functions_ref];
|
static BASIC_MNEMONICS: [MnemonicFunction; #opcode_count] = [#basic_functions_ref];
|
||||||
#[inline]
|
pub(crate) fn parse_basic(out: &mut ParsedIns, ins: Ins) {
|
||||||
pub fn parse_basic(out: &mut ParsedIns, ins: Ins) {
|
|
||||||
match BASIC_MNEMONICS.get(ins.op as usize) {
|
match BASIC_MNEMONICS.get(ins.op as usize) {
|
||||||
Some(f) => f(out, ins),
|
Some(f) => f(out, ins),
|
||||||
None => mnemonic_illegal(out, ins),
|
None => mnemonic_illegal(out, ins),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static SIMPLIFIED_MNEMONICS: [MnemonicFunction; #opcode_count] = [#simplified_functions_ref];
|
static SIMPLIFIED_MNEMONICS: [MnemonicFunction; #opcode_count] = [#simplified_functions_ref];
|
||||||
#[inline]
|
pub(crate) fn parse_simplified(out: &mut ParsedIns, ins: Ins) {
|
||||||
pub fn parse_simplified(out: &mut ParsedIns, ins: Ins) {
|
|
||||||
match SIMPLIFIED_MNEMONICS.get(ins.op as usize) {
|
match SIMPLIFIED_MNEMONICS.get(ins.op as usize) {
|
||||||
Some(f) => f(out, ins),
|
Some(f) => f(out, ins),
|
||||||
None => mnemonic_illegal(out, ins),
|
None => mnemonic_illegal(out, ins),
|
||||||
@ -399,16 +520,14 @@ pub fn gen_disasm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
|||||||
type DefsUsesFunction = fn(&mut Arguments, Ins);
|
type DefsUsesFunction = fn(&mut Arguments, Ins);
|
||||||
#defs_uses_functions
|
#defs_uses_functions
|
||||||
static DEFS_FUNCTIONS: [DefsUsesFunction; #opcode_count] = [#defs_refs];
|
static DEFS_FUNCTIONS: [DefsUsesFunction; #opcode_count] = [#defs_refs];
|
||||||
#[inline]
|
pub(crate) fn parse_defs(out: &mut Arguments, ins: Ins) {
|
||||||
pub fn parse_defs(out: &mut Arguments, ins: Ins) {
|
|
||||||
match DEFS_FUNCTIONS.get(ins.op as usize) {
|
match DEFS_FUNCTIONS.get(ins.op as usize) {
|
||||||
Some(f) => f(out, ins),
|
Some(f) => f(out, ins),
|
||||||
None => defs_uses_empty(out, ins),
|
None => defs_uses_empty(out, ins),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static USES_FUNCTIONS: [DefsUsesFunction; #opcode_count] = [#uses_refs];
|
static USES_FUNCTIONS: [DefsUsesFunction; #opcode_count] = [#uses_refs];
|
||||||
#[inline]
|
pub(crate) fn parse_uses(out: &mut Arguments, ins: Ins) {
|
||||||
pub fn parse_uses(out: &mut Arguments, ins: Ins) {
|
|
||||||
match USES_FUNCTIONS.get(ins.op as usize) {
|
match USES_FUNCTIONS.get(ins.op as usize) {
|
||||||
Some(f) => f(out, ins),
|
Some(f) => f(out, ins),
|
||||||
None => defs_uses_empty(out, ins),
|
None => defs_uses_empty(out, ins),
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::{fs::File, path::Path, str::FromStr};
|
use std::{fs::File, path::Path, str::FromStr};
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
|
use indexmap::IndexMap;
|
||||||
use num_traits::PrimInt;
|
use num_traits::PrimInt;
|
||||||
use proc_macro2::{Ident, Span, TokenStream};
|
use proc_macro2::{Ident, Span, TokenStream};
|
||||||
use quote::{format_ident, ToTokens};
|
use quote::{format_ident, ToTokens};
|
||||||
@ -11,8 +12,55 @@ use serde::{Deserialize, Deserializer, Serialize};
|
|||||||
pub fn load_isa(path: &Path) -> Result<Isa> {
|
pub fn load_isa(path: &Path) -> Result<Isa> {
|
||||||
let yaml_file =
|
let yaml_file =
|
||||||
File::open(path).with_context(|| format!("Failed to open file {}", path.display()))?;
|
File::open(path).with_context(|| format!("Failed to open file {}", path.display()))?;
|
||||||
let isa: Isa = serde_yaml::from_reader(yaml_file)
|
let mut isa: Isa = serde_yaml::from_reader(yaml_file)
|
||||||
.with_context(|| format!("While parsing file {}", path.display()))?;
|
.with_context(|| format!("While parsing file {}", path.display()))?;
|
||||||
|
// Merge in all extensions
|
||||||
|
for extension in isa.extensions.values() {
|
||||||
|
for field in &extension.fields {
|
||||||
|
if isa.find_field(&field.name).is_some() {
|
||||||
|
bail!(
|
||||||
|
"Field {} already exists (while applying extension {})",
|
||||||
|
field.name,
|
||||||
|
extension.name
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
isa.fields.push(field.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for modifier in &extension.modifiers {
|
||||||
|
if isa.find_modifier(&modifier.name).is_some() {
|
||||||
|
bail!(
|
||||||
|
"Modifier {} already exists (while applying extension {})",
|
||||||
|
modifier.name,
|
||||||
|
extension.name
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
isa.modifiers.push(modifier.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for opcode in &extension.opcodes {
|
||||||
|
if isa.find_opcode(&opcode.name).is_some() {
|
||||||
|
bail!(
|
||||||
|
"Opcode {} already exists (while applying extension {})",
|
||||||
|
opcode.name,
|
||||||
|
extension.name
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
isa.opcodes.push(opcode.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for mnemonic in &extension.mnemonics {
|
||||||
|
if isa.find_mnemonic(&mnemonic.name).is_some() {
|
||||||
|
bail!(
|
||||||
|
"Mnemonic {} already exists (while applying extension {})",
|
||||||
|
mnemonic.name,
|
||||||
|
extension.name
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
isa.mnemonics.push(mnemonic.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(isa)
|
Ok(isa)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,6 +71,18 @@ pub struct Isa {
|
|||||||
pub modifiers: Vec<Modifier>,
|
pub modifiers: Vec<Modifier>,
|
||||||
pub opcodes: Vec<Opcode>,
|
pub opcodes: Vec<Opcode>,
|
||||||
pub mnemonics: Vec<Mnemonic>,
|
pub mnemonics: Vec<Mnemonic>,
|
||||||
|
pub extensions: IndexMap<String, Extension>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Clone, Debug, Default)]
|
||||||
|
#[serde(default)]
|
||||||
|
pub struct Extension {
|
||||||
|
pub name: String,
|
||||||
|
pub requires: Vec<String>,
|
||||||
|
pub fields: Vec<Field>,
|
||||||
|
pub modifiers: Vec<Modifier>,
|
||||||
|
pub opcodes: Vec<Opcode>,
|
||||||
|
pub mnemonics: Vec<Mnemonic>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Isa {
|
impl Isa {
|
||||||
@ -37,6 +97,10 @@ impl Isa {
|
|||||||
pub fn find_opcode(&self, name: &str) -> Option<&Opcode> {
|
pub fn find_opcode(&self, name: &str) -> Option<&Opcode> {
|
||||||
self.opcodes.iter().find(|o| o.name == name)
|
self.opcodes.iter().find(|o| o.name == name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn find_mnemonic(&self, name: &str) -> Option<&Mnemonic> {
|
||||||
|
self.mnemonics.iter().find(|m| m.name == name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Clone, Debug, Default)]
|
#[derive(Deserialize, Serialize, Clone, Debug, Default)]
|
||||||
@ -222,6 +286,7 @@ pub fn modifiers_iter<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default)]
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
|
#[repr(transparent)]
|
||||||
pub struct BitRange(pub (u8, u8));
|
pub struct BitRange(pub (u8, u8));
|
||||||
|
|
||||||
impl BitRange {
|
impl BitRange {
|
||||||
@ -231,42 +296,42 @@ impl BitRange {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn start(&self) -> u8 {
|
pub fn start(self) -> u8 {
|
||||||
self.0 .0
|
self.0 .0
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn end(&self) -> u8 {
|
pub fn end(self) -> u8 {
|
||||||
self.0 .1
|
self.0 .1
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate the mask from the range
|
/// Calculate the mask from the range
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn mask(&self) -> u32 {
|
pub fn mask(self) -> u32 {
|
||||||
self.max_value() << self.shift()
|
self.max_value() << self.shift()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Number of bits to shift
|
/// Number of bits to shift
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn shift(&self) -> u8 {
|
pub fn shift(self) -> u8 {
|
||||||
32 - self.end()
|
32 - self.end()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Number of bits in the range
|
/// Number of bits in the range
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn len(&self) -> u8 {
|
pub fn len(self) -> u8 {
|
||||||
self.end() - self.start()
|
self.end() - self.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shift and mask a value according to the range
|
/// Shift and mask a value according to the range
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn shift_value(&self, value: u32) -> u32 {
|
pub fn shift_value(self, value: u32) -> u32 {
|
||||||
(value & self.max_value()) << self.shift()
|
(value & self.max_value()) << self.shift()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate the maximum value that can be represented by the range
|
/// Calculate the maximum value that can be represented by the range
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn max_value(&self) -> u32 {
|
pub fn max_value(self) -> u32 {
|
||||||
(1 << self.len()) - 1
|
(1 << self.len()) - 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -308,13 +373,13 @@ pub struct SplitBitRange(pub Vec<BitRange>);
|
|||||||
impl SplitBitRange {
|
impl SplitBitRange {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn end(&self) -> u8 {
|
pub fn end(&self) -> u8 {
|
||||||
self.0.iter().map(|r| r.end()).max().unwrap_or(0)
|
self.iter().map(BitRange::end).max().unwrap_or(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate the mask from the range
|
/// Calculate the mask from the range
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn mask(&self) -> u32 {
|
pub fn mask(&self) -> u32 {
|
||||||
self.0.iter().map(|r| r.mask()).fold(0, |acc, m| acc | m)
|
self.iter().map(BitRange::mask).fold(0, |acc, m| acc | m)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Number of bits to shift
|
/// Number of bits to shift
|
||||||
@ -326,14 +391,14 @@ impl SplitBitRange {
|
|||||||
/// Number of bits in the range
|
/// Number of bits in the range
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn len(&self) -> u8 {
|
pub fn len(&self) -> u8 {
|
||||||
self.0.iter().map(|r| r.len()).sum()
|
self.iter().map(BitRange::len).sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shift and mask a value according to the range
|
/// Shift and mask a value according to the range
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn shift_value(&self, mut value: u32) -> u32 {
|
pub fn shift_value(&self, mut value: u32) -> u32 {
|
||||||
let mut result = 0;
|
let mut result = 0;
|
||||||
for range in self.0.iter().rev() {
|
for range in self.iter().rev() {
|
||||||
result |= range.shift_value(value);
|
result |= range.shift_value(value);
|
||||||
value >>= range.len();
|
value >>= range.len();
|
||||||
}
|
}
|
||||||
@ -345,6 +410,12 @@ impl SplitBitRange {
|
|||||||
pub fn max_value(&self) -> u32 {
|
pub fn max_value(&self) -> u32 {
|
||||||
(1 << self.len()) - 1
|
(1 << self.len()) - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create an iterator over the contained bit ranges
|
||||||
|
#[inline]
|
||||||
|
pub fn iter(&self) -> impl DoubleEndedIterator<Item = BitRange> + use<'_> {
|
||||||
|
self.0.iter().copied()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for SplitBitRange {
|
impl<'de> Deserialize<'de> for SplitBitRange {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user