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