Compare commits
4 Commits
24c3880224
...
3e183d51ec
| Author | SHA1 | Date |
|---|---|---|
|
|
3e183d51ec | |
|
|
74b823aad6 | |
|
|
003daaf7a1 | |
|
|
a6e7572f72 |
203
src/emu.rs
203
src/emu.rs
|
|
@ -21,6 +21,44 @@ pub struct GameBoy {
|
||||||
bp_manager: BreakPointManager,
|
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 {
|
impl GameBoy {
|
||||||
pub fn new() -> GameBoy {
|
pub fn new() -> GameBoy {
|
||||||
GameBoy {
|
GameBoy {
|
||||||
|
|
@ -34,10 +72,23 @@ impl GameBoy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn set(adr: u16, val: u8) {
|
fn set(adr: u16, val: u8) {
|
||||||
// check for special registers
|
// 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) {
|
fn set_fast(adr: u16, val: u8) {
|
||||||
// no checks for special registers
|
// no checks for special registers
|
||||||
}
|
}
|
||||||
|
|
@ -108,69 +159,80 @@ impl GameBoy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_ld(&mut self, dst: Loc, src: Loc, modifier: i8) {
|
fn op_ld(&mut self, dst: ValSrc, src: ValSrc, postOp: PostOp) {
|
||||||
let pc = self.pc as usize;
|
let pc = self.pc as usize;
|
||||||
|
|
||||||
let (val, src_adr) = match src {
|
let (src_ref, src_adr) = match src {
|
||||||
Loc::Reg(i) => (self.reg[i as usize], i as usize),
|
ValSrc::Reg(i) => (&mut self.reg[i as usize], i as usize),
|
||||||
Loc::Val => (self.mem[pc + 1], 0),
|
ValSrc::Direct => (&mut self.mem[pc + 1], 0),
|
||||||
Loc::Mem(ref src) => match src {
|
ValSrc::Mem(ref src) => match src {
|
||||||
AddrLoc::Reg(reg) => {
|
AddrLoc::Reg(reg) => {
|
||||||
let mem_loc = self.reg[*reg] as usize;
|
let mem_loc = self.reg[*reg] as usize;
|
||||||
(self.mem[mem_loc], mem_loc)
|
(&mut self.mem[mem_loc], mem_loc)
|
||||||
}
|
}
|
||||||
AddrLoc::Mem => todo!(),
|
AddrLoc::Direct => todo!(),
|
||||||
AddrLoc::Val => todo!(),
|
|
||||||
}
|
}
|
||||||
Loc::HMem(_) => todo!(),
|
ValSrc::HMem(_) => todo!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let val = *src_ref;
|
||||||
|
let src_str = src.repr(src_adr, val);
|
||||||
|
|
||||||
let (dst_ref, dst_adr) = match dst {
|
let (dst_ref, dst_adr) = match dst {
|
||||||
Loc::Mem(ref src) => match src {
|
ValSrc::Mem(ref src) => match src {
|
||||||
AddrLoc::Val => {
|
AddrLoc::Direct => {
|
||||||
let adr = self.read16(pc) as usize;
|
let adr = self.read16(pc) as usize;
|
||||||
(&mut self.mem[adr], adr)
|
(&mut self.mem[adr], adr)
|
||||||
}
|
}
|
||||||
_ => todo!(),
|
AddrLoc::Reg(reg) => {
|
||||||
|
let adr = self.reg[*reg] as usize;
|
||||||
|
(&mut self.mem[adr], adr)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Loc::HMem(ref src) => match src {
|
ValSrc::HMem(ref src) => match src {
|
||||||
AddrLoc::Val => {
|
AddrLoc::Direct => {
|
||||||
let adr = self.mem[pc + 1] as usize + 0xFF00;
|
let adr = self.mem[pc + 1] as usize + 0xFF00;
|
||||||
(&mut self.mem[adr], adr)
|
(&mut self.mem[adr], adr)
|
||||||
}
|
}
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
},
|
},
|
||||||
Loc::Reg(r) => (&mut self.reg[r as usize], r as usize),
|
ValSrc::Reg(r) => (&mut self.reg[r as usize], r as usize),
|
||||||
Loc::Val => panic!("val as destination should not be possible"),
|
ValSrc::Direct => panic!("val as destination should not be possible"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let dst_str = dst.repr(dst_adr, *dst_ref);
|
let dst_str = dst.repr(dst_adr, *dst_ref);
|
||||||
let src_str = src.repr(src_adr, val);
|
|
||||||
println!("ld {dst_str} <- {src_str} + {modifier}");
|
|
||||||
|
|
||||||
*dst_ref = (val as i16 + modifier as i16) as u8;
|
println!("ld {dst_str} <- {src_str}; post: {postOp:?}");
|
||||||
|
|
||||||
|
*dst_ref = val;
|
||||||
|
|
||||||
|
match postOp {
|
||||||
|
PostOp::None => (),
|
||||||
|
PostOp::Inc(reg) => add_inplace(&mut self.reg[reg], 1),
|
||||||
|
PostOp::Dec(reg) => sub_inplace(&mut self.reg[reg], 1),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_ld16(&mut self, dst: Loc, src: Loc) {
|
fn op_ld16(&mut self, dst: ValSrc, src: ValSrc) {
|
||||||
let pc = self.pc as usize;
|
let pc = self.pc as usize;
|
||||||
|
|
||||||
let (l, h) = match src {
|
let (l, h) = match src {
|
||||||
Loc::Val => (self.mem[pc + 1], self.mem[pc + 2]),
|
ValSrc::Direct => (self.mem[pc + 1], self.mem[pc + 2]),
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let dst_ref = match dst {
|
let dst_ref = match dst {
|
||||||
Loc::Reg(reg) => {
|
ValSrc::Reg(reg) => {
|
||||||
// FIXME assuming DD is a typo for DE...
|
// FIXME assuming DD is a typo for DE...
|
||||||
let reg_loc = reg.into();
|
let reg_loc = reg.into();
|
||||||
&mut self.reg[reg_loc..=reg_loc + 1]
|
&mut self.reg[reg_loc..=reg_loc + 1]
|
||||||
}
|
}
|
||||||
Loc::Val => panic!("value as dst is not allowed!"),
|
ValSrc::Direct => panic!("value as dst is not allowed!"),
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
match dst {
|
match dst {
|
||||||
Loc::Reg(_) => println!("ld16 {dst:6?} <- 0x{h:02x}{l:02x}"),
|
ValSrc::Reg(_) => println!("ld16 {dst:6?} <- 0x{h:02x}{l:02x}"),
|
||||||
_ => panic!("print address"),
|
_ => panic!("print address"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -185,9 +247,9 @@ impl GameBoy {
|
||||||
(hadr << 8) + ladr
|
(hadr << 8) + ladr
|
||||||
}
|
}
|
||||||
|
|
||||||
fn store16(&mut self, loc: Loc, _loc_val: u8, h: u8, l: u8) {
|
fn store16(&mut self, loc: ValSrc, _loc_val: u8, h: u8, l: u8) {
|
||||||
match loc {
|
match loc {
|
||||||
Loc::Reg(dst_reg) => {
|
ValSrc::Reg(dst_reg) => {
|
||||||
let dst_reg: usize = dst_reg.into();
|
let dst_reg: usize = dst_reg.into();
|
||||||
self.reg[dst_reg] = h;
|
self.reg[dst_reg] = h;
|
||||||
self.reg[dst_reg + 1] = l;
|
self.reg[dst_reg + 1] = l;
|
||||||
|
|
@ -230,16 +292,34 @@ impl GameBoy {
|
||||||
let new_pc = self.read16(pc+1);
|
let new_pc = self.read16(pc+1);
|
||||||
self.pc = new_pc;
|
self.pc = new_pc;
|
||||||
println!("jp {pc:#06x}->{new_pc:#06x}");
|
println!("jp {pc:#06x}->{new_pc:#06x}");
|
||||||
|
// TODO remember cycle count!
|
||||||
return;
|
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 e = self.mem[pc + 1] as i8 + 2;
|
||||||
let old_pc = self.pc;
|
let old_pc = self.pc;
|
||||||
let new_pc = (old_pc as i16 + e as i16) as u16;
|
let new_pc = (old_pc as i16 + e as i16) as u16;
|
||||||
self.pc = new_pc;
|
self.pc = new_pc;
|
||||||
println!("jr {new_pc:#06x} <- {old_pc:#06x} + {e}");
|
println!("jr {new_pc:#06x} <- {old_pc:#06x} + {e}");
|
||||||
|
// TODO remember cycle count 3 for jump, 2 for not!!!
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Call => {
|
Call => {
|
||||||
let sp = self.sp as usize;
|
let sp = self.sp as usize;
|
||||||
self.mem[sp - 1] = ((pc+op.offs) >> 8) as u8;
|
self.mem[sp - 1] = ((pc+op.offs) >> 8) as u8;
|
||||||
|
|
@ -273,26 +353,26 @@ impl GameBoy {
|
||||||
self.sp += 2;
|
self.sp += 2;
|
||||||
println!("pop Reg({reg:#04b})");
|
println!("pop Reg({reg:#04b})");
|
||||||
}
|
}
|
||||||
Inc(reg) => {
|
Inc(dst) => {
|
||||||
self.reg[reg] += 1;
|
match dst {
|
||||||
println!("Inc Reg({reg:#04b})");
|
ValSrc::Reg(reg) => add_inplace(&mut self.reg[reg], 1),
|
||||||
|
_ => todo!()
|
||||||
}
|
}
|
||||||
Dec(reg) => {
|
println!("Inc {dst:?}");
|
||||||
self.reg[reg as usize] -= 1;
|
|
||||||
println!("Dec Reg({reg:#04b})");
|
|
||||||
}
|
}
|
||||||
Ld(dst, src) => self.op_ld(dst, src, 0),
|
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),
|
Ld16(dst, src) => self.op_ld16(dst, src),
|
||||||
LdInc(src) => {
|
Bit(bit_op, src) => {
|
||||||
self.op_ld(Loc::Reg(Register::A), src, 1)
|
|
||||||
}
|
|
||||||
LdDec(src) => {
|
|
||||||
self.op_ld(Loc::Reg(Register::A), src, -1)
|
|
||||||
}
|
|
||||||
Or(src) => {
|
|
||||||
let v = match src {
|
let v = match src {
|
||||||
Loc::Reg(r) => self.reg[r],
|
ValSrc::Reg(r) => self.reg[r],
|
||||||
Loc::Mem(ref src) => {
|
ValSrc::Mem(ref src) => {
|
||||||
let err_text = "Or Mem(Reg) only defined for register HR)";
|
let err_text = "Or Mem(Reg) only defined for register HR)";
|
||||||
let AddrLoc::Reg(src) = src else {
|
let AddrLoc::Reg(src) = src else {
|
||||||
panic!("Invalid op: {err_text}");
|
panic!("Invalid op: {err_text}");
|
||||||
|
|
@ -300,11 +380,17 @@ impl GameBoy {
|
||||||
assert!(src == &Register::HL, "{err_text}");
|
assert!(src == &Register::HL, "{err_text}");
|
||||||
self.mem[self.reg[*src] as usize]
|
self.mem[self.reg[*src] as usize]
|
||||||
},
|
},
|
||||||
Loc::Val => self.mem[pc+1],
|
ValSrc::Direct => self.mem[pc+1],
|
||||||
_ => panic!("invalid command")
|
_ => panic!("invalid command")
|
||||||
};
|
};
|
||||||
self.reg[Register::A] |= v;
|
|
||||||
println!("Or {src:?}");
|
let dst = &mut self.reg[Register::A];
|
||||||
|
match bit_op {
|
||||||
|
BitOp::Or => *dst |= v,
|
||||||
|
BitOp::And => *dst &= v,
|
||||||
|
BitOp::Xor => *dst ^= v,
|
||||||
|
}
|
||||||
|
println!("{bit_op:?} {src:?}");
|
||||||
}
|
}
|
||||||
Di => println!("FIXME Di instruction not implemented"),
|
Di => println!("FIXME Di instruction not implemented"),
|
||||||
}
|
}
|
||||||
|
|
@ -313,3 +399,28 @@ impl GameBoy {
|
||||||
self.pc = pc as u16;
|
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);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
141
src/op.rs
141
src/op.rs
|
|
@ -21,58 +21,66 @@ pub enum Instr {
|
||||||
Pop(Register),
|
Pop(Register),
|
||||||
|
|
||||||
// Load
|
// Load
|
||||||
Ld(Loc, Loc),
|
Ld(ValSrc, ValSrc, PostOp),
|
||||||
Ld16(Loc, Loc),
|
Ld16(ValSrc, ValSrc),
|
||||||
LdInc(Loc), // loads into A
|
|
||||||
LdDec(Loc), // loads into A
|
|
||||||
|
|
||||||
// Arith
|
// Arith
|
||||||
// TODO can we do Inc16/Inc8 with one via Register::WIDE
|
// TODO can we do Inc16/Inc8 with one via Register::WIDE
|
||||||
Inc(Register),
|
Inc(ValSrc),
|
||||||
Dec(Register),
|
Dec(ValSrc),
|
||||||
Or(Loc), // TODO all bit can probably be done with this one
|
Bit(BitOp, ValSrc),
|
||||||
|
|
||||||
// CPU ctl
|
// CPU ctl
|
||||||
Di,
|
Di,
|
||||||
|
|
||||||
// Jump
|
// Jump
|
||||||
Jp,
|
Jp,
|
||||||
Jr,
|
Jr(JmpCond),
|
||||||
Call,
|
Call,
|
||||||
Ret,
|
Ret,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum AddrLoc {
|
pub enum PostOp {
|
||||||
Reg(Register),
|
None,
|
||||||
Mem,
|
Inc(Register),
|
||||||
Val,
|
Dec(Register)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Loc { // TODO this should probably be called ValSrc or similar
|
pub enum AddrLoc {
|
||||||
|
Reg(Register),
|
||||||
|
Direct,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ValSrc {
|
||||||
Reg(Register), // direct
|
Reg(Register), // direct
|
||||||
Mem(AddrLoc),
|
Mem(AddrLoc),
|
||||||
HMem(AddrLoc),
|
HMem(AddrLoc),
|
||||||
Val, // TODO and then this Direct
|
Direct,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum JmpCond {
|
||||||
|
None,
|
||||||
|
Flags(u8)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
pub enum Register {
|
pub enum Register {
|
||||||
A,
|
A, F,
|
||||||
F,
|
B, C,
|
||||||
B,
|
D, E,
|
||||||
C,
|
H, L,
|
||||||
D,
|
|
||||||
E,
|
|
||||||
H,
|
|
||||||
L,
|
|
||||||
|
|
||||||
|
// Double Width
|
||||||
AF,
|
AF,
|
||||||
BC,
|
BC,
|
||||||
DE,
|
DE,
|
||||||
HL,
|
HL,
|
||||||
|
|
||||||
|
// Misc
|
||||||
SP,
|
SP,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -140,12 +148,12 @@ impl fmt::Binary for Register {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Loc {
|
impl ValSrc {
|
||||||
pub fn repr(&self, adr: usize, val: u8) -> String {
|
pub fn repr(&self, adr: usize, val: u8) -> String {
|
||||||
match self {
|
match self {
|
||||||
Loc::Reg(_) => format!("{self:6?}"),
|
ValSrc::Reg(_) => format!("{self:6?}"),
|
||||||
Loc::Mem(_) | Loc::HMem(_) => format!("({adr:#06x})"),
|
ValSrc::Mem(_) | ValSrc::HMem(_) => format!("({adr:#06x})"),
|
||||||
Loc::Val => format!("{val:#04x}"),
|
ValSrc::Direct => format!("{val:#04x}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -170,6 +178,13 @@ impl TryFrom<u8> for Register {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum BitOp {
|
||||||
|
Or,
|
||||||
|
And,
|
||||||
|
Xor
|
||||||
|
}
|
||||||
|
|
||||||
fn payload(v: u8) -> u8 {
|
fn payload(v: u8) -> u8 {
|
||||||
(v & 0b110_000) >> 4
|
(v & 0b110_000) >> 4
|
||||||
}
|
}
|
||||||
|
|
@ -179,29 +194,67 @@ impl TryFrom<u8> for Operation {
|
||||||
|
|
||||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||||
use Instr::*;
|
use Instr::*;
|
||||||
use Loc::*;
|
use ValSrc::*;
|
||||||
|
use Register::*;
|
||||||
|
use BitOp::*;
|
||||||
|
|
||||||
let (inst, cycl, offs) = match value {
|
let (inst, cycl, offs) = match value {
|
||||||
0b00_000_000 => (Nop, 1, 1),
|
0b00_000_000 => (Nop, 1, 1),
|
||||||
0b11_000_011 => (Jp, 4, 3),
|
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_101 => (Call, 6, 3),
|
||||||
0b11_001_001 => (Ret, 4, 1),
|
0b11_001_001 => (Ret, 4, 1),
|
||||||
0b11_110_011 => (Di, 1, 1),
|
0b11_110_011 => (Di, 1, 1),
|
||||||
0b11_101_010 => (Ld(Mem(AddrLoc::Val), Reg(Register::A)), 4, 3),
|
0b11_101_010 =>
|
||||||
0b11_100_000 => (Ld(HMem(AddrLoc::Val), Reg(Register::A)), 3, 2),
|
(Ld(Mem(AddrLoc::Direct), Reg(A), PostOp::None), 4, 3),
|
||||||
0b00_101_010 => (LdInc(Mem(AddrLoc::Reg(Register::HL))), 2, 1),
|
0b11_100_000 =>
|
||||||
0b00_111_010 => (LdDec(Mem(AddrLoc::Reg(Register::HL))), 2, 1),
|
(Ld(HMem(AddrLoc::Direct), Reg(A), PostOp::None), 3, 2),
|
||||||
0b10_110_110 => (Or(Mem(AddrLoc::Reg(Register::HL))), 2, 1),
|
0b00_100_010 =>
|
||||||
0b11_110_110 => (Or(Val), 2, 1),
|
(Ld(Mem(AddrLoc::Reg(HL)), Reg(A), PostOp::Inc(HL)), 2, 1),
|
||||||
|
0b00_101_010 =>
|
||||||
|
(Ld(Reg(A), Mem(AddrLoc::Reg(HL)), PostOp::Inc(HL)), 2, 1),
|
||||||
|
0b00_110_010 =>
|
||||||
|
(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_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),
|
||||||
|
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) => {
|
_ if (value & (HD_MSK | T1_MSK) == 0b10_110_000) => {
|
||||||
(Or(Reg(Register::try_from(value & 0b111)?)), 1, 1)
|
(Bit(Or, Reg(Register::try_from(value & 0b111)?)), 1, 1)
|
||||||
|
}
|
||||||
|
_ if (value & (HD_MSK | T1_MSK) == 0b10_101_000) => {
|
||||||
|
(Bit(Xor, Reg(Register::try_from(value & 0b111)?)), 1, 1)
|
||||||
|
}
|
||||||
|
_ if (value & (HD_MSK | T1_MSK) == 0b10_100_000) => {
|
||||||
|
(Bit(And, Reg(Register::try_from(value & 0b111)?)), 1, 1)
|
||||||
}
|
}
|
||||||
_ if (value & (HD_MSK | 0b1_000 | T2_MSK) == 0b00_000_011) => {
|
_ 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) => {
|
_ 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) => {
|
_ if (value & (HD_MSK | 0b1_000 | T2_MSK) == 0b11_000_101) => {
|
||||||
(Psh(Register::repr_psh(payload(value))), 4, 1)
|
(Psh(Register::repr_psh(payload(value))), 4, 1)
|
||||||
|
|
@ -211,22 +264,22 @@ impl TryFrom<u8> for Operation {
|
||||||
}
|
}
|
||||||
_ if (value & (HD_MSK | T2_MSK)) == 0b00_000_110 => {
|
_ if (value & (HD_MSK | T2_MSK)) == 0b00_000_110 => {
|
||||||
let reg = Register::try_from((value & T1_MSK) >> 3)?;
|
let reg = Register::try_from((value & T1_MSK) >> 3)?;
|
||||||
(Ld(Reg(reg), Val), 2, 2)
|
(Ld(Reg(reg), Direct, PostOp::None), 2, 2)
|
||||||
}
|
}
|
||||||
_ if (value & HD_MSK) == 0b01_000_000 => {
|
_ if (value & HD_MSK) == 0b01_000_000 => {
|
||||||
let dst = Register::try_from((value & T1_MSK) >> 3)?;
|
let dst = Register::try_from((value & T1_MSK) >> 3)?;
|
||||||
let src = Register::try_from(value & T2_MSK)?;
|
let src = Register::try_from(value & T2_MSK)?;
|
||||||
(Ld(Reg(dst), Reg(src)), 1, 1)
|
(Ld(Reg(dst), Reg(src), PostOp::None), 1, 1)
|
||||||
}
|
}
|
||||||
_ if (value & (HD_MSK | 0b1_000 | T2_MSK)) == 0b00_000_001 => {
|
_ if (value & (HD_MSK | 0b1_000 | T2_MSK)) == 0b00_000_001 => {
|
||||||
let reg = match payload(value) {
|
let reg = match payload(value) {
|
||||||
0b00 => Register::B,
|
0b00 => B,
|
||||||
0b01 => Register::D,
|
0b01 => D,
|
||||||
0b10 => Register::H,
|
0b10 => H,
|
||||||
0b11 => Register::SP,
|
0b11 => SP,
|
||||||
val => return Err(format!("invalid register {val}")),
|
val => return Err(format!("invalid register {val}")),
|
||||||
};
|
};
|
||||||
(Ld16(Reg(reg), Val), 3, 3)
|
(Ld16(Reg(reg), Direct), 3, 3)
|
||||||
}
|
}
|
||||||
_ => (Unimpl(value), 0, 0),
|
_ => (Unimpl(value), 0, 0),
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue