Compare commits

...
4 Commits
7 changed files with 61 additions and 14 deletions
+1 -1
View File
@@ -5,7 +5,7 @@ A Virtual Boy emulator built around the shrooms-vb core. Written in Rust, using
## Setup
Install the following dependencies:
- `cargo`
- `cargo` (via [rustup](https://rustup.rs/), the version from your package manager is too old)
Run
```sh
+4 -2
View File
@@ -23,13 +23,15 @@ ENV PATH="/osxcross/bin:$PATH" \
CXX_x86_64-apple-darwin="o64-clang++" \
CC_aarch64-apple-darwin="oa64-clang" \
CXX_aarch64-apple-darwin="o6a4-clang++" \
SHROOMS_CFLAGS_x86_64-unknown-linux-gnu="-flto" \
CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS="-Clinker-plugin-lto -Clinker=clang -Clink-arg=-fuse-ld=lld" \
SHROOMS_CFLAGS_x86_64-pc-windows-msvc="-flto" \
CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUSTFLAGS="-Lnative=/xwin/crt/lib/x86_64 -Lnative=/xwin/sdk/lib/um/x86_64 -Lnative=/xwin/sdk/lib/ucrt/x86_64 -Clinker-plugin-lto -Clink-arg=-fuse-ld=lld" \
CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER="lld-link-19" \
CARGO_TARGET_X86_64_APPLE_DARWIN_LINKER="o64-clang" \
CARGO_TARGET_X86_64_APPLE_DARWIN_AR="llvm-ar-19" \
CARGO_TARGET_AARCH64_APPLE_DARWIN_LINKER="oa64-clang" \
CARGO_TARGET_AARCH64_APPLE_DARWIN_AR="llvm-ar-19" \
CROSS_COMPILE="setting-this-to-silence-a-warning-" \
SHROOMS_CFLAGS="-flto" \
RC_PATH="llvm-rc-19" \
RUSTFLAGS="-Lnative=/xwin/crt/lib/x86_64 -Lnative=/xwin/sdk/lib/um/x86_64 -Lnative=/xwin/sdk/lib/ucrt/x86_64" \
MACOSX_DEPLOYMENT_TARGET="14.5"
+1
View File
@@ -24,6 +24,7 @@ fn main() -> Result<(), Box<dyn Error>> {
.define("VB_SIGNED_PROPAGATE", None)
.define("VB_DIV_GENERIC", None)
.define("VB_DIRECT_EXECUTE", "on_execute")
.define("VB_DIRECT_FRAME", "on_frame")
.define("VB_DIRECT_READ", "on_read")
.define("VB_DIRECT_WRITE", "on_write")
.file(Path::new("shrooms-vb-core/core/vb.c"))
+3 -3
View File
@@ -311,7 +311,7 @@ impl Emulator {
}
let debug = DebugInfo {
sender,
stop_reason: Some(DebugStopReason::Trapped),
stop_reason: Some(DebugStopReason::Paused),
};
self.debuggers.insert(sim_id, debug);
self.state
@@ -334,7 +334,7 @@ impl Emulator {
}
fn debug_interrupt(&mut self, sim_id: SimId) {
self.debug_stop(sim_id, DebugStopReason::Trapped);
self.debug_stop(sim_id, DebugStopReason::Paused);
}
fn debug_stop(&mut self, sim_id: SimId, reason: DebugStopReason) {
@@ -686,7 +686,7 @@ pub enum DebugStopReason {
// We hit a watchpoint
Watchpoint(VBWatchpointType, u32),
// The debugger told us to pause
Trapped,
Paused,
}
struct DebugInfo {
+41 -2
View File
@@ -1,11 +1,11 @@
use std::{
collections::{BTreeMap, HashSet},
collections::{BTreeMap, BTreeSet},
ops::Bound,
};
#[derive(Debug, Default)]
pub struct AddressSet {
ranges: HashSet<(u32, usize)>,
ranges: BTreeSet<(u32, usize)>,
bounds: BTreeMap<u32, usize>,
}
@@ -109,6 +109,22 @@ impl AddressSet {
.next_back()
.is_some_and(|(_, &val)| val > 0)
}
pub fn start_of_range_containing(&self, address: u32) -> Option<u32> {
if !self.contains(address) {
return None;
}
self.ranges
.range(..=(address, usize::MAX))
.rev()
.find_map(|&(start, length)| {
let contains = start <= address
&& (start as usize)
.checked_add(length)
.is_none_or(|end| end > address as usize);
contains.then_some(start)
})
}
}
#[cfg(test)]
@@ -120,6 +136,7 @@ mod tests {
let set = AddressSet::new();
assert!(set.is_empty());
assert!(!set.contains(0x13374200));
assert_eq!(set.start_of_range_containing(0x13374200), None);
}
#[test]
@@ -127,6 +144,7 @@ mod tests {
let mut set = AddressSet::new();
set.add(0x00000000, 0x100000000);
assert!(set.contains(0x13374200));
assert_eq!(set.start_of_range_containing(0x13374200), Some(0x00000000));
}
#[test]
@@ -228,4 +246,25 @@ mod tests {
assert!(!set.contains(address));
}
}
#[test]
fn should_find_start_of_range() {
let mut set = AddressSet::new();
set.add(0x13374200, 4);
assert_eq!(set.start_of_range_containing(0x133741ff), None);
for address in 0x13374200..0x13374204 {
assert_eq!(set.start_of_range_containing(address), Some(0x13374200));
}
assert_eq!(set.start_of_range_containing(0x13374204), None);
}
#[test]
fn should_ignore_ranges_not_containing_address() {
let mut set = AddressSet::new();
set.add(0x10000000, 1024);
set.add(0x30000000, 1024);
assert!(!set.contains(0x20000000));
assert_eq!(set.start_of_range_containing(0x20000000), None);
}
}
+5 -4
View File
@@ -170,6 +170,7 @@ extern "C" {
fn vb_write(sim: *mut VB, address: u32, _type: VBDataType, value: i32) -> i32;
}
#[no_mangle]
extern "C" fn on_frame(sim: *mut VB) -> c_int {
// SAFETY: the *mut VB owns its userdata.
// There is no way for the userdata to be null or otherwise invalid.
@@ -214,13 +215,13 @@ extern "C" fn on_read(
// There is no way for the userdata to be null or otherwise invalid.
let data: &mut VBState = unsafe { &mut *vb_get_user_data(sim).cast() };
if data.read_watchpoints.contains(address) {
if let Some(start) = data.read_watchpoints.start_of_range_containing(address) {
let watch = if data.write_watchpoints.contains(address) {
VBWatchpointType::Access
} else {
VBWatchpointType::Read
};
data.stop_reason = Some(StopReason::Watchpoint(watch, address));
data.stop_reason = Some(StopReason::Watchpoint(watch, start));
}
// Don't stop here, the debugger expects us to break after the memory access.
@@ -241,13 +242,13 @@ extern "C" fn on_write(
// There is no way for the userdata to be null or otherwise invalid.
let data: &mut VBState = unsafe { &mut *vb_get_user_data(sim).cast() };
if data.write_watchpoints.contains(address) {
if let Some(start) = data.write_watchpoints.start_of_range_containing(address) {
let watch = if data.read_watchpoints.contains(address) {
VBWatchpointType::Access
} else {
VBWatchpointType::Write
};
data.stop_reason = Some(StopReason::Watchpoint(watch, address));
data.stop_reason = Some(StopReason::Watchpoint(watch, start));
}
// Don't stop here, the debugger expects us to break after the memory access.
+6 -2
View File
@@ -521,7 +521,11 @@ fn parse_memory_range(req: &mut Request<'_>) -> Option<(u32, usize)> {
fn debug_stop_reason_string(reason: Option<DebugStopReason>) -> String {
let mut result = String::new();
result += if reason.is_some() { "T05;" } else { "T00;" };
result += if reason.is_some_and(|r| r != DebugStopReason::Paused) {
"T05;"
} else {
"T00;"
};
if let Some(DebugStopReason::Breakpoint) = reason {
result += "swbreak;";
}
@@ -542,7 +546,7 @@ fn debug_stop_reason_string(reason: Option<DebugStopReason>) -> String {
DebugStopReason::Trace => "trace;",
DebugStopReason::Breakpoint => "breakpoint;",
DebugStopReason::Watchpoint(_, _) => "watchpoint;",
DebugStopReason::Trapped => "trap;",
DebugStopReason::Paused => "trap;",
};
}