Start modeling CPU
Wrote a shallow skeleton of a CPU Core. Finally made the disassembler a clap subcommand.
This commit is contained in:
parent
addea1efa0
commit
9330c53957
142
Cargo.lock
generated
142
Cargo.lock
generated
|
@ -1,12 +1,21 @@
|
||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arm7tdmi"
|
name = "ansi_term"
|
||||||
version = "0.1.0"
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"enum-primitive-derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
]
|
||||||
"num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
|
[[package]]
|
||||||
|
name = "atty"
|
||||||
|
version = "0.2.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"termion 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -19,11 +28,31 @@ name = "bit"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "2.33.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"yaml-rust 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "enum-primitive-derive"
|
name = "enum-primitive-derive"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
|
@ -34,6 +63,11 @@ dependencies = [
|
||||||
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.58"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.1.43"
|
version = "0.1.43"
|
||||||
|
@ -50,19 +84,45 @@ dependencies = [
|
||||||
"autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "numtoa"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "0.3.15"
|
version = "0.3.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.1.54"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_termios"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustboyadvance"
|
name = "rustboyadvance"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arm7tdmi 0.1.0",
|
"bit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"enum-primitive-derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "0.11.11"
|
version = "0.11.11"
|
||||||
|
@ -81,19 +141,89 @@ dependencies = [
|
||||||
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termion"
|
||||||
|
version = "1.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "textwrap"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
version = "0.0.4"
|
version = "0.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vec_map"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yaml-rust"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
|
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||||
|
"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
|
||||||
"checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf"
|
"checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf"
|
||||||
"checksum bit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2b645c5c09a7d4035949cfce1a915785aaad6f17800c35fda8a8c311c491f284"
|
"checksum bit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2b645c5c09a7d4035949cfce1a915785aaad6f17800c35fda8a8c311c491f284"
|
||||||
|
"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd"
|
||||||
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
|
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
|
||||||
|
"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
|
||||||
"checksum enum-primitive-derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2b90e520ec62c1864c8c78d637acbfe8baf5f63240f2fb8165b8325c07812dd"
|
"checksum enum-primitive-derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2b90e520ec62c1864c8c78d637acbfe8baf5f63240f2fb8165b8325c07812dd"
|
||||||
|
"checksum libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "6281b86796ba5e4366000be6e9e18bf35580adf9e63fbe2294aadb587613a319"
|
||||||
"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31"
|
"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31"
|
||||||
"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32"
|
"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32"
|
||||||
|
"checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
|
||||||
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
|
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
|
||||||
|
"checksum redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)" = "12229c14a0f65c4f1cb046a3b52047cdd9da1f4b30f8a39c5063c8bae515e252"
|
||||||
|
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
|
||||||
|
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||||
"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
|
"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
|
||||||
"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
|
"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
|
||||||
|
"checksum termion 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a8fb22f7cde82c8220e5aeacb3258ed7ce996142c77cba193f203515e26c330"
|
||||||
|
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||||
|
"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
|
||||||
"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
|
"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
|
||||||
|
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
|
||||||
|
"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770"
|
||||||
|
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
"checksum yaml-rust 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e66366e18dc58b46801afbf2ca7661a9f59cc8c5962c29892b6039b4f86fa992"
|
||||||
|
|
|
@ -10,7 +10,3 @@ enum-primitive-derive = "^0.1"
|
||||||
num-traits = "^0.1"
|
num-traits = "^0.1"
|
||||||
bit = "^0.1"
|
bit = "^0.1"
|
||||||
clap = {version = "2.33", features = ["color", "yaml"]}
|
clap = {version = "2.33", features = ["color", "yaml"]}
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "disass"
|
|
||||||
path = "src/disass.rs"
|
|
|
@ -1,373 +0,0 @@
|
||||||
use crate::bit::BitIndex;
|
|
||||||
use crate::num_traits::FromPrimitive;
|
|
||||||
use std::convert::TryFrom;
|
|
||||||
|
|
||||||
pub use super::display;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub enum ArmError {
|
|
||||||
UnknownInstructionFormat(u32),
|
|
||||||
UndefinedConditionCode(u32),
|
|
||||||
InvalidShiftType(u32),
|
|
||||||
InvalidHSBits(u32),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Primitive)]
|
|
||||||
pub enum ArmCond {
|
|
||||||
Equal = 0b0000,
|
|
||||||
NotEqual = 0b0001,
|
|
||||||
UnsignedHigherOrSame = 0b0010,
|
|
||||||
UnsignedLower = 0b0011,
|
|
||||||
Negative = 0b0100,
|
|
||||||
PositiveOrZero = 0b0101,
|
|
||||||
Overflow = 0b0110,
|
|
||||||
NoOverflow = 0b0111,
|
|
||||||
UnsignedHigher = 0b1000,
|
|
||||||
UnsignedLowerOrSame = 0b1001,
|
|
||||||
GreaterOrEqual = 0b1010,
|
|
||||||
LessThan = 0b1011,
|
|
||||||
GreaterThan = 0b1100,
|
|
||||||
LessThanOrEqual = 0b1101,
|
|
||||||
Always = 0b1110,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
pub enum ArmInstructionFormat {
|
|
||||||
// Branch and Exchange
|
|
||||||
BX,
|
|
||||||
// Branch /w Link
|
|
||||||
B_BL,
|
|
||||||
// Multiply and Multiply-Accumulate
|
|
||||||
MUL_MLA,
|
|
||||||
// Multiply Long and Multiply-Accumulate Long
|
|
||||||
MULL_MLAL,
|
|
||||||
// Single Data Transfer
|
|
||||||
LDR_STR,
|
|
||||||
// Halfword and Signed Data Transfer
|
|
||||||
LDR_STR_HS_REG,
|
|
||||||
// Halfword and Signed Data Transfer
|
|
||||||
LDR_STR_HS_IMM,
|
|
||||||
// Data Processing
|
|
||||||
DP,
|
|
||||||
// Block Data Transfer
|
|
||||||
LDM_STM,
|
|
||||||
// Single Data Swap
|
|
||||||
SWP,
|
|
||||||
// Transfer PSR contents to a register
|
|
||||||
MRS,
|
|
||||||
// Transfer register contents to PSR
|
|
||||||
MSR_REG,
|
|
||||||
// Tanssfer immediate/register to PSR flags only
|
|
||||||
MSR_FLAGS,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Primitive)]
|
|
||||||
pub enum ArmOpCode {
|
|
||||||
AND = 0b0000,
|
|
||||||
EOR = 0b0001,
|
|
||||||
SUB = 0b0010,
|
|
||||||
RSB = 0b0011,
|
|
||||||
ADD = 0b0100,
|
|
||||||
ADC = 0b0101,
|
|
||||||
SBC = 0b0110,
|
|
||||||
RSC = 0b0111,
|
|
||||||
TST = 0b1000,
|
|
||||||
TEQ = 0b1001,
|
|
||||||
CMP = 0b1010,
|
|
||||||
CMN = 0b1011,
|
|
||||||
ORR = 0b1100,
|
|
||||||
MOV = 0b1101,
|
|
||||||
BIC = 0b1110,
|
|
||||||
MVN = 0b1111,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Primitive)]
|
|
||||||
pub enum ArmHalfwordTransferType {
|
|
||||||
UnsignedHalfwords = 0b01,
|
|
||||||
SignedByte = 0b10,
|
|
||||||
SignedHalfwords = 0b11,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
|
||||||
pub struct ArmInstruction {
|
|
||||||
pub cond: ArmCond,
|
|
||||||
pub fmt: ArmInstructionFormat,
|
|
||||||
pub raw: u32,
|
|
||||||
pub pc: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<(u32, u32)> for ArmInstruction {
|
|
||||||
type Error = ArmError;
|
|
||||||
|
|
||||||
fn try_from(value: (u32, u32)) -> Result<Self, Self::Error> {
|
|
||||||
use ArmInstructionFormat::*;
|
|
||||||
let (raw, addr) = value;
|
|
||||||
|
|
||||||
let cond_code = raw.bit_range(28..32) as u8;
|
|
||||||
let cond = match ArmCond::from_u8(cond_code) {
|
|
||||||
Some(cond) => Ok(cond),
|
|
||||||
None => Err(ArmError::UndefinedConditionCode(cond_code as u32)),
|
|
||||||
}?;
|
|
||||||
|
|
||||||
let fmt = if (0x0fff_fff0 & raw) == 0x012f_ff10 {
|
|
||||||
Ok(BX)
|
|
||||||
} else if (0x0e00_0000 & raw) == 0x0a00_0000 {
|
|
||||||
Ok(B_BL)
|
|
||||||
} else if (0xe000_0010 & raw) == 0x0600_0000 {
|
|
||||||
Err(ArmError::UnknownInstructionFormat(raw))
|
|
||||||
} else if (0x0fb0_0ff0 & raw) == 0x0100_0090 {
|
|
||||||
Ok(SWP)
|
|
||||||
} else if (0x0fc0_00f0 & raw) == 0x0000_0090 {
|
|
||||||
Ok(MUL_MLA)
|
|
||||||
} else if (0x0f80_00f0 & raw) == 0x0080_0090 {
|
|
||||||
Ok(MULL_MLAL)
|
|
||||||
} else if (0x0fbf_0fff & raw) == 0x010f_0000 {
|
|
||||||
Ok(MRS)
|
|
||||||
} else if (0x0fbf_fff0 & raw) == 0x0129_f000 {
|
|
||||||
Ok(MSR_REG)
|
|
||||||
} else if (0x0dbf_f000 & raw) == 0x0128_f000 {
|
|
||||||
Ok(MSR_FLAGS)
|
|
||||||
} else if (0x0c00_0000 & raw) == 0x0400_0000 {
|
|
||||||
Ok(LDR_STR)
|
|
||||||
} else if (0x0e40_0F90 & raw) == 0x0000_0090 {
|
|
||||||
Ok(LDR_STR_HS_REG)
|
|
||||||
} else if (0x0e40_0090 & raw) == 0x0040_0090 {
|
|
||||||
Ok(LDR_STR_HS_IMM)
|
|
||||||
} else if (0x0e00_0000 & raw) == 0x0800_0000 {
|
|
||||||
Ok(LDM_STM)
|
|
||||||
} else if (0x0c00_0000 & raw) == 0x0000_0000 {
|
|
||||||
Ok(DP)
|
|
||||||
} else {
|
|
||||||
Err(ArmError::UnknownInstructionFormat(raw))
|
|
||||||
}?;
|
|
||||||
|
|
||||||
Ok(ArmInstruction {
|
|
||||||
cond: cond,
|
|
||||||
fmt: fmt,
|
|
||||||
raw: raw,
|
|
||||||
pc: addr,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Primitive)]
|
|
||||||
pub enum ArmShiftType {
|
|
||||||
LSL = 0,
|
|
||||||
LSR = 1,
|
|
||||||
ASR = 2,
|
|
||||||
ROR = 3,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub enum ArmShift {
|
|
||||||
ImmediateShift(u32, ArmShiftType),
|
|
||||||
RegisterShift(usize, ArmShiftType),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<u32> for ArmShift {
|
|
||||||
type Error = ArmError;
|
|
||||||
|
|
||||||
fn try_from(v: u32) -> Result<Self, Self::Error> {
|
|
||||||
let typ = match ArmShiftType::from_u8(v.bit_range(5..7) as u8) {
|
|
||||||
Some(s) => Ok(s),
|
|
||||||
_ => Err(ArmError::InvalidShiftType(v.bit_range(5..7))),
|
|
||||||
}?;
|
|
||||||
if v.bit(4) {
|
|
||||||
let rs = v.bit_range(8..12) as usize;
|
|
||||||
Ok(ArmShift::RegisterShift(rs, typ))
|
|
||||||
} else {
|
|
||||||
let amount = v.bit_range(7..12) as u32;
|
|
||||||
Ok(ArmShift::ImmediateShift(amount, typ))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub enum ArmShiftedValue {
|
|
||||||
ImmediateValue(i32),
|
|
||||||
RotatedImmediate(u32, u32),
|
|
||||||
ShiftedRegister {
|
|
||||||
reg: usize,
|
|
||||||
shift: ArmShift,
|
|
||||||
added: Option<bool>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ArmShiftedValue {
|
|
||||||
/// Decode operand2 as an immediate value
|
|
||||||
pub fn decode_rotated_immediate(&self) -> Option<i32> {
|
|
||||||
if let ArmShiftedValue::RotatedImmediate(immediate, rotate) = self {
|
|
||||||
return Some(immediate.rotate_right(*rotate) as i32);
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ArmInstruction {
|
|
||||||
pub fn rn(&self) -> usize {
|
|
||||||
match self.fmt {
|
|
||||||
ArmInstructionFormat::MUL_MLA => self.raw.bit_range(12..16) as usize,
|
|
||||||
ArmInstructionFormat::MULL_MLAL => self.raw.bit_range(8..12) as usize,
|
|
||||||
ArmInstructionFormat::BX => self.raw.bit_range(0..4) as usize,
|
|
||||||
_ => self.raw.bit_range(16..20) as usize,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn rd(&self) -> usize {
|
|
||||||
match self.fmt {
|
|
||||||
ArmInstructionFormat::MUL_MLA => self.raw.bit_range(16..20) as usize,
|
|
||||||
_ => self.raw.bit_range(12..16) as usize,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn rm(&self) -> usize {
|
|
||||||
self.raw.bit_range(0..4) as usize
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn rs(&self) -> usize {
|
|
||||||
self.raw.bit_range(8..12) as usize
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn rd_lo(&self) -> usize {
|
|
||||||
self.raw.bit_range(12..16) as usize
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn rd_hi(&self) -> usize {
|
|
||||||
self.raw.bit_range(16..20) as usize
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn opcode(&self) -> Option<ArmOpCode> {
|
|
||||||
ArmOpCode::from_u32(self.raw.bit_range(21..25))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn branch_offset(&self) -> i32 {
|
|
||||||
((((self.raw << 8) as i32) >> 8) << 2) + 8
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_flag(&self) -> bool {
|
|
||||||
self.raw.bit(20)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_cond_flag(&self) -> bool {
|
|
||||||
self.raw.bit(20)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_back_flag(&self) -> bool {
|
|
||||||
self.raw.bit(21)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn accumulate_flag(&self) -> bool {
|
|
||||||
self.raw.bit(21)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn u_flag(&self) -> bool {
|
|
||||||
self.raw.bit(22)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn halfword_data_transfer_type(&self) -> Result<ArmHalfwordTransferType, ArmError> {
|
|
||||||
match ArmHalfwordTransferType::from_u32((self.raw & 0b1100000) >> 5) {
|
|
||||||
Some(x) => Ok(x),
|
|
||||||
None => Err(ArmError::InvalidHSBits(self.raw)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn transfer_size(&self) -> usize {
|
|
||||||
if self.raw.bit(22) {
|
|
||||||
1
|
|
||||||
} else {
|
|
||||||
4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_loading_psr_and_forcing_user_mode(&self) -> bool {
|
|
||||||
self.raw.bit(22)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn spsr_flag(&self) -> bool {
|
|
||||||
self.raw.bit(22)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_offset_flag(&self) -> bool {
|
|
||||||
self.raw.bit(23)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pre_index_flag(&self) -> bool {
|
|
||||||
self.raw.bit(24)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn link_flag(&self) -> bool {
|
|
||||||
self.raw.bit(24)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// gets offset used by ldr/str instructions
|
|
||||||
pub fn ldr_str_offset(&self) -> ArmShiftedValue {
|
|
||||||
let ofs = self.raw.bit_range(0..12);
|
|
||||||
if self.raw.bit(25) {
|
|
||||||
let rm = ofs & 0xf;
|
|
||||||
let shift = ArmShift::try_from(ofs).unwrap();
|
|
||||||
ArmShiftedValue::ShiftedRegister {
|
|
||||||
reg: rm as usize,
|
|
||||||
shift: shift,
|
|
||||||
added: Some(self.add_offset_flag()),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let ofs = if self.add_offset_flag() {
|
|
||||||
ofs as i32
|
|
||||||
} else {
|
|
||||||
-(ofs as i32)
|
|
||||||
};
|
|
||||||
ArmShiftedValue::ImmediateValue(ofs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ldr_str_hs_offset(&self) -> Option<ArmShiftedValue> {
|
|
||||||
match self.fmt {
|
|
||||||
ArmInstructionFormat::LDR_STR_HS_IMM => {
|
|
||||||
let offset8 = (self.raw.bit_range(8..12) << 4) + self.raw.bit_range(0..4);
|
|
||||||
let offset8 = if self.add_offset_flag() {
|
|
||||||
offset8 as i32
|
|
||||||
} else {
|
|
||||||
-(offset8 as i32)
|
|
||||||
};
|
|
||||||
Some(ArmShiftedValue::ImmediateValue(offset8))
|
|
||||||
},
|
|
||||||
ArmInstructionFormat::LDR_STR_HS_REG => {
|
|
||||||
Some(ArmShiftedValue::ShiftedRegister {
|
|
||||||
reg: (self.raw & 0xf) as usize,
|
|
||||||
shift: ArmShift::ImmediateShift(0, ArmShiftType::LSL),
|
|
||||||
added: Some(self.add_offset_flag())
|
|
||||||
})
|
|
||||||
},
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn operand2(&self) -> ArmShiftedValue {
|
|
||||||
let op2 = self.raw.bit_range(0..12);
|
|
||||||
if self.raw.bit(25) {
|
|
||||||
let immediate = op2 & 0xff;
|
|
||||||
let rotate = 2 * op2.bit_range(8..12);
|
|
||||||
ArmShiftedValue::RotatedImmediate(immediate, rotate)
|
|
||||||
} else {
|
|
||||||
let reg = op2 & 0xf;
|
|
||||||
let shift = ArmShift::try_from(op2).unwrap(); // TODO error handling
|
|
||||||
ArmShiftedValue::ShiftedRegister {
|
|
||||||
reg: reg as usize,
|
|
||||||
shift: shift,
|
|
||||||
added: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn register_list(&self) -> Vec<usize> {
|
|
||||||
let list_bits = self.raw & 0xffff;
|
|
||||||
let mut list = Vec::with_capacity(16);
|
|
||||||
for i in 0..16 {
|
|
||||||
if (list_bits & (1 << i)) != 0 {
|
|
||||||
list.push(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
list
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
use super::super::{reg_string, REG_PC};
|
use super::super::{reg_string, REG_PC};
|
||||||
use super::arm_isa::{
|
use super::{
|
||||||
ArmCond, ArmInstruction, ArmInstructionFormat, ArmShiftedValue, ArmOpCode, ArmShift,
|
ArmCond, ArmHalfwordTransferType, ArmInstruction, ArmInstructionFormat, ArmOpCode, ArmShift,
|
||||||
ArmShiftType, ArmHalfwordTransferType
|
ArmShiftType, ArmShiftedValue,
|
||||||
};
|
};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
@ -110,7 +110,11 @@ impl ArmInstruction {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_cond_mark(&self) -> &str {
|
fn set_cond_mark(&self) -> &str {
|
||||||
if self.set_cond_flag() { "s" } else { "" }
|
if self.set_cond_flag() {
|
||||||
|
"s"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_data_processing(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt_data_processing(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
@ -145,21 +149,27 @@ impl ArmInstruction {
|
||||||
),
|
),
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
let operand2 = self.operand2();
|
let operand2 = self.operand2().unwrap();
|
||||||
match operand2 {
|
match operand2 {
|
||||||
ArmShiftedValue::RotatedImmediate(_, _) => {
|
ArmShiftedValue::RotatedImmediate(_, _) => {
|
||||||
let value = operand2.decode_rotated_immediate().unwrap();
|
let value = operand2.decode_rotated_immediate().unwrap();
|
||||||
write!(f, "#{}\t; {:#x}", value, value)
|
write!(f, "#{}\t; {:#x}", value, value)
|
||||||
}
|
}
|
||||||
ArmShiftedValue::ShiftedRegister{reg, shift, added: _} => {
|
ArmShiftedValue::ShiftedRegister {
|
||||||
write!(f, "{}", self.make_shifted_reg_string(reg, shift))
|
reg,
|
||||||
}
|
shift,
|
||||||
|
added: _,
|
||||||
|
} => write!(f, "{}", self.make_shifted_reg_string(reg, shift)),
|
||||||
_ => write!(f, "RegisterNotImpl"),
|
_ => write!(f, "RegisterNotImpl"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn auto_incremenet_mark(&self) -> &str {
|
fn auto_incremenet_mark(&self) -> &str {
|
||||||
if self.write_back_flag() { "!" } else { "" }
|
if self.write_back_flag() {
|
||||||
|
"!"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_rn_offset(&self, f: &mut fmt::Formatter, offset: ArmShiftedValue) -> fmt::Result {
|
fn fmt_rn_offset(&self, f: &mut fmt::Formatter, offset: ArmShiftedValue) -> fmt::Result {
|
||||||
|
@ -176,8 +186,16 @@ impl ArmInstruction {
|
||||||
Some(format!("\t; {:#x}", value_for_commnet)),
|
Some(format!("\t; {:#x}", value_for_commnet)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
ArmShiftedValue::ShiftedRegister{reg, shift, added: Some(added)} => (
|
ArmShiftedValue::ShiftedRegister {
|
||||||
format!("{}{}", if added { "" } else { "-" }, self.make_shifted_reg_string(reg, shift)),
|
reg,
|
||||||
|
shift,
|
||||||
|
added: Some(added),
|
||||||
|
} => (
|
||||||
|
format!(
|
||||||
|
"{}{}",
|
||||||
|
if added { "" } else { "-" },
|
||||||
|
self.make_shifted_reg_string(reg, shift)
|
||||||
|
),
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
_ => panic!("bad barrel shifter"),
|
_ => panic!("bad barrel shifter"),
|
||||||
|
@ -211,7 +229,7 @@ impl ArmInstruction {
|
||||||
Rd = reg_string(self.rd()),
|
Rd = reg_string(self.rd()),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
self.fmt_rn_offset(f, self.ldr_str_offset())
|
self.fmt_rn_offset(f, self.ldr_str_offset().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_ldm_stm(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt_ldm_stm(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
@ -284,7 +302,11 @@ impl ArmInstruction {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sign_mark(&self) -> &str {
|
fn sign_mark(&self) -> &str {
|
||||||
if self.u_flag() { "s" } else { "u" }
|
if self.u_flag() {
|
||||||
|
"s"
|
||||||
|
} else {
|
||||||
|
"u"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_mull_mlal(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt_mull_mlal(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
@ -314,7 +336,7 @@ impl ArmInstruction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_ldr_str_hs(&self, f: &mut fmt::Formatter) -> fmt::Result{
|
fn fmt_ldr_str_hs(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
if let Ok(transfer_type) = self.halfword_data_transfer_type() {
|
if let Ok(transfer_type) = self.halfword_data_transfer_type() {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
|
|
|
@ -1,2 +1,403 @@
|
||||||
pub mod arm_isa;
|
|
||||||
pub mod display;
|
pub mod display;
|
||||||
|
|
||||||
|
use crate::bit::BitIndex;
|
||||||
|
use crate::num_traits::FromPrimitive;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum ArmDecodeErrorKind {
|
||||||
|
UnknownInstructionFormat,
|
||||||
|
DecodedPartDoesNotBelongToInstruction,
|
||||||
|
UndefinedConditionCode(u32),
|
||||||
|
InvalidShiftType(u32),
|
||||||
|
InvalidHSBits(u32),
|
||||||
|
}
|
||||||
|
use ArmDecodeErrorKind::*;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct ArmDecodeError {
|
||||||
|
pub kind: ArmDecodeErrorKind,
|
||||||
|
pub insn: u32,
|
||||||
|
pub addr: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ArmDecodeError {
|
||||||
|
fn new(kind: ArmDecodeErrorKind, insn: u32, addr: u32) -> ArmDecodeError {
|
||||||
|
ArmDecodeError {
|
||||||
|
kind: kind,
|
||||||
|
insn: insn,
|
||||||
|
addr: addr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Primitive)]
|
||||||
|
pub enum ArmCond {
|
||||||
|
Equal = 0b0000,
|
||||||
|
NotEqual = 0b0001,
|
||||||
|
UnsignedHigherOrSame = 0b0010,
|
||||||
|
UnsignedLower = 0b0011,
|
||||||
|
Negative = 0b0100,
|
||||||
|
PositiveOrZero = 0b0101,
|
||||||
|
Overflow = 0b0110,
|
||||||
|
NoOverflow = 0b0111,
|
||||||
|
UnsignedHigher = 0b1000,
|
||||||
|
UnsignedLowerOrSame = 0b1001,
|
||||||
|
GreaterOrEqual = 0b1010,
|
||||||
|
LessThan = 0b1011,
|
||||||
|
GreaterThan = 0b1100,
|
||||||
|
LessThanOrEqual = 0b1101,
|
||||||
|
Always = 0b1110,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub enum ArmInstructionFormat {
|
||||||
|
// Branch and Exchange
|
||||||
|
BX,
|
||||||
|
// Branch /w Link
|
||||||
|
B_BL,
|
||||||
|
// Multiply and Multiply-Accumulate
|
||||||
|
MUL_MLA,
|
||||||
|
// Multiply Long and Multiply-Accumulate Long
|
||||||
|
MULL_MLAL,
|
||||||
|
// Single Data Transfer
|
||||||
|
LDR_STR,
|
||||||
|
// Halfword and Signed Data Transfer
|
||||||
|
LDR_STR_HS_REG,
|
||||||
|
// Halfword and Signed Data Transfer
|
||||||
|
LDR_STR_HS_IMM,
|
||||||
|
// Data Processing
|
||||||
|
DP,
|
||||||
|
// Block Data Transfer
|
||||||
|
LDM_STM,
|
||||||
|
// Single Data Swap
|
||||||
|
SWP,
|
||||||
|
// Transfer PSR contents to a register
|
||||||
|
MRS,
|
||||||
|
// Transfer register contents to PSR
|
||||||
|
MSR_REG,
|
||||||
|
// Tanssfer immediate/register to PSR flags only
|
||||||
|
MSR_FLAGS,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Primitive)]
|
||||||
|
pub enum ArmOpCode {
|
||||||
|
AND = 0b0000,
|
||||||
|
EOR = 0b0001,
|
||||||
|
SUB = 0b0010,
|
||||||
|
RSB = 0b0011,
|
||||||
|
ADD = 0b0100,
|
||||||
|
ADC = 0b0101,
|
||||||
|
SBC = 0b0110,
|
||||||
|
RSC = 0b0111,
|
||||||
|
TST = 0b1000,
|
||||||
|
TEQ = 0b1001,
|
||||||
|
CMP = 0b1010,
|
||||||
|
CMN = 0b1011,
|
||||||
|
ORR = 0b1100,
|
||||||
|
MOV = 0b1101,
|
||||||
|
BIC = 0b1110,
|
||||||
|
MVN = 0b1111,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Primitive)]
|
||||||
|
pub enum ArmHalfwordTransferType {
|
||||||
|
UnsignedHalfwords = 0b01,
|
||||||
|
SignedByte = 0b10,
|
||||||
|
SignedHalfwords = 0b11,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
pub struct ArmInstruction {
|
||||||
|
pub cond: ArmCond,
|
||||||
|
pub fmt: ArmInstructionFormat,
|
||||||
|
pub raw: u32,
|
||||||
|
pub pc: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<(u32, u32)> for ArmInstruction {
|
||||||
|
type Error = ArmDecodeError;
|
||||||
|
|
||||||
|
fn try_from(value: (u32, u32)) -> Result<Self, Self::Error> {
|
||||||
|
use ArmInstructionFormat::*;
|
||||||
|
let (raw, addr) = value;
|
||||||
|
|
||||||
|
let cond_code = raw.bit_range(28..32) as u8;
|
||||||
|
let cond = match ArmCond::from_u8(cond_code) {
|
||||||
|
Some(cond) => Ok(cond),
|
||||||
|
None => Err(ArmDecodeError::new(
|
||||||
|
UndefinedConditionCode(cond_code as u32),
|
||||||
|
raw,
|
||||||
|
addr,
|
||||||
|
)),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
let fmt = if (0x0fff_fff0 & raw) == 0x012f_ff10 {
|
||||||
|
Ok(BX)
|
||||||
|
} else if (0x0e00_0000 & raw) == 0x0a00_0000 {
|
||||||
|
Ok(B_BL)
|
||||||
|
} else if (0xe000_0010 & raw) == 0x0600_0000 {
|
||||||
|
Err(ArmDecodeError::new(UnknownInstructionFormat, raw, addr))
|
||||||
|
} else if (0x0fb0_0ff0 & raw) == 0x0100_0090 {
|
||||||
|
Ok(SWP)
|
||||||
|
} else if (0x0fc0_00f0 & raw) == 0x0000_0090 {
|
||||||
|
Ok(MUL_MLA)
|
||||||
|
} else if (0x0f80_00f0 & raw) == 0x0080_0090 {
|
||||||
|
Ok(MULL_MLAL)
|
||||||
|
} else if (0x0fbf_0fff & raw) == 0x010f_0000 {
|
||||||
|
Ok(MRS)
|
||||||
|
} else if (0x0fbf_fff0 & raw) == 0x0129_f000 {
|
||||||
|
Ok(MSR_REG)
|
||||||
|
} else if (0x0dbf_f000 & raw) == 0x0128_f000 {
|
||||||
|
Ok(MSR_FLAGS)
|
||||||
|
} else if (0x0c00_0000 & raw) == 0x0400_0000 {
|
||||||
|
Ok(LDR_STR)
|
||||||
|
} else if (0x0e40_0F90 & raw) == 0x0000_0090 {
|
||||||
|
Ok(LDR_STR_HS_REG)
|
||||||
|
} else if (0x0e40_0090 & raw) == 0x0040_0090 {
|
||||||
|
Ok(LDR_STR_HS_IMM)
|
||||||
|
} else if (0x0e00_0000 & raw) == 0x0800_0000 {
|
||||||
|
Ok(LDM_STM)
|
||||||
|
} else if (0x0c00_0000 & raw) == 0x0000_0000 {
|
||||||
|
Ok(DP)
|
||||||
|
} else {
|
||||||
|
Err(ArmDecodeError::new(UnknownInstructionFormat, raw, addr))
|
||||||
|
}?;
|
||||||
|
|
||||||
|
Ok(ArmInstruction {
|
||||||
|
cond: cond,
|
||||||
|
fmt: fmt,
|
||||||
|
raw: raw,
|
||||||
|
pc: addr,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Primitive)]
|
||||||
|
pub enum ArmShiftType {
|
||||||
|
LSL = 0,
|
||||||
|
LSR = 1,
|
||||||
|
ASR = 2,
|
||||||
|
ROR = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum ArmShift {
|
||||||
|
ImmediateShift(u32, ArmShiftType),
|
||||||
|
RegisterShift(usize, ArmShiftType),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<u32> for ArmShift {
|
||||||
|
type Error = ArmDecodeErrorKind;
|
||||||
|
|
||||||
|
fn try_from(v: u32) -> Result<Self, Self::Error> {
|
||||||
|
let typ = match ArmShiftType::from_u8(v.bit_range(5..7) as u8) {
|
||||||
|
Some(s) => Ok(s),
|
||||||
|
_ => Err(InvalidShiftType(v.bit_range(5..7))),
|
||||||
|
}?;
|
||||||
|
if v.bit(4) {
|
||||||
|
let rs = v.bit_range(8..12) as usize;
|
||||||
|
Ok(ArmShift::RegisterShift(rs, typ))
|
||||||
|
} else {
|
||||||
|
let amount = v.bit_range(7..12) as u32;
|
||||||
|
Ok(ArmShift::ImmediateShift(amount, typ))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum ArmShiftedValue {
|
||||||
|
ImmediateValue(i32),
|
||||||
|
RotatedImmediate(u32, u32),
|
||||||
|
ShiftedRegister {
|
||||||
|
reg: usize,
|
||||||
|
shift: ArmShift,
|
||||||
|
added: Option<bool>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ArmShiftedValue {
|
||||||
|
/// Decode operand2 as an immediate value
|
||||||
|
pub fn decode_rotated_immediate(&self) -> Option<i32> {
|
||||||
|
if let ArmShiftedValue::RotatedImmediate(immediate, rotate) = self {
|
||||||
|
return Some(immediate.rotate_right(*rotate) as i32);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ArmInstruction {
|
||||||
|
fn make_decode_error(&self, kind: ArmDecodeErrorKind) -> ArmDecodeError {
|
||||||
|
ArmDecodeError {
|
||||||
|
kind: kind,
|
||||||
|
insn: self.raw,
|
||||||
|
addr: self.pc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rn(&self) -> usize {
|
||||||
|
match self.fmt {
|
||||||
|
ArmInstructionFormat::MUL_MLA => self.raw.bit_range(12..16) as usize,
|
||||||
|
ArmInstructionFormat::MULL_MLAL => self.raw.bit_range(8..12) as usize,
|
||||||
|
ArmInstructionFormat::BX => self.raw.bit_range(0..4) as usize,
|
||||||
|
_ => self.raw.bit_range(16..20) as usize,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rd(&self) -> usize {
|
||||||
|
match self.fmt {
|
||||||
|
ArmInstructionFormat::MUL_MLA => self.raw.bit_range(16..20) as usize,
|
||||||
|
_ => self.raw.bit_range(12..16) as usize,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rm(&self) -> usize {
|
||||||
|
self.raw.bit_range(0..4) as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rs(&self) -> usize {
|
||||||
|
self.raw.bit_range(8..12) as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rd_lo(&self) -> usize {
|
||||||
|
self.raw.bit_range(12..16) as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rd_hi(&self) -> usize {
|
||||||
|
self.raw.bit_range(16..20) as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn opcode(&self) -> Option<ArmOpCode> {
|
||||||
|
ArmOpCode::from_u32(self.raw.bit_range(21..25))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn branch_offset(&self) -> i32 {
|
||||||
|
((((self.raw << 8) as i32) >> 8) << 2) + 8
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_flag(&self) -> bool {
|
||||||
|
self.raw.bit(20)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_cond_flag(&self) -> bool {
|
||||||
|
self.raw.bit(20)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_back_flag(&self) -> bool {
|
||||||
|
self.raw.bit(21)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn accumulate_flag(&self) -> bool {
|
||||||
|
self.raw.bit(21)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn u_flag(&self) -> bool {
|
||||||
|
self.raw.bit(22)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn halfword_data_transfer_type(&self) -> Result<ArmHalfwordTransferType, ArmDecodeError> {
|
||||||
|
let bits = (self.raw & 0b1100000) >> 5;
|
||||||
|
match ArmHalfwordTransferType::from_u32(bits) {
|
||||||
|
Some(x) => Ok(x),
|
||||||
|
None => Err(ArmDecodeError::new(InvalidHSBits(bits), self.raw, self.pc)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn transfer_size(&self) -> usize {
|
||||||
|
if self.raw.bit(22) {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_loading_psr_and_forcing_user_mode(&self) -> bool {
|
||||||
|
self.raw.bit(22)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spsr_flag(&self) -> bool {
|
||||||
|
self.raw.bit(22)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_offset_flag(&self) -> bool {
|
||||||
|
self.raw.bit(23)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pre_index_flag(&self) -> bool {
|
||||||
|
self.raw.bit(24)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn link_flag(&self) -> bool {
|
||||||
|
self.raw.bit(24)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// gets offset used by ldr/str instructions
|
||||||
|
pub fn ldr_str_offset(&self) -> Result<ArmShiftedValue, ArmDecodeError> {
|
||||||
|
let ofs = self.raw.bit_range(0..12);
|
||||||
|
if self.raw.bit(25) {
|
||||||
|
let rm = ofs & 0xf;
|
||||||
|
let shift = ArmShift::try_from(ofs).map_err(|kind| self.make_decode_error(kind))?;
|
||||||
|
Ok(ArmShiftedValue::ShiftedRegister {
|
||||||
|
reg: rm as usize,
|
||||||
|
shift: shift,
|
||||||
|
added: Some(self.add_offset_flag()),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
let ofs = if self.add_offset_flag() {
|
||||||
|
ofs as i32
|
||||||
|
} else {
|
||||||
|
-(ofs as i32)
|
||||||
|
};
|
||||||
|
Ok(ArmShiftedValue::ImmediateValue(ofs))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ldr_str_hs_offset(&self) -> Result<ArmShiftedValue, ArmDecodeError> {
|
||||||
|
match self.fmt {
|
||||||
|
ArmInstructionFormat::LDR_STR_HS_IMM => {
|
||||||
|
let offset8 = (self.raw.bit_range(8..12) << 4) + self.raw.bit_range(0..4);
|
||||||
|
let offset8 = if self.add_offset_flag() {
|
||||||
|
offset8 as i32
|
||||||
|
} else {
|
||||||
|
-(offset8 as i32)
|
||||||
|
};
|
||||||
|
Ok(ArmShiftedValue::ImmediateValue(offset8))
|
||||||
|
}
|
||||||
|
ArmInstructionFormat::LDR_STR_HS_REG => Ok(ArmShiftedValue::ShiftedRegister {
|
||||||
|
reg: (self.raw & 0xf) as usize,
|
||||||
|
shift: ArmShift::ImmediateShift(0, ArmShiftType::LSL),
|
||||||
|
added: Some(self.add_offset_flag()),
|
||||||
|
}),
|
||||||
|
_ => Err(self.make_decode_error(DecodedPartDoesNotBelongToInstruction)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn operand2(&self) -> Result<ArmShiftedValue, ArmDecodeError> {
|
||||||
|
let op2 = self.raw.bit_range(0..12);
|
||||||
|
if self.raw.bit(25) {
|
||||||
|
let immediate = op2 & 0xff;
|
||||||
|
let rotate = 2 * op2.bit_range(8..12);
|
||||||
|
Ok(ArmShiftedValue::RotatedImmediate(immediate, rotate))
|
||||||
|
} else {
|
||||||
|
let reg = op2 & 0xf;
|
||||||
|
let shift = ArmShift::try_from(op2).map_err(|kind| self.make_decode_error(kind))?; // TODO error handling
|
||||||
|
Ok(ArmShiftedValue::ShiftedRegister {
|
||||||
|
reg: reg as usize,
|
||||||
|
shift: shift,
|
||||||
|
added: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register_list(&self) -> Vec<usize> {
|
||||||
|
let list_bits = self.raw & 0xffff;
|
||||||
|
let mut list = Vec::with_capacity(16);
|
||||||
|
for i in 0..16 {
|
||||||
|
if (list_bits & (1 << i)) != 0 {
|
||||||
|
list.push(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
108
src/arm7tdmi/cpu.rs
Normal file
108
src/arm7tdmi/cpu.rs
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
use crate::num_traits::FromPrimitive;
|
||||||
|
|
||||||
|
use super::arm::*;
|
||||||
|
use super::sysbus::SysBus;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum CpuError {
|
||||||
|
IllegalInstruction,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type CpuResult<T> = Result<T, CpuError>;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum CpuState {
|
||||||
|
ARM,
|
||||||
|
THUMB,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Primitive)]
|
||||||
|
#[repr(u8)]
|
||||||
|
enum CpuMode {
|
||||||
|
User = 0b10000,
|
||||||
|
Fiq = 0b10001,
|
||||||
|
Irq = 0b10010,
|
||||||
|
Supervisor = 0b10011,
|
||||||
|
Abort = 0b10111,
|
||||||
|
Undefined = 0b11011,
|
||||||
|
System = 0b11111,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CpuModeContext {
|
||||||
|
// r8-r14
|
||||||
|
banked_gpr: [u32; 7],
|
||||||
|
spsr: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Core {
|
||||||
|
pc: u32,
|
||||||
|
// r0-r7
|
||||||
|
gpr: [u32; 8],
|
||||||
|
cpsr: u32,
|
||||||
|
|
||||||
|
mode: CpuMode,
|
||||||
|
state: CpuState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Core {
|
||||||
|
pub fn new() -> Core {
|
||||||
|
Core {
|
||||||
|
pc: 0,
|
||||||
|
gpr: [0; 8],
|
||||||
|
cpsr: 0,
|
||||||
|
mode: CpuMode::System,
|
||||||
|
state: CpuState::ARM,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_reg(&self, reg_num: usize) -> u32 {
|
||||||
|
match reg_num {
|
||||||
|
0...7 => self.gpr[reg_num],
|
||||||
|
15 => self.pc,
|
||||||
|
_ => unimplemented!("TODO banked registers"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_reg(&mut self, reg_num: usize, val: u32) {
|
||||||
|
match reg_num {
|
||||||
|
0...7 => self.gpr[reg_num] = val,
|
||||||
|
15 => self.pc = val,
|
||||||
|
_ => unimplemented!("TODO banked registers"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resets the cpu
|
||||||
|
pub fn reset(&mut self) {
|
||||||
|
self.pc = 0;
|
||||||
|
self.cpsr = 0;
|
||||||
|
self.mode = CpuMode::System;
|
||||||
|
self.state = CpuState::ARM;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn word_size(&self) -> usize {
|
||||||
|
match self.state {
|
||||||
|
CpuState::ARM => 4,
|
||||||
|
CpuState::THUMB => 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn advance_pc(&mut self) {
|
||||||
|
self.pc = self.pc.wrapping_add(self.word_size() as u32)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn step_arm(&mut self, sysbus: &mut SysBus) -> CpuResult<()> {
|
||||||
|
Err(CpuError::IllegalInstruction)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn step(&mut self, sysbus: &mut SysBus) -> CpuResult<()> {
|
||||||
|
match self.state {
|
||||||
|
CpuState::ARM => self.step_arm(sysbus)?,
|
||||||
|
CpuState::THUMB => unimplemented!("thumb not implemented :("),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.advance_pc();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,13 @@
|
||||||
pub mod arm;
|
pub mod arm;
|
||||||
pub use arm::arm_isa::ArmInstruction;
|
pub mod cpu;
|
||||||
|
pub use super::sysbus;
|
||||||
|
|
||||||
pub const REG_PC: usize = 15;
|
pub const REG_PC: usize = 15;
|
||||||
|
|
||||||
pub fn reg_string(reg: usize) -> &'static str {
|
pub fn reg_string(reg: usize) -> &'static str {
|
||||||
let reg_names = &[
|
let reg_names = &[
|
||||||
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "fp", "ip", "sp",
|
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "fp", "ip", "sp", "lr",
|
||||||
"lr", "pc",
|
"pc",
|
||||||
];
|
];
|
||||||
reg_names[reg]
|
reg_names[reg]
|
||||||
}
|
}
|
||||||
|
|
20
src/cli.yml
Normal file
20
src/cli.yml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
name: rustboyadvance
|
||||||
|
author: Michel Heily <michelheily@gmail.com>
|
||||||
|
about: Game boy advance emulator
|
||||||
|
subcommands:
|
||||||
|
- debug:
|
||||||
|
about: debug the bios with the arm core emulation
|
||||||
|
args:
|
||||||
|
- BIOS:
|
||||||
|
help: Sets the bios file to use
|
||||||
|
required: false
|
||||||
|
default_value: gba_bios.bin
|
||||||
|
index: 1
|
||||||
|
- disass:
|
||||||
|
about: disassemble an arm binary file
|
||||||
|
args:
|
||||||
|
- INPUT:
|
||||||
|
help: Sets the input file to use
|
||||||
|
required: true
|
||||||
|
index: 1
|
||||||
|
|
|
@ -1,69 +0,0 @@
|
||||||
use std::env;
|
|
||||||
use std::io;
|
|
||||||
use std::io::Cursor;
|
|
||||||
use std::io::prelude::*;
|
|
||||||
use std::fs::File;
|
|
||||||
use std::convert::TryFrom;
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate enum_primitive_derive;
|
|
||||||
extern crate num_traits;
|
|
||||||
|
|
||||||
extern crate bit;
|
|
||||||
|
|
||||||
extern crate byteorder;
|
|
||||||
use byteorder::{LittleEndian, ReadBytesExt};
|
|
||||||
|
|
||||||
mod arm7tdmi;
|
|
||||||
use arm7tdmi::ArmInstruction;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum DisassemblerError {
|
|
||||||
IO(io::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<io::Error> for DisassemblerError {
|
|
||||||
fn from(err: io::Error) -> DisassemblerError {
|
|
||||||
DisassemblerError::IO(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_file(filename: &str) -> Result<Vec<u8>, DisassemblerError> {
|
|
||||||
let mut buf = Vec::new();
|
|
||||||
let mut file = File::open(filename)?;
|
|
||||||
file.read_to_end(&mut buf)?;
|
|
||||||
Ok(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let filename = match env::args().nth(1) {
|
|
||||||
Some(filename) => filename,
|
|
||||||
None => panic!("usage: {} <file> <n>", env::args().nth(0).unwrap())
|
|
||||||
};
|
|
||||||
|
|
||||||
// let num_instructions = match env::args().nth(2) {
|
|
||||||
// Some(n) => n,
|
|
||||||
// None => panic!("usage: {} <file> <n>", env::args().nth(0).unwrap())
|
|
||||||
// }.parse::<usize>();
|
|
||||||
|
|
||||||
let buf = match read_file(&filename) {
|
|
||||||
Ok(buf) => buf,
|
|
||||||
Err(e) => panic!(e)
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut rdr = Cursor::new(buf);
|
|
||||||
loop {
|
|
||||||
let value: u32 = match rdr.read_u32::<LittleEndian>() {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(err) => {
|
|
||||||
panic!("got an error {:?}", err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let addr = (rdr.position() - 4) as u32;
|
|
||||||
print!("{:8x}:\t{:08x} \t", addr, value);
|
|
||||||
match ArmInstruction::try_from((value, addr)) {
|
|
||||||
Ok(insn) => println!("{}", insn),
|
|
||||||
Err(_) => println!("<UNDEFINED>")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
111
src/main.rs
111
src/main.rs
|
@ -1,3 +1,110 @@
|
||||||
fn main() {
|
use std::convert::TryFrom;
|
||||||
println!("Hello, world!");
|
use std::fs::File;
|
||||||
|
use std::io;
|
||||||
|
use std::io::prelude::*;
|
||||||
|
use std::io::ErrorKind;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate enum_primitive_derive;
|
||||||
|
extern crate num_traits;
|
||||||
|
|
||||||
|
extern crate bit;
|
||||||
|
|
||||||
|
extern crate byteorder;
|
||||||
|
use byteorder::{LittleEndian, ReadBytesExt};
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate clap;
|
||||||
|
use clap::{App, ArgMatches};
|
||||||
|
|
||||||
|
pub mod sysbus;
|
||||||
|
use sysbus::SysBus;
|
||||||
|
mod arm7tdmi;
|
||||||
|
use arm7tdmi::arm;
|
||||||
|
use arm7tdmi::cpu;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum GBAError {
|
||||||
|
IO(io::Error),
|
||||||
|
ArmDecodeError(arm::ArmDecodeError),
|
||||||
|
CpuError(cpu::CpuError),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type GBAResult<T> = Result<T, GBAError>;
|
||||||
|
|
||||||
|
impl From<io::Error> for GBAError {
|
||||||
|
fn from(err: io::Error) -> GBAError {
|
||||||
|
GBAError::IO(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<arm::ArmDecodeError> for GBAError {
|
||||||
|
fn from(err: arm::ArmDecodeError) -> GBAError {
|
||||||
|
GBAError::ArmDecodeError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<cpu::CpuError> for GBAError {
|
||||||
|
fn from(err: cpu::CpuError) -> GBAError {
|
||||||
|
GBAError::CpuError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_bin_file(filename: &str) -> GBAResult<Vec<u8>> {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
let mut file = File::open(filename)?;
|
||||||
|
file.read_to_end(&mut buf)?;
|
||||||
|
Ok(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_disass(matches: &ArgMatches) -> GBAResult<()> {
|
||||||
|
let input = matches.value_of("INPUT").unwrap();
|
||||||
|
let bin = read_bin_file(&input)?;
|
||||||
|
|
||||||
|
let mut rdr = io::Cursor::new(bin);
|
||||||
|
loop {
|
||||||
|
let value: u32 = match rdr.read_u32::<LittleEndian>() {
|
||||||
|
Ok(value) => Ok(value),
|
||||||
|
Err(e) => match e.kind() {
|
||||||
|
ErrorKind::UnexpectedEof => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_ => Err(e),
|
||||||
|
},
|
||||||
|
}?;
|
||||||
|
let addr = (rdr.position() - 4) as u32;
|
||||||
|
print!("{:8x}:\t{:08x} \t", addr, value);
|
||||||
|
match arm::ArmInstruction::try_from((value, addr)) {
|
||||||
|
Ok(insn) => println!("{}", insn),
|
||||||
|
Err(_) => println!("<UNDEFINED>"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_debug(matches: &ArgMatches) -> GBAResult<()> {
|
||||||
|
let gba_bios_path = matches.value_of("BIOS").unwrap_or_default();
|
||||||
|
println!("Loading BIOS: {}", gba_bios_path);
|
||||||
|
let bios_bin = read_bin_file(gba_bios_path)?;
|
||||||
|
|
||||||
|
let sysbus = SysBus::new(bios_bin);
|
||||||
|
let core = cpu::Core::new();
|
||||||
|
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let yaml = load_yaml!("cli.yml");
|
||||||
|
let matches = App::from_yaml(yaml).get_matches();
|
||||||
|
|
||||||
|
let result = match matches.subcommand() {
|
||||||
|
("debug", Some(m)) => run_debug(m),
|
||||||
|
("disass", Some(m)) => run_disass(m),
|
||||||
|
_ => Ok(()),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(err) = result {
|
||||||
|
println!("Got an error: {:?}", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
34
src/sysbus.rs
Normal file
34
src/sysbus.rs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
|
||||||
|
const VIDEO_RAM_SIZE: usize = 128 * 1024;
|
||||||
|
const WORK_RAM_SIZE: usize = 256 * 1024;
|
||||||
|
const INTERNAL_RAM: usize = 32 * 1024;
|
||||||
|
const PALETTE_AM_SIZE: usize = 1 * 1024;
|
||||||
|
const OAM_SIZE: usize = 1 * 1024;
|
||||||
|
const BIOS_SIZE: usize = 16 * 1024;
|
||||||
|
const GAMEPAK_ROM_SIZE: usize = 32 * 1024 * 1024;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SysBus {
|
||||||
|
bios_rom: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SysBus {
|
||||||
|
pub fn new(bios_rom: Vec<u8>) -> SysBus {
|
||||||
|
SysBus { bios_rom: bios_rom }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_32(&self, addr: u32) -> u32 {
|
||||||
|
let addr = addr as usize;
|
||||||
|
(&self.bios_rom[addr..addr + 4])
|
||||||
|
.read_u32::<LittleEndian>()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_16(&self, addr: u32) -> u16 {
|
||||||
|
let addr = addr as usize;
|
||||||
|
(&self.bios_rom[addr..addr + 4])
|
||||||
|
.read_u16::<LittleEndian>()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
Reference in a new issue