//! 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 { /// Constructs a [`Lexer`] from Self fn lex(self) -> Lexer where Self: Sized; } impl> Lexible for T { fn lex(self) -> Lexer { Lexer { text: self.peekable(), } } } #[derive(Clone, Debug)] pub struct Lexer> { text: Peekable, } impl> Lexer { 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 { Some(kind) } fn digits(&mut self) -> Option { 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 { 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 { 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 { match self.peek() { Some(c) if *c == expect => self.take().emit(becomes), _ => self.emit(Token::Other(expect)), } } fn range(&mut self) -> Option { match self.peek() { Some('.') => self.take(), _ => self, } .emit(Token::Op(Range)) } } impl> Iterator for Lexer { type Item = Token; fn next(&mut self) -> Option { 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 + Sized { fn parse(self) -> Parser; } impl + Sized> Parsible for T { fn parse(self) -> Parser { Parser::new(self) } } pub struct Parser> { lexer: Peekable, } impl> Parser { 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 { self.lexer.next() } #[must_use] fn then(&mut self, tok: Token) -> Option<&mut Self> { (*self.peek()? == tok).then(|| { self.take(); self }) } } impl> Iterator for Parser { type Item = Request; fn next(&mut self) -> Option { // 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>(lexer: &mut Parser) -> Option { 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>(lexer: &mut Parser) -> Option { 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>(p: &mut Parser, level: u8) -> Option { 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; fn infix(&self) -> Option; fn postfix(&self) -> Option; } impl Precedence for Op { fn prefix(&self) -> Option { 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 { 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 { 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) -> &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), Binary(Op, Box, Box), Postfix(Op, Box), Array(Vec), Index(Box, Box), Range(Box, Box), } 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) -> &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 { match self { &Expr::Int(i) => Some(i), _ => None, } } /// Gets the value of a [bool expression](Expr::Bool) pub fn bool(&self) -> Option { match self { &Expr::Bool(b) => Some(b), _ => None, } } /// Gets the value of a [register expression](Expr::Reg) pub fn reg(&self) -> Option { match self { &Expr::Reg(r) => Some(r), _ => None, } } pub fn int_range(&self) -> Option> { 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 { 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 { 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 { 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 { 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), Many(std::vec::IntoIter), } impl Iterator for SubExprIter { type Item = Expr; fn next(&mut self) -> Option { 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 { trace: bool, cpu: CPU, bus: T, } impl Gameboy { 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 { 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()) } }