watchmaker_vm/bytecode/
serialization.rs

1use crate::instruction::*;
2use crate::{
3    MASK_VALUE, OFFSET_IS_ADDRESS, OFFSET_IS_INDIRECT, OFFSET_IS_STATE, OFFSET_OPERAND_1,
4    OFFSET_OPERAND_2, OFFSET_OPERAND_3, OFFSET_OP_CODE,
5};
6use half::f16;
7
8// Value when a 16-bit word is not used by an instruction.
9const UNUSED_WORD: u64 = 0;
10
11/// Convert from a type-safe and programmer friendly type to a raw u64 bytecode instruction.
12pub fn serialize(instruction: &Instruction) -> u64 {
13    match instruction {
14        NOP() => 0,
15        HLT() => 1 << OFFSET_OP_CODE,
16        SYN() => 2 << OFFSET_OP_CODE,
17        IIMOV(lint, rint) => make_instruction(3, make_lint(lint), make_rint(rint), UNUSED_WORD),
18        IDMOV(lint, rdouble) => {
19            make_instruction(4, make_lint(lint), make_rdouble(rdouble), UNUSED_WORD)
20        }
21        DIMOV(ldouble, rint) => {
22            make_instruction(5, make_ldouble(ldouble), make_rint(rint), UNUSED_WORD)
23        }
24        DDMOV(ldouble, rdouble) => {
25            make_instruction(6, make_ldouble(ldouble), make_rdouble(rdouble), UNUSED_WORD)
26        }
27        IADD(lint1, lint2, rint) => {
28            make_instruction(7, make_lint(lint1), make_lint(lint2), make_rint(rint))
29        }
30        IDIV(lint1, lint2, rint) => {
31            make_instruction(8, make_lint(lint1), make_lint(lint2), make_rint(rint))
32        }
33        IMOD(lint1, lint2, rint) => {
34            make_instruction(9, make_lint(lint1), make_lint(lint2), make_rint(rint))
35        }
36        IMUL(lint1, lint2, rint) => {
37            make_instruction(10, make_lint(lint1), make_lint(lint2), make_rint(rint))
38        }
39        ISUB(lint1, lint2, rint) => {
40            make_instruction(11, make_lint(lint1), make_lint(lint2), make_rint(rint))
41        }
42        DADD(ldouble1, ldouble2, rdouble) => make_instruction(
43            12,
44            make_ldouble(ldouble1),
45            make_ldouble(ldouble2),
46            make_rdouble(rdouble),
47        ),
48        DDIV(ldouble1, ldouble2, rdouble) => make_instruction(
49            13,
50            make_ldouble(ldouble1),
51            make_ldouble(ldouble2),
52            make_rdouble(rdouble),
53        ),
54        DMOD(ldouble1, ldouble2, rdouble) => make_instruction(
55            14,
56            make_ldouble(ldouble1),
57            make_ldouble(ldouble2),
58            make_rdouble(rdouble),
59        ),
60        DMUL(ldouble1, ldouble2, rdouble) => make_instruction(
61            15,
62            make_ldouble(ldouble1),
63            make_ldouble(ldouble2),
64            make_rdouble(rdouble),
65        ),
66        DSUB(ldouble1, ldouble2, rdouble) => make_instruction(
67            16,
68            make_ldouble(ldouble1),
69            make_ldouble(ldouble2),
70            make_rdouble(rdouble),
71        ),
72        IJEQ(lint1, lint2, code_offset) => make_instruction(
73            17,
74            make_lint(lint1),
75            make_lint(lint2),
76            make_code_offset(code_offset.offset),
77        ),
78        IJNE(lint1, lint2, code_offset) => make_instruction(
79            18,
80            make_lint(lint1),
81            make_lint(lint2),
82            make_code_offset(code_offset.offset),
83        ),
84        IJGT(lint1, lint2, code_offset) => make_instruction(
85            19,
86            make_lint(lint1),
87            make_lint(lint2),
88            make_code_offset(code_offset.offset),
89        ),
90        IJLT(lint1, lint2, code_offset) => make_instruction(
91            20,
92            make_lint(lint1),
93            make_lint(lint2),
94            make_code_offset(code_offset.offset),
95        ),
96        DJEQ(ldouble1, ldouble2, code_offset) => make_instruction(
97            21,
98            make_ldouble(ldouble1),
99            make_ldouble(ldouble2),
100            make_code_offset(code_offset.offset),
101        ),
102        DJNE(ldouble1, ldouble2, code_offset) => make_instruction(
103            22,
104            make_ldouble(ldouble1),
105            make_ldouble(ldouble2),
106            make_code_offset(code_offset.offset),
107        ),
108        DJGT(ldouble1, ldouble2, code_offset) => make_instruction(
109            23,
110            make_ldouble(ldouble1),
111            make_ldouble(ldouble2),
112            make_code_offset(code_offset.offset),
113        ),
114        DJLT(ldouble1, ldouble2, code_offset) => make_instruction(
115            24,
116            make_ldouble(ldouble1),
117            make_ldouble(ldouble2),
118            make_code_offset(code_offset.offset),
119        ),
120    }
121}
122
123pub fn make_instruction(op_code: u64, operand1: u64, operand2: u64, operand3: u64) -> u64 {
124    (op_code << OFFSET_OP_CODE)
125        | (operand1 << OFFSET_OPERAND_1)
126        | (operand2 << OFFSET_OPERAND_2)
127        | (operand3 << OFFSET_OPERAND_3)
128}
129
130fn make_operand(is_address: bool, is_indirect: bool, is_state: bool, value: u16) -> u64 {
131    (if is_address {
132        1 << OFFSET_IS_ADDRESS
133    } else {
134        0
135    }) | (if is_indirect {
136        1 << OFFSET_IS_INDIRECT
137    } else {
138        0
139    }) | (if is_state { 1 << OFFSET_IS_STATE } else { 0 })
140        | value as u64
141}
142
143fn make_ldouble(ldouble: &LeftDouble) -> u64 {
144    match ldouble {
145        LeftDouble::State(index, mode) => make_operand(true, *mode == Mode::Indirect, true, *index),
146        LeftDouble::Input(index, mode) => {
147            make_operand(true, *mode == Mode::Indirect, false, *index)
148        }
149        LeftDouble::Constant(value) => {
150            make_operand(false, false, false, f16::from_f64(*value).to_bits())
151        }
152    }
153}
154
155fn make_lint(lint: &LeftInteger) -> u64 {
156    match lint {
157        LeftInteger::State(index, mode) => {
158            make_operand(true, *mode == Mode::Indirect, true, *index)
159        }
160        LeftInteger::Input(index, mode) => {
161            make_operand(true, *mode == Mode::Indirect, false, *index)
162        }
163        LeftInteger::Constant(value) => make_operand(false, false, false, *value as u16),
164    }
165}
166
167fn make_rdouble(rdouble: &RightDouble) -> u64 {
168    match rdouble {
169        RightDouble::State(index, mode) => {
170            make_operand(true, *mode == Mode::Indirect, true, *index)
171        }
172        RightDouble::Output(index, mode) => {
173            make_operand(true, *mode == Mode::Indirect, false, *index)
174        }
175    }
176}
177
178fn make_rint(rint: &RightInteger) -> u64 {
179    match rint {
180        RightInteger::State(index, mode) => {
181            make_operand(true, *mode == Mode::Indirect, true, *index)
182        }
183        RightInteger::Output(index, mode) => {
184            make_operand(true, *mode == Mode::Indirect, false, *index)
185        }
186    }
187}
188
189fn make_code_offset(code_offset: i16) -> u64 {
190    (code_offset as u64) & MASK_VALUE
191}