diff options
author | H. Peter Anvin <hpa@zytor.com> | 2008-10-10 22:10:31 -0700 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2008-10-23 23:03:59 -0700 |
commit | dcffe4b9f651bb420c62b7cffb5aa5169310763d (patch) | |
tree | 3d99a77d257fac05857a36958744c2ed03311412 | |
parent | b21141a30128d2d2d8001cd4d9babab18366b867 (diff) | |
download | nasm-dcffe4b9f651bb420c62b7cffb5aa5169310763d.tar.gz nasm-dcffe4b9f651bb420c62b7cffb5aa5169310763d.tar.bz2 nasm-dcffe4b9f651bb420c62b7cffb5aa5169310763d.zip |
Add extension bytecodes to support operands 4+
The bytecode format assumes max 4 operands pretty strictly, but we
already have one instruction with 5 operands, and it's likely to get
more. Support them via extension prefixes (similar to REX prefixes).
For bytecodes which use argument bytes we encode the number directly,
however.
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
-rw-r--r-- | assemble.c | 55 | ||||
-rw-r--r-- | disasm.c | 1 | ||||
-rwxr-xr-x | insns.pl | 84 |
3 files changed, 97 insertions, 43 deletions
@@ -7,7 +7,10 @@ * * the actual codes (C syntax, i.e. octal): * \0 - terminates the code. (Unless it's a literal of course.) - * \1, \2, \3 - that many literal bytes follow in the code stream + * \1..\4 - that many literal bytes follow in the code stream + * \5 - add 4 to the primary operand number (b, low octdigit) + * \6 - add 4 to the secondary operand number (a, middle octdigit) + * \7 - add 4 to both the primary and the secondary operand number * \10..\13 - a literal byte follows in the code stream, to be added * to the register value of operand 0..3 * \14..\17 - a signed byte immediate operand, from operand 0..3 @@ -784,7 +787,9 @@ static int64_t calcsize(int32_t segment, int64_t offset, int bits, int64_t length = 0; uint8_t c; int rex_mask = ~0; + int op1, op2; struct operand *opx; + uint8_t opex = 0; ins->rex = 0; /* Ensure REX is reset */ @@ -796,14 +801,25 @@ static int64_t calcsize(int32_t segment, int64_t offset, int bits, while (*codes) { c = *codes++; - opx = &ins->oprs[c & 3]; + op1 = (c & 3) + ((opex & 1) << 2); + op2 = ((c >> 3) & 3) + ((opex & 2) << 1); + opx = &ins->oprs[op1]; + opex = 0; /* For the next iteration */ + switch (c) { case 01: case 02: case 03: + case 04: codes += c, length += c; break; + case 05: + case 06: + case 07: + opex = c; + break; + case4(010): ins->rex |= op_rexflags(opx, REX_B|REX_H|REX_P|REX_W); @@ -1060,16 +1076,14 @@ static int64_t calcsize(int32_t segment, int64_t offset, int bits, if (c <= 0177) { /* pick rfield from operand b */ - rflags = regflag(&ins->oprs[c & 7]); - rfield = nasm_regvals[ins->oprs[c & 7].basereg]; + rflags = regflag(&ins->oprs[op1]); + rfield = nasm_regvals[ins->oprs[op1].basereg]; } else { rflags = 0; rfield = c & 7; } - - if (!process_ea - (&ins->oprs[(c >> 3) & 7], &ea_data, bits, - ins->addr_size, rfield, rflags)) { + if (!process_ea(&ins->oprs[op2], &ea_data, bits, + ins->addr_size, rfield, rflags)) { errfunc(ERR_NONFATAL, "invalid effective address"); return -1; } else { @@ -1170,25 +1184,38 @@ static void gencode(int32_t segment, int64_t offset, int bits, uint8_t bytes[4]; int64_t size; int64_t data; + int op1, op2; struct operand *opx; const uint8_t *codes = temp->code; + uint8_t opex = 0; while (*codes) { c = *codes++; - opx = &ins->oprs[c & 3]; + op1 = (c & 3) + ((opex & 1) << 2); + op2 = ((c >> 3) & 3) + ((opex & 2) << 1); + opx = &ins->oprs[op1]; + opex = 0; /* For the next iteration */ + switch (c) { case 01: case 02: case 03: + case 04: EMIT_REX(); out(offset, segment, codes, OUT_RAWDATA, c, NO_SEG, NO_SEG); codes += c; offset += c; break; + case 05: + case 06: + case 07: + opex = c; + break; + case4(010): EMIT_REX(); - bytes[0] = *codes++ + ((regval(opx)) & 7); + bytes[0] = *codes++ + (regval(opx) & 7); out(offset, segment, bytes, OUT_RAWDATA, 1, NO_SEG, NO_SEG); offset += 1; break; @@ -1199,7 +1226,7 @@ static void gencode(int32_t segment, int64_t offset, int bits, warn on explicit BYTE directives. Also warn, obviously, if the optimizer isn't enabled. */ if (((opx->type & BITS8) || - !(opx->type & temp->opd[c & 3] & BYTENESS)) && + !(opx->type & temp->opd[op1] & BYTENESS)) && (opx->offset < -128 || opx->offset > 127)) { errfunc(ERR_WARNING | ERR_PASS2 | ERR_WARN_NOV, "signed byte value exceeds bounds"); @@ -1749,8 +1776,8 @@ static void gencode(int32_t segment, int64_t offset, int bits, if (c <= 0177) { /* pick rfield from operand b */ - rflags = regflag(&ins->oprs[c & 7]); - rfield = nasm_regvals[ins->oprs[c & 7].basereg]; + rflags = regflag(&ins->oprs[op1]); + rfield = nasm_regvals[ins->oprs[op1].basereg]; } else { /* rfield is constant */ rflags = 0; @@ -1758,7 +1785,7 @@ static void gencode(int32_t segment, int64_t offset, int bits, } if (!process_ea - (&ins->oprs[(c >> 3) & 7], &ea_data, bits, + (&ins->oprs[op2], &ea_data, bits, ins->addr_size, rfield, rflags)) { errfunc(ERR_NONFATAL, "invalid effective address"); } @@ -402,6 +402,7 @@ static int matches(const struct itemplate *t, uint8_t *data, case 01: case 02: case 03: + case 04: while (c--) if (*r++ != *data++) return false; @@ -324,7 +324,7 @@ sub count_bytecodes(@) { next; } $bytecode_count[$bc]++; - if ($bc >= 01 && $bc <= 03) { + if ($bc >= 01 && $bc <= 04) { $skip = $bc; } elsif (($bc & ~03) == 010) { $skip = 1; @@ -458,11 +458,11 @@ sub hexstr(@) { # Here we determine the range of possible starting bytes for a given # instruction. We need only consider the codes: -# \1 \2 \3 mean literal bytes, of course -# \4 \5 \6 \7 mean PUSH/POP of segment registers: special case +# \[1234] mean literal bytes, of course # \1[0123] mean byte plus register value # \330 means byte plus condition code # \0 or \340 mean give up and return empty set +# \34[4567] mean PUSH/POP of segment registers: special case # \17[234] skip is4 control byte # \26x \270 skip VEX control bytes sub startseq($) { @@ -477,11 +477,11 @@ sub startseq($) { while ($c0 = shift(@codes)) { $c1 = $codes[0]; - if ($c0 == 01 || $c0 == 02 || $c0 == 03) { + if ($c0 >= 01 && $c0 <= 04) { # Fixed byte string my $fbs = $prefix; while (1) { - if ($c0 == 01 || $c0 == 02 || $c0 == 03) { + if ($c0 >= 01 && $c0 <= 04) { while ($c0--) { $fbs .= sprintf("%02X", shift(@codes)); } @@ -565,6 +565,7 @@ sub byte_code_compile($) { my %oppos = (); my $i; my $op, $oq; + my $opex; unless ($str =~ /^(([^\s:]*)\:|)\s*(.*\S)\s*$/) { die "$fname: $line: cannot parse: [$str]\n"; @@ -618,7 +619,8 @@ sub byte_code_compile($) { push(@codes, 0360); } } elsif ($op =~ /^[0-9a-f]{2}$/) { - if (defined($litix) && $litix+$codes[$litix]+1 == scalar @codes) { + if (defined($litix) && $litix+$codes[$litix]+1 == scalar @codes && + $codes[$litix] < 4) { $codes[$litix]++; push(@codes, hex $op); } else { @@ -630,13 +632,17 @@ sub byte_code_compile($) { if (!defined($oppos{'r'}) || !defined($oppos{'m'})) { die "$fname: $line: $op requires r and m operands\n"; } - push(@codes, 0100 + ($oppos{'m'} << 3) + $oppos{'r'}); + $opex = (($oppos{'m'} & 4) ? 06 : 0) | + (($oppos{'r'} & 4) ? 05 : 0); + push(@codes, $opex) if ($opex); + push(@codes, 0100 + (($oppos{'m'} & 3) << 3) + ($oppos{'r'} & 3)); $prefix_ok = 0; } elsif ($op =~ m:^/([0-7])$:) { if (!defined($oppos{'m'})) { die "$fname: $line: $op requires m operand\n"; } - push(@codes, 0200 + ($oppos{'m'} << 3) + $1); + push(@codes, 06) if ($oppos{'m'} & 4); + push(@codes, 0200 + (($oppos{'m'} & 3) << 3) + $1); $prefix_ok = 0; } elsif ($op =~ /^vex(|\..*)$/) { my ($m,$w,$l,$p) = (undef,2,undef,0); @@ -685,7 +691,7 @@ sub byte_code_compile($) { if (defined($oppos{'v'}) && !$has_nds) { die "$fname: $line: 'v' operand without vex.nds or vex.ndd\n"; } - push(@codes, defined($oppos{'v'}) ? 0260+$oppos{'v'} : 0270, + push(@codes, defined($oppos{'v'}) ? 0260+($oppos{'v'} & 3) : 0270, $m, ($w << 3)+($l << 2)+$p); $prefix_ok = 0; } elsif ($op =~ /^\/drex([01])$/) { @@ -699,60 +705,79 @@ sub byte_code_compile($) { # this at (roughly) the position of the drex byte itself. # This allows us to match the AMD documentation and still # do the right thing. - unshift(@codes, 0160+$oppos{'d'}+($oc0 ? 4 : 0)); + unshift(@codes, 0160+($oppos{'d'} & 3)+($oc0 ? 4 : 0)); + unshift(@codes, 05) if ($oppos{'d'} & 4); } elsif ($op =~ /^(ib\,s|ib|ibx|ib\,w|iw|iwd|id|idx|iwdq|rel|rel8|rel16|rel32|iq|seg|ibw|ibd|ibd,s)$/) { if (!defined($oppos{'i'})) { die "$fname: $line: $op without 'i' operand\n"; } if ($op eq 'ib,s') { # Signed imm8 - push(@codes, 014+$oppos{'i'}); + push(@codes, 05) if ($oppos{'i'} & 4); + push(@codes, 014+($oppos{'i'} & 3)); } elsif ($op eq 'ib') { # imm8 - push(@codes, 020+$oppos{'i'}); + push(@codes, 05) if ($oppos{'i'} & 4); + push(@codes, 020+($oppos{'i'} & 3)); } elsif ($op eq 'ib,u') { # Unsigned imm8 - push(@codes, 024+$oppos{'i'}); + push(@codes, 05) if ($oppos{'i'} & 4); + push(@codes, 024+($oppos{'i'} & 3)); } elsif ($op eq 'iw') { # imm16 - push(@codes, 030+$oppos{'i'}); + push(@codes, 05) if ($oppos{'i'} & 4); + push(@codes, 030+($oppos{'i'} & 3)); } elsif ($op eq 'ibx') { # imm8 sign-extended to opsize - push(@codes, 0274+$oppos{'i'}); + push(@codes, 05) if ($oppos{'i'} & 4); + push(@codes, 0274+($oppos{'i'} & 3)); } elsif ($op eq 'iwd') { # imm16 or imm32, depending on opsize - push(@codes, 034+$oppos{'i'}); + push(@codes, 05) if ($oppos{'i'} & 4); + push(@codes, 034+($oppos{'i'} & 3)); } elsif ($op eq 'id') { # imm32 - push(@codes, 040+$oppos{'i'}); + push(@codes, 05) if ($oppos{'i'} & 4); + push(@codes, 040+($oppos{'i'} & 3)); } elsif ($op eq 'idx') { # imm32 extended to 64 bits - push(@codes, 0254+$oppos{'i'}); + push(@codes, 05) if ($oppos{'i'} & 4); + push(@codes, 0254+($oppos{'i'} & 3)); } elsif ($op eq 'iwdq') { # imm16/32/64, depending on opsize - push(@codes, 044+$oppos{'i'}); + push(@codes, 05) if ($oppos{'i'} & 4); + push(@codes, 044+($oppos{'i'} & 3)); } elsif ($op eq 'rel8') { - push(@codes, 050+$oppos{'i'}); + push(@codes, 05) if ($oppos{'i'} & 4); + push(@codes, 050+($oppos{'i'} & 3)); } elsif ($op eq 'iq') { - push(@codes, 054+$oppos{'i'}); + push(@codes, 05) if ($oppos{'i'} & 4); + push(@codes, 054+($oppos{'i'} & 3)); } elsif ($op eq 'rel16') { - push(@codes, 060+$oppos{'i'}); + push(@codes, 05) if ($oppos{'i'} & 4); + push(@codes, 060+($oppos{'i'} & 3)); } elsif ($op eq 'rel') { # 16 or 32 bit relative operand - push(@codes, 064+$oppos{'i'}); + push(@codes, 05) if ($oppos{'i'} & 4); + push(@codes, 064+($oppos{'i'} & 3)); } elsif ($op eq 'rel32') { - push(@codes, 070+$oppos{'i'}); + push(@codes, 05) if ($oppos{'i'} & 4); + push(@codes, 070+($oppos{'i'} & 3)); } elsif ($op eq 'seg') { - push(@codes, 074+$oppos{'i'}); + push(@codes, 05) if ($oppos{'i'} & 4); + push(@codes, 074+($oppos{'i'} & 3)); } elsif ($op eq 'ibw') { # imm16 that can be bytified if (!defined($s_pos)) { die "$fname: $line: $op without a +s byte\n"; } $codes[$s_pos] += 0144; - push(@codes, 0140+$oppos{'i'}); + push(@codes, 05) if ($oppos{'i'} & 4); + push(@codes, 0140+($oppos{'i'} & 3)); } elsif ($op eq 'ibd') { # imm32 that can be bytified if (!defined($s_pos)) { die "$fname: $line: $op without a +s byte\n"; } $codes[$s_pos] += 0154; - push(@codes, 0150+$oppos{'i'}); + push(@codes, 05) if ($oppos{'i'} & 4); + push(@codes, 0150+($oppos{'i'} & 3)); } elsif ($op eq 'ibd,s') { # imm32 that can be bytified, sign extended to 64 bits if (!defined($s_pos)) { die "$fname: $line: $op without a +s byte\n"; } $codes[$s_pos] += 0154; - push(@codes, 0250+$oppos{'i'}); + push(@codes, 05) if ($oppos{'i'} & 4); + push(@codes, 0250+($oppos{'i'} & 3)); } $prefix_ok = 0; } elsif ($op eq '/is4') { @@ -780,7 +805,8 @@ sub byte_code_compile($) { die "$fname: $line: $op without 'i' operand\n"; } $s_pos = scalar @codes; - push(@codes, $oppos{'i'}, hex $1); + push(@codes, 05) if ($oppos{'i'} & 4); + push(@codes, $oppos{'i'} & 3, hex $1); $prefix_ok = 0; } elsif ($op =~ /^([0-9a-f]{2})\+c$/) { push(@codes, 0330, hex $1); |