Disassembler: select table based on VEX prefixes
[platform/upstream/nasm.git] / insns.pl
1 #!/usr/bin/perl
2 #
3 # insns.pl   produce insnsa.c, insnsd.c, insnsi.h, insnsn.c from insns.dat
4 #
5 # The Netwide Assembler is copyright (C) 1996 Simon Tatham and
6 # Julian Hall. All rights reserved. The software is
7 # redistributable under the license given in the file "LICENSE"
8 # distributed in the NASM archive.
9
10 # Opcode prefixes which need their own opcode tables
11 # LONGER PREFIXES FIRST!
12 @disasm_prefixes = qw(0F24 0F25 0F38 0F3A 0F7A 0FA6 0FA7 0F);
13
14 # This should match MAX_OPERANDS from nasm.h
15 $MAX_OPERANDS = 5;
16
17 # Add VEX prefixes
18 @vexlist = ();
19 for ($m = 0; $m < 32; $m++) {
20     for ($lp = 0; $lp < 8; $lp++) {
21         push(@vexlist, sprintf("VEX%02X%01X", $m, $lp));
22     }
23 }
24 @disasm_prefixes = (@vexlist, @disasm_prefixes);
25
26 print STDERR "Reading insns.dat...\n";
27
28 @args   = ();
29 undef $output;
30 foreach $arg ( @ARGV ) {
31     if ( $arg =~ /^\-/ ) {
32         if  ( $arg =~ /^\-([abdin])$/ ) {
33             $output = $1;
34         } else {
35             die "$0: Unknown option: ${arg}\n";
36         }
37     } else {
38         push (@args, $arg);
39     }
40 }
41
42 $fname = "insns.dat" unless $fname = $args[0];
43 open (F, $fname) || die "unable to open $fname";
44
45 %dinstables = ();
46 @bytecode_list = ();
47
48 $line = 0;
49 $insns = 0;
50 while (<F>) {
51   $line++;
52   chomp;
53   next if ( /^\s*(\;.*|)$/ );   # comments or blank lines
54
55   unless (/^\s*(\S+)\s+(\S+)\s+(\S+|\[.*\])\s+(\S+)\s*$/) {
56       warn "line $line does not contain four fields\n";
57       next;
58   }
59   @fields = ($1, $2, $3, $4);
60   ($formatted, $nd) = format_insn(@fields);
61   if ($formatted) {
62     $insns++;
63     $aname = "aa_$fields[0]";
64     push @$aname, $formatted;
65   }
66   if ( $fields[0] =~ /cc$/ ) {
67       # Conditional instruction
68       $k_opcodes_cc{$fields[0]}++;
69   } else {
70       # Unconditional instruction
71       $k_opcodes{$fields[0]}++;
72   }
73   if ($formatted && !$nd) {
74     push @big, $formatted;
75     my @sseq = startseq($fields[2]);
76     foreach $i (@sseq) {
77         if (!defined($dinstables{$i})) {
78             $dinstables{$i} = [];
79         }
80         push(@{$dinstables{$i}}, $#big);
81     }
82   }
83 }
84
85 close F;
86
87 #
88 # Generate the bytecode array.  At this point, @bytecode_list contains
89 # the full set of bytecodes.
90 #
91
92 # Sort by descending length
93 @bytecode_list = sort { scalar(@$b) <=> scalar(@$a) } @bytecode_list;
94 @bytecode_array = ();
95 %bytecode_pos = ();
96 $bytecode_next = 0;
97 foreach $bl (@bytecode_list) {
98     my $h = hexstr(@$bl);
99     next if (defined($bytecode_pos{$h}));
100
101     push(@bytecode_array, $bl);
102     while ($h ne '') {
103         $bytecode_pos{$h} = $bytecode_next;
104         $h = substr($h, 2);
105         $bytecode_next++;
106     }
107 }
108 undef @bytecode_list;
109
110 @opcodes    = sort keys(%k_opcodes);
111 @opcodes_cc = sort keys(%k_opcodes_cc);
112
113 if ( !defined($output) || $output eq 'b') {
114     print STDERR "Writing insnsb.c...\n";
115
116     open B, ">insnsb.c";
117
118     print B "/* This file auto-generated from insns.dat by insns.pl" .
119         " - don't edit it */\n\n";
120
121     print B "#include \"nasm.h\"\n";
122     print B "#include \"insns.h\"\n\n";
123
124     print B "const uint8_t nasm_bytecodes[$bytecode_next] = {\n";
125
126     $p = 0;
127     foreach $bl (@bytecode_array) {
128         printf B "    /* %5d */ ", $p;
129         foreach $d (@$bl) {
130             printf B "%#o,", $d;
131             $p++;
132         }
133         printf B "\n";
134     }
135     print B "};\n";
136
137     close B;
138 }
139
140 if ( !defined($output) || $output eq 'a' ) {
141     print STDERR "Writing insnsa.c...\n";
142
143     open A, ">insnsa.c";
144
145     print A "/* This file auto-generated from insns.dat by insns.pl" .
146         " - don't edit it */\n\n";
147
148     print A "#include \"nasm.h\"\n";
149     print A "#include \"insns.h\"\n\n";
150
151     foreach $i (@opcodes, @opcodes_cc) {
152         print A "static const struct itemplate instrux_${i}[] = {\n";
153         $aname = "aa_$i";
154         foreach $j (@$aname) {
155             print A "    ", codesubst($j), "\n";
156         }
157         print A "    ITEMPLATE_END\n};\n\n";
158     }
159     print A "const struct itemplate * const nasm_instructions[] = {\n";
160     foreach $i (@opcodes, @opcodes_cc) {
161         print A "    instrux_${i},\n";
162     }
163     print A "};\n";
164
165     close A;
166 }
167
168 if ( !defined($output) || $output eq 'd' ) {
169     print STDERR "Writing insnsd.c...\n";
170
171     open D, ">insnsd.c";
172
173     print D "/* This file auto-generated from insns.dat by insns.pl" .
174         " - don't edit it */\n\n";
175
176     print D "#include \"nasm.h\"\n";
177     print D "#include \"insns.h\"\n\n";
178
179     print D "static const struct itemplate instrux[] = {\n";
180     $n = 0;
181     foreach $j (@big) {
182         printf D "    /* %4d */ %s\n", $n++, codesubst($j);
183     }
184     print D "};\n";
185
186     foreach $h (sort(keys(%dinstables))) {
187         print D "\nstatic const struct itemplate * const itable_${h}[] = {\n";
188         foreach $j (@{$dinstables{$h}}) {
189             print D "    instrux + $j,\n";
190         }
191         print D "};\n";
192     }
193
194     @prefix_list = ();
195     foreach $h (@disasm_prefixes, '') {
196         for ($c = 0; $c < 256; $c++) {
197             $nn = sprintf("%s%02X", $h, $c);
198             if ($is_prefix{$nn} || defined($dinstables{$nn})) {
199                 # At least one entry in this prefix table
200                 push(@prefix_list, $h);
201                 $is_prefix{$h} = 1;
202                 last;
203             }
204         }
205     }
206
207     foreach $h (@prefix_list) {
208         print D "\n";
209         print D "static " unless ($h eq '');
210         print D "const struct disasm_index ";
211         print D ($h eq '') ? 'itable' : "itable_$h";
212         print D "[256] = {\n";
213         for ($c = 0; $c < 256; $c++) {
214             $nn = sprintf("%s%02X", $h, $c);
215             if ($is_prefix{$nn}) {
216                 die "$0: ambiguous decoding of $nn\n"
217                     if (defined($dinstables{$nn}));
218                 printf D "    { itable_%s, -1 },\n", $nn;
219             } elsif (defined($dinstables{$nn})) {
220                 printf D "    { itable_%s, %u },\n",
221                 $nn, scalar(@{$dinstables{$nn}});
222             } else {
223                 printf D "    { NULL, 0 },\n";
224             }
225         }
226         print D "};\n";
227     }
228
229     print D "\nconst struct disasm_index * const itable_VEX[32][8] = {\n";
230     for ($m = 0; $m < 32; $m++) {
231         print D "\t{\n";
232         for ($lp = 0; $lp < 8; $lp++) {
233             $vp = sprintf("VEX%02X%01X", $m, $lp);
234             if ($is_prefix{$vp}) {
235                 printf D "\t\titable_%s,\n", $vp;
236             } else {
237                 print  D "\t\tNULL,\n";
238             }
239         }
240         print D "\t},\n";
241     }
242     print D "};\n";
243
244     close D;
245 }
246
247 if ( !defined($output) || $output eq 'i' ) {
248     print STDERR "Writing insnsi.h...\n";
249
250     open I, ">insnsi.h";
251
252     print I "/* This file is auto-generated from insns.dat by insns.pl" .
253         " - don't edit it */\n\n";
254     print I "/* This file in included by nasm.h */\n\n";
255
256     print I "/* Instruction names */\n\n";
257     print I "#ifndef NASM_INSNSI_H\n";
258     print I "#define NASM_INSNSI_H 1\n\n";
259     print I "enum opcode {\n";
260     $maxlen = 0;
261     foreach $i (@opcodes, @opcodes_cc) {
262         print I "\tI_${i},\n";
263         $len = length($i);
264         $len++ if ( $i =~ /cc$/ );      # Condition codes can be 3 characters long
265         $maxlen = $len if ( $len > $maxlen );
266     }
267     print I "\tI_none = -1\n";
268     print I "\n};\n\n";
269     print I "#define MAX_INSLEN ", $maxlen, "\n";
270     print I "#define FIRST_COND_OPCODE I_", $opcodes_cc[0], "\n\n";
271     print I "#endif /* NASM_INSNSI_H */\n";
272
273     close I;
274 }
275
276 if ( !defined($output) || $output eq 'n' ) {
277     print STDERR "Writing insnsn.c...\n";
278
279     open N, ">insnsn.c";
280
281     print N "/* This file is auto-generated from insns.dat by insns.pl" .
282         " - don't edit it */\n\n";
283     print N "#include \"tables.h\"\n\n";
284
285     print N "const char * const nasm_insn_names[] = {";
286     $first = 1;
287     foreach $i (@opcodes) {
288         print N "," if ( !$first );
289         $first = 0;
290         $ilower = $i;
291         $ilower =~ tr/A-Z/a-z/; # Change to lower case (Perl 4 compatible)
292         print N "\n\t\"${ilower}\"";
293     }
294     print N "\n};\n\n";
295     print N "/* Conditional instructions */\n";
296     print N "const char * const nasm_cond_insn_names[] = {";
297     $first = 1;
298     foreach $i (@opcodes_cc) {
299         print N "," if ( !$first );
300         $first = 0;
301         $ilower = $i;
302         $ilower =~ s/cc$//;             # Skip cc suffix
303         $ilower =~ tr/A-Z/a-z/; # Change to lower case (Perl 4 compatible)
304         print N "\n\t\"${ilower}\"";
305     }
306
307     print N "\n};\n\n";
308     print N "/* and the corresponding opcodes */\n";
309     print N "const enum opcode nasm_cond_insn_opcodes[] = {";
310     $first = 1;
311     foreach $i (@opcodes_cc) {
312         print N "," if ( !$first );
313         $first = 0;
314         print N "\n\tI_$i";
315     }
316
317     print N "\n};\n";
318     close N;
319 }
320
321 printf STDERR "Done: %d instructions\n", $insns;
322
323 sub format_insn(@) {
324     my ($opcode, $operands, $codes, $flags) = @_;
325     my $num, $nd = 0;
326     my @bytecode;
327
328     return (undef, undef) if $operands eq "ignore";
329
330     # format the operands
331     $operands =~ s/:/|colon,/g;
332     $operands =~ s/mem(\d+)/mem|bits$1/g;
333     $operands =~ s/mem/memory/g;
334     $operands =~ s/memory_offs/mem_offs/g;
335     $operands =~ s/imm(\d+)/imm|bits$1/g;
336     $operands =~ s/imm/immediate/g;
337     $operands =~ s/rm(\d+)/rm_gpr|bits$1/g;
338     $operands =~ s/(mmx|xmm|ymm)rm/rm_$1/g;
339     $operands =~ s/\=([0-9]+)/same_as|$1/g;
340     if ($operands eq 'void') {
341         @ops = ();
342     } else {
343         @ops = split(/\,/, $operands);
344     }
345     $num = scalar(@ops);
346     while (scalar(@ops) < $MAX_OPERANDS) {
347         push(@ops, '0');
348     }
349     $operands = join(',', @ops);
350     $operands =~ tr/a-z/A-Z/;
351
352     # format the flags
353     $flags =~ s/,/|IF_/g;
354     $flags =~ s/(\|IF_ND|IF_ND\|)//, $nd = 1 if $flags =~ /IF_ND/;
355     $flags = "IF_" . $flags;
356
357     @bytecode = (decodify($codes), 0);
358     push(@bytecode_list, [@bytecode]);
359     $codes = hexstr(@bytecode);
360
361     ("{I_$opcode, $num, {$operands}, \@\@CODES-$codes\@\@, $flags},", $nd);
362 }
363
364 #
365 # Look for @@CODES-xxx@@ sequences and replace them with the appropriate
366 # offset into nasm_bytecodes
367 #
368 sub codesubst($) {
369     my($s) = @_;
370     my $n;
371
372     while ($s =~ /\@\@CODES-([0-9A-F]+)\@\@/) {
373         my $pos = $bytecode_pos{$1};
374         if (!defined($pos)) {
375             die "$0: no position assigned to byte code $1\n";
376         }
377         $s = $` . "nasm_bytecodes+${pos}" . "$'";
378     }
379     return $s;
380 }
381
382 sub addprefix ($@) {
383     my ($prefix, @list) = @_;
384     my $x;
385     my @l = ();
386
387     foreach $x (@list) {
388         push(@l, sprintf("%s%02X", $prefix, $x));
389     }
390
391     return @l;
392 }
393
394 #
395 # Turn a code string into a sequence of bytes
396 #
397 sub decodify($) {
398   # Although these are C-syntax strings, by convention they should have
399   # only octal escapes (for directives) and hexadecimal escapes
400   # (for verbatim bytes)
401     my($codestr) = @_;
402
403     if ($codestr =~ /^\s*\[([^\]]*)\]\s*$/) {
404         return byte_code_compile($1);
405     }
406
407     my $c = $codestr;
408     my @codes = ();
409
410     while ($c ne '') {
411         if ($c =~ /^\\x([0-9a-f]+)(.*)$/i) {
412             push(@codes, hex $1);
413             $c = $2;
414             next;
415         } elsif ($c =~ /^\\([0-7]{1,3})(.*)$/) {
416             push(@codes, oct $1);
417             $c = $2;
418             next;
419         } else {
420             die "$0: unknown code format in \"$codestr\"\n";
421         }
422     }
423
424     return @codes;
425 }
426
427 # Turn a numeric list into a hex string
428 sub hexstr(@) {
429     my $s = '';
430     my $c;
431
432     foreach $c (@_) {
433         $s .= sprintf("%02X", $c);
434     }
435     return $s;
436 }
437
438 # Here we determine the range of possible starting bytes for a given
439 # instruction. We need only consider the codes:
440 # \1 \2 \3     mean literal bytes, of course
441 # \4 \5 \6 \7  mean PUSH/POP of segment registers: special case
442 # \1[0123]     mean byte plus register value
443 # \330         means byte plus condition code
444 # \0 or \340   mean give up and return empty set
445 # \17[234]     skip is4 control byte
446 # \26x \270    skip VEX control bytes
447 sub startseq($) {
448   my ($codestr) = @_;
449   my $word, @range;
450   my @codes = ();
451   my $c = $codestr;
452   my $c0, $c1, $i;
453   my $prefix = '';
454
455   @codes = decodify($codestr);
456
457   while ($c0 = shift(@codes)) {
458       $c1 = $codes[0];
459       if ($c0 == 01 || $c0 == 02 || $c0 == 03) {
460           # Fixed byte string
461           my $fbs = $prefix;
462           while (1) {
463               if ($c0 == 01 || $c0 == 02 || $c0 == 03) {
464                   while ($c0--) {
465                       $fbs .= sprintf("%02X", shift(@codes));
466                   }
467               } else {
468                   last;
469               }
470               $c0 = shift(@codes);
471           }
472
473           foreach $pfx (@disasm_prefixes) {
474               if (substr($fbs, 0, length($pfx)) eq $pfx) {
475                   $prefix = $pfx;
476                   $fbs = substr($fbs, length($pfx));
477                   last;
478               }
479           }
480
481           if ($fbs ne '') {
482               return ($prefix.substr($fbs,0,2));
483           }
484
485           unshift(@codes, $c0);
486       } elsif ($c0 == 04) {
487           return addprefix($prefix, 0x07, 0x17, 0x1F);
488       } elsif ($c0 == 05) {
489           return addprefix($prefix, 0xA1, 0xA9);
490       } elsif ($c0 == 06) {
491           return addprefix($prefix, 0x06, 0x0E, 0x16, 0x1E);
492       } elsif ($c0 == 07) {
493           return addprefix($prefix, 0xA0, 0xA8);
494       } elsif ($c0 >= 010 && $c0 <= 013) {
495           return addprefix($prefix, $c1..($c1+7));
496       } elsif (($c0 & ~013) == 0144) {
497           return addprefix($prefix, $c1, $c1|2);
498       } elsif ($c0 == 0330) {
499           return addprefix($prefix, $c1..($c1+15));
500       } elsif ($c0 == 0 || $c0 == 0340) {
501           return $prefix;
502       } elsif (($c0 & ~3) == 0260 || $c0 == 0270) {
503           my $m,$wlp,$vxp;
504           $m   = shift(@codes);
505           $wlp = shift(@codes);
506           $prefix .= sprintf('VEX%02X%01X', $m, $wlp & 7);
507       } elsif ($c0 >= 0172 && $c0 <= 174) {
508           shift(@codes);        # Skip is4 control byte
509       } else {
510           # We really need to be able to distinguish "forbidden"
511           # and "ignorable" codes here
512       }
513   }
514   return $prefix;
515 }
516
517 #
518 # This function takes a series of byte codes in a format which is more
519 # typical of the Intel documentation, and encode it.
520 #
521 # The format looks like:
522 #
523 # [operands: opcodes]
524 #
525 # The operands word lists the order of the operands:
526 #
527 # r = register field in the modr/m
528 # m = modr/m
529 # v = VEX "v" field
530 # d = DREX "dst" field
531 # i = immediate
532 # s = register field of is4/imz2 field
533 # - = implicit (unencoded) operand
534 #
535 # For an operand that should be filled into more than one field,
536 # enter it as e.g. "r+v".
537 #
538 sub byte_code_compile($) {
539     my($str) = @_;
540     my $opr;
541     my $opc;
542     my @codes = ();
543     my $litix = undef;
544     my %oppos = ();
545     my $i;
546     my $op, $oq;
547
548     if ($str =~ /^(\S*)\:\s*(.*\S)\s*$/) {
549         $opr = "\L$1";
550         $opc = "\L$2";
551     } else {
552         $opr = '';
553         $opc = "\L$str";
554     }
555
556     my $op = 0;
557     for ($i = 0; $i < length($opr); $i++) {
558         my $c = substr($opr,$i,1);
559         if ($c eq '+') {
560             $op--;
561         } else {
562             $oppos{$c} = $op++;
563         }
564     }
565
566     $prefix_ok = 1;
567     foreach $op (split(/\s*(?:\s|(?=[\/\\]))/, $opc)) {
568         if ($op eq 'o16') {
569             push(@codes, 0320);
570         } elsif ($op eq 'o32') {
571             push(@codes, 0321);
572         } elsif ($op eq 'o64') {  # 64-bit operand size requiring REX.W
573             push(@codes, 0324);
574         } elsif ($op eq 'o64i') { # Implied 64-bit operand size (no REX.W)
575             push(@codes, 0323);
576         } elsif ($op eq 'a16') {
577             push(@codes, 0310);
578         } elsif ($op eq 'a32') {
579             push(@codes, 0311);
580         } elsif ($op eq 'a64') {
581             push(@codes, 0313);
582         } elsif ($op eq '!osp') {
583             push(@codes, 0364);
584         } elsif ($op eq '!asp') {
585             push(@codes, 0365);
586         } elsif ($op eq 'rex.l') {
587             push(@codes, 0334);
588         } elsif ($op eq 'repe') {
589             push(@codes, 0335);
590         } elsif ($prefix_ok && $op =~ /^(66|f2|f3|np)$/) {
591             # 66/F2/F3 prefix used as an opcode extension, or np = no prefix
592             if ($op eq '66') {
593                 push(@codes, 0361);
594             } elsif ($op eq 'f2') {
595                 push(@codes, 0362);
596             } elsif ($op eq 'f3') {
597                 push(@codes, 0363);
598             } else {
599                 push(@codes, 0360);
600             }
601         } elsif ($op =~ /^[0-9a-f]{2}$/) {
602             if (defined($litix) && $litix+$codes[$litix]+1 == scalar @codes) {
603                 $codes[$litix]++;
604                 push(@codes, hex $op);
605             } else {
606                 $litix = scalar(@codes);
607                 push(@codes, 01, hex $op);
608             }
609             $prefix_ok = 0;
610         } elsif ($op eq '/r') {
611             if (!defined($oppos{'r'}) || !defined($oppos{'m'})) {
612                 die "$0: $line: $op requires r and m operands\n";
613             }
614             push(@codes, 0100 + ($oppos{'m'} << 3) + $oppos{'r'});
615             $prefix_ok = 0;
616         } elsif ($op =~ m:^/([0-7])$:) {
617             if (!defined($oppos{'m'})) {
618                 die "$0: $line: $op requires m operand\n";
619             }
620             push(@codes, 0200 + ($oppos{'m'} << 3) + $1);
621             $prefix_ok = 0;
622         } elsif ($op =~ /^vex(|\..*)$/) {
623             my ($m,$w,$l,$p) = (undef,2,undef,0);
624             foreach $oq (split(/\./, $op)) {
625                 if ($oq eq 'vex') {
626                     # prefix
627                 } elsif ($oq eq '128' || $oq eq 'l0') {
628                     $l = 0;
629                 } elsif ($oq eq '256' || $oq eq 'l1') {
630                     $l = 1;
631                 } elsif ($oq eq 'w0') {
632                     $w = 0;
633                 } elsif ($oq eq 'w1') {
634                     $w = 1;
635                 } elsif ($oq eq '66') {
636                     $p = 1;
637                 } elsif ($oq eq 'f3') {
638                     $p = 2;
639                 } elsif ($oq eq 'f2') {
640                     $p = 3;
641                 } elsif ($oq eq '0f') {
642                     $m = 1;
643                 } elsif ($oq eq '0f38') {
644                     $m = 2;
645                 } elsif ($oq eq '0f3a') {
646                     $m = 3;
647                 } elsif ($oq =~ /^m([0-9]+)$/) {
648                     $m = $1+0;
649                 } elsif ($oq eq 'nds' || $oq eq 'ndd') {
650                     if (!defined($oppos{'v'})) {
651                         die "$0: $line: vex.$oq without 'v' operand\n";
652                     }
653                 } else {
654                     die "$0: $line: undefined VEX subcode: $oq\n";
655                 }
656             }
657             if (!defined($m) || !defined($w) || !defined($l) || !defined($p)) {
658                 die "$0: $line: missing fields in VEX specification\n";
659             }
660             push(@codes, defined($oppos{'v'}) ? 0260+$oppos{'v'} : 0270,
661                  $m, ($w << 3)+($l << 2)+$p);
662             $prefix_ok = 0;
663         } elsif ($op =~ /^drex(|..*)$/) {
664             my ($oc0) = (0);
665             foreach $oq (split(/\./, $op)) {
666                 if ($oq eq 'drex') {
667                     #prefix
668                 } elsif ($oq eq 'oc0') {
669                     $oc0 = 1;
670                 } else {
671                     die "$0: $line: undefined DREX subcode: $oq\n";
672                 }
673             }
674             if (!defined($oppos{'d'})) {
675                 die "$0: $line: DREX without a 'd' operand\n";
676             }
677             push(@codes, 0160+$oppos{'d'}+($oc0 ? 4 : 0));
678         } elsif ($op =~ /^(ib\,s|ib|ib\,w|iw|iwd|id|iwdq|rel|rel8|rel16|rel32|iq|seg|ibw|ibd|ibd,s)$/) {
679             if (!defined($oppos{'i'})) {
680                 die "$0: $op without 'i' operand\n";
681             }
682             if ($op eq 'ib,s') { # Signed imm8
683                 push(@codes, 014+$oppos{'i'});
684             } elsif ($op eq 'ib') { # imm8
685                 push(@codes, 020+$oppos{'i'});
686             } elsif ($op eq 'ib,u') { # Unsigned imm8
687                 push(@codes, 024+$oppos{'i'});
688             } elsif ($op eq 'iw') { # imm16
689                 push(@codes, 030+$oppos{'i'});
690             } elsif ($op eq 'iwd') { # imm16 or imm32, depending on opsize
691                 push(@codes, 034+$oppos{'i'});
692             } elsif ($op eq 'id') { # imm32
693                 push(@codes, 040+$oppos{'i'});
694             } elsif ($op eq 'iwdq') { # imm16/32/64, depending on opsize
695                 push(@codes, 044+$oppos{'i'});
696             } elsif ($op eq 'rel8') {
697                 push(@codes, 050+$oppos{'i'});
698             } elsif ($op eq 'iq') {
699                 push(@codes, 054+$oppos{'i'});
700             } elsif ($op eq 'rel16') {
701                 push(@codes, 060+$oppos{'i'});
702             } elsif ($op eq 'rel') { # 16 or 32 bit relative operand
703                 push(@codes, 064+$oppos{'i'});
704             } elsif ($op eq 'rel32') {
705                 push(@codes, 070+$oppos{'i'});
706             } elsif ($op eq 'seg') {
707                 push(@codes, 074+$oppos{'i'});
708             } elsif ($op eq 'ibw') { # imm16 that can be bytified
709                 if (!defined($s_pos)) {
710                     die "$0: $line: $op without a +s byte\n";
711                 }
712                 $codes[$s_pos] += 0144;
713                 push(@codes, 0140+$oppos{'i'});
714             } elsif ($op eq 'ibd') { # imm32 that can be bytified
715                 if (!defined($s_pos)) {
716                     die "$0: $line: $op without a +s byte\n";
717                 }
718                 $codes[$s_pos] += 0154;
719                 push(@codes, 0150+$oppos{'i'});
720             } elsif ($op eq 'ibd,s') {
721                 # imm32 that can be bytified, sign extended to 64 bits
722                 if (!defined($s_pos)) {
723                     die "$0: $line: $op without a +s byte\n";
724                 }
725                 $codes[$s_pos] += 0154;
726                 push(@codes, 0250+$oppos{'i'});
727             }
728             $prefix_ok = 0;
729         } elsif ($op eq '/is4') {
730             if (!defined($oppos{'s'})) {
731                 die "$0: $line: $op without 's' operand\n";
732             }
733             if (defined($oppos{'i'})) {
734                 push(@codes, 0172, ($oppos{'s'} << 3)+$oppos{'i'});
735             } else {
736                 push(@codes, 0174, $oppos{'s'});
737             }
738             $prefix_ok = 0;
739         } elsif ($op =~ /^\/is4\=([0-9]+)$/) {
740             my $imm = $1;
741             if (!defined($oppos{'s'})) {
742                 die "$0: $line: $op without 's' operand\n";
743             }
744             if ($imm < 0 || $imm > 15) {
745                 die "$0: $line: invalid imm4 value for $op: $imm\n";
746             }
747             push(@codes, 0173, ($oppos{'s'} << 4) + $imm);
748             $prefix_ok = 0;
749         } elsif ($op =~ /^([0-9a-f]{2})\+s$/) {
750             if (!defined($oppos{'i'})) {
751                 die "$0: $op without 'i' operand\n";
752             }
753             $s_pos = scalar @codes;
754             push(@codes, $oppos{'i'}, hex $1);
755             $prefix_ok = 0;
756         } elsif ($op =~ /^([0-9a-f]{2})\+c$/) {
757             push(@codes, 0330, hex $1);
758             $prefix_ok = 0;
759         } elsif ($op =~ /^\\([0-7]+|x[0-9a-f]{2})$/) {
760             # Escape to enter literal bytecodes
761             push(@codes, oct $1);
762         } else {
763             die "$0: unknown operation: $op\n";
764         }
765     }
766
767     return @codes;
768 }