use itertools::Either;
use itertools::Either::{Left, Right};
use ux::{u12, u4};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Register(pub u4);
impl From<Register> for usize {
fn from(register: Register) -> Self {
usize::try_from(register.0).unwrap()
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Byte {
Register(Register),
Immediate(u8),
}
impl From<Byte> for u8 {
fn from(byte: Byte) -> Self {
match byte {
Byte::Register(Register(x)) => u8::from(x),
Byte::Immediate(x) => x,
}
}
}
#[non_exhaustive]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Instruction {
Halt,
Exit(Option<u8>),
ScrollUp(u4),
ScrollDown(u4),
ScrollRight,
ScrollLeft,
Clear,
Return,
ToggleLoadStoreQuirk,
LoRes,
HiRes,
CallMachineCode(u12),
Jump(u12),
Call(u12),
SkipIfEqual(Register, Byte),
SkipIfNotEqual(Register, Byte),
Add(Register, Byte),
Set(Register, Byte),
Or(Register, Register),
And(Register, Register),
Xor(Register, Register),
Sub(Register, Register),
ShiftRight(Register, Register),
ShiftLeft(Register, Register),
SubReverse(Register, Register),
SetIndex(u16),
JumpRelative(u16),
Random(Register, u8),
Draw(Register, Register, u4),
SkipKey(Register),
SkipNotKey(Register),
SetIndexLong,
LoadAudio,
LoadDelay(Register),
BlockKey(Register),
SelectPlane(u4),
SetPitch(Register),
SetDelay(Register),
SetSound(Register),
AddRegisterToIndex(Register),
FontCharacter(Register),
BigFontCharacter(Register),
Bcd(Register),
Store(Register),
Load(Register),
StoreRange(Register, Register),
LoadRange(Register, Register),
StoreFlags(Register),
LoadFlags(Register),
}
impl From<Instruction> for Either<u16, u32> {
fn from(instruction: Instruction) -> Either<u16, u32> {
match instruction {
Instruction::Halt => Left(0x0000),
Instruction::Exit(None) => Left(0x00FD),
Instruction::Exit(Some(n)) => Left(0x0010 + u16::from(n)),
Instruction::ScrollUp(_) => todo!(),
Instruction::ScrollDown(_) => todo!(),
Instruction::ScrollRight => todo!(),
Instruction::ScrollLeft => todo!(),
Instruction::Clear => Left(0x00E0),
Instruction::Return => Left(0x00EE),
Instruction::ToggleLoadStoreQuirk => todo!(),
Instruction::LoRes => todo!(),
Instruction::HiRes => todo!(),
Instruction::CallMachineCode(_) => todo!(),
Instruction::Jump(_) => todo!(),
Instruction::Call(_) => todo!(),
Instruction::SkipIfEqual(_, _) => todo!(),
Instruction::SkipIfNotEqual(_, _) => todo!(),
Instruction::Add(_, _) => todo!(),
Instruction::Set(_, _) => todo!(),
Instruction::Or(_, _) => todo!(),
Instruction::And(_, _) => todo!(),
Instruction::Xor(_, _) => todo!(),
Instruction::Sub(_, _) => todo!(),
Instruction::ShiftRight(_, _) => todo!(),
Instruction::ShiftLeft(_, _) => todo!(),
Instruction::SubReverse(_, _) => todo!(),
Instruction::SetIndex(n) => {
if n <= 0xFFF {
Left(0xA000 + n)
} else {
Right(0xF000_0000 + u32::from(n))
}
}
Instruction::JumpRelative(_) => todo!(),
Instruction::Random(_, _) => todo!(),
Instruction::Draw(_, _, _) => todo!(),
Instruction::SkipKey(_) => todo!(),
Instruction::SkipNotKey(_) => todo!(),
Instruction::SetIndexLong => Left(0xF000),
Instruction::LoadAudio => todo!(),
Instruction::LoadDelay(_) => todo!(),
Instruction::BlockKey(_) => todo!(),
Instruction::SelectPlane(_) => todo!(),
Instruction::SetPitch(_) => todo!(),
Instruction::SetDelay(_) => todo!(),
Instruction::SetSound(_) => todo!(),
Instruction::AddRegisterToIndex(_) => todo!(),
Instruction::FontCharacter(_) => todo!(),
Instruction::BigFontCharacter(_) => todo!(),
Instruction::Bcd(_) => todo!(),
Instruction::Store(_) => todo!(),
Instruction::Load(_) => todo!(),
Instruction::StoreRange(_, _) => todo!(),
Instruction::LoadRange(_, _) => todo!(),
Instruction::StoreFlags(_) => todo!(),
Instruction::LoadFlags(_) => todo!(),
}
}
}
impl TryFrom<u32> for Instruction {
type Error = String;
fn try_from(opcode: u32) -> Result<Self, Self::Error> {
let prefix = u16::try_from(opcode >> 16).unwrap();
let suffix = u16::try_from(opcode & 0x0000_FFFF).unwrap();
if prefix == 0x0000 {
Ok(Instruction::try_from(suffix)?)
} else if prefix == 0xF000 {
Ok(Instruction::SetIndex(suffix))
} else {
Err(format!("Unknown opcode {:#010x}", opcode))
}
}
}
impl TryFrom<u16> for Instruction {
type Error = String;
fn try_from(opcode: u16) -> Result<Self, Self::Error> {
let x = u4::try_from((opcode & 0x0F00) >> 8).unwrap();
let y = u4::try_from((opcode & 0x00F0) >> 4).unwrap();
let nnn = u12::try_from(opcode & 0x0FFF).unwrap();
let kk = (opcode & 0x00FF) as u8;
let n = u4::try_from(opcode & 0x000F).unwrap();
let op1 = (opcode & 0xF000) >> 12;
let op2 = (opcode & 0x0F00) >> 8;
let op3 = (opcode & 0x00F0) >> 4;
let op4 = opcode & 0x000F;
Ok(
match (op1, op2, op3, op4) {
#![allow(clippy::match_same_arms)]
(0x0, 0x0, 0x0, 0x0) => Instruction::Halt,
(0x0, 0x0, 0x1, _) => Instruction::Exit(Some(op4 as u8)),
(0x0, 0x0, 0xB, _) => Instruction::ScrollUp(n),
(0x0, 0x0, 0xC, _) => Instruction::ScrollDown(n),
(0x0, 0x0, 0xD, _) => Instruction::ScrollUp(n),
(0x0, 0x0, 0xE, 0x0) => Instruction::Clear,
(0x0, 0x0, 0xE, 0xE) => Instruction::Return,
(0x0, 0x0, 0xF, 0xA) => Instruction::ToggleLoadStoreQuirk,
(0x0, 0x0, 0xF, 0xB) => Instruction::ScrollRight,
(0x0, 0x0, 0xF, 0xC) => Instruction::ScrollLeft,
(0x0, 0x0, 0xF, 0xD) => Instruction::Exit(None),
(0x0, 0x0, 0xF, 0xE) => Instruction::LoRes,
(0x0, 0x0, 0xF, 0xF) => Instruction::HiRes,
(0x0, _, _, _) => Instruction::CallMachineCode(nnn),
(0x1, _, _, _) => Instruction::Jump(nnn),
(0x2, _, _, _) => Instruction::Call(nnn),
(0x3, _, _, _) => Instruction::SkipIfEqual(Register(x), Byte::Immediate(kk)),
(0x4, _, _, _) => Instruction::SkipIfNotEqual(Register(x), Byte::Immediate(kk)),
(0x5, _, _, 0x0) => {
Instruction::SkipIfEqual(Register(x), Byte::Register(Register(y)))
}
(0x5, _, _, 0x2) => Instruction::StoreRange(Register(x), Register(y)),
(0x5, _, _, 0x3) => Instruction::LoadRange(Register(x), Register(y)),
(0x6, _, _, _) => Instruction::Set(Register(x), Byte::Immediate(kk)),
(0x7, _, _, _) => Instruction::Add(Register(x), Byte::Immediate(kk)),
(0x8, _, _, 0) => Instruction::Set(Register(x), Byte::Register(Register(y))),
(0x8, _, _, 1) => Instruction::Or(Register(x), Register(y)),
(0x8, _, _, 2) => Instruction::And(Register(x), Register(y)),
(0x8, _, _, 3) => Instruction::Xor(Register(x), Register(y)),
(0x8, _, _, 4) => Instruction::Add(Register(x), Byte::Register(Register(y))),
(0x8, _, _, 5) => Instruction::Sub(Register(x), Register(y)),
(0x8, _, _, 0x6) => Instruction::ShiftRight(Register(x), Register(y)),
(0x8, _, _, 0x7) => Instruction::SubReverse(Register(x), Register(y)),
(0x8, _, _, 0xE) => Instruction::ShiftLeft(Register(x), Register(y)),
(0x9, _, _, 0x0) => {
Instruction::SkipIfNotEqual(Register(x), Byte::Register(Register(y)))
}
(0xA, _, _, _) => Instruction::SetIndex(u16::from(nnn)),
(0xB, _, _, _) => Instruction::JumpRelative(u16::from(nnn)),
(0xC, _x, _, _) => Instruction::Random(Register(x), kk),
(0xD, _, _, _) => Instruction::Draw(Register(x), Register(y), n),
(0xE, _x, 0x9, 0xE) => Instruction::SkipKey(Register(x)),
(0xE, _x, 0xA, 0x1) => Instruction::SkipNotKey(Register(x)),
(0xF, 0x0, 0x0, 0x0) => Instruction::SetIndexLong,
(0xF, 0x0, 0x0, 0x2) => Instruction::LoadAudio,
(0xF, _, 0x0, 0x7) => Instruction::LoadDelay(Register(x)),
(0xF, _x, 0x0, 0xA) => Instruction::BlockKey(Register(x)),
(0xF, _, 0x0, 0x1) => Instruction::SelectPlane(x),
(0xF, _x, 0x3, 0xA) => Instruction::SetPitch(Register(x)),
(0xF, _, 0x1, 0x5) => Instruction::SetDelay(Register(x)),
(0xF, _, 0x1, 0x8) => Instruction::SetSound(Register(x)),
(0xF, _, 0x1, 0xE) => Instruction::AddRegisterToIndex(Register(x)),
(0xF, _x, 0x2, 0x9) => Instruction::FontCharacter(Register(x)),
(0xF, _x, 0x3, 0x0) => Instruction::BigFontCharacter(Register(x)),
(0xF, _x, 0x3, 0x3) => Instruction::Bcd(Register(x)),
(0xF, _, 0x5, 0x5) => Instruction::Store(Register(x)),
(0xF, _, 0x6, 0x5) => Instruction::Load(Register(x)),
(0xF, _x, 0x7, 0x5) => Instruction::StoreFlags(Register(x)),
(0xF, _x, 0x8, 0x5) => Instruction::LoadFlags(Register(x)),
_ => return Err(format!("Unknown opcode {:#06x}", opcode)),
},
)
}
}