#[derive(Clone, Copy, Debug)]
pub enum Register {
V0 = 0x0,
V1 = 0x1,
V2 = 0x2,
V3 = 0x3,
V4 = 0x4,
V5 = 0x5,
V6 = 0x6,
V7 = 0x7,
V8 = 0x8,
V9 = 0x9,
VA = 0xA,
VB = 0xB,
VC = 0xC,
VD = 0xD,
VE = 0xE,
VF = 0xF,
}
impl Register {
pub fn new(bits: u8) -> Result<Register, ()> {
use self::Register::*;
match bits {
0x0 => Ok(V0),
0x1 => Ok(V1),
0x2 => Ok(V2),
0x3 => Ok(V3),
0x4 => Ok(V4),
0x5 => Ok(V5),
0x6 => Ok(V6),
0x7 => Ok(V7),
0x8 => Ok(V8),
0x9 => Ok(V9),
0xA => Ok(VA),
0xB => Ok(VB),
0xC => Ok(VC),
0xD => Ok(VD),
0xE => Ok(VE),
0xF => Ok(VF),
_ => Err(())
}
}
}
pub type Vx = Register;
pub type Vy = Register;
pub type Byte = u8;
#[derive(Clone, Copy, Debug)]
pub struct Nibble {
pub bits: u8,
}
impl Nibble {
pub fn new(bits: u8) -> Nibble {
Nibble { bits: bits & 0x0F }
}
}
#[derive(Clone, Copy, Debug)]
pub struct Addr {
pub bits: u16,
}
impl Addr {
pub fn new(bits: u16) -> Addr {
Addr { bits: bits & 0x0FFF }
}
}
#[derive(Clone, Copy)]
pub struct RawInstruction {
bits: u16
}
impl RawInstruction {
pub fn new(bits: u16) -> RawInstruction {
RawInstruction{ bits: bits }
}
#[allow(dead_code)]
pub fn bits(&self) -> u16 {
self.bits
}
pub fn addr(&self) -> Addr {
Addr::new(self.bits & 0x0FFF)
}
pub fn x(&self) -> Vx {
Register::new(((self.bits & 0x0F00) >> 8) as u8).unwrap()
}
pub fn y(&self) -> Vy {
Register::new(((self.bits & 0x00F0) >> 4) as u8).unwrap()
}
pub fn n_high(&self) -> Nibble {
Nibble::new(((self.bits & 0xF000) >> 12) as u8)
}
pub fn n_low(&self) -> Nibble {
Nibble::new((self.bits & 0x000F) as u8)
}
pub fn k(&self) -> Byte {
(self.bits & 0x00FF) as u8
}
}
#[derive(Clone, Copy,Debug)]
pub enum Instruction {
Sys(Addr), Clear, Return, Jump(Addr), Call(Addr), SkipEqualK(Vx, Byte), SkipNotEqualK(Vx, Byte), SkipEqual(Vx, Vy), SetK(Vx, Byte), AddK(Vx, Byte), Set(Vx, Vy), Or(Vx, Vy), And(Vx, Vy), XOr(Vx, Vy), Add(Vx, Vy), Sub(Vx, Vy), ShiftRight(Vx, Vy), SubInv(Vx, Vy), ShiftLeft(Vx, Vy), SkipNotEqual(Vx, Vy), LoadI(Addr), LongJump(Addr), Rand(Vx, Byte), Draw(Vx, Vy, Nibble), SkipPressed(Vx), SkipNotPressed(Vx), GetTimer(Vx), WaitKey(Vx), SetTimer(Vx), SetSoundTimer(Vx), AddToI(Vx), LoadHexGlyph(Vx), StoreBCD(Vx), StoreRegisters(Vx), LoadRegisters(Vx), Unknown,
}
impl Instruction {
pub fn from_raw(raw: &RawInstruction) -> Instruction {
use self::Instruction::*;
match raw.n_high().bits {
0x0 => {
match raw.k() {
0xE0 => Clear,
0xEE => Return,
_ => Sys(raw.addr())
}
},
0x1 => Jump(raw.addr()),
0x2 => Call(raw.addr()),
0x3 => SkipEqualK(raw.x(), raw.k()),
0x4 => SkipNotEqualK(raw.x(), raw.k()),
0x5 => SkipEqual(raw.x(), raw.y()),
0x6 => SetK(raw.x(), raw.k()),
0x7 => AddK(raw.x(), raw.k()),
0x8 => {
match raw.n_low().bits {
0x0 => Set(raw.x(), raw.y()),
0x1 => Or(raw.x(), raw.y()),
0x2 => And(raw.x(), raw.y()),
0x3 => XOr(raw.x(), raw.y()),
0x4 => Add(raw.x(), raw.y()),
0x5 => Sub(raw.x(), raw.y()),
0x6 => ShiftRight(raw.x(), raw.y()),
0x7 => SubInv(raw.x(), raw.y()),
0xE => ShiftLeft(raw.x(), raw.y()),
_ => Unknown
}
},
0x9 => SkipNotEqual(raw.x(), raw.y()),
0xA => LoadI(raw.addr()),
0xB => LongJump(raw.addr()),
0xC => Rand(raw.x(), raw.k()),
0xD => Draw(raw.x(), raw.y(), raw.n_low()),
0xE => {
match raw.k() {
0x9E => SkipPressed(raw.x()),
0xA1 => SkipNotPressed(raw.x()),
_ => Unknown,
}
},
0xF => {
match raw.k() {
0x07 => GetTimer(raw.x()),
0x0A => WaitKey(raw.x()),
0x15 => SetTimer(raw.x()),
0x18 => SetSoundTimer(raw.x()),
0x1E => AddToI(raw.x()),
0x29 => LoadHexGlyph(raw.x()),
0x33 => StoreBCD(raw.x()),
0x55 => StoreRegisters(raw.x()),
0x65 => LoadRegisters(raw.x()),
_ => Unknown,
}
}
_ => Unknown
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn unknown_register() {
assert!(Register::new(0x42).is_err())
}
#[test]
fn known_register() {
assert_eq!(Register::new(0xF).unwrap() as u8, Register::VF as u8)
}
}