1use core::ops::RangeInclusive;
24
25use aluvm::isa::{Bytecode, BytecodeRead, BytecodeWrite, CodeEofError, CtrlInstr, ReservedInstr};
26use aluvm::SiteId;
27use amplify::num::{u2, u256, u3, u4};
28
29use super::{Bits, ConstVal, FieldInstr, Instr};
30use crate::{fe256, RegE};
31
32impl FieldInstr {
33 const START: u8 = 64;
34 const END: u8 = Self::START + Self::MUL;
35 const SET: u8 = 0;
36 const MOV: u8 = 1;
37 const EQ: u8 = 2;
38 const NEG: u8 = 3;
39 const ADD: u8 = 4;
40 const MUL: u8 = 5;
41}
42
43const SUB_TEST: u8 = 0b_0000;
44const SUB_CLR: u8 = 0b_0001;
45const SUB_PUTD: u8 = 0b_0010;
46const SUB_PUTZ: u8 = 0b_0011;
47const MASK_PUTV: u8 = 0b_1100;
48const TEST_PUTV: u8 = 0b_0100;
49const MASK_FITS: u8 = 0b_1000;
50const TEST_FITS: u8 = 0b_1000;
51
52impl<Id: SiteId> Bytecode<Id> for FieldInstr {
53 fn op_range() -> RangeInclusive<u8> { Self::START..=Self::END }
54
55 fn opcode_byte(&self) -> u8 {
56 Self::START
57 + match *self {
58 FieldInstr::Test { .. }
59 | FieldInstr::Clr { .. }
60 | FieldInstr::PutD { .. }
61 | FieldInstr::PutZ { .. }
62 | FieldInstr::PutV { .. }
63 | FieldInstr::Fits { .. } => Self::SET,
64 FieldInstr::Mov { .. } => Self::MOV,
65 FieldInstr::Eq { .. } => Self::EQ,
66 FieldInstr::Neg { .. } => Self::NEG,
67 FieldInstr::Add { .. } => Self::ADD,
68 FieldInstr::Mul { .. } => Self::MUL,
69 }
70 }
71
72 fn code_byte_len(&self) -> u16 {
73 let arg_len = match *self {
74 FieldInstr::Test { src: _ } => 1,
75 FieldInstr::Clr { dst: _ } => 1,
76 FieldInstr::PutD { dst: _, data: _ } => 3,
77 FieldInstr::PutZ { dst: _ } => 1,
78 FieldInstr::PutV { dst: _, val: _ } => 1,
79 FieldInstr::Fits { src: _, bits: _ } => 1,
80 FieldInstr::Mov { dst: _, src: _ } => 1,
81 FieldInstr::Eq { src1: _, src2: _ } => 1,
82 FieldInstr::Neg { dst: _, src: _ } => 1,
83 FieldInstr::Add { dst_src: _, src: _ } => 1,
84 FieldInstr::Mul { dst_src: _, src: _ } => 1,
85 };
86 arg_len + 1
87 }
88
89 fn external_ref(&self) -> Option<Id> { None }
90
91 fn encode_operands<W>(&self, writer: &mut W) -> Result<(), W::Error>
92 where W: BytecodeWrite<Id> {
93 match *self {
94 FieldInstr::Test { src } => {
95 writer.write_4bits(u4::with(SUB_TEST))?;
96 writer.write_4bits(src.to_u4())?;
97 }
98 FieldInstr::Clr { dst } => {
99 writer.write_4bits(u4::with(SUB_CLR))?;
100 writer.write_4bits(dst.to_u4())?;
101 }
102 FieldInstr::PutD { dst, data } => {
103 writer.write_4bits(u4::with(SUB_PUTD))?;
104 writer.write_4bits(dst.to_u4())?;
105 writer.write_fixed(data.to_u256().to_le_bytes())?;
106 }
107 FieldInstr::PutZ { dst } => {
108 writer.write_4bits(u4::with(SUB_PUTZ))?;
109 writer.write_4bits(dst.to_u4())?;
110 }
111 FieldInstr::PutV { dst, val } => {
112 let half = u4::with(TEST_PUTV | val.to_u2().to_u8());
113 writer.write_4bits(half)?;
114 writer.write_4bits(dst.to_u4())?;
115 }
116 FieldInstr::Fits { src, bits } => {
117 let half = u4::with(TEST_FITS | bits.to_u3().to_u8());
118 writer.write_4bits(half)?;
119 writer.write_4bits(src.to_u4())?;
120 }
121 FieldInstr::Mov { dst, src } => {
122 writer.write_4bits(dst.to_u4())?;
123 writer.write_4bits(src.to_u4())?;
124 }
125 FieldInstr::Eq { src1, src2 } => {
126 writer.write_4bits(src1.to_u4())?;
127 writer.write_4bits(src2.to_u4())?;
128 }
129 FieldInstr::Neg { dst, src } => {
130 writer.write_4bits(dst.to_u4())?;
131 writer.write_4bits(src.to_u4())?;
132 }
133 FieldInstr::Add { dst_src, src } => {
134 writer.write_4bits(dst_src.to_u4())?;
135 writer.write_4bits(src.to_u4())?;
136 }
137 FieldInstr::Mul { dst_src, src } => {
138 writer.write_4bits(dst_src.to_u4())?;
139 writer.write_4bits(src.to_u4())?;
140 }
141 }
142 Ok(())
143 }
144
145 fn decode_operands<R>(reader: &mut R, opcode: u8) -> Result<Self, CodeEofError>
146 where
147 Self: Sized,
148 R: BytecodeRead<Id>,
149 {
150 Ok(match opcode - Self::START {
151 Self::SET => {
152 let sub = u4::from(reader.read_4bits()?).to_u8();
153 match sub {
154 SUB_TEST => {
155 let src = RegE::from(reader.read_4bits()?);
156 FieldInstr::Test { src }
157 }
158 SUB_CLR => {
159 let dst = RegE::from(reader.read_4bits()?);
160 FieldInstr::Clr { dst }
161 }
162 SUB_PUTD => {
163 let dst = RegE::from(reader.read_4bits()?);
164 let data = reader.read_fixed(|d: [u8; 32]| fe256::from(u256::from_le_bytes(d)))?;
165 FieldInstr::PutD { dst, data }
166 }
167 SUB_PUTZ => {
168 let dst = RegE::from(reader.read_4bits()?);
169 FieldInstr::PutZ { dst }
170 }
171 x if x & MASK_PUTV == TEST_PUTV => {
172 let val = ConstVal::from(u2::with(sub & !MASK_PUTV));
173 let dst = RegE::from(reader.read_4bits()?);
174 FieldInstr::PutV { dst, val }
175 }
176 x if x & MASK_FITS == TEST_FITS => {
177 let bits = Bits::from(u3::with(sub & !MASK_FITS));
178 let src = RegE::from(reader.read_4bits()?);
179 FieldInstr::Fits { src, bits }
180 }
181 _ => unreachable!(),
182 }
183 }
184 Self::MOV => {
185 let dst = RegE::from(reader.read_4bits()?);
186 let src = RegE::from(reader.read_4bits()?);
187 FieldInstr::Mov { dst, src }
188 }
189 Self::EQ => {
190 let src1 = RegE::from(reader.read_4bits()?);
191 let src2 = RegE::from(reader.read_4bits()?);
192 FieldInstr::Eq { src1, src2 }
193 }
194 Self::NEG => {
195 let dst = RegE::from(reader.read_4bits()?);
196 let src = RegE::from(reader.read_4bits()?);
197 FieldInstr::Neg { dst, src }
198 }
199 Self::ADD => {
200 let dst_src = RegE::from(reader.read_4bits()?);
201 let src = RegE::from(reader.read_4bits()?);
202 FieldInstr::Add { dst_src, src }
203 }
204 Self::MUL => {
205 let dst_src = RegE::from(reader.read_4bits()?);
206 let src = RegE::from(reader.read_4bits()?);
207 FieldInstr::Mul { dst_src, src }
208 }
209 _ => unreachable!(),
210 })
211 }
212}
213
214impl<Id: SiteId> Bytecode<Id> for Instr<Id> {
215 fn op_range() -> RangeInclusive<u8> { 0..=0xFF }
216
217 fn opcode_byte(&self) -> u8 {
218 match self {
219 Instr::Ctrl(instr) => instr.opcode_byte(),
220 Instr::Gfa(instr) => Bytecode::<Id>::opcode_byte(instr),
221 Instr::Reserved(instr) => Bytecode::<Id>::opcode_byte(instr),
222 }
223 }
224
225 fn code_byte_len(&self) -> u16 {
226 match self {
227 Instr::Ctrl(instr) => instr.code_byte_len(),
228 Instr::Gfa(instr) => Bytecode::<Id>::code_byte_len(instr),
229 Instr::Reserved(instr) => Bytecode::<Id>::code_byte_len(instr),
230 }
231 }
232
233 fn external_ref(&self) -> Option<Id> {
234 match self {
235 Instr::Ctrl(instr) => instr.external_ref(),
236 Instr::Gfa(instr) => Bytecode::<Id>::external_ref(instr),
237 Instr::Reserved(instr) => Bytecode::<Id>::external_ref(instr),
238 }
239 }
240
241 fn encode_operands<W>(&self, writer: &mut W) -> Result<(), W::Error>
242 where W: BytecodeWrite<Id> {
243 match self {
244 Instr::Ctrl(instr) => instr.encode_operands(writer),
245 Instr::Gfa(instr) => instr.encode_operands(writer),
246 Instr::Reserved(instr) => instr.encode_operands(writer),
247 }
248 }
249
250 fn decode_operands<R>(reader: &mut R, opcode: u8) -> Result<Self, CodeEofError>
251 where
252 Self: Sized,
253 R: BytecodeRead<Id>,
254 {
255 match opcode {
256 op if CtrlInstr::<Id>::op_range().contains(&op) => {
257 CtrlInstr::<Id>::decode_operands(reader, op).map(Self::Ctrl)
258 }
259 op if <FieldInstr as Bytecode<Id>>::op_range().contains(&op) => {
260 FieldInstr::decode_operands(reader, op).map(Self::Gfa)
261 }
262 _ => ReservedInstr::decode_operands(reader, opcode).map(Self::Reserved),
263 }
264 }
265}
266
267#[cfg(test)]
268mod test {
269 use core::str::FromStr;
270
271 use aluvm::{LibId, LibsSeg, Marshaller};
272 use amplify::confinement::SmallBlob;
273
274 use super::*;
275 use crate::RegE;
276
277 const LIB_ID: &str = "5iMb1eHJ-bN5BOe6-9RvBjYL-jF1ELjj-VV7c8Bm-WvFen1Q";
278
279 fn roundtrip(instr: impl Into<Instr<LibId>>, bytecode: impl AsRef<[u8]>, dataseg: Option<&[u8]>) -> SmallBlob {
280 let instr = instr.into();
281 let mut libs = LibsSeg::new();
282 libs.push(LibId::from_str(LIB_ID).unwrap()).unwrap();
283 let mut marshaller = Marshaller::new(&libs);
284 instr.encode_instr(&mut marshaller).unwrap();
285 let (code, data) = marshaller.finish();
286 assert_eq!(code.as_slice(), bytecode.as_ref());
287 if let Some(d) = dataseg {
288 assert_eq!(data.as_slice(), d.as_ref());
289 } else {
290 assert!(data.is_empty());
291 }
292 let mut marshaller = Marshaller::with(code, data, &libs);
293 let decoded = Instr::<LibId>::decode_instr(&mut marshaller).unwrap();
294 assert_eq!(decoded, instr);
295 marshaller.into_code_data().1
296 }
297
298 #[test]
299 fn test() {
300 for reg in RegE::ALL {
301 let instr = FieldInstr::Test { src: reg };
302 let opcode = FieldInstr::START + FieldInstr::SET;
303 let sub = reg.to_u4().to_u8() << 4 | SUB_TEST;
304
305 roundtrip(instr, [opcode, sub], None);
306 }
307 }
308
309 #[test]
310 fn clr() {
311 for reg in RegE::ALL {
312 let instr = FieldInstr::Clr { dst: reg };
313 let opcode = FieldInstr::START + FieldInstr::SET;
314 let sub = reg.to_u4().to_u8() << 4 | SUB_CLR;
315
316 roundtrip(instr, [opcode, sub], None);
317 }
318 }
319
320 #[test]
321 fn putd() {
322 for reg in RegE::ALL {
323 let val = u256::from(0xdeadcafe1badbeef_u64);
324 let data = val.to_le_bytes();
325
326 let instr = FieldInstr::PutD {
327 dst: reg,
328 data: fe256::from(val),
329 };
330 let opcode = FieldInstr::START + FieldInstr::SET;
331 let sub = reg.to_u4().to_u8() << 4 | SUB_PUTD;
332
333 roundtrip(instr, [opcode, sub, 0, 0], Some(&data[..]));
334 }
335 }
336
337 #[test]
338 fn putz() {
339 for reg in RegE::ALL {
340 let instr = FieldInstr::PutZ { dst: reg };
341 let opcode = FieldInstr::START + FieldInstr::SET;
342 let sub = reg.to_u4().to_u8() << 4 | SUB_PUTZ;
343
344 roundtrip(instr, [opcode, sub], None);
345 }
346 }
347
348 #[test]
349 fn putv() {
350 for reg in RegE::ALL {
351 for val_u8 in 0..4 {
352 let val = ConstVal::from(u2::with(val_u8));
353 let instr = FieldInstr::PutV { dst: reg, val };
354 let opcode = FieldInstr::START + FieldInstr::SET;
355 let sub = reg.to_u4().to_u8() << 4 | TEST_PUTV | val.to_u2().to_u8();
356
357 roundtrip(instr, [opcode, sub], None);
358 }
359 }
360 }
361
362 #[test]
363 fn fits() {
364 for reg in RegE::ALL {
365 for bits_u8 in 0..8 {
366 let bits = Bits::from(u3::with(bits_u8));
367 let instr = FieldInstr::Fits { src: reg, bits };
368 let opcode = FieldInstr::START + FieldInstr::SET;
369 let sub = reg.to_u4().to_u8() << 4 | TEST_FITS | bits.to_u3().to_u8();
370
371 roundtrip(instr, [opcode, sub], None);
372 }
373 }
374 }
375
376 #[test]
377 fn mov() {
378 for reg1 in RegE::ALL {
379 for reg2 in RegE::ALL {
380 let instr = FieldInstr::Mov { dst: reg1, src: reg2 };
381 let opcode = FieldInstr::START + FieldInstr::MOV;
382 let regs = reg2.to_u4().to_u8() << 4 | reg1.to_u4().to_u8();
383
384 roundtrip(instr, [opcode, regs], None);
385 }
386 }
387 }
388
389 #[test]
390 fn eq() {
391 for reg1 in RegE::ALL {
392 for reg2 in RegE::ALL {
393 let instr = FieldInstr::Eq { src1: reg1, src2: reg2 };
394 let opcode = FieldInstr::START + FieldInstr::EQ;
395 let regs = reg2.to_u4().to_u8() << 4 | reg1.to_u4().to_u8();
396
397 roundtrip(instr, [opcode, regs], None);
398 }
399 }
400 }
401
402 #[test]
403 fn neq_mod() {
404 for reg1 in RegE::ALL {
405 for reg2 in RegE::ALL {
406 let instr = FieldInstr::Neg { dst: reg1, src: reg2 };
407 let opcode = FieldInstr::START + FieldInstr::NEG;
408 let regs = reg2.to_u4().to_u8() << 4 | reg1.to_u4().to_u8();
409
410 roundtrip(instr, [opcode, regs], None);
411 }
412 }
413 }
414
415 #[test]
416 fn add_mod() {
417 for reg1 in RegE::ALL {
418 for reg2 in RegE::ALL {
419 let instr = FieldInstr::Add {
420 dst_src: reg1,
421 src: reg2,
422 };
423 let opcode = FieldInstr::START + FieldInstr::ADD;
424 let regs = reg2.to_u4().to_u8() << 4 | reg1.to_u4().to_u8();
425
426 roundtrip(instr, [opcode, regs], None);
427 }
428 }
429 }
430
431 #[test]
432 fn mul_mod() {
433 for reg1 in RegE::ALL {
434 for reg2 in RegE::ALL {
435 let instr = FieldInstr::Mul {
436 dst_src: reg1,
437 src: reg2,
438 };
439 let opcode = FieldInstr::START + FieldInstr::MUL;
440 let regs = reg2.to_u4().to_u8() << 4 | reg1.to_u4().to_u8();
441
442 roundtrip(instr, [opcode, regs], None);
443 }
444 }
445 }
446}