Bweh/boy-debug/src/lib.rs

1128 lines
40 KiB
Rust

//! Utilities for presenting a common debug interface
pub mod bus;
pub mod disassembler;
pub mod cli {
//! Command line interface for boy-debug
pub mod token {
//! Tokens for the [Lexer](super::lexer::Lexer) to emit
pub use boy::cpu::disasm::{R16, R8};
use std::fmt::Display;
/// A unit of lexical information
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Token {
Op(Op),
Num(usize),
Bool(bool),
Reg(Reg),
Ident(String),
Other(char),
}
impl Display for Token {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Token::Op(op) => write!(f, "{op}"),
Token::Num(num) => num.fmt(f),
Token::Bool(b) => b.fmt(f),
Token::Reg(reg) => write!(f, "{reg}"),
Token::Ident(s) => s.fmt(f),
Token::Other(c) => write!(f, "'{c}' (Invalid)"),
}
}
}
/// An arithmetic or bitwise operator
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Op {
/// `*`: Multiplication
Mul,
/// `/`: Division
Div,
/// `%`: Remainder
Rem,
/// `+`: Addition
Add,
/// `-`: Subtraction or Negation
Sub,
/// `>>`: Shift Right
Shr,
/// `<<`: Shift Left
Shl,
/// `&`: Bitwise And
And,
/// `|`: Bitwise Or
Or,
/// `^`: Bitwise Xor
Xor,
/// `!`: Bitwise Not
Bang,
/// `..`: Range
Range,
/// `(`: Open parenthesis
ParenOpen,
/// `)`: Close parenthesis
ParenClose,
/// `[`: Open brackets
BrackOpen,
/// `]`: Close brackets
BrackClose,
/// ',': Comma
Comma,
/// `=`: Equals Sign
Eq,
}
impl Display for Op {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Op::Mul => "*",
Op::Div => "/",
Op::Rem => "%",
Op::Add => "+",
Op::Sub => "-",
Op::Shr => ">>",
Op::Shl => "<<",
Op::And => "&",
Op::Or => "|",
Op::Xor => "^",
Op::Bang => "!",
Op::Range => "..",
Op::ParenOpen => "(",
Op::ParenClose => ")",
Op::BrackOpen => "[",
Op::BrackClose => "]",
Op::Comma => ",",
Op::Eq => "=",
}
.fmt(f)
}
}
/// Describes an abstract CPU register
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Reg {
A,
F,
B,
C,
D,
E,
H,
L,
AF,
BC,
DE,
HL,
SP,
PC,
}
impl Display for Reg {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Reg::A => write!(f, "a"),
Reg::F => write!(f, "f"),
Reg::B => write!(f, "b"),
Reg::C => write!(f, "c"),
Reg::D => write!(f, "d"),
Reg::E => write!(f, "e"),
Reg::H => write!(f, "h"),
Reg::L => write!(f, "l"),
Reg::AF => write!(f, "af"),
Reg::BC => write!(f, "bc"),
Reg::DE => write!(f, "de"),
Reg::HL => write!(f, "hl"),
Reg::SP => write!(f, "sp"),
Reg::PC => write!(f, "pc"),
}
}
}
}
pub mod lexer {
//! A [Lexer] lexically analyzes a stream of utf-8 characters, producing a stream of tokens
use super::token::{
Op::*,
Reg,
Token::{self, *},
};
use std::iter::Peekable;
pub trait Lexible: Iterator<Item = char> {
/// Constructs a [`Lexer`] from Self
fn lex(self) -> Lexer<Self>
where
Self: Sized;
}
impl<T: Iterator<Item = char>> Lexible for T {
fn lex(self) -> Lexer<Self> {
Lexer {
text: self.peekable(),
}
}
}
#[derive(Clone, Debug)]
pub struct Lexer<I: Iterator<Item = char>> {
text: Peekable<I>,
}
impl<I: Iterator<Item = char>> Lexer<I> {
fn sync(&mut self) -> &mut Self {
while self.peek().map(|c| c.is_whitespace()).unwrap_or(false) {
self.take();
}
self
}
fn peek(&mut self) -> Option<&char> {
self.text.peek()
}
fn take(&mut self) -> &mut Self {
self.text.next();
self
}
fn emit(&mut self, kind: Token) -> Option<Token> {
Some(kind)
}
fn digits<const B: u32>(&mut self) -> Option<Token> {
let mut out = 0;
while let Some(Some(next)) = self.peek().map(|c| c.to_digit(B)) {
self.take();
out = (out * B as usize) + next as usize;
}
Some(Num(out))
}
fn base(&mut self) -> Option<Token> {
match self.peek() {
Some('b') => self.take().digits::<2>(),
Some('d') => self.take().digits::<10>(),
Some('o') => self.take().digits::<8>(),
Some('x') => self.take().digits::<16>(),
_ => self.digits::<16>(),
}
}
fn ident(&mut self) -> Option<Token> {
let mut out = String::new();
while let Some(&c) = self.peek().filter(|&&c| c.is_alphanumeric() || c == '_') {
self.take();
out.push(c)
}
// Match keyword
Some(match out.as_str() {
"false" | "off" => Token::Bool(false),
"true" | "on" => Token::Bool(true),
"rA" | "a" => Token::Reg(Reg::A),
"rF" | "f" => Token::Reg(Reg::F),
"rB" | "b" => Token::Reg(Reg::B),
"rC" | "c" => Token::Reg(Reg::C),
"rD" | "d" => Token::Reg(Reg::D),
"rE" | "e" => Token::Reg(Reg::E),
"rH" | "h" => Token::Reg(Reg::H),
"rL" | "l" => Token::Reg(Reg::L),
"rAF" | "af" => Token::Reg(Reg::AF),
"rBC" | "bc" => Token::Reg(Reg::BC),
"rDE" | "de" => Token::Reg(Reg::DE),
"rHL" | "hl" => Token::Reg(Reg::HL),
"rSP" | "sp" => Token::Reg(Reg::SP),
"rPC" | "pc" => Token::Reg(Reg::PC),
_ => match usize::from_str_radix(&out, 16) {
Ok(n) => Token::Num(n),
Err(_) => Token::Ident(out),
},
})
}
/// Checks the following character, producing the provided [Token]
/// if it matches the expected [char]. Otherwise, prodices [Token::Other].
fn chain(&mut self, expect: char, becomes: Token) -> Option<Token> {
match self.peek() {
Some(c) if *c == expect => self.take().emit(becomes),
_ => self.emit(Token::Other(expect)),
}
}
fn range(&mut self) -> Option<Token> {
match self.peek() {
Some('.') => self.take(),
_ => self,
}
.emit(Token::Op(Range))
}
}
impl<I: Iterator<Item = char>> Iterator for Lexer<I> {
type Item = Token;
fn next(&mut self) -> Option<Self::Item> {
match self.sync().peek()? {
// '{' => self.take().emit(Op(CurlyOpen)),
'[' => self.take().emit(Op(BrackOpen)),
'(' => self.take().emit(Op(ParenOpen)),
// '}' => self.take().emit(Op(CurlyClose)),
']' => self.take().emit(Op(BrackClose)),
')' => self.take().emit(Op(ParenClose)),
'*' => self.take().emit(Op(Mul)),
'/' => self.take().emit(Op(Div)),
'%' => self.take().emit(Op(Rem)),
'+' => self.take().emit(Op(Add)),
'-' => self.take().emit(Op(Sub)),
'&' => self.take().emit(Op(And)),
'|' => self.take().emit(Op(Or)),
'^' => self.take().emit(Op(Xor)),
',' => self.take().emit(Op(Comma)),
'=' => self.take().emit(Op(Eq)),
'>' => self.take().chain('>', Op(Shr)),
'<' => self.take().chain('<', Op(Shl)),
'.' => self.take().range(), // special handling allows for one or two dots
'?' => self.take().emit(Token::Ident("regs".into())),
'$' | '#' => self.take().digits::<16>(),
'0' => self.take().base(),
'1'..='9' | 'A'..='F' => self.digits::<16>(),
c if c.is_alphabetic() => self.ident(),
&other => self.take().emit(Other(other)),
}
}
}
}
pub mod parser {
//! A [Parser] parses a sequence of Tokens and produces a sequence of [Request]
use super::token::Token;
use crate::ast::*;
use crate::message::Request;
use std::iter::Peekable;
pub trait Parsible: Iterator<Item = Token> + Sized {
fn parse(self) -> Parser<Self>;
}
impl<T: Iterator<Item = Token> + Sized> Parsible for T {
fn parse(self) -> Parser<Self> {
Parser::new(self)
}
}
pub struct Parser<T: Iterator<Item = Token>> {
lexer: Peekable<T>,
}
impl<T: Iterator<Item = Token>> Parser<T> {
pub fn new(t: T) -> Self {
Self {
lexer: t.peekable(),
}
}
#[must_use]
fn peek(&mut self) -> Option<&Token> {
self.lexer.peek()
}
fn take(&mut self) -> Option<Token> {
self.lexer.next()
}
#[must_use]
fn then(&mut self, tok: Token) -> Option<&mut Self> {
(*self.peek()? == tok).then(|| {
self.take();
self
})
}
}
impl<T: Iterator<Item = Token>> Iterator for Parser<T> {
type Item = Request;
fn next(&mut self) -> Option<Self::Item> {
// match self.peek()? {
// Token::Op(_) => todo!(),
// Token::Num(_) => todo!(),
// Token::Bool(_) => todo!(),
// Token::Reg(_) => todo!(),
// Token::Ident(_) => todo!(),
// Token::Other(_) => todo!(),
// }
Some(Request::Do(Stmt::parse(self)?))
}
}
impl Stmt {
pub fn parse<I: Iterator<Item = Token>>(lexer: &mut Parser<I>) -> Option<Stmt> {
let verb = Verb::parse(lexer)?;
let [dst, src] = match verb {
Verb::Set => [
Expr::parse(lexer, 0)?,
Expr::parse(lexer.then(Token::Op(Op::Comma))?, 0)?,
],
Verb::Get
| Verb::Run
| Verb::Break
| Verb::Unbreak
| Verb::Eval
| Verb::Disasm => [Expr::parse(lexer, 0)?, Expr::default()],
Verb::DumpRegs | Verb::DumpCart | Verb::Trace | Verb::Reset | Verb::Help => {
[Expr::default(), Expr::default()]
}
};
Some(Stmt { verb, dst, src })
}
}
impl Verb {
pub fn parse<I: Iterator<Item = Token>>(lexer: &mut Parser<I>) -> Option<Verb> {
let Some(Token::Ident(id)) = (match lexer.peek()? {
Token::Ident(_) => lexer.take(),
Token::Op(Op::Eq) => {
lexer.take();
return Some(Verb::Eval);
}
_ => return Some(Verb::Get),
}) else {
unreachable!()
};
Some(match id.as_str() {
"set" | "write" => Verb::Set,
"get" | "read" | "dump" => Verb::Get,
"run" | "step" => Verb::Run,
"br" | "break" => Verb::Break,
"ub" | "unbreak" => Verb::Unbreak,
"di" | "dis" => Verb::Disasm,
"eval" => Verb::Eval,
"regs" => Verb::DumpRegs,
"cart" => Verb::DumpCart,
"trace" => Verb::Trace,
"reset" => Verb::Reset,
"help" => Verb::Help,
_ => None?,
})
}
}
impl Expr {
pub fn parse<I: Iterator<Item = Token>>(p: &mut Parser<I>, level: u8) -> Option<Self> {
let mut head = match p.take()? {
Token::Reg(r) => Expr::Reg(r),
Token::Num(n) => Expr::Int(n as _),
Token::Bool(b) => Expr::Bool(b),
Token::Op(Op::ParenOpen) => {
let head = Expr::parse(p, 0)?;
if p.then(Token::Op(Op::ParenClose)).is_none() {
println!("Unterminated parentheses!")
}
head
}
Token::Op(Op::BrackOpen) => {
let mut exprs = vec![];
while p.then(Token::Op(Op::BrackClose)).is_none() {
exprs.push(Expr::parse(p, 0)?);
if p.then(Token::Op(Op::Comma)).is_none() {
break;
}
}
if p.then(Token::Op(Op::BrackClose)).is_none() {
println!("Unterminated brackets in Array!");
return None;
};
Expr::Array(exprs)
}
Token::Op(Op::Mul) => {
let after = Op::Mul.prefix()?.after();
Expr::Index(Box::new(Expr::parse(p, after)?), Box::new(Expr::Int(0)))
}
Token::Op(op) => {
let after = op.prefix()?.after();
Expr::Prefix(op, Box::new(Expr::parse(p, after)?))
}
tok => {
println!("Unexpected token: {tok}");
None?
}
};
while let Some(&Token::Op(op)) = p.peek() {
if let Some(prec) = op.postfix() {
if prec.before() < level {
break;
}
p.take();
head = match op {
Op::BrackOpen => {
let tail = Expr::parse(p, 0)?;
if p.then(Token::Op(Op::BrackClose)).is_none() {
println!("Unterminated brackets in Index!");
return None;
};
Expr::Index(Box::new(head), Box::new(tail))
}
op => Expr::Postfix(op, Box::new(head)),
};
continue;
}
if let Some(prec) = op.infix() {
if prec.before() < level {
break;
}
p.take();
let tail = Expr::parse(p, prec.after())?;
head = match op {
Op::Range => Expr::Range(head.into(), tail.into()),
_ => Expr::Binary(op, Box::new(head), Box::new(tail)),
};
continue;
}
break;
}
Some(head)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Level {
Postfix,
Range,
Array,
Bit,
Factor,
Shift,
Term,
Sign,
}
impl Level {
fn level(self) -> u8 {
(self as u8) << 1
}
pub fn before(self) -> u8 {
self.level()
}
pub fn after(self) -> u8 {
self.level() + 1
}
}
pub trait Precedence {
fn prefix(&self) -> Option<Level>;
fn infix(&self) -> Option<Level>;
fn postfix(&self) -> Option<Level>;
}
impl Precedence for Op {
fn prefix(&self) -> Option<Level> {
match self {
Op::Mul | Op::Add | Op::Sub | Op::Bang => Some(Level::Sign),
Op::Range => Some(Level::Range),
Op::BrackOpen => Some(Level::Array),
_ => None,
}
}
fn infix(&self) -> Option<Level> {
match self {
Op::Mul | Op::Div | Op::Rem => Some(Level::Term),
Op::Add | Op::Sub => Some(Level::Factor),
Op::Shr | Op::Shl => Some(Level::Shift),
Op::And | Op::Or | Op::Xor => Some(Level::Bit),
Op::Bang => None,
Op::Range => Some(Level::Range),
_ => None,
}
}
fn postfix(&self) -> Option<Level> {
match self {
Op::BrackOpen => Some(Level::Postfix),
_ => None,
}
}
}
}
}
pub mod ast {
pub use crate::cli::token::{Op, Reg};
use std::{fmt::Display, mem::take};
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Stmt {
pub verb: Verb,
pub dst: Expr,
pub src: Expr,
}
impl Stmt {
pub fn visit(&mut self, f: &impl Fn(&mut Expr)) {
self.dst.visit(f);
self.src.visit(f);
}
/// Solves the statement arguments using the provided solver
pub fn solve(&mut self, solver: &impl Fn(&mut Expr) -> Option<Expr>) -> &mut Self {
self.visit(&|expr| {
expr.solve(solver);
});
self
}
}
impl Display for Stmt {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { verb, dst, src } = self;
match verb {
Verb::Set => write!(f, "{verb} {dst}, {src}"),
Verb::DumpRegs | Verb::DumpCart | Verb::Trace | Verb::Reset | Verb::Help => {
write!(f, "{verb}")
}
_ => write!(f, "{verb} {dst}"),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Verb {
Set,
Get,
Run,
Break,
Unbreak,
Eval,
Disasm,
DumpRegs,
DumpCart,
Trace,
Reset,
Help,
}
impl Display for Verb {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Verb::Set => "set",
Verb::Get => "get",
Verb::Run => "run",
Verb::Break => "break",
Verb::Unbreak => "unbreak",
Verb::Eval => "eval",
Verb::Disasm => "dis",
Verb::DumpRegs => "regs",
Verb::DumpCart => "cart",
Verb::Trace => "trace",
Verb::Reset => "reset",
Verb::Help => "help",
}
.fmt(f)
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Expr {
Int(isize),
Bool(bool),
Reg(Reg),
Prefix(Op, Box<Expr>),
Binary(Op, Box<Expr>, Box<Expr>),
Postfix(Op, Box<Expr>),
Array(Vec<Expr>),
Index(Box<Expr>, Box<Expr>),
Range(Box<Expr>, Box<Expr>),
}
impl Expr {
/// Traverses the expression depth-first, calling function f() on every subexpression
pub fn visit(&mut self, f: &impl Fn(&mut Expr)) {
match self {
Expr::Int(_) | Expr::Bool(_) | Expr::Reg(_) => {}
Expr::Array(v) => v.iter_mut().for_each(|e| e.visit(f)),
Expr::Prefix(_, e) | Expr::Postfix(_, e) => e.visit(f),
Expr::Binary(_, a, b) | Expr::Range(a, b) | Expr::Index(a, b) => {
[a, b].into_iter().for_each(|e| e.visit(f))
}
}
f(self)
}
/// Replaces the expression with a solution produced by the solver.
pub fn solve(&mut self, solver: impl Fn(&mut Expr) -> Option<Expr>) -> &mut Self {
if let Some(value) = solver(self) {
*self = value;
}
self
}
/// Gets the value of an [integer expression](Expr::Int)
pub fn int(&self) -> Option<isize> {
match self {
&Expr::Int(i) => Some(i),
_ => None,
}
}
/// Gets the value of a [bool expression](Expr::Bool)
pub fn bool(&self) -> Option<bool> {
match self {
&Expr::Bool(b) => Some(b),
_ => None,
}
}
/// Gets the value of a [register expression](Expr::Reg)
pub fn reg(&self) -> Option<Reg> {
match self {
&Expr::Reg(r) => Some(r),
_ => None,
}
}
pub fn int_range(&self) -> Option<std::ops::Range<isize>> {
match self {
Expr::Range(start, end) => Some(start.int()?..end.int()?),
_ => None,
}
}
/// Attempts to solve a top-level expression using basic arithmetic rules
pub fn eval_one(&mut self) -> Option<Expr> {
match self {
Expr::Int(_) | Expr::Bool(_) | Expr::Reg(_) => None,
Expr::Array(_) | Expr::Range(_, _) => None,
Expr::Prefix(op, e) => e.eval_prefix(*op),
Expr::Binary(op, lhs, rhs) => lhs.eval_binary(*op, rhs),
Expr::Postfix(_, _) => todo!("Postfix expression: {self}"),
Expr::Index(a, idx) => a.eval_index(idx),
}
}
fn eval_prefix(&self, op: Op) -> Option<Expr> {
Some(match (op, self) {
(Op::Add, Expr::Int(v)) => Expr::Int(*v),
(Op::Sub, Expr::Int(v)) => Expr::Int(-v),
(Op::Bang, Expr::Int(v)) => Expr::Int(!v),
(Op::Bang, Expr::Bool(v)) => Expr::Bool(!v),
_ => return None,
})
}
fn eval_binary(&self, op: Op, rhs: &Self) -> Option<Expr> {
use Expr::*;
Some(match (op, self, rhs) {
(Op::Mul, Int(lhs), Int(rhs)) => Int(lhs * rhs),
(Op::Div, Int(lhs), Int(rhs)) => Int(lhs / rhs),
(Op::Rem, Int(lhs), Int(rhs)) => Int(lhs % rhs),
(Op::Add, Int(lhs), Int(rhs)) => Int(lhs + rhs),
(Op::Sub, Int(lhs), Int(rhs)) => Int(lhs - rhs),
(Op::Shr, Int(lhs), Int(rhs)) => Int(lhs >> rhs),
(Op::Shl, Int(lhs), Int(rhs)) => Int(lhs << rhs),
(Op::And, Int(lhs), Int(rhs)) => Int(lhs & rhs),
(Op::And, Bool(lhs), Bool(rhs)) => Bool(lhs & rhs),
(Op::Or, Int(lhs), Int(rhs)) => Int(lhs | rhs),
(Op::Or, Bool(lhs), Bool(rhs)) => Bool(lhs | rhs),
(Op::Xor, Int(lhs), Int(rhs)) => Int(lhs ^ rhs),
(Op::Xor, Bool(lhs), Bool(rhs)) => Bool(lhs ^ rhs),
_ => return None,
})
}
/// Evaluates an index expression: a[10]
fn eval_index(&mut self, idx: &Self) -> Option<Expr> {
match (self, idx) {
(Expr::Array(a), &Expr::Int(idx)) if a.len() > idx as _ => {
Some(take(a).swap_remove(idx as _))
}
_ => None,
}
}
}
impl Default for Expr {
fn default() -> Self {
Expr::Bool(false)
}
}
impl Display for Expr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Expr::Int(i) => write!(f, "{i:02x}"),
Expr::Bool(b) => b.fmt(f),
Expr::Reg(r) => r.fmt(f),
Expr::Prefix(op, e) => write!(f, "{op}{e}"),
Expr::Binary(op, a, b) => write!(f, "{a} {op} {b}"),
Expr::Postfix(op, e) => write!(f, "{e}{op}"),
Expr::Array(v) => {
write!(f, "[")?;
for (idx, e) in v.iter().enumerate() {
if idx != 0 {
f.write_str(", ")?;
}
e.fmt(f)?
}
write!(f, "]")
}
Expr::Range(a, b) => write!(f, "{a}..{b}"),
Expr::Index(a, i) => write!(f, "{a}[{i}]"),
}
}
}
pub enum SubExprIter {
Once(std::iter::Once<Expr>),
Many(std::vec::IntoIter<Expr>),
}
impl Iterator for SubExprIter {
type Item = Expr;
fn next(&mut self) -> Option<Self::Item> {
match self {
SubExprIter::Once(iter) => iter.next(),
SubExprIter::Many(iter) => iter.next(),
}
}
}
impl IntoIterator for Expr {
type Item = Expr;
type IntoIter = SubExprIter;
fn into_iter(self) -> Self::IntoIter {
match self {
Expr::Int(_)
| Expr::Bool(_)
| Expr::Reg(_)
| Expr::Prefix(_, _)
| Expr::Binary(_, _, _)
| Expr::Postfix(_, _)
| Expr::Range(_, _)
| Expr::Index(_, _) => SubExprIter::Once(std::iter::once(self)),
Expr::Array(a) => SubExprIter::Many(a.into_iter()),
}
}
}
}
pub mod message {
//! Represents all possible debugger commands, and their return values
use crate::ast::Stmt;
use std::fmt::Display;
/// A request sent to the emulated gameboy
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Request {
Do(Stmt),
}
/// A response returned from the emulated Gameboy
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Response {
/// The request executed successfully
Success,
/// The request failed
Failure,
/// The data
Data(usize),
}
impl Display for Request {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Request::Do(stmt) => write!(f, "Execute `{stmt}`"),
}
}
}
}
pub mod gameboy {
use std::io::Write;
use boy::cpu::CPU;
use boy::memory::io::*;
use boy_utils::*;
use crate::{
ast::{Expr, Reg, Stmt, Verb},
bus::BusIOTools,
disassembler::Disassemble,
message::{Request, Response},
};
#[derive(Clone, Debug)]
pub struct Gameboy<T: BusIO> {
trace: bool,
cpu: CPU,
bus: T,
}
impl<T: BusIO> Gameboy<T> {
pub fn new(bus: T) -> Self {
Self {
trace: false,
cpu: CPU::new(),
bus,
}
}
pub fn process(&mut self, message: Request) -> Response {
match message {
Request::Do(stmt) => self.interpret(stmt),
}
}
fn hexdump(&self, start: isize, end: isize) -> Response {
let (start, end) = (start as usize, end as usize);
match bus_hexdump(&self.bus, start..end) {
Ok(_) => Response::Success,
Err(_) => Response::Failure,
}
}
fn disasm(&self, start: isize, end: isize, lim: usize) -> Response {
let (start, end) = (start as _, end as _);
self.bus
.read_iter_from(start)
.disassemble()
.take(lim)
.filter(|(idx, _)| start + idx <= end)
.for_each(|(idx, insn)| println!("{:04x}: {}", start + idx, insn));
Response::Success
}
pub fn interpret(&mut self, mut stmt: Stmt) -> Response {
// Solve statement args
if let Verb::Set = stmt.verb {
stmt.src.solve(|expr| self.substitute(expr));
} else {
stmt.solve(&|expr| self.substitute(expr));
}
// Intrepret basic statements
let Stmt { verb, dst, src } = stmt.solve(&Expr::eval_one);
match (verb, dst, src) {
(Verb::Set, Expr::Int(addr), Expr::Int(data)) => {
self.bus.write(*addr as _, *data as _);
}
(Verb::Set, Expr::Reg(reg), Expr::Int(data)) => {
set_register(&mut self.cpu, *reg, *data as _);
}
(Verb::Get, Expr::Int(addr), ..) => return self.hexdump(*addr, *addr + 0x10),
(Verb::Get, range @ Expr::Range(..), ..) => match range.int_range() {
Some(range) => return self.hexdump(range.start, range.end),
None => return Response::Failure,
},
(Verb::Run, Expr::Int(step), ..) => return self.step(*step as _),
(Verb::Break, Expr::Int(addr), ..) => {
if self.cpu.set_break(*addr as _) {
println!("Set breakpoint {addr:04x}")
}
}
(Verb::Unbreak, Expr::Int(addr), ..) => {
if self.cpu.unset_break(*addr as _) {
println!("Unset breakpoint {addr:04x}")
}
}
(Verb::Disasm, Expr::Int(addr), ..) => {
return self.disasm(*addr, 0x10000, 8);
}
(Verb::Disasm, range @ Expr::Range(..), ..) => match range.int_range() {
Some(range) => return self.disasm(range.start, range.end, usize::MAX),
None => return Response::Failure,
},
(Verb::Eval, expr, ..) => {
println!(" = {expr}");
return Response::Success;
}
(Verb::DumpRegs, ..) => println!("{}", self.cpu),
(Verb::DumpCart, ..) => bus_info(&self.bus),
(Verb::Trace, ..) => self.trace ^= true,
(Verb::Reset, ..) => self.cpu = CPU::new(),
(Verb::Help, ..) => todo!("Help"),
_ => {}
}
// Perform iteration and pairing
let Stmt { verb, dst, src } = stmt;
match (dst, src) {
// Array/range-to-array/range operations are done on zipped arguments
(Expr::Array(dst), Expr::Array(src)) => {
for (dst, src) in dst.into_iter().zip(src) {
self.interpret(Stmt { verb, dst, src });
}
}
(Expr::Array(dst), src @ Expr::Range(..)) => {
let Some(src) = src.int_range() else {
return Response::Failure;
};
for (dst, src) in dst.into_iter().zip(src) {
let src = Expr::Int(src);
self.interpret(Stmt { verb, dst, src });
}
}
(dst @ Expr::Range(..), Expr::Array(src)) => {
let Some(dst) = dst.int_range() else {
return Response::Failure;
};
for (dst, src) in dst.into_iter().zip(src) {
let dst = Expr::Int(dst);
self.interpret(Stmt { verb, dst, src });
}
}
(dst @ Expr::Range(..), src @ Expr::Range(..)) => {
let (Some(dst), Some(src)) = (dst.int_range(), src.int_range()) else {
return Response::Failure;
};
for (dst, src) in dst.into_iter().zip(src) {
let (dst, src) = (Expr::Int(dst), Expr::Int(src));
self.interpret(Stmt { verb, dst, src });
}
}
// Arrays get copied to an absolute address
(Expr::Int(addr), Expr::Array(src)) => {
for (idx, src) in src.into_iter().enumerate() {
let dst = Expr::Int(addr + idx as isize);
self.interpret(Stmt { verb, dst, src });
}
}
// Ranges get evaluated to a contiguous position
(Expr::Int(addr), src @ Expr::Range(..)) => {
let Some(src) = src.int_range() else {
return Response::Failure;
};
for (idx, src) in src.into_iter().enumerate() {
let (dst, src) = (Expr::Int(addr + idx as isize), Expr::Int(src));
self.interpret(Stmt { verb, dst, src });
}
}
// Arrays in dst position are iterated over
(Expr::Array(dst), src) => {
for dst in dst {
let src = src.clone();
self.interpret(Stmt { verb, dst, src });
}
}
// Ranges in dst position are iterated over
(dst @ Expr::Range(..), src) => {
let Some(dst) = dst.int_range() else {
return Response::Failure;
};
for dst in dst {
let (dst, src) = (Expr::Int(dst), src.clone());
self.interpret(Stmt { verb, dst, src });
}
}
_ => {}
}
Response::Success
}
fn substitute(&self, expr: &Expr) -> Option<Expr> {
Some(match expr {
Expr::Reg(r) => Expr::Int(get_register(&self.cpu, *r) as _),
Expr::Index(a, idx) => {
let (a, idx) = (a.int()?, idx.int()?);
Expr::Int(self.bus.read((a + idx) as _).unwrap_or(0xff) as _)
}
_ => None?,
})
}
fn step(&mut self, steps: usize) -> Response {
let Self { trace, cpu, bus } = self;
for step in 1..=steps {
let (loc, insn) = (cpu.pc(), cpu.ir());
match match trace {
true => cpu.run(&mut bus.trace()),
false => cpu.run(bus),
} {
Ok(cycles) => {
if insn == boy::cpu::disasm::Insn::Nop {
continue;
}
if steps < 100 {
print!(
"{loc:04x}: [{insn:?}]\x1b[23GTook {cycles} cycle{}\x1b[50G",
if cycles == 1 { "" } else { "s" }
);
}
if steps < 500 {
bus.read_iter_from(loc.wrapping_sub(1) as usize)
.disassemble()
.map(|(_, s)| println!("{:04x}: {s}", loc.wrapping_sub(1)))
.next();
let _ = std::io::stdout().flush();
}
}
Err(e) => {
println!(
"\n{loc:04x}: [{insn:?}]\x1b[23G{insn}\x1b[50G\x1b[31mStep {step:x}: {e}\x1b[0m"
);
return Response::Failure;
}
}
}
println!();
bus.diag(0);
Response::Success
}
}
fn get_register(cpu: &CPU, reg: Reg) -> usize {
use boy::cpu::disasm::{R16Stack, R16, R8};
match reg {
Reg::F => cpu.flags().into_bits() as _,
Reg::A => cpu[R8::A].0 as _,
Reg::B => cpu[R8::B].0 as _,
Reg::C => cpu[R8::C].0 as _,
Reg::D => cpu[R8::D].0 as _,
Reg::E => cpu[R8::E].0 as _,
Reg::H => cpu[R8::H].0 as _,
Reg::L => cpu[R8::L].0 as _,
Reg::AF => cpu[R16Stack::AF].0 as _,
Reg::BC => cpu[R16::BC].0 as _,
Reg::DE => cpu[R16::DE].0 as _,
Reg::HL => cpu[R16::HL].0 as _,
Reg::SP => cpu[R16::SP].0 as _,
Reg::PC => cpu.pc() as _,
}
}
fn set_register(cpu: &mut CPU, reg: Reg, value: u16) {
use boy::cpu::{
disasm::{R16Stack, R16, R8},
Flags,
};
match reg {
Reg::F => *cpu.flags_mut() = Flags::from_bits(value as _),
Reg::A => cpu[R8::A].0 = value as _,
Reg::B => cpu[R8::B].0 = value as _,
Reg::C => cpu[R8::C].0 = value as _,
Reg::D => cpu[R8::D].0 = value as _,
Reg::E => cpu[R8::E].0 = value as _,
Reg::H => cpu[R8::H].0 = value as _,
Reg::L => cpu[R8::L].0 = value as _,
Reg::AF => cpu[R16Stack::AF].0 = value,
Reg::BC => cpu[R16::BC].0 = value,
Reg::DE => cpu[R16::DE].0 = value,
Reg::HL => cpu[R16::HL].0 = value,
Reg::SP => cpu[R16::SP].0 = value,
Reg::PC => cpu.set_pc(value),
}
}
fn bus_info(bus: &impl BusIO) {
println!("Entry: {:02x?}", bus.entry());
println!("Logo: ");
let _ = match bus.logo() {
Ok(logo) | Err(logo) => bus_hexdump(&logo, 0..0x30),
};
println!("Title: {:02x?}", bus.title());
println!("MFCode: {:02x?}", bus.mfcode());
println!("CGB Flag: {:02x?}", bus.cgbflag());
println!("New Lic: {:02x?}", bus.newlicensee());
println!("SGB Flag: {:02x?}", bus.sgbflag());
println!("Cart Type: {:02x?}", bus.carttype());
println!("ROM Size: {:02x?}", bus.romsize());
println!("RAM Size: {:02x?}", bus.ramsize());
println!("Dest Code: {:02x?}", bus.destcode());
println!("Old Lic: {:02x?}", bus.oldlicencee());
println!("ROM Ver: {:02x?}", bus.maskromver());
println!("H Cksum: {:02x?}", bus.headercksum());
println!("G Cksum: {:04x?}", bus.globalcksum())
}
}