0
0
mirror of https://github.com/netwide-assembler/nasm.git synced 2025-11-08 23:27:15 -05:00

Fix matching of branch instructions with prefixes and sizes

Matching of branch instructions with prefixes and sizes is, to say the
least, tricky. Work through it, and add a new macro to help.

Fixes: https://github.com/netwide-assembler/nasm/issues/144
Signed-off-by: H. Peter Anvin (Intel) <hpa@zytor.com>
This commit is contained in:
H. Peter Anvin (Intel)
2025-10-10 13:03:33 -07:00
parent 2c71e67762
commit a7457e66cf
22 changed files with 6875 additions and 100 deletions

View File

@@ -1400,28 +1400,9 @@ static int64_t calcsize(insn *ins, const struct itemplate * const temp)
ins->itemp = temp; /* Instruction template */
eat = EA_SCALAR; /* Expect a scalar EA */
/* Default operand size */
/* Default operand size (prefixes are handled in the byte code) */
ins->op_size = bits != 16 ? 32 : 16;
if (bits == 64) {
if (ins->prefixes[PPS_ASIZE] == P_A16) {
nasm_warn(WARN_PREFIX_BADMODE_A16,
"a64 prefix invalid in 64-bit mode");
ins->prefixes[PPS_ASIZE] = 0;
}
} else {
if (ins->prefixes[PPS_OSIZE] == P_O64) {
nasm_warn(WARN_PREFIX_BADMODE_O64,
"o64 prefix invalid in %d-bit mode", bits);
ins->prefixes[PPS_OSIZE] = P_none;
}
if (ins->prefixes[PPS_ASIZE] == P_A64) {
nasm_warn(WARN_PREFIX_BADMODE_A64,
"a64 prefix invalid in %d-bit mode", bits);
ins->prefixes[PPS_ASIZE] = P_none;
}
}
nasm_zero(need_pfx);
while (*codes) {
@@ -3061,7 +3042,7 @@ static enum match_result matches(const struct itemplate * const itemp,
/* "Default" operand size (from mode and prefixes only) */
op_size = ins->op_size;
if (itemp_has(itemp, IF_NWSIZE) && op_size == 32) {
if (bits == 64 && itemp_has(itemp, IF_NWSIZE) && op_size == 32) {
/* If this is an nw instruction, default to 64 bits in 64-bit mode */
op_size = bits;
}
@@ -3101,18 +3082,23 @@ static enum match_result matches(const struct itemplate * const itemp,
/* Handle implied SHORT or NEAR */
if (unlikely(ttype & (NEAR|SHORT))) {
/* Treat BYTE as an alias for SHORT, ignoring size */
if (isize[i] == BITS8) {
itype[i] |= SHORT;
isize[i] = 0;
}
/* An explicit SHORT or BITS8 cancels NEAR; are synonyms */
if (itype[i] & SHORT) {
itype[i] &= ~NEAR;
}
/* NEAR is implicit unless otherwise specified */
if (!(itype[i] & (FAR|SHORT))) {
itype[i] |= ttype & NEAR;
}
if ((ttype & (NEAR|SHORT)) == (NEAR|SHORT)) {
/* Only a short form exists; allow both NEAR and SHORT */
/* Only a short form exists; this is specially coded */
if (!(itype[i] & (FAR|ABS)))
itype[i] |= NEAR|SHORT;
} else if ((itype[i] & SHORT) || isize[i] == BITS8) {
/* An explicit SHORT or BITS8 cancel NEAR; are synonyms */
itype[i] &= ~NEAR;
if (!isize[i])
isize[i] = BITS8;
} else if (!(itype[i] & (FAR|ABS|SHORT))) {
/* NEAR is implicit unless otherwise specified */
itype[i] |= ttype & NEAR;
}
}

View File

@@ -964,8 +964,10 @@ restart_parse:
while (i == TOKEN_SPECIAL || i == TOKEN_SIZE) {
switch (tokval.t_integer) {
case S_BYTE:
if (!setsize) /* we want to use only the first */
if (!setsize) { /* we want to use only the first */
result->opt |= OPTIM_NO_Jcc_RELAX | OPTIM_NO_JMP_RELAX;
op->type |= BITS8;
}
setsize = 1;
break;
case S_WORD:
@@ -1014,9 +1016,12 @@ restart_parse:
op->type |= FAR;
break;
case S_NEAR:
/* This is not legacy behavior, even if it perhaps should be */
/* result->opt |= OPTIM_NO_Jcc_RELAX | OPTIM_NO_JMP_RELAX; */
op->type |= NEAR;
break;
case S_SHORT:
result->opt |= OPTIM_NO_Jcc_RELAX | OPTIM_NO_JMP_RELAX;
op->type |= SHORT;
break;
case S_ABS:

View File

@@ -259,20 +259,11 @@ pragma-unknown [off] unknown \c{%pragma} facility or directive
Warns about an unknown \c{%pragma} directive.
This is not yet implemented for most cases.
prefix-badmode-a64 [err] a64 prefix invalid in 16/32-bit mode
Warns that an \c{a64} prefix was specified in 16- or 32-bit
mode. If the error is demoted to a warning or suppressed, the
prefix is ignored by the assembler.
prefix-badmode-o64 [err] o64 prefix invalid in 16/32-bit mode
Warns that an \c{a64} prefix was specified in 16- or 32-bit
mode. If the error is demoted to a warning or suppressed, the
prefix is ignored by the assembler.
prefix-badmode-a16 [err] a16 prefix invalid in 64-bit mode
Warns that an \c{a16} prefix was specified in 64-bit mode.
If the error is demoted to a warning or suppressed, the
prefix is ignored by the assembler.
prefix is ignored by the assembler, but is likely to trigger
futher errors.
prefix-bnd [on] invalid \c{BND} prefix
=bnd

View File

@@ -54,10 +54,9 @@ It is the production version of NASM since 2025.
\b Hopefully fix building with OpenWatcom.
\b Generate a warning, promoted to error by default, on the use of
\c{a64} or \c{o64} prefixes in 16- or 32-bit mode or \c{a16}
prefixes in 64-bit mode. Those prefixes are not encodable; if
demoted to a warning or suppressed the prefix is ignored, but
likely will generate additional, harder to debug, error messages.
\c{o64} prefixes in 16- or 32-bit mode. If demoted to a warning or
suppressed the prefix is ignored, but likely will trigger
subsequent, harder to debug, error messages.
\S{cl-3.00} Version 3.00

3297
test/jmpxx.asm Normal file

File diff suppressed because it is too large Load Diff

97
test/jmpxx.pl Normal file
View File

@@ -0,0 +1,97 @@
#!/usr/bin/perl
use strict;
use integer;
my $bw = 1;
my @errs = ();
foreach my $errf (@ARGV) {
if (open(my $e, '<', $errf)) {
while (defined(my $l = <$e>)) {
if ($l =~ /^.*?\:([0-9]+)\:/) {
$errs[$1] |= $bw;
}
}
close($e);
}
$bw <<= 1;
}
my $ln = 0;
$ln++; print "%pragma list options -befms\n";
$ln++; print "%ifndef ERR\n";
$ln++; print " %define ERR 0\n";
$ln++; print "%endif\n";
my @nots = ('');
for (my $cc = 1; $cc < 7; $cc++) {
my $ss = 'no';
$ss .= 'w' if ($cc & 1);
$ss .= 'd' if ($cc & 2);
$ss .= 'q' if ($cc & 4);
$ln++; print "%macro $ss 1+.nolist\n";
$ln++; printf " %%if ERR || !(__BITS__ & 0x%02x)\n", $cc << 4;
$ln++; print "\t%1\n";
$ln++; print " %endif\n";
$ln++; print "%endmacro\n";
push(@nots, "$ss");
}
$ln++; print "%macro bogus 1+.nolist\n";
$ln++; printf " %%if ERR\n";
$ln++; print "\t%1\n";
$ln++; print " %endif\n";
$ln++; print "%endmacro\n";
push(@nots, 'bogus');
$ln++; print "\n";
$ln++; print "\tsection text1\n";
$ln++; print "top:\n";
$ln++; print "\ttimes 128 nop\n";
$ln++; print "\n";
foreach my $insn ('jmp', 'call', 'jz', 'jcxz', 'jecxz', 'jrcxz',
'loop', 'loope', 'loopne') {
$ln++; print "here_$insn:\n";
foreach my $tgt ('$', 'top', 'there') {
foreach my $str ('', 'strict') {
foreach my $sz ('', 'byte', 'word', 'dword', 'qword') {
foreach my $o ('', 'o16', 'o32', 'o64') {
foreach my $sn ('', 'short', 'near') {
my $is_short =
($sn eq 'short' || $insn =~ /^(j\w?cxz|loop\w*)$/);
$ln++;
$errs[$ln] |= 3 if ($sz eq 'qword' || $o eq 'o64'
|| $insn eq 'jrcxz');
$errs[$ln] |= 4 if ($sz =~ /^d?word$'/ || $o =~ /^o(16|32)$/ ||
$insn eq 'jcxz');
if (($sz eq 'word' && $o =~ /^o(32|64)$/) ||
($sz eq 'dword' && $o =~ /^o(16|64)$/) ||
($sz eq 'qword' && $o =~ /^o(16|32)$/) ||
($sz eq 'byte') ||
($is_short &&
($sn eq 'near' || $tgt ne '$' || $insn eq 'call'))) {
$errs[$ln] |= 7;
}
my $is_short =
printf " %-5s %s\n",
$nots[$errs[$ln]],
join(' ', grep { $_ ne '' }
($o,$insn,$str,$sz,$sn,$tgt));
}
}
}
}
}
}
$ln++; print "\n";
$ln++; print "\tsection text2\n";
$ln++; print "there:\n";
$ln++; print "\tret\n";

View File

@@ -8,5 +8,5 @@
}
}
],
"error": "over"
"update": false
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

3297
travis/test/jmpxx.asm Normal file

File diff suppressed because it is too large Load Diff

76
travis/test/jmpxx.json Normal file
View File

@@ -0,0 +1,76 @@
[
{
"description": "Test combinations of jump instructions (-Ox, 16 bits)",
"id": "jmpxx",
"format": "bin",
"source": "jmpxx.asm",
"option": "--bits 16 -Ox",
"target": [
{ "output": "jmpxx-ox.bin16" }
]
},
{
"description": "Test combinations of jump instructions (-O0, 16 bits)",
"ref": "jmpxx",
"option": "--bits 16 -O0",
"target": [
{ "output": "jmpxx-o0.bin16" }
]
},
{
"description": "Test combinations of jump instructions (-O1, 16 bits)",
"ref": "jmpxx",
"option": "--bits 16 -O1",
"target": [
{ "output": "jmpxx-o1.bin16" }
]
},
{
"description": "Test combinations of jump instructions (-Ox, 32 bits)",
"ref": "jmpxx",
"option": "--bits 32 -Ox",
"target": [
{ "output": "jmpxx-ox.bin32" }
]
},
{
"description": "Test combinations of jump instructions (-O0, 32 bits)",
"ref": "jmpxx",
"option": "--bits 32 -O0",
"target": [
{ "output": "jmpxx-o0.bin32" }
]
},
{
"description": "Test combinations of jump instructions (-O1, 32 bits)",
"ref": "jmpxx",
"option": "--bits 32 -O1",
"target": [
{ "output": "jmpxx-o1.bin32" }
]
},
{
"description": "Test combinations of jump instructions (-Ox, 64 bits)",
"ref": "jmpxx",
"option": "--bits 64 -Ox",
"target": [
{ "output": "jmpxx-ox.bin64" }
]
},
{
"description": "Test combinations of jump instructions (-O0, 64 bits)",
"ref": "jmpxx",
"option": "--bits 64 -O0",
"target": [
{ "output": "jmpxx-o0.bin64" }
]
},
{
"description": "Test combinations of jump instructions (-O1, 64 bits)",
"ref": "jmpxx",
"option": "--bits 64 -O1",
"target": [
{ "output": "jmpxx-o1.bin64" }
]
}
]

View File

@@ -30,7 +30,6 @@ if_("AR2", "SB, SW, SD applies to operand 2");
if_("AR3", "SB, SW, SD applies to operand 3");
if_("AR4", "SB, SW, SD applies to operand 4");
# These must match the order of the BITSx flags in opflags.h
# Are these obsolete?
if_("SB", "Unsized operands can't be non-byte");
if_("SW", "Unsized operands can't be non-word");
if_("SD", "Unsized operands can't be non-dword");
@@ -41,7 +40,8 @@ if_("SY", "Unsized operands can't be non-yword");
if_("SZ", "Unsized operands can't be non-zword");
# End BITSx order match requirement
if_("NWSIZE", "Operand size defaults to 64 in 64-bit mode");
if_("OSIZE", "Unsized operands must match the default operand size");
# OSIZE can be modified by osp prefixes, but not by other operands
if_("OSIZE", "Unsized operands must match the operand size");
if_("ASIZE", "Unsized operands must match the address size");
if_("ANYSIZE", "Ignore operand size even if explicit");
if_("SX", "Unsized operands not allowed");

View File

@@ -262,19 +262,15 @@ $bwd CMPXCHG486 rm#,reg# [mr: 0f a6# /r] 486,SM,UNDOC,NOLONG,ND,LOCK,OBSOL
;# Jumps
; APX absolute 64-bit jmp
JMPABS imm64 [i: a64 np rex2 a1 iq ] APX
JMP imm64|abs [i: a64 np rex2 a1 iq ] APX,ND
JMPABS imm64|abs [i: a64 np rex2 a1 iq ] APX,ND
; Jump-over emulation of JMPABS on !APX
JMPABS imm64 [i: a64 ff 25 00 00 00 00 iq ] NOAPX,LONG,ND
JMP imm64|abs [i: a64 ff 25 00 00 00 00 iq ] NOAPX,LONG,ND
JMPABS imm64|abs [i: a64 ff 25 00 00 00 00 iq ] NOAPX,LONG,ND
JMPABS imm64|near [i: a64 np rex2 a1 iq ] APX
JMP imm64|abs|near [i: a64 np rex2 a1 iq ] APX,ND
JMPABS imm64|abs|near [i: a64 np rex2 a1 iq ] APX,ND
; Call/jmp near imm/reg/mem are always 64-bit in long mode.
JMP imm8|short [i: nw eb rel8] 8086,NOAPX
JMP imm [i: jmp8 nw eb rel8] 8086,JMP_RELAX,NOAPX,ND
$wdq JMP imm#|near [i: nw o# e9 rel] 8086,BND,(wd:NOLONG,OSIZE)
$wdq JMP rm#|near [m: nw o# ff /4] 8086,BND,(wd:NOLONG,OSIZE)
$br JMP short [i: os eb rel8] 8086,NOAPX,ND
$br JMP near [i: jmp8 os eb rel8] 8086,NOAPX,JMP_RELAX
$br JMP near [i: os e9 rel] 8086,BND
$wdq JMP rm#|near [m: nw o# ff /4] 8086,BND,OSIZE
$wd JMP imm#|far [i: o# ea iwd seg] 8086,OSIZE,ND,NOLONG
; These are hacks to support the legacy syntax "[d]word seg:offs" to mean "seg:[d]word offs"
@@ -287,37 +283,37 @@ $wd JMP imm16:imm#|far [ji: o# ea i# iw] 8086,OSIZE,AR1,NOLONG,ND
; This is an intentional "programmer friendliness" quirk.
$wdq JMP mem#|far [m: o# ff /5] 8086,OSIZE,NWSIZE,(w:NOLONG)
Jcc imm8|short [i: nw 70+c rel8] 8086,ND,BND,SX,JCC_HINT,NOAPX
Jcc imm [i: jcc8 nw 70+c rel8] 8086,BND,SX,JCC_HINT,NOAPX
$wdq Jcc imm#|near [i: nw o# 0f 80+c rel] 386,BND,NOAPX,JCC_HINT,(wd:NOLONG,OSIZE)
$br Jcc short [i: os 70+c rel8] 8086,ND,BND,JCC_HINT,NOAPX
$br Jcc near [i: jcc8 os 70+c rel8] 8086,BND,JCC_HINT,JMP_RELAX,NOAPX
$br Jcc near [i: os 0f 80+c rel] 386,BND,NOAPX,JCC_HINT
; Jump-over emulation of Jcc on < 386
Jcc imm16|near [i: nw 71+c jlen e9 rel16] 8086,ND,NOAPX,NOLONG
; This could/should be improved to handle osp properly
Jcc imm [i: 71+c jlen e9 rel] 8086,ND,NOAPX,OSIZE
; The following only have short forms, hence imm8|near|short
JCXZ imm8|near|short [i: a16 e3 rel8] 8086,NOLONG
JECXZ imm8|near|short [i: a32 e3 rel8] 386,NOAPX
JRCXZ imm8|near|short [i: a64 e3 rel8] X86_64,LONG,NOAPX
$wdq JCXZ imm8|near|short,cx# [i-: a# e3 rel8] 8086,ND
; The following only have short forms, but use the *address* size to encode
; the size of the counter register.
$br $wdq JCX#Z near|short [i: a# os e3 rel8] 8086,NOAPX
$br $wdq JCXZ near|short,cx# [i: a# os e3 rel8] 8086,NOAPX,ND
$zwdq LOOP% imm8|near|short [i: a# nw e2 rel8] 8086,NOAPX,(wdq:ND)
$zwdq LOOPE% imm8|near|short [i: a# nw e1 rel8] 8086,NOAPX,(wdq:ND)
$zwdq LOOPNE% imm8|near|short [i: a# nw e0 rel8] 8086,NOAPX,(wdq:ND)
$zwdq LOOPZ% imm8|near|short [i: a# nw e1 rel8] 8086,NOAPX,ND
$zwdq LOOPNZ% imm8|near|short [i: a# nw e0 rel8] 8086,NOAPX,ND
$wdq LOOP imm8|near|short,cx# [i-: a# nw e2 rel8] 8086,NOAPX
$wdq LOOPE imm8|near|short,cx# [i-: a# nw e1 rel8] 8086,NOAPX
$wdq LOOPNE imm8|near|short,cx# [i-: a# nw e0 rel8] 8086,NOAPX
$wdq LOOPZ imm8|near|short,cx# [i-: a# nw e1 rel8] 8086,NOAPX,ND
$wdq LOOPNZ imm8|near|short,cx# [i-: a# nw e0 rel8] 8086,NOAPX,ND
$br $zwdq LOOP% near|short [i: a# os e2 rel8] 8086,NOAPX,(wdq:ND)
$br $zwdq LOOPE% near|short [i: a# os e1 rel8] 8086,NOAPX,(wdq:ND)
$br $zwdq LOOPNE% near|short [i: a# os e0 rel8] 8086,NOAPX,(wdq:ND)
$br $zwdq LOOPZ% near|short [i: a# os e1 rel8] 8086,NOAPX,ND
$br $zwdq LOOPNZ% near|short [i: a# os e0 rel8] 8086,NOAPX,ND
$br $wdq LOOP near|short,cx# [i-: a# os e2 rel8] 8086,NOAPX
$br $wdq LOOPE near|short,cx# [i-: a# os e1 rel8] 8086,NOAPX
$br $wdq LOOPNE near|short,cx# [i-: a# os e0 rel8] 8086,NOAPX
$br $wdq LOOPZ near|short,cx# [i-: a# os e1 rel8] 8086,NOAPX
$br $wdq LOOPNZ near|short,cx# [i-: a# os e0 rel8] 8086,NOAPX
; JMPE is obsolete, but seems to be used by a fair number of virtual environments?
$zwdq JMPE imm##|near [i: nw o# 0f b8 rel] IA64
$br JMPE near [i: os 0f b8 rel] IA64
; 0f 00 /6 with a prefix has been repurposed in long mode
$wdq JMPE rm#|near [m: nw o# np 0f 00 /6] IA64
$wd JMPE rm#|near [m: o# 0f 00 /6] IA64,ND,NOLONG
$wdq JMPE rm#|near [m: nw o# np 0f 00 /6] IA64,OSIZE
$wd JMPE rm#|near [m: o# 0f 00 /6] IA64,OSIZE,NOLONG
;# Call and return
$wdq CALL imm##|near [i: nw o# e8 rel] 8086,BND,NOAPX,(wd:OSIZE,NOLONG)
$br CALL near [i: os e8 rel] 8086,BND,NOAPX
$wdq CALL rm#|near [m: nw o# ff /2] 8086,BND,(wd:OSIZE,NOLONG)
$wd CALL imm#|far [i: o# 9a iwd seg] 8086,ND,NOLONG,OSIZE
@@ -2913,18 +2909,10 @@ VPGATHERQQ ymmreg,ymem64,ymmreg [rmv: vm64y vex.dds.256.66.0f38.w1 91 /r] AVX2
;# Intel Transactional Synchronization Extensions (TSX)
XABORT imm8 [i: c6 f8 ib] RTM
XBEGIN imm [i: nw odf c7 f8 rel] RTM
XBEGIN imm|near [i: nw odf c7 f8 rel] RTM,SX,ND
XBEGIN imm16 [i: o16 c7 f8 rel] RTM,NOLONG,SX
XBEGIN imm16|near [i: o16 c7 f8 rel] RTM,NOLONG,SX,ND
XBEGIN imm32 [i: o32 c7 f8 rel] RTM,NOLONG,SX
XBEGIN imm32|near [i: o32 c7 f8 rel] RTM,NOLONG,SX,ND
XBEGIN imm64 [i: o64nw c7 f8 rel] RTM,LONG,SX
XBEGIN imm64|near [i: o64nw c7 f8 rel] RTM,LONG,SX,ND
$br XBEGIN near [i: os c7 f8 rel] RTM
XEND void [ 0f 01 d5] RTM
XTEST void [ 0f 01 d6] HLE,RTM
PREFETCHWT1 mem8 [m: 0f 0d /2 ] PREFETCHWT1
;# Intel Memory Protection Extensions (MPX)

View File

@@ -487,7 +487,7 @@ if ( $output eq 'b') {
print B " *";
for ($j = 0; $j < 256; $j += 32) {
print B " |" if ($j);
printf B " %3o:%4d", $i+$j, $bytecode_count[$i+$j];
printf B " %3o:%5d", $i+$j, $bytecode_count[$i+$j];
}
print B "\n";
}
@@ -691,12 +691,14 @@ sub count_bytecodes(@) {
$skip = 1;
} elsif (($bc & ~013) == 0144) {
$skip = 1;
} elsif ($bc == 0172 || $bc == 0173) {
} elsif ($bc >= 0171 && $bc <= 0173) {
$skip = 1;
} elsif (($bc & ~3) == 0260 || $bc == 0270) { # VEX
$skip = 2;
} elsif (($bc & ~3) == 0240 || $bc == 0250) { # EVEX
$skip = 4;
} elsif (($bc & ~3) == 0304) {
$skip = 2;
} elsif ($bc == 0330) {
$skip = 1;
}

View File

@@ -152,7 +152,7 @@ sub func_multisize($$$) {
$ins = $o.$ins;
$o = '';
while ($ins =~ /^(.*?)((?:\b[0-9a-f]{2}(?:\+r)?|\bsbyte|\bimm|\bsel|\bopt\w?|\b[ioa]d?|\b(?:reg_)?[abcd]x|\bk?reg|\bk?rm|\bw)?\#{1,2}|\b(?:reg|rm)64\b|\b(?:o64)?nw\b|\b(?:NO)?LONG\w+\b|\%{1,2})(.*)$/) {
while ($ins =~ /^(.*?)((?:\b[0-9a-f]{2}(?:\+r)?|\bsbyte|\bimm|\bsel|\bopt\w?|\b[ioa]d?|\b(?:reg_)?[abcd]x|\bk?reg|\bk?rm|\bw|\bS\b)?\#{1,2}|\b(?:reg|rm)64\b|\b(?:o64)?nw\b|\b(?:NO)?LONG\w+\b|\%{1,2}|[ABCD]X\#)(.*)$/) {
$o .= $1;
my $mw = $2;
$ins = $3;
@@ -178,15 +178,17 @@ sub func_multisize($$$) {
$o .= !$i ? 'iwd' : ($s >= 64) ? 'id,s' : "i$sn";
} elsif ($mw eq 'i##') {
$o .= !$i ? 'iwdq' : "i$sn";
} elsif ($mw =~ /^(?:reg_)?([abcd])x\#$/) {
} elsif ($mw =~ /^(?:reg_)?([abcd])x\#$/i) {
my $rl = $1;
my $upr = ($rl =~ /^[A-Z]/);
if ($i == 1) {
$o .= "reg_${1}l";
$o .= $upr ? "${rl}L" : "reg_${rl}l";
} elsif ($i == 2) {
$o .= "reg_${1}x";
$o .= $upr ? "${rl}X" : "reg_${rl}x";
} elsif ($i == 3) {
$o .= "reg_e${1}x";
$o .= $upr ? "E${rl}X" : "reg_e${rl}x";
} elsif ($i == 4) {
$o .= "reg_r${1}x";
$o .= $upr ? "R${rl}X" : "reg_r${rl}x";
$long |= 1;
} else {
die "$0:$infile:$line: register cannot be used with z\n";
@@ -237,6 +239,8 @@ sub func_multisize($$$) {
}
} elsif ($mw eq 'w##') {
$o .= 'w'.(($i-1) & 1);
} elsif ($mw eq 'S#') {
$o .= 'S'
} elsif ($mw eq '#') {
$o .= $s;
} else {
@@ -260,6 +264,39 @@ sub func_multisize($$$) {
return @ol;
}
# Near branch operand size patterns
# This allows the "normal" size patterns to be used for
# address size features, as used by JCXZ and LOOP.
# This also allows the syntax "jmp dword foo" in 64-bit
# mode, even though it is really bogus.
$macros{'br'} = {
'func' =>
sub {
my($mac, $args, $rawargs) = @_;
my @ol;
my $ins = join(' ', @$rawargs);
foreach my $wx ([16,16], [32,32], [64,64], [64,32]) {
my($w,$iw,$sz) = @$wx;
my $i = $ins;
my $argn;
if ($i =~ /^(.*)\b(near|short)\b/) {
my $what = $2;
next if ($what eq 'short' && $iw != $w);
(my $argn = $1) =~ s/[^,:]+//g;
$argn = 'AR'.length($argn);
}
$i =~ s/\b(near|short)\b/imm$iw|$1/;
$i =~ s/\bos\b/nw o$w/;
$i .= ",$argn";
$i .= ($iw != $w) ? ',SX,ND' : ',OSIZE';
$i .= ($w == 64) ? ',LONG' : ',NOLONG';
push(@ol, $i);
}
return(@ol);
}
};
# Common pattern for K-register instructions
$macros{'k'} = {
'func' =>