Merge branch 'nasm-2.03.x'
[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         next if ($h eq '');     # Skip pseudo-instructions
188         print D "\nstatic const struct itemplate * const itable_${h}[] = {\n";
189         foreach $j (@{$dinstables{$h}}) {
190             print D "    instrux + $j,\n";
191         }
192         print D "};\n";
193     }
194
195     @prefix_list = ();
196     foreach $h (@disasm_prefixes, '') {
197         for ($c = 0; $c < 256; $c++) {
198             $nn = sprintf("%s%02X", $h, $c);
199             if ($is_prefix{$nn} || defined($dinstables{$nn})) {
200                 # At least one entry in this prefix table
201                 push(@prefix_list, $h);
202                 $is_prefix{$h} = 1;
203                 last;
204             }
205         }
206     }
207
208     foreach $h (@prefix_list) {
209         print D "\n";
210         print D "static " unless ($h eq '');
211         print D "const struct disasm_index ";
212         print D ($h eq '') ? 'itable' : "itable_$h";
213         print D "[256] = {\n";
214         for ($c = 0; $c < 256; $c++) {
215             $nn = sprintf("%s%02X", $h, $c);
216             if ($is_prefix{$nn}) {
217                 die "$fname: ambiguous decoding of $nn\n"
218                     if (defined($dinstables{$nn}));
219                 printf D "    { itable_%s, -1 },\n", $nn;
220             } elsif (defined($dinstables{$nn})) {
221                 printf D "    { itable_%s, %u },\n",
222                 $nn, scalar(@{$dinstables{$nn}});
223             } else {
224                 printf D "    { NULL, 0 },\n";
225             }
226         }
227         print D "};\n";
228     }
229
230     print D "\nconst struct disasm_index * const itable_VEX[32][8] = {\n   ";
231     for ($m = 0; $m < 32; $m++) {
232         print D " {\n";
233         for ($lp = 0; $lp < 8; $lp++) {
234             $vp = sprintf("VEX%02X%01X", $m, $lp);
235             if ($is_prefix{$vp}) {
236                 printf D "        itable_%s,\n", $vp;
237             } else {
238                 print  D "        NULL,\n";
239             }
240         }
241         print D "    },";
242     }
243     print D "\n};\n";
244
245     close D;
246 }
247
248 if ( !defined($output) || $output eq 'i' ) {
249     print STDERR "Writing insnsi.h...\n";
250
251     open I, ">insnsi.h";
252
253     print I "/* This file is auto-generated from insns.dat by insns.pl" .
254         " - don't edit it */\n\n";
255     print I "/* This file in included by nasm.h */\n\n";
256
257     print I "/* Instruction names */\n\n";
258     print I "#ifndef NASM_INSNSI_H\n";
259     print I "#define NASM_INSNSI_H 1\n\n";
260     print I "enum opcode {\n";
261     $maxlen = 0;
262     foreach $i (@opcodes, @opcodes_cc) {
263         print I "\tI_${i},\n";
264         $len = length($i);
265         $len++ if ( $i =~ /cc$/ );      # Condition codes can be 3 characters long
266         $maxlen = $len if ( $len > $maxlen );
267     }
268     print I "\tI_none = -1\n";
269     print I "\n};\n\n";
270     print I "#define MAX_INSLEN ", $maxlen, "\n";
271     print I "#define FIRST_COND_OPCODE I_", $opcodes_cc[0], "\n\n";
272     print I "#endif /* NASM_INSNSI_H */\n";
273
274     close I;
275 }
276
277 if ( !defined($output) || $output eq 'n' ) {
278     print STDERR "Writing insnsn.c...\n";
279
280     open N, ">insnsn.c";
281
282     print N "/* This file is auto-generated from insns.dat by insns.pl" .
283         " - don't edit it */\n\n";
284     print N "#include \"tables.h\"\n\n";
285
286     print N "const char * const nasm_insn_names[] = {";
287     $first = 1;
288     foreach $i (@opcodes, @opcodes_cc) {
289         print N "," if ( !$first );
290         $first = 0;
291         $ilower = $i;
292         $ilower =~ s/cc$//;     # Remove conditional cc suffix
293         $ilower =~ tr/A-Z/a-z/; # Change to lower case (Perl 4 compatible)
294         print N "\n\t\"${ilower}\"";
295     }
296     print N "\n};\n";
297     close N;
298 }
299
300 printf STDERR "Done: %d instructions\n", $insns;
301
302 sub format_insn(@) {
303     my ($opcode, $operands, $codes, $flags) = @_;
304     my $num, $nd = 0;
305     my @bytecode;
306
307     return (undef, undef) if $operands eq "ignore";
308
309     # format the operands
310     $operands =~ s/:/|colon,/g;
311     $operands =~ s/mem(\d+)/mem|bits$1/g;
312     $operands =~ s/mem/memory/g;
313     $operands =~ s/memory_offs/mem_offs/g;
314     $operands =~ s/imm(\d+)/imm|bits$1/g;
315     $operands =~ s/imm/immediate/g;
316     $operands =~ s/rm(\d+)/rm_gpr|bits$1/g;
317     $operands =~ s/(mmx|xmm|ymm)rm/rm_$1/g;
318     $operands =~ s/\=([0-9]+)/same_as|$1/g;
319     if ($operands eq 'void') {
320         @ops = ();
321     } else {
322         @ops = split(/\,/, $operands);
323     }
324     $num = scalar(@ops);
325     while (scalar(@ops) < $MAX_OPERANDS) {
326         push(@ops, '0');
327     }
328     $operands = join(',', @ops);
329     $operands =~ tr/a-z/A-Z/;
330
331     # format the flags
332     $flags =~ s/,/|IF_/g;
333     $flags =~ s/(\|IF_ND|IF_ND\|)//, $nd = 1 if $flags =~ /IF_ND/;
334     $flags = "IF_" . $flags;
335
336     @bytecode = (decodify($codes), 0);
337     push(@bytecode_list, [@bytecode]);
338     $codes = hexstr(@bytecode);
339
340     ("{I_$opcode, $num, {$operands}, \@\@CODES-$codes\@\@, $flags},", $nd);
341 }
342
343 #
344 # Look for @@CODES-xxx@@ sequences and replace them with the appropriate
345 # offset into nasm_bytecodes
346 #
347 sub codesubst($) {
348     my($s) = @_;
349     my $n;
350
351     while ($s =~ /\@\@CODES-([0-9A-F]+)\@\@/) {
352         my $pos = $bytecode_pos{$1};
353         if (!defined($pos)) {
354             die "$fname: no position assigned to byte code $1\n";
355         }
356         $s = $` . "nasm_bytecodes+${pos}" . "$'";
357     }
358     return $s;
359 }
360
361 sub addprefix ($@) {
362     my ($prefix, @list) = @_;
363     my $x;
364     my @l = ();
365
366     foreach $x (@list) {
367         push(@l, sprintf("%s%02X", $prefix, $x));
368     }
369
370     return @l;
371 }
372
373 #
374 # Turn a code string into a sequence of bytes
375 #
376 sub decodify($) {
377   # Although these are C-syntax strings, by convention they should have
378   # only octal escapes (for directives) and hexadecimal escapes
379   # (for verbatim bytes)
380     my($codestr) = @_;
381
382     if ($codestr =~ /^\s*\[([^\]]*)\]\s*$/) {
383         return byte_code_compile($1);
384     }
385
386     my $c = $codestr;
387     my @codes = ();
388
389     while ($c ne '') {
390         if ($c =~ /^\\x([0-9a-f]+)(.*)$/i) {
391             push(@codes, hex $1);
392             $c = $2;
393             next;
394         } elsif ($c =~ /^\\([0-7]{1,3})(.*)$/) {
395             push(@codes, oct $1);
396             $c = $2;
397             next;
398         } else {
399             die "$fname: unknown code format in \"$codestr\"\n";
400         }
401     }
402
403     return @codes;
404 }
405
406 # Turn a numeric list into a hex string
407 sub hexstr(@) {
408     my $s = '';
409     my $c;
410
411     foreach $c (@_) {
412         $s .= sprintf("%02X", $c);
413     }
414     return $s;
415 }
416
417 # Here we determine the range of possible starting bytes for a given
418 # instruction. We need only consider the codes:
419 # \1 \2 \3     mean literal bytes, of course
420 # \4 \5 \6 \7  mean PUSH/POP of segment registers: special case
421 # \1[0123]     mean byte plus register value
422 # \330         means byte plus condition code
423 # \0 or \340   mean give up and return empty set
424 # \17[234]     skip is4 control byte
425 # \26x \270    skip VEX control bytes
426 sub startseq($) {
427   my ($codestr) = @_;
428   my $word, @range;
429   my @codes = ();
430   my $c = $codestr;
431   my $c0, $c1, $i;
432   my $prefix = '';
433
434   @codes = decodify($codestr);
435
436   while ($c0 = shift(@codes)) {
437       $c1 = $codes[0];
438       if ($c0 == 01 || $c0 == 02 || $c0 == 03) {
439           # Fixed byte string
440           my $fbs = $prefix;
441           while (1) {
442               if ($c0 == 01 || $c0 == 02 || $c0 == 03) {
443                   while ($c0--) {
444                       $fbs .= sprintf("%02X", shift(@codes));
445                   }
446               } else {
447                   last;
448               }
449               $c0 = shift(@codes);
450           }
451
452           foreach $pfx (@disasm_prefixes) {
453               if (substr($fbs, 0, length($pfx)) eq $pfx) {
454                   $prefix = $pfx;
455                   $fbs = substr($fbs, length($pfx));
456                   last;
457               }
458           }
459
460           if ($fbs ne '') {
461               return ($prefix.substr($fbs,0,2));
462           }
463
464           unshift(@codes, $c0);
465       } elsif ($c0 == 04) {
466           return addprefix($prefix, 0x07, 0x17, 0x1F);
467       } elsif ($c0 == 05) {
468           return addprefix($prefix, 0xA1, 0xA9);
469       } elsif ($c0 == 06) {
470           return addprefix($prefix, 0x06, 0x0E, 0x16, 0x1E);
471       } elsif ($c0 == 07) {
472           return addprefix($prefix, 0xA0, 0xA8);
473       } elsif ($c0 >= 010 && $c0 <= 013) {
474           return addprefix($prefix, $c1..($c1+7));
475       } elsif (($c0 & ~013) == 0144) {
476           return addprefix($prefix, $c1, $c1|2);
477       } elsif ($c0 == 0330) {
478           return addprefix($prefix, $c1..($c1+15));
479       } elsif ($c0 == 0 || $c0 == 0340) {
480           return $prefix;
481       } elsif (($c0 & ~3) == 0260 || $c0 == 0270) {
482           my $m,$wlp,$vxp;
483           $m   = shift(@codes);
484           $wlp = shift(@codes);
485           $prefix .= sprintf('VEX%02X%01X', $m, $wlp & 7);
486       } elsif ($c0 >= 0172 && $c0 <= 174) {
487           shift(@codes);        # Skip is4 control byte
488       } else {
489           # We really need to be able to distinguish "forbidden"
490           # and "ignorable" codes here
491       }
492   }
493   return $prefix;
494 }
495
496 #
497 # This function takes a series of byte codes in a format which is more
498 # typical of the Intel documentation, and encode it.
499 #
500 # The format looks like:
501 #
502 # [operands: opcodes]
503 #
504 # The operands word lists the order of the operands:
505 #
506 # r = register field in the modr/m
507 # m = modr/m
508 # v = VEX "v" field
509 # d = DREX "dst" field
510 # i = immediate
511 # s = register field of is4/imz2 field
512 # - = implicit (unencoded) operand
513 #
514 # For an operand that should be filled into more than one field,
515 # enter it as e.g. "r+v".
516 #
517 sub byte_code_compile($) {
518     my($str) = @_;
519     my $opr;
520     my $opc;
521     my @codes = ();
522     my $litix = undef;
523     my %oppos = ();
524     my $i;
525     my $op, $oq;
526
527     unless ($str =~ /^(([^\s:]*)\:|)\s*(.*\S)\s*$/) {
528         die "$fname: $line: cannot parse: [$str]\n";
529     }
530     $opr = "\L$2";
531     $opc = "\L$3";
532
533     my $op = 0;
534     for ($i = 0; $i < length($opr); $i++) {
535         my $c = substr($opr,$i,1);
536         if ($c eq '+') {
537             $op--;
538         } else {
539             $oppos{$c} = $op++;
540         }
541     }
542
543     $prefix_ok = 1;
544     foreach $op (split(/\s*(?:\s|(?=[\/\\]))/, $opc)) {
545         if ($op eq 'o16') {
546             push(@codes, 0320);
547         } elsif ($op eq 'o32') {
548             push(@codes, 0321);
549         } elsif ($op eq 'o64') {  # 64-bit operand size requiring REX.W
550             push(@codes, 0324);
551         } elsif ($op eq 'o64nw') { # Implied 64-bit operand size (no REX.W)
552             push(@codes, 0323);
553         } elsif ($op eq 'a16') {
554             push(@codes, 0310);
555         } elsif ($op eq 'a32') {
556             push(@codes, 0311);
557         } elsif ($op eq 'a64') {
558             push(@codes, 0313);
559         } elsif ($op eq '!osp') {
560             push(@codes, 0364);
561         } elsif ($op eq '!asp') {
562             push(@codes, 0365);
563         } elsif ($op eq 'rex.l') {
564             push(@codes, 0334);
565         } elsif ($op eq 'repe') {
566             push(@codes, 0335);
567         } elsif ($prefix_ok && $op =~ /^(66|f2|f3|np)$/) {
568             # 66/F2/F3 prefix used as an opcode extension, or np = no prefix
569             if ($op eq '66') {
570                 push(@codes, 0361);
571             } elsif ($op eq 'f2') {
572                 push(@codes, 0362);
573             } elsif ($op eq 'f3') {
574                 push(@codes, 0363);
575             } else {
576                 push(@codes, 0360);
577             }
578         } elsif ($op =~ /^[0-9a-f]{2}$/) {
579             if (defined($litix) && $litix+$codes[$litix]+1 == scalar @codes) {
580                 $codes[$litix]++;
581                 push(@codes, hex $op);
582             } else {
583                 $litix = scalar(@codes);
584                 push(@codes, 01, hex $op);
585             }
586             $prefix_ok = 0;
587         } elsif ($op eq '/r') {
588             if (!defined($oppos{'r'}) || !defined($oppos{'m'})) {
589                 die "$fname: $line: $op requires r and m operands\n";
590             }
591             push(@codes, 0100 + ($oppos{'m'} << 3) + $oppos{'r'});
592             $prefix_ok = 0;
593         } elsif ($op =~ m:^/([0-7])$:) {
594             if (!defined($oppos{'m'})) {
595                 die "$fname: $line: $op requires m operand\n";
596             }
597             push(@codes, 0200 + ($oppos{'m'} << 3) + $1);
598             $prefix_ok = 0;
599         } elsif ($op =~ /^vex(|\..*)$/) {
600             my ($m,$w,$l,$p) = (undef,2,undef,0);
601             my $has_nds = 0;
602             foreach $oq (split(/\./, $op)) {
603                 if ($oq eq 'vex') {
604                     # prefix
605                 } elsif ($oq eq '128' || $oq eq 'l0') {
606                     $l = 0;
607                 } elsif ($oq eq '256' || $oq eq 'l1') {
608                     $l = 1;
609                 } elsif ($oq eq 'w0') {
610                     $w = 0;
611                 } elsif ($oq eq 'w1') {
612                     $w = 1;
613                 } elsif ($oq eq 'wx') {
614                     $w = 2;
615                 } elsif ($oq eq 'ww') {
616                     $w = 3;
617                 } elsif ($oq eq '66') {
618                     $p = 1;
619                 } elsif ($oq eq 'f3') {
620                     $p = 2;
621                 } elsif ($oq eq 'f2') {
622                     $p = 3;
623                 } elsif ($oq eq '0f') {
624                     $m = 1;
625                 } elsif ($oq eq '0f38') {
626                     $m = 2;
627                 } elsif ($oq eq '0f3a') {
628                     $m = 3;
629                 } elsif ($oq =~ /^m([0-9]+)$/) {
630                     $m = $1+0;
631                 } elsif ($oq eq 'nds' || $oq eq 'ndd') {
632                     if (!defined($oppos{'v'})) {
633                         die "$fname: $line: vex.$oq without 'v' operand\n";
634                     }
635                     $has_nds = 1;
636                 } else {
637                     die "$fname: $line: undefined VEX subcode: $oq\n";
638                 }
639             }
640             if (!defined($m) || !defined($w) || !defined($l) || !defined($p)) {
641                 die "$fname: $line: missing fields in VEX specification\n";
642             }
643             if (defined($oppos{'v'}) && !$has_nds) {
644                 die "$fname: $line: 'v' operand without vex.nds or vex.ndd\n";
645             }
646             push(@codes, defined($oppos{'v'}) ? 0260+$oppos{'v'} : 0270,
647                  $m, ($w << 3)+($l << 2)+$p);
648             $prefix_ok = 0;
649         } elsif ($op =~ /^\/drex([01])$/) {
650             my $oc0 = $1;
651             if (!defined($oppos{'d'})) {
652                 die "$fname: $line: DREX without a 'd' operand\n";
653             }
654             # Note the use of *unshift* here, as opposed to *push*.
655             # This is because NASM want this byte code at the start of
656             # the instruction sequence, but the AMD documentation puts
657             # this at (roughly) the position of the drex byte itself.
658             # This allows us to match the AMD documentation and still
659             # do the right thing.
660             unshift(@codes, 0160+$oppos{'d'}+($oc0 ? 4 : 0));
661         } elsif ($op =~ /^(ib\,s|ib|ib\,w|iw|iwd|id|iwdq|rel|rel8|rel16|rel32|iq|seg|ibw|ibd|ibd,s)$/) {
662             if (!defined($oppos{'i'})) {
663                 die "$fname: $line: $op without 'i' operand\n";
664             }
665             if ($op eq 'ib,s') { # Signed imm8
666                 push(@codes, 014+$oppos{'i'});
667             } elsif ($op eq 'ib') { # imm8
668                 push(@codes, 020+$oppos{'i'});
669             } elsif ($op eq 'ib,u') { # Unsigned imm8
670                 push(@codes, 024+$oppos{'i'});
671             } elsif ($op eq 'iw') { # imm16
672                 push(@codes, 030+$oppos{'i'});
673             } elsif ($op eq 'iwd') { # imm16 or imm32, depending on opsize
674                 push(@codes, 034+$oppos{'i'});
675             } elsif ($op eq 'id') { # imm32
676                 push(@codes, 040+$oppos{'i'});
677             } elsif ($op eq 'iwdq') { # imm16/32/64, depending on opsize
678                 push(@codes, 044+$oppos{'i'});
679             } elsif ($op eq 'rel8') {
680                 push(@codes, 050+$oppos{'i'});
681             } elsif ($op eq 'iq') {
682                 push(@codes, 054+$oppos{'i'});
683             } elsif ($op eq 'rel16') {
684                 push(@codes, 060+$oppos{'i'});
685             } elsif ($op eq 'rel') { # 16 or 32 bit relative operand
686                 push(@codes, 064+$oppos{'i'});
687             } elsif ($op eq 'rel32') {
688                 push(@codes, 070+$oppos{'i'});
689             } elsif ($op eq 'seg') {
690                 push(@codes, 074+$oppos{'i'});
691             } elsif ($op eq 'ibw') { # imm16 that can be bytified
692                 if (!defined($s_pos)) {
693                     die "$fname: $line: $op without a +s byte\n";
694                 }
695                 $codes[$s_pos] += 0144;
696                 push(@codes, 0140+$oppos{'i'});
697             } elsif ($op eq 'ibd') { # imm32 that can be bytified
698                 if (!defined($s_pos)) {
699                     die "$fname: $line: $op without a +s byte\n";
700                 }
701                 $codes[$s_pos] += 0154;
702                 push(@codes, 0150+$oppos{'i'});
703             } elsif ($op eq 'ibd,s') {
704                 # imm32 that can be bytified, sign extended to 64 bits
705                 if (!defined($s_pos)) {
706                     die "$fname: $line: $op without a +s byte\n";
707                 }
708                 $codes[$s_pos] += 0154;
709                 push(@codes, 0250+$oppos{'i'});
710             }
711             $prefix_ok = 0;
712         } elsif ($op eq '/is4') {
713             if (!defined($oppos{'s'})) {
714                 die "$fname: $line: $op without 's' operand\n";
715             }
716             if (defined($oppos{'i'})) {
717                 push(@codes, 0172, ($oppos{'s'} << 3)+$oppos{'i'});
718             } else {
719                 push(@codes, 0174, $oppos{'s'});
720             }
721             $prefix_ok = 0;
722         } elsif ($op =~ /^\/is4\=([0-9]+)$/) {
723             my $imm = $1;
724             if (!defined($oppos{'s'})) {
725                 die "$fname: $line: $op without 's' operand\n";
726             }
727             if ($imm < 0 || $imm > 15) {
728                 die "$fname: $line: invalid imm4 value for $op: $imm\n";
729             }
730             push(@codes, 0173, ($oppos{'s'} << 4) + $imm);
731             $prefix_ok = 0;
732         } elsif ($op =~ /^([0-9a-f]{2})\+s$/) {
733             if (!defined($oppos{'i'})) {
734                 die "$fname: $line: $op without 'i' operand\n";
735             }
736             $s_pos = scalar @codes;
737             push(@codes, $oppos{'i'}, hex $1);
738             $prefix_ok = 0;
739         } elsif ($op =~ /^([0-9a-f]{2})\+c$/) {
740             push(@codes, 0330, hex $1);
741             $prefix_ok = 0;
742         } elsif ($op =~ /^\\([0-7]+|x[0-9a-f]{2})$/) {
743             # Escape to enter literal bytecodes
744             push(@codes, oct $1);
745         } else {
746             die "$fname: $line: unknown operation: $op\n";
747         }
748     }
749
750     return @codes;
751 }