From 9330c53957edc8c2eb261811f1ea89cea0068b8c Mon Sep 17 00:00:00 2001 From: Michel Heily Date: Tue, 25 Jun 2019 02:10:09 +0300 Subject: [PATCH] Start modeling CPU Wrote a shallow skeleton of a CPU Core. Finally made the disassembler a clap subcommand. --- Cargo.lock | 142 ++++++++++++- Cargo.toml | 6 +- src/arm7tdmi/arm/arm_isa.rs | 373 --------------------------------- src/arm7tdmi/arm/display.rs | 52 +++-- src/arm7tdmi/arm/mod.rs | 403 +++++++++++++++++++++++++++++++++++- src/arm7tdmi/cpu.rs | 108 ++++++++++ src/arm7tdmi/mod.rs | 7 +- src/cli.yml | 20 ++ src/disass.rs | 69 ------ src/main.rs | 111 +++++++++- src/sysbus.rs | 34 +++ 11 files changed, 851 insertions(+), 474 deletions(-) create mode 100644 src/arm7tdmi/cpu.rs create mode 100644 src/cli.yml delete mode 100644 src/disass.rs create mode 100644 src/sysbus.rs diff --git a/Cargo.lock b/Cargo.lock index 5a83e04..0a4cc77 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,12 +1,21 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] -name = "arm7tdmi" -version = "0.1.0" +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bit 0.1.1 (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)", + "winapi 0.3.7 (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]] @@ -19,11 +28,31 @@ name = "bit" version = "0.1.1" 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]] name = "byteorder" version = "1.3.2" 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]] name = "enum-primitive-derive" version = "0.1.2" @@ -34,6 +63,11 @@ dependencies = [ "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]] name = "num-traits" version = "0.1.43" @@ -50,19 +84,45 @@ dependencies = [ "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]] name = "quote" version = "0.3.15" 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]] name = "rustboyadvance" version = "0.1.0" 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)", + "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]] name = "syn" version = "0.11.11" @@ -81,19 +141,89 @@ dependencies = [ "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]] name = "unicode-xid" version = "0.0.4" 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] +"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 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 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 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.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 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 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 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" diff --git a/Cargo.toml b/Cargo.toml index 89e0b33..379de1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,8 +9,4 @@ byteorder = "*" enum-primitive-derive = "^0.1" num-traits = "^0.1" bit = "^0.1" -clap = {version = "2.33", features = ["color", "yaml"]} - -[[bin]] -name = "disass" -path = "src/disass.rs" \ No newline at end of file +clap = {version = "2.33", features = ["color", "yaml"]} \ No newline at end of file diff --git a/src/arm7tdmi/arm/arm_isa.rs b/src/arm7tdmi/arm/arm_isa.rs index 057bb18..e69de29 100644 --- a/src/arm7tdmi/arm/arm_isa.rs +++ b/src/arm7tdmi/arm/arm_isa.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 { - 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 for ArmShift { - type Error = ArmError; - - fn try_from(v: u32) -> Result { - 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, - }, -} - -impl ArmShiftedValue { - /// Decode operand2 as an immediate value - pub fn decode_rotated_immediate(&self) -> Option { - 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::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 { - 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 { - 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 { - 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 - } -} diff --git a/src/arm7tdmi/arm/display.rs b/src/arm7tdmi/arm/display.rs index 92d532d..23dcd76 100644 --- a/src/arm7tdmi/arm/display.rs +++ b/src/arm7tdmi/arm/display.rs @@ -1,7 +1,7 @@ use super::super::{reg_string, REG_PC}; -use super::arm_isa::{ - ArmCond, ArmInstruction, ArmInstructionFormat, ArmShiftedValue, ArmOpCode, ArmShift, - ArmShiftType, ArmHalfwordTransferType +use super::{ + ArmCond, ArmHalfwordTransferType, ArmInstruction, ArmInstructionFormat, ArmOpCode, ArmShift, + ArmShiftType, ArmShiftedValue, }; use std::fmt; @@ -108,9 +108,13 @@ impl ArmInstruction { ofs = self.pc.wrapping_add(self.branch_offset() as u32) as u32 ) } - + 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 { @@ -145,21 +149,27 @@ impl ArmInstruction { ), }?; - let operand2 = self.operand2(); + let operand2 = self.operand2().unwrap(); match operand2 { ArmShiftedValue::RotatedImmediate(_, _) => { let value = operand2.decode_rotated_immediate().unwrap(); write!(f, "#{}\t; {:#x}", value, value) } - ArmShiftedValue::ShiftedRegister{reg, shift, added: _} => { - write!(f, "{}", self.make_shifted_reg_string(reg, shift)) - } + ArmShiftedValue::ShiftedRegister { + reg, + shift, + added: _, + } => write!(f, "{}", self.make_shifted_reg_string(reg, shift)), _ => write!(f, "RegisterNotImpl"), } } 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 { @@ -176,8 +186,16 @@ impl ArmInstruction { Some(format!("\t; {:#x}", value_for_commnet)), ) } - ArmShiftedValue::ShiftedRegister{reg, shift, added: Some(added)} => ( - format!("{}{}", if added { "" } else { "-" }, self.make_shifted_reg_string(reg, shift)), + ArmShiftedValue::ShiftedRegister { + reg, + shift, + added: Some(added), + } => ( + format!( + "{}{}", + if added { "" } else { "-" }, + self.make_shifted_reg_string(reg, shift) + ), None, ), _ => panic!("bad barrel shifter"), @@ -211,7 +229,7 @@ impl ArmInstruction { 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 { @@ -284,7 +302,11 @@ impl ArmInstruction { } 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 { @@ -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() { write!( f, diff --git a/src/arm7tdmi/arm/mod.rs b/src/arm7tdmi/arm/mod.rs index de577ea..4669f2c 100644 --- a/src/arm7tdmi/arm/mod.rs +++ b/src/arm7tdmi/arm/mod.rs @@ -1,2 +1,403 @@ -pub mod arm_isa; 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 { + 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 for ArmShift { + type Error = ArmDecodeErrorKind; + + fn try_from(v: u32) -> Result { + 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, + }, +} + +impl ArmShiftedValue { + /// Decode operand2 as an immediate value + pub fn decode_rotated_immediate(&self) -> Option { + 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::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 { + 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 { + 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 { + 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 { + 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 { + 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 + } +} diff --git a/src/arm7tdmi/cpu.rs b/src/arm7tdmi/cpu.rs new file mode 100644 index 0000000..06b455a --- /dev/null +++ b/src/arm7tdmi/cpu.rs @@ -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 = Result; + +#[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(()) + } +} diff --git a/src/arm7tdmi/mod.rs b/src/arm7tdmi/mod.rs index 4a9cfca..1fd85e3 100644 --- a/src/arm7tdmi/mod.rs +++ b/src/arm7tdmi/mod.rs @@ -1,12 +1,13 @@ pub mod arm; -pub use arm::arm_isa::ArmInstruction; +pub mod cpu; +pub use super::sysbus; pub const REG_PC: usize = 15; pub fn reg_string(reg: usize) -> &'static str { let reg_names = &[ - "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "fp", "ip", "sp", - "lr", "pc", + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "fp", "ip", "sp", "lr", + "pc", ]; reg_names[reg] } diff --git a/src/cli.yml b/src/cli.yml new file mode 100644 index 0000000..e978146 --- /dev/null +++ b/src/cli.yml @@ -0,0 +1,20 @@ +name: rustboyadvance +author: Michel Heily +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 + diff --git a/src/disass.rs b/src/disass.rs deleted file mode 100644 index d2db387..0000000 --- a/src/disass.rs +++ /dev/null @@ -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 for DisassemblerError { - fn from(err: io::Error) -> DisassemblerError { - DisassemblerError::IO(err) - } -} - -fn read_file(filename: &str) -> Result, 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: {} ", env::args().nth(0).unwrap()) - }; - - // let num_instructions = match env::args().nth(2) { - // Some(n) => n, - // None => panic!("usage: {} ", env::args().nth(0).unwrap()) - // }.parse::(); - - 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::() { - 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!("") - } - } -} diff --git a/src/main.rs b/src/main.rs index e7a11a9..38e3ed4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,110 @@ -fn main() { - println!("Hello, world!"); +use std::convert::TryFrom; +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 = Result; + +impl From for GBAError { + fn from(err: io::Error) -> GBAError { + GBAError::IO(err) + } +} + +impl From for GBAError { + fn from(err: arm::ArmDecodeError) -> GBAError { + GBAError::ArmDecodeError(err) + } +} + +impl From for GBAError { + fn from(err: cpu::CpuError) -> GBAError { + GBAError::CpuError(err) + } +} + +fn read_bin_file(filename: &str) -> GBAResult> { + 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::() { + 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!(""), + }; + } + + 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); + } } diff --git a/src/sysbus.rs b/src/sysbus.rs new file mode 100644 index 0000000..82ab4ef --- /dev/null +++ b/src/sysbus.rs @@ -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, +} + +impl SysBus { + pub fn new(bios_rom: Vec) -> 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::() + .unwrap() + } + + pub fn read_16(&self, addr: u32) -> u16 { + let addr = addr as usize; + (&self.bios_rom[addr..addr + 4]) + .read_u16::() + .unwrap() + } +}