1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use std::fmt;


mod opcode_table;
use opcode_table::AddressMode;

pub use opcode_table::Opcode;

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum Operand {
    Absolute(u16),
    AbsoluteX(u16),
    AbsoluteY(u16),
    Accumulator,
    Immediate(u8),
    Implied,
    IndexedIndirect(u8),
    Indirect(u16),
    IndirectIndexed(u8),
    Relative(i8),
    ZeroPage(u8),
    ZeroPageX(u8),
    ZeroPageY(u8)
}


impl fmt::Display for Operand {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Operand::Absolute(v) => write!(f, "${:04X}", v),
            Operand::AbsoluteX(v) => write!(f, "${:04X}", v),
            Operand::AbsoluteY(v) => write!(f, "${:04X}", v),
            Operand::Accumulator => write!(f, "A"),
            Operand::Immediate(v) => write!(f, "#${:02X}", v),
            Operand::Implied => write!(f, ""),
            Operand::Indirect(v) => write!(f, "(${:04X})", v),
            Operand::IndexedIndirect(v) => write!(f, "(${:02X},X)", v),
            Operand::IndirectIndexed(v) => write!(f, "(${:02X}),Y", v),
            Operand::Relative(v) => write!(f, "${:02X}", v),
            Operand::ZeroPage(v) => write!(f, "${:02X}", v),
            Operand::ZeroPageX(v) => write!(f, "${:02X}", v),
            Operand::ZeroPageY(v) => write!(f, "${:02X}", v),
        }
    }
}



#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct Instruction {
    opcode: Opcode,
    operand: Operand,
    size: usize
}


impl Instruction {
    fn new(opcode: Opcode, operand: Operand, size: usize) -> Instruction {
        Instruction {
            opcode: opcode,
            operand: operand,
            size: size
        }
    }

    pub fn opcode(&self) -> &Opcode {
        &self.opcode
    }

    pub fn operand(&self) -> &Operand {
        &self.operand
    }

    pub fn size(&self) -> usize {
        self.size
    }
}


impl fmt::Display for Instruction {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.operand {
            Operand::Implied => self.opcode.fmt(f),
            _ => write!(f, "{} {}", self.opcode, self.operand)
        }
    }
}



pub fn decode(buf: &[u8]) -> Option<Instruction> {
    fn op16(buf: &[u8]) -> Option<u16> {
        Some(((buf.get(2)?.clone() as u16) << 8) | (buf.get(1)?.clone() as u16))
    }

    fn op8(buf: &[u8]) -> Option<u8> {
        Some(buf.get(1)?.clone())
    }

    let opcode = buf.get(0)?.clone();
    let &(ref opcode, ref address_mode) =
        opcode_table::OPCODE_TABLE[opcode as usize].as_ref()?;


    let (operand, size) = match *address_mode {
        AddressMode::Absolute => (Operand::Absolute(op16(buf)?), 3),
        AddressMode::AbsoluteX => (Operand::AbsoluteX(op16(buf)?), 3),
        AddressMode::AbsoluteY => (Operand::AbsoluteY(op16(buf)?), 3),
        AddressMode::Accumulator => (Operand::Accumulator, 1),
        AddressMode::Immediate => (Operand::Immediate(op8(buf)?), 2),
        AddressMode::Implied => (Operand::Implied, 1),
        AddressMode::IndexedIndirect => (Operand::IndexedIndirect(op8(buf)?), 2),
        AddressMode::Indirect => (Operand::Indirect(op16(buf)?), 3),
        AddressMode::IndirectIndexed => (Operand::IndirectIndexed(op8(buf)?), 2),
        AddressMode::Relative => (Operand::Relative(op8(buf)? as i8), 2),
        AddressMode::ZeroPage => (Operand::ZeroPage(op8(buf)?), 2),
        AddressMode::ZeroPageX => (Operand::ZeroPageX(op8(buf)?), 2),
        AddressMode::ZeroPageY => (Operand::ZeroPageY(op8(buf)?), 2),
    };

    Some(Instruction::new(opcode.clone(), operand, size))
}