use std::fmt;
use crate::opcode::args::{AluOp, AluUnaryOp, ConditionCode, Operand16, Operand8};
pub mod args;
pub mod defs;
pub struct InternalFetch;
impl fmt::Display for InternalFetch {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("[Internal Fetch]")
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum Opcode {
Nop,
Stop,
JumpRelative(ConditionCode),
Inc8(Operand8),
Dec8(Operand8),
Load8 { dest: Operand8, source: Operand8 },
Inc16(Operand16),
Dec16(Operand16),
Load16 { dest: Operand16, source: Operand16 },
Add16(Operand16),
Halt,
AluOp { op: AluOp, operand: Operand8 },
AluUnary(AluUnaryOp),
Call(ConditionCode),
Jump(ConditionCode),
Ret(ConditionCode),
Push(Operand16),
Pop(Operand16),
PrefixCB,
DisableInterrupts,
EnableInterrupts,
RetInterrupt,
OffsetSp,
AddressOfOffsetSp,
JumpHL,
Reset(u8),
MissingInstruction(u8),
}
impl Opcode {
pub fn decode(opcode: u8) -> Self {
let x = (opcode & 0b11000000) >> 6;
let p = (opcode & 0b00110000) >> 4;
let y = (opcode & 0b00111000) >> 3;
let q = (opcode & 0b00001000) != 0;
let z = opcode & 0b00000111;
match x {
0 => match z {
0 => match y {
0 => Self::Nop,
1 => Self::Load16 {
dest: Operand16::AddrImmediate,
source: Operand16::Sp,
},
2 => Self::Stop,
3..=7 => Self::JumpRelative(ConditionCode::from_relative_cond_code(y)),
_ => unreachable!(),
},
1 => match q {
false => Self::Load16 {
dest: Operand16::from_pair_code_sp(p),
source: Operand16::Immediate,
},
true => Self::Add16(Operand16::from_pair_code_sp(p)),
},
2 => match q {
false => Self::Load8 {
dest: Operand8::from_indirect(p),
source: Operand8::A,
},
true => Self::Load8 {
dest: Operand8::A,
source: Operand8::from_indirect(p),
},
},
3 => match q {
false => Self::Inc16(Operand16::from_pair_code_sp(p)),
true => Self::Dec16(Operand16::from_pair_code_sp(p)),
},
4 => Self::Inc8(Operand8::from_regcode(y)),
5 => Self::Dec8(Operand8::from_regcode(y)),
6 => Self::Load8 {
dest: Operand8::from_regcode(y),
source: Operand8::Immediate,
},
7 => Self::AluUnary(AluUnaryOp::from_ycode(y)),
_ => unreachable!(),
},
1 => match (z, y) {
(6, 6) => Self::Halt,
_ => Self::Load8 {
dest: Operand8::from_regcode(y),
source: Operand8::from_regcode(z),
},
},
2 => Self::AluOp {
op: AluOp::from_ycode(y),
operand: Operand8::from_regcode(z),
},
3 => match z {
0 => match y {
0..=3 => Self::Ret(ConditionCode::from_absolute_cond_code(y)),
4 => Self::Load8 {
dest: Operand8::AddrRelImmediate,
source: Operand8::A,
},
5 => Self::OffsetSp,
6 => Self::Load8 {
dest: Operand8::A,
source: Operand8::AddrRelImmediate,
},
7 => Self::AddressOfOffsetSp,
_ => unreachable!(),
},
1 => match q {
false => Opcode::Pop(Operand16::from_pair_code_af(p)),
true => match p {
0 => Opcode::Ret(ConditionCode::Unconditional),
1 => Opcode::RetInterrupt,
2 => Opcode::JumpHL,
3 => Opcode::Load16 {
dest: Operand16::Sp,
source: Operand16::HL,
},
_ => unreachable!(),
},
},
2 => match y {
0..=3 => Self::Jump(ConditionCode::from_absolute_cond_code(y)),
4 => Self::Load8 {
dest: Operand8::AddrRelC,
source: Operand8::A,
},
5 => Self::Load8 {
dest: Operand8::AddrImmediate,
source: Operand8::A,
},
6 => Self::Load8 {
dest: Operand8::A,
source: Operand8::AddrRelC,
},
7 => Self::Load8 {
dest: Operand8::A,
source: Operand8::AddrImmediate,
},
_ => unreachable!(),
},
3 => match y {
0 => Self::Jump(ConditionCode::Unconditional),
1 => Self::PrefixCB,
2..=5 => Self::MissingInstruction(opcode),
6 => Self::DisableInterrupts,
7 => Self::EnableInterrupts,
_ => unreachable!(),
},
4 => match y {
0..=3 => Self::Call(ConditionCode::from_absolute_cond_code(y)),
4..=7 => Self::MissingInstruction(opcode),
_ => unreachable!(),
},
5 => match q {
false => Opcode::Push(Operand16::from_pair_code_af(p)),
true => match p {
0 => Opcode::Call(ConditionCode::Unconditional),
1..=3 => Opcode::MissingInstruction(opcode),
_ => unreachable!(),
},
},
6 => Self::AluOp {
op: AluOp::from_ycode(y),
operand: Operand8::Immediate,
},
7 => Opcode::Reset(y * 8),
_ => unreachable!(),
},
_ => unreachable!(),
}
}
}
impl fmt::Display for Opcode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Self::Nop => f.write_str("NOP"),
Self::Stop => f.write_str("STOP"),
Self::JumpRelative(ConditionCode::Unconditional) => f.write_str("JR i8"),
Self::JumpRelative(code) => write!(f, "JR {},i8", code),
Self::Inc8(operand) => write!(f, "INC {}", operand),
Self::Dec8(operand) => write!(f, "DEC {}", operand),
Self::Load8 { dest, source } => write!(f, "LD {},{}", dest, source),
Self::Inc16(operand) => write!(f, "INC {}", operand),
Self::Dec16(operand) => write!(f, "DEC {}", operand),
Self::Load16 { dest, source } => write!(f, "LD {},{}", dest, source),
Self::Add16(operand) => write!(f, "ADD HL,{}", operand),
Self::Halt => f.write_str("HALT"),
Self::AluOp { op, operand } => write!(f, "{} A,{}", op, operand),
Self::AluUnary(op) => fmt::Display::fmt(&op, f),
Self::Call(ConditionCode::Unconditional) => f.write_str("CALL u16"),
Self::Call(code) => write!(f, "CALL {},u16", code),
Self::Jump(ConditionCode::Unconditional) => f.write_str("JP u16"),
Self::Jump(code) => write!(f, "JP {},u16", code),
Self::Ret(ConditionCode::Unconditional) => f.write_str("RET"),
Self::Ret(code) => write!(f, "RET {}", code),
Self::Pop(operand) => write!(f, "POP {}", operand),
Self::Push(operand) => write!(f, "PUSH {}", operand),
Self::PrefixCB => f.write_str("PREFIX CB"),
Self::DisableInterrupts => f.write_str("DI"),
Self::EnableInterrupts => f.write_str("EI"),
Self::RetInterrupt => f.write_str("RETI"),
Self::OffsetSp => f.write_str("ADD SP,i8"),
Self::AddressOfOffsetSp => f.write_str("LD HL,SP+i8"),
Self::JumpHL => f.write_str("JP HL"),
Self::Reset(target) => write!(f, "RST {:02X}h", target),
Self::MissingInstruction(opcode) => write!(f, "<Missing Instruction {:2X}>", opcode),
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct CBOpcode {
pub operand: Operand8,
pub op: CBOperation,
}
impl CBOpcode {
pub fn decode(opcode: u8) -> Self {
let x = (opcode & 0b11000000) >> 6;
let y = (opcode & 0b00111000) >> 3;
let z = opcode & 0b00000111;
let operand = Operand8::from_regcode(z);
let op = match x {
0 => match y {
0 => CBOperation::RotateLeft8,
1 => CBOperation::RotateRight8,
2 => CBOperation::RotateLeft9,
3 => CBOperation::RotateRight9,
4 => CBOperation::ShiftLeft,
5 => CBOperation::ShiftRightSignExt,
6 => CBOperation::Swap,
7 => CBOperation::ShiftRight,
_ => unreachable!(),
},
1 => CBOperation::TestBit(y),
2 => CBOperation::ResetBit(y),
3 => CBOperation::SetBit(y),
_ => unreachable!(),
};
Self { operand, op }
}
}
impl fmt::Display for CBOpcode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.op.is_bit_op() {
write!(f, "{},{}", self.op, self.operand)
} else {
write!(f, "{} {}", self.op, self.operand)
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum CBOperation {
RotateLeft8,
RotateLeft9,
RotateRight8,
RotateRight9,
ShiftLeft,
ShiftRight,
ShiftRightSignExt,
Swap,
TestBit(u8),
SetBit(u8),
ResetBit(u8),
}
impl CBOperation {
fn is_bit_op(self) -> bool {
matches!(self, Self::TestBit(_) | Self::SetBit(_) | Self::ResetBit(_))
}
}
impl fmt::Display for CBOperation {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Self::RotateLeft8 => f.write_str("RLC"),
Self::RotateRight8 => f.write_str("RRC"),
Self::RotateLeft9 => f.write_str("RL"),
Self::RotateRight9 => f.write_str("RR"),
Self::ShiftLeft => f.write_str("SLA"),
Self::ShiftRightSignExt => f.write_str("SRA"),
Self::Swap => f.write_str("SWAP"),
Self::ShiftRight => f.write_str("SRL"),
Self::TestBit(bit) => write!(f, "BIT {}", bit),
Self::ResetBit(bit) => write!(f, "RES {}", bit),
Self::SetBit(bit) => write!(f, "SET {}", bit),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn can_decode_any_opcode() {
for op in 0u8..=0xff {
Opcode::decode(op);
}
}
#[test]
fn can_decode_any_cb_opcode() {
for op in 0u8..=0xff {
CBOpcode::decode(op);
}
}
}