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",
|
||||
"byteorder",
|
||||
"cfg-if 1.0.0",
|
||||
"colored",
|
||||
"colored 1.9.3",
|
||||
"enum-primitive-derive",
|
||||
"gdbstub 0.6.3",
|
||||
"gdbstub_arch",
|
||||
"log 0.4.11",
|
||||
"num 0.2.1",
|
||||
"num-traits 0.2.12",
|
||||
"rustboyadvance-utils",
|
||||
"serde",
|
||||
"simple_logger",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -201,9 +205,9 @@ checksum = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
|
|||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "blake2b_simd"
|
||||
|
@ -333,7 +337,7 @@ checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
|
|||
dependencies = [
|
||||
"ansi_term 0.11.0",
|
||||
"atty",
|
||||
"bitflags 1.2.1",
|
||||
"bitflags 1.3.2",
|
||||
"strsim",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
|
@ -352,6 +356,17 @@ dependencies = [
|
|||
"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]]
|
||||
name = "combine"
|
||||
version = "4.3.2"
|
||||
|
@ -485,7 +500,7 @@ checksum = "00affe7f6ab566df61b4be3ce8cf16bc2576bca0963ceb0955e45d514bf9a279"
|
|||
dependencies = [
|
||||
"bstr",
|
||||
"csv-core",
|
||||
"itoa",
|
||||
"itoa 0.4.6",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
@ -654,6 +669,30 @@ dependencies = [
|
|||
"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]]
|
||||
name = "gdi32-sys"
|
||||
version = "0.1.2"
|
||||
|
@ -793,6 +832,12 @@ version = "0.4.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
|
||||
|
||||
[[package]]
|
||||
name = "jni"
|
||||
version = "0.17.0"
|
||||
|
@ -851,7 +896,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "db65c6da02e61f55dae90a0ae427b2a5f6b3e8db09f58d10efab23af92592616"
|
||||
dependencies = [
|
||||
"arrayvec 0.5.2",
|
||||
"bitflags 1.2.1",
|
||||
"bitflags 1.3.2",
|
||||
"cfg-if 0.1.10",
|
||||
"ryu",
|
||||
"static_assertions",
|
||||
|
@ -859,9 +904,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.77"
|
||||
version = "0.2.132"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235"
|
||||
checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
|
||||
|
||||
[[package]]
|
||||
name = "libretro-backend"
|
||||
|
@ -906,6 +951,12 @@ dependencies = [
|
|||
"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]]
|
||||
name = "maybe-uninit"
|
||||
version = "2.0.0"
|
||||
|
@ -980,7 +1031,7 @@ version = "0.18.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83450fe6a6142ddd95fb064b746083fc4ef1705fe81f64a64e1d4b39f54a1055"
|
||||
dependencies = [
|
||||
"bitflags 1.2.1",
|
||||
"bitflags 1.3.2",
|
||||
"cc",
|
||||
"cfg-if 0.1.10",
|
||||
"libc",
|
||||
|
@ -1110,6 +1161,15 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_threads"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.20.0"
|
||||
|
@ -1144,6 +1204,12 @@ dependencies = [
|
|||
"sdl2 0.31.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.1.9"
|
||||
|
@ -1374,18 +1440,17 @@ dependencies = [
|
|||
"bit-set",
|
||||
"bit_reverse",
|
||||
"bitfield",
|
||||
"bitflags 1.2.1",
|
||||
"bitflags 1.3.2",
|
||||
"byteorder",
|
||||
"bytesize",
|
||||
"cfg-if 1.0.0",
|
||||
"chrono",
|
||||
"colored",
|
||||
"colored 1.9.3",
|
||||
"criterion",
|
||||
"debug_stub_derive",
|
||||
"enum-primitive-derive",
|
||||
"fuzzy-matcher",
|
||||
"gdbstub",
|
||||
"goblin",
|
||||
"gdbstub 0.1.2",
|
||||
"hex-literal",
|
||||
"hexdump",
|
||||
"lazy_static 1.4.0",
|
||||
|
@ -1461,7 +1526,9 @@ dependencies = [
|
|||
name = "rustboyadvance-utils"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"goblin",
|
||||
"instant",
|
||||
"log 0.4.11",
|
||||
"ringbuf",
|
||||
]
|
||||
|
||||
|
@ -1583,7 +1650,7 @@ version = "0.33.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f74124048ea86b5cd50236b2443f6f57cf4625a8e8818009b4e50dbb8729a43"
|
||||
dependencies = [
|
||||
"bitflags 1.2.1",
|
||||
"bitflags 1.3.2",
|
||||
"lazy_static 1.4.0",
|
||||
"libc",
|
||||
"sdl2-sys 0.33.0",
|
||||
|
@ -1659,7 +1726,7 @@ version = "1.0.57"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"itoa 0.4.6",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
@ -1682,6 +1749,19 @@ dependencies = [
|
|||
"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]]
|
||||
name = "smart-default"
|
||||
version = "0.6.0"
|
||||
|
@ -1872,11 +1952,23 @@ dependencies = [
|
|||
"libc",
|
||||
"standback",
|
||||
"stdweb",
|
||||
"time-macros",
|
||||
"time-macros 0.1.1",
|
||||
"version_check",
|
||||
"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]]
|
||||
name = "time-macros"
|
||||
version = "0.1.1"
|
||||
|
@ -1887,6 +1979,12 @@ dependencies = [
|
|||
"time-macros-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros-impl"
|
||||
version = "0.1.1"
|
||||
|
|
|
@ -7,6 +7,7 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
rustboyadvance-utils = {"path" = "../utils" }
|
||||
log = "0.4.8"
|
||||
bit = "^0.1"
|
||||
cfg-if = "1.0.0"
|
||||
serde = { version = "1.0.104", features = ["derive", "rc"] }
|
||||
|
@ -16,6 +17,11 @@ byteorder = "1"
|
|||
num = "0.2.1"
|
||||
num-traits = "0.2"
|
||||
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]
|
||||
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)]
|
||||
pub(super) struct BankedRegisters {
|
||||
pub struct BankedRegisters {
|
||||
// r13 and r14 are banked for all modes. System&User mode share them
|
||||
pub(super) gpr_banked_r13: [u32; 6],
|
||||
pub(super) gpr_banked_r14: [u32; 6],
|
||||
pub gpr_banked_r13: [u32; 6],
|
||||
pub gpr_banked_r14: [u32; 6],
|
||||
// r8-r12 are banked for fiq mode
|
||||
pub(super) gpr_banked_old_r8_12: [u32; 5],
|
||||
pub(super) gpr_banked_fiq_r8_12: [u32; 5],
|
||||
pub(super) spsr_bank: [RegPSR; 6],
|
||||
pub gpr_banked_old_r8_12: [u32; 5],
|
||||
pub gpr_banked_fiq_r8_12: [u32; 5],
|
||||
pub spsr_bank: [RegPSR; 6],
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
|
@ -106,17 +106,21 @@ impl Default for DebuggerState {
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct Arm7tdmiCore<I: MemoryInterface> {
|
||||
pub pc: u32,
|
||||
pub(super) bus: Shared<I>,
|
||||
pub bus: Shared<I>,
|
||||
|
||||
next_fetch_access: MemoryAccess,
|
||||
pipeline: [u32; 2],
|
||||
pub gpr: [u32; 15],
|
||||
|
||||
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")]
|
||||
pub dbg: DebuggerState,
|
||||
}
|
||||
|
@ -134,6 +138,8 @@ impl<I: MemoryInterface> Arm7tdmiCore<I> {
|
|||
spsr: Default::default(),
|
||||
banks: BankedRegisters::default(),
|
||||
|
||||
breakpoints: Vec::new(),
|
||||
|
||||
#[cfg(feature = "debugger")]
|
||||
dbg: DebuggerState::default(),
|
||||
}
|
||||
|
@ -156,6 +162,8 @@ impl<I: MemoryInterface> Arm7tdmiCore<I> {
|
|||
pipeline: state.pipeline,
|
||||
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
|
||||
#[cfg(feature = "debugger")]
|
||||
dbg: DebuggerState::default(),
|
||||
|
@ -473,20 +481,6 @@ impl<I: MemoryInterface> Arm7tdmiCore<I> {
|
|||
pub fn get_cpu_state(&self) -> CpuState {
|
||||
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")]
|
||||
|
|
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;
|
||||
pub mod disass;
|
||||
pub mod exception;
|
||||
pub mod gdb;
|
||||
pub mod psr;
|
||||
mod simple_memory;
|
||||
pub use simple_memory::SimpleMemory;
|
||||
|
||||
pub mod registers_consts {
|
||||
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_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();
|
||||
for b in range {
|
||||
bytes.push(self.debug_read_8(b));
|
||||
}
|
||||
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,
|
||||
|
|
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 }
|
||||
nom = { version = "5.0.0", optional = true }
|
||||
gdbstub = { version = "0.1.2", optional = true, features = ["std"] }
|
||||
goblin = { version = "0.2", optional = true }
|
||||
fuzzy-matcher = { version = "0.3.4", optional = true }
|
||||
bit_reverse = "0.1.8"
|
||||
yaml-rust = "0.4"
|
||||
|
@ -53,7 +52,7 @@ harness = false
|
|||
|
||||
[features]
|
||||
default = []
|
||||
elf_support = ["goblin"]
|
||||
elf_support = []
|
||||
debugger = ["nom", "rustyline", "fuzzy-matcher", "elf_support"]
|
||||
gdb = ["gdbstub"]
|
||||
# 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::path::Path;
|
||||
|
||||
#[cfg(feature = "elf_support")]
|
||||
use rustboyadvance_utils::elf::{load_elf, GoblinError};
|
||||
use rustboyadvance_utils::read_bin_file;
|
||||
use zip::ZipArchive;
|
||||
|
||||
#[cfg(feature = "elf_support")]
|
||||
use goblin;
|
||||
|
||||
pub enum LoadRom {
|
||||
#[cfg(feature = "elf_support")]
|
||||
|
@ -23,12 +23,22 @@ pub enum LoadRom {
|
|||
type LoadRomResult = GBAResult<LoadRom>;
|
||||
|
||||
#[cfg(feature = "elf_support")]
|
||||
impl From<goblin::error::Error> for GBAError {
|
||||
fn from(err: goblin::error::Error) -> GBAError {
|
||||
impl From<GoblinError> for GBAError {
|
||||
fn from(err: GoblinError) -> GBAError {
|
||||
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 {
|
||||
let reader = Cursor::new(data);
|
||||
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 {
|
||||
let bytes = read_bin_file(path)?;
|
||||
|
||||
|
|
|
@ -331,7 +331,15 @@ impl GameBoyAdvance {
|
|||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use super::arm7tdmi::CpuState;
|
||||
use super::core::GameBoyAdvance;
|
||||
use super::GameBoyAdvance;
|
||||
use super::interrupt::*;
|
||||
use super::iodev::IoDevices;
|
||||
use super::sysbus::SysBus;
|
||||
|
|
|
@ -52,9 +52,6 @@ pub use bus::*;
|
|||
mod mgba_debug;
|
||||
pub(crate) mod overrides;
|
||||
|
||||
#[cfg(feature = "gdb")]
|
||||
pub mod gdb;
|
||||
|
||||
#[cfg(feature = "debugger")]
|
||||
pub mod debugger;
|
||||
|
||||
|
|
|
@ -20,5 +20,6 @@ spin_sleep = "0.3.7"
|
|||
winres = "0.1"
|
||||
|
||||
[features]
|
||||
default = ["gdb"]
|
||||
debugger = ["rustboyadvance-core/debugger"]
|
||||
gdb = ["rustboyadvance-core/gdb"]
|
|
@ -41,14 +41,11 @@ use input::create_input;
|
|||
use video::{create_video_interface, SCREEN_HEIGHT, SCREEN_WIDTH};
|
||||
|
||||
use rustboyadvance_core::cartridge::BackupType;
|
||||
#[cfg(feature = "gdb")]
|
||||
use rustboyadvance_core::gdb::spawn_and_run_gdb_server;
|
||||
use rustboyadvance_core::prelude::*;
|
||||
|
||||
use rustboyadvance_utils::FpsCounter;
|
||||
|
||||
const LOG_DIR: &str = ".logs";
|
||||
#[cfg(feature = "gdb")]
|
||||
const DEFAULT_GDB_SERVER_ADDR: &'static str = "localhost:1337";
|
||||
|
||||
const CANVAS_WIDTH: u32 = SCREEN_WIDTH;
|
||||
|
@ -240,13 +237,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gdb")]
|
||||
if with_gdbserver {
|
||||
spawn_and_run_gdb_server(&mut gba, DEFAULT_GDB_SERVER_ADDR)?;
|
||||
}
|
||||
#[cfg(not(feature = "gdb"))]
|
||||
if with_gdbserver {
|
||||
panic!("Please compile me with 'gdb' feature")
|
||||
todo!("gdb")
|
||||
}
|
||||
|
||||
let mut fps_counter = FpsCounter::default();
|
||||
|
@ -277,7 +269,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
info!("ending debugger...")
|
||||
}
|
||||
#[cfg(feature = "gdb")]
|
||||
Scancode::F2 => spawn_and_run_gdb_server(&mut gba, DEFAULT_GDB_SERVER_ADDR)?,
|
||||
Scancode::F2 => todo!("gdb"),
|
||||
Scancode::F5 => {
|
||||
info!("Saving state ...");
|
||||
let save = gba.save_state()?;
|
||||
|
|
|
@ -7,6 +7,9 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
ringbuf = "0.2.2"
|
||||
log = "0.4.8"
|
||||
goblin = "0.2"
|
||||
|
||||
|
||||
[target.'cfg(target_arch="wasm32")'.dependencies]
|
||||
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::time;
|
||||
|
||||
pub mod elf;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
type Instant = time::Instant;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
|
|
Reference in a new issue