implement more inc/dec variants and conditional jump
/ build (push) Successful in 23s
Details
/ build (push) Successful in 23s
Details
This commit is contained in:
parent
74b823aad6
commit
3e183d51ec
120
src/emu.rs
120
src/emu.rs
|
|
@ -21,6 +21,44 @@ pub struct GameBoy {
|
|||
bp_manager: BreakPointManager,
|
||||
}
|
||||
|
||||
fn add(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 {
|
||||
a + b
|
||||
}
|
||||
}
|
||||
|
||||
fn sub(a: u8, b: u8) -> u8 {
|
||||
if a < b {
|
||||
std::u8::MAX - (b - a) + 1
|
||||
} else {
|
||||
a - b
|
||||
}
|
||||
}
|
||||
|
||||
fn add_inplace(a: &mut u8, b: u8) {
|
||||
*a = add(*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,
|
||||
}
|
||||
}
|
||||
|
||||
impl GameBoy {
|
||||
pub fn new() -> GameBoy {
|
||||
GameBoy {
|
||||
|
|
@ -34,10 +72,23 @@ impl GameBoy {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
fn set(adr: u16, val: u8) {
|
||||
// check for special registers
|
||||
}
|
||||
|
||||
fn set_flag(&mut self, f: Flag, v: bool) {
|
||||
let r = &mut self.reg[Register::F];
|
||||
let v = v as u8;
|
||||
use Flag::*;
|
||||
match f {
|
||||
Z => *r = v << 7,
|
||||
N => *r = v << 6,
|
||||
H => *r = v << 5,
|
||||
CY => *r = v << 4
|
||||
}
|
||||
}
|
||||
|
||||
fn set_fast(adr: u16, val: u8) {
|
||||
// no checks for special registers
|
||||
}
|
||||
|
|
@ -157,8 +208,8 @@ impl GameBoy {
|
|||
|
||||
match postOp {
|
||||
PostOp::None => (),
|
||||
PostOp::Inc(reg) => self.reg[reg] += 1,
|
||||
PostOp::Dec(reg) => self.reg[reg] -= 1,
|
||||
PostOp::Inc(reg) => add_inplace(&mut self.reg[reg], 1),
|
||||
PostOp::Dec(reg) => sub_inplace(&mut self.reg[reg], 1),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -241,16 +292,34 @@ impl GameBoy {
|
|||
let new_pc = self.read16(pc+1);
|
||||
self.pc = new_pc;
|
||||
println!("jp {pc:#06x}->{new_pc:#06x}");
|
||||
// TODO remember cycle count!
|
||||
return;
|
||||
}
|
||||
Jr => {
|
||||
Jr(cond) => {
|
||||
let jump = match cond {
|
||||
JmpCond::None => true,
|
||||
JmpCond::Flags(f) => {
|
||||
let reg_f = self.reg[Register::F];
|
||||
match f {
|
||||
0b00 => !flag_set(Flag::Z, reg_f),
|
||||
0b01 => flag_set(Flag::Z, reg_f),
|
||||
0b10 => !flag_set(Flag::CY, reg_f),
|
||||
0b11 => flag_set(Flag::CY, reg_f),
|
||||
_ => panic!("invalid jump condition")
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if jump {
|
||||
let e = self.mem[pc + 1] as i8 + 2;
|
||||
let old_pc = self.pc;
|
||||
let new_pc = (old_pc as i16 + e as i16) as u16;
|
||||
self.pc = new_pc;
|
||||
println!("jr {new_pc:#06x} <- {old_pc:#06x} + {e}");
|
||||
// TODO remember cycle count 3 for jump, 2 for not!!!
|
||||
return;
|
||||
}
|
||||
}
|
||||
Call => {
|
||||
let sp = self.sp as usize;
|
||||
self.mem[sp - 1] = ((pc+op.offs) >> 8) as u8;
|
||||
|
|
@ -284,15 +353,21 @@ impl GameBoy {
|
|||
self.sp += 2;
|
||||
println!("pop Reg({reg:#04b})");
|
||||
}
|
||||
Inc(reg) => {
|
||||
self.reg[reg] += 1;
|
||||
println!("Inc Reg({reg:#04b})");
|
||||
Inc(dst) => {
|
||||
match dst {
|
||||
ValSrc::Reg(reg) => add_inplace(&mut self.reg[reg], 1),
|
||||
_ => todo!()
|
||||
}
|
||||
Dec(reg) => {
|
||||
self.reg[reg as usize] -= 1;
|
||||
println!("Dec Reg({reg:#04b})");
|
||||
println!("Inc {dst:?}");
|
||||
}
|
||||
Ld(dst, src, postOp) => self.op_ld(dst, src, postOp),
|
||||
Dec(dst) => {
|
||||
match dst {
|
||||
ValSrc::Reg(reg) => sub_inplace(&mut self.reg[reg], 1),
|
||||
_ => todo!()
|
||||
}
|
||||
println!("Dec {dst:?}");
|
||||
}
|
||||
Ld(dst, src, post_op) => self.op_ld(dst, src, post_op),
|
||||
Ld16(dst, src) => self.op_ld16(dst, src),
|
||||
Bit(bit_op, src) => {
|
||||
let v = match src {
|
||||
|
|
@ -324,3 +399,28 @@ impl GameBoy {
|
|||
self.pc = pc as u16;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
// #[test]
|
||||
// fn add_overflow() {
|
||||
// use crate::emu::add;
|
||||
// assert_eq!(add(1, 1), 2);
|
||||
|
||||
// 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!(add(std::u8::MAX - 3, 3), std::u8::MAX);
|
||||
// assert_eq!(add(3, std::u8::MAX - 3), std::u8::MAX);
|
||||
// }
|
||||
|
||||
// #[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);
|
||||
// }
|
||||
}
|
||||
|
||||
|
|
|
|||
36
src/op.rs
36
src/op.rs
|
|
@ -26,8 +26,8 @@ pub enum Instr {
|
|||
|
||||
// Arith
|
||||
// TODO can we do Inc16/Inc8 with one via Register::WIDE
|
||||
Inc(Register),
|
||||
Dec(Register),
|
||||
Inc(ValSrc),
|
||||
Dec(ValSrc),
|
||||
Bit(BitOp, ValSrc),
|
||||
|
||||
// CPU ctl
|
||||
|
|
@ -35,7 +35,7 @@ pub enum Instr {
|
|||
|
||||
// Jump
|
||||
Jp,
|
||||
Jr,
|
||||
Jr(JmpCond),
|
||||
Call,
|
||||
Ret,
|
||||
}
|
||||
|
|
@ -61,6 +61,12 @@ pub enum ValSrc {
|
|||
Direct,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum JmpCond {
|
||||
None,
|
||||
Flags(u8)
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum Register {
|
||||
A, F,
|
||||
|
|
@ -195,7 +201,7 @@ impl TryFrom<u8> for Operation {
|
|||
let (inst, cycl, offs) = match value {
|
||||
0b00_000_000 => (Nop, 1, 1),
|
||||
0b11_000_011 => (Jp, 4, 3),
|
||||
0b00_011_000 => (Jr, 3, 2),
|
||||
0b00_011_000 => (Jr(JmpCond::None), 3, 2),
|
||||
0b11_001_101 => (Call, 6, 3),
|
||||
0b11_001_001 => (Ret, 4, 1),
|
||||
0b11_110_011 => (Di, 1, 1),
|
||||
|
|
@ -217,6 +223,10 @@ impl TryFrom<u8> for Operation {
|
|||
0b11_110_110 => (Bit(Or, Direct), 2, 2), // TODO can bit be grouped via HD & T2?
|
||||
0b11_101_110 => (Bit(Xor, Direct), 2, 2),
|
||||
0b11_100_110 => (Bit(And, Direct), 2, 2),
|
||||
_ if value & (HD_MSK | 0b100_000 | T2_MSK) == 0b00_100_000 => {
|
||||
let cc = (value & 0b11_000) >> 3;
|
||||
(Jr(JmpCond::Flags(cc)), 3, 2) // TODO cycle can be 2 or 3!
|
||||
}
|
||||
_ if (value & (HD_MSK | T1_MSK) == 0b10_110_000) => {
|
||||
(Bit(Or, Reg(Register::try_from(value & 0b111)?)), 1, 1)
|
||||
}
|
||||
|
|
@ -227,10 +237,24 @@ impl TryFrom<u8> for Operation {
|
|||
(Bit(And, Reg(Register::try_from(value & 0b111)?)), 1, 1)
|
||||
}
|
||||
_ if (value & (HD_MSK | 0b1_000 | T2_MSK) == 0b00_000_011) => {
|
||||
(Inc(Register::from16_rep(payload(value))), 2, 1)
|
||||
(Inc(Reg(Register::from16_rep(payload(value)))), 2, 1)
|
||||
}
|
||||
_ if (value & (HD_MSK | 0b1_000 | T2_MSK) == 0b00_001_011) => {
|
||||
(Dec(Register::from16_rep(payload(value))), 2, 1)
|
||||
(Dec(Reg(Register::from16_rep(payload(value)))), 2, 1)
|
||||
}
|
||||
_ if (value & (HD_MSK | T2_MSK) == 0b00_000_100) => {
|
||||
if (T1_MSK & value) == 0b110_000 {
|
||||
(Inc(Mem(AddrLoc::Reg(HL))), 3, 1)
|
||||
} else {
|
||||
(Inc(Reg(Register::try_from((value & T1_MSK) >> 3)?)), 1, 1)
|
||||
}
|
||||
}
|
||||
_ if (value & (HD_MSK | T2_MSK) == 0b00_000_101) => {
|
||||
if (T1_MSK & value) == 0b110_000 {
|
||||
(Dec(Mem(AddrLoc::Reg(HL))), 3, 1)
|
||||
} else {
|
||||
(Dec(Reg(Register::try_from((value & T1_MSK) >> 3)?)), 1, 1)
|
||||
}
|
||||
}
|
||||
_ if (value & (HD_MSK | 0b1_000 | T2_MSK) == 0b11_000_101) => {
|
||||
(Psh(Register::repr_psh(payload(value))), 4, 1)
|
||||
|
|
|
|||
Loading…
Reference in New Issue