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

use std::fmt;
use crate::bytecode::instruction::Instruction;
use crate::bytecode::opcode::{
  SuperCode,
  misc::{self, move_subcode::DecodedMove},
  index::{self, DecodedIndex},
  comparison::{self, DecodedComparison},
  arithmetic::{self, DecodedArithmetic},
};
use crate::bytecode::operand::{
  FromDestination, FromSource,
  RawRegister, Global,
  WildDestination,
  InstructionPointer, FunctionKey
};

pub fn disassemble_instruction(f: &mut fmt::Formatter, instruction: Instruction) -> fmt::Result {
  match SuperCode::from(instruction) {
    SuperCode::Misc => {
      let subcode = misc::Subcode::from(instruction);
      match subcode {
        misc::Subcode::Jump => {
          use misc::jump_subcode::Reason;
          match Reason::from(instruction) {
            Reason::Special => {
              use misc::jump_subcode::Special;
              match Special::from(instruction) {
                Special::NoOp => write!(f, "nop"),
                Special::Return => write!(f, "ret"),
                _ => unimplemented!(),
              }
            }
            Reason::Always => {
              let instruction_pointer = InstructionPointer::from_both_operands(instruction);
              write!(f, "jmp   {instruction_pointer:<8}")
            }
            Reason::IfFalse => {
              use misc::jump_subcode::ConditionType;
              let instruction_pointer = InstructionPointer::from_both_operands(instruction);
              let condition_type = ConditionType::from(instruction);
              let condition: WildDestination<RawRegister> = match condition_type {
                ConditionType::Register => RawRegister::from_destination(instruction).into(),
                ConditionType::Global => Global::from_destination(instruction).into(),
              };
              write!(f, "jmp   {instruction_pointer:<8}  if !{condition}")
            }
            Reason::IfTrue => {
              use misc::jump_subcode::ConditionType;
              let instruction_pointer = InstructionPointer::from_both_operands(instruction);
              let condition_type = ConditionType::from(instruction);
              let condition: WildDestination<RawRegister> = match condition_type {
                ConditionType::Register => RawRegister::from_destination(instruction).into(),
                ConditionType::Global => Global::from_destination(instruction).into(),
              };
              write!(f, "jmp   {instruction_pointer:<8}  if {condition}")
            }
          }
        }
        misc::Subcode::Move => {
          let DecodedMove { destination, source } = misc::move_subcode::decode(instruction);
          write!(f, "mov   {destination} = {source}")
        }
        misc::Subcode::Call => {
          use misc::call_subcode::ArgCount;
          let arg_count = ArgCount::from(instruction);
          let arg_start = RawRegister::from_second(instruction);
          let dest = RawRegister::from_destination(instruction);
          let function = FunctionKey::from_first(instruction);
          match arg_count {
            ArgCount(0) => write!(f, "call  {dest} = {function}()"),
            ArgCount(1) => write!(f, "call  {dest} = {function}({arg_start})"),
            ArgCount(count) => {
              let last = RawRegister(arg_start.0 + count - 1);
              write!(f, "call  {dest} = {function}({arg_start}...{last})")
            }
          }
        }
        misc::Subcode::Interrupt => unimplemented!(),
      }
    }
    SuperCode::Index => {
      let DecodedIndex { index_on, destination, source, index } = index::decode(instruction);
      match index_on {
        index::On::Destination => write!(f, "idx   {destination}[{index}] = {source}"),
        index::On::Source => write!(f, "idx   {destination} = {source}[{index}]"),
      }
    }
    SuperCode::Comparison => {
      use comparison::Sources;
      let DecodedComparison { subcode, destination, sources } = comparison::decode(instruction);
      let subcode_op_str = subcode.op_str();
      match sources {
        Sources::FirstIsWild(first, second) =>
          write!(f, "{subcode:<4}  {destination} = {first} {subcode_op_str} {second}"),
        Sources::SecondIsWild(first, second) =>
          write!(f, "{subcode:<4}  {destination} = {first} {subcode_op_str} {second}"),
      }
    }
    SuperCode::Arithmetic => {
      use arithmetic::Sources;
      let DecodedArithmetic { subcode, destination, sources } = arithmetic::decode(instruction);
      let subcode_op_str = subcode.op_str();
      match sources {
        Sources::FirstIsWild(first, second) =>
          write!(f, "{subcode:<4}  {destination} = {first} {subcode_op_str} {second}"),
        Sources::SecondIsWild(first, second) =>
          write!(f, "{subcode:<4}  {destination} = {first} {subcode_op_str} {second}"),
      }
    }
  }
}