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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-Exception
// Copyright 2024 Daniel Fox Franke.

//! Implementations for [`Instr`].

use core::fmt::Display;

use crate::error::AssemblerError;
use crate::instr_def::Instr;
use crate::operands::RawOperand;
use crate::resolver::Resolver;
use arrayvec::ArrayVec;
use bytes::BufMut;

/// The largest number of operands taken by any instruction.
///
/// `linearsearch` and `binarysearch` set the high-water mark.
pub(crate) const MAX_OPERANDS: usize = 8;

/// An encoded instruction ready to be serialized.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) struct RawInstr {
    /// The instruction's opcode.
    pub opcode: u32,
    /// List of operands to the instruction.
    pub operands: ArrayVec<RawOperand, MAX_OPERANDS>,
}

/// Return the number of arguments the macro was called with.
macro_rules! count {
    ($(,)*) => (0u32);
    ($current:expr $(,)*) => (1u32);
    ( $current:expr, $($rest:expr),* $(,)*) => (const { 1 + count!($($rest),*) });
}

/// Call arg.resolve(position, resolver) for each argument after the
/// third, correctly updating position for each call. Position at the start of
/// the invocation should the end of the opcode.
macro_rules! resolve {
    ($position:expr, $resolver:expr, $($x:expr),* $(,)*) => {
        {
            #[allow(unused_mut)]
            let mut v = ArrayVec::<RawOperand, MAX_OPERANDS>::new();
            let n_args = count!($($x),*);

            let mut _position = $position.checked_add(n_args.div_ceil(2))
                .ok_or(AssemblerError::Overflow)?;

            $(
                let operand = $x.resolve(_position, $resolver)?;
                _position = _position.checked_add(u32::try_from(operand.len())
                    .or(Err(AssemblerError::Overflow))?)
                    .ok_or(AssemblerError::Overflow)?;
                v.push(operand);
            )*

            v
        }
    };
}

/// Call arg.worst_len() on each argument and return the sum of the results plus
/// the space occupied by the addressing-mode nibbles.
macro_rules! worst_len {
    ($($x:expr),* $(,)*) => {
        {
            let oplens = [$($x.worst_len()),*];
            let oplens_slice = oplens.as_slice();
            let modelen  = oplens_slice.len().div_ceil(2);
            let oplen_sum = oplens_slice.iter().copied().sum::<usize>();
            modelen + oplen_sum
        }
    };
}

impl<L> Instr<L> {
    /// Applies the given mapping function to all labels within the instruction.
    pub fn map<F, M>(self, mut f: F) -> Instr<M>
    where
        F: FnMut(L) -> M,
    {
        match self {
            Instr::Nop => Instr::Nop,
            Instr::Add(l1, l2, s1) => Instr::Add(l1.map(&mut f), l2.map(&mut f), s1.map(&mut f)),
            Instr::Sub(l1, l2, s1) => Instr::Sub(l1.map(&mut f), l2.map(&mut f), s1.map(&mut f)),
            Instr::Mul(l1, l2, s1) => Instr::Mul(l1.map(&mut f), l2.map(&mut f), s1.map(&mut f)),
            Instr::Div(l1, l2, s1) => Instr::Div(l1.map(&mut f), l2.map(&mut f), s1.map(&mut f)),
            Instr::Mod(l1, l2, s1) => Instr::Mod(l1.map(&mut f), l2.map(&mut f), s1.map(&mut f)),
            Instr::Neg(l1, s1) => Instr::Neg(l1.map(&mut f), s1.map(&mut f)),
            Instr::Bitand(l1, l2, s1) => {
                Instr::Bitand(l1.map(&mut f), l2.map(&mut f), s1.map(&mut f))
            }
            Instr::Bitor(l1, l2, s1) => {
                Instr::Bitor(l1.map(&mut f), l2.map(&mut f), s1.map(&mut f))
            }
            Instr::Bitxor(l1, l2, s1) => {
                Instr::Bitxor(l1.map(&mut f), l2.map(&mut f), s1.map(&mut f))
            }
            Instr::Bitnot(l1, s1) => Instr::Bitnot(l1.map(&mut f), s1.map(&mut f)),
            Instr::Shiftl(l1, l2, s1) => {
                Instr::Shiftl(l1.map(&mut f), l2.map(&mut f), s1.map(&mut f))
            }
            Instr::Ushiftr(l1, l2, s1) => {
                Instr::Ushiftr(l1.map(&mut f), l2.map(&mut f), s1.map(&mut f))
            }
            Instr::Sshiftr(l1, l2, s1) => {
                Instr::Sshiftr(l1.map(&mut f), l2.map(&mut f), s1.map(&mut f))
            }
            Instr::Jump(l1) => Instr::Jump(l1.map(&mut f)),
            Instr::Jz(l1, l2) => Instr::Jz(l1.map(&mut f), l2.map(&mut f)),
            Instr::Jnz(l1, l2) => Instr::Jnz(l1.map(&mut f), l2.map(&mut f)),
            Instr::Jeq(l1, l2, l3) => Instr::Jeq(l1.map(&mut f), l2.map(&mut f), l3.map(&mut f)),
            Instr::Jne(l1, l2, l3) => Instr::Jne(l1.map(&mut f), l2.map(&mut f), l3.map(&mut f)),
            Instr::Jlt(l1, l2, l3) => Instr::Jlt(l1.map(&mut f), l2.map(&mut f), l3.map(&mut f)),
            Instr::Jle(l1, l2, l3) => Instr::Jle(l1.map(&mut f), l2.map(&mut f), l3.map(&mut f)),
            Instr::Jgt(l1, l2, l3) => Instr::Jgt(l1.map(&mut f), l2.map(&mut f), l3.map(&mut f)),
            Instr::Jge(l1, l2, l3) => Instr::Jge(l1.map(&mut f), l2.map(&mut f), l3.map(&mut f)),
            Instr::Jltu(l1, l2, l3) => Instr::Jltu(l1.map(&mut f), l2.map(&mut f), l3.map(&mut f)),
            Instr::Jleu(l1, l2, l3) => Instr::Jleu(l1.map(&mut f), l2.map(&mut f), l3.map(&mut f)),
            Instr::Jgtu(l1, l2, l3) => Instr::Jgtu(l1.map(&mut f), l2.map(&mut f), l3.map(&mut f)),
            Instr::Jgeu(l1, l2, l3) => Instr::Jgeu(l1.map(&mut f), l2.map(&mut f), l3.map(&mut f)),
            Instr::Jumpabs(l1) => Instr::Jumpabs(l1.map(&mut f)),
            Instr::Copy(l1, s1) => Instr::Copy(l1.map(&mut f), s1.map(&mut f)),
            Instr::Copys(l1, s1) => Instr::Copys(l1.map(&mut f), s1.map(&mut f)),
            Instr::Copyb(l1, s1) => Instr::Copyb(l1.map(&mut f), s1.map(&mut f)),
            Instr::Sexs(l1, s1) => Instr::Sexs(l1.map(&mut f), s1.map(&mut f)),
            Instr::Sexb(l1, s1) => Instr::Sexb(l1.map(&mut f), s1.map(&mut f)),
            Instr::Astore(l1, l2, l3) => {
                Instr::Astore(l1.map(&mut f), l2.map(&mut f), l3.map(&mut f))
            }
            Instr::Aload(l1, l2, s1) => {
                Instr::Aload(l1.map(&mut f), l2.map(&mut f), s1.map(&mut f))
            }
            Instr::Astores(l1, l2, l3) => {
                Instr::Astores(l1.map(&mut f), l2.map(&mut f), l3.map(&mut f))
            }
            Instr::Aloads(l1, l2, s1) => {
                Instr::Aloads(l1.map(&mut f), l2.map(&mut f), s1.map(&mut f))
            }
            Instr::Astoreb(l1, l2, l3) => {
                Instr::Astoreb(l1.map(&mut f), l2.map(&mut f), l3.map(&mut f))
            }
            Instr::Aloadb(l1, l2, s1) => {
                Instr::Aloadb(l1.map(&mut f), l2.map(&mut f), s1.map(&mut f))
            }
            Instr::Astorebit(l1, l2, l3) => {
                Instr::Astorebit(l1.map(&mut f), l2.map(&mut f), l3.map(&mut f))
            }
            Instr::Aloadbit(l1, l2, s1) => {
                Instr::Aloadbit(l1.map(&mut f), l2.map(&mut f), s1.map(&mut f))
            }
            Instr::Stkcount(s1) => Instr::Stkcount(s1.map(&mut f)),
            Instr::Stkpeek(l1, s1) => Instr::Stkpeek(l1.map(&mut f), s1.map(&mut f)),
            Instr::Stkswap => Instr::Stkswap,
            Instr::Stkcopy(l1) => Instr::Stkcopy(l1.map(&mut f)),
            Instr::Stkroll(l1, l2) => Instr::Stkroll(l1.map(&mut f), l2.map(&mut f)),
            Instr::Call(l1, l2, s1) => Instr::Call(l1.map(&mut f), l2.map(&mut f), s1.map(&mut f)),
            Instr::Callf(l1, s1) => Instr::Callf(l1.map(&mut f), s1.map(&mut f)),
            Instr::Callfi(l1, l2, s1) => {
                Instr::Callfi(l1.map(&mut f), l2.map(&mut f), s1.map(&mut f))
            }
            Instr::Callfii(l1, l2, l3, s1) => Instr::Callfii(
                l1.map(&mut f),
                l2.map(&mut f),
                l3.map(&mut f),
                s1.map(&mut f),
            ),
            Instr::Callfiii(l1, l2, l3, l4, s1) => Instr::Callfiii(
                l1.map(&mut f),
                l2.map(&mut f),
                l3.map(&mut f),
                l4.map(&mut f),
                s1.map(&mut f),
            ),
            Instr::Return(l1) => Instr::Return(l1.map(&mut f)),
            Instr::Tailcall(l1, l2) => Instr::Tailcall(l1.map(&mut f), l2.map(&mut f)),
            Instr::Catch(s1, l1) => Instr::Catch(s1.map(&mut f), l1.map(&mut f)),
            Instr::Throw(l1, l2) => Instr::Throw(l1.map(&mut f), l2.map(&mut f)),
            Instr::Getmemsize(s1) => Instr::Getmemsize(s1.map(&mut f)),
            Instr::Setmemsize(l1, s1) => Instr::Setmemsize(l1.map(&mut f), s1.map(&mut f)),
            Instr::Malloc(l1, s1) => Instr::Malloc(l1.map(&mut f), s1.map(&mut f)),
            Instr::Mfree(l1) => Instr::Mfree(l1.map(&mut f)),
            Instr::Quit => Instr::Quit,
            Instr::Restart => Instr::Restart,
            Instr::Save(l1, s1) => Instr::Save(l1.map(&mut f), s1.map(&mut f)),
            Instr::Restore(l1, s1) => Instr::Restore(l1.map(&mut f), s1.map(&mut f)),
            Instr::Saveundo(s1) => Instr::Saveundo(s1.map(&mut f)),
            Instr::Restoreundo(s1) => Instr::Restoreundo(s1.map(&mut f)),
            Instr::Hasundo(s1) => Instr::Hasundo(s1.map(&mut f)),
            Instr::Discardundo => Instr::Discardundo,
            Instr::Protect(l1, l2) => Instr::Protect(l1.map(&mut f), l2.map(&mut f)),
            Instr::Verify(s1) => Instr::Verify(s1.map(&mut f)),
            Instr::Getiosys(s1, s2) => Instr::Getiosys(s1.map(&mut f), s2.map(&mut f)),
            Instr::Setiosys(l1, l2) => Instr::Setiosys(l1.map(&mut f), l2.map(&mut f)),
            Instr::Streamchar(l1) => Instr::Streamchar(l1.map(&mut f)),
            Instr::Streamunichar(l1) => Instr::Streamunichar(l1.map(&mut f)),
            Instr::Streamnum(l1) => Instr::Streamnum(l1.map(&mut f)),
            Instr::Streamstr(l1) => Instr::Streamstr(l1.map(&mut f)),
            Instr::Getstringtbl(s1) => Instr::Getstringtbl(s1.map(&mut f)),
            Instr::Setstringtbl(l1) => Instr::Setstringtbl(l1.map(&mut f)),
            Instr::Numtof(l1, s1) => Instr::Numtof(l1.map(&mut f), s1.map(&mut f)),
            Instr::Ftonumz(l1, s1) => Instr::Ftonumz(l1.map(&mut f), s1.map(&mut f)),
            Instr::Ftonumn(l1, s1) => Instr::Ftonumn(l1.map(&mut f), s1.map(&mut f)),
            Instr::Fadd(l1, l2, s1) => Instr::Fadd(l1.map(&mut f), l2.map(&mut f), s1.map(&mut f)),
            Instr::Fsub(l1, l2, s1) => Instr::Fsub(l1.map(&mut f), l2.map(&mut f), s1.map(&mut f)),
            Instr::Fmul(l1, l2, s1) => Instr::Fmul(l1.map(&mut f), l2.map(&mut f), s1.map(&mut f)),
            Instr::Fdiv(l1, l2, s1) => Instr::Fdiv(l1.map(&mut f), l2.map(&mut f), s1.map(&mut f)),
            Instr::Fmod(l1, l2, s1, s2) => Instr::Fmod(
                l1.map(&mut f),
                l2.map(&mut f),
                s1.map(&mut f),
                s2.map(&mut f),
            ),
            Instr::Ceil(l1, s1) => Instr::Ceil(l1.map(&mut f), s1.map(&mut f)),
            Instr::Floor(l1, s1) => Instr::Floor(l1.map(&mut f), s1.map(&mut f)),
            Instr::Sqrt(l1, s1) => Instr::Sqrt(l1.map(&mut f), s1.map(&mut f)),
            Instr::Exp(l1, s1) => Instr::Exp(l1.map(&mut f), s1.map(&mut f)),
            Instr::Log(l1, s1) => Instr::Log(l1.map(&mut f), s1.map(&mut f)),
            Instr::Pow(l1, l2, s1) => Instr::Pow(l1.map(&mut f), l2.map(&mut f), s1.map(&mut f)),
            Instr::Sin(l1, s1) => Instr::Sin(l1.map(&mut f), s1.map(&mut f)),
            Instr::Cos(l1, s1) => Instr::Cos(l1.map(&mut f), s1.map(&mut f)),
            Instr::Tan(l1, s1) => Instr::Tan(l1.map(&mut f), s1.map(&mut f)),
            Instr::Asin(l1, s1) => Instr::Asin(l1.map(&mut f), s1.map(&mut f)),
            Instr::Acos(l1, s1) => Instr::Acos(l1.map(&mut f), s1.map(&mut f)),
            Instr::Atan(l1, s1) => Instr::Atan(l1.map(&mut f), s1.map(&mut f)),
            Instr::Atan2(l1, s1) => Instr::Atan2(l1.map(&mut f), s1.map(&mut f)),
            Instr::Numtod(l1, s1, s2) => {
                Instr::Numtod(l1.map(&mut f), s1.map(&mut f), s2.map(&mut f))
            }
            Instr::Dtonumz(l1, l2, s1) => {
                Instr::Dtonumz(l1.map(&mut f), l2.map(&mut f), s1.map(&mut f))
            }
            Instr::Dtonumn(l1, l2, s1) => {
                Instr::Dtonumn(l1.map(&mut f), l2.map(&mut f), s1.map(&mut f))
            }
            Instr::Ftod(l1, s1, s2) => Instr::Ftod(l1.map(&mut f), s1.map(&mut f), s2.map(&mut f)),
            Instr::Dtof(l1, l2, s1) => Instr::Dtof(l1.map(&mut f), l2.map(&mut f), s1.map(&mut f)),
            Instr::Dadd(l1, l2, l3, l4, s1, s2) => Instr::Dadd(
                l1.map(&mut f),
                l2.map(&mut f),
                l3.map(&mut f),
                l4.map(&mut f),
                s1.map(&mut f),
                s2.map(&mut f),
            ),
            Instr::Dsub(l1, l2, l3, l4, s1, s2) => Instr::Dsub(
                l1.map(&mut f),
                l2.map(&mut f),
                l3.map(&mut f),
                l4.map(&mut f),
                s1.map(&mut f),
                s2.map(&mut f),
            ),
            Instr::Dmul(l1, l2, l3, l4, s1, s2) => Instr::Dmul(
                l1.map(&mut f),
                l2.map(&mut f),
                l3.map(&mut f),
                l4.map(&mut f),
                s1.map(&mut f),
                s2.map(&mut f),
            ),
            Instr::Ddiv(l1, l2, l3, l4, s1, s2) => Instr::Ddiv(
                l1.map(&mut f),
                l2.map(&mut f),
                l3.map(&mut f),
                l4.map(&mut f),
                s1.map(&mut f),
                s2.map(&mut f),
            ),
            Instr::Dmodr(l1, l2, l3, l4, s1, s2) => Instr::Dmodr(
                l1.map(&mut f),
                l2.map(&mut f),
                l3.map(&mut f),
                l4.map(&mut f),
                s1.map(&mut f),
                s2.map(&mut f),
            ),
            Instr::Dmodq(l1, l2, l3, l4, s1, s2) => Instr::Dmodq(
                l1.map(&mut f),
                l2.map(&mut f),
                l3.map(&mut f),
                l4.map(&mut f),
                s1.map(&mut f),
                s2.map(&mut f),
            ),
            Instr::Dceil(l1, l2, s1, s2) => Instr::Dceil(
                l1.map(&mut f),
                l2.map(&mut f),
                s1.map(&mut f),
                s2.map(&mut f),
            ),
            Instr::Dfloor(l1, l2, s1, s2) => Instr::Dfloor(
                l1.map(&mut f),
                l2.map(&mut f),
                s1.map(&mut f),
                s2.map(&mut f),
            ),
            Instr::Dsqrt(l1, l2, s1, s2) => Instr::Dsqrt(
                l1.map(&mut f),
                l2.map(&mut f),
                s1.map(&mut f),
                s2.map(&mut f),
            ),
            Instr::Dexp(l1, l2, s1, s2) => Instr::Dexp(
                l1.map(&mut f),
                l2.map(&mut f),
                s1.map(&mut f),
                s2.map(&mut f),
            ),
            Instr::Dlog(l1, l2, s1, s2) => Instr::Dlog(
                l1.map(&mut f),
                l2.map(&mut f),
                s1.map(&mut f),
                s2.map(&mut f),
            ),
            Instr::Dpow(l1, l2, l3, l4, s1, s2) => Instr::Dpow(
                l1.map(&mut f),
                l2.map(&mut f),
                l3.map(&mut f),
                l4.map(&mut f),
                s1.map(&mut f),
                s2.map(&mut f),
            ),
            Instr::Dsin(l1, l2, s1, s2) => Instr::Dsin(
                l1.map(&mut f),
                l2.map(&mut f),
                s1.map(&mut f),
                s2.map(&mut f),
            ),
            Instr::Dcos(l1, l2, s1, s2) => Instr::Dcos(
                l1.map(&mut f),
                l2.map(&mut f),
                s1.map(&mut f),
                s2.map(&mut f),
            ),
            Instr::Dtan(l1, l2, s1, s2) => Instr::Dtan(
                l1.map(&mut f),
                l2.map(&mut f),
                s1.map(&mut f),
                s2.map(&mut f),
            ),
            Instr::Dasin(l1, l2, s1, s2) => Instr::Dasin(
                l1.map(&mut f),
                l2.map(&mut f),
                s1.map(&mut f),
                s2.map(&mut f),
            ),
            Instr::Dacos(l1, l2, s1, s2) => Instr::Dacos(
                l1.map(&mut f),
                l2.map(&mut f),
                s1.map(&mut f),
                s2.map(&mut f),
            ),
            Instr::Datan(l1, l2, s1, s2) => Instr::Datan(
                l1.map(&mut f),
                l2.map(&mut f),
                s1.map(&mut f),
                s2.map(&mut f),
            ),
            Instr::Datan2(l1, l2, l3, l4, s1, s2) => Instr::Datan2(
                l1.map(&mut f),
                l2.map(&mut f),
                l3.map(&mut f),
                l4.map(&mut f),
                s1.map(&mut f),
                s2.map(&mut f),
            ),
            Instr::Jisnan(l1, l2) => Instr::Jisnan(l1.map(&mut f), l2.map(&mut f)),
            Instr::Jisinf(l1, l2) => Instr::Jisinf(l1.map(&mut f), l2.map(&mut f)),
            Instr::Jfeq(l1, l2, l3, l4) => Instr::Jfeq(
                l1.map(&mut f),
                l2.map(&mut f),
                l3.map(&mut f),
                l4.map(&mut f),
            ),
            Instr::Jfne(l1, l2, l3, l4) => Instr::Jfne(
                l1.map(&mut f),
                l2.map(&mut f),
                l3.map(&mut f),
                l4.map(&mut f),
            ),
            Instr::Jflt(l1, l2, l3) => Instr::Jflt(l1.map(&mut f), l2.map(&mut f), l3.map(&mut f)),
            Instr::Jfle(l1, l2, l3) => Instr::Jfle(l1.map(&mut f), l2.map(&mut f), l3.map(&mut f)),
            Instr::Jfgt(l1, l2, l3) => Instr::Jfgt(l1.map(&mut f), l2.map(&mut f), l3.map(&mut f)),
            Instr::Jfge(l1, l2, l3) => Instr::Jfge(l1.map(&mut f), l2.map(&mut f), l3.map(&mut f)),
            Instr::Jdisnan(l1, l2, l3) => {
                Instr::Jdisnan(l1.map(&mut f), l2.map(&mut f), l3.map(&mut f))
            }
            Instr::Jdisinf(l1, l2, l3) => {
                Instr::Jdisinf(l1.map(&mut f), l2.map(&mut f), l3.map(&mut f))
            }
            Instr::Jdeq(l1, l2, l3, l4, l5, l6, l7) => Instr::Jdeq(
                l1.map(&mut f),
                l2.map(&mut f),
                l3.map(&mut f),
                l4.map(&mut f),
                l5.map(&mut f),
                l6.map(&mut f),
                l7.map(&mut f),
            ),
            Instr::Jdne(l1, l2, l3, l4, l5, l6, l7) => Instr::Jdne(
                l1.map(&mut f),
                l2.map(&mut f),
                l3.map(&mut f),
                l4.map(&mut f),
                l5.map(&mut f),
                l6.map(&mut f),
                l7.map(&mut f),
            ),
            Instr::Jdlt(l1, l2, l3, l4, l5) => Instr::Jdlt(
                l1.map(&mut f),
                l2.map(&mut f),
                l3.map(&mut f),
                l4.map(&mut f),
                l5.map(&mut f),
            ),
            Instr::Jdle(l1, l2, l3, l4, l5) => Instr::Jdle(
                l1.map(&mut f),
                l2.map(&mut f),
                l3.map(&mut f),
                l4.map(&mut f),
                l5.map(&mut f),
            ),
            Instr::Jdgt(l1, l2, l3, l4, l5) => Instr::Jdgt(
                l1.map(&mut f),
                l2.map(&mut f),
                l3.map(&mut f),
                l4.map(&mut f),
                l5.map(&mut f),
            ),
            Instr::Jdge(l1, l2, l3, l4, l5) => Instr::Jdge(
                l1.map(&mut f),
                l2.map(&mut f),
                l3.map(&mut f),
                l4.map(&mut f),
                l5.map(&mut f),
            ),
            Instr::Random(l1, s1) => Instr::Random(l1.map(&mut f), s1.map(&mut f)),
            Instr::Setrandom(l1) => Instr::Setrandom(l1.map(&mut f)),
            Instr::Mzero(l1, l2) => Instr::Mzero(l1.map(&mut f), l2.map(&mut f)),
            Instr::Mcopy(l1, l2, l3) => {
                Instr::Mcopy(l1.map(&mut f), l2.map(&mut f), l3.map(&mut f))
            }
            Instr::Linearsearch(l1, l2, l3, l4, l5, l6, l7, s1) => Instr::Linearsearch(
                l1.map(&mut f),
                l2.map(&mut f),
                l3.map(&mut f),
                l4.map(&mut f),
                l5.map(&mut f),
                l6.map(&mut f),
                l7.map(&mut f),
                s1.map(&mut f),
            ),
            Instr::Binarysearch(l1, l2, l3, l4, l5, l6, l7, s1) => Instr::Binarysearch(
                l1.map(&mut f),
                l2.map(&mut f),
                l3.map(&mut f),
                l4.map(&mut f),
                l5.map(&mut f),
                l6.map(&mut f),
                l7.map(&mut f),
                s1.map(&mut f),
            ),
            Instr::Linkedsearch(l1, l2, l3, l4, l5, l6, s1) => Instr::Linkedsearch(
                l1.map(&mut f),
                l2.map(&mut f),
                l3.map(&mut f),
                l4.map(&mut f),
                l5.map(&mut f),
                l6.map(&mut f),
                s1.map(&mut f),
            ),
            Instr::Accelfunc(l1, l2) => Instr::Accelfunc(l1.map(&mut f), l2.map(&mut f)),
            Instr::Accelparam(l1, l2) => Instr::Accelparam(l1.map(&mut f), l2.map(&mut f)),
            Instr::Gestalt(l1, l2, s1) => {
                Instr::Gestalt(l1.map(&mut f), l2.map(&mut f), s1.map(&mut f))
            }
            Instr::Debugtrap(l1) => Instr::Debugtrap(l1.map(&mut f)),
            Instr::Glk(l1, l2, s1) => Instr::Glk(l1.map(&mut f), l2.map(&mut f), s1.map(&mut f)),
        }
    }
}

impl<L> Instr<L>
where
    L: Clone,
{
    /// Returns an upper bound on how long the serialized instruction might be,
    /// regardless of its position.
    pub(crate) fn worst_len(&self) -> usize {
        let opcode = self.opcode();
        let opcode_length = opcode_len(opcode);

        let operands_length: usize = match self {
            Instr::Nop => 0,
            Instr::Add(l1, l2, s1) => worst_len!(l1, l2, s1),
            Instr::Sub(l1, l2, s1) => worst_len!(l1, l2, s1),
            Instr::Mul(l1, l2, s1) => worst_len!(l1, l2, s1),
            Instr::Div(l1, l2, s1) => worst_len!(l1, l2, s1),
            Instr::Mod(l1, l2, s1) => worst_len!(l1, l2, s1),
            Instr::Neg(l1, s1) => worst_len!(l1, s1),
            Instr::Bitand(l1, l2, s1) => worst_len!(l1, l2, s1),
            Instr::Bitor(l1, l2, s1) => worst_len!(l1, l2, s1),
            Instr::Bitxor(l1, l2, s1) => worst_len!(l1, l2, s1),
            Instr::Bitnot(l1, s1) => worst_len!(l1, s1),
            Instr::Shiftl(l1, l2, s1) => worst_len!(l1, l2, s1),
            Instr::Ushiftr(l1, l2, s1) => worst_len!(l1, l2, s1),
            Instr::Sshiftr(l1, l2, s1) => worst_len!(l1, l2, s1),
            Instr::Jump(l1) => worst_len!(l1),
            Instr::Jz(l1, l2) => worst_len!(l1, l2),
            Instr::Jnz(l1, l2) => worst_len!(l1, l2),
            Instr::Jeq(l1, l2, l3) => worst_len!(l1, l2, l3),
            Instr::Jne(l1, l2, l3) => worst_len!(l1, l2, l3),
            Instr::Jlt(l1, l2, l3) => worst_len!(l1, l2, l3),
            Instr::Jle(l1, l2, l3) => worst_len!(l1, l2, l3),
            Instr::Jgt(l1, l2, l3) => worst_len!(l1, l2, l3),
            Instr::Jge(l1, l2, l3) => worst_len!(l1, l2, l3),
            Instr::Jltu(l1, l2, l3) => worst_len!(l1, l2, l3),
            Instr::Jleu(l1, l2, l3) => worst_len!(l1, l2, l3),
            Instr::Jgtu(l1, l2, l3) => worst_len!(l1, l2, l3),
            Instr::Jgeu(l1, l2, l3) => worst_len!(l1, l2, l3),
            Instr::Jumpabs(l1) => worst_len!(l1),
            Instr::Copy(l1, s1) => worst_len!(l1, s1),
            Instr::Copys(l1, s1) => worst_len!(l1, s1),
            Instr::Copyb(l1, s1) => worst_len!(l1, s1),
            Instr::Sexs(l1, s1) => worst_len!(l1, s1),
            Instr::Sexb(l1, s1) => worst_len!(l1, s1),
            Instr::Astore(l1, l2, l3) => worst_len!(l1, l2, l3),
            Instr::Aload(l1, l2, s1) => worst_len!(l1, l2, s1),
            Instr::Astores(l1, l2, l3) => worst_len!(l1, l2, l3),
            Instr::Aloads(l1, l2, s1) => worst_len!(l1, l2, s1),
            Instr::Astoreb(l1, l2, l3) => worst_len!(l1, l2, l3),
            Instr::Aloadb(l1, l2, s1) => worst_len!(l1, l2, s1),
            Instr::Astorebit(l1, l2, l3) => worst_len!(l1, l2, l3),
            Instr::Aloadbit(l1, l2, s1) => worst_len!(l1, l2, s1),
            Instr::Stkcount(s1) => worst_len!(s1),
            Instr::Stkpeek(l1, s1) => worst_len!(l1, s1),
            Instr::Stkswap => 0,
            Instr::Stkcopy(l1) => worst_len!(l1),
            Instr::Stkroll(l1, l2) => worst_len!(l1, l2),
            Instr::Call(l1, l2, s1) => worst_len!(l1, l2, s1),
            Instr::Callf(l1, s1) => worst_len!(l1, s1),
            Instr::Callfi(l1, l2, s1) => worst_len!(l1, l2, s1),
            Instr::Callfii(l1, l2, l3, s1) => worst_len!(l1, l2, l3, s1),
            Instr::Callfiii(l1, l2, l3, l4, s1) => worst_len!(l1, l2, l3, l4, s1),
            Instr::Return(l1) => worst_len!(l1),
            Instr::Tailcall(l1, l2) => worst_len!(l1, l2),
            Instr::Catch(s1, l1) => worst_len!(s1, l1),
            Instr::Throw(l1, l2) => worst_len!(l1, l2),
            Instr::Getmemsize(s1) => worst_len!(s1),
            Instr::Setmemsize(l1, s1) => worst_len!(l1, s1),
            Instr::Malloc(l1, s1) => worst_len!(l1, s1),
            Instr::Mfree(l1) => worst_len!(l1),
            Instr::Quit => 0,
            Instr::Restart => 0,
            Instr::Save(l1, s1) => worst_len!(l1, s1),
            Instr::Restore(l1, s1) => worst_len!(l1, s1),
            Instr::Saveundo(s1) => worst_len!(s1),
            Instr::Restoreundo(s1) => worst_len!(s1),
            Instr::Hasundo(s1) => worst_len!(s1),
            Instr::Discardundo => 0,
            Instr::Protect(l1, l2) => worst_len!(l1, l2),
            Instr::Verify(s1) => worst_len!(s1),
            Instr::Getiosys(s1, s2) => worst_len!(s1, s2),
            Instr::Setiosys(l1, l2) => worst_len!(l1, l2),
            Instr::Streamchar(l1) => worst_len!(l1),
            Instr::Streamunichar(l1) => worst_len!(l1),
            Instr::Streamnum(l1) => worst_len!(l1),
            Instr::Streamstr(l1) => worst_len!(l1),
            Instr::Getstringtbl(s1) => worst_len!(s1),
            Instr::Setstringtbl(l1) => worst_len!(l1),
            Instr::Numtof(l1, s1) => worst_len!(l1, s1),
            Instr::Ftonumz(l1, s1) => worst_len!(l1, s1),
            Instr::Ftonumn(l1, s1) => worst_len!(l1, s1),
            Instr::Fadd(l1, l2, s1) => worst_len!(l1, l2, s1),
            Instr::Fsub(l1, l2, s1) => worst_len!(l1, l2, s1),
            Instr::Fmul(l1, l2, s1) => worst_len!(l1, l2, s1),
            Instr::Fdiv(l1, l2, s1) => worst_len!(l1, l2, s1),
            Instr::Fmod(l1, l2, s1, s2) => worst_len!(l1, l2, s1, s2),
            Instr::Ceil(l1, s1) => worst_len!(l1, s1),
            Instr::Floor(l1, s1) => worst_len!(l1, s1),
            Instr::Sqrt(l1, s1) => worst_len!(l1, s1),
            Instr::Exp(l1, s1) => worst_len!(l1, s1),
            Instr::Log(l1, s1) => worst_len!(l1, s1),
            Instr::Pow(l1, l2, s1) => worst_len!(l1, l2, s1),
            Instr::Sin(l1, s1) => worst_len!(l1, s1),
            Instr::Cos(l1, s1) => worst_len!(l1, s1),
            Instr::Tan(l1, s1) => worst_len!(l1, s1),
            Instr::Asin(l1, s1) => worst_len!(l1, s1),
            Instr::Acos(l1, s1) => worst_len!(l1, s1),
            Instr::Atan(l1, s1) => worst_len!(l1, s1),
            Instr::Atan2(l1, s1) => worst_len!(l1, s1),
            Instr::Numtod(l1, s1, s2) => worst_len!(l1, s1, s2),
            Instr::Dtonumz(l1, l2, s1) => worst_len!(l1, l2, s1),
            Instr::Dtonumn(l1, l2, s1) => worst_len!(l1, l2, s1),
            Instr::Ftod(l1, s1, s2) => worst_len!(l1, s1, s2),
            Instr::Dtof(l1, l2, s1) => worst_len!(l1, l2, s1),
            Instr::Dadd(l1, l2, l3, l4, s1, s2) => worst_len!(l1, l2, l3, l4, s1, s2),
            Instr::Dsub(l1, l2, l3, l4, s1, s2) => worst_len!(l1, l2, l3, l4, s1, s2),
            Instr::Dmul(l1, l2, l3, l4, s1, s2) => worst_len!(l1, l2, l3, l4, s1, s2),
            Instr::Ddiv(l1, l2, l3, l4, s1, s2) => worst_len!(l1, l2, l3, l4, s1, s2),
            Instr::Dmodr(l1, l2, l3, l4, s1, s2) => worst_len!(l1, l2, l3, l4, s1, s2),
            Instr::Dmodq(l1, l2, l3, l4, s1, s2) => worst_len!(l1, l2, l3, l4, s1, s2),
            Instr::Dceil(l1, l2, s1, s2) => worst_len!(l1, l2, s1, s2),
            Instr::Dfloor(l1, l2, s1, s2) => worst_len!(l1, l2, s1, s2),
            Instr::Dsqrt(l1, l2, s1, s2) => worst_len!(l1, l2, s1, s2),
            Instr::Dexp(l1, l2, s1, s2) => worst_len!(l1, l2, s1, s2),
            Instr::Dlog(l1, l2, s1, s2) => worst_len!(l1, l2, s1, s2),
            Instr::Dpow(l1, l2, l3, l4, s1, s2) => worst_len!(l1, l2, l3, l4, s1, s2),
            Instr::Dsin(l1, l2, s1, s2) => worst_len!(l1, l2, s1, s2),
            Instr::Dcos(l1, l2, s1, s2) => worst_len!(l1, l2, s1, s2),
            Instr::Dtan(l1, l2, s1, s2) => worst_len!(l1, l2, s1, s2),
            Instr::Dasin(l1, l2, s1, s2) => worst_len!(l1, l2, s1, s2),
            Instr::Dacos(l1, l2, s1, s2) => worst_len!(l1, l2, s1, s2),
            Instr::Datan(l1, l2, s1, s2) => worst_len!(l1, l2, s1, s2),
            Instr::Datan2(l1, l2, l3, l4, s1, s2) => worst_len!(l1, l2, l3, l4, s1, s2),
            Instr::Jisnan(l1, l2) => worst_len!(l1, l2),
            Instr::Jisinf(l1, l2) => worst_len!(l1, l2),
            Instr::Jfeq(l1, l2, l3, l4) => worst_len!(l1, l2, l3, l4),
            Instr::Jfne(l1, l2, l3, l4) => worst_len!(l1, l2, l3, l4),
            Instr::Jflt(l1, l2, l3) => worst_len!(l1, l2, l3),
            Instr::Jfle(l1, l2, l3) => worst_len!(l1, l2, l3),
            Instr::Jfgt(l1, l2, l3) => worst_len!(l1, l2, l3),
            Instr::Jfge(l1, l2, l3) => worst_len!(l1, l2, l3),
            Instr::Jdisnan(l1, l2, l3) => worst_len!(l1, l2, l3),
            Instr::Jdisinf(l1, l2, l3) => worst_len!(l1, l2, l3),
            Instr::Jdeq(l1, l2, l3, l4, l5, l6, l7) => worst_len!(l1, l2, l3, l4, l5, l6, l7),
            Instr::Jdne(l1, l2, l3, l4, l5, l6, l7) => worst_len!(l1, l2, l3, l4, l5, l6, l7),
            Instr::Jdlt(l1, l2, l3, l4, l5) => worst_len!(l1, l2, l3, l4, l5),
            Instr::Jdle(l1, l2, l3, l4, l5) => worst_len!(l1, l2, l3, l4, l5),
            Instr::Jdgt(l1, l2, l3, l4, l5) => worst_len!(l1, l2, l3, l4, l5),
            Instr::Jdge(l1, l2, l3, l4, l5) => worst_len!(l1, l2, l3, l4, l5),
            Instr::Random(l1, s1) => worst_len!(l1, s1),
            Instr::Setrandom(l1) => worst_len!(l1),
            Instr::Mzero(l1, l2) => worst_len!(l1, l2),
            Instr::Mcopy(l1, l2, l3) => worst_len!(l1, l2, l3),
            Instr::Linearsearch(l1, l2, l3, l4, l5, l6, l7, s1) => {
                worst_len!(l1, l2, l3, l4, l5, l6, l7, s1)
            }
            Instr::Binarysearch(l1, l2, l3, l4, l5, l6, l7, s1) => {
                worst_len!(l1, l2, l3, l4, l5, l6, l7, s1)
            }
            Instr::Linkedsearch(l1, l2, l3, l4, l5, l6, s1) => {
                worst_len!(l1, l2, l3, l4, l5, l6, s1)
            }
            Instr::Accelfunc(l1, l2) => worst_len!(l1, l2),
            Instr::Accelparam(l1, l2) => worst_len!(l1, l2),
            Instr::Gestalt(l1, l2, s1) => worst_len!(l1, l2, s1),
            Instr::Debugtrap(l1) => worst_len!(l1),
            Instr::Glk(l1, l2, s1) => worst_len!(l1, l2, s1),
        };

        opcode_length + operands_length
    }

    /// Returrns the instruction's opcode.
    pub fn opcode(&self) -> u32 {
        match self {
            Instr::Nop => 0x00,
            Instr::Add(_, _, _) => 0x10,
            Instr::Sub(_, _, _) => 0x11,
            Instr::Mul(_, _, _) => 0x12,
            Instr::Div(_, _, _) => 0x13,
            Instr::Mod(_, _, _) => 0x14,
            Instr::Neg(_, _) => 0x15,
            Instr::Bitand(_, _, _) => 0x18,
            Instr::Bitor(_, _, _) => 0x19,
            Instr::Bitxor(_, _, _) => 0x1A,
            Instr::Bitnot(_, _) => 0x1B,
            Instr::Shiftl(_, _, _) => 0x1C,
            Instr::Sshiftr(_, _, _) => 0x1D,
            Instr::Ushiftr(_, _, _) => 0x1E,
            Instr::Jump(_) => 0x20,
            Instr::Jz(_, _) => 0x22,
            Instr::Jnz(_, _) => 0x23,
            Instr::Jeq(_, _, _) => 0x24,
            Instr::Jne(_, _, _) => 0x25,
            Instr::Jlt(_, _, _) => 0x26,
            Instr::Jge(_, _, _) => 0x27,
            Instr::Jgt(_, _, _) => 0x28,
            Instr::Jle(_, _, _) => 0x29,
            Instr::Jltu(_, _, _) => 0x2A,
            Instr::Jgeu(_, _, _) => 0x2B,
            Instr::Jgtu(_, _, _) => 0x2C,
            Instr::Jleu(_, _, _) => 0x2D,
            Instr::Call(_, _, _) => 0x30,
            Instr::Return(_) => 0x31,
            Instr::Catch(_, _) => 0x32,
            Instr::Throw(_, _) => 0x33,
            Instr::Tailcall(_, _) => 0x34,
            Instr::Copy(_, _) => 0x40,
            Instr::Copys(_, _) => 0x41,
            Instr::Copyb(_, _) => 0x42,
            Instr::Sexs(_, _) => 0x44,
            Instr::Sexb(_, _) => 0x45,
            Instr::Aload(_, _, _) => 0x48,
            Instr::Aloads(_, _, _) => 0x49,
            Instr::Aloadb(_, _, _) => 0x4A,
            Instr::Aloadbit(_, _, _) => 0x4B,
            Instr::Astore(_, _, _) => 0x4C,
            Instr::Astores(_, _, _) => 0x4D,
            Instr::Astoreb(_, _, _) => 0x4E,
            Instr::Astorebit(_, _, _) => 0x4F,
            Instr::Stkcount(_) => 0x50,
            Instr::Stkpeek(_, _) => 0x51,
            Instr::Stkswap => 0x52,
            Instr::Stkroll(_, _) => 0x53,
            Instr::Stkcopy(_) => 0x54,
            Instr::Streamchar(_) => 0x70,
            Instr::Streamnum(_) => 0x71,
            Instr::Streamstr(_) => 0x72,
            Instr::Streamunichar(_) => 0x73,
            Instr::Gestalt(_, _, _) => 0x100,
            Instr::Debugtrap(_) => 0x101,
            Instr::Getmemsize(_) => 0x102,
            Instr::Setmemsize(_, _) => 0x103,
            Instr::Jumpabs(_) => 0x104,
            Instr::Random(_, _) => 0x110,
            Instr::Setrandom(_) => 0x111,
            Instr::Quit => 0x120,
            Instr::Verify(_) => 0x121,
            Instr::Restart => 0x122,
            Instr::Save(_, _) => 0x123,
            Instr::Restore(_, _) => 0x124,
            Instr::Saveundo(_) => 0x125,
            Instr::Restoreundo(_) => 0x126,
            Instr::Protect(_, _) => 0x127,
            Instr::Hasundo(_) => 0x128,
            Instr::Discardundo => 0x129,
            Instr::Glk(_, _, _) => 0x130,
            Instr::Getstringtbl(_) => 0x140,
            Instr::Setstringtbl(_) => 0x141,
            Instr::Getiosys(_, _) => 0x148,
            Instr::Setiosys(_, _) => 0x149,
            Instr::Linearsearch(_, _, _, _, _, _, _, _) => 0x150,
            Instr::Binarysearch(_, _, _, _, _, _, _, _) => 0x151,
            Instr::Linkedsearch(_, _, _, _, _, _, _) => 0x152,
            Instr::Callf(_, _) => 0x160,
            Instr::Callfi(_, _, _) => 0x161,
            Instr::Callfii(_, _, _, _) => 0x162,
            Instr::Callfiii(_, _, _, _, _) => 0x163,
            Instr::Mzero(_, _) => 0x170,
            Instr::Mcopy(_, _, _) => 0x171,
            Instr::Malloc(_, _) => 0x178,
            Instr::Mfree(_) => 0x179,
            Instr::Accelfunc(_, _) => 0x180,
            Instr::Accelparam(_, _) => 0x181,
            Instr::Numtof(_, _) => 0x190,
            Instr::Ftonumz(_, _) => 0x191,
            Instr::Ftonumn(_, _) => 0x192,
            Instr::Ceil(_, _) => 0x198,
            Instr::Floor(_, _) => 0x199,
            Instr::Fadd(_, _, _) => 0x1A0,
            Instr::Fsub(_, _, _) => 0x1A1,
            Instr::Fmul(_, _, _) => 0x1A2,
            Instr::Fdiv(_, _, _) => 0x1A3,
            Instr::Fmod(_, _, _, _) => 0x1A4,
            Instr::Sqrt(_, _) => 0x1A8,
            Instr::Exp(_, _) => 0x1A9,
            Instr::Log(_, _) => 0x1AA,
            Instr::Pow(_, _, _) => 0x1AB,
            Instr::Sin(_, _) => 0x1B0,
            Instr::Cos(_, _) => 0x1B1,
            Instr::Tan(_, _) => 0x1B2,
            Instr::Asin(_, _) => 0x1B3,
            Instr::Acos(_, _) => 0x1B4,
            Instr::Atan(_, _) => 0x1B5,
            Instr::Atan2(_, _) => 0x1B6,
            Instr::Jfeq(_, _, _, _) => 0x1C0,
            Instr::Jfne(_, _, _, _) => 0x1C1,
            Instr::Jflt(_, _, _) => 0x1C2,
            Instr::Jfle(_, _, _) => 0x1C3,
            Instr::Jfgt(_, _, _) => 0x1C4,
            Instr::Jfge(_, _, _) => 0x1C5,
            Instr::Jisnan(_, _) => 0x1C8,
            Instr::Jisinf(_, _) => 0x1C9,
            Instr::Numtod(_, _, _) => 0x200,
            Instr::Dtonumz(_, _, _) => 0x201,
            Instr::Dtonumn(_, _, _) => 0x202,
            Instr::Ftod(_, _, _) => 0x203,
            Instr::Dtof(_, _, _) => 0x204,
            Instr::Dceil(_, _, _, _) => 0x208,
            Instr::Dfloor(_, _, _, _) => 0x209,
            Instr::Dadd(_, _, _, _, _, _) => 0x210,
            Instr::Dsub(_, _, _, _, _, _) => 0x211,
            Instr::Dmul(_, _, _, _, _, _) => 0x212,
            Instr::Ddiv(_, _, _, _, _, _) => 0x213,
            Instr::Dmodr(_, _, _, _, _, _) => 0x214,
            Instr::Dmodq(_, _, _, _, _, _) => 0x215,
            Instr::Dsqrt(_, _, _, _) => 0x218,
            Instr::Dexp(_, _, _, _) => 0x219,
            Instr::Dlog(_, _, _, _) => 0x21A,
            Instr::Dpow(_, _, _, _, _, _) => 0x21B,
            Instr::Dsin(_, _, _, _) => 0x220,
            Instr::Dcos(_, _, _, _) => 0x221,
            Instr::Dtan(_, _, _, _) => 0x222,
            Instr::Dasin(_, _, _, _) => 0x223,
            Instr::Dacos(_, _, _, _) => 0x224,
            Instr::Datan(_, _, _, _) => 0x225,
            Instr::Datan2(_, _, _, _, _, _) => 0x226,
            Instr::Jdeq(_, _, _, _, _, _, _) => 0x230,
            Instr::Jdne(_, _, _, _, _, _, _) => 0x231,
            Instr::Jdlt(_, _, _, _, _) => 0x232,
            Instr::Jdle(_, _, _, _, _) => 0x233,
            Instr::Jdgt(_, _, _, _, _) => 0x234,
            Instr::Jdge(_, _, _, _, _) => 0x235,
            Instr::Jdisnan(_, _, _) => 0x238,
            Instr::Jdisinf(_, _, _) => 0x239,
        }
    }

    /// Resolves all labels in the instruction to produce a [`RawInstr`].
    pub(crate) fn resolve<R>(
        &self,
        mut position: u32,
        resolver: &R,
    ) -> Result<RawInstr, AssemblerError<L>>
    where
        R: Resolver<Label = L>,
    {
        let opcode = self.opcode();
        let opcode_length = u32::try_from(opcode_len(opcode)).unwrap();

        position = position
            .checked_add(opcode_length)
            .ok_or(AssemblerError::Overflow)?;

        let operands = match self {
            Instr::Nop => resolve!(position, resolver,),
            Instr::Add(l1, l2, s1) => resolve!(position, resolver, l1, l2, s1),
            Instr::Sub(l1, l2, s1) => resolve!(position, resolver, l1, l2, s1),
            Instr::Mul(l1, l2, s1) => resolve!(position, resolver, l1, l2, s1),
            Instr::Div(l1, l2, s1) => resolve!(position, resolver, l1, l2, s1),
            Instr::Mod(l1, l2, s1) => resolve!(position, resolver, l1, l2, s1),
            Instr::Neg(l1, s1) => resolve!(position, resolver, l1, s1),
            Instr::Bitand(l1, l2, s1) => resolve!(position, resolver, l1, l2, s1),
            Instr::Bitor(l1, l2, s1) => resolve!(position, resolver, l1, l2, s1),
            Instr::Bitxor(l1, l2, s1) => resolve!(position, resolver, l1, l2, s1),
            Instr::Bitnot(l1, s1) => resolve!(position, resolver, l1, s1),
            Instr::Shiftl(l1, l2, s1) => resolve!(position, resolver, l1, l2, s1),
            Instr::Ushiftr(l1, l2, s1) => resolve!(position, resolver, l1, l2, s1),
            Instr::Sshiftr(l1, l2, s1) => resolve!(position, resolver, l1, l2, s1),
            Instr::Jump(l1) => resolve!(position, resolver, l1),
            Instr::Jz(l1, l2) => resolve!(position, resolver, l1, l2),
            Instr::Jnz(l1, l2) => resolve!(position, resolver, l1, l2),
            Instr::Jeq(l1, l2, l3) => resolve!(position, resolver, l1, l2, l3),
            Instr::Jne(l1, l2, l3) => resolve!(position, resolver, l1, l2, l3),
            Instr::Jlt(l1, l2, l3) => resolve!(position, resolver, l1, l2, l3),
            Instr::Jle(l1, l2, l3) => resolve!(position, resolver, l1, l2, l3),
            Instr::Jgt(l1, l2, l3) => resolve!(position, resolver, l1, l2, l3),
            Instr::Jge(l1, l2, l3) => resolve!(position, resolver, l1, l2, l3),
            Instr::Jltu(l1, l2, l3) => resolve!(position, resolver, l1, l2, l3),
            Instr::Jleu(l1, l2, l3) => resolve!(position, resolver, l1, l2, l3),
            Instr::Jgtu(l1, l2, l3) => resolve!(position, resolver, l1, l2, l3),
            Instr::Jgeu(l1, l2, l3) => resolve!(position, resolver, l1, l2, l3),
            Instr::Jumpabs(l1) => resolve!(position, resolver, l1),
            Instr::Copy(l1, s1) => resolve!(position, resolver, l1, s1),
            Instr::Copys(l1, s1) => resolve!(position, resolver, l1, s1),
            Instr::Copyb(l1, s1) => resolve!(position, resolver, l1, s1),
            Instr::Sexs(l1, s1) => resolve!(position, resolver, l1, s1),
            Instr::Sexb(l1, s1) => resolve!(position, resolver, l1, s1),
            Instr::Astore(l1, l2, l3) => resolve!(position, resolver, l1, l2, l3),
            Instr::Aload(l1, l2, s1) => resolve!(position, resolver, l1, l2, s1),
            Instr::Astores(l1, l2, l3) => resolve!(position, resolver, l1, l2, l3),
            Instr::Aloads(l1, l2, s1) => resolve!(position, resolver, l1, l2, s1),
            Instr::Astoreb(l1, l2, l3) => resolve!(position, resolver, l1, l2, l3),
            Instr::Aloadb(l1, l2, s1) => resolve!(position, resolver, l1, l2, s1),
            Instr::Astorebit(l1, l2, l3) => resolve!(position, resolver, l1, l2, l3),
            Instr::Aloadbit(l1, l2, s1) => resolve!(position, resolver, l1, l2, s1),
            Instr::Stkcount(s1) => resolve!(position, resolver, s1),
            Instr::Stkpeek(l1, s1) => resolve!(position, resolver, l1, s1),
            Instr::Stkswap => resolve!(position, resolver,),
            Instr::Stkcopy(l1) => resolve!(position, resolver, l1),
            Instr::Stkroll(l1, l2) => resolve!(position, resolver, l1, l2),
            Instr::Call(l1, l2, s1) => resolve!(position, resolver, l1, l2, s1),
            Instr::Callf(l1, s1) => resolve!(position, resolver, l1, s1),
            Instr::Callfi(l1, l2, s1) => resolve!(position, resolver, l1, l2, s1),
            Instr::Callfii(l1, l2, l3, s1) => {
                resolve!(position, resolver, l1, l2, l3, s1)
            }
            Instr::Callfiii(l1, l2, l3, l4, s1) => {
                resolve!(position, resolver, l1, l2, l3, l4, s1)
            }
            Instr::Return(l1) => resolve!(position, resolver, l1),
            Instr::Tailcall(l1, l2) => resolve!(position, resolver, l1, l2),
            Instr::Catch(s1, l1) => resolve!(position, resolver, s1, l1),
            Instr::Throw(l1, l2) => resolve!(position, resolver, l1, l2),
            Instr::Getmemsize(s1) => resolve!(position, resolver, s1),
            Instr::Setmemsize(l1, s1) => resolve!(position, resolver, l1, s1),
            Instr::Malloc(l1, s1) => resolve!(position, resolver, l1, s1),
            Instr::Mfree(l1) => resolve!(position, resolver, l1),
            Instr::Quit => resolve!(position, resolver,),
            Instr::Restart => resolve!(position, resolver,),
            Instr::Save(l1, s1) => resolve!(position, resolver, l1, s1),
            Instr::Restore(l1, s1) => resolve!(position, resolver, l1, s1),
            Instr::Saveundo(s1) => resolve!(position, resolver, s1),
            Instr::Restoreundo(s1) => resolve!(position, resolver, s1),
            Instr::Hasundo(s1) => resolve!(position, resolver, s1),
            Instr::Discardundo => resolve!(position, resolver,),
            Instr::Protect(l1, l2) => resolve!(position, resolver, l1, l2),
            Instr::Verify(s1) => resolve!(position, resolver, s1),
            Instr::Getiosys(s1, s2) => resolve!(position, resolver, s1, s2),
            Instr::Setiosys(l1, l2) => resolve!(position, resolver, l1, l2),
            Instr::Streamchar(l1) => resolve!(position, resolver, l1),
            Instr::Streamunichar(l1) => resolve!(position, resolver, l1),
            Instr::Streamnum(l1) => resolve!(position, resolver, l1),
            Instr::Streamstr(l1) => resolve!(position, resolver, l1),
            Instr::Getstringtbl(s1) => resolve!(position, resolver, s1),
            Instr::Setstringtbl(l1) => resolve!(position, resolver, l1),
            Instr::Numtof(l1, s1) => resolve!(position, resolver, l1, s1),
            Instr::Ftonumz(l1, s1) => resolve!(position, resolver, l1, s1),
            Instr::Ftonumn(l1, s1) => resolve!(position, resolver, l1, s1),
            Instr::Fadd(l1, l2, s1) => resolve!(position, resolver, l1, l2, s1),
            Instr::Fsub(l1, l2, s1) => resolve!(position, resolver, l1, l2, s1),
            Instr::Fmul(l1, l2, s1) => resolve!(position, resolver, l1, l2, s1),
            Instr::Fdiv(l1, l2, s1) => resolve!(position, resolver, l1, l2, s1),
            Instr::Fmod(l1, l2, s1, s2) => resolve!(position, resolver, l1, l2, s1, s2),
            Instr::Ceil(l1, s1) => resolve!(position, resolver, l1, s1),
            Instr::Floor(l1, s1) => resolve!(position, resolver, l1, s1),
            Instr::Sqrt(l1, s1) => resolve!(position, resolver, l1, s1),
            Instr::Exp(l1, s1) => resolve!(position, resolver, l1, s1),
            Instr::Log(l1, s1) => resolve!(position, resolver, l1, s1),
            Instr::Pow(l1, l2, s1) => resolve!(position, resolver, l1, l2, s1),
            Instr::Sin(l1, s1) => resolve!(position, resolver, l1, s1),
            Instr::Cos(l1, s1) => resolve!(position, resolver, l1, s1),
            Instr::Tan(l1, s1) => resolve!(position, resolver, l1, s1),
            Instr::Asin(l1, s1) => resolve!(position, resolver, l1, s1),
            Instr::Acos(l1, s1) => resolve!(position, resolver, l1, s1),
            Instr::Atan(l1, s1) => resolve!(position, resolver, l1, s1),
            Instr::Atan2(l1, s1) => resolve!(position, resolver, l1, s1),
            Instr::Numtod(l1, s1, s2) => resolve!(position, resolver, l1, s1, s2),
            Instr::Dtonumz(l1, l2, s1) => resolve!(position, resolver, l1, l2, s1),
            Instr::Dtonumn(l1, l2, s1) => resolve!(position, resolver, l1, l2, s1),
            Instr::Ftod(l1, s1, s2) => resolve!(position, resolver, l1, s1, s2),
            Instr::Dtof(l1, l2, s1) => resolve!(position, resolver, l1, l2, s1),
            Instr::Dadd(l1, l2, l3, l4, s1, s2) => {
                resolve!(position, resolver, l1, l2, l3, l4, s1, s2)
            }
            Instr::Dsub(l1, l2, l3, l4, s1, s2) => {
                resolve!(position, resolver, l1, l2, l3, l4, s1, s2)
            }
            Instr::Dmul(l1, l2, l3, l4, s1, s2) => {
                resolve!(position, resolver, l1, l2, l3, l4, s1, s2)
            }
            Instr::Ddiv(l1, l2, l3, l4, s1, s2) => {
                resolve!(position, resolver, l1, l2, l3, l4, s1, s2)
            }
            Instr::Dmodr(l1, l2, l3, l4, s1, s2) => {
                resolve!(position, resolver, l1, l2, l3, l4, s1, s2)
            }
            Instr::Dmodq(l1, l2, l3, l4, s1, s2) => {
                resolve!(position, resolver, l1, l2, l3, l4, s1, s2)
            }
            Instr::Dceil(l1, l2, s1, s2) => resolve!(position, resolver, l1, l2, s1, s2),
            Instr::Dfloor(l1, l2, s1, s2) => resolve!(position, resolver, l1, l2, s1, s2),
            Instr::Dsqrt(l1, l2, s1, s2) => resolve!(position, resolver, l1, l2, s1, s2),
            Instr::Dexp(l1, l2, s1, s2) => resolve!(position, resolver, l1, l2, s1, s2),
            Instr::Dlog(l1, l2, s1, s2) => resolve!(position, resolver, l1, l2, s1, s2),
            Instr::Dpow(l1, l2, l3, l4, s1, s2) => {
                resolve!(position, resolver, l1, l2, l3, l4, s1, s2)
            }
            Instr::Dsin(l1, l2, s1, s2) => resolve!(position, resolver, l1, l2, s1, s2),
            Instr::Dcos(l1, l2, s1, s2) => resolve!(position, resolver, l1, l2, s1, s2),
            Instr::Dtan(l1, l2, s1, s2) => resolve!(position, resolver, l1, l2, s1, s2),
            Instr::Dasin(l1, l2, s1, s2) => resolve!(position, resolver, l1, l2, s1, s2),
            Instr::Dacos(l1, l2, s1, s2) => resolve!(position, resolver, l1, l2, s1, s2),
            Instr::Datan(l1, l2, s1, s2) => resolve!(position, resolver, l1, l2, s1, s2),
            Instr::Datan2(l1, l2, l3, l4, s1, s2) => {
                resolve!(position, resolver, l1, l2, l3, l4, s1, s2)
            }
            Instr::Jisnan(l1, l2) => resolve!(position, resolver, l1, l2),
            Instr::Jisinf(l1, l2) => resolve!(position, resolver, l1, l2),
            Instr::Jfeq(l1, l2, l3, l4) => resolve!(position, resolver, l1, l2, l3, l4),
            Instr::Jfne(l1, l2, l3, l4) => resolve!(position, resolver, l1, l2, l3, l4),
            Instr::Jflt(l1, l2, l3) => resolve!(position, resolver, l1, l2, l3),
            Instr::Jfle(l1, l2, l3) => resolve!(position, resolver, l1, l2, l3),
            Instr::Jfgt(l1, l2, l3) => resolve!(position, resolver, l1, l2, l3),
            Instr::Jfge(l1, l2, l3) => resolve!(position, resolver, l1, l2, l3),
            Instr::Jdisnan(l1, l2, l3) => resolve!(position, resolver, l1, l2, l3),
            Instr::Jdisinf(l1, l2, l3) => resolve!(position, resolver, l1, l2, l3),
            Instr::Jdeq(l1, l2, l3, l4, l5, l6, l7) => {
                resolve!(position, resolver, l1, l2, l3, l4, l5, l6, l7)
            }
            Instr::Jdne(l1, l2, l3, l4, l5, l6, l7) => {
                resolve!(position, resolver, l1, l2, l3, l4, l5, l6, l7)
            }
            Instr::Jdlt(l1, l2, l3, l4, l5) => {
                resolve!(position, resolver, l1, l2, l3, l4, l5)
            }
            Instr::Jdle(l1, l2, l3, l4, l5) => {
                resolve!(position, resolver, l1, l2, l3, l4, l5)
            }
            Instr::Jdgt(l1, l2, l3, l4, l5) => {
                resolve!(position, resolver, l1, l2, l3, l4, l5)
            }
            Instr::Jdge(l1, l2, l3, l4, l5) => {
                resolve!(position, resolver, l1, l2, l3, l4, l5)
            }
            Instr::Random(l1, s1) => resolve!(position, resolver, l1, s1),
            Instr::Setrandom(l1) => resolve!(position, resolver, l1),
            Instr::Mzero(l1, l2) => resolve!(position, resolver, l1, l2),
            Instr::Mcopy(l1, l2, l3) => resolve!(position, resolver, l1, l2, l3),
            Instr::Linearsearch(l1, l2, l3, l4, l5, l6, l7, s1) => {
                resolve!(position, resolver, l1, l2, l3, l4, l5, l6, l7, s1)
            }
            Instr::Binarysearch(l1, l2, l3, l4, l5, l6, l7, s1) => {
                resolve!(position, resolver, l1, l2, l3, l4, l5, l6, l7, s1)
            }
            Instr::Linkedsearch(l1, l2, l3, l4, l5, l6, s1) => {
                resolve!(position, resolver, l1, l2, l3, l4, l5, l6, s1)
            }
            Instr::Accelfunc(l1, l2) => resolve!(position, resolver, l1, l2),
            Instr::Accelparam(l1, l2) => resolve!(position, resolver, l1, l2),
            Instr::Gestalt(l1, l2, s1) => resolve!(position, resolver, l1, l2, s1),
            Instr::Debugtrap(l1) => resolve!(position, resolver, l1),
            Instr::Glk(l1, l2, s1) => resolve!(position, resolver, l1, l2, s1),
        };

        Ok(RawInstr { opcode, operands })
    }
}

impl<L> Display for Instr<L>
where
    L: Display,
{
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            Instr::Nop => write!(f, "nop")?,
            Instr::Add(l1, l2, s1) => write!(f, "add {l1} {l2} {s1}")?,
            Instr::Sub(l1, l2, s1) => write!(f, "sub {l1} {l2} {s1}")?,
            Instr::Mul(l1, l2, s1) => write!(f, "mul {l1} {l2} {s1}")?,
            Instr::Div(l1, l2, s1) => write!(f, "div {l1} {l2} {s1}")?,
            Instr::Mod(l1, l2, s1) => write!(f, "mod {l1} {l2} {s1}")?,
            Instr::Neg(l1, s1) => write!(f, "neg {l1} {s1}")?,
            Instr::Bitand(l1, l2, s1) => write!(f, "bitand {l1} {l2} {s1}")?,
            Instr::Bitor(l1, l2, s1) => write!(f, "bitor {l1} {l2} {s1}")?,
            Instr::Bitxor(l1, l2, s1) => write!(f, "bitxor {l1} {l2} {s1}")?,
            Instr::Bitnot(l1, s1) => write!(f, "bitnot {l1} {s1}")?,
            Instr::Shiftl(l1, l2, s1) => write!(f, "shiftl {l1} {l2} {s1}")?,
            Instr::Ushiftr(l1, l2, s1) => write!(f, "ushiftr {l1} {l2} {s1}")?,
            Instr::Sshiftr(l1, l2, s1) => write!(f, "sshiftr {l1} {l2} {s1}")?,
            Instr::Jump(bt) => write!(f, "jump {bt}")?,
            Instr::Jz(l1, bt) => write!(f, "jz {l1} {bt}")?,
            Instr::Jnz(l1, bt) => write!(f, "jnz {l1} {bt}")?,
            Instr::Jeq(l1, l2, bt) => write!(f, "jeq {l1} {l2} {bt}")?,
            Instr::Jne(l1, l2, bt) => write!(f, "jne {l1} {l2} {bt}")?,
            Instr::Jlt(l1, l2, bt) => write!(f, "jlt {l1} {l2} {bt}")?,
            Instr::Jle(l1, l2, bt) => write!(f, "jle {l1} {l2} {bt}")?,
            Instr::Jgt(l1, l2, bt) => write!(f, "jgt {l1} {l2} {bt}")?,
            Instr::Jge(l1, l2, bt) => write!(f, "jge {l1} {l2} {bt}")?,
            Instr::Jltu(l1, l2, bt) => write!(f, "jltu {l1} {l2} {bt}")?,
            Instr::Jleu(l1, l2, bt) => write!(f, "jleu {l1} {l2} {bt}")?,
            Instr::Jgtu(l1, l2, bt) => write!(f, "jgtu {l1} {l2} {bt}")?,
            Instr::Jgeu(l1, l2, bt) => write!(f, "jgeu {l1} {l2} {bt}")?,
            Instr::Jumpabs(l1) => write!(f, "jumpabs {l1}")?,
            Instr::Copy(l1, s1) => write!(f, "copy {l1} {s1}")?,
            Instr::Copys(l1, s1) => write!(f, "copys {l1} {s1}")?,
            Instr::Copyb(l1, s1) => write!(f, "copyb {l1} {s1}")?,
            Instr::Sexs(l1, s1) => write!(f, "sexs {l1} {s1}")?,
            Instr::Sexb(l1, s1) => write!(f, "sexb {l1} {s1}")?,
            Instr::Astore(l1, l2, l3) => write!(f, "astore {l1} {l2} {l3}")?,
            Instr::Aload(l1, l2, s1) => write!(f, "aload {l1} {l2} {s1}")?,
            Instr::Astores(l1, l2, l3) => write!(f, "astores {l1} {l2} {l3}")?,
            Instr::Aloads(l1, l2, s1) => write!(f, "aloads {l1} {l2} {s1}")?,
            Instr::Astoreb(l1, l2, l3) => write!(f, "astoreb {l1} {l2} {l3}")?,
            Instr::Aloadb(l1, l2, s1) => write!(f, "aloadb {l1} {l2} {s1}")?,
            Instr::Astorebit(l1, l2, l3) => write!(f, "astorebit {l1} {l2} {l3}")?,
            Instr::Aloadbit(l1, l2, s1) => write!(f, "aloadbit {l1} {l2} {s1}")?,
            Instr::Stkcount(s1) => write!(f, "stkcount {s1}")?,
            Instr::Stkpeek(l1, s1) => write!(f, "stkpeek {l1} {s1}")?,
            Instr::Stkswap => write!(f, "stkswap")?,
            Instr::Stkcopy(l1) => write!(f, "stkcopy {l1}")?,
            Instr::Stkroll(l1, l2) => write!(f, "stkroll {l1} {l2}")?,
            Instr::Call(l1, l2, s1) => write!(f, "call {l1} {l2} {s1}")?,
            Instr::Callf(l1, s1) => write!(f, "callf {l1} {s1}")?,
            Instr::Callfi(l1, l2, s1) => write!(f, "callfi {l1} {l2} {s1}")?,
            Instr::Callfii(l1, l2, l3, s1) => write!(f, "callfii {l1} {l2} {l3} {s1}")?,
            Instr::Callfiii(l1, l2, l3, l4, s1) => write!(f, "callfiii {l1} {l2} {l3} {l4} {s1}")?,
            Instr::Return(l1) => write!(f, "return {l1}")?,
            Instr::Tailcall(l1, l2) => write!(f, "tailcall {l1} {l2}")?,
            Instr::Catch(s1, bt) => write!(f, "catch {s1} {bt}")?,
            Instr::Throw(l1, l2) => write!(f, "throw {l1} {l2}")?,
            Instr::Getmemsize(s1) => write!(f, "getmemsize {s1}")?,
            Instr::Setmemsize(l1, s1) => write!(f, "setmemsize {l1} {s1}")?,
            Instr::Malloc(l1, s1) => write!(f, "malloc {l1} {s1}")?,
            Instr::Mfree(l1) => write!(f, "mfree {l1}")?,
            Instr::Quit => write!(f, "quit")?,
            Instr::Restart => write!(f, "restart")?,
            Instr::Save(l1, s1) => write!(f, "save {l1} {s1}")?,
            Instr::Restore(l1, s1) => write!(f, "restore {l1} {s1}")?,
            Instr::Saveundo(s1) => write!(f, "saveundo {s1}")?,
            Instr::Restoreundo(s1) => write!(f, "restoreundo {s1}")?,
            Instr::Hasundo(s1) => write!(f, "hasundo {s1}")?,
            Instr::Discardundo => write!(f, "discardundo")?,
            Instr::Protect(l1, l2) => write!(f, "protect {l1} {l2}")?,
            Instr::Verify(s1) => write!(f, "verify {s1}")?,
            Instr::Getiosys(s1, s2) => write!(f, "getiosys {s1} {s2}")?,
            Instr::Setiosys(l1, l2) => write!(f, "setiosys {l1} {l2}")?,
            Instr::Streamchar(l1) => write!(f, "streamchar {l1}")?,
            Instr::Streamunichar(l1) => write!(f, "streamunichar {l1}")?,
            Instr::Streamnum(l1) => write!(f, "streamnum {l1}")?,
            Instr::Streamstr(l1) => write!(f, "streamstr {l1}")?,
            Instr::Getstringtbl(s1) => write!(f, "getstringtbl {s1}")?,
            Instr::Setstringtbl(l1) => write!(f, "setstringtbl {l1}")?,
            Instr::Numtof(l1, s1) => write!(f, "numtof {l1} {s1}")?,
            Instr::Ftonumz(l1, s1) => write!(f, "ftonumz {l1} {s1}")?,
            Instr::Ftonumn(l1, s1) => write!(f, "ftonumn {l1} {s1}")?,
            Instr::Fadd(l1, l2, s1) => write!(f, "fadd {l1} {l2} {s1}")?,
            Instr::Fsub(l1, l2, s1) => write!(f, "fsub {l1} {l2} {s1}")?,
            Instr::Fmul(l1, l2, s1) => write!(f, "fmul {l1} {l2} {s1}")?,
            Instr::Fdiv(l1, l2, s1) => write!(f, "fdiv {l1} {l2} {s1}")?,
            Instr::Fmod(l1, l2, s1, s2) => write!(f, "fmod {l1} {l2} {s1} {s2}")?,
            Instr::Ceil(l1, s1) => write!(f, "ceil {l1} {s1}")?,
            Instr::Floor(l1, s1) => write!(f, "floor {l1} {s1}")?,
            Instr::Sqrt(l1, s1) => write!(f, "sqrt {l1} {s1}")?,
            Instr::Exp(l1, s1) => write!(f, "exp {l1} {s1}")?,
            Instr::Log(l1, s1) => write!(f, "log {l1} {s1}")?,
            Instr::Pow(l1, l2, s1) => write!(f, "pow {l1} {l2} {s1}")?,
            Instr::Sin(l1, s1) => write!(f, "sin {l1} {s1}")?,
            Instr::Cos(l1, s1) => write!(f, "cos {l1} {s1}")?,
            Instr::Tan(l1, s1) => write!(f, "tan {l1} {s1}")?,
            Instr::Asin(l1, s1) => write!(f, "asin {l1} {s1}")?,
            Instr::Acos(l1, s1) => write!(f, "acos {l1} {s1}")?,
            Instr::Atan(l1, s1) => write!(f, "atan {l1} {s1}")?,
            Instr::Atan2(l1, s1) => write!(f, "atan2 {l1} {s1}")?,
            Instr::Numtod(l1, s1, s2) => write!(f, "numtod {l1} {s1} {s2}")?,
            Instr::Dtonumz(l1, l2, s1) => write!(f, "dtonumz {l1} {l2} {s1}")?,
            Instr::Dtonumn(l1, l2, s1) => write!(f, "dtonumn {l1} {l2} {s1}")?,
            Instr::Ftod(l1, s1, s2) => write!(f, "ftod {l1} {s1} {s2}")?,
            Instr::Dtof(l1, l2, s1) => write!(f, "dtof {l1} {l2} {s1}")?,
            Instr::Dadd(l1, l2, l3, l4, s1, s2) => write!(f, "dadd {l1} {l2} {l3} {l4} {s1} {s2}")?,
            Instr::Dsub(l1, l2, l3, l4, s1, s2) => write!(f, "dsub {l1} {l2} {l3} {l4} {s1} {s2}")?,
            Instr::Dmul(l1, l2, l3, l4, s1, s2) => write!(f, "dmul {l1} {l2} {l3} {l4} {s1} {s2}")?,
            Instr::Ddiv(l1, l2, l3, l4, s1, s2) => write!(f, "ddiv {l1} {l2} {l3} {l4} {s1} {s2}")?,
            Instr::Dmodr(l1, l2, l3, l4, s1, s2) => {
                write!(f, "dmodr {l1} {l2} {l3} {l4} {s1} {s2}")?
            }
            Instr::Dmodq(l1, l2, l3, l4, s1, s2) => {
                write!(f, "dmodq {l1} {l2} {l3} {l4} {s1} {s2}")?
            }
            Instr::Dceil(l1, l2, s1, s2) => write!(f, "dceil {l1} {l2} {s1} {s2}")?,
            Instr::Dfloor(l1, l2, s1, s2) => write!(f, "dfloor {l1} {l2} {s1} {s2}")?,
            Instr::Dsqrt(l1, l2, s1, s2) => write!(f, "dsqrt {l1} {l2} {s1} {s2}")?,
            Instr::Dexp(l1, l2, s1, s2) => write!(f, "dexp {l1} {l2} {s1} {s2}")?,
            Instr::Dlog(l1, l2, s1, s2) => write!(f, "dlog {l1} {l2} {s1} {s2}")?,
            Instr::Dpow(l1, l2, l3, l4, s1, s2) => write!(f, "dpow {l1} {l2} {l3} {l4} {s1} {s2}")?,
            Instr::Dsin(l1, l2, s1, s2) => write!(f, "dsin {l1} {l2} {s1} {s2}")?,
            Instr::Dcos(l1, l2, s1, s2) => write!(f, "dcos {l1} {l2} {s1} {s2}")?,
            Instr::Dtan(l1, l2, s1, s2) => write!(f, "dtan {l1} {l2} {s1} {s2}")?,
            Instr::Dasin(l1, l2, s1, s2) => write!(f, "dasin {l1} {l2} {s1} {s2}")?,
            Instr::Dacos(l1, l2, s1, s2) => write!(f, "dacos {l1} {l2} {s1} {s2}")?,
            Instr::Datan(l1, l2, s1, s2) => write!(f, "datan {l1} {l2} {s1} {s2}")?,
            Instr::Datan2(l1, l2, l3, l4, s1, s2) => {
                write!(f, "datan2 {l1} {l2} {l3} {l4} {s1} {s2}")?
            }
            Instr::Jisnan(l1, bt) => write!(f, "jisnan {l1} {bt}")?,
            Instr::Jisinf(l1, bt) => write!(f, "jisinf {l1} {bt}")?,
            Instr::Jfeq(l1, l2, l3, bt) => write!(f, "jfeq {l1} {l2} {l3} {bt}")?,
            Instr::Jfne(l1, l2, l3, bt) => write!(f, "jfne {l1} {l2} {l3} {bt}")?,
            Instr::Jflt(l1, l2, bt) => write!(f, "jflt {l1} {l2} {bt}")?,
            Instr::Jfle(l1, l2, bt) => write!(f, "jfle {l1} {l2} {bt}")?,
            Instr::Jfgt(l1, l2, bt) => write!(f, "jfgt {l1} {l2} {bt}")?,
            Instr::Jfge(l1, l2, bt) => write!(f, "jfge {l1} {l2} {bt}")?,
            Instr::Jdisnan(l1, l2, bt) => write!(f, "jdisnan {l1} {l2} {bt}")?,
            Instr::Jdisinf(l1, l2, bt) => write!(f, "jdisinf {l1} {l2} {bt}")?,
            Instr::Jdeq(l1, l2, l3, l4, l5, l6, bt) => {
                write!(f, "jdeq {l1} {l2} {l3} {l4} {l5} {l6} {bt}")?
            }
            Instr::Jdne(l1, l2, l3, l4, l5, l6, bt) => {
                write!(f, "jdne {l1} {l2} {l3} {l4} {l5} {l6} {bt}")?
            }
            Instr::Jdlt(l1, l2, l3, l4, bt) => write!(f, "jdlt {l1} {l2} {l3} {l4} {bt}")?,
            Instr::Jdle(l1, l2, l3, l4, bt) => write!(f, "jdle {l1} {l2} {l3} {l4} {bt}")?,
            Instr::Jdgt(l1, l2, l3, l4, bt) => write!(f, "jdgt {l1} {l2} {l3} {l4} {bt}")?,
            Instr::Jdge(l1, l2, l3, l4, bt) => write!(f, "jdge {l1} {l2} {l3} {l4} {bt}")?,
            Instr::Random(l1, s1) => write!(f, "random {l1} {s1}")?,
            Instr::Setrandom(l1) => write!(f, "setrandom {l1}")?,
            Instr::Mzero(l1, l2) => write!(f, "mzero {l1} {l2}")?,
            Instr::Mcopy(l1, l2, l3) => write!(f, "mcopy {l1} {l2} {l3}")?,
            Instr::Linearsearch(l1, l2, l3, l4, l5, l6, l7, s1) => {
                write!(f, "linearsearch {l1} {l2} {l3} {l4} {l5} {l6} {l7} {s1}")?
            }
            Instr::Binarysearch(l1, l2, l3, l4, l5, l6, l7, s1) => {
                write!(f, "binarysearch {l1} {l2} {l3} {l4} {l5} {l6} {l7} {s1}")?
            }
            Instr::Linkedsearch(l1, l2, l3, l4, l5, l6, s1) => {
                write!(f, "linkedsearch {l1} {l2} {l3} {l4} {l5} {l6} {s1}")?
            }
            Instr::Accelfunc(l1, l2) => write!(f, "accelfunc {l1} {l2}")?,
            Instr::Accelparam(l1, l2) => write!(f, "accelparam {l1} {l2}")?,
            Instr::Gestalt(l1, l2, s1) => write!(f, "gestalt {l1} {l2} {s1}")?,
            Instr::Debugtrap(l1) => write!(f, "debugtrap {l1}")?,
            Instr::Glk(l1, l2, s1) => write!(f, "glk {l1} {l2} {s1}")?,
        }
        Ok(())
    }
}

impl RawInstr {
    /// Returns the serialized length of the instruction.
    pub(crate) fn len(&self) -> usize {
        opcode_len(self.opcode)
            + self.operands.len().div_ceil(2)
            + self.operands.iter().map(|op| op.len()).sum::<usize>()
    }

    /// Serializes the instruction.
    pub(crate) fn serialize<B: BufMut>(&self, mut buf: B) {
        if self.opcode < 0x80 {
            buf.put_u8(
                self.opcode
                    .try_into()
                    .expect("opcode range should have already been checked"),
            )
        } else if self.opcode < 0x4000 {
            buf.put_u16(
                (self.opcode + 0x8000)
                    .try_into()
                    .expect("opcode range should have already been checked"),
            )
        } else {
            buf.put_u32(
                self.opcode
                    .checked_add(0xC0000000)
                    .expect("opcode should not exceed 0x0FFFFFFF"),
            )
        }

        let mut odd = false;
        let mut modebyte: u8 = 0;
        for operand in &self.operands {
            if odd {
                modebyte += operand.mode() << 4;
                buf.put_u8(modebyte);
                odd = false;
            } else {
                modebyte = operand.mode();
                odd = true;
            }
        }

        if odd {
            buf.put_u8(modebyte);
        }

        for operand in &self.operands {
            operand.serialize(&mut buf)
        }
    }
}

/// Returns the serialized length of the opcode.
fn opcode_len(opcode: u32) -> usize {
    if opcode < 0x80 {
        1
    } else if opcode < 0x4000 {
        2
    } else {
        4
    }
}