use super::super::Core;
use super::Debugger;
use super::history::Input;
use super::packet::{PACKET_SIZE, from_hex, hex, to_hex};
use crate::emulator::memory::{from_target, to_target};
use crate::emulator::{Access, AccessKind, BitAlias};
use crate::soc::Width;
pub(super) fn span(text: &str) -> Option<(u64, u64)> {
let (address, length) = text.split_once(',')?;
let (address, length) = (hex(address)?, hex(length)?);
address.checked_add(length)?;
Some((address, length))
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(super) enum Reads {
Peeks,
Firmware,
}
pub(super) fn read_memory(core: &mut Core, rest: &str, reads: Reads) -> Option<String> {
let (address, length) = span(rest)?;
let length = length.min(PACKET_SIZE as u64 / 2);
let bytes = core.debugger_read(address, length, reads);
(!bytes.is_empty()).then(|| to_hex(&bytes))
}
pub(super) fn hex_write(rest: &str) -> Option<(u64, Vec<u8>)> {
let (span_text, data) = rest.split_once(':')?;
let (address, length) = span(span_text)?;
let bytes = from_hex(data)?;
(bytes.len() as u64 == length).then_some((address, bytes))
}
pub(super) fn binary_write(rest: &str) -> Option<(u64, Vec<u8>)> {
let (span_text, data) = rest.split_once(':')?;
let (address, length) = span(span_text)?;
let bytes = binary(data)?;
(bytes.len() as u64 == length).then_some((address, bytes))
}
fn binary(data: &str) -> Option<Vec<u8>> {
let mut bytes = Vec::with_capacity(data.len());
let mut escaped = false;
for character in data.chars() {
let byte = u8::try_from(u32::from(character)).ok()?;
if escaped {
bytes.push(byte ^ 0x20);
escaped = false;
} else if byte == b'}' {
escaped = true;
} else {
bytes.push(byte);
}
}
Some(bytes)
}
impl Debugger<'_> {
pub(super) fn flash(&mut self, core: &mut Core, rest: &str) -> &'static str {
if let Some(erased) = rest.strip_prefix("FlashErase:") {
self.flash_erase(core, erased)
} else if let Some(written) = rest.strip_prefix("FlashWrite:") {
self.flash_write(core, written)
} else if rest == "FlashDone" {
"OK"
} else {
""
}
}
fn flash_erase(&mut self, core: &mut Core, rest: &str) -> &'static str {
let Some((address, erased)) = span(rest).and_then(|(address, length)| {
let value = self.map.erases(address, length)?;
let length = usize::try_from(length).expect("a region in memory");
Some((address, vec![value; length]))
}) else {
return "E01";
};
let flash = Input::Flash {
address,
bytes: erased.clone(),
};
self.note(core, flash, |core| core.debugger_flash(address, &erased));
"OK"
}
fn flash_write(&mut self, core: &mut Core, rest: &str) -> &'static str {
let written = rest.split_once(':').and_then(|(address, data)| {
let address = hex(address)?;
let bytes = binary(data)?;
address.checked_add(bytes.len() as u64)?;
Some((address, bytes))
});
match written {
Some((address, bytes)) if self.map.holds(address, bytes.len() as u64) => {
let flash = Input::Flash {
address,
bytes: bytes.clone(),
};
self.note(core, flash, |core| core.debugger_flash(address, &bytes));
"OK"
}
Some(_) => "E.memtype",
None => "E01",
}
}
}
fn widest(address: u64, left: u64, pointer: Width) -> Width {
[Width::W64, Width::W32, Width::W16]
.into_iter()
.filter(|width| width.bytes() <= pointer.bytes())
.find(|width| {
let bytes = u64::from(width.bytes());
address.is_multiple_of(bytes) && left >= bytes
})
.unwrap_or(Width::W8)
}
pub(super) fn peek(core: &mut Core, address: u64, width: Width) -> Option<u64> {
let bytes = core.debugger_read(address, u64::from(width.bytes()), Reads::Peeks);
(bytes.len() as u64 == u64::from(width.bytes()))
.then(|| from_target(&bytes, core.machine.cpu().byte_order(address)))
}
impl Core {
pub(super) fn debugger_read(&mut self, address: u64, length: u64, reads: Reads) -> Vec<u8> {
let mut bytes = Vec::new();
let Some(end) = address.checked_add(length) else {
return bytes;
};
let mut at = address;
while at < end {
let width = if self.machine.mapped_byte(at) {
Width::W8
} else {
widest(at, end - at, self.machine.cpu().data_model().pointer())
};
let Some(value) =
self.debugger_serve(self.debugger_access(AccessKind::Read, at, width, 0), reads)
else {
break;
};
let count = usize::try_from(width.bytes()).expect("a width fits");
let order = self.machine.cpu().byte_order(at);
bytes.extend_from_slice(&to_target(value, count, order)[..count]);
at += u64::from(width.bytes());
}
bytes
}
pub(super) fn debugger_write(&mut self, address: u64, bytes: &[u8]) -> bool {
let mut at = 0;
while at < bytes.len() {
let target = address + at as u64;
if self.machine.mapped_byte(target) {
self.machine.read_only_written |= self.machine.memory.read_only(target);
self.machine.memory.place(target, &bytes[at..=at]);
self.machine.invalidate(target, 1);
at += 1;
continue;
}
let pointer = self.machine.cpu().data_model().pointer();
let width = widest(target, (bytes.len() - at) as u64, pointer);
let count = usize::try_from(width.bytes()).expect("a width fits");
let value = from_target(
&bytes[at..at + count],
self.machine.cpu().byte_order(target),
);
if self
.debugger_serve(
self.debugger_access(AccessKind::Write, target, width, value),
Reads::Firmware,
)
.is_none()
{
return false;
}
at += count;
}
true
}
pub(super) fn debugger_flash(&mut self, address: u64, bytes: &[u8]) {
let placed = self.machine.memory.place(address, bytes);
debug_assert!(placed, "flash in the memory map is memory the core maps");
self.machine.read_only_written |= self.machine.memory.read_only(address);
self.machine.invalidate(address, bytes.len() as u64);
}
fn debugger_serve(&mut self, access: Access, reads: Reads) -> Option<u64> {
let write = (access.kind == AccessKind::Write).then_some(access.value);
let machine = &mut self.machine;
if let Some(answer) =
machine
.cpu()
.debugger_access(write, access.address, access.width, access.virtual_time)
{
let traced = machine.cpu().take_debugger_trace();
if let Some(trace) = self.trace.as_mut().filter(|_| !traced.is_empty()) {
trace.send(&traced);
}
return Some(answer);
}
if let Some(answer) =
machine.mapped(access.kind, access.address, access.width, access.value)
{
return Some(answer);
}
if let Some(BitAlias { unit, bit }) = machine.cpu().bit_alias(access.address, access.width)
{
return self.debugger_bit(access, unit, bit, reads);
}
if access.kind == AccessKind::Read && reads == Reads::Peeks {
return self
.driven
.peek(access.virtual_time, access.address, access.width);
}
let (answer, next, _) = self.driven.access(access, machine);
machine.deadline = next;
(!self.driven.refused()).then_some(answer)
}
fn debugger_bit(&mut self, access: Access, unit: u64, bit: u32, reads: Reads) -> Option<u64> {
let (reads, write) = match access.kind {
AccessKind::Read => (reads, None),
AccessKind::Write => (Reads::Peeks, Some(access.value & 1)),
};
let read = Access {
kind: AccessKind::Read,
address: unit,
value: 0,
..access
};
let held = self.debugger_serve(read, reads)?;
let Some(value) = write else {
return Some(held >> bit & 1);
};
let written = Access {
address: unit,
value: held & !(1 << bit) | value << bit,
..access
};
self.debugger_serve(written, Reads::Firmware).map(|_| value)
}
fn debugger_access(&self, kind: AccessKind, address: u64, width: Width, value: u64) -> Access {
let retired = self.machine.retired();
Access {
kind,
address,
width,
value,
instruction_count: retired,
virtual_time: self.machine.time(retired),
program_counter: None,
}
}
}