use crate::compiler::instr::builder::{InstrBuilder, MicrocodeReadable, MicrocodeWritable};
use crate::compiler::instr::{InstrDef, InstrId};
use crate::gbz80types::Flags;
use crate::microcode::args::{Reg16, Reg8};
use crate::microcode::combocodes::{
GbStack16, HandleHalt, HlDec, HlInc, Immediate16, Immediate8, LoadAndExecute, Mem,
ServiceInterrupt,
};
use crate::microcode::Microcode;
use crate::opcode::args::{AluOp, AluUnaryOp, ConditionCode, Operand16, Operand8};
use crate::opcode::{CBOpcode, CBOperation, InternalFetch, Opcode};
impl From<InternalFetch> for InstrDef {
fn from(_: InternalFetch) -> Self {
InstrBuilder::first(HandleHalt)
.then(ServiceInterrupt)
.then(LoadAndExecute)
.build(InstrId::InternalFetch)
}
}
impl From<Opcode> for InstrDef {
fn from(value: Opcode) -> Self {
let builder = match value {
Opcode::Nop => InstrBuilder::new(),
Opcode::Stop => Microcode::Stop.into(),
Opcode::JumpRelative(cond) => jump_relative(cond),
Opcode::Inc8(operand) => inc8(operand),
Opcode::Dec8(operand) => dec8(operand),
Opcode::Load8 { dest, source } => InstrBuilder::read(source).then_write(dest),
Opcode::Inc16(operand) => inc16(operand),
Opcode::Dec16(operand) => dec16(operand),
Opcode::Load16 { dest, source } => load16(dest, source),
Opcode::Add16(operand) => add16(operand),
Opcode::Halt => Microcode::Halt.into(),
Opcode::AluOp { operand, op } => InstrBuilder::read(operand).then(op),
Opcode::AluUnary(op) => op.into(),
Opcode::Call(cond) => call(cond),
Opcode::Jump(cond) => jump(cond),
Opcode::Ret(cond) => ret(cond),
Opcode::Push(operand) => InstrBuilder::read(operand)
.then_yield()
.then_write(GbStack16),
Opcode::Pop(operand) => InstrBuilder::read(GbStack16).then_write(operand),
Opcode::PrefixCB => InstrBuilder::read(Immediate8).then(Microcode::ParseCBOpcode),
Opcode::DisableInterrupts => Microcode::DisableInterrupts.into(),
Opcode::EnableInterrupts => Microcode::EnableInterrupts { immediate: false }.into(),
Opcode::RetInterrupt => interrupt_return(),
Opcode::OffsetSp => offset_sp(),
Opcode::AddressOfOffsetSp => address_of_offset_sp(),
Opcode::JumpHL => InstrBuilder::read(Reg16::HL).then_write(Reg16::Pc),
Opcode::Reset(dest) => reset(dest),
Opcode::MissingInstruction(_) => InstrBuilder::new(),
};
builder.build(InstrId::Opcode(value))
}
}
fn jump_relative(cond: ConditionCode) -> InstrBuilder {
InstrBuilder::read(Immediate8)
.then_read(Reg16::Pc)
.then(Microcode::OffsetAddr)
.then(Microcode::Discard8)
.then(cond.cond(
InstrBuilder::r#yield().then_write(Reg16::Pc),
Microcode::Discard16,
))
}
fn inc8(operand: Operand8) -> InstrBuilder {
const MASK: Flags = Flags::all().difference(Flags::CARRY);
InstrBuilder::first(Microcode::Append { val: 1 })
.then_read(operand)
.then(Microcode::Add)
.then_write(MASK)
.then_write(operand)
}
fn dec8(operand: Operand8) -> InstrBuilder {
const MASK: Flags = Flags::all().difference(Flags::CARRY);
InstrBuilder::first(Microcode::Append { val: 1 })
.then_read(operand)
.then(Microcode::Sub)
.then_write(MASK)
.then_write(operand)
}
fn inc16(operand: Operand16) -> InstrBuilder {
InstrBuilder::read(operand)
.then(Microcode::Inc16)
.then(Microcode::Yield)
.then_write(operand)
}
fn dec16(operand: Operand16) -> InstrBuilder {
InstrBuilder::read(operand)
.then(Microcode::Dec16)
.then(Microcode::Yield)
.then_write(operand)
}
fn load16(dest: Operand16, source: Operand16) -> InstrBuilder {
InstrBuilder::read(source)
.then(if (dest, source) == (Operand16::Sp, Operand16::HL) {
Some(Microcode::Yield)
} else {
None
})
.then_write(dest)
}
fn add16(arg: Operand16) -> InstrBuilder {
const MASK: Flags = Flags::all().difference(Flags::ZERO);
InstrBuilder::read(arg)
.then_read(Reg16::HL)
.then(Microcode::Add16)
.then_yield()
.then_write(MASK)
.then_write(Reg16::HL)
}
fn call(cond: ConditionCode) -> InstrBuilder {
InstrBuilder::read(Immediate16)
.then(
cond.cond(
InstrBuilder::read(Reg16::Pc)
.then_yield()
.then_write(GbStack16)
.then_write(Reg16::Pc),
Microcode::Discard16,
),
)
}
fn jump(cond: ConditionCode) -> InstrBuilder {
InstrBuilder::read(Immediate16)
.then(
cond.cond(
InstrBuilder::r#yield()
.then_write(Reg16::Pc),
Microcode::Discard16,
),
)
}
fn ret(cond: ConditionCode) -> InstrBuilder {
InstrBuilder::first(match cond {
ConditionCode::Unconditional => None,
_ => Some(Microcode::Yield),
})
.then(
cond.if_true(
InstrBuilder::read(GbStack16)
.then_yield()
.then_write(Reg16::Pc),
),
)
}
fn interrupt_return() -> InstrBuilder {
InstrBuilder::read(GbStack16)
.then_yield()
.then_write(Reg16::Pc)
.then(Microcode::EnableInterrupts { immediate: true })
}
fn offset_sp() -> InstrBuilder {
InstrBuilder::read(Immediate8)
.then_read(Reg16::Sp)
.then(Microcode::OffsetAddr)
.then_yield()
.then_yield()
.then_write(Flags::all())
.then_write(Reg16::Sp)
}
fn address_of_offset_sp() -> InstrBuilder {
InstrBuilder::read(Immediate8)
.then_read(Reg16::Sp)
.then(Microcode::OffsetAddr)
.then_yield()
.then_write(Flags::all())
.then_write(Reg16::HL)
}
fn reset(dest: u8) -> InstrBuilder {
InstrBuilder::r#yield()
.then_read(Reg16::Pc)
.then_write(GbStack16)
.then(Microcode::Append { val: dest })
.then(Microcode::Append { val: 0 })
.then_write(Reg16::Pc)
}
impl From<CBOpcode> for InstrDef {
fn from(value: CBOpcode) -> Self {
fn cb_unary_op(operand: Operand8, operator: Microcode) -> InstrBuilder {
InstrBuilder::read(operand)
.then(
if matches!(operator, Microcode::RotateLeft9 | Microcode::RotateRight9) {
Some(Microcode::GetFlagsMasked { mask: Flags::all() })
} else {
None
},
)
.then(operator)
.then_write(Flags::all())
.then_write(operand)
}
let builder = match value.op {
CBOperation::RotateLeft8 => cb_unary_op(value.operand, Microcode::RotateLeft8),
CBOperation::RotateLeft9 => cb_unary_op(value.operand, Microcode::RotateLeft9),
CBOperation::RotateRight8 => cb_unary_op(value.operand, Microcode::RotateRight8),
CBOperation::RotateRight9 => cb_unary_op(value.operand, Microcode::RotateRight9),
CBOperation::ShiftLeft => cb_unary_op(value.operand, Microcode::ShiftLeft),
CBOperation::ShiftRight => cb_unary_op(value.operand, Microcode::ShiftRight),
CBOperation::ShiftRightSignExt => {
cb_unary_op(value.operand, Microcode::ShiftRightSignExt)
}
CBOperation::Swap => cb_unary_op(value.operand, Microcode::Swap),
CBOperation::TestBit(bit) => {
const MASK: Flags = Flags::all().difference(Flags::CARRY);
InstrBuilder::read(value.operand)
.then(Microcode::TestBit { bit })
.then_write(MASK)
}
CBOperation::SetBit(bit) => InstrBuilder::read(value.operand)
.then(Microcode::SetBit { bit })
.then_write(value.operand),
CBOperation::ResetBit(bit) => InstrBuilder::read(value.operand)
.then(Microcode::ResetBit { bit })
.then_write(value.operand),
};
builder.build(InstrId::CBOpcode(value))
}
}
impl From<AluOp> for InstrBuilder {
fn from(op: AluOp) -> Self {
InstrBuilder::read(Reg8::Acc)
.then(if matches!(op, AluOp::AddCarry | AluOp::SubCarry) {
Some(Microcode::GetFlagsMasked { mask: Flags::all() })
} else {
None
})
.then(match op {
AluOp::Add => Microcode::Add,
AluOp::AddCarry => Microcode::Adc,
AluOp::Sub => Microcode::Sub,
AluOp::SubCarry => Microcode::Sbc,
AluOp::And => Microcode::And,
AluOp::Or => Microcode::Or,
AluOp::Xor => Microcode::Xor,
AluOp::Compare => Microcode::Sub,
})
.then_write(Flags::all())
.then(match op {
AluOp::Compare => Microcode::Discard8,
_ => Microcode::WriteReg { reg: Reg8::Acc },
})
}
}
impl From<AluUnaryOp> for InstrBuilder {
fn from(value: AluUnaryOp) -> Self {
match value {
AluUnaryOp::RotateLeft8 => InstrBuilder::read(Reg8::Acc)
.then(Microcode::RotateLeft8)
.then_write(Flags::all())
.then(Microcode::Append { val: 0 })
.then_write(Flags::ZERO)
.then_write(Reg8::Acc),
AluUnaryOp::RotateLeft9 => InstrBuilder::read(Reg8::Acc)
.then_read(Flags::all())
.then(Microcode::RotateLeft9)
.then_write(Flags::all())
.then(Microcode::Append { val: 0 })
.then_write(Flags::ZERO)
.then_write(Reg8::Acc),
AluUnaryOp::RotateRight8 => InstrBuilder::read(Reg8::Acc)
.then(Microcode::RotateRight8)
.then_write(Flags::all())
.then(Microcode::Append { val: 0 })
.then_write(Flags::ZERO)
.then_write(Reg8::Acc),
AluUnaryOp::RotateRight9 => InstrBuilder::read(Reg8::Acc)
.then_read(Flags::all())
.then(Microcode::RotateRight9)
.then_write(Flags::all())
.then(Microcode::Append { val: 0 })
.then_write(Flags::ZERO)
.then_write(Reg8::Acc),
AluUnaryOp::DecimalAdjust => {
const MASK: Flags = Flags::all().difference(Flags::SUB);
InstrBuilder::read(Reg8::Acc)
.then_read(Flags::all())
.then(Microcode::DecimalAdjust)
.then_write(MASK)
.then_write(Reg8::Acc)
}
AluUnaryOp::Compliment => {
const MASK: Flags = Flags::SUB.union(Flags::HALFCARRY);
InstrBuilder::read(Reg8::Acc)
.then(Microcode::Compliment)
.then_write(MASK)
.then_write(Reg8::Acc)
}
AluUnaryOp::SetCarryFlag => {
const MASK: Flags = Flags::all().difference(Flags::ZERO);
InstrBuilder::first(Microcode::Append {
val: Flags::CARRY.bits(),
})
.then_write(MASK)
}
AluUnaryOp::ComplimentCarryFlag => {
InstrBuilder::read(Flags::CARRY)
.then(Microcode::Compliment)
.then(Microcode::Discard8)
.then_write(Flags::CARRY)
.then(Microcode::Append { val: 0 })
.then_write(Flags::SUB.union(Flags::HALFCARRY))
}
}
}
}
impl MicrocodeReadable for Operand8 {
fn to_read(self) -> InstrBuilder {
match self {
Self::A => InstrBuilder::read(Reg8::Acc),
Self::B => InstrBuilder::read(Reg8::B),
Self::C => InstrBuilder::read(Reg8::C),
Self::D => InstrBuilder::read(Reg8::D),
Self::E => InstrBuilder::read(Reg8::E),
Self::H => InstrBuilder::read(Reg8::H),
Self::L => InstrBuilder::read(Reg8::L),
Self::AddrHL => InstrBuilder::read(Reg16::HL).then_read(Mem),
Self::AddrBC => InstrBuilder::read(Reg16::BC).then_read(Mem),
Self::AddrDE => InstrBuilder::read(Reg16::DE).then_read(Mem),
Self::AddrHLInc => {
InstrBuilder::read(HlInc)
.then_read(Mem)
}
Self::AddrHLDec => {
InstrBuilder::read(HlDec)
.then_read(Mem)
}
Self::Immediate => InstrBuilder::read(Immediate8),
Self::AddrImmediate => InstrBuilder::read(Immediate16).then_read(Mem),
Self::AddrRelC => InstrBuilder::read(Reg8::C)
.then(Microcode::Append { val: 0xff })
.then_read(Mem),
Self::AddrRelImmediate => InstrBuilder::read(Immediate8)
.then(Microcode::Append { val: 0xff })
.then_read(Mem),
}
}
}
impl MicrocodeWritable for Operand8 {
fn to_write(self) -> InstrBuilder {
match self {
Self::A => InstrBuilder::write(Reg8::Acc),
Self::B => InstrBuilder::write(Reg8::B),
Self::C => InstrBuilder::write(Reg8::C),
Self::D => InstrBuilder::write(Reg8::D),
Self::E => InstrBuilder::write(Reg8::E),
Self::H => InstrBuilder::write(Reg8::H),
Self::L => InstrBuilder::write(Reg8::L),
Self::AddrHL => InstrBuilder::read(Reg16::HL).then_write(Mem),
Self::AddrBC => InstrBuilder::read(Reg16::BC).then_write(Mem),
Self::AddrDE => InstrBuilder::read(Reg16::DE).then_write(Mem),
Self::AddrHLInc => {
InstrBuilder::read(HlInc)
.then_write(Mem)
}
Self::AddrHLDec => {
InstrBuilder::read(HlDec)
.then_write(Mem)
}
Self::Immediate => panic!("Immediates cannot be used as store destinations"),
Self::AddrImmediate => InstrBuilder::read(Immediate16).then_write(Mem),
Self::AddrRelC => InstrBuilder::read(Reg8::C)
.then(Microcode::Append { val: 0xff })
.then_write(Mem),
Self::AddrRelImmediate => InstrBuilder::read(Immediate8)
.then(Microcode::Append { val: 0xff })
.then_write(Mem),
}
}
}
impl MicrocodeReadable for Operand16 {
fn to_read(self) -> InstrBuilder {
match self {
Self::BC => InstrBuilder::read(Reg16::BC),
Self::DE => InstrBuilder::read(Reg16::DE),
Self::HL => InstrBuilder::read(Reg16::HL),
Self::AF => InstrBuilder::read(Reg16::AF),
Self::Sp => InstrBuilder::read(Reg16::Sp),
Self::Immediate => InstrBuilder::read(Immediate16),
Self::AddrImmediate => {
panic!("No actual operation uses (u16) as the source for a 16 bit load")
}
}
}
}
impl MicrocodeWritable for Operand16 {
fn to_write(self) -> InstrBuilder {
match self {
Self::BC => InstrBuilder::write(Reg16::BC),
Self::DE => InstrBuilder::write(Reg16::DE),
Self::HL => InstrBuilder::write(Reg16::HL),
Self::AF => InstrBuilder::write(Reg16::AF),
Self::Sp => InstrBuilder::write(Reg16::Sp),
Self::Immediate => panic!("Immediates cannot be used as store destinations"),
Self::AddrImmediate => InstrBuilder::read(Immediate16)
.then(Microcode::Intersperse)
.then_write(Mem)
.then(Microcode::Inc16)
.then_write(Mem),
}
}
}
impl ConditionCode {
fn cond(
self,
code_if_condition_true: impl Into<InstrBuilder>,
code_if_condition_false: impl Into<InstrBuilder>,
) -> InstrBuilder {
match self {
ConditionCode::Unconditional => code_if_condition_true.into(),
ConditionCode::NonZero => InstrBuilder::read(Flags::ZERO).then(InstrBuilder::cond(
code_if_condition_false,
code_if_condition_true,
)),
ConditionCode::Zero => InstrBuilder::read(Flags::ZERO).then(InstrBuilder::cond(
code_if_condition_true,
code_if_condition_false,
)),
ConditionCode::NoCarry => InstrBuilder::read(Flags::CARRY).then(InstrBuilder::cond(
code_if_condition_false,
code_if_condition_true,
)),
ConditionCode::Carry => InstrBuilder::read(Flags::CARRY).then(InstrBuilder::cond(
code_if_condition_true,
code_if_condition_false,
)),
}
}
fn if_true(self, code_if_condition_true: impl Into<InstrBuilder>) -> InstrBuilder {
self.cond(code_if_condition_true, InstrBuilder::new())
}
}