Merge pull request #17 from terorie/auto-gen-isa

Auto-generate opcode detection, fields and printing
This commit is contained in:
Richard Patel 2022-04-07 05:45:28 +02:00 committed by GitHub
commit fb65bc8e7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 10514 additions and 3665 deletions

357
Cargo.lock generated
View File

@ -2,15 +2,6 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
"winapi",
]
[[package]] [[package]]
name = "atty" name = "atty"
version = "0.2.14" version = "0.2.14"
@ -24,9 +15,9 @@ dependencies = [
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.0.1" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]] [[package]]
name = "bincode" name = "bincode"
@ -51,24 +42,24 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "clap" name = "clap"
version = "2.33.3" version = "3.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" checksum = "71c47df61d9e16dc010b55dba1952a57d8c215dbb533fd13cdd13369aac73b1c"
dependencies = [ dependencies = [
"ansi_term",
"atty", "atty",
"bitflags", "bitflags",
"indexmap",
"os_str_bytes",
"strsim", "strsim",
"termcolor",
"textwrap", "textwrap",
"unicode-width",
"vec_map",
] ]
[[package]] [[package]]
name = "ctor" name = "ctor"
version = "0.1.20" version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e98e2ad1a782e33928b96fc3948e7c355e5af34ba4de7670fe8bac2a3b2006d" checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c"
dependencies = [ dependencies = [
"quote", "quote",
"syn", "syn",
@ -91,15 +82,15 @@ checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]] [[package]]
name = "fixedbitset" name = "fixedbitset"
version = "0.4.0" version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "398ea4fabe40b9b0d885340a2a991a44c8a645624075ad966d21f88688e2b69e" checksum = "279fb028e20b3c4c320317955b77c5e0c9701f05a1d309905d6fc702cdc5053e"
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.1.16" version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
@ -134,9 +125,9 @@ dependencies = [
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "1.7.0" version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"hashbrown", "hashbrown",
@ -144,82 +135,69 @@ dependencies = [
[[package]] [[package]]
name = "indoc" name = "indoc"
version = "0.3.6" version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47741a8bc60fb26eb8d6e0238bbb26d8575ff623fdc97b1a2c00c050b9684ed8" checksum = "e7906a9fababaeacb774f72410e497a1d18de916322e33797bb2cd29baa23c9e"
dependencies = [ dependencies = [
"indoc-impl",
"proc-macro-hack",
]
[[package]]
name = "indoc-impl"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce046d161f000fffde5f432a0d034d0341dc152643b2598ed5bfce44c4f3a8f0"
dependencies = [
"proc-macro-hack",
"proc-macro2",
"quote",
"syn",
"unindent", "unindent",
] ]
[[package]] [[package]]
name = "instant" name = "instant"
version = "0.1.10" version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]] [[package]]
name = "inventory" name = "inventory"
version = "0.1.10" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f0f7efb804ec95e33db9ad49e4252f049e37e8b0a4652e3cd61f7999f2eff7f" checksum = "ce6b5d8c669bfbad811d95ddd7a1c6cf9cfdbf2777e59928b6f3fa8ff54f72a0"
dependencies = [ dependencies = [
"ctor", "ctor",
"ghost", "ghost",
"inventory-impl",
]
[[package]]
name = "inventory-impl"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75c094e94816723ab936484666968f5b58060492e880f3c8d00489a1e244fa51"
dependencies = [
"proc-macro2",
"quote",
"syn",
] ]
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.10.1" version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
dependencies = [ dependencies = [
"either", "either",
] ]
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.99" version = "0.2.122"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765" checksum = "ec647867e2bf0772e28c8bcde4f0d19a9216916e890543b5a03ed8ef27b8f259"
[[package]]
name = "linked-hash-map"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
[[package]] [[package]]
name = "lock_api" name = "lock_api"
version = "0.4.4" version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
dependencies = [ dependencies = [
"autocfg",
"scopeguard", "scopeguard",
] ]
[[package]]
name = "memchr"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.14" version = "0.2.14"
@ -231,9 +209,9 @@ dependencies = [
[[package]] [[package]]
name = "num_cpus" name = "num_cpus"
version = "1.13.0" version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
dependencies = [ dependencies = [
"hermit-abi", "hermit-abi",
"libc", "libc",
@ -241,15 +219,24 @@ dependencies = [
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.8.0" version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
[[package]]
name = "os_str_bytes"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "parking_lot" name = "parking_lot"
version = "0.11.1" version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
dependencies = [ dependencies = [
"instant", "instant",
"lock_api", "lock_api",
@ -258,9 +245,9 @@ dependencies = [
[[package]] [[package]]
name = "parking_lot_core" name = "parking_lot_core"
version = "0.8.3" version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"instant", "instant",
@ -272,32 +259,13 @@ dependencies = [
[[package]] [[package]]
name = "parse_int" name = "parse_int"
version = "0.5.0" version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15f96500577cfa0a3bad8a88a3c4daa66684828af2e7d349012fa7fc3c725f0c" checksum = "2d695b79916a2c08bcff7be7647ab60d1402885265005a6658ffe6d763553c5a"
dependencies = [ dependencies = [
"num-traits", "num-traits",
] ]
[[package]]
name = "paste"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45ca20c77d80be666aef2b45486da86238fabe33e38306bd3118fe4af33fa880"
dependencies = [
"paste-impl",
"proc-macro-hack",
]
[[package]]
name = "paste-impl"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d95a7db200b97ef370c8e6de0088252f7e0dfff7d047a28528e47456c0fc98b6"
dependencies = [
"proc-macro-hack",
]
[[package]] [[package]]
name = "petgraph" name = "petgraph"
version = "0.6.0" version = "0.6.0"
@ -310,15 +278,15 @@ dependencies = [
[[package]] [[package]]
name = "ppc750cl" name = "ppc750cl"
version = "0.1.1" version = "0.2.0"
dependencies = [ dependencies = [
"num-traits", "num-traits",
"ppc750cl-macros", "serde",
] ]
[[package]] [[package]]
name = "ppc750cl-flow-graph" name = "ppc750cl-flow-graph"
version = "0.1.1" version = "0.2.0"
dependencies = [ dependencies = [
"clap", "clap",
"dol", "dol",
@ -330,24 +298,27 @@ dependencies = [
[[package]] [[package]]
name = "ppc750cl-fuzz" name = "ppc750cl-fuzz"
version = "0.1.1" version = "0.2.0"
dependencies = [ dependencies = [
"num_cpus", "num_cpus",
"ppc750cl", "ppc750cl",
] ]
[[package]] [[package]]
name = "ppc750cl-macros" name = "ppc750cl-genisa"
version = "0.1.1" version = "0.2.0"
dependencies = [ dependencies = [
"itertools",
"proc-macro2", "proc-macro2",
"quote", "quote",
"serde",
"serde_yaml",
"syn", "syn",
] ]
[[package]] [[package]]
name = "ppc750cl-py" name = "ppc750cl-py"
version = "0.1.1" version = "0.2.0"
dependencies = [ dependencies = [
"ppc750cl", "ppc750cl",
"pyo3", "pyo3",
@ -355,7 +326,7 @@ dependencies = [
[[package]] [[package]]
name = "ppc750cl-rand" name = "ppc750cl-rand"
version = "0.1.1" version = "0.2.0"
dependencies = [ dependencies = [
"ppc750cl", "ppc750cl",
"rand_core", "rand_core",
@ -364,57 +335,63 @@ dependencies = [
[[package]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
version = "0.2.10" version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]]
name = "proc-macro-hack"
version = "0.5.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.28" version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612" checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1"
dependencies = [ dependencies = [
"unicode-xid", "unicode-xid",
] ]
[[package]] [[package]]
name = "pyo3" name = "pyo3"
version = "0.14.2" version = "0.16.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af205762ba65eec9f27a2fa1a57a40644e8e3368784b8c8b2f2de48f6e8ddd96" checksum = "6b3e99c4c3e790e4fc365b42b70c1f7801f42eadc4ea648fa327e6f5ca29f215"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"indoc", "indoc",
"inventory", "inventory",
"libc", "libc",
"parking_lot", "parking_lot",
"paste",
"pyo3-build-config", "pyo3-build-config",
"pyo3-ffi",
"pyo3-macros", "pyo3-macros",
"unindent", "unindent",
] ]
[[package]] [[package]]
name = "pyo3-build-config" name = "pyo3-build-config"
version = "0.14.2" version = "0.16.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "755944027ce803c7238e59c5a18e59c1d0a4553db50b23e9ba209a568353028d" checksum = "2486b96281859ff0a3929ba6467b13751627b974f7137362db38e2bed14b2094"
dependencies = [ dependencies = [
"once_cell", "once_cell",
"target-lexicon",
]
[[package]]
name = "pyo3-ffi"
version = "0.16.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd9de1d94557751599f8bd321f10e6c1ef2801067acb58c91138deef2ae83a17"
dependencies = [
"libc",
"pyo3-build-config",
] ]
[[package]] [[package]]
name = "pyo3-macros" name = "pyo3-macros"
version = "0.14.2" version = "0.16.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd31b36bccfd902c78804bd96c28ea93eac6fa0ca311f9d21ef2230b6665b29a" checksum = "0b9584049129b1cfb615243391a6345c726690271ae195ffd6aa3766177296aa"
dependencies = [ dependencies = [
"proc-macro2",
"pyo3-macros-backend", "pyo3-macros-backend",
"quote", "quote",
"syn", "syn",
@ -422,43 +399,40 @@ dependencies = [
[[package]] [[package]]
name = "pyo3-macros-backend" name = "pyo3-macros-backend"
version = "0.14.2" version = "0.16.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c21c59ba36db9c823e931c662766b0dd01a030b1d96585b67d8857a96a56b972" checksum = "b6c4717e6a55c51a9958eda1f5481ff7f62cccd21f45309c10e4731cb7198dbc"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"pyo3-build-config",
"quote", "quote",
"syn", "syn",
] ]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.9" version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]] [[package]]
name = "rand" name = "rand"
version = "0.7.3" version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [ dependencies = [
"getrandom",
"libc", "libc",
"rand_chacha", "rand_chacha",
"rand_core", "rand_core",
"rand_hc",
] ]
[[package]] [[package]]
name = "rand_chacha" name = "rand_chacha"
version = "0.2.2" version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [ dependencies = [
"ppv-lite86", "ppv-lite86",
"rand_core", "rand_core",
@ -466,31 +440,28 @@ dependencies = [
[[package]] [[package]]
name = "rand_core" name = "rand_core"
version = "0.5.1" version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [ dependencies = [
"getrandom", "getrandom",
] ]
[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
"rand_core",
]
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.2.10" version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
dependencies = [ dependencies = [
"bitflags", "bitflags",
] ]
[[package]]
name = "ryu"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
[[package]] [[package]]
name = "scopeguard" name = "scopeguard"
version = "1.1.0" version = "1.1.0"
@ -499,18 +470,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.128" version = "1.0.136"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1056a0db1978e9dbf0f6e4fca677f6f9143dc1c19de346f22cac23e422196834" checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.128" version = "1.0.136"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13af2fbb8b60a8950d6c72a56d2095c28870367cc8e10c55e9745bac4995a2c4" checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -518,10 +489,22 @@ dependencies = [
] ]
[[package]] [[package]]
name = "sfmt" name = "serde_yaml"
version = "0.6.0" version = "0.8.23"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "269239cd28e19133d5dc0179d060b00b0b95ce1224792ea5b7c075241d4ee309" checksum = "a4a521f2940385c165a24ee286aa8599633d162077a54bdcae2a6fd5a7bfa7a0"
dependencies = [
"indexmap",
"ryu",
"serde",
"yaml-rust",
]
[[package]]
name = "sfmt"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e389a3c1536438f85612667cd992dfb7169f60784252342278458e90d37c7565"
dependencies = [ dependencies = [
"rand", "rand",
"rand_core", "rand_core",
@ -529,21 +512,21 @@ dependencies = [
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.6.1" version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.8.0" version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.74" version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c" checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -551,40 +534,46 @@ dependencies = [
] ]
[[package]] [[package]]
name = "textwrap" name = "target-lexicon"
version = "0.11.0" version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" checksum = "d7fa7e55043acb85fca6b3c01485a2eeb6b69c5d21002e273c79e465f43b7ac1"
[[package]]
name = "termcolor"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
dependencies = [ dependencies = [
"unicode-width", "winapi-util",
] ]
[[package]] [[package]]
name = "thiserror" name = "textwrap"
version = "1.0.26" version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2" checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
[[package]]
name = "thiserror"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl",
] ]
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "1.0.26" version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745" checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn",
] ]
[[package]]
name = "unicode-width"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
[[package]] [[package]]
name = "unicode-xid" name = "unicode-xid"
version = "0.2.2" version = "0.2.2"
@ -593,21 +582,15 @@ checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]] [[package]]
name = "unindent" name = "unindent"
version = "0.1.7" version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7" checksum = "514672a55d7380da379785a4d70ca8386c8883ff7eaae877be4d2081cebe73d8"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.9.0+wasi-snapshot-preview1" version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]] [[package]]
name = "winapi" name = "winapi"
@ -625,8 +608,26 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]] [[package]]
name = "winapi-x86_64-pc-windows-gnu" name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "yaml-rust"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [
"linked-hash-map",
]

View File

@ -3,8 +3,8 @@ members = [
"disasm", "disasm",
"disasm-py", "disasm-py",
"dol", "dol",
"macros",
"fuzz", "fuzz",
"genisa",
"flow-graph", "flow-graph",
"rand", "rand",
] ]

View File

@ -2,9 +2,15 @@
Rust tools for working with the PowerPC 750CL family of processors. Rust tools for working with the PowerPC 750CL family of processors.
### Python module ### Rust crates
Build instructions: ```shell
rustup components add rustfmt
cargo run --package ppc750cl-genisa
cargo build --release
```
### Python module
```shell ```shell
python -m venv env python -m venv env
@ -12,3 +18,21 @@ source ./env/bin/activate
pip install maturin pip install maturin
maturin build -m ./disasm-py/Cargo.toml maturin build -m ./disasm-py/Cargo.toml
``` ```
### Instruction Set
For those unfamiliar with PowerPC, here are some basics.
- PowerPC 7xx is a family of RISC CPUs produced from 1997 to 2012.
- They operate with 32-bit words and every instruction is 32-bits wide.
- This project focuses (only) on compatibility with the PowerPC 750CL.
- This chip is famously packaged as codename "Broadway" for the Nintendo Wii.
- Its predecessor PowerPC 750CXe is used in the Nintendo GameCube.
- It adds a "paired-singles" SIMD unit and a bunch of other instructions.
### isa.yaml
The file [isa.yaml](./isa.yaml) contains a full definition of the PowerPC 750CL instruction set.
It powers the disassembler, assembler, and Rust/Python bindings code analysis tools.
Similarly to LLVM TableGen, the program `ppc750cl-genisa` generates a Rust file implementing an instruction decoder.

2
codegen/.gitignore vendored
View File

@ -1,2 +0,0 @@
masks.txt
__pycache__

View File

@ -1,38 +0,0 @@
import sys
def apply_pattern(pattern, mask, bits):
start, stop, value = map(int, pattern.split(","))
bit_count = stop - start + 1
shift = 31 - stop
mask |= ((1 << bit_count) - 1) << shift
bits |= value << shift
return mask, bits
def dump_mask(line):
parts = line.split(" ")
opcode = parts[0]
patterns = parts[1:]
assert len(patterns) > 0
mask, bits = 0, 0
for pattern in patterns:
mask, bits = apply_pattern(pattern, mask, bits)
print(f' "{opcode}" & {hex(mask)} == {hex(bits)};')
def main():
with open("patterns.txt", "r") as patterns, open(
"../disasm/src/isa.rs", "w"
) as isa_file:
sys.stdout = isa_file
print("use ppc750cl_macros::isa;")
print()
print("isa! {")
for line in patterns.readlines():
dump_mask(line)
print("}")
if __name__ == "__main__":
main()

View File

@ -1,222 +0,0 @@
add 0,5,31 21,30,266
addc 0,5,31 21,30,21
adde 0,5,31 21,30,138
addi 0,5,14
addic 0,5,12
addic. 0,5,13
addis 0,5,15
addme 0,5,31 16,20,0 22,30,234
addze 0,5,31 16,20,0 22,30,202
and 0,5,31 21,30,28
andc 0,5,31 21,30,60
andi. 0,5,28
andis. 0,5,29
b 0,5,18
bc 0,5,16
bcctr 0,5,19 16,20,0 21,31,528
bclr 0,5,19 16,20,0 21,30,16
cmp 0,5,31 9,9,0 21,31,0
cmpi 0,5,11 9,9,0
cmpl 0,5,31 9,9,0 21,30,32 31,31,0
cmpli 0,5,10 9,9,0
cntlzw 0,5,31 16,20,0 21,30,26
crand 0,5,19 21,30,257 31,31,0
crandc 0,5,19 21,30,129 31,31,0
creqv 0,5,19 21,30,289 31,31,0
crnand 0,5,19 21,30,225 31,31,0
crnor 0,5,19 21,30,33 31,31,0
cror 0,5,19 21,30,449 31,31,0
crorc 0,5,19 21,30,417 31,31,0
crxor 0,5,19 21,30,193 31,31,0
dcbf 0,5,31 6,10,0 21,30,86 31,31,0
dcbi 0,5,31 6,10,0 21,30,470 31,31,0
dcbst 0,5,31 6,10,0 21,30,54 31,31,0
dcbt 0,5,31 6,10,0 21,30,278 31,31,0
dcbtst 0,5,31 6,10,0 21,30,246 31,31,0
dcbz 0,5,31 6,10,0 21,30,1014 31,31,0
dcbz_l 0,5,4 6,10,0 21,30,1014 31,31,0
divw 0,5,31 22,30,491
divwu 0,5,31 22,30,459
eciwx 0,5,31 22,30,310 31,31,0
ecowx 0,5,31 22,30,438 31,31,0
eieio 0,5,31 6,20,0 21,30,854 31,31,0
eqv 0,5,31 22,30,284
extsb 0,5,31 16,20,0 21,30,954
extsh 0,5,31 16,20,0 21,30,922
fabs 0,5,63 11,15,0 21,30,922
fadd 0,5,63 21,25,0 26,30,21
fadds 0,5,59 21,25,0 26,30,21
fcmpo 0,5,63 9,10,0 21,30,32 31,31,0
fcmpu 0,5,63 9,10,0 21,30,0 31,31,0
fctiw 0,5,63 11,15,0 21,30,14
fctiwz 0,5,63 11,15,0 21,30,15
fdiv 0,5,63 21,25,0 26,30,18
fdivs 0,5,59 21,25,0 26,30,18
fmadd 0,5,63 26,30,29
fmadds 0,5,59 26,30,29
fmr 0,5,63 11,15,0 21,30,72
fmsub 0,5,63 26,30,28
fmsubs 0,5,59 26,30,28
fmul 0,5,63 16,20,0 26,30,25
fmuls 0,5,59 16,20,0 26,30,25
fnabs 0,5,63 11,15,0 21,30,136
fneg 0,5,63 11,15,0 21,30,40
fnmadd 0,5,63 26,30,31
fnmadds 0,5,59 26,30,31
fnmsub 0,5,63 26,30,30
fnmsubs 0,5,59 26,30,30
fres 0,5,59 11,15,0 21,25,0 26,30,24
frsp 0,5,63 11,15,0 21,30,12
frsqrte 0,5,63 11,15,0 21,25,0 26,30,26
fsel 0,5,63 26,30,23
fsub 0,5,63 21,25,0 26,30,20
fsubs 0,5,59 21,25,0 26,30,20
icbi 0,5,31 6,10,0 21,30,982 31,31,0
isync 0,5,19 6,20,0 21,30,150 31,31,0
lbz 0,5,34
lbzu 0,5,35
lbzux 0,5,31 21,30,119 31,31,0
lbzx 0,5,31 21,30,87 31,31,0
lfd 0,5,50
lfdu 0,5,51
lfdux 0,5,31 21,30,631 31,31,0
lfdx 0,5,31 21,30,559 31,31,0
lfs 0,5,48
lfsu 0,5,49
lfsux 0,5,31 21,30,567 31,31,0
lfsx 0,5,31 21,30,535 31,31,0
lha 0,5,42
lhau 0,5,43
lhaux 0,5,31 21,30,375 31,31,0
lhax 0,5,31 21,30,343 31,31,0
lhbrx 0,5,31 21,30,790 31,31,0
lhz 0,5,40
lhzu 0,5,41
lhzux 0,5,31 21,30,311 31,31,0
lhzx 0,5,31 21,30,279 31,31,0
lmw 0,5,46
lswi 0,5,31 21,30,597 31,31,0
lswx 0,5,31 21,30,533 31,31,0
lwarx 0,5,31 21,30,20 31,31,0
lwbrx 0,5,31 21,30,534 31,31,0
lwz 0,5,32
lwzu 0,5,33
lwzux 0,5,31 21,30,55 31,31,0
lwzx 0,5,31 21,30,23 31,31,0
mcrf 0,5,19 10,11,0 20,31,0
mcrfs 0,5,63 10,11,0 16,24,0 25,30,64 31,31,0
mcrxr 0,5,31 10,11,0 16,24,0 25,30,512 31,31,0
mfcr 0,5,31 11,20,0 21,30,19 31,31,0
mffs 0,5,31 11,20,0 21,30,583
mfmsr 0,5,31 11,20,0 21,30,83 31,31,0
mfspr 0,5,31 21,30,339 31,31,0
mfsr 0,5,31 11,11,0 16,20,0 21,30,595 31,31,0
mfsrin 0,5,31 11,15,0 21,30,659 31,31,0
mftb 0,5,31 21,30,371 31,31,0
mtcrf 0,5,31 11,11,0 20,20,0 21,30,144 31,31,0
mtfsb0 0,5,63 11,20,0 21,30,70
mtfsb1 0,5,63 11,20,0 21,30,38
mtfsf 0,5,63 6,6,0 15,15,0 21,30,711
mtfsfi 0,5,63 9,15,0 20,20,0 21,30,134
mtmsr 0,5,31 11,20,0 21,30,146 31,31,0
mtspr 0,5,31 21,30,467 31,31,0
mtsr 0,5,31 11,11,0 16,20,0 21,30,210 31,31,0
mtsrin 0,5,31 11,15,0 21,30,242 31,31,0
mulhw 0,5,31 21,21,0 22,30,75
mulhwu 0,5,31 21,21,0 22,30,11
mulli 0,5,7
mullw 0,5,31 22,30,235
nand 0,5,31 21,30,476
neg 0,5,31 16,20,0 21,30,104
nor 0,5,31 21,30,124
or 0,5,31 21,30,444
orc 0,5,31 21,30,412
ori 0,5,24
oris 0,5,25
psq_l 0,5,56
psq_lu 0,5,57
psq_lux 0,5,4 25,30,38 31,31,0
psq_lx 0,5,4 25,30,6 31,31,0
psq_st 0,5,60
psq_stu 0,5,61
psq_stux 0,5,4 25,30,39 31,31,0
psq_stx 0,5,4 25,30,7 31,31,0
ps_abs 0,5,4 11,15,0 21,30,264
ps_add 0,5,4 21,25,0 26,30,21
ps_cmpo0 0,5,4 9,10,0 21,30,32 31,31,0
ps_cmpo1 0,5,4 9,10,0 21,30,96 31,31,0
ps_cmpu0 0,5,4 9,10,0 21,30,0 31,31,0
ps_cmpu1 0,5,4 9,10,0 21,30,64 31,31,0
ps_div 0,5,4 21,25,0 26,30,18
ps_madd 0,5,4 26,30,29
ps_madds0 0,5,4 26,30,14
ps_madds1 0,5,4 26,30,15
ps_merge00 0,5,4 21,30,528
ps_merge01 0,5,4 21,30,560
ps_merge10 0,5,4 21,30,592
ps_merge11 0,5,4 21,30,624
ps_mr 0,5,4 11,15,0 21,30,72
ps_msub 0,5,4 26,30,28
ps_mul 0,5,4 16,20,0 26,30,25
ps_muls0 0,5,4 16,20,0 26,30,12
ps_muls1 0,5,4 16,20,0 26,30,13
ps_nabs 0,5,4 11,15,0 21,30,136
ps_neg 0,5,4 11,15,0 21,30,40
ps_nmadd 0,5,4 26,30,31
ps_nmsub 0,5,4 26,30,30
ps_res 0,5,4 11,15,0 21,25,0 26,30,24
ps_rsqrte 0,5,4 11,15,0 21,25,0 26,30,26
ps_sel 0,5,4 26,30,23
ps_sub 0,5,4 21,25,0 26,30,20
ps_sum0 0,5,4 26,30,10
ps_sum1 0,5,4 26,30,11
rfi 0,5,19 6,20,0 31,31,0
rlwimi 0,5,20
rlwinm 0,5,21
rlwnm 0,5,23
sc 0,5,17 6,29,0 30,30,1 31,31,0
slw 0,5,31 21,30,24
sraw 0,5,31 21,30,792
srawi 0,5,31 21,30,824
srw 0,5,31 21,30,536
stb 0,5,38
stbu 0,5,39
stbux 0,5,31 22,30,247 31,31,0
stbx 0,5,31 22,30,215 31,31,0
stfd 0,5,54
stfdu 0,5,55
stfdux 0,5,31 21,30,759 31,31,0
stfdx 0,5,31 21,30,727 31,31,0
stfiwx 0,5,31 21,30,983 31,31,0
stfs 0,5,52
stfsu 0,5,53
stfsux 0,5,31 21,30,695 31,31,0
stfsx 0,5,31 21,30,663 31,31,0
sth 0,5,44
sthbrx 0,5,31 21,30,918 31,31,0
sthu 0,5,45
sthux 0,5,31 21,30,439 31,31,0
sthx 0,5,31 21,30,407 31,31,0
stmw 0,5,47
stswi 0,5,31 21,30,725 31,31,0
stswx 0,5,31 21,30,661 31,31,0
stw 0,5,36
stwbrx 0,5,31 21,30,662 31,31,0
stwcx. 0,5,31 21,30,150 31,31,1
stwu 0,5,37
stwux 0,5,31 21,30,183 31,31,0
stwx 0,5,31 21,30,151 31,31,0
subf 0,5,31 22,30,40
subfc 0,5,31 22,30,8
subfe 0,5,31 22,30,136
subfic 0,5,8
subfme 0,5,31 16,20,0 22,30,232
subfze 0,5,31 16,20,0 22,30,200
sync 0,5,31 6,20,0 21,30,598 31,31,0
tlbie 0,5,31 6,15,0 21,30,306 31,31,0
tlbsync 0,5,31 6,20,0 21,30,566 31,31,0
tw 0,5,31 21,30,4 31,31,0
twi 0,5,3
xor 0,5,31 21,30,316
xori 0,5,26
xoris 0,5,27

View File

@ -1,7 +1,7 @@
[package] [package]
name = "ppc750cl-py" name = "ppc750cl-py"
version = "0.1.1" version = "0.2.0"
edition = "2018" edition = "2021"
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 = "Python bindings for PowerPC 750CL Disassembler" description = "Python bindings for PowerPC 750CL Disassembler"
@ -16,5 +16,5 @@ extension-module = ["pyo3/extension-module"]
default = ["extension-module"] default = ["extension-module"]
[dependencies] [dependencies]
pyo3 = { version = "0.14", features = ["multiple-pymethods"] } pyo3 = { version = "0.16", features = ["multiple-pymethods"] }
ppc750cl = { version = "0.1.1", path = "../disasm" } ppc750cl = { version = "0.2.0", path = "../disasm" }

View File

@ -1,34 +1,11 @@
use pyo3::prelude::*; use pyo3::prelude::*;
use pyo3::types::PyBytes; use pyo3::types::PyBytes;
use pyo3::{PyIterProtocol, PyObjectProtocol};
use ppc750cl::formatter::FormattedIns;
#[pyclass] #[pyclass]
struct Ins(ppc750cl::Ins); struct Ins(ppc750cl::Ins);
macro_rules! ins_ufield {
($name:ident) => {
#[pymethods]
impl Ins {
#[getter]
fn $name(&self) -> PyResult<u32> {
Ok(self.0.$name() as u32)
}
}
};
}
macro_rules! ins_ifield {
($name:ident) => {
#[pymethods]
impl Ins {
#[getter]
fn $name(&self) -> PyResult<i32> {
Ok(self.0.$name() as i32)
}
}
};
}
#[pymethods] #[pymethods]
impl Ins { impl Ins {
#[new] #[new]
@ -45,41 +22,11 @@ impl Ins {
fn addr(&self) -> PyResult<u32> { fn addr(&self) -> PyResult<u32> {
Ok(self.0.addr) Ok(self.0.addr)
} }
}
ins_ufield!(rc); fn __str__(&self) -> String {
ins_ufield!(aa); FormattedIns(self.0.clone()).to_string()
ins_ufield!(lk); }
ins_ufield!(l); }
ins_ufield!(oe);
ins_ufield!(w);
ins_ufield!(s);
ins_ufield!(d);
ins_ufield!(a);
ins_ufield!(b);
ins_ufield!(c);
ins_ufield!(crb_d);
ins_ufield!(crb_a);
ins_ufield!(crb_b);
ins_ufield!(crm);
ins_ufield!(sr);
ins_ufield!(spr);
ins_ufield!(fm);
ins_ufield!(crf_d);
ins_ufield!(crf_s);
ins_ifield!(simm);
ins_ufield!(uimm);
ins_ufield!(bo);
ins_ufield!(bi);
ins_ufield!(sh);
ins_ufield!(mb);
ins_ufield!(me);
ins_ufield!(me_31sub);
ins_ifield!(bd);
ins_ifield!(li);
ins_ufield!(to);
ins_ufield!(ps_l);
ins_ifield!(ps_d);
impl From<ppc750cl::Ins> for Ins { impl From<ppc750cl::Ins> for Ins {
fn from(ins: ppc750cl::Ins) -> Self { fn from(ins: ppc750cl::Ins) -> Self {
@ -87,13 +34,6 @@ impl From<ppc750cl::Ins> for Ins {
} }
} }
#[pyproto]
impl<'a> PyObjectProtocol<'a> for Ins {
fn __str__(&self) -> String {
self.0.to_string()
}
}
#[pyclass] #[pyclass]
struct DisasmIterator { struct DisasmIterator {
bytes: Py<PyBytes>, bytes: Py<PyBytes>,
@ -102,8 +42,8 @@ struct DisasmIterator {
left: usize, left: usize,
} }
#[pyproto] #[pymethods]
impl PyIterProtocol for DisasmIterator { impl DisasmIterator {
fn __iter__(slf: PyRef<Self>) -> PyRef<DisasmIterator> { fn __iter__(slf: PyRef<Self>) -> PyRef<DisasmIterator> {
slf slf
} }

View File

@ -1,7 +1,7 @@
[package] [package]
name = "ppc750cl" name = "ppc750cl"
version = "0.1.1" version = "0.2.0"
edition = "2018" edition = "2021"
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 = "Disassembler for PowerPC 750CL" description = "Disassembler for PowerPC 750CL"
@ -9,5 +9,5 @@ keywords = ["powerpc", "wii", "gamecube"]
repository = "https://github.com/terorie/ppc750cl" repository = "https://github.com/terorie/ppc750cl"
[dependencies] [dependencies]
num-traits = "0.2.14" num-traits = "0.2"
ppc750cl-macros = { path = "../macros", version = "0.1.1" } serde = "1.0"

View File

@ -1,251 +1,33 @@
use std::fmt::{Display, LowerHex, UpperHex}; use std::fmt::{Display, Formatter};
use std::io::Write;
use num_traits::PrimInt; use crate::prelude::*;
use crate::Ins; pub struct FormattedIns(pub Ins);
type IOResult = std::io::Result<()>; impl Display for FormattedIns {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
pub trait AsmFormatter<W> let simple = self.0.clone().simplified();
where write!(f, "{}{}", simple.mnemonic, simple.ins.suffix())?;
W: Write, let mut writing_offset = false;
{ for (i, arg) in simple.args.iter().enumerate() {
/// Returns the underlying writer. if i == 0 {
fn writer(&mut self) -> &mut W; write!(f, " ")?;
/// Callback for custom styling before writing an instruction.
fn before_instruction(&mut self, _: &Ins) -> IOResult {
Ok(())
} }
/// Callback for custom styling after writing an instruction. if i > 0 && !writing_offset {
fn after_instruction(&mut self, _: &Ins) -> IOResult { write!(f, ", ")?;
Ok(())
} }
if let Argument::Offset(val) = arg {
/// Writes the instruction mnemonic. write!(f, "{}(", val)?;
fn write_mnemonic(&mut self, name: &str) -> IOResult { writing_offset = true;
write!(self.writer(), "{}", name) continue;
}
/// Separates the instruction mnemonic and arguments.
fn write_opcode_separator(&mut self) -> IOResult {
write!(self.writer(), " ")
}
/// Separates two instruction arguments (e.g. registers).
fn write_operand_separator(&mut self) -> IOResult {
write!(self.writer(), ", ")
}
/// Writes a general-purpose register argument.
fn write_gpr(&mut self, reg: u8) -> IOResult {
write!(self.writer(), "r{}", reg)
}
/// Writes a nullable general-purpose register argument.
fn write_gpr0(&mut self, reg: u8) -> IOResult {
if reg != 0 {
self.write_gpr(reg)
} else { } else {
write!(self.writer(), "0") write!(f, "{}", arg)?;
} }
if writing_offset {
write!(f, ")")?;
writing_offset = false;
} }
/// Writes a floating point register argument.
fn write_fpr(&mut self, reg: u8) -> IOResult {
write!(self.writer(), "f{}", reg)
}
/// Writes a condition register argument.
fn write_cr(&mut self, reg: u8) -> IOResult {
write!(self.writer(), "cr{}", reg)
}
/// Writes a paired-singles quantization register argument.
fn write_qr(&mut self, reg: u8) -> IOResult {
write!(self.writer(), "qr{}", reg)
}
fn write_sr(&mut self, reg: u8) -> IOResult {
write!(self.writer(), "{}", reg)
}
/// Sets the mnemonic 'o' suffix.
fn write_oe(&mut self, oe: u8) -> IOResult {
if oe != 0 {
write!(self.writer(), "o")?;
} }
Ok(()) Ok(())
} }
/// Sets the mnemonic 'a' suffix.
fn write_aa(&mut self, aa: u8) -> IOResult {
if aa != 0 {
write!(self.writer(), "a")?;
}
Ok(())
}
/// Sets the mnemonic 'l' suffix.
fn write_lk(&mut self, lk: u8) -> IOResult {
if lk != 0 {
write!(self.writer(), "l")?;
}
Ok(())
}
/// Sets the mnemonic '.' suffix.
fn write_rc(&mut self, rc: u8) -> IOResult {
if rc != 0 {
write!(self.writer(), ".")?;
}
Ok(())
}
/// Writes an unsigned immediate.
fn write_uimm<T: Into<u16>>(&mut self, uimm: T) -> IOResult {
let uimm = uimm.into();
if uimm < 16 {
write!(self.writer(), "{}", uimm)
} else {
write!(self.writer(), "{:#x}", uimm)
}
}
/// Writes a signed immediate.
fn write_simm<T: PrimInt + Into<i32> + Display>(&mut self, simm: T) -> IOResult {
let simm: i32 = simm.into();
if simm < 8 {
write!(self.writer(), "{}", simm)
} else {
write!(self.writer(), "{:#x}", ReallySigned(simm))
}
}
/// Writes an instruction-specific field like the compare mode.
fn write_mode<P: PrimInt + Display>(&mut self, mode: P) -> IOResult {
write!(self.writer(), "{}", mode)
}
fn write_fm(&mut self, fm: u16) -> IOResult {
write!(self.writer(), "{}", fm)
}
fn write_offset_unsigned_open<T: Into<u32>>(&mut self, offset: T) -> IOResult {
let offset = offset.into();
if offset < 15 {
write!(self.writer(), "{}(", offset)
} else {
write!(self.writer(), "{:#x}(", offset)
}
}
/// Writes an offset prefix.
///
/// The next write calls that follow should be:
/// - An operand (almost always a general-purpose register)
/// - `write_offset_close()`
fn write_offset_open<T: Into<i32>>(&mut self, offset: T) -> IOResult {
let offset = offset.into();
if -9 < offset && offset < 10 {
write!(self.writer(), "{}(", offset)
} else {
write!(self.writer(), "{:#x}(", ReallySigned(offset))
}
}
/// Closes an offset prefix.
fn write_offset_close(&mut self) -> IOResult {
write!(self.writer(), ")")
}
/// Writes a branch target given the jump offset and current program counter.
fn write_branch_target(&mut self, offset: i32, _: u32) -> IOResult {
write!(self.writer(), "{:#x}", ReallySigned(offset))
}
}
pub struct SimpleFormatter<W: Write> {
pub writer: W,
}
impl<W: Write> SimpleFormatter<W> {
pub fn new(writer: W) -> Self {
Self { writer }
}
}
impl<W: Write> AsmFormatter<W> for SimpleFormatter<W> {
fn writer(&mut self) -> &mut W {
&mut self.writer
}
}
pub struct DoldecompFormatter<W: Write> {
pub writer: W,
}
impl<W: Write> DoldecompFormatter<W> {
pub fn new(writer: W) -> Self {
Self { writer }
}
}
impl<W: Write> AsmFormatter<W> for DoldecompFormatter<W> {
fn writer(&mut self) -> &mut W {
&mut self.writer
}
fn before_instruction(&mut self, ins: &Ins) -> IOResult {
write!(
&mut self.writer,
"/* {:0>8X} {:0>2X} {:0>2X} {:0>2X} {:0>2X} */\t",
ins.addr,
(ins.code >> 24) as u8,
(ins.code >> 16) as u8,
(ins.code >> 8) as u8,
ins.code as u8
)
}
}
// https://stackoverflow.com/questions/44711012/how-do-i-format-a-signed-integer-to-a-sign-aware-hexadecimal-representation
struct ReallySigned<N: PrimInt>(N);
impl<N: PrimInt> LowerHex for ReallySigned<N> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let num = self.0.to_i32().unwrap();
let prefix = if f.alternate() { "0x" } else { "" };
let bare_hex = format!("{:x}", num.abs());
f.pad_integral(num >= 0, prefix, &bare_hex)
}
}
impl<N: PrimInt> UpperHex for ReallySigned<N> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let num = self.0.to_i32().unwrap();
let prefix = if f.alternate() { "0x" } else { "" };
let bare_hex = format!("{:X}", num.abs());
f.pad_integral(num >= 0, prefix, &bare_hex)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_doldecomp_formatter() {
let buf = Vec::<u8>::new();
let mut formatter = DoldecompFormatter::new(buf);
let ins = Ins::new(0x48000007, 6);
ins.write_string(&mut formatter).unwrap();
assert_eq!(
String::from_utf8(formatter.writer).unwrap(),
"/* 00000006 48 00 00 07 */\tbla 0x4"
);
}
} }

5855
disasm/src/generated.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,570 +0,0 @@
#![allow(clippy::bad_bit_mask)]
use crate::{bit, bits};
use ppc750cl_macros::isa;
isa! {
"add" & 0xfc0007fe == 0x7c000214;
//"addc" & 0xfc0007fe == 0x7c00002a;
"addc" & 0x0 == 0x0;
"adde" & 0xfc0007fe == 0x7c000114;
"addi" & 0xfc000000 == 0x38000000;
"addic" & 0xfc000000 == 0x30000000;
"addic." & 0xfc000000 == 0x34000000;
"addis" & 0xfc000000 == 0x3c000000;
"addme" & 0xfc00fbfe == 0x7c0001d4;
"addze" & 0xfc00fbfe == 0x7c000194;
"and" & 0xfc0007fe == 0x7c000038;
"andc" & 0xfc0007fe == 0x7c000078;
"andi." & 0xfc000000 == 0x70000000;
"andis." & 0xfc000000 == 0x74000000;
"b" & 0xfc000000 == 0x48000000;
//"bc" & 0xfc000000 == 0x40000000;
"bc" & 0x0 == 0x0; // TODO
//"bcctr" & 0xfc00ffff == 0x4c000210;
"bcctr" & 0x0 == 0x0; // TODO
"bclr" & 0xfc00fffe == 0x4c000020;
"cmp" & 0xfc4007ff == 0x7c000000;
"cmpi" & 0xfc400000 == 0x2c000000;
"cmpl" & 0xfc4007ff == 0x7c000040;
"cmpli" & 0xfc400000 == 0x28000000;
"cntlzw" & 0xfc00fffe == 0x7c000034;
"crand" & 0xfc0007ff == 0x4c000202;
"crandc" & 0xfc0007ff == 0x4c000102;
"creqv" & 0xfc0007ff == 0x4c000242;
"crnand" & 0xfc0007ff == 0x4c0001c2;
"crnor" & 0xfc0007ff == 0x4c000042;
"cror" & 0xfc0007ff == 0x4c000382;
"crorc" & 0xfc0007ff == 0x4c000342;
"crxor" & 0xfc0007ff == 0x4c000182;
"dcbf" & 0xffe007ff == 0x7c0000ac;
"dcbi" & 0xffe007ff == 0x7c0003ac;
"dcbst" & 0xffe007ff == 0x7c00006c;
"dcbt" & 0xffe007ff == 0x7c00022c;
"dcbtst" & 0xffe007ff == 0x7c0001ec;
"dcbz" & 0xffe007ff == 0x7c0007ec;
"dcbz_l" & 0xffe007ff == 0x100007ec;
"divw" & 0xfc0003fe == 0x7c0003d6;
"divwu" & 0xfc0003fe == 0x7c000396;
"eciwx" & 0xfc0003ff == 0x7c00026c;
"ecowx" & 0xfc0003ff == 0x7c00036c;
"eieio" & 0xffffffff == 0x7c0006ac;
"eqv" & 0xfc0003fe == 0x7c000238;
"extsb" & 0xfc00fffe == 0x7c000774;
"extsh" & 0xfc00fffe == 0x7c000734;
//"fabs" & 0xfc1f07fe == 0xfc000734;
"fabs" & 0x0 == 0x0; // TODO
"fadd" & 0xfc0007fe == 0xfc00002a;
"fadds" & 0xfc0007fe == 0xec00002a;
"fcmpo" & 0xfc6007ff == 0xfc000040;
"fcmpu" & 0xfc6007ff == 0xfc000000;
"fctiw" & 0xfc1f07fe == 0xfc00001c;
"fctiwz" & 0xfc1f07fe == 0xfc00001e;
"fdiv" & 0xfc0007fe == 0xfc000024;
"fdivs" & 0xfc0007fe == 0xec000024;
"fmadd" & 0xfc00003e == 0xfc00003a;
"fmadds" & 0xfc00003e == 0xec00003a;
"fmr" & 0xfc1f07fe == 0xfc000090;
"fmsub" & 0xfc00003e == 0xfc000038;
"fmsubs" & 0xfc00003e == 0xec000038;
"fmul" & 0xfc00f83e == 0xfc000032;
"fmuls" & 0xfc00f83e == 0xec000032;
"fnabs" & 0xfc1f07fe == 0xfc000110;
//"fneg" & 0xfc1f07fe == 0xfc000050;
"fneg" & 0x0 == 0x0; // TODO
"fnmadd" & 0xfc00003e == 0xfc00003e;
"fnmadds" & 0xfc00003e == 0xec00003e;
"fnmsub" & 0xfc00003e == 0xfc00003c;
"fnmsubs" & 0xfc00003e == 0xec00003c;
"fres" & 0xfc1f07fe == 0xec000030;
"frsp" & 0xfc1f07fe == 0xfc000018;
"frsqrte" & 0xfc1f07fe == 0xfc000034;
"fsel" & 0xfc00003e == 0xfc00002e;
"fsub" & 0xfc0007fe == 0xfc000028;
"fsubs" & 0xfc0007fe == 0xec000028;
"icbi" & 0xffe007ff == 0x7c0007ac;
"isync" & 0xffffffff == 0x4c00012c;
"lbz" & 0xfc000000 == 0x88000000;
"lbzu" & 0xfc000000 == 0x8c000000;
"lbzux" & 0xfc0007ff == 0x7c0000ee;
"lbzx" & 0xfc0007ff == 0x7c0000ae;
"lfd" & 0xfc000000 == 0xc8000000;
"lfdu" & 0xfc000000 == 0xcc000000;
"lfdux" & 0xfc0007ff == 0x7c0004ee;
//"lfdx" & 0xfc0007ff == 0x7c00045e;
"lfdx" & 0x0 == 0x0;
"lfs" & 0xfc000000 == 0xc0000000;
"lfsu" & 0xfc000000 == 0xc4000000;
"lfsux" & 0xfc0007ff == 0x7c00046e;
"lfsx" & 0xfc0007ff == 0x7c00042e;
"lha" & 0xfc000000 == 0xa8000000;
"lhau" & 0xfc000000 == 0xac000000;
"lhaux" & 0xfc0007ff == 0x7c0002ee;
"lhax" & 0xfc0007ff == 0x7c0002ae;
"lhbrx" & 0xfc0007ff == 0x7c00062c;
"lhz" & 0xfc000000 == 0xa0000000;
"lhzu" & 0xfc000000 == 0xa4000000;
"lhzux" & 0xfc0007ff == 0x7c00026e;
"lhzx" & 0xfc0007ff == 0x7c00022e;
"lmw" & 0xfc000000 == 0xb8000000;
"lswi" & 0xfc0007ff == 0x7c0004aa;
"lswx" & 0xfc0007ff == 0x7c00042a;
"lwarx" & 0xfc0007ff == 0x7c000028;
"lwbrx" & 0xfc0007ff == 0x7c00042c;
"lwz" & 0xfc000000 == 0x80000000;
"lwzu" & 0xfc000000 == 0x84000000;
"lwzux" & 0xfc0007ff == 0x7c00006e;
"lwzx" & 0xfc0007ff == 0x7c00002e;
"mcrf" & 0xfc300fff == 0x4c000000;
"mcrfs" & 0xfc30ffff == 0xfc000080;
"mcrxr" & 0xfc30ffff == 0x7c000400;
"mfcr" & 0xfc1fffff == 0x7c000026;
//"mffs" & 0xfc1ffffe == 0x7c00048e;
"mffs" & 0x0 == 0x0; // TODO
"mfmsr" & 0xfc1fffff == 0x7c0000a6;
"mfspr" & 0xfc0007ff == 0x7c0002a6;
"mfsr" & 0xfc10ffff == 0x7c0004a6;
"mfsrin" & 0xfc1f07ff == 0x7c000526;
"mftb" & 0xfc0007ff == 0x7c0002e6;
"mtcrf" & 0xfc100fff == 0x7c000120;
"mtfsb0" & 0xfc1ffffe == 0xfc00008c;
"mtfsb1" & 0xfc1ffffe == 0xfc00004c;
"mtfsf" & 0xfe0107fe == 0xfc00058e;
"mtfsfi" & 0xfc7f0ffe == 0xfc00010c;
"mtmsr" & 0xfc1fffff == 0x7c000124;
"mtspr" & 0xfc0007ff == 0x7c0003a6;
"mtsr" & 0xfc10ffff == 0x7c0001a4;
"mtsrin" & 0xfc1f07ff == 0x7c0001e4;
//"mulhw" & 0xfc0007fe == 0x7c000096;
"mulhw" & 0x0 == 0x0;
//"mulhwu" & 0xfc0007fe == 0x7c000016;
"mulhwu" & 0x0 == 0x0;
"mulli" & 0xfc000000 == 0x1c000000;
"mullw" & 0xfc0003fe == 0x7c0001d6;
"nand" & 0xfc0007fe == 0x7c0003b8;
"neg" & 0xfc00fffe == 0x7c0000d0;
"nor" & 0xfc0007fe == 0x7c0000f8;
"or" & 0xfc0007fe == 0x7c000378;
"orc" & 0xfc0007fe == 0x7c000338;
"ori" & 0xfc000000 == 0x60000000;
"oris" & 0xfc000000 == 0x64000000;
"psq_l" & 0xfc000000 == 0xe0000000;
"psq_lu" & 0xfc000000 == 0xe4000000;
"psq_lux" & 0xfc00007f == 0x1000004c;
"psq_lx" & 0xfc00007f == 0x1000000c;
"psq_st" & 0xfc000000 == 0xf0000000;
"psq_stu" & 0xfc000000 == 0xf4000000;
"psq_stux" & 0xfc00007f == 0x1000004e;
"psq_stx" & 0xfc00007f == 0x1000000e;
"ps_abs" & 0xfc1f07fe == 0x10000210;
"ps_add" & 0xfc0007fe == 0x1000002a;
"ps_cmpo0" & 0xfc6007ff == 0x10000040;
"ps_cmpo1" & 0xfc6007ff == 0x100000c0;
"ps_cmpu0" & 0xfc6007ff == 0x10000000;
"ps_cmpu1" & 0xfc6007ff == 0x10000080;
"ps_div" & 0xfc0007fe == 0x10000024;
"ps_madd" & 0xfc00003e == 0x1000003a;
"ps_madds0" & 0xfc00003e == 0x1000001c;
"ps_madds1" & 0xfc00003e == 0x1000001e;
"ps_merge00" & 0xfc0007fe == 0x10000420;
"ps_merge01" & 0xfc0007fe == 0x10000460;
"ps_merge10" & 0xfc0007fe == 0x100004a0;
"ps_merge11" & 0xfc0007fe == 0x100004e0;
"ps_mr" & 0xfc1f07fe == 0x10000090;
"ps_msub" & 0xfc00003e == 0x10000038;
"ps_mul" & 0xfc00f83e == 0x10000032;
"ps_muls0" & 0xfc00f83e == 0x10000018;
"ps_muls1" & 0xfc00f83e == 0x1000001a;
"ps_nabs" & 0xfc1f07fe == 0x10000110;
"ps_neg" & 0xfc1f07fe == 0x10000050;
"ps_nmadd" & 0xfc00003e == 0x1000003e;
"ps_nmsub" & 0xfc00003e == 0x1000003c;
"ps_res" & 0xfc1f07fe == 0x10000030;
"ps_rsqrte" & 0xfc1f07fe == 0x10000034;
"ps_sel" & 0xfc00003e == 0x1000002e;
"ps_sub" & 0xfc0007fe == 0x10000028;
"ps_sum0" & 0xfc00003e == 0x10000014;
"ps_sum1" & 0xfc00003e == 0x10000016;
"rfi" & 0xfffff801 == 0x4c000000;
"rlwimi" & 0xfc000000 == 0x50000000;
"rlwinm" & 0xfc000000 == 0x54000000;
"rlwnm" & 0xfc000000 == 0x5c000000;
"sc" & 0xffffffff == 0x44000002;
"slw" & 0xfc0007fe == 0x7c000030;
"sraw" & 0xfc0007fe == 0x7c000630;
"srawi" & 0xfc0007fe == 0x7c000670;
"srw" & 0xfc0007fe == 0x7c000430;
"stb" & 0xfc000000 == 0x98000000;
"stbu" & 0xfc000000 == 0x9c000000;
"stbux" & 0xfc0003ff == 0x7c0001ee;
"stbx" & 0xfc0003ff == 0x7c0001ae;
"stfd" & 0xfc000000 == 0xd8000000;
"stfdu" & 0xfc000000 == 0xdc000000;
"stfdux" & 0xfc0007ff == 0x7c0005ee;
"stfdx" & 0xfc0007ff == 0x7c0005ae;
"stfiwx" & 0xfc0007ff == 0x7c0007ae;
"stfs" & 0xfc000000 == 0xd0000000;
"stfsu" & 0xfc000000 == 0xd4000000;
"stfsux" & 0xfc0007ff == 0x7c00056e;
"stfsx" & 0xfc0007ff == 0x7c00052e;
"sth" & 0xfc000000 == 0xb0000000;
"sthbrx" & 0xfc0007ff == 0x7c00072c;
"sthu" & 0xfc000000 == 0xb4000000;
"sthux" & 0xfc0007ff == 0x7c00036e;
"sthx" & 0xfc0007ff == 0x7c00032e;
"stmw" & 0xfc000000 == 0xbc000000;
"stswi" & 0xfc0007ff == 0x7c0005aa;
"stswx" & 0xfc0007ff == 0x7c00052a;
"stw" & 0xfc000000 == 0x90000000;
"stwbrx" & 0xfc0007ff == 0x7c00052c;
"stwcx." & 0xfc0007ff == 0x7c00012d;
"stwu" & 0xfc000000 == 0x94000000;
"stwux" & 0xfc0007ff == 0x7c00016e;
"stwx" & 0xfc0007ff == 0x7c00012e;
"subf" & 0xfc0003fe == 0x7c000050;
"subfc" & 0xfc0003fe == 0x7c000010;
"subfe" & 0xfc0003fe == 0x7c000110;
"subfic" & 0xfc000000 == 0x20000000;
"subfme" & 0xfc00fbfe == 0x7c0001d0;
"subfze" & 0xfc00fbfe == 0x7c000190;
"sync" & 0xffffffff == 0x7c0004ac;
"tlbie" & 0xffff07ff == 0x7c000264;
"tlbsync" & 0xffffffff == 0x7c00046c;
"tw" & 0xfc0007ff == 0x7c000008;
"twi" & 0xfc000000 == 0xc000000;
"xor" & 0xfc0007fe == 0x7c000278;
"xori" & 0xfc000000 == 0x68000000;
"xoris" & 0xfc000000 == 0x6c000000;
}
impl Opcode {
pub const BLR: u32 = 0x4c000020;
pub fn from_code(x: u32) -> Self {
let op = match bits(x, 0..6) {
0b000011 => Opcode::Twi,
0b000100 => Self::from_code_cl_ext(x),
0b000111..=0b001111 => Self::from_code_basic1(x),
0b010000 => Opcode::Bc,
0b010001 => Opcode::Sc,
0b010010 => Opcode::B,
0b010011 => Self::from_code_010011(x),
0b010100..=0b011101 => Self::from_code_basic2(x),
0b011111 => Self::from_code_011111(x),
0b100000..=0b110111 => Self::from_code_basic3(x),
0b111000..=0b111001 => Self::from_code_psq(x),
0b111011 => Self::from_code_111011(x),
0b111100..=0b111101 => Self::from_code_psq(x),
0b111111 => Self::from_code_111111(x),
_ => Opcode::Illegal,
};
if !op.is_valid(x) {
return Opcode::Illegal;
}
op
}
fn from_code_cl_ext(x: u32) -> Self {
match bits(x, 26..31) {
0b00000 => match bits(x, 21..26) {
0b00000 => Opcode::PsCmpu0,
0b00001 => Opcode::PsCmpo0,
0b00010 => Opcode::PsCmpu1,
0b00011 => Opcode::PsCmpo1,
_ => Opcode::Illegal,
},
0b00110 => {
if bit(x, 25) == 0 {
Opcode::PsqLx
} else {
Opcode::PsqLux
}
}
0b00111 => {
if bit(x, 25) == 0 {
Opcode::PsqStx
} else {
Opcode::PsqStux
}
}
0b01010 => Opcode::PsSum0,
0b01011 => Opcode::PsSum1,
0b01110 => Opcode::PsMadds0,
0b01111 => Opcode::PsMadds1,
0b10111 => Opcode::PsSel,
0b11100 => Opcode::PsMsub,
0b11101 => Opcode::PsMadd,
0b11110 => Opcode::PsNmsub,
0b11111 => Opcode::PsNmadd,
0b01100 => Opcode::PsMuls0,
0b01101 => Opcode::PsMuls1,
0b11001 => Opcode::PsMul,
0b10010 => Opcode::PsDiv,
0b10100 => Opcode::PsSub,
0b10101 => Opcode::PsAdd,
0b11000 => Opcode::PsRes,
0b11010 => Opcode::PsRsqrte,
0b01000 => match bits(x, 21..26) {
0b00001 => Opcode::PsNeg,
0b00010 => Opcode::PsMr,
0b00100 => Opcode::PsNabs,
0b01000 => Opcode::PsAbs,
_ => Opcode::Illegal,
},
0b10000 => match bits(x, 21..26) {
0b10000 => Opcode::PsMerge00,
0b10001 => Opcode::PsMerge01,
0b10010 => Opcode::PsMerge10,
0b10011 => Opcode::PsMerge11,
_ => Opcode::Illegal,
},
0b10110 => Opcode::DcbzL,
// Unknown paired-singles key.
_ => Opcode::Illegal,
}
}
fn from_code_basic1(x: u32) -> Self {
match bits(x, 0..6) {
0b000111 => Opcode::Mulli,
0b001000 => Opcode::Subfic,
0b001010 => Opcode::Cmpli,
0b001011 => Opcode::Cmpi,
0b001100 => Opcode::Addic,
0b001101 => Opcode::Addic_,
0b001110 => Opcode::Addi,
0b001111 => Opcode::Addis,
_ => Opcode::Illegal,
}
}
fn from_code_010011(x: u32) -> Self {
match bits(x, 21..27) {
0b000000 => Opcode::Mcrf,
0b000001 => Opcode::Bclr,
0b100001 => Opcode::Bcctr,
0b000011 => Opcode::Rfi,
0b001001 => Opcode::Isync,
0b000010 => Opcode::Crnor,
0b001000 => Opcode::Crandc,
0b001100 => Opcode::Crxor,
0b001110 => Opcode::Crnand,
0b010000 => Opcode::Crand,
0b010010 => Opcode::Creqv,
0b011010 => Opcode::Crorc,
0b011100 => Opcode::Cror,
_ => Opcode::Illegal,
}
}
fn from_code_basic2(x: u32) -> Self {
match bits(x, 0..6) {
0b10100 => Opcode::Rlwimi,
0b10101 => Opcode::Rlwinm,
0b10111 => Opcode::Rlwnm,
0b11000 => Opcode::Ori,
0b11001 => Opcode::Oris,
0b11010 => Opcode::Xori,
0b11011 => Opcode::Xoris,
0b11100 => Opcode::Andi_,
0b11101 => Opcode::Andis_,
_ => Opcode::Illegal,
}
}
fn from_code_011111(x: u32) -> Self {
match bits::<u32>(x, 21..31) {
0b00_0000_0000 => Opcode::Cmp,
0b00_0010_0000 => Opcode::Cmpl,
0b00_0000_0100 => Opcode::Tw,
0b00_0000_1000 => Opcode::Subfc,
0b00_0000_1010 => Opcode::Addc,
0b00_0000_1011 => Opcode::Mulhwu,
0b00_0001_0011 => Opcode::Mfcr,
0b00_0001_0100 => Opcode::Lwarx,
0b00_0001_0111 => Opcode::Lwzx,
0b00_0001_1000 => Opcode::Slw,
0b00_0001_1010 => Opcode::Cntlzw,
0b00_0001_1100 => Opcode::And,
0b00_0010_1000 => Opcode::Subf,
0b00_0011_0110 => Opcode::Dcbst,
0b00_0011_0111 => Opcode::Lwzux,
0b00_0011_1100 => Opcode::Andc,
0b00_0100_1011 => Opcode::Mulhw,
0b00_0101_0011 => Opcode::Mfmsr,
0b00_0101_0110 => Opcode::Dcbf,
0b00_0101_0111 => Opcode::Lbzx,
0b00_0110_1000 => Opcode::Neg,
0b00_0111_0111 => Opcode::Lbzux,
0b00_0111_1100 => Opcode::Nor,
0b00_1000_1000 => Opcode::Subfe,
0b00_1000_1010 => Opcode::Adde,
0b00_1001_0000 => Opcode::Mtcrf,
0b00_1001_0010 => Opcode::Mtmsr,
0b00_1001_0110 => Opcode::Stwcx_,
0b00_1001_0111 => Opcode::Stwx,
0b00_1011_0111 => Opcode::Stwux,
0b00_1100_1000 => Opcode::Subfze,
0b00_1100_1010 => Opcode::Addze,
0b00_1101_0010 => Opcode::Mtsr,
0b00_1101_0111 => Opcode::Stbx,
0b00_1110_1000 => Opcode::Subfme,
0b00_1110_1010 => Opcode::Addme,
0b00_1110_1011 => Opcode::Mullw,
0b00_1111_0010 => Opcode::Mtsrin,
0b00_1111_0110 => Opcode::Dcbtst,
0b00_1111_0111 => Opcode::Stbux,
0b01_0000_1010 => Opcode::Add,
0b01_0001_0110 => Opcode::Dcbt,
0b01_0001_0111 => Opcode::Lhzx,
0b01_0001_1100 => Opcode::Eqv,
0b01_0011_0010 => Opcode::Tlbie,
0b01_0011_0110 => Opcode::Eciwx,
0b01_0011_0111 => Opcode::Lhzux,
0b01_0011_1100 => Opcode::Xor,
0b01_0101_0011 => Opcode::Mfspr,
0b01_0101_0111 => Opcode::Lhax,
0b01_0111_0011 => Opcode::Mftb,
0b01_0111_0111 => Opcode::Lhaux,
0b01_1001_0111 => Opcode::Sthx,
0b01_1001_1100 => Opcode::Orc,
0b01_1011_0110 => Opcode::Ecowx,
0b01_1011_0111 => Opcode::Sthux,
0b01_1011_1100 => Opcode::Or,
0b01_1100_1011 => Opcode::Divwu,
0b01_1101_0011 => Opcode::Mtspr,
0b01_1101_0110 => Opcode::Dcbi,
0b01_1101_1100 => Opcode::Nand,
0b01_1110_1011 => Opcode::Divw,
0b10_0000_0000 => Opcode::Mcrxr,
0b10_0001_0101 => Opcode::Lswx,
0b10_0001_0110 => Opcode::Lwbrx,
0b10_0001_0111 => Opcode::Lfsx,
0b10_0001_1000 => Opcode::Srw,
0b10_0011_0110 => Opcode::Tlbsync,
0b10_0011_0111 => Opcode::Lfsux,
0b10_0101_0011 => Opcode::Mfsr,
0b10_0101_0101 => Opcode::Lswi,
0b10_0101_0110 => Opcode::Sync,
0b10_0101_0111 => Opcode::Lfdx,
0b10_0111_0111 => Opcode::Lfdux,
0b10_1001_0011 => Opcode::Mfsrin,
0b10_1001_0101 => Opcode::Stswx,
0b10_1001_0110 => Opcode::Stwbrx,
0b10_1001_0111 => Opcode::Stfsx,
0b10_1011_0111 => Opcode::Stfsux,
0b10_1101_0101 => Opcode::Stswi,
0b10_1101_0111 => Opcode::Stfdx,
0b10_1111_0111 => Opcode::Stfdux,
0b11_0001_0110 => Opcode::Lhbrx,
0b11_0001_1000 => Opcode::Sraw,
0b11_0011_1000 => Opcode::Srawi,
0b11_0101_0110 => Opcode::Eieio,
0b11_1001_0110 => Opcode::Sthbrx,
0b11_1001_1010 => Opcode::Extsh,
0b11_1011_1010 => Opcode::Extsb,
0b11_1101_0110 => Opcode::Icbi,
0b11_1101_0111 => Opcode::Stfiwx,
0b11_1111_0110 => Opcode::Dcbz,
_ => Opcode::Illegal,
}
}
fn from_code_basic3(x: u32) -> Self {
match bits(x, 0..6) {
0b100000 => Opcode::Lwz,
0b100001 => Opcode::Lwzu,
0b100010 => Opcode::Lbz,
0b100011 => Opcode::Lbzu,
0b100100 => Opcode::Stw,
0b100101 => Opcode::Stwu,
0b100110 => Opcode::Stb,
0b100111 => Opcode::Stbu,
0b101000 => Opcode::Lhz,
0b101001 => Opcode::Lhzu,
0b101010 => Opcode::Lha,
0b101011 => Opcode::Lhau,
0b101100 => Opcode::Sth,
0b101101 => Opcode::Sthu,
0b101110 => Opcode::Lmw,
0b101111 => Opcode::Stmw,
0b110000 => Opcode::Lfs,
0b110001 => Opcode::Lfsu,
0b110010 => Opcode::Lfd,
0b110011 => Opcode::Lfdu,
0b110100 => Opcode::Stfs,
0b110101 => Opcode::Stfsu,
0b110110 => Opcode::Stfd,
0b110111 => Opcode::Stfdu,
_ => disasm_unreachable!(x),
}
}
fn from_code_psq(x: u32) -> Self {
match bits(x, 0..6) {
0b111000 => Opcode::PsqL,
0b111001 => Opcode::PsqLu,
0b111100 => Opcode::PsqSt,
0b111101 => Opcode::PsqStu,
_ => disasm_unreachable!(x),
}
}
fn from_code_111011(x: u32) -> Self {
match bits(x, 26..31) {
0b10010 => Opcode::Fdivs,
0b10100 => Opcode::Fsubs,
0b10101 => Opcode::Fadds,
0b11000 => Opcode::Fres,
0b11001 => Opcode::Fmuls,
0b11100 => Opcode::Fmsubs,
0b11101 => Opcode::Fmadds,
0b11110 => Opcode::Fnmsubs,
0b11111 => Opcode::Fnmadds,
_ => Opcode::Illegal,
}
}
fn from_code_111111(x: u32) -> Self {
match bits::<u32>(x, 26..31) {
0b00000 => match bits(x, 24..26) {
0b00 => Opcode::Fcmpu,
0b01 => Opcode::Fcmpo,
0b10 => Opcode::Mcrfs,
_ => Opcode::Illegal,
},
0b00110 => match bits(x, 23..26) {
0b001 => Opcode::Mtfsb1,
0b010 => Opcode::Mtfsb0,
0b100 => Opcode::Mtfsfi,
_ => Opcode::Illegal,
},
0b00111 => match bits(x, 21..26) {
0b10010 => Opcode::Mffs,
0b10110 => Opcode::Mtfsf,
_ => Opcode::Illegal,
},
0b01000 => match bits(x, 22..26) {
0b0001 => Opcode::Fneg,
0b0010 => Opcode::Fmr,
0b0100 => Opcode::Fnabs,
0b1000 => Opcode::Fabs,
_ => Opcode::Illegal,
},
0b01100 => Opcode::Frsp,
0b01110 => Opcode::Fctiw,
0b01111 => Opcode::Fctiwz,
0b10010 => Opcode::Fdiv,
0b10100 => Opcode::Fsub,
0b10101 => Opcode::Fadd,
0b10111 => Opcode::Fsel,
0b11001 => Opcode::Fmul,
0b11010 => Opcode::Frsqrte,
0b11100 => Opcode::Fmsub,
0b11101 => Opcode::Fmadd,
0b11110 => Opcode::Fnmsub,
0b11111 => Opcode::Fnmadd,
_ => Opcode::Illegal,
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +0,0 @@
macro_rules! disasm_unreachable {
($msg:expr $(,)?) => {{
panic!(
"internal error: entered unreachable code disassembling instruction 0x{:08x}",
$msg
)
}};
}

980
disasm/tests/test_disasm.rs Normal file
View File

@ -0,0 +1,980 @@
use ppc750cl::prelude::*;
macro_rules! assert_asm {
($ins:ident, $disasm:literal) => {{
assert_eq!(format!("{}", FormattedIns($ins)), $disasm)
}};
($code:literal, $disasm:literal) => {{
let ins = Ins::new($code, 0x8000_0000);
assert_eq!(format!("{}", FormattedIns(ins)), $disasm)
}};
}
#[test]
fn test_ins_addc() {
let ins = Ins::new(0x7c002014, 0x8000_0000u32);
assert_eq!(ins.op, Addc);
assert_eq!(ins.fields(), vec![rD(GPR(0)), rA(GPR(0)), rB(GPR(4))]);
assert_asm!(ins, "addc r0, r0, r4");
}
#[test]
fn test_ins_addi() {
let ins = Ins::new(0x38010140, 0x8000_0000u32);
assert_eq!(ins.op, Addi);
assert_eq!(
ins.fields(),
vec![rD(GPR(0)), rA(GPR(1)), simm(Simm(0x140))]
);
assert_eq!(ins.defs(), vec![rD(GPR(0))]);
assert_eq!(ins.uses(), vec![rA(GPR(1))]);
assert_asm!(ins, "addi r0, r1, 0x140");
assert_asm!(0x38010008, "addi r0, r1, 0x8");
assert_asm!(0x38010010, "addi r0, r1, 0x10");
assert_asm!(0x38010018, "addi r0, r1, 0x18");
assert_asm!(0x38010140, "addi r0, r1, 0x140");
assert_asm!(0x38049000, "addi r0, r4, -0x7000");
assert_asm!(0x38a00000, "li r5, 0x0");
}
#[test]
fn test_ins_adde() {
assert_asm!(0x7c006114, "adde r0, r0, r12");
}
#[test]
fn test_ins_addic() {
assert_asm!(0x3060ffff, "addic r3, r0, -0x1");
assert_asm!(0x30840800, "addic r4, r4, 0x800");
assert_asm!(0x30a50008, "addic r5, r5, 0x8");
assert_asm!(0x37DF001C, "addic. r30, r31, 0x1c");
assert_asm!(0x37E06278, "addic. r31, r0, 0x6278");
assert_asm!(0x37E3FFFF, "addic. r31, r3, -0x1");
}
#[test]
fn test_ins_addis() {
assert_asm!(0x3C030000, "addis r0, r3, 0x0");
assert_asm!(0x3C038000, "addis r0, r3, 0x8000");
assert_asm!(0x3D00EFCE, "lis r8, 0xefce");
}
#[test]
fn test_ins_addze() {
assert_asm!(0x7C000194, "addze r0, r0");
}
#[test]
fn test_ins_and() {
assert_asm!(0x7C001838, "and r0, r0, r3");
assert_asm!(0x7C001839, "and. r0, r0, r3");
}
#[test]
fn test_ins_andc() {
assert_asm!(0x7C001878, "andc r0, r0, r3");
}
#[test]
fn test_ins_andi_() {
assert_asm!(0x70000009, "andi. r0, r0, 0x9");
}
#[test]
fn test_ins_andis_() {
assert_asm!(0x77c802ff, "andis. r8, r30, 0x2ff");
}
#[test]
fn test_ins_b() {
assert_asm!(0x48000000, "b 0x0");
assert_asm!(0x48000004, "b 0x4");
assert_asm!(0x4800A5C9, "bl 0xa5c8");
assert_asm!(0x4823B4D9, "bl 0x23b4d8");
assert_asm!(0x4BE03C99, "bl -0x1fc368");
assert_asm!(0x4BDC1A59, "bl -0x23e5a8");
}
#[test]
fn test_ins_bc() {
assert_asm!(0x40800008, "bge 0x8");
assert_asm!(0x40802350, "bge 0x2350");
assert_asm!(0x4080FC7C, "bge -0x384");
assert_asm!(0x408100AC, "ble 0xac");
assert_asm!(0x4081F788, "ble -0x878");
assert_asm!(0x40821BA0, "bne 0x1ba0");
assert_asm!(0x4082E3C4, "bne -0x1c3c");
assert_asm!(0x408600D8, "bne cr1, 0xd8");
assert_asm!(0x4086FECC, "bne cr1, -0x134");
assert_asm!(0x409C000C, "bge cr7, 0xc");
assert_asm!(0x4180000C, "blt 0xc");
assert_asm!(0x4180F9C0, "blt -0x640");
assert_asm!(0x4181021C, "bgt 0x21c");
assert_asm!(0x4181FD80, "bgt -0x280");
assert_asm!(0x41822304, "beq 0x2304");
assert_asm!(0x4182FE3C, "beq -0x1c4");
assert_asm!(0x418401AC, "blt cr1, 0x1ac");
assert_asm!(0x4184FCE4, "blt cr1, -0x31c");
assert_asm!(0x418500C0, "bgt cr1, 0xc0");
assert_asm!(0x418502E4, "bgt cr1, 0x2e4");
assert_asm!(0x419A0138, "beq cr6, 0x138");
assert_asm!(0x419C0008, "blt cr7, 0x8");
assert_asm!(0x4200F560, "bdnz -0xaa0");
}
#[test]
fn test_ins_bcctr() {
assert_asm!(0x4E800420, "bctr");
assert_asm!(0x4E800421, "bctrl");
}
#[test]
fn test_ins_bclr() {
assert_asm!(0x4C800020, "bgelr");
assert_asm!(0x4C810020, "blelr");
assert_asm!(0x4C820020, "bnelr");
assert_asm!(0x4C9E0020, "bnelr cr7");
assert_asm!(0x4D800020, "bltlr");
assert_asm!(0x4D810020, "bgtlr");
assert_asm!(0x4D820020, "beqlr");
assert_asm!(0x4D860020, "beqlr cr1");
assert_asm!(0x4E800020, "blr");
assert_asm!(0x4E800021, "blrl");
}
#[test]
fn test_ins_cmp() {
assert_asm!(0x7C030000, "cmpw r3, r0");
}
/*
#[test]
fn test_ins_cmpi() {
assert_asm!(0x2C050D00, "cmpwi r5, 0xd00");
assert_asm!(0x2F1F0000, "cmpwi cr6, r31, 0");
}
#[test]
fn test_ins_cmpl() {
assert_asm!(0x7C9A2040, "cmplw cr1, r26, r4");
}
#[test]
fn test_ins_cmpli() {
assert_asm!(0x2803FFF3, "cmplwi r3, 0xfff3");
assert_asm!(0x2884F8F0, "cmplwi cr1, r4, 0xf8f0");
}
*/
#[test]
fn test_ins_cntlzw() {
assert_asm!(0x7C030034, "cntlzw r3, r0");
}
/*
#[test]
fn test_ins_cror() {
assert_asm!(0x4C411382, "cror cr2, cr1, cr2");
}
*/
#[test]
fn test_ins_dcbf() {
assert_asm!(0x7C0028AC, "dcbf r0, r5");
}
#[test]
fn test_ins_dcbi() {
assert_asm!(0x7C001BAC, "dcbi r0, r3");
}
#[test]
fn test_ins_dcbst() {
assert_asm!(0x7C00286C, "dcbst r0, r5");
}
#[test]
fn test_ins_dcbt() {
assert_asm!(0x7C001A2C, "dcbt r0, r3");
}
#[test]
fn test_ins_dcbz() {
assert_asm!(0x7C001FEC, "dcbz r0, r3");
}
#[test]
fn test_ins_dcbz_l() {
assert_asm!(0x10061FEC, "dcbz_l r6, r3");
}
#[test]
fn test_ins_divw() {
assert_asm!(0x7C8073D6, "divw r4, r0, r14");
}
#[test]
fn test_ins_divwu() {
assert_asm!(0x7C69E396, "divwu r3, r9, r28");
}
#[test]
fn test_ins_extsb() {
assert_asm!(0x7C650774, "extsb r5, r3");
assert_asm!(0x7C650775, "extsb. r5, r3");
}
#[test]
fn test_ins_extsh() {
assert_asm!(0x7C000734, "extsh r0, r0");
assert_asm!(0x7C000735, "extsh. r0, r0");
}
#[test]
fn test_ins_fabs() {
assert_asm!(0xFC000A10, "fabs f0, f1");
}
#[test]
fn test_ins_fadd() {
assert_asm!(0xFC00282A, "fadd f0, f0, f5");
}
#[test]
fn test_ins_fadds() {
assert_asm!(0xEC41602A, "fadds f2, f1, f12");
}
#[test]
fn test_ins_fcmpo() {
assert_asm!(0xFC00C840, "fcmpo cr0, f0, f25");
}
#[test]
fn test_ins_fcmpu() {
assert_asm!(0xFC00D000, "fcmpu cr0, f0, f26");
}
#[test]
fn test_ins_fctiwz() {
assert_asm!(0xFC20001E, "fctiwz f1, f0");
}
#[test]
fn test_ins_fdiv() {
assert_asm!(0xFC200024, "fdiv f1, f0, f0");
}
#[test]
fn test_ins_fdivs() {
assert_asm!(0xEC01F824, "fdivs f0, f1, f31");
}
#[test]
fn test_ins_fmadds() {
assert_asm!(0xEC0200FA, "fmadds f0, f2, f3, f0");
}
#[test]
fn test_ins_fmsub() {
assert_asm!(0xFC000028, "fsub f0, f0, f0");
}
#[test]
fn test_ins_fmsubs() {
assert_asm!(0xEC00B828, "fsubs f0, f0, f23");
}
#[test]
fn test_ins_fmul() {
assert_asm!(0xFC0000B2, "fmul f0, f0, f2");
assert_asm!(0xFC0000F2, "fmul f0, f0, f3");
}
#[test]
fn test_ins_fmuls() {
assert_asm!(0xEC0007B2, "fmuls f0, f0, f30");
}
#[test]
fn test_ins_fneg() {
assert_asm!(0xFCE00050, "fneg f7, f0");
}
#[test]
fn test_ins_fnmsub() {
assert_asm!(0xFCC640BC, "fnmsub f6, f6, f2, f8");
}
#[test]
fn test_ins_fnmsubs() {
assert_asm!(0xEC022B3C, "fnmsubs f0, f2, f12, f5");
}
#[test]
fn test_ins_fres() {
assert_asm!(0xEC000830, "fres f0, f1");
}
#[test]
fn test_ins_frsp() {
assert_asm!(0xFC000018, "frsp f0, f0");
}
#[test]
fn test_ins_frsqrte() {
assert_asm!(0xFC000834, "frsqrte f0, f1");
}
#[test]
fn test_ins_fsel() {
assert_asm!(0xFC01F82E, "fsel f0, f1, f0, f31");
}
#[test]
fn test_ins_fsub() {
assert_asm!(0xFC000828, "fsub f0, f0, f1");
}
#[test]
fn test_ins_fsubs() {
assert_asm!(0xEC000828, "fsubs f0, f0, f1");
}
#[test]
fn test_ins_icbi() {
assert_asm!(0x7C001FAC, "icbi r0, r3");
}
#[test]
fn test_ins_isync() {
assert_asm!(0x4C00012C, "isync");
}
#[test]
fn test_ins_lbz() {
assert_asm!(0x880104CC, "lbz r0, 0x4cc(r1)");
assert_asm!(0x8802801B, "lbz r0, -0x7fe5(r2)");
}
#[test]
fn test_ins_lbzu() {
assert_asm!(0x8D9DCA10, "lbzu r12, -0x35f0(r29)");
assert_asm!(0x8E3053EC, "lbzu r17, 0x53ec(r16)");
}
#[test]
fn test_ins_lbzux() {
assert_asm!(0x7C0400EE, "lbzux r0, r4, r0");
}
#[test]
fn test_ins_lbzx() {
assert_asm!(0x7C0300AE, "lbzx r0, r3, r0");
}
#[test]
fn test_ins_lfd() {
assert_asm!(0xC80140C8, "lfd f0, 0x40c8(r1)");
assert_asm!(0xC8028090, "lfd f0, -0x7f70(r2)");
}
#[test]
fn test_ins_lfdu() {
assert_asm!(0xCC03FFC0, "lfdu f0, -0x40(r3)");
}
#[test]
fn test_ins_lfdx() {
assert_asm!(0x7C0404AE, "lfdx f0, r4, r0");
}
#[test]
fn test_ins_lfs() {
assert_asm!(0xC001027C, "lfs f0, 0x27c(r1)");
assert_asm!(0xC0028000, "lfs f0, -0x8000(r2)");
}
#[test]
fn test_ins_lfsu() {
assert_asm!(0xC404FFF4, "lfsu f0, -0xc(r4)");
assert_asm!(0xC4170084, "lfsu f0, 0x84(r23)");
}
#[test]
fn test_ins_lfsux() {
assert_asm!(0x7C03846E, "lfsux f0, r3, r16");
}
#[test]
fn test_ins_lfsx() {
assert_asm!(0x7C03042E, "lfsx f0, r3, r0");
}
#[test]
fn test_ins_lha() {
assert_asm!(0xA861000E, "lha r3, 0xe(r1)");
assert_asm!(0xA80D9F64, "lha r0, -0x609c(r13)");
}
#[test]
fn test_ins_lhau() {
assert_asm!(0xAC060006, "lhau r0, 0x6(r6)");
assert_asm!(0xAC06FFFA, "lhau r0, -0x6(r6)");
}
#[test]
fn test_ins_lhax() {
assert_asm!(0x7C0402AE, "lhax r0, r4, r0");
}
#[test]
fn test_ins_lhz() {
assert_asm!(0xA00104D6, "lhz r0, 0x4d6(r1)");
assert_asm!(0xA00296DA, "lhz r0, -0x6926(r2)");
}
#[test]
fn test_ins_lhzu() {
assert_asm!(0xA40A0004, "lhzu r0, 0x4(r10)");
}
#[test]
fn test_ins_lhzux() {
assert_asm!(0x7C04026E, "lhzux r0, r4, r0");
}
#[test]
fn test_ins_lhzx() {
assert_asm!(0x7C03022E, "lhzx r0, r3, r0");
}
#[test]
fn test_ins_lmw() {
assert_asm!(0xBB210444, "lmw r25, 0x444(r1)");
}
#[test]
fn test_ins_lwbrx() {
assert_asm!(0x7D80242C, "lwbrx r12, r0, r4");
}
#[test]
fn test_ins_lwz() {
assert_asm!(0x800294F4, "lwz r0, -0x6b0c(r2)");
assert_asm!(0x80011254, "lwz r0, 0x1254(r1)");
}
#[test]
fn test_ins_lwzu() {
assert_asm!(0x84038608, "lwzu r0, -0x79f8(r3)");
assert_asm!(0x873E5058, "lwzu r25, 0x5058(r30)");
}
#[test]
fn test_ins_lwzux() {
assert_asm!(0x7C03006E, "lwzux r0, r3, r0");
}
#[test]
fn test_ins_lwzx() {
assert_asm!(0x7C03002E, "lwzx r0, r3, r0");
}
#[test]
fn test_ins_mfcr() {
assert_asm!(0x7C000026, "mfcr cr0");
}
/*
#[test]
fn test_ins_mffs() {
assert_asm!(0xFC00048E, "mffs f0");
}
*/
#[test]
fn test_ins_mfmsr() {
assert_asm!(0x7C0000A6, "mfmsr r0");
}
#[test]
fn test_ins_mfspr() {
assert_asm!(0x7E1A02A6, "mfspr r16, 832");
}
#[test]
fn test_ins_mfsr() {
assert_asm!(0x7E0004A6, "mfsr r16, 0");
}
#[test]
fn test_ins_mftb() {
assert_asm!(0x7C8C42E6, "mftb r4, 392");
}
#[test]
fn test_ins_mtcrf() {
assert_asm!(0x7C6FF120, "mtcrf 255, r3");
}
/*
#[test]
fn test_ins_mtfsb0() {}
#[test]
fn test_ins_mtfsb1() {
assert_asm!(0xFFA0004C, "mtfsb1 0x1d");
}
*/
#[test]
fn test_ins_mtfsf() {
assert_asm!(0xFDFE058E, "mtfsf 255, f0");
assert_asm!(0xFDFEFD8E, "mtfsf 255, f31");
}
#[test]
fn test_ins_mtmsr() {
assert_asm!(0x7C000124, "mtmsr r0");
}
#[test]
fn test_ins_mtspr() {
assert_asm!(0x7E75FBA6, "mtspr 703, r19");
}
#[test]
fn test_ins_mtsr() {
assert_asm!(0x7E0001A4, "mtsr 0, r16");
}
#[test]
fn test_ins_mulhw() {
assert_asm!(0x7C7F2096, "mulhw r3, r31, r4");
}
#[test]
fn test_ins_mulhwu() {
assert_asm!(0x7C7D0016, "mulhwu r3, r29, r0");
}
#[test]
fn test_ins_mulli() {
assert_asm!(0x1C001880, "mulli r0, r0, 0x1880");
assert_asm!(0x1FBD0030, "mulli r29, r29, 0x30");
}
#[test]
fn test_ins_mullw() {
assert_asm!(0x7C7D01D6, "mullw r3, r29, r0");
}
#[test]
fn test_ins_nand() {
assert_asm!(0x7C7D03B8, "nand r29, r3, r0");
}
#[test]
fn test_ins_neg() {
assert_asm!(0x7C0600D0, "neg r0, r6");
}
#[test]
fn test_ins_nor() {
assert_asm!(0x7C0500F8, "nor r5, r0, r0");
}
#[test]
fn test_ins_or() {
assert_asm!(0x7C04DB78, "or r4, r0, r27");
}
#[test]
fn test_ins_orc() {
assert_asm!(0x7C042338, "orc r4, r0, r4");
}
#[test]
fn test_ins_ori() {
assert_asm!(0x60002204, "ori r0, r0, 0x2204");
}
#[test]
fn test_ins_oris() {
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, 0x8000_0000u32);
assert_eq!(ins.op, PsqLx);
assert_eq!(
ins.fields(),
vec![
frD(FPR(0)),
rA(GPR(0)),
rB(GPR(0)),
ps_W(OpaqueU(0)),
ps_l(GQR(0))
]
);
assert_eq!(ins.defs(), vec![frD(FPR(0))]);
assert_eq!(ins.uses(), vec![rB(GPR(0))]);
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, 8(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_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]
fn test_ins_rfi() {
assert_asm!(0x4C000064, "rfi");
}
#[test]
fn test_ins_rlwimi() {
assert_asm!(0x500306FE, "rlwimi r3, r0, 0, 27, 31");
assert_asm!(0x50032D74, "rlwimi r3, r0, 5, 21, 26");
}
#[test]
fn test_ins_rlwinm() {
assert_asm!(0x54000423, "rlwinm. r0, r0, 0, 16, 17");
assert_asm!(0x54000432, "rlwinm r0, r0, 0, 16, 25");
}
#[test]
fn test_ins_rlwnm() {
assert_asm!(0x5D6A67FE, "rlwnm r10, r11, r12, 31, 31");
assert_asm!(0x5FC52EFE, "rlwnm r5, r30, r5, 27, 31");
}
#[test]
fn test_ins_sc() {
assert_asm!(0x44000002, "sc");
}
#[test]
fn test_ins_slw() {
assert_asm!(0x7C042830, "slw r4, r0, r5");
}
#[test]
fn test_ins_sraw() {
assert_asm!(0x7C043E30, "sraw r4, r0, r7");
}
#[test]
fn test_ins_srawi() {
assert_asm!(0x7C000E70, "srawi r0, r0, 1");
assert_asm!(0x7C001670, "srawi r0, r0, 2");
}
#[test]
fn test_ins_srw() {
assert_asm!(0x7C001C30, "srw r0, r0, r3");
}
#[test]
fn test_ins_stb() {
assert_asm!(0x980105EC, "stb r0, 0x5ec(r1)");
assert_asm!(0x98030000, "stb r0, 0x0(r3)");
}
#[test]
fn test_ins_stbu() {
assert_asm!(0x9D2A7428, "stbu r9, 0x7428(r10)");
assert_asm!(0x9D66FFFF, "stbu r11, -0x1(r6)");
}
#[test]
fn test_ins_stbux() {
assert_asm!(0x7C08F9EE, "stbux r0, r8, r31");
}
#[test]
fn test_ins_stbx() {
assert_asm!(0x7C03F9AE, "stbx r0, r3, r31");
}
#[test]
fn test_ins_stfd() {
assert_asm!(0xD80D97B0, "stfd f0, -0x6850(r13)");
assert_asm!(0xD8050090, "stfd f0, 0x90(r5)");
}
#[test]
fn test_ins_stfdu() {
assert_asm!(0xDC24FFC0, "stfdu f1, -0x40(r4)");
}
/*
#[test]
fn test_ins_stfdx() {
assert_asm!(0x7C4405AE, "stfdx f2, r4, r0");
}
*/
#[test]
fn test_ins_stfs() {
assert_asm!(0xD003086C, "stfs f0, 0x86c(r3)");
assert_asm!(0xD0038000, "stfs f0, -0x8000(r3)");
}
#[test]
fn test_ins_stfsx() {
assert_asm!(0x7C465D2E, "stfsx f2, r6, r11");
}
#[test]
fn test_ins_sth() {
assert_asm!(0xB0038A7C, "sth r0, -0x7584(r3)");
assert_asm!(0xB0035036, "sth r0, 0x5036(r3)");
}
#[test]
fn test_ins_sthbrx() {
assert_asm!(0x7C60072C, "sthbrx r3, r0, r0");
}
#[test]
fn test_ins_sthu() {
assert_asm!(0xB4055B88, "sthu r0, 0x5b88(r5)");
}
#[test]
fn test_ins_sthux() {
assert_asm!(0x7C03236E, "sthux r0, r3, r4");
}
#[test]
fn test_ins_sthx() {
assert_asm!(0x7C1C2B2E, "sthx r0, r28, r5");
}
#[test]
fn test_ins_stmw() {
assert_asm!(0xBFA202A4, "stmw r29, 0x2a4(r2)");
}
#[test]
fn test_ins_stw() {
assert_asm!(0x900140CC, "stw r0, 0x40cc(r1)");
assert_asm!(0x9003FFBC, "stw r0, -0x44(r3)");
}
#[test]
fn test_ins_stwbrx() {
assert_asm!(0x7C00FD2C, "stwbrx r0, r0, r31");
}
#[test]
fn test_ins_stwu() {
assert_asm!(0x9421EBC0, "stwu r1, -0x1440(r1)");
}
#[test]
fn test_ins_stwux() {
assert_asm!(0x7C01B96E, "stwux r0, r1, r23");
}
#[test]
fn test_ins_stwx() {
assert_asm!(0x7C03212E, "stwx r0, r3, r4");
}
#[test]
fn test_ins_subf() {
assert_asm!(0x7C051850, "subf r0, r5, r3");
assert_asm!(0x7C051851, "subf. r0, r5, r3");
}
#[test]
fn test_ins_subfc() {
assert_asm!(0x7C040010, "subfc r0, r4, r0");
}
#[test]
fn test_ins_subfe() {
assert_asm!(0x7C030110, "subfe r0, r3, r0");
}
#[test]
fn test_ins_subfic() {
assert_asm!(0x200602FF, "subfic r0, r6, 0x2ff");
}
#[test]
fn test_ins_subfze() {
assert_asm!(0x7C000190, "subfze r0, r0");
}
#[test]
fn test_ins_sync() {
assert_asm!(0x7C0004AC, "sync");
}
#[test]
fn test_ins_xor() {
assert_asm!(0x7C052A78, "xor r5, r0, r5");
}
#[test]
fn test_ins_xori() {
assert_asm!(0x68E71021, "xori r7, r7, 0x1021");
}
#[test]
fn test_ins_xoris() {
assert_asm!(0x6E3D8000, "xoris r29, r17, 0x8000");
}

View File

@ -1,7 +1,7 @@
[package] [package]
name = "dol" name = "dol"
version = "0.1.0" version = "0.1.0"
edition = "2018" edition = "2021"
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 = "Deserializer for the DOL executable format" description = "Deserializer for the DOL executable format"

View File

@ -121,7 +121,7 @@ pub enum DolSectionType {
Bss, Bss,
} }
#[derive()] #[derive(Debug, Clone)]
pub struct DolSection { pub struct DolSection {
pub kind: DolSectionType, pub kind: DolSectionType,
pub index: usize, pub index: usize,

View File

@ -1,16 +1,16 @@
[package] [package]
name = "ppc750cl-flow-graph" name = "ppc750cl-flow-graph"
version = "0.1.1" version = "0.2.0"
edition = "2018" edition = "2021"
authors = ["riidefi <riidefi@rii.dev>", "Richard Patel <me@terorie.dev>"] authors = ["riidefi <riidefi@rii.dev>", "Richard Patel <me@terorie.dev>"]
license = "GPL-3.0-or-later" license = "GPL-3.0-or-later"
description = "Control flow graph analysis for PowerPC 750CL" description = "Control flow graph analysis for PowerPC 750CL"
repository = "https://github.com/terorie/ppc750cl" repository = "https://github.com/terorie/ppc750cl"
[dependencies] [dependencies]
clap = "2.33" clap = "3"
dol = { version = "0.1.0", path = "../dol" } dol = { version = "0.1.0", path = "../dol" }
itertools = "0.10" itertools = "0.10"
parse_int = "0.5" parse_int = "0.6"
petgraph = "0.6" petgraph = "0.6"
ppc750cl = { version = "0.1.1", path = "../disasm" } ppc750cl = { version = "0.2.0", path = "../disasm" }

View File

@ -8,6 +8,7 @@ use petgraph::algo::dominators::Dominators;
use petgraph::graph::{DefaultIx, NodeIndex}; use petgraph::graph::{DefaultIx, NodeIndex};
use petgraph::Graph; use petgraph::Graph;
use ppc750cl::formatter::FormattedIns;
use ppc750cl::{Ins, Opcode}; use ppc750cl::{Ins, Opcode};
use crate::slices::{BasicSlices, CodeIdx}; use crate::slices::{BasicSlices, CodeIdx};
@ -53,18 +54,21 @@ impl<'a> BasicBlock<'a> {
for ins in code { for ins in code {
match ins.op { match ins.op {
Opcode::Addis => { Opcode::Addis => {
if ins.a() == 0 { if ins.field_rA() == 0 {
// lis // lis
defs.insert(ins.d(), ins.uimm()); defs.insert(ins.field_rD() as u8, ins.field_uimm() as u16);
} else { } else {
defs.remove(&ins.d()); defs.remove(&(ins.field_rD() as u8));
} }
} }
Opcode::Addi => { Opcode::Addi => {
if let Some(hi) = defs.get(&ins.a()) { if let Some(hi) = defs.get(&(ins.field_rA() as u8)) {
data_refs.insert(ins.addr / 4, ((*hi as u32) << 16) + (ins.uimm() as u32)); data_refs.insert(
ins.addr / 4,
((*hi as u32) << 16) + (ins.field_uimm() as u32),
);
} }
defs.remove(&ins.d()); defs.remove(&(ins.field_rD() as u8));
} }
_ => (), _ => (),
} }
@ -88,7 +92,7 @@ impl<'a> Debug for BasicBlock<'a> {
self.range.end * 4 self.range.end * 4
)?; )?;
for ins in self.code { for ins in self.code {
writeln!(f, "{}", ins.to_string())?; writeln!(f, "{}", FormattedIns(ins.clone()))?;
if let Some(addr) = self.data_refs.get(&(ins.addr / 4)) { if let Some(addr) = self.data_refs.get(&(ins.addr / 4)) {
writeln!(f, " ref: {:0>#8x}", addr)?; writeln!(f, " ref: {:0>#8x}", addr)?;
} }
@ -151,8 +155,8 @@ impl<'a> FlowGraph<'a> {
// Get last instruction of left block. // Get last instruction of left block.
// Unless it's an unconditional branch, we can connect the blocks. // Unless it's an unconditional branch, we can connect the blocks.
let last_ins = &src_block.code.last().unwrap(); let last_ins = &src_block.code.last().unwrap();
if last_ins.code == Opcode::BLR if last_ins.code == 0x4E800020
|| (last_ins.op == Opcode::B && last_ins.bo() == 0b10100) || (last_ins.op == Opcode::B && last_ins.field_BO() == 0b10100)
{ {
continue; continue;
} }

View File

@ -1,4 +1,3 @@
use clap::clap_app;
use petgraph::dot::{Config as DotConfig, Dot}; use petgraph::dot::{Config as DotConfig, Dot};
use ppc750cl::{disasm_iter, Ins}; use ppc750cl::{disasm_iter, Ins};
@ -11,12 +10,27 @@ use crate::slices::BasicSlices;
use dol::Dol; use dol::Dol;
fn main() { fn main() {
let matches = clap_app!(myapp => let matches = clap::Command::new("ppc750cl-flow-graph")
(version: "1.0") .version("0.2.0")
(about: "Control flow graph analysis for PowerPC 750CL") .about("Control flow graph analysis for PowerPC 750CL")
(@arg START: --start +required +takes_value "Start address") .arg(
(@arg STOP: --stop +required +takes_value "Stop address") clap::Arg::new("START")
(@arg INPUT: +required "Binary input file") .long("--start")
.required(true)
.takes_value(true)
.help("Start address"),
)
.arg(
clap::Arg::new("STOP")
.long("--stop")
.required(true)
.takes_value(true)
.help("Stop address"),
)
.arg(
clap::Arg::new("INPUT")
.required(true)
.help("Binary input file"),
) )
.get_matches(); .get_matches();

View File

@ -24,7 +24,7 @@ impl BasicSlices {
let is_control_flow_ins = match ins.op { let is_control_flow_ins = match ins.op {
// Direct branches are control flow instructions if they don't save the link register. // Direct branches are control flow instructions if they don't save the link register.
// If they do, we encountered a function call. // If they do, we encountered a function call.
Opcode::B | Opcode::Bc => ins.lk() == 0, Opcode::B | Opcode::Bc => !ins.field_LK(),
// Switch table // Switch table
Opcode::Bcctr => panic!("jump tables not supported yet"), Opcode::Bcctr => panic!("jump tables not supported yet"),
_ => false, _ => false,
@ -33,7 +33,7 @@ impl BasicSlices {
continue; continue;
} }
// We encountered some kind of control flow instruction. // We encountered some kind of control flow instruction.
if ins.code != Opcode::BLR { if ins.field_BO() == 20 && ins.field_BI() == 0 {
// There's a possibility that branch can be taken. // There's a possibility that branch can be taken.
// Branch destinations are always the first instruction of a block. // Branch destinations are always the first instruction of a block.
// Thus, we also found the end of another block. // Thus, we also found the end of another block.
@ -58,5 +58,5 @@ fn is_conditional_branch(ins: &Ins) -> bool {
_ => return false, _ => return false,
}; };
// Check whether bits "branch always". // Check whether bits "branch always".
ins.bo() & 0b10100 != 0b10100 ins.field_BO() & 0b10100 != 0b10100
} }

View File

@ -1,7 +1,7 @@
[package] [package]
name = "ppc750cl-fuzz" name = "ppc750cl-fuzz"
version = "0.1.1" version = "0.2.0"
edition = "2018" edition = "2021"
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 ppc750cl"
@ -9,4 +9,4 @@ repository = "https://github.com/terorie/ppc750cl"
[dependencies] [dependencies]
num_cpus = "1.13" num_cpus = "1.13"
ppc750cl = { path = "../disasm", version = "0.1.1" } ppc750cl = { path = "../disasm", version = "0.2.0" }

View File

@ -1,10 +1,11 @@
use std::io::Write;
use std::ops::Range;
use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Arc; use std::sync::Arc;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use ppc750cl::formatter::SimpleFormatter; use ppc750cl::formatter::FormattedIns;
use ppc750cl::Ins; use ppc750cl::Ins;
use std::ops::Range;
fn main() { fn main() {
let start = Instant::now(); let start = Instant::now();
@ -62,7 +63,6 @@ impl MultiFuzzer {
// for most of the time. // for most of the time.
handle.join().expect("thread panicked"); handle.join().expect("thread panicked");
} }
disasm(0xFFFF_FFFF);
} }
} }
@ -81,11 +81,14 @@ impl Fuzzer {
} }
fn dispatch(&self) -> std::thread::JoinHandle<()> { fn dispatch(&self) -> std::thread::JoinHandle<()> {
let mut devnull = DevNull;
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 || {
for x in range.clone() { for x in range.clone() {
disasm(x); let ins = Ins::new(x, 0x8000_0000);
writeln!(&mut devnull, "{}", FormattedIns(ins)).unwrap();
if x % (1 << 19) == 0 { if x % (1 << 19) == 0 {
counter.store(x, Ordering::Relaxed); counter.store(x, Ordering::Relaxed);
} }
@ -95,13 +98,6 @@ impl Fuzzer {
} }
} }
fn disasm(x: u32) {
let devnull = DevNull;
let mut formatter = SimpleFormatter { writer: devnull };
let ins = Ins::new(x, 0x8000_0000u32);
ins.write_string(&mut formatter).unwrap();
}
struct DevNull; struct DevNull;
impl std::io::Write for DevNull { impl std::io::Write for DevNull {

17
genisa/Cargo.toml Normal file
View File

@ -0,0 +1,17 @@
[package]
name = "ppc750cl-genisa"
version = "0.2.0"
edition = "2021"
authors = ["Richard Patel <me@terorie.dev>"]
license = "GPL-3.0-or-later"
description = "Rust code generator for ppc750cl"
repository = "https://github.com/terorie/ppc750cl"
[dependencies]
itertools = "0.10"
proc-macro2 = "1.0"
quote = "1.0"
syn = "1.0"
serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.8"

693
genisa/src/main.rs Normal file
View File

@ -0,0 +1,693 @@
use std::collections::HashMap;
use std::fs::File;
use std::io::Write;
use std::ops::Range;
use std::process::{Command, Stdio};
use std::str::FromStr;
use itertools::Itertools;
use proc_macro2::{Ident, Literal, Span, TokenStream, TokenTree};
use quote::quote;
use serde::{Deserialize, Deserializer};
use syn::{LitChar, LitInt, LitStr};
macro_rules! token_stream {
($stream:ident) => {
TokenStream::from_iter($stream.into_iter())
};
}
fn main() {
if let Err(err) = _main() {
eprintln!("{}", err);
std::process::exit(1);
}
}
type Error = Box<dyn std::error::Error>;
type Result<T> = std::result::Result<T, Error>;
fn _main() -> Result<()> {
let isa = load_isa()?;
let mut unformatted_code = Vec::<u8>::new();
writeln!(
&mut unformatted_code,
"{}",
quote! {
use crate::prelude::*;
}
)?;
writeln!(&mut unformatted_code, "{}", isa.gen_opcode_enum()?)?;
writeln!(&mut unformatted_code, "{}", isa.gen_field_enum()?)?;
writeln!(&mut unformatted_code, "{}", isa.gen_ins_impl()?)?;
let formatted_code = rustfmt(unformatted_code);
File::create("./disasm/src/generated.rs")?.write_all(&formatted_code)?;
Ok(())
}
fn rustfmt(code: Vec<u8>) -> Vec<u8> {
let mut rustfmt = Command::new("rustfmt")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.expect("failed to spawn rustfmt");
let mut stdin = rustfmt.stdin.take().unwrap();
std::thread::spawn(move || {
let _ = stdin.write_all(&code);
});
let rustfmt_res = rustfmt.wait_with_output().expect("failed to run rustfmt");
if !rustfmt_res.status.success() {
panic!("rustfmt failed");
}
rustfmt_res.stdout
}
#[derive(Default)]
pub(crate) struct BitRange(Range<u8>);
impl<'de> Deserialize<'de> for BitRange {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let range_str: String = Deserialize::deserialize(deserializer)?;
if let Some((start_str, stop_str)) = range_str.split_once("..") {
let start = start_str.parse::<u8>().map_err(serde::de::Error::custom)?;
let stop = stop_str.parse::<u8>().map_err(serde::de::Error::custom)?;
Ok(Self(start..stop))
} else {
let bit_idx = range_str.parse::<u8>().map_err(serde::de::Error::custom)?;
Ok(Self(bit_idx..bit_idx))
}
}
}
#[derive(Deserialize, Default)]
#[serde(default)]
pub(crate) struct Field {
name: String,
desc: String,
bits: BitRange,
signed: bool,
split: bool,
arg: Option<String>,
shift_left: u8,
}
impl Field {
fn variant_identifier(&self) -> Option<TokenTree> {
self.identifier("")
}
fn identifier(&self, prefix: &str) -> Option<TokenTree> {
if self.name.strip_suffix(".nz").is_none() {
Some(to_rust_ident(prefix, &self.name))
} else {
None
}
}
fn express_value(&self, code: TokenStream) -> TokenStream {
let mut val = quote!(#code);
let shift = 32 - self.bits.0.end;
if shift > 0 {
val = quote!((#val >> #shift));
}
let mask = (1u32 << self.bits.0.len()) - 1;
if mask != 0xFFFF_FFFF {
let mask = LitInt::new(&format!("0x{:x}", mask), Span::call_site());
val = quote!((#val & #mask));
}
// https://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend
if self.signed {
let mask2 = 1u32 << (self.bits.0.len() - 1);
let mask2 = LitInt::new(&format!("0x{:x}", mask2), Span::call_site());
val = quote!(((#val ^ #mask2).wrapping_sub(#mask2)))
}
let val_shift = self.shift_left;
if val_shift > 0 {
val = quote!((#val << #val_shift));
}
val
}
fn express_value_self(&self) -> TokenStream {
self.express_value(quote!(self.code))
}
fn enum_variant_definition(&self) -> Option<TokenStream> {
let ident = self.variant_identifier()?;
Some(if let Some(arg) = &self.arg {
let arg = TokenTree::Ident(Ident::new(arg, Span::call_site()));
quote! {
#ident(#arg),
}
} else {
quote! {
#ident,
}
})
}
fn construct_variant(&self, code: TokenStream) -> TokenStream {
let field_variant = self.variant_identifier();
if let Some(arg) = &self.arg {
let field_arg = TokenTree::Ident(Ident::new(arg, Span::call_site()));
let value = self.express_value(code);
quote! {
Field::#field_variant(#field_arg(#value as _))
}
} else {
quote! {
Field::#field_variant
}
}
}
fn construct_variant_self(&self) -> TokenStream {
self.construct_variant(quote!(self.code))
}
fn construct_accessor(&self) -> TokenStream {
let field_variant = match self.identifier("field_") {
Some(v) => v,
None => return TokenStream::new(),
};
if self.arg.is_none() {
return TokenStream::new();
}
let value = self.express_value_self();
let ret_type = if self.signed {
quote!(isize)
} else {
quote!(usize)
};
quote! {
#[inline(always)]
pub fn #field_variant(&self) -> #ret_type {
#value as _
}
}
}
}
#[derive(Deserialize, Default)]
#[serde(default)]
pub(crate) struct Opcode {
name: String,
desc: String,
bitmask: u32,
pattern: u32,
modifiers: Vec<String>,
side_effects: Vec<String>,
args: Vec<String>,
defs: Vec<String>,
uses: Vec<String>,
}
impl Opcode {
fn variant_identifier(&self) -> Result<TokenTree> {
to_rust_variant(&self.name)
}
}
#[derive(Deserialize, Default)]
#[serde(default)]
pub(crate) struct Mnemonic {
name: String,
opcode: String,
modifiers: Vec<String>,
args: Vec<String>,
condition: String,
}
#[derive(Deserialize, Default)]
#[serde(default)]
pub(crate) struct Modifier {
name: String,
suffix: char,
bit: u8,
}
impl Modifier {
fn express_value_self(&self) -> TokenStream {
let modifier_bit = self.bit as usize;
quote!(self.bit(#modifier_bit))
}
fn construct_accessor(&self) -> TokenStream {
let field_variant = to_rust_ident("field_", &self.name);
let value = self.express_value_self();
quote! {
#[inline(always)]
pub fn #field_variant(&self) -> bool {
#value
}
}
}
}
#[derive(Deserialize, Default)]
#[serde(default)]
pub(crate) struct Isa {
fields: Vec<Field>,
modifiers: Vec<Modifier>,
opcodes: Vec<Opcode>,
mnemonics: Vec<Mnemonic>,
}
fn load_isa() -> Result<Isa> {
let yaml_file = File::open("isa.yaml")?;
let isa: Isa = serde_yaml::from_reader(yaml_file)?;
Ok(isa)
}
impl Isa {
fn gen_opcode_enum(&self) -> Result<TokenStream> {
// Create enum variants.
let enum_variants = self
.opcodes
.iter()
.map(|opcode| -> Result<TokenStream> {
let ident = opcode.variant_identifier()?;
Ok(quote! {
#ident,
})
})
.try_collect::<TokenStream, Vec<TokenStream>, Error>()?;
let enum_variants = token_stream!(enum_variants);
// Create functions.
let mnemonic_fn = self.gen_mnemonic_fn()?;
let detect_fn = self.gen_opcode_detect()?;
// Create final enum.
let opcode_enum = quote! {
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Opcode {
Illegal = -1,
#enum_variants
}
#[allow(clippy::all)]
impl Opcode {
#mnemonic_fn
#detect_fn
}
};
Ok(opcode_enum)
}
fn gen_mnemonic_fn(&self) -> Result<TokenStream> {
// Create match arms.
let match_arms = self
.opcodes
.iter()
.map(|opcode| {
let variant = opcode.variant_identifier()?;
let literal = Literal::string(&opcode.name);
Ok(quote! {
Opcode::#variant => #literal,
})
})
.try_collect::<TokenStream, Vec<TokenStream>, Error>()?;
let match_arms = token_stream!(match_arms);
// Create final function.
let mnemonic_fn = quote! {
pub(crate) fn _mnemonic(self) -> &'static str {
match self {
Opcode::Illegal => "<illegal>",
#match_arms
}
}
};
Ok(mnemonic_fn)
}
pub(crate) fn gen_opcode_detect(&self) -> Result<TokenStream> {
// Generate if chain.
let if_chain = self
.opcodes
.iter()
.map(|opcode| {
let bitmask_str = format!("{:>#8x}", opcode.bitmask);
let bitmask = LitInt::new(&bitmask_str, Span::call_site());
let pattern_str = format!("{:>#8x}", opcode.pattern);
let pattern = LitInt::new(&pattern_str, Span::call_site());
let identifier = opcode.variant_identifier()?;
Ok(quote! {
if code & #bitmask == #pattern {
return Opcode::#identifier;
}
})
})
.try_collect::<TokenStream, Vec<TokenStream>, Error>()?;
let if_chain = token_stream!(if_chain);
// Generate function.
let func = quote! {
pub(crate) fn _detect(code: u32) -> Self {
#if_chain
Opcode::Illegal
}
};
Ok(func)
}
pub(crate) fn gen_field_enum(&self) -> Result<TokenStream> {
// Create enum variants.
let mut enum_variants = Vec::new();
for field in &self.fields {
if let Some(field) = field.enum_variant_definition() {
enum_variants.push(field);
}
}
let enum_variants = token_stream!(enum_variants);
// Create final enum.
let field_enum = quote! {
#[allow(non_camel_case_types)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Field {
#enum_variants
}
};
Ok(field_enum)
}
pub(crate) fn gen_ins_impl(&self) -> Result<TokenStream> {
// Map fields by name.
let mut field_by_name = HashMap::<String, &Field>::new();
for field in &self.fields {
field_by_name.insert(field.name.clone(), field);
}
let mut modifier_by_name = HashMap::<String, &Modifier>::new();
for modifier in &self.modifiers {
modifier_by_name.insert(modifier.name.clone(), modifier);
}
// Map mnemonics by opcode.
let mut mnemonics_by_opcode = HashMap::<&String, Vec<&Mnemonic>>::new();
for simple in &self.mnemonics {
mnemonics_by_opcode
.entry(&simple.opcode)
.or_insert_with(Vec::new)
.push(simple)
}
// Generate match arms for each opcode.
let mut field_match_arms = Vec::new();
let mut def_match_arms = Vec::new();
let mut use_match_arms = Vec::new();
let mut suffix_match_arms = Vec::new();
let mut simplified_ins_match_arms = Vec::new();
for opcode in &self.opcodes {
// Generate fields of opcode.
let mut fields = Vec::new();
for arg in &opcode.args {
let field: &Field = field_by_name
.get(arg)
.ok_or_else(|| Error::from(format!("undefined field {}", arg)))?;
let variant = field.construct_variant_self();
fields.extend(quote! { #variant, })
}
let fields = token_stream!(fields);
// Emit match arm.
let ident = opcode.variant_identifier()?;
field_match_arms.push(quote! {
Opcode::#ident => vec![#fields],
});
// Generate modifiers.
let suffix = express_suffix(&modifier_by_name, opcode)?;
suffix_match_arms.push(quote! {
Opcode::#ident => #suffix,
});
// Generate defs.
let mut defs = Vec::new();
for arg in &opcode.defs {
let field: &Field = field_by_name.get(arg).ok_or_else(|| {
syn::Error::new(Span::call_site(), format!("undefined field {}", arg))
})?;
let variant = field.construct_variant_self();
defs.extend(quote! { #variant, })
}
let defs = token_stream!(defs);
let ident = opcode.variant_identifier()?;
def_match_arms.push(quote! {
Opcode::#ident => vec![#defs],
});
// Generate uses.
let mut uses = Vec::new();
let mut special_uses = Vec::new();
for arg in &opcode.uses {
// Detect non-zero modifier.
let mut arg = arg.as_str();
let mut non_zero = false;
if let Some(substr) = arg.strip_suffix(".nz") {
non_zero = true;
arg = substr;
}
// Get underlying field.
let field: &Field = field_by_name.get(arg).ok_or_else(|| {
syn::Error::new(Span::call_site(), format!("undefined field {}", arg))
})?;
let variant = field.construct_variant_self();
if non_zero {
let value = field.express_value_self();
special_uses.extend(quote! {
if (#value) != 0 {
uses.push(#variant);
}
})
} else {
uses.extend(quote! {
#variant,
})
}
}
let uses = token_stream!(uses);
let ident = opcode.variant_identifier()?;
let special_uses = token_stream!(special_uses);
use_match_arms.push(quote! {
Opcode::#ident => {
let mut uses = vec![#uses];
#special_uses
uses
},
});
// Generate instruction simplification.
if let Some(mnemonics) = mnemonics_by_opcode.get(&opcode.name) {
let mut simplified_conditions = Vec::new();
for mnemonic in mnemonics {
if mnemonic.condition.is_empty() {
continue; // TODO else branch
}
simplified_conditions.push(quote!(if));
simplified_conditions.push(compile_mnemonic_condition(
&field_by_name,
&mnemonic.condition,
)?);
// Emit branch.
let mnemonic_lit = LitStr::new(&mnemonic.name, Span::call_site());
// Extract arguments.
let mut args = Vec::new();
for arg in &mnemonic.args {
let field = field_by_name
.get(arg)
.unwrap_or_else(|| panic!("field not found: {}", arg));
let variant = Ident::new(field.arg.as_ref().unwrap(), Span::call_site());
let value = field.express_value_self();
args.push(quote!(Argument::#variant(#variant(#value as _)),));
}
let args = token_stream!(args);
simplified_conditions.push(quote! {
{
return SimplifiedIns {
mnemonic: #mnemonic_lit,
args: vec![#args],
ins: self,
};
}
});
}
let simplified_conditions = token_stream!(simplified_conditions);
simplified_ins_match_arms.push(quote! {
Opcode::#ident => {
#simplified_conditions
},
});
}
}
let field_match_arms = token_stream!(field_match_arms);
let def_match_arms = token_stream!(def_match_arms);
let use_match_arms = token_stream!(use_match_arms);
let suffix_match_arms = token_stream!(suffix_match_arms);
let simplified_ins_match_arms = token_stream!(simplified_ins_match_arms);
let field_accessors = self
.fields
.iter()
.map(|field| field.construct_accessor())
.collect::<Vec<_>>();
let field_accessors = token_stream!(field_accessors);
let modifier_accessors = self
.modifiers
.iter()
.map(|modifier| modifier.construct_accessor())
.collect::<Vec<_>>();
let modifier_accessors = token_stream!(modifier_accessors);
// Generate final fields function.
let ins_impl = quote! {
#[allow(clippy::all, unused_mut)]
impl Ins {
pub(crate) fn _fields(&self) -> Vec<Field> {
match self.op {
Opcode::Illegal => vec![],
#field_match_arms
}
}
pub(crate) fn _defs(&self) -> Vec<Field> {
match self.op {
Opcode::Illegal => vec![],
#def_match_arms
}
}
pub(crate) fn _uses(&self) -> Vec<Field> {
match self.op {
Opcode::Illegal => vec![],
#use_match_arms
}
}
pub(crate) fn _suffix(&self) -> String {
match self.op {
Opcode::Illegal => String::new(),
#suffix_match_arms
}
}
pub(crate) fn _simplified(self) -> SimplifiedIns {
match self.op {
#simplified_ins_match_arms
_ => {}
}
SimplifiedIns::basic_form(self)
}
}
#[allow(clippy::all, non_snake_case)]
impl Ins {
#field_accessors
#modifier_accessors
}
};
Ok(ins_impl)
}
}
/// Converts the given key into an identifier.
fn to_rust_ident(prefix: &str, key: &str) -> TokenTree {
TokenTree::Ident(Ident::new(
&(prefix.to_owned() + &key.replace('.', "_")),
Span::call_site(),
))
}
/// Converts the given key into an enum variant key.
fn to_rust_variant(key: &str) -> Result<TokenTree> {
Ok(TokenTree::Ident(Ident::new(
&to_rust_variant_str(key)?,
Span::call_site(),
)))
}
fn to_rust_variant_str(key: &str) -> Result<String> {
let mut s = String::new();
let mut chars = key.chars();
loop {
// Make first char uppercase.
let c = match chars.next() {
None => return Ok(s),
Some(c) => c,
};
s.push(match c {
'a'..='z' => c.to_ascii_uppercase(),
'A'..='Z' => c,
_ => return Err(format!("invalid identifier: {}", key).into()),
});
loop {
let c = match chars.next() {
None => return Ok(s),
Some(c) => c,
};
match c.to_ascii_lowercase() {
'0'..='9' | 'a'..='z' => s.push(c),
'_' => break,
'.' => {
s.push('_');
break;
}
_ => return Err(format!("invalid character in opcode name: {}", key).into()),
}
}
}
}
/// Compiles conditions such as `S == B` into valid Rust expressions on a PowerPC instruction.
fn compile_mnemonic_condition(
field_by_name: &HashMap<String, &Field>,
code: &str,
) -> Result<TokenStream> {
let src_stream = TokenStream::from_str(code)?;
let token_iter = src_stream.into_iter().flat_map(|token| {
if let TokenTree::Ident(ref ident) = token {
if let Some(field) = field_by_name.get(&ident.to_string()) {
return field.express_value_self();
}
}
token.into()
});
Ok(TokenStream::from_iter(token_iter))
}
fn express_suffix(
modifier_by_name: &HashMap<String, &Modifier>,
opcode: &Opcode,
) -> Result<TokenStream> {
Ok(if opcode.modifiers.is_empty() {
quote!(String::new())
} else {
let mut chars = Vec::new();
for mod_name in &opcode.modifiers {
let modifier: &Modifier = modifier_by_name
.get(mod_name)
.ok_or_else(|| Error::from(format!("undefined modifier {}", mod_name)))?;
let lit_char = LitChar::new(modifier.suffix, Span::call_site());
let modifier_bit = modifier.express_value_self();
chars.push(quote! {
if #modifier_bit {
s.push(#lit_char);
}
});
}
let chars = token_stream!(chars);
quote!({
{
let mut s = String::with_capacity(4);
#chars
s
}
})
})
}

2368
isa.yaml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +0,0 @@
[package]
name = "ppc750cl-macros"
version = "0.1.1"
edition = "2018"
authors = ["Richard Patel <me@terorie.dev>"]
license = "GPL-3.0-or-later"
description = "Procedural macros for powerpc750cl"
repository = "https://github.com/terorie/ppc750cl"
[lib]
proc-macro = true
[dependencies]
proc-macro2 = "1.0.28"
quote = "1.0.9"
syn = { version = "1.0.74", features = ["full", "parsing"] }

View File

@ -1,215 +0,0 @@
use std::iter::FromIterator;
use proc_macro2::{Delimiter, Group, Ident, Literal, Span, TokenStream, TokenTree};
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::punctuated::Punctuated;
use syn::token::{And, EqEq, Semi};
use syn::{LitInt, LitStr};
struct Opcodes {
opcodes: Punctuated<Opcode, Semi>,
}
impl Parse for Opcodes {
fn parse(input: ParseStream) -> syn::Result<Self> {
Ok(Self {
opcodes: Punctuated::parse_terminated(input)?,
})
}
}
#[derive(Debug)]
struct Opcode {
name: String,
variant_name: String,
mask: u32,
bits: u32,
}
impl Parse for Opcode {
fn parse(input: ParseStream) -> syn::Result<Self> {
let name = input.parse::<LitStr>()?;
input.parse::<And>()?;
let mask = input.parse::<LitInt>()?;
input.parse::<EqEq>()?;
let bits = input.parse::<LitInt>()?;
Ok(Self {
name: name.value(),
variant_name: opcode_to_variant_name(&name)?,
mask: hex_from_lit_int(&mask)?,
bits: hex_from_lit_int(&bits)?,
})
}
}
fn hex_from_lit_int(int: &LitInt) -> syn::Result<u32> {
let str = int.to_string();
let hex = match str.strip_prefix("0x") {
None => return Err(syn::Error::new(int.span(), "not a hex integer")),
Some(x) => x,
};
u32::from_str_radix(hex, 16).map_err(|e| syn::Error::new(int.span(), e))
}
fn opcode_to_variant_name(opcode: &LitStr) -> syn::Result<String> {
let mut s = String::new();
let opcode_value = opcode.value();
let mut chars = opcode_value.chars();
loop {
// Make first char uppercase.
let c = match chars.next() {
None => return Ok(s),
Some(c) => c,
};
match c {
'a'..='z' => s.push(c.to_ascii_uppercase()),
_ => return Err(syn::Error::new(opcode.span(), "invalid opcode name")),
}
loop {
let c = match chars.next() {
None => return Ok(s),
Some(c) => c,
};
match c {
'0'..='9' | 'a'..='z' => s.push(c),
'_' => break,
'.' => {
s.push('_');
break;
}
_ => {
return Err(syn::Error::new(
opcode.span(),
"invalid character in opcode name",
))
}
}
}
}
}
fn gen_is_valid_fn(tokens: &mut Vec<TokenTree>, opcodes: &Opcodes) {
tokens.extend(quote!(pub fn is_valid(self, code: u32) -> bool));
let mut parts = Vec::<TokenTree>::new();
parts.extend(quote!(match self));
let mut match_parts = Vec::<TokenTree>::new();
match_parts.extend(quote!(Opcode::Illegal => false,));
for opcode in &opcodes.opcodes {
match_parts.extend(quote!(Opcode::));
match_parts.push(TokenTree::Ident(Ident::new(
&opcode.variant_name,
Span::call_site(),
)));
match_parts.extend(quote!(=> code &));
match_parts.push(TokenTree::Literal(Literal::u32_suffixed(opcode.mask)));
match_parts.extend(quote!(==));
match_parts.push(TokenTree::Literal(Literal::u32_suffixed(opcode.bits)));
match_parts.extend(quote!(,));
}
let match_body = Group::new(Delimiter::Brace, TokenStream::from_iter(match_parts));
parts.push(TokenTree::Group(match_body));
let body = Group::new(Delimiter::Brace, TokenStream::from_iter(parts));
tokens.push(TokenTree::Group(body));
}
fn gen_mnemonic_fn(tokens: &mut Vec<TokenTree>, opcodes: &Opcodes) {
tokens.extend(quote!(pub fn mnemonic(self) -> &'static str));
let mut parts = Vec::<TokenTree>::new();
parts.extend(quote!(match self));
let mut match_parts = Vec::<TokenTree>::new();
match_parts.extend(quote!(Opcode::Illegal => "<illegal>",));
for opcode in &opcodes.opcodes {
match_parts.extend(quote!(Opcode::));
match_parts.push(TokenTree::Ident(Ident::new(
&opcode.variant_name,
Span::call_site(),
)));
match_parts.extend(quote!(=>));
match_parts.push(TokenTree::Literal(Literal::string(&opcode.name)));
match_parts.extend(quote!(,));
}
let match_body = Group::new(Delimiter::Brace, TokenStream::from_iter(match_parts));
parts.push(TokenTree::Group(match_body));
let body = Group::new(Delimiter::Brace, TokenStream::from_iter(parts));
tokens.push(TokenTree::Group(body));
}
pub(crate) fn isa(input: TokenStream) -> syn::Result<TokenStream> {
let opcodes: Opcodes = syn::parse2(input)?;
// Assemble root stream.
let mut root = Vec::<TokenTree>::new();
// Define enum derives and header.
root.extend(quote!(#[derive(Debug, Copy, Clone, Eq, PartialEq)]));
root.extend(quote!(pub enum Opcode));
// Create entries.
// First entry is going to be the illegal entry.
let mut enum_entries = Vec::<TokenTree>::new();
enum_entries.extend(quote!(Illegal = -1,));
// Append the actual opcodes.
for opcode in &opcodes.opcodes {
enum_entries.push(TokenTree::Ident(Ident::new(
&opcode.variant_name,
Span::call_site(),
)));
enum_entries.extend(quote!(,));
}
// Create body.
let enum_body = Group::new(Delimiter::Brace, TokenStream::from_iter(enum_entries));
root.push(TokenTree::Group(enum_body));
// impl Opcode block.
root.extend(quote!(impl Opcode));
let mut impl_opcode_body_parts = Vec::<TokenTree>::new();
gen_is_valid_fn(&mut impl_opcode_body_parts, &opcodes);
gen_mnemonic_fn(&mut impl_opcode_body_parts, &opcodes);
let impl_opcode_body = Group::new(
Delimiter::Brace,
TokenStream::from_iter(impl_opcode_body_parts),
);
root.push(TokenTree::Group(impl_opcode_body));
// Extra code.
root.extend(quote! {
impl Default for Opcode {
fn default() -> Self {
Opcode::Illegal
}
}
impl std::string::ToString for Opcode {
fn to_string(&self) -> String {
let mnemonic = self.mnemonic();
mnemonic.to_owned()
}
}
});
Ok(TokenStream::from_iter(root))
}
#[cfg(test)]
mod tests {
use super::*;
fn _opcode_to_variant_name(input: &str) -> String {
opcode_to_variant_name(&LitStr::new(input, proc_macro2::Span::call_site())).unwrap()
}
#[test]
fn test_opcode_to_variant_name() {
assert_eq!(_opcode_to_variant_name("lol_lel."), "LolLel_");
assert_eq!(_opcode_to_variant_name("ps_nmsub"), "PsNmsub");
}
}

View File

@ -1,22 +0,0 @@
mod isa;
mod writer;
#[proc_macro]
pub fn isa(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = proc_macro2::TokenStream::from(input);
let output = match crate::isa::isa(input) {
Ok(v) => v,
Err(err) => return proc_macro::TokenStream::from(err.to_compile_error()),
};
proc_macro::TokenStream::from(output)
}
#[proc_macro]
pub fn write_asm(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = proc_macro2::TokenStream::from(input);
let output = match crate::writer::write_asm(input) {
Ok(v) => v,
Err(err) => return proc_macro::TokenStream::from(err.to_compile_error()),
};
proc_macro::TokenStream::from(output)
}

View File

@ -1,170 +0,0 @@
use std::iter::FromIterator;
use std::string::ToString;
use proc_macro2::{Delimiter, Group, TokenStream};
use quote::quote;
use quote::ToTokens;
use syn::parse::{Parse, ParseStream};
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::{Expr, ExprLit, ExprPath, Ident};
struct Arguments {
formatter: Expr,
ins: Expr,
args: Punctuated<Argument, syn::token::Semi>,
}
impl Parse for Arguments {
fn parse(input: ParseStream) -> syn::Result<Self> {
let formatter = input.parse()?;
input.parse::<syn::token::Comma>()?;
let ins = input.parse()?;
input.parse::<syn::token::FatArrow>()?;
let content;
syn::braced!(content in input);
let args = Punctuated::parse_terminated(&content)?;
Ok(Self {
formatter,
ins,
args,
})
}
}
/// A single part of an instruction.
///
/// Examples:
/// ```ignore
/// (op.mnemonic, rc, oe) -> mnemonic;
/// d -> fpr;
/// ```
struct Argument {
sources: Vec<Expr>,
target: Ident,
}
impl Parse for Argument {
fn parse(input: ParseStream) -> syn::Result<Self> {
// Parse source part.
let lookahead = input.lookahead1();
let sources;
if lookahead.peek(syn::token::Paren) {
// Parse multiple if we found a parenthesis.
let content;
syn::parenthesized!(content in input);
sources = content
.parse_terminated::<Expr, syn::token::Comma>(Expr::parse)?
.into_iter()
.collect();
} else if lookahead.peek(syn::LitStr) || lookahead.peek(syn::LitInt) {
let expr = input.parse::<ExprLit>()?.into();
sources = vec![expr];
} else {
let expr = input.parse::<ExprPath>()?.into();
sources = vec![expr];
}
input.parse::<syn::token::Colon>()?;
let target = input.parse()?;
Ok(Self { sources, target })
}
}
impl Arguments {
fn format_mnemonic(&self) -> Vec<TokenStream> {
let arg = &self.args[0];
assert!(!arg.sources.is_empty());
// Print the mnemonic.
let mut calls = vec![self.format_call(&arg.target, self.ins_call(&arg.sources[0]))];
// Print any mnemonic suffixes.
for src in arg.sources.iter().skip(1) {
calls.push(self.format_call(
&Ident::new(&src.into_token_stream().to_string(), src.span()),
self.ins_call(src),
));
}
calls
}
fn format_call(&self, method_arg: &Ident, args: TokenStream) -> TokenStream {
let arg_str = method_arg.to_string();
let method_name = format!("write_{}", arg_str);
let method_name = Ident::new(&method_name, method_arg.span());
let formatter = &self.formatter;
if arg_str == "branch_target" {
quote!(#formatter.write_branch_target(#args, self.addr)?)
} else {
quote!(#formatter.#method_name(#args)?)
}
}
fn ins_call(&self, call: &Expr) -> TokenStream {
match call {
Expr::Lit(_) => call.to_token_stream(),
_ => {
let ins = &self.ins;
quote!(#ins.#call())
}
}
}
}
pub(crate) fn write_asm(input: TokenStream) -> syn::Result<TokenStream> {
let arguments: Arguments = syn::parse2(input)?;
assert!(!arguments.args.is_empty());
// Create a list of calls to execute.
let mut calls = Vec::<TokenStream>::new();
calls.extend(arguments.format_mnemonic());
let mut offset_open = false;
for (i, arg) in arguments.args.iter().enumerate().skip(1) {
// Separate operands from one another unless the last one was an offset.
if !offset_open {
if i == 1 {
calls.push(
arguments
.format_call(&Ident::new("opcode_separator", arg.target.span()), quote!()),
);
} else {
calls.push(arguments.format_call(
&Ident::new("operand_separator", arg.target.span()),
quote!(),
));
}
}
// Arguments to out.write_x(...);
let format_args = arg.sources.iter().map(|src| arguments.ins_call(src));
let format_args_punct: Punctuated<TokenStream, syn::token::Comma> =
Punctuated::from_iter(format_args);
// Create call.
if arg.target.to_string().starts_with("offset") {
// Offsets are a special case since we need to call close afterwards.
if offset_open {
return Err(syn::Error::new(
arg.target.span(),
"two consecutive offset arguments",
));
}
calls.push(arguments.format_call(
&Ident::new(&(arg.target.to_string() + "_open"), arg.target.span()),
format_args_punct.to_token_stream(),
));
offset_open = true;
} else {
calls.push(arguments.format_call(&arg.target, format_args_punct.to_token_stream()));
if offset_open {
calls.push(
arguments.format_call(&Ident::new("offset_close", arg.target.span()), quote!()),
);
offset_open = false;
}
}
}
// Wrap calls in a block returning Ok(()).
calls.push(quote!(std::io::Result::Ok(())));
let statements = Punctuated::<TokenStream, syn::token::Semi>::from_iter(calls);
let tokens = Group::new(Delimiter::Brace, statements.to_token_stream());
Ok(tokens.to_token_stream())
}

View File

@ -1,13 +1,13 @@
[package] [package]
name = "ppc750cl-rand" name = "ppc750cl-rand"
version = "0.1.1" version = "0.2.0"
edition = "2018" edition = "2021"
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 = "Generate random PowerPC 750CL instructions" description = "Generate random PowerPC 750CL instructions"
repository = "https://github.com/terorie/ppc750cl" repository = "https://github.com/terorie/ppc750cl"
[dependencies] [dependencies]
ppc750cl = { path = "../disasm", version = "0.1.1" } ppc750cl = { path = "../disasm", version = "0.2.0" }
rand_core = "0.5" rand_core = "0.6"
sfmt = "0.6" sfmt = "0.7"

View File

@ -1,26 +1,16 @@
use rand_core::{RngCore, SeedableRng}; use rand_core::{RngCore, SeedableRng};
use sfmt::SFMT; use sfmt::SFMT;
use ppc750cl::formatter::SimpleFormatter; use ppc750cl::formatter::FormattedIns;
use ppc750cl::{Ins, Opcode}; use ppc750cl::{Ins, Opcode};
use std::io::{BufWriter, Write};
fn main() { fn main() {
let mut rng = SFMT::seed_from_u64(42); let mut rng = SFMT::seed_from_u64(42);
let stdout = std::io::stdout();
let stdout_lock = stdout.lock();
let stream = BufWriter::with_capacity(1_000_000, stdout_lock);
let mut formatter = SimpleFormatter { writer: stream };
loop { loop {
let ins = Ins::new(rng.next_u32(), 0); let ins = Ins::new(rng.next_u32(), 0);
if ins.op == Opcode::Illegal { if ins.op == Opcode::Illegal {
continue; continue;
} }
if ins.write_string(&mut formatter).is_err() { println!("{}", FormattedIns(ins));
return;
}
if formatter.writer.write_all("\n".as_ref()).is_err() {
return;
}
} }
} }