Compare commits
2 Commits
d6bec3fa88
...
34de965cd5
| Author | SHA1 | Date |
|---|---|---|
|
|
34de965cd5 | |
|
|
0638bcf635 |
172
src/emu.rs
172
src/emu.rs
|
|
@ -21,7 +21,25 @@ pub struct GameBoy {
|
|||
bp_manager: BreakPointManager,
|
||||
}
|
||||
|
||||
fn add(a: u8, b: u8) -> u8 {
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
enum Flag {
|
||||
Z, N, H ,CY
|
||||
}
|
||||
|
||||
fn flag_set(f: Flag, v: u8) -> bool {
|
||||
use Flag::*;
|
||||
if f == CY || f == H || f == N {
|
||||
println!("FIXME reading unimplemented flag {f:?}");
|
||||
}
|
||||
match f {
|
||||
Z => v << 0 >> 7 == 1,
|
||||
N => v << 1 >> 7 == 1,
|
||||
H => v << 2 >> 7 == 1,
|
||||
CY => v << 3 >> 7 == 1,
|
||||
}
|
||||
}
|
||||
|
||||
fn add_noflag(a: u8, b: u8) -> u8 {
|
||||
if std::u8::MAX - b < a {
|
||||
((a as u16 + b as u16) - std::u8::MAX as u16) as u8 - 1
|
||||
} else {
|
||||
|
|
@ -29,7 +47,7 @@ fn add(a: u8, b: u8) -> u8 {
|
|||
}
|
||||
}
|
||||
|
||||
fn sub(a: u8, b: u8) -> u8 {
|
||||
fn sub_noflag(a: u8, b: u8) -> u8 {
|
||||
if a < b {
|
||||
std::u8::MAX - (b - a) + 1
|
||||
} else {
|
||||
|
|
@ -38,25 +56,11 @@ fn sub(a: u8, b: u8) -> u8 {
|
|||
}
|
||||
|
||||
fn add_inplace(a: &mut u8, b: u8) {
|
||||
*a = add(*a, b)
|
||||
*a = add_noflag(*a, b)
|
||||
}
|
||||
|
||||
fn sub_inplace(a: &mut u8, b: u8) {
|
||||
*a = sub(*a, b)
|
||||
}
|
||||
|
||||
enum Flag {
|
||||
Z, N, H ,CY
|
||||
}
|
||||
|
||||
fn flag_set(f: Flag, v: u8) -> bool {
|
||||
use Flag::*;
|
||||
match f {
|
||||
Z => v << 0 >> 7 == 1,
|
||||
N => v << 1 >> 6 == 1,
|
||||
H => v << 2 >> 5 == 1,
|
||||
CY => v << 3 >> 4 == 1,
|
||||
}
|
||||
*a = sub_noflag(*a, b)
|
||||
}
|
||||
|
||||
impl GameBoy {
|
||||
|
|
@ -73,6 +77,31 @@ impl GameBoy {
|
|||
}
|
||||
|
||||
|
||||
fn add(&mut self, a: u8, b: u8) -> u8 {
|
||||
let val = add_noflag(a, b);
|
||||
println!("{}", val);
|
||||
|
||||
self.set_flag(Flag::Z, val == 0);
|
||||
self.set_flag(Flag::N, false);
|
||||
self.set_flag(Flag::H, false); // FIXME
|
||||
self.set_flag(Flag::CY, false); // FIXME
|
||||
|
||||
val
|
||||
}
|
||||
|
||||
fn sub(&mut self, a: u8, b: u8) -> u8 {
|
||||
let val = sub_noflag(a, b);
|
||||
|
||||
self.set_flag(Flag::Z, val == 0);
|
||||
self.set_flag(Flag::N, true);
|
||||
self.set_flag(Flag::H, false); // FIXME
|
||||
self.set_flag(Flag::CY, false); // FIXME
|
||||
|
||||
val
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn set(adr: u16, val: u8) {
|
||||
// check for special registers
|
||||
}
|
||||
|
|
@ -80,12 +109,13 @@ impl GameBoy {
|
|||
fn set_flag(&mut self, f: Flag, v: bool) {
|
||||
let r = &mut self.reg[Register::F];
|
||||
let v = v as u8;
|
||||
//println!("setting {f:?}: {v}");
|
||||
use Flag::*;
|
||||
match f {
|
||||
Z => *r = v << 7,
|
||||
N => *r = v << 6,
|
||||
H => *r = v << 5,
|
||||
CY => *r = v << 4
|
||||
Z => *r = (v << 7) | (*r & 0b0111_1111),
|
||||
N => *r = (v << 6) | (*r & 0b1011_1111),
|
||||
H => *r = (v << 5) | (*r & 0b1101_1111),
|
||||
CY => *r = (v << 4) | (*r & 0b1110_1111),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -93,6 +123,15 @@ impl GameBoy {
|
|||
// no checks for special registers
|
||||
}
|
||||
|
||||
fn get_value(&mut self, src: ValSrc) -> u8 {
|
||||
match src {
|
||||
ValSrc::Direct => self.mem[self.pc as usize + 1],
|
||||
ValSrc::Reg(_) => todo!(),
|
||||
ValSrc::Mem(_) => todo!(),
|
||||
ValSrc::HMem(_) => todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load<T: io::Read>(&mut self, mut src: T) {
|
||||
let mut buf = Vec::<u8>::new();
|
||||
let _ = src.read_to_end(&mut buf);
|
||||
|
|
@ -172,7 +211,14 @@ impl GameBoy {
|
|||
}
|
||||
AddrLoc::Direct => todo!(),
|
||||
}
|
||||
ValSrc::HMem(_) => todo!(),
|
||||
ValSrc::HMem(ref src) => match src {
|
||||
AddrLoc::Direct => {
|
||||
println!("FIXME reading from hmem, possibly using an unimplemented feature");
|
||||
let offs = self.mem[pc + 1];
|
||||
(&mut self.mem[0xFF00 + offs as usize], 0)
|
||||
}
|
||||
AddrLoc::Reg(_) => todo!()
|
||||
}
|
||||
};
|
||||
|
||||
let val = *src_ref;
|
||||
|
|
@ -278,7 +324,8 @@ impl GameBoy {
|
|||
BpCommand::Kill => panic!("kill from breakpoint"),
|
||||
};
|
||||
|
||||
println!("pc:{pc:#08x}:{win:02x?} {op:?}");
|
||||
let regs = &self.reg[..8];
|
||||
println!("\u{1B}[1;31mpc:{pc:#08x}\u{1B}[0m:{win:02x?} {op:?} {regs:?}");
|
||||
|
||||
match op.inst {
|
||||
Unimpl(instr) => {
|
||||
|
|
@ -318,6 +365,8 @@ impl GameBoy {
|
|||
println!("jr {new_pc:#06x} <- {old_pc:#06x} + {e}");
|
||||
// TODO remember cycle count 3 for jump, 2 for not!!!
|
||||
return;
|
||||
} else {
|
||||
println!("jr cond not met");
|
||||
}
|
||||
}
|
||||
Call => {
|
||||
|
|
@ -355,18 +404,25 @@ impl GameBoy {
|
|||
}
|
||||
Inc(dst) => {
|
||||
match dst {
|
||||
ValSrc::Reg(reg) => add_inplace(&mut self.reg[reg], 1),
|
||||
ValSrc::Reg(reg) => self.reg[reg] = self.add(self.reg[reg], 1),
|
||||
_ => todo!()
|
||||
}
|
||||
println!("Inc {dst:?}");
|
||||
}
|
||||
Dec(dst) => {
|
||||
match dst {
|
||||
ValSrc::Reg(reg) => sub_inplace(&mut self.reg[reg], 1),
|
||||
ValSrc::Reg(reg) => self.reg[reg] = self.sub(self.reg[reg], 1),
|
||||
_ => todo!()
|
||||
}
|
||||
println!("Dec {dst:?}");
|
||||
}
|
||||
Cmp(src) => {
|
||||
let a = self.reg[Register::A];
|
||||
let b = self.get_value(src);
|
||||
self.set_flag(Flag::N, true);
|
||||
if a == b {
|
||||
}
|
||||
}
|
||||
Ld(dst, src, post_op) => self.op_ld(dst, src, post_op),
|
||||
Ld16(dst, src) => self.op_ld16(dst, src),
|
||||
Bit(bit_op, src) => {
|
||||
|
|
@ -402,25 +458,55 @@ impl GameBoy {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
// #[test]
|
||||
// fn add_overflow() {
|
||||
// use crate::emu::add;
|
||||
// assert_eq!(add(1, 1), 2);
|
||||
#[test]
|
||||
fn add_overflow() {
|
||||
let mut gb = crate::emu::GameBoy::new();
|
||||
|
||||
// assert_eq!(add(std::u8::MAX, 1), 0);
|
||||
// assert_eq!(add(std::u8::MAX, 2), 1);
|
||||
// assert_eq!(add(1, std::u8::MAX), 0);
|
||||
assert_eq!(gb.add(1, 1), 2);
|
||||
|
||||
// assert_eq!(add(std::u8::MAX - 3, 3), std::u8::MAX);
|
||||
// assert_eq!(add(3, std::u8::MAX - 3), std::u8::MAX);
|
||||
// }
|
||||
assert_eq!(gb.add(std::u8::MAX, 1), 0);
|
||||
assert_eq!(gb.add(std::u8::MAX, 2), 1);
|
||||
assert_eq!(gb.add(1, std::u8::MAX), 0);
|
||||
|
||||
// #[test]
|
||||
// fn sub_underflow() {
|
||||
// use crate::emu::sub;
|
||||
// assert_eq!(sub(1, 1), 0);
|
||||
// assert_eq!(sub(1, 2), std::u8::MAX);
|
||||
// assert_eq!(sub(1, 3), std::u8::MAX - 1);
|
||||
// }
|
||||
assert_eq!(gb.add(std::u8::MAX - 3, 3), std::u8::MAX);
|
||||
assert_eq!(gb.add(3, std::u8::MAX - 3), std::u8::MAX);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sub_underflow() {
|
||||
let mut gb = crate::emu::GameBoy::new();
|
||||
|
||||
assert_eq!(gb.sub(1, 1), 0);
|
||||
assert_eq!(gb.sub(1, 2), std::u8::MAX);
|
||||
assert_eq!(gb.sub(1, 3), std::u8::MAX - 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flag_check() {
|
||||
let mut gb = crate::emu::GameBoy::new();
|
||||
|
||||
use crate::emu::*;
|
||||
|
||||
for f in [Flag::Z, Flag::N, Flag::H, Flag::CY] {
|
||||
assert!(!flag_set(f, gb.reg[Register::F]));
|
||||
gb.set_flag(f, true);
|
||||
assert!(flag_set(f, gb.reg[Register::F]));
|
||||
gb.set_flag(f, false);
|
||||
assert!(!flag_set(f, gb.reg[Register::F]));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flag_set() {
|
||||
use crate::emu::*;
|
||||
|
||||
let mut gb = GameBoy::new();
|
||||
|
||||
assert!(!flag_set(Flag::Z, gb.reg[Register::F]));
|
||||
gb.add(std::u8::MAX - 1 , 1);
|
||||
assert!(!flag_set(Flag::Z, gb.reg[Register::F]));
|
||||
gb.add(std::u8::MAX, 1);
|
||||
assert!(flag_set(Flag::Z, gb.reg[Register::F]));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ pub enum Instr {
|
|||
Inc(ValSrc),
|
||||
Dec(ValSrc),
|
||||
Bit(BitOp, ValSrc),
|
||||
Cmp(ValSrc),
|
||||
|
||||
// CPU ctl
|
||||
Di,
|
||||
|
|
@ -209,6 +210,8 @@ impl TryFrom<u8> for Operation {
|
|||
(Ld(Mem(AddrLoc::Direct), Reg(A), PostOp::None), 4, 3),
|
||||
0b11_100_000 =>
|
||||
(Ld(HMem(AddrLoc::Direct), Reg(A), PostOp::None), 3, 2),
|
||||
0b11_110_000 =>
|
||||
(Ld(Reg(A), HMem(AddrLoc::Direct), PostOp::None), 3, 2),
|
||||
0b00_100_010 =>
|
||||
(Ld(Mem(AddrLoc::Reg(HL)), Reg(A), PostOp::Inc(HL)), 2, 1),
|
||||
0b00_101_010 =>
|
||||
|
|
@ -217,6 +220,8 @@ impl TryFrom<u8> for Operation {
|
|||
(Ld(Mem(AddrLoc::Reg(HL)), Reg(A), PostOp::Dec(HL)), 2, 1),
|
||||
0b00_111_010 =>
|
||||
(Ld(Reg(A), Mem(AddrLoc::Reg(HL)), PostOp::Dec(HL)), 2, 1),
|
||||
0b10_111_110 => (Cmp(Mem(AddrLoc::Reg(HL))), 2, 1),
|
||||
0b11_111_110 => (Cmp(Direct), 2, 2),
|
||||
0b10_110_110 => (Bit(Or, Mem(AddrLoc::Reg(HL))), 2, 1),
|
||||
0b10_101_110 => (Bit(Xor, Mem(AddrLoc::Reg(HL))), 2, 1),
|
||||
0b10_100_110 => (Bit(And, Mem(AddrLoc::Reg(HL))), 2, 1),
|
||||
|
|
@ -251,6 +256,8 @@ impl TryFrom<u8> for Operation {
|
|||
(Dec(Reg(Register::try_from((value & T1_MSK) >> 3)?)), 1, 1)
|
||||
}
|
||||
}
|
||||
_ if (value & (HD_MSK | T1_MSK) == 0b10_111_000) =>
|
||||
(Cmp(Reg(Register::try_from(value & T2_MSK)?)), 1, 1),
|
||||
_ if (value & (HD_MSK | 0b1_000 | T2_MSK) == 0b11_000_101) =>
|
||||
(Psh(Register::repr_psh(payload(value))), 4, 1),
|
||||
_ if (value & (HD_MSK | 0b1_000 | T2_MSK) == 0b11_000_001) =>
|
||||
|
|
@ -274,6 +281,7 @@ impl TryFrom<u8> for Operation {
|
|||
};
|
||||
(Ld16(Reg(reg), Direct), 3, 3)
|
||||
}
|
||||
|
||||
_ => (Unimpl(value), 0, 0),
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue