Implement new gdbstub target for arm7tdmi, add a small example & test as well
Former-commit-id: 16ba6dada28f8beb5971413acbbe153a26aa9ec3 Former-commit-id: f0661ad20245110797fd99fe16d3fb11382bb78a
This commit is contained in:
parent
4c5d35c5d1
commit
f3fac5e3b8
130
Cargo.lock
generated
130
Cargo.lock
generated
|
@ -77,12 +77,16 @@ dependencies = [
|
||||||
"bit",
|
"bit",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"colored",
|
"colored 1.9.3",
|
||||||
"enum-primitive-derive",
|
"enum-primitive-derive",
|
||||||
|
"gdbstub 0.6.3",
|
||||||
|
"gdbstub_arch",
|
||||||
|
"log 0.4.11",
|
||||||
"num 0.2.1",
|
"num 0.2.1",
|
||||||
"num-traits 0.2.12",
|
"num-traits 0.2.12",
|
||||||
"rustboyadvance-utils",
|
"rustboyadvance-utils",
|
||||||
"serde",
|
"serde",
|
||||||
|
"simple_logger",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -201,9 +205,9 @@ checksum = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.2.1"
|
version = "1.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "blake2b_simd"
|
name = "blake2b_simd"
|
||||||
|
@ -333,7 +337,7 @@ checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi_term 0.11.0",
|
"ansi_term 0.11.0",
|
||||||
"atty",
|
"atty",
|
||||||
"bitflags 1.2.1",
|
"bitflags 1.3.2",
|
||||||
"strsim",
|
"strsim",
|
||||||
"textwrap",
|
"textwrap",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
|
@ -352,6 +356,17 @@ dependencies = [
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colored"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd"
|
||||||
|
dependencies = [
|
||||||
|
"atty",
|
||||||
|
"lazy_static 1.4.0",
|
||||||
|
"winapi 0.3.9",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "combine"
|
name = "combine"
|
||||||
version = "4.3.2"
|
version = "4.3.2"
|
||||||
|
@ -485,7 +500,7 @@ checksum = "00affe7f6ab566df61b4be3ce8cf16bc2576bca0963ceb0955e45d514bf9a279"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bstr",
|
"bstr",
|
||||||
"csv-core",
|
"csv-core",
|
||||||
"itoa",
|
"itoa 0.4.6",
|
||||||
"ryu",
|
"ryu",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
@ -654,6 +669,30 @@ dependencies = [
|
||||||
"num-traits 0.2.12",
|
"num-traits 0.2.12",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gdbstub"
|
||||||
|
version = "0.6.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32c95766e0414f8bfc1d07055574c621b67739466d6ba516c4fef8e99d30d2e6"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"log 0.4.11",
|
||||||
|
"managed",
|
||||||
|
"num-traits 0.2.12",
|
||||||
|
"paste",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gdbstub_arch"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eecb536c55c43593a00dde9074dbbdb0e81ce5f20dbca921400f8779c21dea9c"
|
||||||
|
dependencies = [
|
||||||
|
"gdbstub 0.6.3",
|
||||||
|
"num-traits 0.2.12",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gdi32-sys"
|
name = "gdi32-sys"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
|
@ -793,6 +832,12 @@ version = "0.4.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
|
checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jni"
|
name = "jni"
|
||||||
version = "0.17.0"
|
version = "0.17.0"
|
||||||
|
@ -851,7 +896,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "db65c6da02e61f55dae90a0ae427b2a5f6b3e8db09f58d10efab23af92592616"
|
checksum = "db65c6da02e61f55dae90a0ae427b2a5f6b3e8db09f58d10efab23af92592616"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec 0.5.2",
|
"arrayvec 0.5.2",
|
||||||
"bitflags 1.2.1",
|
"bitflags 1.3.2",
|
||||||
"cfg-if 0.1.10",
|
"cfg-if 0.1.10",
|
||||||
"ryu",
|
"ryu",
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
|
@ -859,9 +904,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.77"
|
version = "0.2.132"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235"
|
checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libretro-backend"
|
name = "libretro-backend"
|
||||||
|
@ -906,6 +951,12 @@ dependencies = [
|
||||||
"cfg-if 0.1.10",
|
"cfg-if 0.1.10",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "managed"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "maybe-uninit"
|
name = "maybe-uninit"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
|
@ -980,7 +1031,7 @@ version = "0.18.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "83450fe6a6142ddd95fb064b746083fc4ef1705fe81f64a64e1d4b39f54a1055"
|
checksum = "83450fe6a6142ddd95fb064b746083fc4ef1705fe81f64a64e1d4b39f54a1055"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.2.1",
|
"bitflags 1.3.2",
|
||||||
"cc",
|
"cc",
|
||||||
"cfg-if 0.1.10",
|
"cfg-if 0.1.10",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -1110,6 +1161,15 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_threads"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.20.0"
|
version = "0.20.0"
|
||||||
|
@ -1144,6 +1204,12 @@ dependencies = [
|
||||||
"sdl2 0.31.0",
|
"sdl2 0.31.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "paste"
|
||||||
|
version = "1.0.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.1.9"
|
version = "0.1.9"
|
||||||
|
@ -1374,18 +1440,17 @@ dependencies = [
|
||||||
"bit-set",
|
"bit-set",
|
||||||
"bit_reverse",
|
"bit_reverse",
|
||||||
"bitfield",
|
"bitfield",
|
||||||
"bitflags 1.2.1",
|
"bitflags 1.3.2",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"bytesize",
|
"bytesize",
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"chrono",
|
"chrono",
|
||||||
"colored",
|
"colored 1.9.3",
|
||||||
"criterion",
|
"criterion",
|
||||||
"debug_stub_derive",
|
"debug_stub_derive",
|
||||||
"enum-primitive-derive",
|
"enum-primitive-derive",
|
||||||
"fuzzy-matcher",
|
"fuzzy-matcher",
|
||||||
"gdbstub",
|
"gdbstub 0.1.2",
|
||||||
"goblin",
|
|
||||||
"hex-literal",
|
"hex-literal",
|
||||||
"hexdump",
|
"hexdump",
|
||||||
"lazy_static 1.4.0",
|
"lazy_static 1.4.0",
|
||||||
|
@ -1461,7 +1526,9 @@ dependencies = [
|
||||||
name = "rustboyadvance-utils"
|
name = "rustboyadvance-utils"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"goblin",
|
||||||
"instant",
|
"instant",
|
||||||
|
"log 0.4.11",
|
||||||
"ringbuf",
|
"ringbuf",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1583,7 +1650,7 @@ version = "0.33.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1f74124048ea86b5cd50236b2443f6f57cf4625a8e8818009b4e50dbb8729a43"
|
checksum = "1f74124048ea86b5cd50236b2443f6f57cf4625a8e8818009b4e50dbb8729a43"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.2.1",
|
"bitflags 1.3.2",
|
||||||
"lazy_static 1.4.0",
|
"lazy_static 1.4.0",
|
||||||
"libc",
|
"libc",
|
||||||
"sdl2-sys 0.33.0",
|
"sdl2-sys 0.33.0",
|
||||||
|
@ -1659,7 +1726,7 @@ version = "1.0.57"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c"
|
checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa 0.4.6",
|
||||||
"ryu",
|
"ryu",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
@ -1682,6 +1749,19 @@ dependencies = [
|
||||||
"opaque-debug",
|
"opaque-debug",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simple_logger"
|
||||||
|
version = "2.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "48047e77b528151aaf841a10a9025f9459da80ba820e425ff7eb005708a76dc7"
|
||||||
|
dependencies = [
|
||||||
|
"atty",
|
||||||
|
"colored 2.0.0",
|
||||||
|
"log 0.4.11",
|
||||||
|
"time 0.3.14",
|
||||||
|
"winapi 0.3.9",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smart-default"
|
name = "smart-default"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
|
@ -1872,11 +1952,23 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"standback",
|
"standback",
|
||||||
"stdweb",
|
"stdweb",
|
||||||
"time-macros",
|
"time-macros 0.1.1",
|
||||||
"version_check",
|
"version_check",
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time"
|
||||||
|
version = "0.3.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c3f9a28b618c3a6b9251b6908e9c99e04b9e5c02e6581ccbb67d59c34ef7f9b"
|
||||||
|
dependencies = [
|
||||||
|
"itoa 1.0.3",
|
||||||
|
"libc",
|
||||||
|
"num_threads",
|
||||||
|
"time-macros 0.2.4",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time-macros"
|
name = "time-macros"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
|
@ -1887,6 +1979,12 @@ dependencies = [
|
||||||
"time-macros-impl",
|
"time-macros-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time-macros"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time-macros-impl"
|
name = "time-macros-impl"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
|
|
|
@ -7,6 +7,7 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rustboyadvance-utils = {"path" = "../utils" }
|
rustboyadvance-utils = {"path" = "../utils" }
|
||||||
|
log = "0.4.8"
|
||||||
bit = "^0.1"
|
bit = "^0.1"
|
||||||
cfg-if = "1.0.0"
|
cfg-if = "1.0.0"
|
||||||
serde = { version = "1.0.104", features = ["derive", "rc"] }
|
serde = { version = "1.0.104", features = ["derive", "rc"] }
|
||||||
|
@ -16,6 +17,11 @@ byteorder = "1"
|
||||||
num = "0.2.1"
|
num = "0.2.1"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
enum-primitive-derive = "^0.1"
|
enum-primitive-derive = "^0.1"
|
||||||
|
gdbstub = "0.6.3"
|
||||||
|
gdbstub_arch = "0.2.4"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
simple_logger = "2.3.0" # For the examples
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
bit = "^0.1"
|
bit = "^0.1"
|
129
arm7tdmi/examples/simple_emulator.rs
Normal file
129
arm7tdmi/examples/simple_emulator.rs
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
use log::info;
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
|
||||||
|
use gdbstub::common::Signal;
|
||||||
|
use gdbstub::conn::{Connection, ConnectionExt};
|
||||||
|
use gdbstub::stub::{run_blocking, GdbStub, SingleThreadStopReason};
|
||||||
|
use gdbstub::target::Target;
|
||||||
|
|
||||||
|
use arm7tdmi::gdb::wait_for_connection;
|
||||||
|
use arm7tdmi::{Arm7tdmiCore, SimpleMemory};
|
||||||
|
|
||||||
|
use rustboyadvance_utils::Shared;
|
||||||
|
|
||||||
|
struct SimpleEmulator {
|
||||||
|
cpu: Arm7tdmiCore<SimpleMemory>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SimpleEmulator {
|
||||||
|
fn new(program: &[u8]) -> SimpleEmulator {
|
||||||
|
let mut memory = SimpleMemory::new(0x4000);
|
||||||
|
memory.load_program(program);
|
||||||
|
|
||||||
|
let bus = Shared::new(memory);
|
||||||
|
let mut cpu = Arm7tdmiCore::new(bus);
|
||||||
|
cpu.reset();
|
||||||
|
|
||||||
|
SimpleEmulator { cpu }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl run_blocking::BlockingEventLoop for SimpleEmulator {
|
||||||
|
type Target = Arm7tdmiCore<SimpleMemory>;
|
||||||
|
type Connection = Box<dyn ConnectionExt<Error = std::io::Error>>;
|
||||||
|
type StopReason = SingleThreadStopReason<u32>;
|
||||||
|
|
||||||
|
fn wait_for_stop_reason(
|
||||||
|
target: &mut Self::Target,
|
||||||
|
conn: &mut Self::Connection,
|
||||||
|
) -> Result<
|
||||||
|
run_blocking::Event<SingleThreadStopReason<u32>>,
|
||||||
|
run_blocking::WaitForStopReasonError<
|
||||||
|
<Self::Target as Target>::Error,
|
||||||
|
<Self::Connection as Connection>::Error,
|
||||||
|
>,
|
||||||
|
> {
|
||||||
|
let mut steps = 0;
|
||||||
|
loop {
|
||||||
|
if conn.peek().map(|b| b.is_some()).unwrap_or(true) {
|
||||||
|
let byte = conn
|
||||||
|
.read()
|
||||||
|
.map_err(run_blocking::WaitForStopReasonError::Connection)?;
|
||||||
|
return Ok(run_blocking::Event::IncomingData(byte));
|
||||||
|
} else {
|
||||||
|
target.step();
|
||||||
|
if target.check_breakpoint().is_some() {
|
||||||
|
return Ok(run_blocking::Event::TargetStopped(
|
||||||
|
SingleThreadStopReason::SwBreak(()),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
steps += 1;
|
||||||
|
if steps % 1024 == 0 {
|
||||||
|
return Ok(run_blocking::Event::TargetStopped(
|
||||||
|
SingleThreadStopReason::SwBreak(()),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_interrupt(
|
||||||
|
_target: &mut Arm7tdmiCore<SimpleMemory>,
|
||||||
|
) -> Result<Option<SingleThreadStopReason<u32>>, <Arm7tdmiCore<SimpleMemory> as Target>::Error>
|
||||||
|
{
|
||||||
|
// Because this emulator runs as part of the GDB stub loop, there isn't any
|
||||||
|
// special action that needs to be taken to interrupt the underlying target. It
|
||||||
|
// is implicitly paused whenever the stub isn't within the
|
||||||
|
// `wait_for_stop_reason` callback.
|
||||||
|
Ok(Some(SingleThreadStopReason::Signal(Signal::SIGINT)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
SimpleLogger::new().env().init().unwrap();
|
||||||
|
|
||||||
|
let mut emulator = SimpleEmulator::new(include_bytes!("test_program/example.bin"));
|
||||||
|
|
||||||
|
let conn: Box<dyn ConnectionExt<Error = std::io::Error>> = Box::new(wait_for_connection(1337)?);
|
||||||
|
let gdb = GdbStub::new(conn);
|
||||||
|
let result = gdb.run_blocking::<SimpleEmulator>(&mut emulator.cpu);
|
||||||
|
|
||||||
|
info!("emulator stopped, gdb result {:?}", result);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use arm7tdmi::memory::DebugRead;
|
||||||
|
use rustboyadvance_utils::elf::read_symbols;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_breakpoint() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let mut emulator = SimpleEmulator::new(include_bytes!("test_program/example.bin"));
|
||||||
|
let symbol_map = read_symbols(include_bytes!("test_program/example.elf"))?;
|
||||||
|
let breakpoint_addr = *symbol_map.get("breakpoint_on_me").unwrap();
|
||||||
|
println!("breakpoint_addr = {:08x}", breakpoint_addr);
|
||||||
|
let breakpoint_counter_addr = *symbol_map.get("breakpoint_count").unwrap();
|
||||||
|
emulator.cpu.breakpoints.push(breakpoint_addr);
|
||||||
|
|
||||||
|
for x in 0..10 {
|
||||||
|
println!("{}", x);
|
||||||
|
let timeout = std::time::Instant::now() + std::time::Duration::from_secs(1);
|
||||||
|
loop {
|
||||||
|
emulator.cpu.step();
|
||||||
|
if let Some(addr) = emulator.cpu.check_breakpoint() {
|
||||||
|
emulator.cpu.step();
|
||||||
|
assert_eq!(addr, breakpoint_addr);
|
||||||
|
assert_eq!(emulator.cpu.bus.debug_read_32(breakpoint_counter_addr), x);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
assert!(std::time::Instant::now() < timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
2
arm7tdmi/examples/test_program/.gitignore
vendored
Normal file
2
arm7tdmi/examples/test_program/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/*.o
|
||||||
|
/*.gdb_history
|
37
arm7tdmi/examples/test_program/Makefile
Normal file
37
arm7tdmi/examples/test_program/Makefile
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
# Easiest to build this using `docker run -it --rm -v $(pwd):/code -w /code devkitpro/devkitarm:latest make
|
||||||
|
TOOLCHAIN := ${DEVKITARM}/bin/arm-none-eabi
|
||||||
|
CC := ${TOOLCHAIN}-gcc
|
||||||
|
OBJCOPY := ${TOOLCHAIN}-objcopy
|
||||||
|
OBJDUMP := ${TOOLCHAIN}-objdump
|
||||||
|
|
||||||
|
TARGET := test
|
||||||
|
|
||||||
|
CFLAGS := -O0 -g -std=c11 -march=armv4t -mthumb
|
||||||
|
LDFLAGS := -Wl,-static
|
||||||
|
|
||||||
|
|
||||||
|
SOURCES := $(wildcard *.[cs])
|
||||||
|
OBJECTS := $(filter %.o,$(SOURCES:%.c=%.o))
|
||||||
|
OBJECTS += $(filter %.o,$(SOURCES:%.s=%.o))
|
||||||
|
|
||||||
|
all: ${TARGET}.bin objdump
|
||||||
|
|
||||||
|
${OBJECTS}: ${SOURCES}
|
||||||
|
${CC} -c ${CFLAGS} $^
|
||||||
|
|
||||||
|
${TARGET}.elf ${TARGET}.map: ${OBJECTS} ${TARGET}.ld
|
||||||
|
${CC} ${LDFLAGS} -Wl,-Map=${TARGET}.map -Wl,-T${TARGET}.ld ${OBJECTS} -o $@
|
||||||
|
|
||||||
|
${TARGET}.bin: ${TARGET}.elf
|
||||||
|
$(OBJCOPY) -v -O binary $^ $@
|
||||||
|
|
||||||
|
objdump: ${TARGET}.elf
|
||||||
|
${OBJDUMP} -d $^
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f ${TARGET}.elf
|
||||||
|
rm -f ${TARGET}.bin
|
||||||
|
rm -f ${TARGET}.map
|
||||||
|
rm -f *.o
|
||||||
|
|
||||||
|
.PHONY: all clean
|
40
arm7tdmi/examples/test_program/reset.s
Normal file
40
arm7tdmi/examples/test_program/reset.s
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
.section ".init"
|
||||||
|
|
||||||
|
.global _interrupt_vector
|
||||||
|
.align 4
|
||||||
|
.arm
|
||||||
|
|
||||||
|
.section ".init.vector"
|
||||||
|
_interrupt_vector:
|
||||||
|
b _reset
|
||||||
|
b _not_implemented
|
||||||
|
b _not_implemented
|
||||||
|
b _not_implemented
|
||||||
|
b _not_implemented
|
||||||
|
b _not_implemented
|
||||||
|
b _not_implemented
|
||||||
|
b _not_implemented
|
||||||
|
|
||||||
|
.section ".init.text"
|
||||||
|
.global _reset
|
||||||
|
_reset:
|
||||||
|
mrs r1, cpsr @ save the mode bits from CPSR
|
||||||
|
bic r0, r1, #0x1F
|
||||||
|
orr r0, r0, #0x13 @ supervisor
|
||||||
|
ldr sp, =_stack_top
|
||||||
|
mov r0, #0
|
||||||
|
mov r1, #0
|
||||||
|
mov r2, #0
|
||||||
|
mov r3, #0
|
||||||
|
mov r4, #0
|
||||||
|
mov r5, #0
|
||||||
|
mov r6, #0
|
||||||
|
mov r7, #0
|
||||||
|
mov r9, #0
|
||||||
|
mov r10, #0
|
||||||
|
mov r11, #0
|
||||||
|
mov r12, #0
|
||||||
|
b main
|
||||||
|
|
||||||
|
_not_implemented:
|
||||||
|
b _not_implemented
|
BIN
arm7tdmi/examples/test_program/test.bin
Normal file
BIN
arm7tdmi/examples/test_program/test.bin
Normal file
Binary file not shown.
17
arm7tdmi/examples/test_program/test.c
Normal file
17
arm7tdmi/examples/test_program/test.c
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
int breakpoint_count = 0;
|
||||||
|
volatile int breakpoint_on_me() {
|
||||||
|
breakpoint_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int x = 1337;
|
||||||
|
for (;;) {
|
||||||
|
x += 1;
|
||||||
|
if (x == 7331) {
|
||||||
|
breakpoint_on_me();
|
||||||
|
x = 1337;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
BIN
arm7tdmi/examples/test_program/test.elf
Normal file
BIN
arm7tdmi/examples/test_program/test.elf
Normal file
Binary file not shown.
32
arm7tdmi/examples/test_program/test.ld
Normal file
32
arm7tdmi/examples/test_program/test.ld
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
ENTRY(_reset)
|
||||||
|
|
||||||
|
MEMORY {
|
||||||
|
RAM : ORIGIN = 0x00000000, LENGTH = 16K
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTIONS {
|
||||||
|
. = ORIGIN(RAM);
|
||||||
|
|
||||||
|
.init : {
|
||||||
|
*(.init.vector);
|
||||||
|
*(.init*);
|
||||||
|
} > RAM
|
||||||
|
|
||||||
|
.text : ALIGN(4) {
|
||||||
|
*(.text)
|
||||||
|
*(.text.*)
|
||||||
|
} > RAM
|
||||||
|
.data : ALIGN(4) {
|
||||||
|
*(.rodata*);
|
||||||
|
*(.data*);
|
||||||
|
} > RAM
|
||||||
|
.bss : ALIGN(4) {
|
||||||
|
*(.bss*)
|
||||||
|
} > RAM
|
||||||
|
|
||||||
|
.stack : AT (0x00003000) {
|
||||||
|
_stack_bottom = .;
|
||||||
|
. += 0xfe0;
|
||||||
|
_stack_top = .;
|
||||||
|
} > RAM
|
||||||
|
}
|
162
arm7tdmi/examples/test_program/test.map
Normal file
162
arm7tdmi/examples/test_program/test.map
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
|
||||||
|
Memory Configuration
|
||||||
|
|
||||||
|
Name Origin Length Attributes
|
||||||
|
RAM 0x0000000000000000 0x0000000000004000
|
||||||
|
*default* 0x0000000000000000 0xffffffffffffffff
|
||||||
|
|
||||||
|
Linker script and memory map
|
||||||
|
|
||||||
|
LOAD /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crti.o
|
||||||
|
LOAD /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
|
||||||
|
0x0000000000000000 . = ORIGIN (RAM)
|
||||||
|
|
||||||
|
.init 0x0000000000000000 0x88
|
||||||
|
*(.init.vector)
|
||||||
|
.init.vector 0x0000000000000000 0x20 reset.o
|
||||||
|
0x0000000000000000 _interrupt_vector
|
||||||
|
*(.init*)
|
||||||
|
.init 0x0000000000000020 0xc /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crti.o
|
||||||
|
0x0000000000000020 _init
|
||||||
|
.init_array 0x000000000000002c 0x4 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
|
||||||
|
.init 0x0000000000000030 0x0 reset.o
|
||||||
|
.init.text 0x0000000000000030 0x4c reset.o
|
||||||
|
0x0000000000000030 _reset
|
||||||
|
.init 0x000000000000007c 0xc /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtn.o
|
||||||
|
|
||||||
|
.text 0x0000000000000088 0xbc
|
||||||
|
*(.text)
|
||||||
|
.text 0x0000000000000088 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crti.o
|
||||||
|
.text 0x0000000000000088 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
|
||||||
|
.text 0x0000000000000088 0x4c test.o
|
||||||
|
0x0000000000000088 breakpoint_on_me
|
||||||
|
0x00000000000000a8 main
|
||||||
|
.text 0x00000000000000d4 0x0 reset.o
|
||||||
|
.text 0x00000000000000d4 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtend.o
|
||||||
|
.text 0x00000000000000d4 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtn.o
|
||||||
|
*(.text.*)
|
||||||
|
.text.__do_global_dtors_aux
|
||||||
|
0x00000000000000d4 0x40 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
|
||||||
|
.text.frame_dummy
|
||||||
|
0x0000000000000114 0x30 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
|
||||||
|
|
||||||
|
.glue_7 0x0000000000000144 0x0
|
||||||
|
.glue_7 0x0000000000000144 0x0 linker stubs
|
||||||
|
|
||||||
|
.glue_7t 0x0000000000000144 0x0
|
||||||
|
.glue_7t 0x0000000000000144 0x0 linker stubs
|
||||||
|
|
||||||
|
.vfp11_veneer 0x0000000000000144 0x0
|
||||||
|
.vfp11_veneer 0x0000000000000144 0x0 linker stubs
|
||||||
|
|
||||||
|
.v4_bx 0x0000000000000144 0x0
|
||||||
|
.v4_bx 0x0000000000000144 0x0 linker stubs
|
||||||
|
|
||||||
|
.fini 0x0000000000000144 0x18
|
||||||
|
.fini 0x0000000000000144 0xc /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crti.o
|
||||||
|
0x0000000000000144 _fini
|
||||||
|
.fini 0x0000000000000150 0xc /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtn.o
|
||||||
|
|
||||||
|
.iplt 0x000000000000015c 0x0
|
||||||
|
.iplt 0x000000000000015c 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
|
||||||
|
|
||||||
|
.eh_frame 0x000000000000015c 0x4
|
||||||
|
.eh_frame 0x000000000000015c 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
|
||||||
|
.eh_frame 0x000000000000015c 0x4 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtend.o
|
||||||
|
|
||||||
|
.rel.dyn 0x0000000000000160 0x0
|
||||||
|
.rel.iplt 0x0000000000000160 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
|
||||||
|
|
||||||
|
.data 0x0000000000000160 0x4c
|
||||||
|
*(.rodata*)
|
||||||
|
.rodata.all_implied_fbits
|
||||||
|
0x0000000000000160 0x24 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
|
||||||
|
.rodata.all_implied_fbits
|
||||||
|
0x0000000000000184 0x24 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtend.o
|
||||||
|
*(.data*)
|
||||||
|
.data 0x00000000000001a8 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crti.o
|
||||||
|
.data 0x00000000000001a8 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
|
||||||
|
.data.__dso_handle
|
||||||
|
0x00000000000001a8 0x4 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
|
||||||
|
0x00000000000001a8 __dso_handle
|
||||||
|
.data 0x00000000000001ac 0x0 test.o
|
||||||
|
.data 0x00000000000001ac 0x0 reset.o
|
||||||
|
.data 0x00000000000001ac 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtend.o
|
||||||
|
.data 0x00000000000001ac 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtn.o
|
||||||
|
|
||||||
|
.fini_array 0x00000000000001ac 0x4
|
||||||
|
.fini_array 0x00000000000001ac 0x4 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
|
||||||
|
|
||||||
|
.igot.plt 0x00000000000001b0 0x0
|
||||||
|
.igot.plt 0x00000000000001b0 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
|
||||||
|
|
||||||
|
.bss 0x00000000000001b0 0x20
|
||||||
|
*(.bss*)
|
||||||
|
.bss 0x00000000000001b0 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crti.o
|
||||||
|
.bss 0x00000000000001b0 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
|
||||||
|
.bss.completed.1
|
||||||
|
0x00000000000001b0 0x1 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
|
||||||
|
*fill* 0x00000000000001b1 0x3
|
||||||
|
.bss.object.0 0x00000000000001b4 0x18 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
|
||||||
|
.bss 0x00000000000001cc 0x4 test.o
|
||||||
|
0x00000000000001cc breakpoint_count
|
||||||
|
.bss 0x00000000000001d0 0x0 reset.o
|
||||||
|
.bss 0x00000000000001d0 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtend.o
|
||||||
|
.bss 0x00000000000001d0 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtn.o
|
||||||
|
|
||||||
|
.stack 0x00000000000001d0 0xfe0 load address 0x0000000000003000
|
||||||
|
0x00000000000001d0 _stack_bottom = .
|
||||||
|
0x00000000000011b0 . = (. + 0xfe0)
|
||||||
|
*fill* 0x00000000000001d0 0xfe0
|
||||||
|
0x00000000000011b0 _stack_top = .
|
||||||
|
LOAD test.o
|
||||||
|
LOAD reset.o
|
||||||
|
START GROUP
|
||||||
|
LOAD /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/libgcc.a
|
||||||
|
LOAD /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/../../../../arm-none-eabi/lib/libc.a
|
||||||
|
LOAD /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/../../../../arm-none-eabi/lib/libsysbase.a
|
||||||
|
END GROUP
|
||||||
|
LOAD /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtend.o
|
||||||
|
LOAD /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtn.o
|
||||||
|
OUTPUT(test.elf elf32-littlearm)
|
||||||
|
LOAD linker stubs
|
||||||
|
|
||||||
|
.ARM.attributes
|
||||||
|
0x0000000000000000 0x26
|
||||||
|
.ARM.attributes
|
||||||
|
0x0000000000000000 0x1c /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crti.o
|
||||||
|
.ARM.attributes
|
||||||
|
0x000000000000001c 0x2a /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
|
||||||
|
.ARM.attributes
|
||||||
|
0x0000000000000046 0x2a test.o
|
||||||
|
.ARM.attributes
|
||||||
|
0x0000000000000070 0x1a reset.o
|
||||||
|
.ARM.attributes
|
||||||
|
0x000000000000008a 0x2a /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtend.o
|
||||||
|
.ARM.attributes
|
||||||
|
0x00000000000000b4 0x1c /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtn.o
|
||||||
|
|
||||||
|
.comment 0x0000000000000000 0x23
|
||||||
|
.comment 0x0000000000000000 0x23 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
|
||||||
|
0x24 (size before relaxing)
|
||||||
|
.comment 0x0000000000000023 0x24 test.o
|
||||||
|
.comment 0x0000000000000023 0x24 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtend.o
|
||||||
|
|
||||||
|
.debug_info 0x0000000000000000 0x7e
|
||||||
|
.debug_info 0x0000000000000000 0x7e test.o
|
||||||
|
|
||||||
|
.debug_abbrev 0x0000000000000000 0x77
|
||||||
|
.debug_abbrev 0x0000000000000000 0x77 test.o
|
||||||
|
|
||||||
|
.debug_aranges 0x0000000000000000 0x20
|
||||||
|
.debug_aranges
|
||||||
|
0x0000000000000000 0x20 test.o
|
||||||
|
|
||||||
|
.debug_line 0x0000000000000000 0x51
|
||||||
|
.debug_line 0x0000000000000000 0x51 test.o
|
||||||
|
|
||||||
|
.debug_str 0x0000000000000000 0x69
|
||||||
|
.debug_str 0x0000000000000000 0x69 test.o
|
||||||
|
|
||||||
|
.debug_frame 0x0000000000000000 0x4c
|
||||||
|
.debug_frame 0x0000000000000000 0x4c test.o
|
|
@ -54,14 +54,14 @@ pub enum CpuAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||||
pub(super) struct BankedRegisters {
|
pub struct BankedRegisters {
|
||||||
// r13 and r14 are banked for all modes. System&User mode share them
|
// r13 and r14 are banked for all modes. System&User mode share them
|
||||||
pub(super) gpr_banked_r13: [u32; 6],
|
pub gpr_banked_r13: [u32; 6],
|
||||||
pub(super) gpr_banked_r14: [u32; 6],
|
pub gpr_banked_r14: [u32; 6],
|
||||||
// r8-r12 are banked for fiq mode
|
// r8-r12 are banked for fiq mode
|
||||||
pub(super) gpr_banked_old_r8_12: [u32; 5],
|
pub gpr_banked_old_r8_12: [u32; 5],
|
||||||
pub(super) gpr_banked_fiq_r8_12: [u32; 5],
|
pub gpr_banked_fiq_r8_12: [u32; 5],
|
||||||
pub(super) spsr_bank: [RegPSR; 6],
|
pub spsr_bank: [RegPSR; 6],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
@ -106,17 +106,21 @@ impl Default for DebuggerState {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Arm7tdmiCore<I: MemoryInterface> {
|
pub struct Arm7tdmiCore<I: MemoryInterface> {
|
||||||
pub pc: u32,
|
pub pc: u32,
|
||||||
pub(super) bus: Shared<I>,
|
pub bus: Shared<I>,
|
||||||
|
|
||||||
next_fetch_access: MemoryAccess,
|
next_fetch_access: MemoryAccess,
|
||||||
pipeline: [u32; 2],
|
pipeline: [u32; 2],
|
||||||
pub gpr: [u32; 15],
|
pub gpr: [u32; 15],
|
||||||
|
|
||||||
pub cpsr: RegPSR,
|
pub cpsr: RegPSR,
|
||||||
pub(super) spsr: RegPSR,
|
pub spsr: RegPSR,
|
||||||
|
|
||||||
pub(super) banks: BankedRegisters,
|
pub banks: BankedRegisters,
|
||||||
|
|
||||||
|
/// Hardware breakpoints for use by gdb
|
||||||
|
pub breakpoints: Vec<Addr>,
|
||||||
|
|
||||||
|
/// Deprecated in-house debugger state
|
||||||
#[cfg(feature = "debugger")]
|
#[cfg(feature = "debugger")]
|
||||||
pub dbg: DebuggerState,
|
pub dbg: DebuggerState,
|
||||||
}
|
}
|
||||||
|
@ -134,6 +138,8 @@ impl<I: MemoryInterface> Arm7tdmiCore<I> {
|
||||||
spsr: Default::default(),
|
spsr: Default::default(),
|
||||||
banks: BankedRegisters::default(),
|
banks: BankedRegisters::default(),
|
||||||
|
|
||||||
|
breakpoints: Vec::new(),
|
||||||
|
|
||||||
#[cfg(feature = "debugger")]
|
#[cfg(feature = "debugger")]
|
||||||
dbg: DebuggerState::default(),
|
dbg: DebuggerState::default(),
|
||||||
}
|
}
|
||||||
|
@ -156,6 +162,8 @@ impl<I: MemoryInterface> Arm7tdmiCore<I> {
|
||||||
pipeline: state.pipeline,
|
pipeline: state.pipeline,
|
||||||
next_fetch_access: state.next_fetch_access,
|
next_fetch_access: state.next_fetch_access,
|
||||||
|
|
||||||
|
breakpoints: Vec::new(), // TODO include breakpoints in saved state
|
||||||
|
|
||||||
// savestate does not keep debugger related information, so just reinitialize to default
|
// savestate does not keep debugger related information, so just reinitialize to default
|
||||||
#[cfg(feature = "debugger")]
|
#[cfg(feature = "debugger")]
|
||||||
dbg: DebuggerState::default(),
|
dbg: DebuggerState::default(),
|
||||||
|
@ -473,20 +481,6 @@ impl<I: MemoryInterface> Arm7tdmiCore<I> {
|
||||||
pub fn get_cpu_state(&self) -> CpuState {
|
pub fn get_cpu_state(&self) -> CpuState {
|
||||||
self.cpsr.state()
|
self.cpsr.state()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn skip_bios(&mut self) {
|
|
||||||
self.banks.gpr_banked_r13[0] = 0x0300_7f00; // USR/SYS
|
|
||||||
self.banks.gpr_banked_r13[1] = 0x0300_7f00; // FIQ
|
|
||||||
self.banks.gpr_banked_r13[2] = 0x0300_7fa0; // IRQ
|
|
||||||
self.banks.gpr_banked_r13[3] = 0x0300_7fe0; // SVC
|
|
||||||
self.banks.gpr_banked_r13[4] = 0x0300_7f00; // ABT
|
|
||||||
self.banks.gpr_banked_r13[5] = 0x0300_7f00; // UND
|
|
||||||
|
|
||||||
self.gpr[13] = 0x0300_7f00;
|
|
||||||
self.pc = 0x0800_0000;
|
|
||||||
|
|
||||||
self.cpsr.set(0x5f);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "debugger")]
|
#[cfg(feature = "debugger")]
|
||||||
|
|
56
arm7tdmi/src/gdb/breakpoints.rs
Normal file
56
arm7tdmi/src/gdb/breakpoints.rs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
use gdbstub::target;
|
||||||
|
use gdbstub::target::TargetResult;
|
||||||
|
use log::debug;
|
||||||
|
|
||||||
|
use crate::Arm7tdmiCore;
|
||||||
|
|
||||||
|
use super::target::MemoryGdbInterface;
|
||||||
|
|
||||||
|
impl<I: MemoryGdbInterface> target::ext::breakpoints::Breakpoints for Arm7tdmiCore<I> {
|
||||||
|
// there are several kinds of breakpoints - this target uses software breakpoints
|
||||||
|
#[inline(always)]
|
||||||
|
fn support_sw_breakpoint(
|
||||||
|
&mut self,
|
||||||
|
) -> Option<target::ext::breakpoints::SwBreakpointOps<'_, Self>> {
|
||||||
|
Some(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: MemoryGdbInterface> target::ext::breakpoints::SwBreakpoint for Arm7tdmiCore<I> {
|
||||||
|
fn add_sw_breakpoint(
|
||||||
|
&mut self,
|
||||||
|
addr: u32,
|
||||||
|
_kind: gdbstub_arch::arm::ArmBreakpointKind,
|
||||||
|
) -> TargetResult<bool, Self> {
|
||||||
|
debug!("adding breakpoint {:08x}", addr);
|
||||||
|
self.breakpoints.push(addr);
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_sw_breakpoint(
|
||||||
|
&mut self,
|
||||||
|
addr: u32,
|
||||||
|
_kind: gdbstub_arch::arm::ArmBreakpointKind,
|
||||||
|
) -> TargetResult<bool, Self> {
|
||||||
|
match self.breakpoints.iter().position(|x| *x == addr) {
|
||||||
|
None => Ok(false),
|
||||||
|
Some(pos) => {
|
||||||
|
debug!("deleting breakpoint {:08x}", addr);
|
||||||
|
self.breakpoints.remove(pos);
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: MemoryGdbInterface> Arm7tdmiCore<I> {
|
||||||
|
pub fn check_breakpoint(&self) -> Option<u32> {
|
||||||
|
let next_pc = self.get_next_pc();
|
||||||
|
for bp in &self.breakpoints {
|
||||||
|
if (*bp & !1) == next_pc {
|
||||||
|
return Some(*bp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
45
arm7tdmi/src/gdb/mod.rs
Normal file
45
arm7tdmi/src/gdb/mod.rs
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
use std::io;
|
||||||
|
use std::net::{TcpListener, TcpStream};
|
||||||
|
|
||||||
|
use log::info;
|
||||||
|
|
||||||
|
mod breakpoints;
|
||||||
|
pub mod target;
|
||||||
|
|
||||||
|
/// Wait for tcp connection on port
|
||||||
|
pub fn wait_for_connection(port: u16) -> io::Result<TcpStream> {
|
||||||
|
let bind_addr = format!("localhost:{port}");
|
||||||
|
let sock = TcpListener::bind(bind_addr)?;
|
||||||
|
|
||||||
|
info!("waiting for connection");
|
||||||
|
// Blocks until a GDB client connects via TCP.
|
||||||
|
// i.e: Running `target remote localhost:<port>` from the GDB prompt.
|
||||||
|
let (stream, addr) = sock.accept()?;
|
||||||
|
|
||||||
|
info!("gdb connected from {:?}", addr);
|
||||||
|
|
||||||
|
Ok(stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Copy all bytes of `data` to `buf`.
|
||||||
|
/// Return the size of data copied.
|
||||||
|
pub fn copy_to_buf(data: &[u8], buf: &mut [u8]) -> usize {
|
||||||
|
let len = buf.len().min(data.len());
|
||||||
|
buf[..len].copy_from_slice(&data[..len]);
|
||||||
|
len
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Copy a range of `data` (start at `offset` with a size of `length`) to `buf`.
|
||||||
|
/// Return the size of data copied. Returns 0 if `offset >= buf.len()`.
|
||||||
|
///
|
||||||
|
/// Mainly used by qXfer:_object_:read commands.
|
||||||
|
pub fn copy_range_to_buf(data: &[u8], offset: u64, length: usize, buf: &mut [u8]) -> usize {
|
||||||
|
let offset = offset as usize;
|
||||||
|
if offset > data.len() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let start = offset;
|
||||||
|
let end = (offset + length).min(data.len());
|
||||||
|
copy_to_buf(&data[start..end], buf)
|
||||||
|
}
|
113
arm7tdmi/src/gdb/target.rs
Normal file
113
arm7tdmi/src/gdb/target.rs
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
/// Implementing the Target trait for gdbstub
|
||||||
|
use gdbstub::common::Signal;
|
||||||
|
use gdbstub::target::ext::base::singlethread::{
|
||||||
|
SingleThreadBase, SingleThreadResume, SingleThreadSingleStep,
|
||||||
|
};
|
||||||
|
use gdbstub::target::ext::base::singlethread::{SingleThreadResumeOps, SingleThreadSingleStepOps};
|
||||||
|
use gdbstub::target::ext::base::BaseOps;
|
||||||
|
use gdbstub::target::ext::breakpoints::BreakpointsOps;
|
||||||
|
use gdbstub::target::{self, Target, TargetError, TargetResult};
|
||||||
|
|
||||||
|
use crate::memory::{DebugRead, MemoryInterface};
|
||||||
|
use crate::registers_consts::*;
|
||||||
|
use crate::Arm7tdmiCore;
|
||||||
|
|
||||||
|
pub trait MemoryGdbInterface: MemoryInterface + DebugRead {
|
||||||
|
fn memory_map_xml(&self, offset: u64, length: usize, buf: &mut [u8]) -> usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: MemoryGdbInterface> Target for Arm7tdmiCore<I> {
|
||||||
|
type Error = ();
|
||||||
|
type Arch = gdbstub_arch::arm::Armv4t; // as an example
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn base_ops(&mut self) -> BaseOps<Self::Arch, Self::Error> {
|
||||||
|
BaseOps::SingleThread(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
// opt-in to support for setting/removing breakpoints
|
||||||
|
#[inline(always)]
|
||||||
|
fn support_breakpoints(&mut self) -> Option<BreakpointsOps<Self>> {
|
||||||
|
Some(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn support_memory_map(&mut self) -> Option<target::ext::memory_map::MemoryMapOps<Self>> {
|
||||||
|
Some(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: MemoryGdbInterface> SingleThreadBase for Arm7tdmiCore<I> {
|
||||||
|
fn read_registers(
|
||||||
|
&mut self,
|
||||||
|
regs: &mut gdbstub_arch::arm::reg::ArmCoreRegs,
|
||||||
|
) -> TargetResult<(), Self> {
|
||||||
|
regs.pc = self.get_reg(REG_PC);
|
||||||
|
regs.lr = self.get_reg(REG_LR);
|
||||||
|
regs.sp = self.get_reg(REG_SP);
|
||||||
|
regs.r[..].copy_from_slice(&self.gpr[..13]);
|
||||||
|
regs.cpsr = self.cpsr.get();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_registers(
|
||||||
|
&mut self,
|
||||||
|
regs: &gdbstub_arch::arm::reg::ArmCoreRegs,
|
||||||
|
) -> TargetResult<(), Self> {
|
||||||
|
self.set_reg(REG_PC, regs.pc);
|
||||||
|
self.set_reg(REG_LR, regs.lr);
|
||||||
|
self.set_reg(REG_SP, regs.sp);
|
||||||
|
self.gpr.copy_from_slice(®s.r);
|
||||||
|
self.cpsr.set(regs.cpsr);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_addrs(&mut self, start_addr: u32, data: &mut [u8]) -> TargetResult<(), Self> {
|
||||||
|
self.bus.debug_get_into_bytes(start_addr, data);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_addrs(&mut self, _start_addr: u32, _data: &[u8]) -> TargetResult<(), Self> {
|
||||||
|
// todo!("implement DebugWrite bus extention")
|
||||||
|
Err(TargetError::NonFatal)
|
||||||
|
}
|
||||||
|
|
||||||
|
// most targets will want to support at resumption as well...
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn support_resume(&mut self) -> Option<SingleThreadResumeOps<Self>> {
|
||||||
|
Some(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: MemoryGdbInterface> SingleThreadResume for Arm7tdmiCore<I> {
|
||||||
|
fn resume(&mut self, _signal: Option<Signal>) -> Result<(), Self::Error> {
|
||||||
|
// do nothing
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...and if the target supports resumption, it'll likely want to support
|
||||||
|
// single-step resume as well
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn support_single_step(&mut self) -> Option<SingleThreadSingleStepOps<'_, Self>> {
|
||||||
|
Some(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: MemoryGdbInterface> SingleThreadSingleStep for Arm7tdmiCore<I> {
|
||||||
|
fn step(&mut self, _signal: Option<Signal>) -> Result<(), Self::Error> {
|
||||||
|
self.step();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: MemoryGdbInterface> target::ext::memory_map::MemoryMap for Arm7tdmiCore<I> {
|
||||||
|
fn memory_map_xml(
|
||||||
|
&self,
|
||||||
|
offset: u64,
|
||||||
|
length: usize,
|
||||||
|
buf: &mut [u8],
|
||||||
|
) -> TargetResult<usize, Self> {
|
||||||
|
Ok(self.bus.memory_map_xml(offset, length, buf))
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,7 +23,10 @@ pub use alu::*;
|
||||||
use memory::Addr;
|
use memory::Addr;
|
||||||
pub mod disass;
|
pub mod disass;
|
||||||
pub mod exception;
|
pub mod exception;
|
||||||
|
pub mod gdb;
|
||||||
pub mod psr;
|
pub mod psr;
|
||||||
|
mod simple_memory;
|
||||||
|
pub use simple_memory::SimpleMemory;
|
||||||
|
|
||||||
pub mod registers_consts {
|
pub mod registers_consts {
|
||||||
pub const REG_PC: usize = 15;
|
pub const REG_PC: usize = 15;
|
||||||
|
|
|
@ -225,13 +225,20 @@ pub trait DebugRead: BusIO {
|
||||||
|
|
||||||
fn debug_read_8(&mut self, addr: Addr) -> u8;
|
fn debug_read_8(&mut self, addr: Addr) -> u8;
|
||||||
|
|
||||||
fn debug_get_bytes(&mut self, range: std::ops::Range<u32>) -> Vec<u8> {
|
fn debug_get_bytes(&mut self, range: std::ops::Range<Addr>) -> Vec<u8> {
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = Vec::new();
|
||||||
for b in range {
|
for b in range {
|
||||||
bytes.push(self.debug_read_8(b));
|
bytes.push(self.debug_read_8(b));
|
||||||
}
|
}
|
||||||
bytes
|
bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn debug_get_into_bytes(&mut self, start_addr: Addr, bytes: &mut [u8]) {
|
||||||
|
bytes
|
||||||
|
.iter_mut()
|
||||||
|
.enumerate()
|
||||||
|
.for_each(|(idx, byte)| *byte = self.debug_read_8(start_addr + (idx as Addr)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The caller is assumed to handle out of bound accesses,
|
/// The caller is assumed to handle out of bound accesses,
|
||||||
|
|
83
arm7tdmi/src/simple_memory.rs
Normal file
83
arm7tdmi/src/simple_memory.rs
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
use crate::gdb::{copy_range_to_buf, target::MemoryGdbInterface};
|
||||||
|
use crate::memory::{Addr, BusIO, DebugRead, MemoryAccess, MemoryInterface};
|
||||||
|
|
||||||
|
/// Simple wrapper around a bytearray for memory access
|
||||||
|
/// For use by tests and examples of this crate.
|
||||||
|
pub struct SimpleMemory {
|
||||||
|
data: Box<[u8]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SimpleMemory {
|
||||||
|
pub fn new(capacity: usize) -> SimpleMemory {
|
||||||
|
SimpleMemory {
|
||||||
|
data: vec![0; capacity].into_boxed_slice(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn load_program(&mut self, program: &[u8]) {
|
||||||
|
self.data[..program.len()].copy_from_slice(program);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemoryInterface for SimpleMemory {
|
||||||
|
#[inline]
|
||||||
|
fn load_8(&mut self, addr: u32, _access: MemoryAccess) -> u8 {
|
||||||
|
self.read_8(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn load_16(&mut self, addr: u32, _access: MemoryAccess) -> u16 {
|
||||||
|
self.read_16(addr & !1)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn load_32(&mut self, addr: u32, _access: MemoryAccess) -> u32 {
|
||||||
|
self.read_32(addr & !3)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn store_8(&mut self, addr: u32, value: u8, _access: MemoryAccess) {
|
||||||
|
self.write_8(addr, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn store_16(&mut self, addr: u32, value: u16, _access: MemoryAccess) {
|
||||||
|
self.write_16(addr & !1, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn store_32(&mut self, addr: u32, value: u32, _access: MemoryAccess) {
|
||||||
|
self.write_32(addr & !3, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn idle_cycle(&mut self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BusIO for SimpleMemory {
|
||||||
|
fn read_8(&mut self, addr: Addr) -> u8 {
|
||||||
|
*self.data.get(addr as usize).unwrap_or(&0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_8(&mut self, addr: Addr, value: u8) {
|
||||||
|
if let 0..=0x3FFF = addr {
|
||||||
|
self.data[addr as usize] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DebugRead for SimpleMemory {
|
||||||
|
fn debug_read_8(&mut self, addr: Addr) -> u8 {
|
||||||
|
*self.data.get(addr as usize).unwrap_or(&0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemoryGdbInterface for SimpleMemory {
|
||||||
|
fn memory_map_xml(&self, offset: u64, length: usize, buf: &mut [u8]) -> usize {
|
||||||
|
let memory_map = r#"<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE memory-map
|
||||||
|
PUBLIC "+//IDN gnu.org//DTD GDB Memory Map V1.0//EN"
|
||||||
|
"http://sourceware.org/gdb/gdb-memory-map.dtd">
|
||||||
|
<memory-map>
|
||||||
|
<memory type="ram" start="0x0" length="16384"/>
|
||||||
|
</memory-map>"#
|
||||||
|
.trim()
|
||||||
|
.as_bytes();
|
||||||
|
copy_range_to_buf(memory_map, offset, length, buf)
|
||||||
|
}
|
||||||
|
}
|
|
@ -37,7 +37,6 @@ hex-literal = "0.2.1"
|
||||||
rustyline = { version = "6.0.0", optional = true }
|
rustyline = { version = "6.0.0", optional = true }
|
||||||
nom = { version = "5.0.0", optional = true }
|
nom = { version = "5.0.0", optional = true }
|
||||||
gdbstub = { version = "0.1.2", optional = true, features = ["std"] }
|
gdbstub = { version = "0.1.2", optional = true, features = ["std"] }
|
||||||
goblin = { version = "0.2", optional = true }
|
|
||||||
fuzzy-matcher = { version = "0.3.4", optional = true }
|
fuzzy-matcher = { version = "0.3.4", optional = true }
|
||||||
bit_reverse = "0.1.8"
|
bit_reverse = "0.1.8"
|
||||||
yaml-rust = "0.4"
|
yaml-rust = "0.4"
|
||||||
|
@ -53,7 +52,7 @@ harness = false
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
elf_support = ["goblin"]
|
elf_support = []
|
||||||
debugger = ["nom", "rustyline", "fuzzy-matcher", "elf_support"]
|
debugger = ["nom", "rustyline", "fuzzy-matcher", "elf_support"]
|
||||||
gdb = ["gdbstub"]
|
gdb = ["gdbstub"]
|
||||||
# For use for ports where VideoInterface is not needed like wasm & jni
|
# For use for ports where VideoInterface is not needed like wasm & jni
|
||||||
|
|
|
@ -6,11 +6,11 @@ use std::io::prelude::*;
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
#[cfg(feature = "elf_support")]
|
||||||
|
use rustboyadvance_utils::elf::{load_elf, GoblinError};
|
||||||
use rustboyadvance_utils::read_bin_file;
|
use rustboyadvance_utils::read_bin_file;
|
||||||
use zip::ZipArchive;
|
use zip::ZipArchive;
|
||||||
|
|
||||||
#[cfg(feature = "elf_support")]
|
|
||||||
use goblin;
|
|
||||||
|
|
||||||
pub enum LoadRom {
|
pub enum LoadRom {
|
||||||
#[cfg(feature = "elf_support")]
|
#[cfg(feature = "elf_support")]
|
||||||
|
@ -23,12 +23,22 @@ pub enum LoadRom {
|
||||||
type LoadRomResult = GBAResult<LoadRom>;
|
type LoadRomResult = GBAResult<LoadRom>;
|
||||||
|
|
||||||
#[cfg(feature = "elf_support")]
|
#[cfg(feature = "elf_support")]
|
||||||
impl From<goblin::error::Error> for GBAError {
|
impl From<GoblinError> for GBAError {
|
||||||
fn from(err: goblin::error::Error) -> GBAError {
|
fn from(err: GoblinError) -> GBAError {
|
||||||
GBAError::CartridgeLoadError(format!("elf parsing error: {}", err))
|
GBAError::CartridgeLoadError(format!("elf parsing error: {}", err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "elf_support")]
|
||||||
|
pub(super) fn try_load_elf(elf_bytes: &[u8]) -> LoadRomResult {
|
||||||
|
const CART_BASE: usize = 0x0800_0000;
|
||||||
|
let elf = load_elf(elf_bytes, CART_BASE)?;
|
||||||
|
Ok(LoadRom::Elf {
|
||||||
|
data: elf.data,
|
||||||
|
symbols: elf.symbols,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn try_load_zip(data: &[u8]) -> LoadRomResult {
|
fn try_load_zip(data: &[u8]) -> LoadRomResult {
|
||||||
let reader = Cursor::new(data);
|
let reader = Cursor::new(data);
|
||||||
let mut archive = ZipArchive::new(reader)?;
|
let mut archive = ZipArchive::new(reader)?;
|
||||||
|
@ -45,62 +55,6 @@ fn try_load_zip(data: &[u8]) -> LoadRomResult {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "elf_support")]
|
|
||||||
fn try_load_elf(elf_bytes: &[u8]) -> LoadRomResult {
|
|
||||||
const CART_BASE: usize = 0x0800_0000;
|
|
||||||
|
|
||||||
let elf = goblin::elf::Elf::parse(&elf_bytes)?;
|
|
||||||
|
|
||||||
let entry = elf.entry;
|
|
||||||
if entry != (CART_BASE as u64) {
|
|
||||||
return Err(GBAError::CartridgeLoadError(
|
|
||||||
"bad elf entry point, maybe multiboot rom ?".to_owned(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut rom = vec![0; 0x200_0000];
|
|
||||||
for phdr in &elf.program_headers {
|
|
||||||
if phdr.p_type == goblin::elf::program_header::PT_LOAD {
|
|
||||||
let file_range = phdr.file_range();
|
|
||||||
let phys_range =
|
|
||||||
(phdr.p_paddr as usize)..(phdr.p_paddr as usize + phdr.p_memsz as usize);
|
|
||||||
let phys_range_adjusted = (phdr.p_paddr as usize - CART_BASE)
|
|
||||||
..(phdr.p_paddr as usize + phdr.p_memsz as usize - CART_BASE);
|
|
||||||
|
|
||||||
if phys_range_adjusted.start + (phdr.p_filesz as usize) >= rom.len() {
|
|
||||||
warn!("ELF: skipping program header {:?}", phdr);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
info!(
|
|
||||||
"ELF: loading segment phdr: {:?} range {:#x?} vec range {:#x?}",
|
|
||||||
phdr, file_range, phys_range,
|
|
||||||
);
|
|
||||||
|
|
||||||
let src = &elf_bytes[file_range];
|
|
||||||
let dst = &mut rom[phys_range_adjusted];
|
|
||||||
dst.copy_from_slice(src);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut symbols = HashMap::new();
|
|
||||||
|
|
||||||
let strtab = elf.strtab;
|
|
||||||
for sym in elf.syms.iter() {
|
|
||||||
if let Some(Ok(name)) = strtab.get(sym.st_name) {
|
|
||||||
// TODO do I also want to save the symbol size ?
|
|
||||||
symbols.insert(name.to_owned(), sym.st_value as u32);
|
|
||||||
} else {
|
|
||||||
warn!("failed to parse symbol name sym {:?}", sym);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(LoadRom::Elf {
|
|
||||||
data: rom,
|
|
||||||
symbols: symbols,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn load_from_file(path: &Path) -> LoadRomResult {
|
pub(super) fn load_from_file(path: &Path) -> LoadRomResult {
|
||||||
let bytes = read_bin_file(path)?;
|
let bytes = read_bin_file(path)?;
|
||||||
|
|
||||||
|
|
|
@ -331,7 +331,15 @@ impl GameBoyAdvance {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn skip_bios(&mut self) {
|
pub fn skip_bios(&mut self) {
|
||||||
self.cpu.skip_bios();
|
self.cpu.banks.gpr_banked_r13[0] = 0x0300_7f00; // USR/SYS
|
||||||
|
self.cpu.banks.gpr_banked_r13[1] = 0x0300_7f00; // FIQ
|
||||||
|
self.cpu.banks.gpr_banked_r13[2] = 0x0300_7fa0; // IRQ
|
||||||
|
self.cpu.banks.gpr_banked_r13[3] = 0x0300_7fe0; // SVC
|
||||||
|
self.cpu.banks.gpr_banked_r13[4] = 0x0300_7f00; // ABT
|
||||||
|
self.cpu.banks.gpr_banked_r13[5] = 0x0300_7f00; // UND
|
||||||
|
self.cpu.gpr[13] = 0x0300_7f00;
|
||||||
|
self.cpu.pc = 0x0800_0000;
|
||||||
|
self.cpu.cpsr.set(0x5f);
|
||||||
self.sysbus.io.gpu.skip_bios();
|
self.sysbus.io.gpu.skip_bios();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use super::arm7tdmi::CpuState;
|
use super::arm7tdmi::CpuState;
|
||||||
use super::core::GameBoyAdvance;
|
use super::GameBoyAdvance;
|
||||||
use super::interrupt::*;
|
use super::interrupt::*;
|
||||||
use super::iodev::IoDevices;
|
use super::iodev::IoDevices;
|
||||||
use super::sysbus::SysBus;
|
use super::sysbus::SysBus;
|
||||||
|
|
|
@ -52,9 +52,6 @@ pub use bus::*;
|
||||||
mod mgba_debug;
|
mod mgba_debug;
|
||||||
pub(crate) mod overrides;
|
pub(crate) mod overrides;
|
||||||
|
|
||||||
#[cfg(feature = "gdb")]
|
|
||||||
pub mod gdb;
|
|
||||||
|
|
||||||
#[cfg(feature = "debugger")]
|
#[cfg(feature = "debugger")]
|
||||||
pub mod debugger;
|
pub mod debugger;
|
||||||
|
|
||||||
|
|
|
@ -20,5 +20,6 @@ spin_sleep = "0.3.7"
|
||||||
winres = "0.1"
|
winres = "0.1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
default = ["gdb"]
|
||||||
debugger = ["rustboyadvance-core/debugger"]
|
debugger = ["rustboyadvance-core/debugger"]
|
||||||
gdb = ["rustboyadvance-core/gdb"]
|
gdb = ["rustboyadvance-core/gdb"]
|
|
@ -41,14 +41,11 @@ use input::create_input;
|
||||||
use video::{create_video_interface, SCREEN_HEIGHT, SCREEN_WIDTH};
|
use video::{create_video_interface, SCREEN_HEIGHT, SCREEN_WIDTH};
|
||||||
|
|
||||||
use rustboyadvance_core::cartridge::BackupType;
|
use rustboyadvance_core::cartridge::BackupType;
|
||||||
#[cfg(feature = "gdb")]
|
|
||||||
use rustboyadvance_core::gdb::spawn_and_run_gdb_server;
|
|
||||||
use rustboyadvance_core::prelude::*;
|
use rustboyadvance_core::prelude::*;
|
||||||
|
|
||||||
use rustboyadvance_utils::FpsCounter;
|
use rustboyadvance_utils::FpsCounter;
|
||||||
|
|
||||||
const LOG_DIR: &str = ".logs";
|
const LOG_DIR: &str = ".logs";
|
||||||
#[cfg(feature = "gdb")]
|
|
||||||
const DEFAULT_GDB_SERVER_ADDR: &'static str = "localhost:1337";
|
const DEFAULT_GDB_SERVER_ADDR: &'static str = "localhost:1337";
|
||||||
|
|
||||||
const CANVAS_WIDTH: u32 = SCREEN_WIDTH;
|
const CANVAS_WIDTH: u32 = SCREEN_WIDTH;
|
||||||
|
@ -240,13 +237,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "gdb")]
|
|
||||||
if with_gdbserver {
|
if with_gdbserver {
|
||||||
spawn_and_run_gdb_server(&mut gba, DEFAULT_GDB_SERVER_ADDR)?;
|
todo!("gdb")
|
||||||
}
|
|
||||||
#[cfg(not(feature = "gdb"))]
|
|
||||||
if with_gdbserver {
|
|
||||||
panic!("Please compile me with 'gdb' feature")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut fps_counter = FpsCounter::default();
|
let mut fps_counter = FpsCounter::default();
|
||||||
|
@ -277,7 +269,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
info!("ending debugger...")
|
info!("ending debugger...")
|
||||||
}
|
}
|
||||||
#[cfg(feature = "gdb")]
|
#[cfg(feature = "gdb")]
|
||||||
Scancode::F2 => spawn_and_run_gdb_server(&mut gba, DEFAULT_GDB_SERVER_ADDR)?,
|
Scancode::F2 => todo!("gdb"),
|
||||||
Scancode::F5 => {
|
Scancode::F5 => {
|
||||||
info!("Saving state ...");
|
info!("Saving state ...");
|
||||||
let save = gba.save_state()?;
|
let save = gba.save_state()?;
|
||||||
|
|
|
@ -7,6 +7,9 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ringbuf = "0.2.2"
|
ringbuf = "0.2.2"
|
||||||
|
log = "0.4.8"
|
||||||
|
goblin = "0.2"
|
||||||
|
|
||||||
|
|
||||||
[target.'cfg(target_arch="wasm32")'.dependencies]
|
[target.'cfg(target_arch="wasm32")'.dependencies]
|
||||||
instant = { version = "0.1.2", features = ["wasm-bindgen"] }
|
instant = { version = "0.1.2", features = ["wasm-bindgen"] }
|
||||||
|
|
67
utils/src/elf.rs
Normal file
67
utils/src/elf.rs
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
use goblin::elf;
|
||||||
|
use log::{info, warn};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub use goblin::error::Error as GoblinError;
|
||||||
|
|
||||||
|
pub type SymbolMap = HashMap<String, u32>;
|
||||||
|
|
||||||
|
pub struct LoadedElf {
|
||||||
|
pub data: Vec<u8>,
|
||||||
|
pub entry: u64,
|
||||||
|
pub symbols: SymbolMap,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_symbols_from_elf(elf: &elf::Elf) -> SymbolMap {
|
||||||
|
let mut symbols = SymbolMap::new();
|
||||||
|
let strtab = &elf.strtab;
|
||||||
|
for sym in elf.syms.iter() {
|
||||||
|
if let Some(Ok(name)) = strtab.get(sym.st_name) {
|
||||||
|
// TODO do I also want to save the symbol size ?
|
||||||
|
symbols.insert(name.to_owned(), sym.st_value as u32);
|
||||||
|
} else {
|
||||||
|
warn!("failed to parse symbol name sym {:?}", sym);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
symbols
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_symbols(elf_bytes: &[u8]) -> goblin::error::Result<SymbolMap> {
|
||||||
|
let elf = elf::Elf::parse(elf_bytes)?;
|
||||||
|
Ok(read_symbols_from_elf(&elf))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_elf(elf_bytes: &[u8], base: usize) -> goblin::error::Result<LoadedElf> {
|
||||||
|
let elf = elf::Elf::parse(elf_bytes)?;
|
||||||
|
|
||||||
|
let mut rom = vec![0; 0x200_0000];
|
||||||
|
for phdr in &elf.program_headers {
|
||||||
|
if phdr.p_type == elf::program_header::PT_LOAD {
|
||||||
|
let file_range = phdr.file_range();
|
||||||
|
let phys_range =
|
||||||
|
(phdr.p_paddr as usize)..(phdr.p_paddr as usize + phdr.p_memsz as usize);
|
||||||
|
let phys_range_adjusted = (phdr.p_paddr as usize - base)
|
||||||
|
..(phdr.p_paddr as usize + phdr.p_memsz as usize - base);
|
||||||
|
|
||||||
|
if phys_range_adjusted.start + (phdr.p_filesz as usize) >= rom.len() {
|
||||||
|
warn!("ELF: skipping program header {:?}", phdr);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"ELF: loading segment phdr: {:?} range {:#x?} vec range {:#x?}",
|
||||||
|
phdr, file_range, phys_range,
|
||||||
|
);
|
||||||
|
|
||||||
|
let src = &elf_bytes[file_range];
|
||||||
|
let dst = &mut rom[phys_range_adjusted];
|
||||||
|
dst.copy_from_slice(src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(LoadedElf {
|
||||||
|
data: rom,
|
||||||
|
entry: elf.entry,
|
||||||
|
symbols: read_symbols_from_elf(&elf),
|
||||||
|
})
|
||||||
|
}
|
|
@ -6,6 +6,8 @@ use std::path::Path;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::time;
|
use std::time;
|
||||||
|
|
||||||
|
pub mod elf;
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
type Instant = time::Instant;
|
type Instant = time::Instant;
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
|
Reference in a new issue