Change small heuristics, add newlines between items
This commit is contained in:
parent
9106d9727f
commit
060a6b068b
@ -7,7 +7,9 @@ use boy::memory::io::BusIO;
|
|||||||
pub trait BusIOTools: BusIO {
|
pub trait BusIOTools: BusIO {
|
||||||
/// Prints all successful reads and writes
|
/// Prints all successful reads and writes
|
||||||
fn trace(&mut self) -> TracingBus<Self>;
|
fn trace(&mut self) -> TracingBus<Self>;
|
||||||
|
|
||||||
fn ascii(&mut self) -> AsciiSerial<Self>;
|
fn ascii(&mut self) -> AsciiSerial<Self>;
|
||||||
|
|
||||||
fn read_file(&mut self, path: impl AsRef<Path>) -> IoResult<&mut Self>
|
fn read_file(&mut self, path: impl AsRef<Path>) -> IoResult<&mut Self>
|
||||||
where
|
where
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
@ -17,6 +19,7 @@ impl<T: BusIO> BusIOTools for T {
|
|||||||
fn trace(&mut self) -> TracingBus<Self> {
|
fn trace(&mut self) -> TracingBus<Self> {
|
||||||
TracingBus { bus: self }
|
TracingBus { bus: self }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ascii(&mut self) -> AsciiSerial<Self> {
|
fn ascii(&mut self) -> AsciiSerial<Self> {
|
||||||
AsciiSerial {
|
AsciiSerial {
|
||||||
data: 0,
|
data: 0,
|
||||||
@ -24,6 +27,7 @@ impl<T: BusIO> BusIOTools for T {
|
|||||||
bus: self,
|
bus: self,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_file(&mut self, path: impl AsRef<Path>) -> IoResult<&mut Self> {
|
fn read_file(&mut self, path: impl AsRef<Path>) -> IoResult<&mut Self> {
|
||||||
let data = std::fs::read(path)?;
|
let data = std::fs::read(path)?;
|
||||||
eprintln!("Read {} bytes.", data.len());
|
eprintln!("Read {} bytes.", data.len());
|
||||||
@ -49,6 +53,7 @@ impl<'t, T: BusIO> BusIO for TracingBus<'t, T> {
|
|||||||
// }
|
// }
|
||||||
self.bus.read(addr)
|
self.bus.read(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write(&mut self, addr: usize, data: u8) -> Option<()> {
|
fn write(&mut self, addr: usize, data: u8) -> Option<()> {
|
||||||
eprintln!("set [{addr:04x}], {data:02x}");
|
eprintln!("set [{addr:04x}], {data:02x}");
|
||||||
self.bus.write(addr, data)
|
self.bus.write(addr, data)
|
||||||
@ -60,11 +65,13 @@ impl<'t, T: BusIO> From<&'t mut T> for TracingBus<'t, T> {
|
|||||||
Self { bus: value }
|
Self { bus: value }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'t, T: BusIO + AsRef<[u8]>> AsRef<[u8]> for TracingBus<'t, T> {
|
impl<'t, T: BusIO + AsRef<[u8]>> AsRef<[u8]> for TracingBus<'t, T> {
|
||||||
fn as_ref(&self) -> &[u8] {
|
fn as_ref(&self) -> &[u8] {
|
||||||
self.bus.as_ref()
|
self.bus.as_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'t, T: BusIO + AsMut<[u8]>> AsMut<[u8]> for TracingBus<'t, T> {
|
impl<'t, T: BusIO + AsMut<[u8]>> AsMut<[u8]> for TracingBus<'t, T> {
|
||||||
fn as_mut(&mut self) -> &mut [u8] {
|
fn as_mut(&mut self) -> &mut [u8] {
|
||||||
self.bus.as_mut()
|
self.bus.as_mut()
|
||||||
@ -93,6 +100,7 @@ impl<'t, T: BusIO + ?Sized> BusIO for AsciiSerial<'t, T> {
|
|||||||
_ => self.bus.read(addr),
|
_ => self.bus.read(addr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write(&mut self, addr: usize, data: u8) -> Option<()> {
|
fn write(&mut self, addr: usize, data: u8) -> Option<()> {
|
||||||
match addr {
|
match addr {
|
||||||
0xff01 => {
|
0xff01 => {
|
||||||
@ -111,6 +119,7 @@ impl<'t, T: BusIO + ?Sized> BusIO for AsciiSerial<'t, T> {
|
|||||||
}
|
}
|
||||||
self.bus.write(addr, data)
|
self.bus.write(addr, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn diag(&mut self, param: usize) {
|
fn diag(&mut self, param: usize) {
|
||||||
println!("debug: '{}'", self.string());
|
println!("debug: '{}'", self.string());
|
||||||
self.buf.clear();
|
self.buf.clear();
|
||||||
|
@ -6,6 +6,7 @@ use std::iter::{Enumerate, Peekable};
|
|||||||
pub trait Disassemble: Iterator<Item = u8> + Sized {
|
pub trait Disassemble: Iterator<Item = u8> + Sized {
|
||||||
fn disassemble(self) -> Disassembler<Self>;
|
fn disassemble(self) -> Disassembler<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Iterator<Item = u8> + Sized> Disassemble for T {
|
impl<T: Iterator<Item = u8> + Sized> Disassemble for T {
|
||||||
fn disassemble(self) -> Disassembler<Self> {
|
fn disassemble(self) -> Disassembler<Self> {
|
||||||
Disassembler::new(self)
|
Disassembler::new(self)
|
||||||
@ -29,27 +30,31 @@ impl<I: Iterator<Item = u8>> Iterator for Disassembler<I> {
|
|||||||
|
|
||||||
impl<I: Iterator<Item = u8>> Disassembler<I> {
|
impl<I: Iterator<Item = u8>> Disassembler<I> {
|
||||||
pub fn new(bytes: I) -> Self {
|
pub fn new(bytes: I) -> Self {
|
||||||
Disassembler {
|
Disassembler { bytes: bytes.enumerate().peekable() }
|
||||||
bytes: bytes.enumerate().peekable(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn index(&mut self) -> Option<usize> {
|
pub fn index(&mut self) -> Option<usize> {
|
||||||
self.bytes.peek().map(|v| v.0)
|
self.bytes.peek().map(|v| v.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn imm8(&mut self) -> Option<u8> {
|
pub fn imm8(&mut self) -> Option<u8> {
|
||||||
self.bytes.next().map(|v| v.1)
|
self.bytes.next().map(|v| v.1)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn smm8(&mut self) -> Option<i8> {
|
pub fn smm8(&mut self) -> Option<i8> {
|
||||||
self.imm8().map(|b| b as i8)
|
self.imm8().map(|b| b as i8)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn imm16(&mut self) -> Option<u16> {
|
pub fn imm16(&mut self) -> Option<u16> {
|
||||||
let low = self.imm8()? as u16;
|
let low = self.imm8()? as u16;
|
||||||
let high = self.imm8()? as u16;
|
let high = self.imm8()? as u16;
|
||||||
Some(high << 8 | low)
|
Some(high << 8 | low)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn smm16(&mut self) -> Option<i16> {
|
pub fn smm16(&mut self) -> Option<i16> {
|
||||||
self.imm16().map(|w| w as i16)
|
self.imm16().map(|w| w as i16)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print(&mut self, insn: Insn) -> Option<String> {
|
pub fn print(&mut self, insn: Insn) -> Option<String> {
|
||||||
Some(match insn {
|
Some(match insn {
|
||||||
Insn::LdImm(reg) => format!("ld\t{reg}, {:02x}", self.imm8()?),
|
Insn::LdImm(reg) => format!("ld\t{reg}, {:02x}", self.imm8()?),
|
||||||
@ -79,6 +84,7 @@ impl<I: Iterator<Item = u8>> Disassembler<I> {
|
|||||||
_ => format!("{insn}"),
|
_ => format!("{insn}"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_prefix_cb(&mut self) -> Option<String> {
|
pub fn print_prefix_cb(&mut self) -> Option<String> {
|
||||||
let prefixed: Prefixed = self.imm8()?.into();
|
let prefixed: Prefixed = self.imm8()?.into();
|
||||||
Some(format!("{prefixed}"))
|
Some(format!("{prefixed}"))
|
||||||
|
@ -77,6 +77,7 @@ pub mod cli {
|
|||||||
/// `=`: Equals Sign
|
/// `=`: Equals Sign
|
||||||
Eq,
|
Eq,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Op {
|
impl Display for Op {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
@ -180,16 +181,20 @@ pub mod cli {
|
|||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn peek(&mut self) -> Option<&char> {
|
fn peek(&mut self) -> Option<&char> {
|
||||||
self.text.peek()
|
self.text.peek()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take(&mut self) -> &mut Self {
|
fn take(&mut self) -> &mut Self {
|
||||||
self.text.next();
|
self.text.next();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit(&mut self, kind: Token) -> Option<Token> {
|
fn emit(&mut self, kind: Token) -> Option<Token> {
|
||||||
Some(kind)
|
Some(kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn digits<const B: u32>(&mut self) -> Option<Token> {
|
fn digits<const B: u32>(&mut self) -> Option<Token> {
|
||||||
let mut out = 0;
|
let mut out = 0;
|
||||||
while let Some(Some(next)) = self.peek().map(|c| c.to_digit(B)) {
|
while let Some(Some(next)) = self.peek().map(|c| c.to_digit(B)) {
|
||||||
@ -198,6 +203,7 @@ pub mod cli {
|
|||||||
}
|
}
|
||||||
Some(Num(out))
|
Some(Num(out))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn base(&mut self) -> Option<Token> {
|
fn base(&mut self) -> Option<Token> {
|
||||||
match self.peek() {
|
match self.peek() {
|
||||||
Some('b') => self.take().digits::<2>(),
|
Some('b') => self.take().digits::<2>(),
|
||||||
@ -207,6 +213,7 @@ pub mod cli {
|
|||||||
_ => self.digits::<16>(),
|
_ => self.digits::<16>(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ident(&mut self) -> Option<Token> {
|
fn ident(&mut self) -> Option<Token> {
|
||||||
let mut out = String::new();
|
let mut out = String::new();
|
||||||
while let Some(&c) = self.peek().filter(|&&c| c.is_alphanumeric() || c == '_') {
|
while let Some(&c) = self.peek().filter(|&&c| c.is_alphanumeric() || c == '_') {
|
||||||
@ -239,6 +246,7 @@ pub mod cli {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks the following character, producing the provided [Token]
|
/// Checks the following character, producing the provided [Token]
|
||||||
/// if it matches the expected [char]. Otherwise, prodices [Token::Other].
|
/// if it matches the expected [char]. Otherwise, prodices [Token::Other].
|
||||||
fn chain(&mut self, expect: char, becomes: Token) -> Option<Token> {
|
fn chain(&mut self, expect: char, becomes: Token) -> Option<Token> {
|
||||||
@ -264,8 +272,10 @@ pub mod cli {
|
|||||||
self.take().emit(Token::Ident(value))
|
self.take().emit(Token::Ident(value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: Iterator<Item = char>> Iterator for Lexer<I> {
|
impl<I: Iterator<Item = char>> Iterator for Lexer<I> {
|
||||||
type Item = Token;
|
type Item = Token;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
match self.sync().peek()? {
|
match self.sync().peek()? {
|
||||||
'[' => self.take().emit(Op(BrackOpen)),
|
'[' => self.take().emit(Op(BrackOpen)),
|
||||||
@ -310,6 +320,7 @@ pub mod cli {
|
|||||||
pub trait Parsible: Iterator<Item = Token> + Sized {
|
pub trait Parsible: Iterator<Item = Token> + Sized {
|
||||||
fn parse(self) -> Parser<Self>;
|
fn parse(self) -> Parser<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Iterator<Item = Token> + Sized> Parsible for T {
|
impl<T: Iterator<Item = Token> + Sized> Parsible for T {
|
||||||
fn parse(self) -> Parser<Self> {
|
fn parse(self) -> Parser<Self> {
|
||||||
Parser::new(self)
|
Parser::new(self)
|
||||||
@ -330,9 +341,11 @@ pub mod cli {
|
|||||||
fn peek(&mut self) -> Option<&Token> {
|
fn peek(&mut self) -> Option<&Token> {
|
||||||
self.lexer.peek()
|
self.lexer.peek()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take(&mut self) -> Option<Token> {
|
fn take(&mut self) -> Option<Token> {
|
||||||
self.lexer.next()
|
self.lexer.next()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn then(&mut self, tok: Token) -> Option<&mut Self> {
|
fn then(&mut self, tok: Token) -> Option<&mut Self> {
|
||||||
(*self.peek()? == tok).then(|| {
|
(*self.peek()? == tok).then(|| {
|
||||||
@ -472,6 +485,7 @@ pub mod cli {
|
|||||||
};
|
};
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(prec) = op.infix() {
|
if let Some(prec) = op.infix() {
|
||||||
if prec.before() < level {
|
if prec.before() < level {
|
||||||
break;
|
break;
|
||||||
@ -501,13 +515,16 @@ pub mod cli {
|
|||||||
Term,
|
Term,
|
||||||
Sign,
|
Sign,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Level {
|
impl Level {
|
||||||
fn level(self) -> u8 {
|
fn level(self) -> u8 {
|
||||||
(self as u8) << 1
|
(self as u8) << 1
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn before(self) -> u8 {
|
pub fn before(self) -> u8 {
|
||||||
self.level()
|
self.level()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn after(self) -> u8 {
|
pub fn after(self) -> u8 {
|
||||||
self.level() + 1
|
self.level() + 1
|
||||||
}
|
}
|
||||||
|
1
rustfmt.toml
Normal file
1
rustfmt.toml
Normal file
@ -0,0 +1 @@
|
|||||||
|
use_small_heuristics = "Max"
|
96
src/cpu.rs
96
src/cpu.rs
@ -35,6 +35,7 @@ pub struct Flags {
|
|||||||
fn half_carry_add(a: u8, b: u8) -> bool {
|
fn half_carry_add(a: u8, b: u8) -> bool {
|
||||||
(a & 0xf) + (b & 0xf) > 0xf
|
(a & 0xf) + (b & 0xf) > 0xf
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs the half-carry calculation for subtraction
|
/// Performs the half-carry calculation for subtraction
|
||||||
fn half_carry_sub(a: u8, b: u8) -> bool {
|
fn half_carry_sub(a: u8, b: u8) -> bool {
|
||||||
(a & 0xf) < (b & 0xf)
|
(a & 0xf) < (b & 0xf)
|
||||||
@ -145,18 +146,22 @@ impl CPU {
|
|||||||
pub fn pc(&self) -> u16 {
|
pub fn pc(&self) -> u16 {
|
||||||
self.pc.0
|
self.pc.0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the address of the next instruction
|
/// Sets the address of the next instruction
|
||||||
pub fn set_pc(&mut self, addr: u16) {
|
pub fn set_pc(&mut self, addr: u16) {
|
||||||
self.pc.0 = addr
|
self.pc.0 = addr
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the current instruction
|
/// Gets the current instruction
|
||||||
pub fn ir(&self) -> Insn {
|
pub fn ir(&self) -> Insn {
|
||||||
self.ir
|
self.ir
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets a breakpoint. Returns true if the breakpoint was not already set.
|
/// Sets a breakpoint. Returns true if the breakpoint was not already set.
|
||||||
pub fn set_break(&mut self, addr: u16) -> bool {
|
pub fn set_break(&mut self, addr: u16) -> bool {
|
||||||
self.breakpoints.insert(addr)
|
self.breakpoints.insert(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes a breakpoint. Returns true if the breakpoint was set.
|
/// Removes a breakpoint. Returns true if the breakpoint was set.
|
||||||
pub fn unset_break(&mut self, addr: u16) -> bool {
|
pub fn unset_break(&mut self, addr: u16) -> bool {
|
||||||
self.breakpoints.remove(&addr)
|
self.breakpoints.remove(&addr)
|
||||||
@ -171,18 +176,21 @@ impl CPU {
|
|||||||
self.pc += 1;
|
self.pc += 1;
|
||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read a 16-bit immediate at the program counter, post-incrementing it twice (adds two cycles)
|
/// Read a 16-bit immediate at the program counter, post-incrementing it twice (adds two cycles)
|
||||||
pub fn imm16(&mut self, bus: &impl BusIO) -> Result<u16, Error> {
|
pub fn imm16(&mut self, bus: &impl BusIO) -> Result<u16, Error> {
|
||||||
let out = self.read16(self.pc.0, bus)?;
|
let out = self.read16(self.pc.0, bus)?;
|
||||||
self.pc += 2;
|
self.pc += 2;
|
||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read an 8-bit value at address `addr` on the `bus` (adds one cycle)
|
/// Read an 8-bit value at address `addr` on the `bus` (adds one cycle)
|
||||||
pub fn read(&mut self, addr: u16, bus: &impl BusIO) -> Result<u8, Error> {
|
pub fn read(&mut self, addr: u16, bus: &impl BusIO) -> Result<u8, Error> {
|
||||||
// Reading a byte takes one cycle
|
// Reading a byte takes one cycle
|
||||||
self.wait();
|
self.wait();
|
||||||
bus.read(addr as usize).ok_or(InvalidAddress(addr).into())
|
bus.read(addr as usize).ok_or(InvalidAddress(addr).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read a 16-bit LE value at address `addr` on the `bus` (adds two cycles)
|
/// Read a 16-bit LE value at address `addr` on the `bus` (adds two cycles)
|
||||||
pub fn read16(&mut self, addr: u16, bus: &impl BusIO) -> Result<u16, Error> {
|
pub fn read16(&mut self, addr: u16, bus: &impl BusIO) -> Result<u16, Error> {
|
||||||
let out = self.read(addr, bus)? as u16;
|
let out = self.read(addr, bus)? as u16;
|
||||||
@ -193,9 +201,9 @@ impl CPU {
|
|||||||
pub fn write(&mut self, addr: u16, data: u8, bus: &mut impl BusIO) -> Result<(), Error> {
|
pub fn write(&mut self, addr: u16, data: u8, bus: &mut impl BusIO) -> Result<(), Error> {
|
||||||
// Writing a byte takes one cycle
|
// Writing a byte takes one cycle
|
||||||
self.wait();
|
self.wait();
|
||||||
bus.write(addr as usize, data)
|
bus.write(addr as usize, data).ok_or(InvalidAddress(addr).into())
|
||||||
.ok_or(InvalidAddress(addr).into())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write a 16-bit LE value to address `addr..=addr+1` on the `bus` (adds two cycles)
|
/// Write a 16-bit LE value to address `addr..=addr+1` on the `bus` (adds two cycles)
|
||||||
pub fn write16(&mut self, addr: u16, data: u16, bus: &mut impl BusIO) -> Result<(), Error> {
|
pub fn write16(&mut self, addr: u16, data: u16, bus: &mut impl BusIO) -> Result<(), Error> {
|
||||||
let data = data.to_le_bytes();
|
let data = data.to_le_bytes();
|
||||||
@ -208,18 +216,21 @@ impl CPU {
|
|||||||
self.sp -= 1;
|
self.sp -= 1;
|
||||||
self.write(self.sp.0, data, bus)
|
self.write(self.sp.0, data, bus)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Push a 16-bit value onto the stack (adds two cycles)
|
/// Push a 16-bit value onto the stack (adds two cycles)
|
||||||
pub fn push16(&mut self, data: u16, bus: &mut impl BusIO) -> Result<(), Error> {
|
pub fn push16(&mut self, data: u16, bus: &mut impl BusIO) -> Result<(), Error> {
|
||||||
let data = data.to_le_bytes();
|
let data = data.to_le_bytes();
|
||||||
self.push(data[0], bus)?;
|
self.push(data[0], bus)?;
|
||||||
self.push(data[1], bus)
|
self.push(data[1], bus)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pop a byte off the stack (adds one cycle)
|
/// Pop a byte off the stack (adds one cycle)
|
||||||
pub fn pop(&mut self, bus: &mut impl BusIO) -> Result<u8, Error> {
|
pub fn pop(&mut self, bus: &mut impl BusIO) -> Result<u8, Error> {
|
||||||
let out = self.read(self.sp.0, bus);
|
let out = self.read(self.sp.0, bus);
|
||||||
self.sp += 1;
|
self.sp += 1;
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pop a double word off the stack (adds two cycles)
|
/// Pop a double word off the stack (adds two cycles)
|
||||||
pub fn pop16(&mut self, bus: &mut impl BusIO) -> Result<u16, Error> {
|
pub fn pop16(&mut self, bus: &mut impl BusIO) -> Result<u16, Error> {
|
||||||
// pushes LH, pops HL
|
// pushes LH, pops HL
|
||||||
@ -268,23 +279,28 @@ mod instructions {
|
|||||||
pub fn nop(&mut self) -> IResult {
|
pub fn nop(&mut self) -> IResult {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stop(&mut self) -> IResult {
|
pub fn stop(&mut self) -> IResult {
|
||||||
println!("Stop requested.\n{self}");
|
println!("Stop requested.\n{self}");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn halt(&mut self) -> IResult {
|
pub fn halt(&mut self) -> IResult {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn di(&mut self) -> IResult {
|
pub fn di(&mut self) -> IResult {
|
||||||
self.ime = Ime::Disabled;
|
self.ime = Ime::Disabled;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ei(&mut self) -> IResult {
|
pub fn ei(&mut self) -> IResult {
|
||||||
// TODO: this is incorrect
|
// TODO: this is incorrect
|
||||||
self.ime = Ime::ShouldEnable;
|
self.ime = Ime::ShouldEnable;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loads and stores
|
/// Loads and stores
|
||||||
impl CPU {
|
impl CPU {
|
||||||
pub fn ld(&mut self, dst: R8, src: R8, bus: &mut impl BusIO) -> IResult {
|
pub fn ld(&mut self, dst: R8, src: R8, bus: &mut impl BusIO) -> IResult {
|
||||||
@ -292,70 +308,84 @@ mod instructions {
|
|||||||
self.set_r8(dst, src, bus);
|
self.set_r8(dst, src, bus);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ld_imm(&mut self, dst: R8, bus: &mut impl BusIO) -> IResult {
|
pub fn ld_imm(&mut self, dst: R8, bus: &mut impl BusIO) -> IResult {
|
||||||
// TODO: open bus behavior
|
// TODO: open bus behavior
|
||||||
let src = self.imm8(bus)?;
|
let src = self.imm8(bus)?;
|
||||||
self.set_r8(dst, src, bus);
|
self.set_r8(dst, src, bus);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ld_imm16(&mut self, dst: R16, bus: &mut impl BusIO) -> IResult {
|
pub fn ld_imm16(&mut self, dst: R16, bus: &mut impl BusIO) -> IResult {
|
||||||
let imm16 = self.imm16(bus)?;
|
let imm16 = self.imm16(bus)?;
|
||||||
self[dst].0 = imm16;
|
self[dst].0 = imm16;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ld_ind(&mut self, src: R16Indirect, bus: &mut impl BusIO) -> IResult {
|
pub fn ld_ind(&mut self, src: R16Indirect, bus: &mut impl BusIO) -> IResult {
|
||||||
self[R8::A].0 = self.read_r16ind(src, bus)?;
|
self[R8::A].0 = self.read_r16ind(src, bus)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn st_ind(&mut self, dst: R16Indirect, bus: &mut impl BusIO) -> IResult {
|
pub fn st_ind(&mut self, dst: R16Indirect, bus: &mut impl BusIO) -> IResult {
|
||||||
self.write_r16ind(dst, self[R8::A].0, bus);
|
self.write_r16ind(dst, self[R8::A].0, bus);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ldc(&mut self, bus: &mut impl BusIO) -> IResult {
|
pub fn ldc(&mut self, bus: &mut impl BusIO) -> IResult {
|
||||||
let addr = self[R8::C].0 as u16 | 0xff00;
|
let addr = self[R8::C].0 as u16 | 0xff00;
|
||||||
self[R8::A].0 = self.read(addr, bus)?;
|
self[R8::A].0 = self.read(addr, bus)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stc(&mut self, bus: &mut impl BusIO) -> IResult {
|
pub fn stc(&mut self, bus: &mut impl BusIO) -> IResult {
|
||||||
let addr = self[R8::C].0 as u16 | 0xff00;
|
let addr = self[R8::C].0 as u16 | 0xff00;
|
||||||
self.write(addr, self[R8::A].0, bus)?;
|
self.write(addr, self[R8::A].0, bus)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ld_abs(&mut self, bus: &mut impl BusIO) -> IResult {
|
pub fn ld_abs(&mut self, bus: &mut impl BusIO) -> IResult {
|
||||||
let addr = self.imm16(bus)?;
|
let addr = self.imm16(bus)?;
|
||||||
self[R8::A].0 = self.read(addr, bus)?;
|
self[R8::A].0 = self.read(addr, bus)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn st_abs(&mut self, bus: &mut impl BusIO) -> IResult {
|
pub fn st_abs(&mut self, bus: &mut impl BusIO) -> IResult {
|
||||||
let addr = self.imm16(bus)?;
|
let addr = self.imm16(bus)?;
|
||||||
self.write(addr, self[R8::A].0, bus)?;
|
self.write(addr, self[R8::A].0, bus)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn st_sp_abs(&mut self, bus: &mut impl BusIO) -> IResult {
|
pub fn st_sp_abs(&mut self, bus: &mut impl BusIO) -> IResult {
|
||||||
let addr = self.imm16(bus)?;
|
let addr = self.imm16(bus)?;
|
||||||
self.write16(addr, self[R16::SP].0, bus)?;
|
self.write16(addr, self[R16::SP].0, bus)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ld_sp_hl(&mut self) -> IResult {
|
pub fn ld_sp_hl(&mut self) -> IResult {
|
||||||
self[R16::HL].0 = self[R16::SP].0;
|
self[R16::HL].0 = self[R16::SP].0;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ld_hl_sp_rel(&mut self, bus: &mut impl BusIO) -> IResult {
|
pub fn ld_hl_sp_rel(&mut self, bus: &mut impl BusIO) -> IResult {
|
||||||
let offset = self.imm8(bus)? as i8 as i16 as u16;
|
let offset = self.imm8(bus)? as i8 as i16 as u16;
|
||||||
self[R16::HL].0 = self.sp.0.wrapping_add(offset);
|
self[R16::HL].0 = self.sp.0.wrapping_add(offset);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ldh(&mut self, bus: &mut impl BusIO) -> IResult {
|
pub fn ldh(&mut self, bus: &mut impl BusIO) -> IResult {
|
||||||
let addr = self.imm8(bus)? as u16 | 0xff00;
|
let addr = self.imm8(bus)? as u16 | 0xff00;
|
||||||
self[R8::A].0 = self.read(addr, bus)?;
|
self[R8::A].0 = self.read(addr, bus)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sth(&mut self, bus: &mut impl BusIO) -> IResult {
|
pub fn sth(&mut self, bus: &mut impl BusIO) -> IResult {
|
||||||
let addr = self.imm8(bus)? as u16 | 0xff00;
|
let addr = self.imm8(bus)? as u16 | 0xff00;
|
||||||
self.write(addr, self[R8::A].0, bus)?;
|
self.write(addr, self[R8::A].0, bus)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inc and Dec
|
/// Inc and Dec
|
||||||
impl CPU {
|
impl CPU {
|
||||||
pub fn inc(&mut self, reg: R8, bus: &mut impl BusIO) -> IResult {
|
pub fn inc(&mut self, reg: R8, bus: &mut impl BusIO) -> IResult {
|
||||||
@ -364,21 +394,25 @@ mod instructions {
|
|||||||
*self.flags_mut() = Flags::new().with_z(data == 0).with_c(carry);
|
*self.flags_mut() = Flags::new().with_z(data == 0).with_c(carry);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dec(&mut self, reg: R8, bus: &mut impl BusIO) -> IResult {
|
pub fn dec(&mut self, reg: R8, bus: &mut impl BusIO) -> IResult {
|
||||||
let (data, carry) = self.get_r8(reg, bus).overflowing_sub(1);
|
let (data, carry) = self.get_r8(reg, bus).overflowing_sub(1);
|
||||||
self.set_r8(reg, data, bus);
|
self.set_r8(reg, data, bus);
|
||||||
*self.flags_mut() = Flags::new().with_z(data == 0).with_c(carry);
|
*self.flags_mut() = Flags::new().with_z(data == 0).with_c(carry);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inc16(&mut self, reg: R16) -> IResult {
|
pub fn inc16(&mut self, reg: R16) -> IResult {
|
||||||
self[reg] += 1;
|
self[reg] += 1;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dec16(&mut self, reg: R16) -> IResult {
|
pub fn dec16(&mut self, reg: R16) -> IResult {
|
||||||
self[reg] -= 1;
|
self[reg] -= 1;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Rotates and shifts
|
/// Rotates and shifts
|
||||||
impl CPU {
|
impl CPU {
|
||||||
pub fn rlnca(&mut self) -> IResult {
|
pub fn rlnca(&mut self) -> IResult {
|
||||||
@ -387,12 +421,14 @@ mod instructions {
|
|||||||
self.set_z(false).set_n(false).set_h(false).set_c(carry);
|
self.set_z(false).set_n(false).set_h(false).set_c(carry);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rrnca(&mut self) -> IResult {
|
pub fn rrnca(&mut self) -> IResult {
|
||||||
let carry = self[R8::A].0 & 1 != 0;
|
let carry = self[R8::A].0 & 1 != 0;
|
||||||
self[R8::A].0 = self[R8::A].0.rotate_right(1);
|
self[R8::A].0 = self[R8::A].0.rotate_right(1);
|
||||||
self.set_z(false).set_n(false).set_h(false).set_c(carry);
|
self.set_z(false).set_n(false).set_h(false).set_c(carry);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rla(&mut self) -> IResult {
|
pub fn rla(&mut self) -> IResult {
|
||||||
// TODO: optimize RLA/RRA
|
// TODO: optimize RLA/RRA
|
||||||
let carry = self[R8::A].0 & 0x80 != 0;
|
let carry = self[R8::A].0 & 0x80 != 0;
|
||||||
@ -402,6 +438,7 @@ mod instructions {
|
|||||||
self.set_z(false).set_n(false).set_h(false).set_c(carry);
|
self.set_z(false).set_n(false).set_h(false).set_c(carry);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rra(&mut self) -> IResult {
|
pub fn rra(&mut self) -> IResult {
|
||||||
let carry = self[R8::A].0 & 1 != 0;
|
let carry = self[R8::A].0 & 1 != 0;
|
||||||
self[R8::A] >>= 1;
|
self[R8::A] >>= 1;
|
||||||
@ -411,6 +448,7 @@ mod instructions {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Funky instructions
|
/// Funky instructions
|
||||||
impl CPU {
|
impl CPU {
|
||||||
pub fn daa(&mut self) -> IResult {
|
pub fn daa(&mut self) -> IResult {
|
||||||
@ -435,22 +473,26 @@ mod instructions {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cpl(&mut self) -> IResult {
|
pub fn cpl(&mut self) -> IResult {
|
||||||
self[R8::A] ^= 0xff;
|
self[R8::A] ^= 0xff;
|
||||||
self.set_n(false).set_h(false);
|
self.set_n(false).set_h(false);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scf(&mut self) -> IResult {
|
pub fn scf(&mut self) -> IResult {
|
||||||
let flags = self.flags_mut();
|
let flags = self.flags_mut();
|
||||||
*flags = flags.with_n(false).with_h(false).with_c(true);
|
*flags = flags.with_n(false).with_h(false).with_c(true);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ccf(&mut self) -> IResult {
|
pub fn ccf(&mut self) -> IResult {
|
||||||
let flags = self.flags_mut();
|
let flags = self.flags_mut();
|
||||||
*flags = flags.with_n(false).with_h(false).with_c(!flags.c());
|
*flags = flags.with_n(false).with_h(false).with_c(!flags.c());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Addition
|
/// Addition
|
||||||
impl CPU {
|
impl CPU {
|
||||||
fn add_impl(&mut self, value: u8) -> IResult {
|
fn add_impl(&mut self, value: u8) -> IResult {
|
||||||
@ -469,6 +511,7 @@ mod instructions {
|
|||||||
self.add_impl(src)?;
|
self.add_impl(src)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add16(&mut self, reg: R16) -> IResult {
|
pub fn add16(&mut self, reg: R16) -> IResult {
|
||||||
let (value, h) = (self[reg], self[R8::H].0);
|
let (value, h) = (self[reg], self[R8::H].0);
|
||||||
let value_h = (value.0 >> 8) as u8;
|
let value_h = (value.0 >> 8) as u8;
|
||||||
@ -479,13 +522,14 @@ mod instructions {
|
|||||||
*self.hl.wide_mut() += value;
|
*self.hl.wide_mut() += value;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub fn add16_spi(&mut self, bus: &mut impl BusIO) -> IResult {
|
pub fn add16_spi(&mut self, bus: &mut impl BusIO) -> IResult {
|
||||||
let offset = self.imm8(bus)? as i8 as i16; // sign extend
|
let offset = self.imm8(bus)? as i8 as i16; // sign extend
|
||||||
|
|
||||||
Err(UnimplementedInsn(self.ir).into())
|
Err(UnimplementedInsn(self.ir).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Addition with carry
|
/// Addition with carry
|
||||||
impl CPU {
|
impl CPU {
|
||||||
fn addc_impl(&mut self, value: u8) -> IResult {
|
fn addc_impl(&mut self, value: u8) -> IResult {
|
||||||
@ -493,10 +537,7 @@ mod instructions {
|
|||||||
let hc = half_carry_add(self[R8::A].0, src);
|
let hc = half_carry_add(self[R8::A].0, src);
|
||||||
let (out, carry) = self[R8::A].0.overflowing_add(src);
|
let (out, carry) = self[R8::A].0.overflowing_add(src);
|
||||||
self[R8::A].0 = out;
|
self[R8::A].0 = out;
|
||||||
self.set_z(out == 0)
|
self.set_z(out == 0).set_n(false).set_h(hc).set_c(carry | carry_in);
|
||||||
.set_n(false)
|
|
||||||
.set_h(hc)
|
|
||||||
.set_c(carry | carry_in);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
pub fn adc(&mut self, reg: R8, bus: &mut impl BusIO) -> IResult {
|
pub fn adc(&mut self, reg: R8, bus: &mut impl BusIO) -> IResult {
|
||||||
@ -508,6 +549,7 @@ mod instructions {
|
|||||||
self.addc_impl(src)
|
self.addc_impl(src)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Subtraction
|
/// Subtraction
|
||||||
impl CPU {
|
impl CPU {
|
||||||
fn sub_impl(&mut self, value: u8) -> IResult {
|
fn sub_impl(&mut self, value: u8) -> IResult {
|
||||||
@ -526,6 +568,7 @@ mod instructions {
|
|||||||
self.sub_impl(value)
|
self.sub_impl(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Subtraction with carry
|
/// Subtraction with carry
|
||||||
impl CPU {
|
impl CPU {
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -534,10 +577,7 @@ mod instructions {
|
|||||||
let (out, carry) = self[R8::A].0.overflowing_sub(src);
|
let (out, carry) = self[R8::A].0.overflowing_sub(src);
|
||||||
let hc = half_carry_sub(self[R8::A].0, src);
|
let hc = half_carry_sub(self[R8::A].0, src);
|
||||||
self[R8::A].0 = out;
|
self[R8::A].0 = out;
|
||||||
self.set_z(out == 0)
|
self.set_z(out == 0).set_n(true).set_h(hc).set_c(carry | cin);
|
||||||
.set_n(true)
|
|
||||||
.set_h(hc)
|
|
||||||
.set_c(carry | cin);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
pub fn sbc(&mut self, reg: R8, bus: &mut impl BusIO) -> IResult {
|
pub fn sbc(&mut self, reg: R8, bus: &mut impl BusIO) -> IResult {
|
||||||
@ -549,14 +589,12 @@ mod instructions {
|
|||||||
self.subc_impl(value)
|
self.subc_impl(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Bitwise AND
|
/// Bitwise AND
|
||||||
impl CPU {
|
impl CPU {
|
||||||
fn and_impl(&mut self, value: u8) -> IResult {
|
fn and_impl(&mut self, value: u8) -> IResult {
|
||||||
self[R8::A] &= value;
|
self[R8::A] &= value;
|
||||||
self.set_z(self[R8::A].0 == 0)
|
self.set_z(self[R8::A].0 == 0).set_n(false).set_h(true).set_c(false);
|
||||||
.set_n(false)
|
|
||||||
.set_h(true)
|
|
||||||
.set_c(false);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
pub fn and(&mut self, reg: R8, bus: &mut impl BusIO) -> IResult {
|
pub fn and(&mut self, reg: R8, bus: &mut impl BusIO) -> IResult {
|
||||||
@ -568,6 +606,7 @@ mod instructions {
|
|||||||
self.and_impl(value)
|
self.and_impl(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Bitwise XOR
|
/// Bitwise XOR
|
||||||
impl CPU {
|
impl CPU {
|
||||||
fn xor_impl(&mut self, value: u8) -> IResult {
|
fn xor_impl(&mut self, value: u8) -> IResult {
|
||||||
@ -584,6 +623,7 @@ mod instructions {
|
|||||||
self.xor_impl(value)
|
self.xor_impl(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Bitwise OR
|
/// Bitwise OR
|
||||||
impl CPU {
|
impl CPU {
|
||||||
fn or_impl(&mut self, value: u8) -> IResult {
|
fn or_impl(&mut self, value: u8) -> IResult {
|
||||||
@ -600,6 +640,7 @@ mod instructions {
|
|||||||
self.or_impl(value)
|
self.or_impl(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Comparison
|
/// Comparison
|
||||||
impl CPU {
|
impl CPU {
|
||||||
fn cmp_impl(&mut self, src: u8) -> IResult {
|
fn cmp_impl(&mut self, src: u8) -> IResult {
|
||||||
@ -617,11 +658,13 @@ mod instructions {
|
|||||||
self.cmp_impl(src)
|
self.cmp_impl(src)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stack ops
|
/// Stack ops
|
||||||
impl CPU {
|
impl CPU {
|
||||||
pub fn insn_push(&mut self, reg: R16Stack, bus: &mut impl BusIO) -> IResult {
|
pub fn insn_push(&mut self, reg: R16Stack, bus: &mut impl BusIO) -> IResult {
|
||||||
self.push16(self[reg].0, bus)
|
self.push16(self[reg].0, bus)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insn_pop(&mut self, reg: R16Stack, bus: &mut impl BusIO) -> IResult {
|
pub fn insn_pop(&mut self, reg: R16Stack, bus: &mut impl BusIO) -> IResult {
|
||||||
self[reg].0 = self.pop16(bus)?;
|
self[reg].0 = self.pop16(bus)?;
|
||||||
if let R16Stack::AF = reg {
|
if let R16Stack::AF = reg {
|
||||||
@ -630,6 +673,7 @@ mod instructions {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Jumps, branches, calls, and returns
|
/// Jumps, branches, calls, and returns
|
||||||
impl CPU {
|
impl CPU {
|
||||||
pub fn jr(&mut self, bus: &mut impl BusIO) -> IResult {
|
pub fn jr(&mut self, bus: &mut impl BusIO) -> IResult {
|
||||||
@ -680,6 +724,7 @@ mod instructions {
|
|||||||
false => Ok(()),
|
false => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ret(&mut self, bus: &mut impl BusIO) -> IResult {
|
pub fn ret(&mut self, bus: &mut impl BusIO) -> IResult {
|
||||||
let addr = self.pop16(bus)?;
|
let addr = self.pop16(bus)?;
|
||||||
self.jumpto(addr);
|
self.jumpto(addr);
|
||||||
@ -696,10 +741,12 @@ mod instructions {
|
|||||||
self.ime = Ime::Enabled; // Technically this should go after ret's pop
|
self.ime = Ime::Enabled; // Technically this should go after ret's pop
|
||||||
self.ret(bus)
|
self.ret(bus)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset(&mut self, addr: u8, bus: &mut impl BusIO) -> IResult {
|
pub fn reset(&mut self, addr: u8, bus: &mut impl BusIO) -> IResult {
|
||||||
self.call_addr(addr as u16 * 8, bus)
|
self.call_addr(addr as u16 * 8, bus)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prefixed instructions
|
/// Prefixed instructions
|
||||||
impl CPU {
|
impl CPU {
|
||||||
/// Rotates left, setting the carry flag to the highest bit
|
/// Rotates left, setting the carry flag to the highest bit
|
||||||
@ -786,7 +833,7 @@ impl CPU {
|
|||||||
pub fn run(&mut self, bus: &mut impl BusIO) -> Result<usize, Error> {
|
pub fn run(&mut self, bus: &mut impl BusIO) -> Result<usize, Error> {
|
||||||
self.m_cycles = 0;
|
self.m_cycles = 0;
|
||||||
// Run the current instruction
|
// Run the current instruction
|
||||||
self.execute(bus)?;
|
let out = self.execute(bus);
|
||||||
|
|
||||||
let prefetch_pc = self.pc.0;
|
let prefetch_pc = self.pc.0;
|
||||||
// Handle interrupts
|
// Handle interrupts
|
||||||
@ -802,9 +849,10 @@ impl CPU {
|
|||||||
if self.breakpoints.contains(&prefetch_pc) {
|
if self.breakpoints.contains(&prefetch_pc) {
|
||||||
Err(HitBreak(prefetch_pc, self.m_cycles).into())
|
Err(HitBreak(prefetch_pc, self.m_cycles).into())
|
||||||
} else {
|
} else {
|
||||||
Ok(self.m_cycles as usize)
|
out.map(|_| self.m_cycles as usize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fetch(&mut self, bus: &mut impl BusIO) -> Result<(), Error> {
|
pub fn fetch(&mut self, bus: &mut impl BusIO) -> Result<(), Error> {
|
||||||
self.ir = match self.ir {
|
self.ir = match self.ir {
|
||||||
// HALT fetches the current instruction, rather than the next
|
// HALT fetches the current instruction, rather than the next
|
||||||
@ -815,6 +863,7 @@ impl CPU {
|
|||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute(&mut self, bus: &mut impl BusIO) -> Result<(), Error> {
|
pub fn execute(&mut self, bus: &mut impl BusIO) -> Result<(), Error> {
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
match self.ir {
|
match self.ir {
|
||||||
@ -874,6 +923,7 @@ impl CPU {
|
|||||||
Insn::OrI => self.ori(bus),
|
Insn::OrI => self.ori(bus),
|
||||||
Insn::Cp(reg) => self.cmp(reg, bus),
|
Insn::Cp(reg) => self.cmp(reg, bus),
|
||||||
Insn::CpI => self.cmpi(bus),
|
Insn::CpI => self.cmpi(bus),
|
||||||
|
|
||||||
Insn::Push(reg) => self.insn_push(reg, bus),
|
Insn::Push(reg) => self.insn_push(reg, bus),
|
||||||
Insn::Pop(reg) => self.insn_pop(reg, bus),
|
Insn::Pop(reg) => self.insn_pop(reg, bus),
|
||||||
// Jumps
|
// Jumps
|
||||||
@ -894,6 +944,7 @@ impl CPU {
|
|||||||
Insn::Invalid(b) => Err(InvalidInstruction(b).into()),
|
Insn::Invalid(b) => Err(InvalidInstruction(b).into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prefixed(&mut self, bus: &mut impl BusIO) -> Result<(), Error> {
|
pub fn prefixed(&mut self, bus: &mut impl BusIO) -> Result<(), Error> {
|
||||||
match Prefixed::from(self.imm8(bus)?) {
|
match Prefixed::from(self.imm8(bus)?) {
|
||||||
Prefixed::Rlc(reg) => self.rlc(reg, bus),
|
Prefixed::Rlc(reg) => self.rlc(reg, bus),
|
||||||
@ -919,6 +970,7 @@ impl CPU {
|
|||||||
_ => self[r8].0,
|
_ => self[r8].0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets an 8-bit register. This requires bus access, since `[HL]` is a valid location
|
/// Sets an 8-bit register. This requires bus access, since `[HL]` is a valid location
|
||||||
///
|
///
|
||||||
/// Panic-free alternative to [IndexMut]
|
/// Panic-free alternative to [IndexMut]
|
||||||
@ -947,21 +999,26 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads data at an R16-indirect address
|
/// Reads data at an R16-indirect address
|
||||||
pub fn read_r16ind(&mut self, r16: R16Indirect, bus: &impl BusIO) -> Result<u8, Error> {
|
pub fn read_r16ind(&mut self, r16: R16Indirect, bus: &impl BusIO) -> Result<u8, Error> {
|
||||||
let addr = self.get_r16ind_addr(r16);
|
let addr = self.get_r16ind_addr(r16);
|
||||||
self.read(addr, bus)
|
self.read(addr, bus)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_r16ind(&mut self, r16: R16Indirect, data: u8, bus: &mut impl BusIO) {
|
pub fn write_r16ind(&mut self, r16: R16Indirect, data: u8, bus: &mut impl BusIO) {
|
||||||
let addr = self.get_r16ind_addr(r16);
|
let addr = self.get_r16ind_addr(r16);
|
||||||
let _ = self.write(addr, data, bus);
|
let _ = self.write(addr, data, bus);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn flags_mut(&mut self) -> &mut Flags {
|
pub fn flags_mut(&mut self) -> &mut Flags {
|
||||||
self.af.flags_mut()
|
self.af.flags_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn flags(&self) -> Flags {
|
pub fn flags(&self) -> Flags {
|
||||||
self.af.flags().to_owned()
|
self.af.flags().to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
flags_getters_setters! {
|
flags_getters_setters! {
|
||||||
/// the `Zero` Flag
|
/// the `Zero` Flag
|
||||||
z, set_z;
|
z, set_z;
|
||||||
@ -972,6 +1029,7 @@ impl CPU {
|
|||||||
/// the `Carry` Flag
|
/// the `Carry` Flag
|
||||||
c, set_c;
|
c, set_c;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compares the given [Cond] to the CPU's condition flags
|
/// Compares the given [Cond] to the CPU's condition flags
|
||||||
pub fn cond(&self, cond: Cond) -> bool {
|
pub fn cond(&self, cond: Cond) -> bool {
|
||||||
match cond {
|
match cond {
|
||||||
@ -1018,6 +1076,7 @@ impl Index<R8> for CPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IndexMut<R8> for CPU {
|
impl IndexMut<R8> for CPU {
|
||||||
/// Performs the mutable indexing (`container[index]`) operation.
|
/// Performs the mutable indexing (`container[index]`) operation.
|
||||||
/// # Panics
|
/// # Panics
|
||||||
@ -1035,6 +1094,7 @@ impl IndexMut<R8> for CPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<R16> for CPU {
|
impl Index<R16> for CPU {
|
||||||
type Output = Wrapping<u16>;
|
type Output = Wrapping<u16>;
|
||||||
/// Performs the indexing (`container[index]`) operation.
|
/// Performs the indexing (`container[index]`) operation.
|
||||||
@ -1047,6 +1107,7 @@ impl Index<R16> for CPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IndexMut<R16> for CPU {
|
impl IndexMut<R16> for CPU {
|
||||||
/// Performs the mutable indexing (`container[index]`) operation.
|
/// Performs the mutable indexing (`container[index]`) operation.
|
||||||
fn index_mut(&mut self, index: R16) -> &mut Self::Output {
|
fn index_mut(&mut self, index: R16) -> &mut Self::Output {
|
||||||
@ -1070,6 +1131,7 @@ impl Index<R16Stack> for CPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IndexMut<R16Stack> for CPU {
|
impl IndexMut<R16Stack> for CPU {
|
||||||
fn index_mut(&mut self, index: R16Stack) -> &mut Self::Output {
|
fn index_mut(&mut self, index: R16Stack) -> &mut Self::Output {
|
||||||
match index {
|
match index {
|
||||||
|
@ -20,6 +20,7 @@ use super::Flags;
|
|||||||
|
|
||||||
/// The low-byte index constant
|
/// The low-byte index constant
|
||||||
const LO: usize = if 1u16.to_be() != 1u16 { 0 } else { 1 };
|
const LO: usize = if 1u16.to_be() != 1u16 { 0 } else { 1 };
|
||||||
|
|
||||||
/// The high-byte index constant
|
/// The high-byte index constant
|
||||||
const HI: usize = 1 - LO;
|
const HI: usize = 1 - LO;
|
||||||
|
|
||||||
@ -30,22 +31,26 @@ pub union SplitRegister {
|
|||||||
af: [Flags; 2],
|
af: [Flags; 2],
|
||||||
r16: Wrapping<u16>,
|
r16: Wrapping<u16>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SplitRegister {
|
impl SplitRegister {
|
||||||
/// Read the low byte of the register
|
/// Read the low byte of the register
|
||||||
pub fn lo(&self) -> &Wrapping<u8> {
|
pub fn lo(&self) -> &Wrapping<u8> {
|
||||||
// SAFETY: The GB's registers store POD types, so it's safe to transmute
|
// SAFETY: The GB's registers store POD types, so it's safe to transmute
|
||||||
unsafe { &self.lh[LO] }
|
unsafe { &self.lh[LO] }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read the high byte of the register
|
/// Read the high byte of the register
|
||||||
pub fn hi(&self) -> &Wrapping<u8> {
|
pub fn hi(&self) -> &Wrapping<u8> {
|
||||||
// SAFETY: See [SplitRegister::lo]
|
// SAFETY: See [SplitRegister::lo]
|
||||||
unsafe { &self.lh[HI] }
|
unsafe { &self.lh[HI] }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read the [flags byte](Flags) of the register
|
/// Read the [flags byte](Flags) of the register
|
||||||
pub fn flags(&self) -> &Flags {
|
pub fn flags(&self) -> &Flags {
|
||||||
// SAFETY: See [SplitRegister::lo]
|
// SAFETY: See [SplitRegister::lo]
|
||||||
unsafe { &self.af[LO] }
|
unsafe { &self.af[LO] }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read the contents of the combined register
|
/// Read the contents of the combined register
|
||||||
pub fn wide(&self) -> &Wrapping<u16> {
|
pub fn wide(&self) -> &Wrapping<u16> {
|
||||||
// SAFETY: See [SplitRegister::lo]
|
// SAFETY: See [SplitRegister::lo]
|
||||||
@ -57,16 +62,19 @@ impl SplitRegister {
|
|||||||
// SAFETY: See [SplitRegister::lo]
|
// SAFETY: See [SplitRegister::lo]
|
||||||
unsafe { &mut self.lh[LO] }
|
unsafe { &mut self.lh[LO] }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a mutable reference to the high byte of the register
|
/// Gets a mutable reference to the high byte of the register
|
||||||
pub fn hi_mut(&mut self) -> &mut Wrapping<u8> {
|
pub fn hi_mut(&mut self) -> &mut Wrapping<u8> {
|
||||||
// SAFETY: See [SplitRegister::lo]
|
// SAFETY: See [SplitRegister::lo]
|
||||||
unsafe { &mut self.lh[HI] }
|
unsafe { &mut self.lh[HI] }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a mutable reference to the [flags byte](Flags) of the register
|
/// Gets a mutable reference to the [flags byte](Flags) of the register
|
||||||
pub fn flags_mut(&mut self) -> &mut Flags {
|
pub fn flags_mut(&mut self) -> &mut Flags {
|
||||||
// SAFETY: See [SplitRegister::lo]
|
// SAFETY: See [SplitRegister::lo]
|
||||||
unsafe { &mut self.af[LO] }
|
unsafe { &mut self.af[LO] }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a mutable reference to the combined register
|
/// Gets a mutable reference to the combined register
|
||||||
pub fn wide_mut(&mut self) -> &mut Wrapping<u16> {
|
pub fn wide_mut(&mut self) -> &mut Wrapping<u16> {
|
||||||
// SAFETY: See [SplitRegister::lo]
|
// SAFETY: See [SplitRegister::lo]
|
||||||
@ -75,61 +83,64 @@ impl SplitRegister {
|
|||||||
|
|
||||||
/// Constructs a SplitRegister from an array of big-endian bytes.
|
/// Constructs a SplitRegister from an array of big-endian bytes.
|
||||||
pub fn from_be_bytes(value: [u8; 2]) -> Self {
|
pub fn from_be_bytes(value: [u8; 2]) -> Self {
|
||||||
Self {
|
Self { lh: [Wrapping(value[HI]), Wrapping(value[LO])] }
|
||||||
lh: [Wrapping(value[HI]), Wrapping(value[LO])],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs a SplitRegister from an array of little-endian bytes.
|
/// Constructs a SplitRegister from an array of little-endian bytes.
|
||||||
pub fn from_le_bytes(value: [u8; 2]) -> Self {
|
pub fn from_le_bytes(value: [u8; 2]) -> Self {
|
||||||
Self {
|
Self { lh: [Wrapping(value[LO]), Wrapping(value[HI])] }
|
||||||
lh: [Wrapping(value[LO]), Wrapping(value[HI])],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs a SplitRegister from an array of host-endian bytes.
|
/// Constructs a SplitRegister from an array of host-endian bytes.
|
||||||
pub fn from_ne_bytes(value: [u8; 2]) -> Self {
|
pub fn from_ne_bytes(value: [u8; 2]) -> Self {
|
||||||
Self {
|
Self { lh: [Wrapping(value[0]), Wrapping(value[1])] }
|
||||||
lh: [Wrapping(value[0]), Wrapping(value[1])],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for SplitRegister {
|
impl Debug for SplitRegister {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "[{:02x}, {:02x}]", self.lo(), self.hi())
|
write!(f, "[{:02x}, {:02x}]", self.lo(), self.hi())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SplitRegister {
|
impl Default for SplitRegister {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self { r16: Wrapping(0) }
|
Self { r16: Wrapping(0) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for SplitRegister {
|
impl PartialEq for SplitRegister {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.wide().eq(other.wide())
|
self.wide().eq(other.wide())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for SplitRegister {}
|
impl Eq for SplitRegister {}
|
||||||
|
|
||||||
impl PartialOrd for SplitRegister {
|
impl PartialOrd for SplitRegister {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
Some(self.cmp(other))
|
Some(self.cmp(other))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ord for SplitRegister {
|
impl Ord for SplitRegister {
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
self.wide().cmp(other.wide())
|
self.wide().cmp(other.wide())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for SplitRegister {
|
impl Hash for SplitRegister {
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
self.wide().hash(state)
|
self.wide().hash(state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<u16> for SplitRegister {
|
impl From<u16> for SplitRegister {
|
||||||
fn from(value: u16) -> Self {
|
fn from(value: u16) -> Self {
|
||||||
Self {
|
Self { r16: Wrapping(value) }
|
||||||
r16: Wrapping(value),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Wrapping<u16>> for SplitRegister {
|
impl From<Wrapping<u16>> for SplitRegister {
|
||||||
fn from(value: Wrapping<u16>) -> Self {
|
fn from(value: Wrapping<u16>) -> Self {
|
||||||
Self { r16: value }
|
Self { r16: value }
|
||||||
@ -158,6 +169,7 @@ macro impl_ops(for $base:ident {$($trait:ident<$($ty:ty),*$(,)?> $func:ident()),
|
|||||||
})*
|
})*
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_ops!(for SplitRegister {
|
impl_ops!(for SplitRegister {
|
||||||
Mul<u8, u16> mul(),
|
Mul<u8, u16> mul(),
|
||||||
Div<u8, u16> div(),
|
Div<u8, u16> div(),
|
||||||
@ -171,18 +183,16 @@ impl_ops!(for SplitRegister {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn low_is_low() {
|
fn low_is_low() {
|
||||||
let reg = SplitRegister {
|
let reg = SplitRegister { r16: Wrapping(0x00ff) };
|
||||||
r16: Wrapping(0x00ff),
|
|
||||||
};
|
|
||||||
assert_eq!(*reg.lo(), Wrapping(0xff))
|
assert_eq!(*reg.lo(), Wrapping(0xff))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hi_is_hi() {
|
fn hi_is_hi() {
|
||||||
let reg = SplitRegister {
|
let reg = SplitRegister { r16: Wrapping(0xff00) };
|
||||||
r16: Wrapping(0xff00),
|
|
||||||
};
|
|
||||||
assert_eq!(*reg.hi(), Wrapping(0xff))
|
assert_eq!(*reg.hi(), Wrapping(0xff))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_bytes() {
|
fn from_bytes() {
|
||||||
let be = SplitRegister::from_be_bytes([0xc5, 0x77]);
|
let be = SplitRegister::from_be_bytes([0xc5, 0x77]);
|
||||||
|
@ -30,6 +30,7 @@ pub enum Mode {
|
|||||||
DMG,
|
DMG,
|
||||||
CGB,
|
CGB,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mode {
|
impl Mode {
|
||||||
pub fn from_bits(value: u8) -> Self {
|
pub fn from_bits(value: u8) -> Self {
|
||||||
match value & 0x40 {
|
match value & 0x40 {
|
||||||
@ -37,6 +38,7 @@ impl Mode {
|
|||||||
_ => Self::CGB,
|
_ => Self::CGB,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_bits(self) -> u8 {
|
pub fn to_bits(self) -> u8 {
|
||||||
(self as u8) << 7
|
(self as u8) << 7
|
||||||
}
|
}
|
||||||
@ -124,6 +126,7 @@ impl Bus {
|
|||||||
hram: [0; 128],
|
hram: [0; 128],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mmio_read(&self, addr: usize) -> Option<u8> {
|
pub fn mmio_read(&self, addr: usize) -> Option<u8> {
|
||||||
match addr {
|
match addr {
|
||||||
// Joypad I/O
|
// Joypad I/O
|
||||||
@ -306,6 +309,7 @@ mod cart {
|
|||||||
Self::new(value)
|
Self::new(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&[u8]> for Cart {
|
impl From<&[u8]> for Cart {
|
||||||
fn from(value: &[u8]) -> Self {
|
fn from(value: &[u8]) -> Self {
|
||||||
Self::new(Vec::from(value))
|
Self::new(Vec::from(value))
|
||||||
@ -366,8 +370,10 @@ pub mod mapper {
|
|||||||
|
|
||||||
pub mod no_mbc {
|
pub mod no_mbc {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
pub struct NoMBC;
|
pub struct NoMBC;
|
||||||
|
|
||||||
impl Mapper for NoMBC {
|
impl Mapper for NoMBC {
|
||||||
fn read(&self, addr: usize) -> Response {
|
fn read(&self, addr: usize) -> Response {
|
||||||
match addr {
|
match addr {
|
||||||
|
@ -13,10 +13,12 @@ impl<const BASE: usize, const LEN: usize, const BANKS: usize> Banked<BASE, LEN,
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Selects a bank between 0 and `LEN` - 1, saturating
|
/// Selects a bank between 0 and `LEN` - 1, saturating
|
||||||
pub fn select(&mut self, bank: usize) {
|
pub fn select(&mut self, bank: usize) {
|
||||||
self.selected = bank.min(BANKS - 1)
|
self.selected = bank.min(BANKS - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn selected(&self) -> usize {
|
pub fn selected(&self) -> usize {
|
||||||
self.selected
|
self.selected
|
||||||
}
|
}
|
||||||
@ -79,10 +81,12 @@ impl<const BASE: usize, const LEN: usize, const BANKS: usize> UpperBanked<BASE,
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Selects a bank between 1 and `LEN` - 1, saturating to `LEN` - 1
|
/// Selects a bank between 1 and `LEN` - 1, saturating to `LEN` - 1
|
||||||
pub fn select(&mut self, bank: usize) {
|
pub fn select(&mut self, bank: usize) {
|
||||||
self.selected = bank.clamp(1, BANKS - 1)
|
self.selected = bank.clamp(1, BANKS - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the currently selected upper bank
|
/// Gets the currently selected upper bank
|
||||||
pub fn selected(&self) -> usize {
|
pub fn selected(&self) -> usize {
|
||||||
self.selected
|
self.selected
|
||||||
@ -159,6 +163,7 @@ mod test {
|
|||||||
assert_eq!(idx, *data as usize)
|
assert_eq!(idx, *data as usize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn upper_banked_bounds() {
|
fn upper_banked_bounds() {
|
||||||
let ub = UpperBanked::<0, 8, 32>::new();
|
let ub = UpperBanked::<0, 8, 32>::new();
|
||||||
|
@ -8,6 +8,7 @@ pub mod iter {
|
|||||||
start: usize,
|
start: usize,
|
||||||
bus: &'a dyn BusIO,
|
bus: &'a dyn BusIO,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iter<'a> {
|
impl<'a> Iter<'a> {
|
||||||
pub fn new(bus: &'a dyn BusIO) -> Self {
|
pub fn new(bus: &'a dyn BusIO) -> Self {
|
||||||
Self { start: 0, bus }
|
Self { start: 0, bus }
|
||||||
@ -16,8 +17,10 @@ pub mod iter {
|
|||||||
Self { start, bus }
|
Self { start, bus }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for Iter<'a> {
|
impl<'a> Iterator for Iter<'a> {
|
||||||
type Item = u8;
|
type Item = u8;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
let Self { start, bus } = self;
|
let Self { start, bus } = self;
|
||||||
let out = bus.read(*start);
|
let out = bus.read(*start);
|
||||||
@ -34,6 +37,7 @@ pub trait BusIO {
|
|||||||
// required functions
|
// required functions
|
||||||
/// Attempts to read a byte from self, returning None if the operation failed.
|
/// Attempts to read a byte from self, returning None if the operation failed.
|
||||||
fn read(&self, addr: usize) -> Option<u8>;
|
fn read(&self, addr: usize) -> Option<u8>;
|
||||||
|
|
||||||
/// Attempts to write a byte to self, returning None if the operation failed.
|
/// Attempts to write a byte to self, returning None if the operation failed.
|
||||||
fn write(&mut self, addr: usize, data: u8) -> Option<()>;
|
fn write(&mut self, addr: usize, data: u8) -> Option<()>;
|
||||||
|
|
||||||
@ -47,6 +51,7 @@ pub trait BusIO {
|
|||||||
{
|
{
|
||||||
iter::Iter::new(self)
|
iter::Iter::new(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets an iterator which reads bytes in an exclusive range
|
/// Gets an iterator which reads bytes in an exclusive range
|
||||||
fn read_iter_from(&self, start: usize) -> iter::Iter
|
fn read_iter_from(&self, start: usize) -> iter::Iter
|
||||||
where
|
where
|
||||||
@ -60,6 +65,7 @@ impl<const N: usize> BusIO for [u8; N] {
|
|||||||
fn read(&self, addr: usize) -> Option<u8> {
|
fn read(&self, addr: usize) -> Option<u8> {
|
||||||
self.get(addr).copied()
|
self.get(addr).copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write(&mut self, addr: usize, data: u8) -> Option<()> {
|
fn write(&mut self, addr: usize, data: u8) -> Option<()> {
|
||||||
self.get_mut(addr).map(|v| *v = data)
|
self.get_mut(addr).map(|v| *v = data)
|
||||||
}
|
}
|
||||||
@ -69,6 +75,7 @@ impl BusIO for [u8] {
|
|||||||
fn read(&self, addr: usize) -> Option<u8> {
|
fn read(&self, addr: usize) -> Option<u8> {
|
||||||
self.get(addr).copied()
|
self.get(addr).copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write(&mut self, addr: usize, data: u8) -> Option<()> {
|
fn write(&mut self, addr: usize, data: u8) -> Option<()> {
|
||||||
self.get_mut(addr).map(|v| *v = data)
|
self.get_mut(addr).map(|v| *v = data)
|
||||||
}
|
}
|
||||||
@ -78,6 +85,7 @@ impl BusIO for Vec<u8> {
|
|||||||
fn read(&self, addr: usize) -> Option<u8> {
|
fn read(&self, addr: usize) -> Option<u8> {
|
||||||
self.get(addr).copied()
|
self.get(addr).copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write(&mut self, addr: usize, data: u8) -> Option<()> {
|
fn write(&mut self, addr: usize, data: u8) -> Option<()> {
|
||||||
self.get_mut(addr).map(|v| *v = data)
|
self.get_mut(addr).map(|v| *v = data)
|
||||||
}
|
}
|
||||||
@ -97,14 +105,17 @@ pub trait BusAux: BusIO {
|
|||||||
}
|
}
|
||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the cart entrypoint bytes (machine code starting at 0x100)
|
/// Gets the cart entrypoint bytes (machine code starting at 0x100)
|
||||||
fn entry(&self) -> Result<[u8; 4], [u8; 4]> {
|
fn entry(&self) -> Result<[u8; 4], [u8; 4]> {
|
||||||
self.read_arr(0x100)
|
self.read_arr(0x100)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the logo bytes (0x104..0x134)
|
/// Gets the logo bytes (0x104..0x134)
|
||||||
fn logo(&self) -> Result<[u8; 0x30], [u8; 0x30]> {
|
fn logo(&self) -> Result<[u8; 0x30], [u8; 0x30]> {
|
||||||
self.read_arr(0x104)
|
self.read_arr(0x104)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the title bytes as a String
|
/// Gets the title bytes as a String
|
||||||
/// # Note
|
/// # Note
|
||||||
/// The title area was repartitioned for other purposes during the Game Boy's life.
|
/// The title area was repartitioned for other purposes during the Game Boy's life.
|
||||||
@ -119,31 +130,36 @@ pub trait BusAux: BusIO {
|
|||||||
.map(|c| unsafe { char::from_u32_unchecked(c as u32) })
|
.map(|c| unsafe { char::from_u32_unchecked(c as u32) })
|
||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the manufacturer code bytes
|
/// Gets the manufacturer code bytes
|
||||||
fn mfcode(&self) -> Result<[u8; 4], [u8; 4]> {
|
fn mfcode(&self) -> Result<[u8; 4], [u8; 4]> {
|
||||||
self.read_arr(0x13f)
|
self.read_arr(0x13f)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the CGB Flag byte
|
/// Gets the CGB Flag byte
|
||||||
fn cgbflag(&self) -> Option<u8> {
|
fn cgbflag(&self) -> Option<u8> {
|
||||||
self.read(0x143)
|
self.read(0x143)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the New Licensee bytes
|
/// Gets the New Licensee bytes
|
||||||
fn newlicensee(&self) -> Result<[u8; 2], [u8; 2]> {
|
fn newlicensee(&self) -> Result<[u8; 2], [u8; 2]> {
|
||||||
self.read_arr(0x144)
|
self.read_arr(0x144)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the SGB Flag byte
|
/// Gets the SGB Flag byte
|
||||||
fn sgbflag(&self) -> Option<u8> {
|
fn sgbflag(&self) -> Option<u8> {
|
||||||
self.read(0x146)
|
self.read(0x146)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the Cartridge Type byte
|
/// Gets the Cartridge Type byte
|
||||||
fn carttype(&self) -> Option<u8> {
|
fn carttype(&self) -> Option<u8> {
|
||||||
self.read(0x147)
|
self.read(0x147)
|
||||||
}
|
}
|
||||||
/// Gets the size of the cartridge ROM (in 0x8000 byte banks)
|
|
||||||
fn romsize(&self) -> Option<usize> {
|
fn romsize(&self) -> Option<usize> {
|
||||||
self.read(0x148).map(|s| 1usize.wrapping_shl(s as u32 + 1))
|
self.read(0x148).map(|s| 1usize.wrapping_shl(s as u32 + 1))
|
||||||
}
|
}
|
||||||
/// Gets the size of the cartridge RAM (in 0x8000 byte banks)
|
|
||||||
fn ramsize(&self) -> Option<usize> {
|
fn ramsize(&self) -> Option<usize> {
|
||||||
self.read(0x149).map(|s| match s {
|
self.read(0x149).map(|s| match s {
|
||||||
0x02 => 1,
|
0x02 => 1,
|
||||||
@ -153,22 +169,27 @@ pub trait BusAux: BusIO {
|
|||||||
_ => 0,
|
_ => 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the destination region code
|
/// Gets the destination region code
|
||||||
fn destcode(&self) -> Option<u8> {
|
fn destcode(&self) -> Option<u8> {
|
||||||
self.read(0x14a)
|
self.read(0x14a)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the old licensee byte
|
/// Gets the old licensee byte
|
||||||
fn oldlicencee(&self) -> Option<u8> {
|
fn oldlicencee(&self) -> Option<u8> {
|
||||||
self.read(0x14b)
|
self.read(0x14b)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the Maskrom Version byte. Usually 0
|
/// Gets the Maskrom Version byte. Usually 0
|
||||||
fn maskromver(&self) -> Option<u8> {
|
fn maskromver(&self) -> Option<u8> {
|
||||||
self.read(0x14c)
|
self.read(0x14c)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the header checksum
|
/// Gets the header checksum
|
||||||
fn headercksum(&self) -> Option<u8> {
|
fn headercksum(&self) -> Option<u8> {
|
||||||
self.read(0x14d)
|
self.read(0x14d)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the checksum of all bytes in ROM
|
/// Gets the checksum of all bytes in ROM
|
||||||
fn globalcksum(&self) -> Option<u16> {
|
fn globalcksum(&self) -> Option<u16> {
|
||||||
self.read_arr(0x14e).ok().map(u16::from_be_bytes)
|
self.read_arr(0x14e).ok().map(u16::from_be_bytes)
|
||||||
|
Loading…
Reference in New Issue
Block a user