use crate::address;
use crate::gameboy;
use crate::registers;
use alloc::string::String;
use core::convert::TryFrom;
use core::str::FromStr;
use derive_more::{Display, From};
pub fn parse_number(src: &str) -> Result<u16, core::num::ParseIntError> {
let lowered = src.to_lowercase();
if lowered.starts_with("0x") {
u16::from_str_radix(&src[2..], 16)
} else if lowered.ends_with('h') {
u16::from_str_radix(&src[..src.len() - 1], 16)
} else {
src.parse()
}
}
#[derive(Debug, From, Clone, Copy, Display, PartialEq, Eq)]
pub enum RWTarget {
#[display(fmt = "memory location {}", "_0")]
Address(address::LiteralAddress),
#[display(fmt = "register {:?}", "_0")]
ByteRegister(registers::ByteRegister),
#[display(fmt = "register {:?}", "_0")]
WordRegister(registers::WordRegister),
#[display(fmt = "cycles")]
Cycles,
#[display(fmt = "time")]
Time,
}
#[derive(Debug, From, Display)]
pub enum ReadError {
#[display(fmt = "Could not read from the address at {}", "_0")]
Memory(address::LiteralAddress),
}
#[cfg(feature = "std")]
impl std::error::Error for ReadError {}
#[derive(Debug, From, Display)]
pub enum WriteError {
#[display(fmt = "Could not write to the address at {}", "_0")]
Memory(address::LiteralAddress),
#[display(fmt = "The value {:X} is too large for the destination", "_0")]
ValueTooLarge(u16),
#[display(fmt = "Cannot update locations of this type")]
Immutable,
}
#[cfg(feature = "std")]
impl std::error::Error for WriteError {}
impl RWTarget {
pub fn read(self, gb: &gameboy::GameBoy) -> Result<u64, ReadError> {
match self {
RWTarget::Address(addr) => gb
.read_memory_u8(addr)
.map(u64::from)
.map_err(|_| addr.into()),
RWTarget::ByteRegister(reg) => Ok(u64::from(gb.read_register_u8(reg))),
RWTarget::WordRegister(reg) => Ok(u64::from(gb.read_register_u16(reg))),
RWTarget::Cycles => Ok(gb.cycles_elapsed()),
RWTarget::Time => Ok(gb.cycles_elapsed() / (1024 * 1024)),
}
}
pub fn write(self, gb: &mut gameboy::GameBoy, val: u16) -> Result<u64, WriteError> {
let current_value = self.read(gb);
match self {
RWTarget::Address(addr) => {
let value = u8::try_from(val).map_err(|_| WriteError::ValueTooLarge(val))?;
gb.write_memory_u8(addr, value)
.map_err(|_| WriteError::Memory(addr))?;
}
RWTarget::ByteRegister(reg) => {
let value = u8::try_from(val).map_err(|_| WriteError::ValueTooLarge(val))?;
gb.write_register_u8(reg, value);
}
RWTarget::WordRegister(reg) => gb.write_register_u16(reg, val),
RWTarget::Cycles | RWTarget::Time => return Err(WriteError::Immutable),
}
Ok(current_value.unwrap())
}
}
#[derive(Debug, Display)]
#[display(fmt = "{} is not a valid register or memory location", _0)]
pub struct TargetParseError(String);
impl FromStr for RWTarget {
type Err = TargetParseError;
fn from_str(s: &str) -> Result<RWTarget, TargetParseError> {
if s == "cycles" {
return Ok(RWTarget::Cycles);
} else if s == "time" {
return Ok(RWTarget::Time);
}
parse_number(s)
.map(|val| address::LiteralAddress(val).into())
.map_err(|_| ())
.or_else(|_| {
s.to_uppercase()
.parse::<registers::WordRegister>()
.map(|wr| wr.into())
.map_err(|_| ())
})
.or_else(|_| {
s.to_uppercase()
.parse::<registers::ByteRegister>()
.map(|br| br.into())
.map_err(|_| ())
})
.map_err(|_| TargetParseError(s.into()))
}
}
#[derive(Debug, Display, Clone, PartialEq, Eq)]
#[display(fmt = "Breakpoint: {} == {:X}", monitor, value)]
pub struct Breakpoint {
pub monitor: RWTarget,
pub value: u64,
}
impl Breakpoint {
pub fn new(monitor: RWTarget, value: u64) -> Breakpoint {
Breakpoint { monitor, value }
}
pub fn should_break(&self, gb: &gameboy::GameBoy) -> bool {
let read_result = self.monitor.read(gb);
read_result.is_ok() && read_result.unwrap() == self.value
}
}