whatcanGOwrong
This commit is contained in:
@@ -0,0 +1,458 @@
|
||||
# arm instruction description version 0.01.
|
||||
# https://golang.org/x/arch/arm
|
||||
#
|
||||
# This file contains a block of comment lines, each beginning with #,
|
||||
# followed by entries in CSV format. All the # comments are at the top
|
||||
# of the file, so a reader can skip past the comments and hand the
|
||||
# rest of the file to a standard CSV reader.
|
||||
#
|
||||
# Each line in the CSV section contains 5 fields:
|
||||
#
|
||||
# mask value mnemonic encoding tags
|
||||
#
|
||||
# The mnemonic and encoding correspond to the notation used in the
|
||||
# ARM Architecture Reference Manual.
|
||||
#
|
||||
# A particular instruction word w matches a line if w&mask == value.
|
||||
# The mask and value are computed from a combination of the encodings
|
||||
# and the additional pseudocode restrictions given in the manual
|
||||
# for each instruction.
|
||||
#
|
||||
# Multiple lines may match a particular instruction word.
|
||||
# The tags specify which line takes priority in these cases,
|
||||
# as a partial order. Eventually the plan is that a tag of the form
|
||||
# 'name:x' marks a line with the name x, and a tag of the form
|
||||
# 'except:x' says that the line on which the tag appears is of lower
|
||||
# priority than the ones marked with name x.
|
||||
# For now, however, a tag of the form 'SEE XXX' is taken from the
|
||||
# manual and is like 'except:x' but without a rigorous resolution for
|
||||
# the x. So far we've been able to just sort instructions into two classes:
|
||||
# those with SEE and those without.
|
||||
#
|
||||
# The tag 'pseudo' indicates that this instruction is an alternate name
|
||||
# for another encoding and should be ignored during disassembly.
|
||||
#
|
||||
# This file was generated by a program reading the PDF version of
|
||||
# the manual, but it was then hand edited to make corrections.
|
||||
# The eventual plan is for the generator to write the
|
||||
# file directly, using the PDF and a list of modifications, but
|
||||
# no hand editing. Then, when a new version of the manual comes out,
|
||||
# updating the list should be as easy as downloading the new PDF
|
||||
# and invoking the generator. However, we are not there yet.
|
||||
#
|
||||
"0x0fe00000","0x02a00000","ADC{S}<c> <Rd>,<Rn>,#<const>","cond:4|0|0|1|0|1|0|1|S|Rn:4|Rd:4|imm12:12","SEE SUBS PC, LR and related instructions"
|
||||
"0x0fe00090","0x00a00010","ADC{S}<c> <Rd>,<Rn>,<Rm>,<type> <Rs>","cond:4|0|0|0|0|1|0|1|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4",""
|
||||
"0x0fe00010","0x00a00000","ADC{S}<c> <Rd>,<Rn>,<Rm>{,<shift>}","cond:4|0|0|0|0|1|0|1|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4","SEE SUBS PC, LR and related instructions"
|
||||
"0x0fe00000","0x02800000","ADD{S}<c> <Rd>,<Rn>,#<const>","cond:4|0|0|1|0|1|0|0|S|Rn:4|Rd:4|imm12:12","SEE ADR SEE ADD (SP plus immediate) SEE SUBS PC, LR and related instructions"
|
||||
"0x0fe00090","0x00800010","ADD{S}<c> <Rd>,<Rn>,<Rm>,<type> <Rs>","cond:4|0|0|0|0|1|0|0|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4",""
|
||||
"0x0fe00010","0x00800000","ADD{S}<c> <Rd>,<Rn>,<Rm>{,<shift>}","cond:4|0|0|0|0|1|0|0|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4","SEE SUBS PC, LR and related instructions SEE ADD (SP plus register)"
|
||||
"0x0fef0000","0x028d0000","ADD{S}<c> <Rd>,SP,#<const>","cond:4|0|0|1|0|1|0|0|S|1|1|0|1|Rd:4|imm12:12","SEE SUBS PC, LR and related instructions"
|
||||
"0x0fef0010","0x008d0000","ADD{S}<c> <Rd>,SP,<Rm>{,<shift>}","cond:4|0|0|0|0|1|0|0|S|1|1|0|1|Rd:4|imm5:5|type:2|0|Rm:4","SEE SUBS PC, LR and related instructions"
|
||||
"0x0fff0000","0x028f0000","ADR<c> <Rd>,<label+12>","cond:4|0|0|1|0|1|0|0|0|1|1|1|1|Rd:4|imm12:12","pseudo"
|
||||
"0x0fff0000","0x024f0000","ADR<c> <Rd>,<label-12>","cond:4|0|0|1|0|0|1|0|0|1|1|1|1|Rd:4|imm12:12","pseudo"
|
||||
"0x0fe00000","0x02000000","AND{S}<c> <Rd>,<Rn>,#<const>","cond:4|0|0|1|0|0|0|0|S|Rn:4|Rd:4|imm12:12","SEE SUBS PC, LR and related instructions"
|
||||
"0x0fe00090","0x00000010","AND{S}<c> <Rd>,<Rn>,<Rm>,<type> <Rs>","cond:4|0|0|0|0|0|0|0|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4",""
|
||||
"0x0fe00010","0x00000000","AND{S}<c> <Rd>,<Rn>,<Rm>{,<shift>}","cond:4|0|0|0|0|0|0|0|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4","SEE SUBS PC, LR and related instructions"
|
||||
"0x0fef0070","0x01a00040","ASR{S}<c> <Rd>,<Rm>,#<imm5_32>","cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|imm5:5|1|0|0|Rm:4",""
|
||||
"0x0fef00f0","0x01a00050","ASR{S}<c> <Rd>,<Rn>,<Rm>","cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|Rm:4|0|1|0|1|Rn:4",""
|
||||
"0x0f000000","0x0a000000","B<c> <label24>","cond:4|1|0|1|0|imm24:24",""
|
||||
"0x0fe0007f","0x07c0001f","BFC<c> <Rd>,#<lsb>,#<width>","cond:4|0|1|1|1|1|1|0|msb:5|Rd:4|lsb:5|0|0|1|1|1|1|1",""
|
||||
"0x0fe00070","0x07c00010","BFI<c> <Rd>,<Rn>,#<lsb>,#<width>","cond:4|0|1|1|1|1|1|0|msb:5|Rd:4|lsb:5|0|0|1|Rn:4","SEE BFC"
|
||||
"0x0fe00000","0x03c00000","BIC{S}<c> <Rd>,<Rn>,#<const>","cond:4|0|0|1|1|1|1|0|S|Rn:4|Rd:4|imm12:12","SEE SUBS PC, LR and related instructions"
|
||||
"0x0fe00090","0x01c00010","BIC{S}<c> <Rd>,<Rn>,<Rm>,<type> <Rs>","cond:4|0|0|0|1|1|1|0|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4",""
|
||||
"0x0fe00010","0x01c00000","BIC{S}<c> <Rd>,<Rn>,<Rm>{,<shift>}","cond:4|0|0|0|1|1|1|0|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4","SEE SUBS PC, LR and related instructions"
|
||||
"0x0ff000f0","0x01200070","BKPT<c> #<imm12+4>","cond:4|0|0|0|1|0|0|1|0|imm12:12|0|1|1|1|imm4:4",""
|
||||
"0x0f000000","0x0b000000","BL<c> <label24>","cond:4|1|0|1|1|imm24:24",""
|
||||
"0xfe000000","0xfa000000","BLX <label24H>","1|1|1|1|1|0|1|H|imm24:24",""
|
||||
"0x0ffffff0","0x012fff30","BLX<c> <Rm>","cond:4|0|0|0|1|0|0|1|0|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4",""
|
||||
"0x0ffffff0","0x012fff10","BX<c> <Rm>","cond:4|0|0|0|1|0|0|1|0|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4",""
|
||||
"0x0ffffff0","0x012fff20","BXJ<c> <Rm>","cond:4|0|0|0|1|0|0|1|0|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|0|0|1|0|Rm:4",""
|
||||
"0xffffffff","0xf57ff01f","CLREX","1|1|1|1|0|1|0|1|0|1|1|1|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|0|0|1|(1)|(1)|(1)|(1)",""
|
||||
"0x0fff0ff0","0x016f0f10","CLZ<c> <Rd>,<Rm>","cond:4|0|0|0|1|0|1|1|0|(1)|(1)|(1)|(1)|Rd:4|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4",""
|
||||
"0x0ff0f000","0x03700000","CMN<c> <Rn>,#<const>","cond:4|0|0|1|1|0|1|1|1|Rn:4|(0)|(0)|(0)|(0)|imm12:12",""
|
||||
"0x0ff0f090","0x01700010","CMN<c> <Rn>,<Rm>,<type> <Rs>","cond:4|0|0|0|1|0|1|1|1|Rn:4|(0)|(0)|(0)|(0)|Rs:4|0|type:2|1|Rm:4",""
|
||||
"0x0ff0f010","0x01700000","CMN<c> <Rn>,<Rm>{,<shift>}","cond:4|0|0|0|1|0|1|1|1|Rn:4|(0)|(0)|(0)|(0)|imm5:5|type:2|0|Rm:4",""
|
||||
"0x0ff0f000","0x03500000","CMP<c> <Rn>,#<const>","cond:4|0|0|1|1|0|1|0|1|Rn:4|(0)|(0)|(0)|(0)|imm12:12",""
|
||||
"0x0ff0f090","0x01500010","CMP<c> <Rn>,<Rm>,<type> <Rs>","cond:4|0|0|0|1|0|1|0|1|Rn:4|(0)|(0)|(0)|(0)|Rs:4|0|type:2|1|Rm:4",""
|
||||
"0x0ff0f010","0x01500000","CMP<c> <Rn>,<Rm>{,<shift>}","cond:4|0|0|0|1|0|1|0|1|Rn:4|(0)|(0)|(0)|(0)|imm5:5|type:2|0|Rm:4",""
|
||||
"0x0ffffff0","0x0320f0f0","DBG<c> #<option>","cond:4|0|0|1|1|0|0|1|0|0|0|0|0|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|1|1|1|1|option:4",""
|
||||
"0xfffffff0","0xf57ff050","DMB #<option>","1|1|1|1|0|1|0|1|0|1|1|1|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|1|0|1|option:4",""
|
||||
"0xfffffff0","0xf57ff040","DSB #<option>","1|1|1|1|0|1|0|1|0|1|1|1|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|1|0|0|option:4",""
|
||||
"0x0fe00000","0x02200000","EOR{S}<c> <Rd>,<Rn>,#<const>","cond:4|0|0|1|0|0|0|1|S|Rn:4|Rd:4|imm12:12","SEE SUBS PC, LR and related instructions"
|
||||
"0x0fe00090","0x00200010","EOR{S}<c> <Rd>,<Rn>,<Rm>,<type> <Rs>","cond:4|0|0|0|0|0|0|1|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4",""
|
||||
"0x0fe00010","0x00200000","EOR{S}<c> <Rd>,<Rn>,<Rm>{,<shift>}","cond:4|0|0|0|0|0|0|1|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4","SEE SUBS PC, LR and related instructions"
|
||||
"0xfffffff0","0xf57ff060","ISB #<option>","1|1|1|1|0|1|0|1|0|1|1|1|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|1|1|0|option:4",""
|
||||
"0x0fd00000","0x08900000","LDM<c> <Rn>{!},<registers>","cond:4|1|0|0|0|1|0|W|1|Rn:4|register_list:16","SEE POP"
|
||||
"0x0fd00000","0x08100000","LDMDA<c> <Rn>{!},<registers>","cond:4|1|0|0|0|0|0|W|1|Rn:4|register_list:16",""
|
||||
"0x0fd00000","0x09100000","LDMDB<c> <Rn>{!},<registers>","cond:4|1|0|0|1|0|0|W|1|Rn:4|register_list:16",""
|
||||
"0x0fd00000","0x09900000","LDMIB<c> <Rn>{!},<registers>","cond:4|1|0|0|1|1|0|W|1|Rn:4|register_list:16",""
|
||||
"0x0f7f0000","0x051f0000","LDR<c> <Rt>,<label+/-12>","cond:4|0|1|0|(1)|U|0|(0)|1|1|1|1|1|Rt:4|imm12:12",""
|
||||
"0x0e500010","0x06100000","LDR<c> <Rt>,[<Rn>,+/-<Rm>{, <shift>}]{!}","cond:4|0|1|1|P|U|0|W|1|Rn:4|Rt:4|imm5:5|type:2|0|Rm:4","SEE LDRT"
|
||||
"0x0e500000","0x04100000","LDR<c> <Rt>,[<Rn>{,#+/-<imm12>}]{!}","cond:4|0|1|0|P|U|0|W|1|Rn:4|Rt:4|imm12:12","SEE LDR (literal) SEE LDRT SEE POP"
|
||||
"0x0f7f0000","0x055f0000","LDRB<c> <Rt>,<label+/-12>","cond:4|0|1|0|(1)|U|1|(0)|1|1|1|1|1|Rt:4|imm12:12",""
|
||||
"0x0e500010","0x06500000","LDRB<c> <Rt>,[<Rn>,+/-<Rm>{, <shift>}]{!}","cond:4|0|1|1|P|U|1|W|1|Rn:4|Rt:4|imm5:5|type:2|0|Rm:4","SEE LDRBT"
|
||||
"0x0e500000","0x04500000","LDRB<c> <Rt>,[<Rn>{,#+/-<imm12>}]{!}","cond:4|0|1|0|P|U|1|W|1|Rn:4|Rt:4|imm12:12","SEE LDRB (literal) SEE LDRBT"
|
||||
"0x0f700000","0x04700000","LDRBT<c> <Rt>,[<Rn>],#+/-<imm12>","cond:4|0|1|0|0|U|1|1|1|Rn:4|Rt:4|imm12:12",""
|
||||
"0x0f700010","0x06700000","LDRBT<c> <Rt>,[<Rn>],+/-<Rm>{, <shift>}","cond:4|0|1|1|0|U|1|1|1|Rn:4|Rt:4|imm5:5|type:2|0|Rm:4",""
|
||||
"0x0f7f10f0","0x014f00d0","LDRD<c> <Rt1>,<Rt2>,<label+/-4+4>","cond:4|0|0|0|(1)|U|1|(0)|0|1|1|1|1|Rt:4|imm4H:4|1|1|0|1|imm4L:4","pseudo"
|
||||
"0x0e500ff0","0x000000d0","LDRD<c> <Rt1>,<Rt2>,[<Rn>,+/-<Rm>]{!}","cond:4|0|0|0|P|U|0|W|0|Rn:4|Rt:4|(0)|(0)|(0)|(0)|1|1|0|1|Rm:4",""
|
||||
"0x0e5000f0","0x004000d0","LDRD<c> <Rt1>,<Rt2>,[<Rn>{,#+/-<imm8>}]{!}","cond:4|0|0|0|P|U|1|W|0|Rn:4|Rt:4|imm4H:4|1|1|0|1|imm4L:4","SEE LDRD (literal)"
|
||||
"0x0ff00fff","0x01900f9f","LDREX<c> <Rt>,[<Rn>]","cond:4|0|0|0|1|1|0|0|1|Rn:4|Rt:4|(1)|(1)|(1)|(1)|1|0|0|1|(1)|(1)|(1)|(1)",""
|
||||
"0x0ff00fff","0x01d00f9f","LDREXB<c> <Rt>, [<Rn>]","cond:4|0|0|0|1|1|1|0|1|Rn:4|Rt:4|(1)|(1)|(1)|(1)|1|0|0|1|(1)|(1)|(1)|(1)",""
|
||||
"0x0ff00fff","0x01b00f9f","LDREXD<c> <Rt1>,<Rt2>,[<Rn>]","cond:4|0|0|0|1|1|0|1|1|Rn:4|Rt:4|(1)|(1)|(1)|(1)|1|0|0|1|(1)|(1)|(1)|(1)",""
|
||||
"0x0ff00fff","0x01f00f9f","LDREXH<c> <Rt>, [<Rn>]","cond:4|0|0|0|1|1|1|1|1|Rn:4|Rt:4|(1)|(1)|(1)|(1)|1|0|0|1|(1)|(1)|(1)|(1)",""
|
||||
"0x0f7f00f0","0x015f00b0","LDRH<c> <Rt>,<label+/-4+4>","cond:4|0|0|0|1|U|1|0|1|1|1|1|1|Rt:4|imm4H:4|1|0|1|1|imm4L:4","pseudo"
|
||||
"0x0e500ff0","0x001000b0","LDRH<c> <Rt>,[<Rn>,+/-<Rm>]{!}","cond:4|0|0|0|P|U|0|W|1|Rn:4|Rt:4|0|0|0|0|1|0|1|1|Rm:4","SEE LDRHT"
|
||||
"0x0e5000f0","0x005000b0","LDRH<c> <Rt>,[<Rn>{,#+/-<imm8>}]{!}","cond:4|0|0|0|P|U|1|W|1|Rn:4|Rt:4|imm4H:4|1|0|1|1|imm4L:4","SEE LDRH literal SEE LDRHT"
|
||||
"0x0f7000f0","0x007000b0","LDRHT<c> <Rt>, [<Rn>] {,#+/-<imm8>}","cond:4|0|0|0|0|U|1|1|1|Rn:4|Rt:4|imm4H:4|1|0|1|1|imm4L:4",""
|
||||
"0x0f700ff0","0x003000b0","LDRHT<c> <Rt>, [<Rn>], +/-<Rm>","cond:4|0|0|0|0|U|0|1|1|Rn:4|Rt:4|0|0|0|0|1|0|1|1|Rm:4",""
|
||||
"0x0f7f00f0","0x015f00d0","LDRSB<c> <Rt>,<label+/-4+4>","cond:4|0|0|0|1|U|1|0|1|1|1|1|1|Rt:4|imm4H:4|1|1|0|1|imm4L:4","pseudo"
|
||||
"0x0e500ff0","0x001000d0","LDRSB<c> <Rt>,[<Rn>,+/-<Rm>]{!}","cond:4|0|0|0|P|U|0|W|1|Rn:4|Rt:4|0|0|0|0|1|1|0|1|Rm:4","SEE LDRSBT"
|
||||
"0x0e5000f0","0x005000d0","LDRSB<c> <Rt>,[<Rn>{,#+/-<imm8>}]{!}","cond:4|0|0|0|P|U|1|W|1|Rn:4|Rt:4|imm4H:4|1|1|0|1|imm4L:4","SEE LDRSB literal SEE LDRSBT"
|
||||
"0x0f7000f0","0x007000d0","LDRSBT<c> <Rt>, [<Rn>] {,#+/-<imm8>}","cond:4|0|0|0|0|U|1|1|1|Rn:4|Rt:4|imm4H:4|1|1|0|1|imm4L:4",""
|
||||
"0x0f700ff0","0x003000d0","LDRSBT<c> <Rt>, [<Rn>], +/-<Rm>","cond:4|0|0|0|0|U|0|1|1|Rn:4|Rt:4|0|0|0|0|1|1|0|1|Rm:4",""
|
||||
"0x0f7f00f0","0x015f00f0","LDRSH<c> <Rt>,<label+/-4+4>","cond:4|0|0|0|1|U|1|0|1|1|1|1|1|Rt:4|imm4H:4|1|1|1|1|imm4L:4","pseudo"
|
||||
"0x0e500ff0","0x001000f0","LDRSH<c> <Rt>,[<Rn>,+/-<Rm>]{!}","cond:4|0|0|0|P|U|0|W|1|Rn:4|Rt:4|0|0|0|0|1|1|1|1|Rm:4","SEE LDRSHT"
|
||||
"0x0e5000f0","0x005000f0","LDRSH<c> <Rt>,[<Rn>{,#+/-<imm8>}]{!}","cond:4|0|0|0|P|U|1|W|1|Rn:4|Rt:4|imm4H:4|1|1|1|1|imm4L:4","SEE LDRSH literal SEE LDRSHT"
|
||||
"0x0f7000f0","0x007000f0","LDRSHT<c> <Rt>, [<Rn>] {,#+/-<imm8>}","cond:4|0|0|0|0|U|1|1|1|Rn:4|Rt:4|imm4H:4|1|1|1|1|imm4L:4",""
|
||||
"0x0f700ff0","0x003000f0","LDRSHT<c> <Rt>, [<Rn>], +/-<Rm>","cond:4|0|0|0|0|U|0|1|1|Rn:4|Rt:4|0|0|0|0|1|1|1|1|Rm:4",""
|
||||
"0x0f700000","0x04300000","LDRT<c> <Rt>, [<Rn>] {,#+/-<imm12>}","cond:4|0|1|0|0|U|0|1|1|Rn:4|Rt:4|imm12:12",""
|
||||
"0x0f700010","0x06300000","LDRT<c> <Rt>,[<Rn>],+/-<Rm>{, <shift>}","cond:4|0|1|1|0|U|0|1|1|Rn:4|Rt:4|imm5:5|type:2|0|Rm:4",""
|
||||
"0x0fef0070","0x01a00000","LSL{S}<c> <Rd>,<Rm>,#<imm5_nz>","cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|imm5:5|0|0|0|Rm:4","SEE MOV register"
|
||||
"0x0fef00f0","0x01a00010","LSL{S}<c> <Rd>,<Rn>,<Rm>","cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|Rm:4|0|0|0|1|Rn:4",""
|
||||
"0x0fef0070","0x01a00020","LSR{S}<c> <Rd>,<Rm>,#<imm5_32>","cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|imm5:5|0|1|0|Rm:4",""
|
||||
"0x0fef00f0","0x01a00030","LSR{S}<c> <Rd>,<Rn>,<Rm>","cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|Rm:4|0|0|1|1|Rn:4",""
|
||||
"0x0fe000f0","0x00200090","MLA{S}<c> <Rd>,<Rn>,<Rm>,<Ra>","cond:4|0|0|0|0|0|0|1|S|Rd:4|Ra:4|Rm:4|1|0|0|1|Rn:4",""
|
||||
"0x0ff000f0","0x00600090","MLS<c> <Rd>,<Rn>,<Rm>,<Ra>","cond:4|0|0|0|0|0|1|1|0|Rd:4|Ra:4|Rm:4|1|0|0|1|Rn:4",""
|
||||
"0x0ff00000","0x03400000","MOVT<c> <Rd>,#<imm12+4>","cond:4|0|0|1|1|0|1|0|0|imm4:4|Rd:4|imm12:12",""
|
||||
"0x0ff00000","0x03000000","MOVW<c> <Rd>,#<imm12+4>","cond:4|0|0|1|1|0|0|0|0|imm4:4|Rd:4|imm12:12",""
|
||||
"0x0fef0000","0x03a00000","MOV{S}<c> <Rd>,#<const>","cond:4|0|0|1|1|1|0|1|S|0|0|0|0|Rd:4|imm12:12","SEE SUBS PC, LR and related instructions"
|
||||
"0x0fef0ff0","0x01a00000","MOV{S}<c> <Rd>,<Rm>","cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|0|0|0|0|0|0|0|0|Rm:4","SEE SUBS PC, LR and related instructions"
|
||||
"0x0fff0fff","0x010f0000","MRS<c> <Rd>,APSR","cond:4|0|0|0|1|0|0|0|0|(1)|(1)|(1)|(1)|Rd:4|(0)|(0)|(0)|(0)|0|0|0|0|(0)|(0)|(0)|(0)",""
|
||||
"0x0ffffff0","0x012cf000","MSR<c> APSR,<Rn>","cond:4|0|0|0|1|0|0|1|0|1|1|0|0|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|0|0|0|Rn:4",""
|
||||
"0x0ffff000","0x032cf000","MSR<c> APSR,#<const>","cond:4|0|0|1|1|0|0|1|0|1|1|0|0|(1)|(1)|(1)|(1)|imm12:12",""
|
||||
"0x0fe0f0f0","0x00000090","MUL{S}<c> <Rd>,<Rn>,<Rm>","cond:4|0|0|0|0|0|0|0|S|Rd:4|(0)|(0)|(0)|(0)|Rm:4|1|0|0|1|Rn:4",""
|
||||
"0x0fef0000","0x03e00000","MVN{S}<c> <Rd>,#<const>","cond:4|0|0|1|1|1|1|1|S|(0)|(0)|(0)|(0)|Rd:4|imm12:12","SEE SUBS PC, LR and related instructions"
|
||||
"0x0fef0090","0x01e00010","MVN{S}<c> <Rd>,<Rm>,<type> <Rs>","cond:4|0|0|0|1|1|1|1|S|(0)|(0)|(0)|(0)|Rd:4|Rs:4|0|type:2|1|Rm:4",""
|
||||
"0x0fef0010","0x01e00000","MVN{S}<c> <Rd>,<Rm>{,<shift>}","cond:4|0|0|0|1|1|1|1|S|(0)|(0)|(0)|(0)|Rd:4|imm5:5|type:2|0|Rm:4","SEE SUBS PC, LR and related instructions"
|
||||
"0x0fffffff","0x0320f000","NOP<c>","cond:4|0|0|1|1|0|0|1|0|0|0|0|0|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|0|0|0|0|0|0|0",""
|
||||
"0x0fe00000","0x03800000","ORR{S}<c> <Rd>,<Rn>,#<const>","cond:4|0|0|1|1|1|0|0|S|Rn:4|Rd:4|imm12:12","SEE SUBS PC, LR and related instructions"
|
||||
"0x0fe00090","0x01800010","ORR{S}<c> <Rd>,<Rn>,<Rm>,<type> <Rs>","cond:4|0|0|0|1|1|0|0|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4",""
|
||||
"0x0fe00010","0x01800000","ORR{S}<c> <Rd>,<Rn>,<Rm>{,<shift>}","cond:4|0|0|0|1|1|0|0|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4","SEE SUBS PC, LR and related instructions"
|
||||
"0x0ff00030","0x06800010","PKH<BT,TB><c> <Rd>,<Rn>,<Rm>{,LSL #<imm5>}","cond:4|0|1|1|0|1|0|0|0|Rn:4|Rd:4|imm5:5|tb|0|1|Rm:4",""
|
||||
"0xff7ff000","0xf55ff000","PLD <label+/-12>","1|1|1|1|0|1|0|1|U|(1)|0|1|1|1|1|1|(1)|(1)|(1)|(1)|imm12:12",""
|
||||
"0xff30f000","0xf510f000","PLD{W} [<Rn>,#+/-<imm12>]","1|1|1|1|0|1|0|1|U|R|0|1|Rn:4|(1)|(1)|(1)|(1)|imm12:12","SEE PLD (literal)"
|
||||
"0xff30f010","0xf710f000","PLD{W} [<Rn>,+/-<Rm>{, <shift>}]","1|1|1|1|0|1|1|1|U|R|0|1|Rn:4|(1)|(1)|(1)|(1)|imm5:5|type:2|0|Rm:4",""
|
||||
"0xff70f000","0xf450f000","PLI [<Rn>,#+/-<imm12>]","1|1|1|1|0|1|0|0|U|1|0|1|Rn:4|(1)|(1)|(1)|(1)|imm12:12",""
|
||||
"0xff70f010","0xf650f000","PLI [<Rn>,+/-<Rm>{, <shift>}]","1|1|1|1|0|1|1|0|U|1|0|1|Rn:4|(1)|(1)|(1)|(1)|imm5:5|type:2|0|Rm:4",""
|
||||
"0x0fff0000","0x08bd0000","POP<c> <registers2>","cond:4|1|0|0|0|1|0|1|1|1|1|0|1|register_list:16",""
|
||||
"0x0fff0fff","0x049d0004","POP<c> <registers1>","cond:4|0|1|0|0|1|0|0|1|1|1|0|1|Rt:4|0|0|0|0|0|0|0|0|0|1|0|0",""
|
||||
"0x0fff0000","0x092d0000","PUSH<c> <registers2>","cond:4|1|0|0|1|0|0|1|0|1|1|0|1|register_list:16",""
|
||||
"0x0fff0fff","0x052d0004","PUSH<c> <registers1>","cond:4|0|1|0|1|0|0|1|0|1|1|0|1|Rt:4|0|0|0|0|0|0|0|0|0|1|0|0",""
|
||||
"0x0ff00ff0","0x06200f10","QADD16<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4",""
|
||||
"0x0ff00ff0","0x06200f90","QADD8<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|0|0|1|Rm:4",""
|
||||
"0x0ff00ff0","0x01000050","QADD<c> <Rd>,<Rm>,<Rn>","cond:4|0|0|0|1|0|0|0|0|Rn:4|Rd:4|(0)|(0)|(0)|(0)|0|1|0|1|Rm:4",""
|
||||
"0x0ff00ff0","0x06200f30","QASX<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4",""
|
||||
"0x0ff00ff0","0x01400050","QDADD<c> <Rd>,<Rm>,<Rn>","cond:4|0|0|0|1|0|1|0|0|Rn:4|Rd:4|(0)|(0)|(0)|(0)|0|1|0|1|Rm:4",""
|
||||
"0x0ff00ff0","0x01600050","QDSUB<c> <Rd>,<Rm>,<Rn>","cond:4|0|0|0|1|0|1|1|0|Rn:4|Rd:4|0|0|0|0|0|1|0|1|Rm:4",""
|
||||
"0x0ff00ff0","0x06200f50","QSAX<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|0|1|Rm:4",""
|
||||
"0x0ff00ff0","0x06200f70","QSUB16<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|1|1|Rm:4",""
|
||||
"0x0ff00ff0","0x06200ff0","QSUB8<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|1|1|1|Rm:4",""
|
||||
"0x0ff00ff0","0x01200050","QSUB<c> <Rd>,<Rm>,<Rn>","cond:4|0|0|0|1|0|0|1|0|Rn:4|Rd:4|0|0|0|0|0|1|0|1|Rm:4",""
|
||||
"0x0fff0ff0","0x06ff0f30","RBIT<c> <Rd>,<Rm>","cond:4|0|1|1|0|1|1|1|1|(1)|(1)|(1)|(1)|Rd:4|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4",""
|
||||
"0x0fff0ff0","0x06bf0fb0","REV16<c> <Rd>,<Rm>","cond:4|0|1|1|0|1|0|1|1|(1)|(1)|(1)|(1)|Rd:4|(1)|(1)|(1)|(1)|1|0|1|1|Rm:4",""
|
||||
"0x0fff0ff0","0x06bf0f30","REV<c> <Rd>,<Rm>","cond:4|0|1|1|0|1|0|1|1|(1)|(1)|(1)|(1)|Rd:4|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4",""
|
||||
"0x0fff0ff0","0x06ff0fb0","REVSH<c> <Rd>,<Rm>","cond:4|0|1|1|0|1|1|1|1|(1)|(1)|(1)|(1)|Rd:4|(1)|(1)|(1)|(1)|1|0|1|1|Rm:4",""
|
||||
"0x0fef0070","0x01a00060","ROR{S}<c> <Rd>,<Rm>,#<imm5>","cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|imm5:5|1|1|0|Rm:4","SEE RRX"
|
||||
"0x0fef00f0","0x01a00070","ROR{S}<c> <Rd>,<Rn>,<Rm>","cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|Rm:4|0|1|1|1|Rn:4",""
|
||||
"0x0fef0ff0","0x01a00060","RRX{S}<c> <Rd>,<Rm>","cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|0|0|0|0|0|1|1|0|Rm:4",""
|
||||
"0x0fe00000","0x02600000","RSB{S}<c> <Rd>,<Rn>,#<const>","cond:4|0|0|1|0|0|1|1|S|Rn:4|Rd:4|imm12:12","SEE SUBS PC, LR and related instructions"
|
||||
"0x0fe00090","0x00600010","RSB{S}<c> <Rd>,<Rn>,<Rm>,<type> <Rs>","cond:4|0|0|0|0|0|1|1|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4",""
|
||||
"0x0fe00010","0x00600000","RSB{S}<c> <Rd>,<Rn>,<Rm>{,<shift>}","cond:4|0|0|0|0|0|1|1|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4","SEE SUBS PC, LR and related instructions"
|
||||
"0x0fe00000","0x02e00000","RSC{S}<c> <Rd>,<Rn>,#<const>","cond:4|0|0|1|0|1|1|1|S|Rn:4|Rd:4|imm12:12","SEE SUBS PC, LR and related instructions"
|
||||
"0x0fe00090","0x00e00010","RSC{S}<c> <Rd>,<Rn>,<Rm>,<type> <Rs>","cond:4|0|0|0|0|1|1|1|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4",""
|
||||
"0x0fe00010","0x00e00000","RSC{S}<c> <Rd>,<Rn>,<Rm>{,<shift>}","cond:4|0|0|0|0|1|1|1|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4","SEE SUBS PC, LR and related instructions"
|
||||
"0x0ff00ff0","0x06100f10","SADD16<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4",""
|
||||
"0x0ff00ff0","0x06100f90","SADD8<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|0|0|1|Rm:4",""
|
||||
"0x0ff00ff0","0x06100f30","SASX<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4",""
|
||||
"0x0fe00000","0x02c00000","SBC{S}<c> <Rd>,<Rn>,#<const>","cond:4|0|0|1|0|1|1|0|S|Rn:4|Rd:4|imm12:12","SEE SUBS PC, LR and related instructions"
|
||||
"0x0fe00090","0x00c00010","SBC{S}<c> <Rd>,<Rn>,<Rm>,<type> <Rs>","cond:4|0|0|0|0|1|1|0|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4",""
|
||||
"0x0fe00010","0x00c00000","SBC{S}<c> <Rd>,<Rn>,<Rm>{,<shift>}","cond:4|0|0|0|0|1|1|0|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4","SEE SUBS PC, LR and related instructions"
|
||||
"0x0fe00070","0x07a00050","SBFX<c> <Rd>,<Rn>,#<lsb>,#<widthm1>","cond:4|0|1|1|1|1|0|1|widthm1:5|Rd:4|lsb:5|1|0|1|Rn:4",""
|
||||
"0x0ff0f0f0","0x0710f010","SDIV<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|1|0|0|0|1|Rd:4|(1)|(1)|(1)|(1)|Rm:4|0|0|0|1|Rn:4",""
|
||||
"0x0ff00ff0","0x06800fb0","SEL<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|1|0|0|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|0|1|1|Rm:4",""
|
||||
"0xfffffdff","0xf1010000","SETEND <endian_specifier>","1|1|1|1|0|0|0|1|0|0|0|0|0|0|0|1|0|0|0|0|0|0|E|(0)|(0)|(0)|(0)|(0)|(0)|(0)|(0)|(0)",""
|
||||
"0x0fffffff","0x0320f004","SEV<c>","cond:4|0|0|1|1|0|0|1|0|0|0|0|0|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|0|0|0|0|1|0|0",""
|
||||
"0x0ff00ff0","0x06300f10","SHADD16<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4",""
|
||||
"0x0ff00ff0","0x06300f90","SHADD8<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|0|0|1|Rm:4",""
|
||||
"0x0ff00ff0","0x06300f30","SHASX<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4",""
|
||||
"0x0ff00ff0","0x06300f50","SHSAX<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|0|1|Rm:4",""
|
||||
"0x0ff00ff0","0x06300f70","SHSUB16<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|1|1|Rm:4",""
|
||||
"0x0ff00ff0","0x06300ff0","SHSUB8<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|1|1|1|Rm:4",""
|
||||
"0x0ff00090","0x01000080","SMLA<x><y><c> <Rd>,<Rn>,<Rm>,<Ra>","cond:4|0|0|0|1|0|0|0|0|Rd:4|Ra:4|Rm:4|1|M|N|0|Rn:4",""
|
||||
"0x0ff000d0","0x07000010","SMLAD{X}<c> <Rd>,<Rn>,<Rm>,<Ra>","cond:4|0|1|1|1|0|0|0|0|Rd:4|Ra:4|Rm:4|0|0|M|1|Rn:4","SEE SMUAD"
|
||||
"0x0ff00090","0x01400080","SMLAL<x><y><c> <RdLo>,<RdHi>,<Rn>,<Rm>","cond:4|0|0|0|1|0|1|0|0|RdHi:4|RdLo:4|Rm:4|1|M|N|0|Rn:4",""
|
||||
"0x0ff000d0","0x07400010","SMLALD{X}<c> <RdLo>,<RdHi>,<Rn>,<Rm>","cond:4|0|1|1|1|0|1|0|0|RdHi:4|RdLo:4|Rm:4|0|0|M|1|Rn:4",""
|
||||
"0x0fe000f0","0x00e00090","SMLAL{S}<c> <RdLo>,<RdHi>,<Rn>,<Rm>","cond:4|0|0|0|0|1|1|1|S|RdHi:4|RdLo:4|Rm:4|1|0|0|1|Rn:4",""
|
||||
"0x0ff000b0","0x01200080","SMLAW<y><c> <Rd>,<Rn>,<Rm>,<Ra>","cond:4|0|0|0|1|0|0|1|0|Rd:4|Ra:4|Rm:4|1|M|0|0|Rn:4",""
|
||||
"0x0ff000d0","0x07000050","SMLSD{X}<c> <Rd>,<Rn>,<Rm>,<Ra>","cond:4|0|1|1|1|0|0|0|0|Rd:4|Ra:4|Rm:4|0|1|M|1|Rn:4","SEE SMUSD"
|
||||
"0x0ff000d0","0x07400050","SMLSLD{X}<c> <RdLo>,<RdHi>,<Rn>,<Rm>","cond:4|0|1|1|1|0|1|0|0|RdHi:4|RdLo:4|Rm:4|0|1|M|1|Rn:4",""
|
||||
"0x0ff000d0","0x07500010","SMMLA{R}<c> <Rd>,<Rn>,<Rm>,<Ra>","cond:4|0|1|1|1|0|1|0|1|Rd:4|Ra:4|Rm:4|0|0|R|1|Rn:4","SEE SMMUL"
|
||||
"0x0ff000d0","0x075000d0","SMMLS{R}<c> <Rd>,<Rn>,<Rm>,<Ra>","cond:4|0|1|1|1|0|1|0|1|Rd:4|Ra:4|Rm:4|1|1|R|1|Rn:4",""
|
||||
"0x0ff0f0d0","0x0750f010","SMMUL{R}<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|1|0|1|0|1|Rd:4|1|1|1|1|Rm:4|0|0|R|1|Rn:4",""
|
||||
"0x0ff0f0d0","0x0700f010","SMUAD{X}<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|1|0|0|0|0|Rd:4|1|1|1|1|Rm:4|0|0|M|1|Rn:4",""
|
||||
"0x0ff0f090","0x01600080","SMUL<x><y><c> <Rd>,<Rn>,<Rm>","cond:4|0|0|0|1|0|1|1|0|Rd:4|0|0|0|0|Rm:4|1|M|N|0|Rn:4",""
|
||||
"0x0fe000f0","0x00c00090","SMULL{S}<c> <RdLo>,<RdHi>,<Rn>,<Rm>","cond:4|0|0|0|0|1|1|0|S|RdHi:4|RdLo:4|Rm:4|1|0|0|1|Rn:4",""
|
||||
"0x0ff0f0b0","0x012000a0","SMULW<y><c> <Rd>,<Rn>,<Rm>","cond:4|0|0|0|1|0|0|1|0|Rd:4|0|0|0|0|Rm:4|1|M|1|0|Rn:4",""
|
||||
"0x0ff0f0d0","0x0700f050","SMUSD{X}<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|1|0|0|0|0|Rd:4|1|1|1|1|Rm:4|0|1|M|1|Rn:4",""
|
||||
"0x0ff00ff0","0x06a00f30","SSAT16<c> <Rd>,#<sat_imm4m1>,<Rn>","cond:4|0|1|1|0|1|0|1|0|sat_imm:4|Rd:4|(1)|(1)|(1)|(1)|0|0|1|1|Rn:4",""
|
||||
"0x0fe00030","0x06a00010","SSAT<c> <Rd>,#<sat_imm5m1>,<Rn>{,<shift>}","cond:4|0|1|1|0|1|0|1|sat_imm:5|Rd:4|imm5:5|sh|0|1|Rn:4",""
|
||||
"0x0ff00ff0","0x06100f50","SSAX<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|0|1|Rm:4",""
|
||||
"0x0ff00ff0","0x06100f70","SSUB16<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|1|1|Rm:4",""
|
||||
"0x0ff00ff0","0x06100ff0","SSUB8<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|1|1|1|Rm:4",""
|
||||
"0x0fd00000","0x08800000","STM<c> <Rn>{!},<registers>","cond:4|1|0|0|0|1|0|W|0|Rn:4|register_list:16",""
|
||||
"0x0fd00000","0x08000000","STMDA<c> <Rn>{!},<registers>","cond:4|1|0|0|0|0|0|W|0|Rn:4|register_list:16",""
|
||||
"0x0fd00000","0x09000000","STMDB<c> <Rn>{!},<registers>","cond:4|1|0|0|1|0|0|W|0|Rn:4|register_list:16","SEE PUSH"
|
||||
"0x0fd00000","0x09800000","STMIB<c> <Rn>{!},<registers>","cond:4|1|0|0|1|1|0|W|0|Rn:4|register_list:16",""
|
||||
"0x0e500018","0x06000000","STR<c> <Rt>,[<Rn>,+/-<Rm>{, <shift>}]{!}","cond:4|0|1|1|P|U|0|W|0|Rn:4|Rt:4|imm5:5|type:2|0|Rm:4","SEE STRT"
|
||||
"0x0e500000","0x04000000","STR<c> <Rt>,[<Rn>{,#+/-<imm12>}]{!}","cond:4|0|1|0|P|U|0|W|0|Rn:4|Rt:4|imm12:12","SEE STRT SEE PUSH"
|
||||
"0x0e500010","0x06400000","STRB<c> <Rt>,[<Rn>,+/-<Rm>{, <shift>}]{!}","cond:4|0|1|1|P|U|1|W|0|Rn:4|Rt:4|imm5:5|type:2|0|Rm:4","SEE STRBT"
|
||||
"0x0e500000","0x04400000","STRB<c> <Rt>,[<Rn>{,#+/-<imm12>}]{!}","cond:4|0|1|0|P|U|1|W|0|Rn:4|Rt:4|imm12:12","SEE STRBT"
|
||||
"0x0f700000","0x04600000","STRBT<c> <Rt>,[<Rn>],#+/-<imm12>","cond:4|0|1|0|0|U|1|1|0|Rn:4|Rt:4|imm12:12",""
|
||||
"0x0f700010","0x06600000","STRBT<c> <Rt>,[<Rn>],+/-<Rm>{, <shift>}","cond:4|0|1|1|0|U|1|1|0|Rn:4|Rt:4|imm5:5|type:2|0|Rm:4",""
|
||||
"0x0e500ff0","0x000000f0","STRD<c> <Rt1>,<Rt2>,[<Rn>,+/-<Rm>]{!}","cond:4|0|0|0|P|U|0|W|0|Rn:4|Rt:4|(0)|(0)|(0)|(0)|1|1|1|1|Rm:4",""
|
||||
"0x0e5000f0","0x004000f0","STRD<c> <Rt1>,<Rt2>,[<Rn>{,#+/-<imm8>}]{!}","cond:4|0|0|0|P|U|1|W|0|Rn:4|Rt:4|imm4H:4|1|1|1|1|imm4L:4",""
|
||||
"0x0ff00ff0","0x01800f90","STREX<c> <Rd>,<Rt>,[<Rn>]","cond:4|0|0|0|1|1|0|0|0|Rn:4|Rd:4|1|1|1|1|1|0|0|1|Rt:4",""
|
||||
"0x0ff00ff0","0x01c00f90","STREXB<c> <Rd>,<Rt>,[<Rn>]","cond:4|0|0|0|1|1|1|0|0|Rn:4|Rd:4|1|1|1|1|1|0|0|1|Rt:4",""
|
||||
"0x0ff00ff0","0x01a00f90","STREXD<c> <Rd>,<Rt1>,<Rt2>,[<Rn>]","cond:4|0|0|0|1|1|0|1|0|Rn:4|Rd:4|1|1|1|1|1|0|0|1|Rt:4",""
|
||||
"0x0ff00ff0","0x01e00f90","STREXH<c> <Rd>,<Rt>,[<Rn>]","cond:4|0|0|0|1|1|1|1|0|Rn:4|Rd:4|1|1|1|1|1|0|0|1|Rt:4",""
|
||||
"0x0e500ff0","0x000000b0","STRH<c> <Rt>,[<Rn>,+/-<Rm>]{!}","cond:4|0|0|0|P|U|0|W|0|Rn:4|Rt:4|0|0|0|0|1|0|1|1|Rm:4","SEE STRHT"
|
||||
"0x0e5000f0","0x004000b0","STRH<c> <Rt>,[<Rn>{,#+/-<imm8>}]{!}","cond:4|0|0|0|P|U|1|W|0|Rn:4|Rt:4|imm4H:4|1|0|1|1|imm4L:4","SEE STRHT"
|
||||
"0x0f7000f0","0x006000b0","STRHT<c> <Rt>, [<Rn>] {,#+/-<imm8>}","cond:4|0|0|0|0|U|1|1|0|Rn:4|Rt:4|imm4H:4|1|0|1|1|imm4L:4",""
|
||||
"0x0f700ff0","0x002000b0","STRHT<c> <Rt>, [<Rn>], +/-<Rm>","cond:4|0|0|0|0|U|0|1|0|Rn:4|Rt:4|0|0|0|0|1|0|1|1|Rm:4",""
|
||||
"0x0f700000","0x04200000","STRT<c> <Rt>, [<Rn>] {,#+/-<imm12>}","cond:4|0|1|0|0|U|0|1|0|Rn:4|Rt:4|imm12:12",""
|
||||
"0x0f700010","0x06200000","STRT<c> <Rt>,[<Rn>],+/-<Rm>{, <shift>}","cond:4|0|1|1|0|U|0|1|0|Rn:4|Rt:4|imm5:5|type:2|0|Rm:4",""
|
||||
"0x0fe00000","0x02400000","SUB{S}<c> <Rd>,<Rn>,#<const>","cond:4|0|0|1|0|0|1|0|S|Rn:4|Rd:4|imm12:12","SEE ADR SEE SUB (SP minus immediate) SEE SUBS PC, LR and related instructions"
|
||||
"0x0fe00090","0x00400010","SUB{S}<c> <Rd>,<Rn>,<Rm>,<type> <Rs>","cond:4|0|0|0|0|0|1|0|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4",""
|
||||
"0x0fe00010","0x00400000","SUB{S}<c> <Rd>,<Rn>,<Rm>{,<shift>}","cond:4|0|0|0|0|0|1|0|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4","SEE SUBS PC, LR and related instructions SEE SUB (SP minus register)"
|
||||
"0x0fef0000","0x024d0000","SUB{S}<c> <Rd>,SP,#<const>","cond:4|0|0|1|0|0|1|0|S|1|1|0|1|Rd:4|imm12:12","SEE SUBS PC, LR and related instructions"
|
||||
"0x0fef0010","0x004d0000","SUB{S}<c> <Rd>,SP,<Rm>{,<shift>}","cond:4|0|0|0|0|0|1|0|S|1|1|0|1|Rd:4|imm5:5|type:2|0|Rm:4","SEE SUBS PC, LR and related instructions"
|
||||
"0x0f000000","0x0f000000","SVC<c> #<imm24>","cond:4|1|1|1|1|imm24:24",""
|
||||
"0x0fb00ff0","0x01000090","SWP{B}<c> <Rt>,<Rm>,[<Rn>]","cond:4|0|0|0|1|0|B|0|0|Rn:4|Rt:4|0|0|0|0|1|0|0|1|Rm:4",""
|
||||
"0x0ff003f0","0x06800070","SXTAB16<c> <Rd>,<Rn>,<Rm>{,<rotation>}","cond:4|0|1|1|0|1|0|0|0|Rn:4|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4","SEE SXTB16"
|
||||
"0x0ff003f0","0x06a00070","SXTAB<c> <Rd>,<Rn>,<Rm>{,<rotation>}","cond:4|0|1|1|0|1|0|1|0|Rn:4|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4","SEE SXTB"
|
||||
"0x0ff003f0","0x06b00070","SXTAH<c> <Rd>,<Rn>,<Rm>{,<rotation>}","cond:4|0|1|1|0|1|0|1|1|Rn:4|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4","SEE SXTH"
|
||||
"0x0fff03f0","0x068f0070","SXTB16<c> <Rd>,<Rm>{,<rotation>}","cond:4|0|1|1|0|1|0|0|0|1|1|1|1|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4",""
|
||||
"0x0fff03f0","0x06af0070","SXTB<c> <Rd>,<Rm>{,<rotation>}","cond:4|0|1|1|0|1|0|1|0|1|1|1|1|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4",""
|
||||
"0x0fff03f0","0x06bf0070","SXTH<c> <Rd>,<Rm>{,<rotation>}","cond:4|0|1|1|0|1|0|1|1|1|1|1|1|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4",""
|
||||
"0x0ff0f000","0x03300000","TEQ<c> <Rn>,#<const>","cond:4|0|0|1|1|0|0|1|1|Rn:4|(0)|(0)|(0)|(0)|imm12:12",""
|
||||
"0x0ff0f090","0x01300010","TEQ<c> <Rn>,<Rm>,<type> <Rs>","cond:4|0|0|0|1|0|0|1|1|Rn:4|(0)|(0)|(0)|(0)|Rs:4|0|type:2|1|Rm:4",""
|
||||
"0x0ff0f010","0x01300000","TEQ<c> <Rn>,<Rm>{,<shift>}","cond:4|0|0|0|1|0|0|1|1|Rn:4|(0)|(0)|(0)|(0)|imm5:5|type:2|0|Rm:4",""
|
||||
"0x0ff0f000","0x03100000","TST<c> <Rn>,#<const>","cond:4|0|0|1|1|0|0|0|1|Rn:4|(0)|(0)|(0)|(0)|imm12:12",""
|
||||
"0x0ff0f090","0x01100010","TST<c> <Rn>,<Rm>,<type> <Rs>","cond:4|0|0|0|1|0|0|0|1|Rn:4|(0)|(0)|(0)|(0)|Rs:4|0|type:2|1|Rm:4",""
|
||||
"0x0ff0f010","0x01100000","TST<c> <Rn>,<Rm>{,<shift>}","cond:4|0|0|0|1|0|0|0|1|Rn:4|(0)|(0)|(0)|(0)|imm5:5|type:2|0|Rm:4",""
|
||||
"0x0ff00ff0","0x06500f10","UADD16<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4",""
|
||||
"0x0ff00ff0","0x06500f90","UADD8<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|0|0|1|Rm:4",""
|
||||
"0x0ff00ff0","0x06500f30","UASX<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4",""
|
||||
"0x0fe00070","0x07e00050","UBFX<c> <Rd>,<Rn>,#<lsb>,#<widthm1>","cond:4|0|1|1|1|1|1|1|widthm1:5|Rd:4|lsb:5|1|0|1|Rn:4",""
|
||||
"0x0ff0f0f0","0x0730f010","UDIV<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|1|0|0|1|1|Rd:4|(1)|(1)|(1)|(1)|Rm:4|0|0|0|1|Rn:4",""
|
||||
"0x0ff00ff0","0x06700f10","UHADD16<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4",""
|
||||
"0x0ff00ff0","0x06700f90","UHADD8<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|0|0|1|Rm:4",""
|
||||
"0x0ff00ff0","0x06700f30","UHASX<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4",""
|
||||
"0x0ff00ff0","0x06700f50","UHSAX<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|0|1|Rm:4",""
|
||||
"0x0ff00ff0","0x06700f70","UHSUB16<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|1|1|Rm:4",""
|
||||
"0x0ff00ff0","0x06700ff0","UHSUB8<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|1|1|1|Rm:4",""
|
||||
"0x0ff000f0","0x00400090","UMAAL<c> <RdLo>,<RdHi>,<Rn>,<Rm>","cond:4|0|0|0|0|0|1|0|0|RdHi:4|RdLo:4|Rm:4|1|0|0|1|Rn:4",""
|
||||
"0x0fe000f0","0x00a00090","UMLAL{S}<c> <RdLo>,<RdHi>,<Rn>,<Rm>","cond:4|0|0|0|0|1|0|1|S|RdHi:4|RdLo:4|Rm:4|1|0|0|1|Rn:4",""
|
||||
"0x0fe000f0","0x00800090","UMULL{S}<c> <RdLo>,<RdHi>,<Rn>,<Rm>","cond:4|0|0|0|0|1|0|0|S|RdHi:4|RdLo:4|Rm:4|1|0|0|1|Rn:4",""
|
||||
"0x0ff00ff0","0x06600f10","UQADD16<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4",""
|
||||
"0x0ff00ff0","0x06600f90","UQADD8<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|0|0|1|Rm:4",""
|
||||
"0x0ff00ff0","0x06600f30","UQASX<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4",""
|
||||
"0x0ff00ff0","0x06600f50","UQSAX<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|0|1|Rm:4",""
|
||||
"0x0ff00ff0","0x06600f70","UQSUB16<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|1|1|Rm:4",""
|
||||
"0x0ff00ff0","0x06600ff0","UQSUB8<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|1|1|1|Rm:4",""
|
||||
"0x0ff0f0f0","0x0780f010","USAD8<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|1|1|0|0|0|Rd:4|1|1|1|1|Rm:4|0|0|0|1|Rn:4",""
|
||||
"0x0ff000f0","0x07800010","USADA8<c> <Rd>,<Rn>,<Rm>,<Ra>","cond:4|0|1|1|1|1|0|0|0|Rd:4|Ra:4|Rm:4|0|0|0|1|Rn:4","SEE USAD8"
|
||||
"0x0ff00ff0","0x06e00f30","USAT16<c> <Rd>,#<sat_imm4>,<Rn>","cond:4|0|1|1|0|1|1|1|0|sat_imm:4|Rd:4|(1)|(1)|(1)|(1)|0|0|1|1|Rn:4",""
|
||||
"0x0fe00030","0x06e00010","USAT<c> <Rd>,#<sat_imm5>,<Rn>{,<shift>}","cond:4|0|1|1|0|1|1|1|sat_imm:5|Rd:4|imm5:5|sh|0|1|Rn:4",""
|
||||
"0x0ff00ff0","0x06500f50","USAX<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|0|1|Rm:4",""
|
||||
"0x0ff00ff0","0x06500f70","USUB16<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|1|1|Rm:4",""
|
||||
"0x0ff00ff0","0x06500ff0","USUB8<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|1|1|1|Rm:4",""
|
||||
"0x0ff003f0","0x06c00070","UXTAB16<c> <Rd>,<Rn>,<Rm>{,<rotation>}","cond:4|0|1|1|0|1|1|0|0|Rn:4|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4","SEE UXTB16"
|
||||
"0x0ff003f0","0x06e00070","UXTAB<c> <Rd>,<Rn>,<Rm>{,<rotation>}","cond:4|0|1|1|0|1|1|1|0|Rn:4|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4","SEE UXTB"
|
||||
"0x0ff003f0","0x06f00070","UXTAH<c> <Rd>,<Rn>,<Rm>{,<rotation>}","cond:4|0|1|1|0|1|1|1|1|Rn:4|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4","SEE UXTH"
|
||||
"0x0fff03f0","0x06cf0070","UXTB16<c> <Rd>,<Rm>{,<rotation>}","cond:4|0|1|1|0|1|1|0|0|1|1|1|1|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4",""
|
||||
"0x0fff03f0","0x06ef0070","UXTB<c> <Rd>,<Rm>{,<rotation>}","cond:4|0|1|1|0|1|1|1|0|1|1|1|1|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4",""
|
||||
"0x0fff03f0","0x06ff0070","UXTH<c> <Rd>,<Rm>{,<rotation>}","cond:4|0|1|1|0|1|1|1|1|1|1|1|1|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4",""
|
||||
"0xff800f10","0xf3000110","V<BIF,BIT,BSL> <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|1|0|D|op:2|Vn:4|Vd:4|0|0|0|1|N|Q|M|1|Vm:4","SEE VEOR"
|
||||
"0xffb00c10","0xf3b00800","V<TBL,TBX>.8 <Dd>, <list_len>, <Dm>","1|1|1|1|0|0|1|1|1|D|1|1|Vn:4|Vd:4|1|0|len:2|N|op|M|0|Vm:4",""
|
||||
"0xfe800a50","0xf2800040","V<MLA,MLS>.<dt> <Qd>, <Qn>, <Dm[x]>","1|1|1|1|0|0|1|Q|1|D|size:2|Vn:4|Vd:4|0|op|0|F|N|1|M|0|Vm:4","SEE “Related encodings”"
|
||||
"0xfe800f00","0xf2000600","V<MAX,MIN>.<dt> <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|1|1|0|N|Q|M|op|Vm:4",""
|
||||
"0xfe800f10","0xf2000900","V<MLA,MLS>.<dt> <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|op|0|D|size:2|Vn:4|Vd:4|1|0|0|1|N|Q|M|0|Vm:4",""
|
||||
"0xff800f10","0xf2000d10","V<MLA,MLS>.F32 <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|0|0|D|op|sz|Vn:4|Vd:4|1|1|0|1|N|Q|M|1|Vm:4",""
|
||||
"0xff800f10","0xf2000f00","V<MAX,MIN>.F32 <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|0|0|D|op|sz|Vn:4|Vd:4|1|1|1|1|N|Q|M|0|Vm:4",""
|
||||
"0xff800f10","0xf3000e10","VAC<GE,GT>.F32 <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|1|0|D|op|sz|Vn:4|Vd:4|1|1|1|0|N|Q|M|1|Vm:4",""
|
||||
"0x0fb00e10","0x0e000a00","V<MLA,MLS><c>.F<32,64> <Sd,Dd>, <Sn,Dn>, <Sm,Dm>","cond:4|1|1|1|0|0|D|0|0|Vn:4|Vd:4|1|0|1|sz|N|op|M|0|Vm:4","vfp"
|
||||
"0xfe801d50","0xf2800800","V<MLA,MLS>L.<dt> <Qd>, <Dn>, <Dm>","1|1|1|1|0|0|1|U|1|D|size:2|Vn:4|Vd:4|1|0|op|0|N|0|M|0|Vm:4","SEE “Related encodings”"
|
||||
"0xfe801b50","0xf2800240","V<MLA,MLS>L.<dt> <Qd>, <Dn>, <Dm[x]>","1|1|1|1|0|0|1|U|1|D|size:2|Vn:4|Vd:4|0|op|1|0|N|1|M|0|Vm:4","SEE “Related encodings”"
|
||||
"0xfe800f10","0xf2000710","VABA.<dt_Usize> <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|1|1|1|N|Q|M|1|Vm:4",""
|
||||
"0xfe801f50","0xf2800500","VABAL.<dt_Usize> <Qd>, <Dn>, <Dm>","1|1|1|1|0|0|1|U|1|D|size:2|Vn:4|Vd:4|0|1|0|1|N|0|M|0|Vm:4","SEE “Related encodings”"
|
||||
"0xfe800f10","0xf2000700","VABD.<dt_Usize> <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|1|1|1|N|Q|M|0|Vm:4",""
|
||||
"0xffa00f10","0xf3200d00","VABD.F32 <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|1|0|D|1|sz|Vn:4|Vd:4|1|1|0|1|N|Q|M|0|Vm:4",""
|
||||
"0xfe801f50","0xf2800700","VABDL.<dt_Usize> <Qd>, <Dn>, <Dm>","1|1|1|1|0|0|1|U|1|D|size:2|Vn:4|Vd:4|0|1|1|1|N|0|M|0|Vm:4","SEE “Related encodings”"
|
||||
"0xffb30b90","0xf3b10300","VABS.<dt_Fsize> <Qd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|1|Vd:4|0|F|1|1|0|Q|M|0|Vm:4",""
|
||||
"0x0fbf0ed0","0x0eb00ac0","VABS<c>.F<32,64> <Sd,Dd>, <Sm,Dm>","cond:4|1|1|1|0|1|D|1|1|0|0|0|0|Vd:4|1|0|1|sz|1|1|M|0|Vm:4","vfp"
|
||||
"0xff800f10","0xf2000800","VADD.<dt_Isize> <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|0|0|D|size:2|Vn:4|Vd:4|1|0|0|0|N|Q|M|0|Vm:4",""
|
||||
"0xffa00f10","0xf2000d00","VADD.F32 <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|0|0|D|0|sz|Vn:4|Vd:4|1|1|0|1|N|Q|M|0|Vm:4",""
|
||||
"0x0fb00e50","0x0e300a00","VADD<c>.F<32,64> <Sd,Dd>, <Sn,Dn>, <Sm,Dm>","cond:4|1|1|1|0|0|D|1|1|Vn:4|Vd:4|1|0|1|sz|N|0|M|0|Vm:4","vfp"
|
||||
"0xff810f51","0xf2800400","VADDHN.<dt_Isize> <Dd>, <Qn>, <Qm>","1|1|1|1|0|0|1|0|1|D|size:2|Vn:4|Vd:4|0|1|0|0|N|0|M|0|Vm:4","SEE “Related encodings”"
|
||||
"0xfe801e50","0xf2800000","VADDL.<dt_Usize> <Qd>, <Dn>, <Dm>","1|1|1|1|0|0|1|U|1|D|size:2|Vn:4|Vd:4|0|0|0|op|N|0|M|0|Vm:4","SEE “Related encodings”"
|
||||
"0xffb00f10","0xf2000110","VAND <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|0|0|D|0|0|Vn:4|Vd:4|0|0|0|1|N|Q|M|1|Vm:4",""
|
||||
"0xffb00f10","0xf2100110","VBIC <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|0|0|D|0|1|Vn:4|Vd:4|0|0|0|1|N|Q|M|1|Vm:4",""
|
||||
"0xfeb800b0","0xf2800030","VBIC.<dt_simd> <Qd>, #<imm_simd1>","1|1|1|1|0|0|1|i|1|D|0|0|0|imm3:3|Vd:4|cmode:4|0|Q|1|1|imm4:4","SEE “Related encodings”"
|
||||
"0xffb30b90","0xf3b10100","VCEQ.<dt_Fsize> <Qd>, <Qm>, #0","1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|1|Vd:4|0|F|0|1|0|Q|M|0|Vm:4",""
|
||||
"0xff800f10","0xf3000810","VCEQ.<dt_Isize> <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|1|0|D|size:2|Vn:4|Vd:4|1|0|0|0|N|Q|M|1|Vm:4",""
|
||||
"0xffa00f10","0xf2000e00","VCEQ.F32 <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|0|0|D|0|sz|Vn:4|Vd:4|1|1|1|0|N|Q|M|0|Vm:4",""
|
||||
"0xffb30b90","0xf3b10080","VCGE.<dt_Fsize> <Qd>, <Qm>, #0","1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|1|Vd:4|0|F|0|0|1|Q|M|0|Vm:4",""
|
||||
"0xfe800f10","0xf2000310","VCGE.<dt_Isize> <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|0|1|1|N|Q|M|1|Vm:4",""
|
||||
"0xffa00f10","0xf3000e00","VCGE.F32 <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|1|0|D|0|sz|Vn:4|Vd:4|1|1|1|0|N|Q|M|0|Vm:4",""
|
||||
"0xffb30b90","0xf3b10000","VCGT.<dt_Fsize> <Qd>, <Qm>, #0","1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|1|Vd:4|0|F|0|0|0|Q|M|0|Vm:4",""
|
||||
"0xfe800f10","0xf2000300","VCGT.<dt_Isize> <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|0|1|1|N|Q|M|0|Vm:4",""
|
||||
"0xffa00f10","0xf3200e00","VCGT.F32 <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|1|0|D|1|sz|Vn:4|Vd:4|1|1|1|0|N|Q|M|0|Vm:4",""
|
||||
"0xffb30b90","0xf3b10180","VCLE.<dt_Fsize> <Qd>, <Qm>, #0","1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|1|Vd:4|0|F|0|1|1|Q|M|0|Vm:4",""
|
||||
"0xffb30f90","0xf3b00400","VCLS.<dt_Isize> <Qd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|0|Vd:4|0|1|0|0|0|Q|M|0|Vm:4",""
|
||||
"0xffb30b90","0xf3b10200","VCLT.<dt_Fsize> <Qd>, <Qm>, #0","1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|1|Vd:4|0|F|1|0|0|Q|M|0|Vm:4",""
|
||||
"0xffb30f90","0xf3b00480","VCLZ.<dt_Isize> <Qd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|0|Vd:4|0|1|0|0|1|Q|M|0|Vm:4",""
|
||||
"0x0fbf0e7f","0x0eb50a40","VCMP{E}<c>.F<32,64> <Sd,Dd>, #0.0","cond:4|1|1|1|0|1|D|1|1|0|1|0|1|Vd:4|1|0|1|sz|E|1|0|0|(0)|(0)|(0)|(0)","vfp"
|
||||
"0x0fbf0e50","0x0eb40a40","VCMP{E}<c>.F<32,64> <Sd,Dd>, <Sm,Dm>","cond:4|1|1|1|0|1|D|1|1|0|1|0|0|Vd:4|1|0|1|sz|E|1|M|0|Vm:4","vfp"
|
||||
"0xffbf0f90","0xf3b00500","VCNT.8 <Qd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|0|Vd:4|0|1|0|1|0|Q|M|0|Vm:4",""
|
||||
"0xffbf0e10","0xf3bb0600","VCVT.<Td>.<Tm_1> <Qd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|1|Vd:4|0|1|1|op:2|Q|M|0|Vm:4",""
|
||||
"0xfea00e90","0xf2a00e10","VCVT.<Td>.<Tm_2> <Qd>, <Qm>, #<fbits>","1|1|1|1|0|0|1|U|1|D|imm6:6|Vd:4|1|1|1|op|0|Q|M|1|Vm:4","SEE “Related encodings”"
|
||||
"0x0fbe0e50","0x0eba0a40","VCVT<c>.F<32,64>.FX<S,U><16,32> <Sd,Dd>, <Sd,Dd>, #<fbits>","cond:4|1|1|1|0|1|D|1|1|1|0|1|U|Vd:4|1|0|1|sz|sx|1|i|0|imm4:4","vfp"
|
||||
"0x0fbe0e50","0x0ebe0a40","VCVT<c>.FX<S,U><16,32>.F<32,64> <Sd,Dd>, <Sd,Dd>, #<fbits>","cond:4|1|1|1|0|1|D|1|1|1|1|1|U|Vd:4|1|0|1|sz|sx|1|i|0|imm4:4","vfp"
|
||||
"0xffbf0ed0","0xf3b60600","VCVT.F32.F16 <Qd>, <Dm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|0|Vd:4|0|1|1|op|0|0|M|0|Vm:4",""
|
||||
"0x0fbf0ed0","0x0eb70ac0","VCVT<c>.<F64.F32,F32.F64> <Dd,Sd>, <Sm,Dm>","cond:4|1|1|1|0|1|D|1|1|0|1|1|1|Vd:4|1|0|1|sz|1|1|M|0|Vm:4","vfp"
|
||||
"0x0fbe0f50","0x0eb20a40","VCVT<B,T><c>.<F32.F16,F16.F32> <Sd>, <Sm>","cond:4|1|1|1|0|1|D|1|1|0|0|1|op|Vd:4|1|0|1|0|T|1|M|0|Vm:4","vfp"
|
||||
"0x0fbf0e50","0x0eb80a40","VCVT<c>.F<32,64>.<U,S>32 <Sd,Dd>, <Sm>","cond:4|1|1|1|0|1|D|1|1|1|0|0|0|Vd:4|1|0|1|sz|op|1|M|0|Vm:4","vfp"
|
||||
"0x0fbe0e50","0x0ebc0a40","VCVT<R,><c>.<U,S>32.F<32,64> <Sd>, <Sm,Dm>","cond:4|1|1|1|0|1|D|1|1|1|1|0|signed|Vd:4|1|0|1|sz|op|1|M|0|Vm:4","vfp"
|
||||
"0x0fb00e50","0x0e800a00","VDIV<c>.F<32,64> <Sd,Dd>, <Sn,Dn>, <Sm,Dm>","cond:4|1|1|1|0|1|D|0|0|Vn:4|Vd:4|1|0|1|sz|N|0|M|0|Vm:4","vfp"
|
||||
"0xffb00f90","0xf3b00c00","VDUP.<size_x> <Qd>, <Dm[size_x]>","1|1|1|1|0|0|1|1|1|D|1|1|imm4:4|Vd:4|1|1|0|0|0|Q|M|0|Vm:4",""
|
||||
"0x0f900f5f","0x0e800b10","VDUP<c>.<size_be> <Qd>, <Rt>","cond:4|1|1|1|0|1|b|Q|0|Vd:4|Rt:4|1|0|1|1|D|0|e|1|(0)|(0)|(0)|(0)",""
|
||||
"0xffb00f10","0xf3000110","VEOR <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|1|0|D|0|0|Vn:4|Vd:4|0|0|0|1|N|Q|M|1|Vm:4",""
|
||||
"0xffb00010","0xf2b00000","VEXT.8 <Qd>, <Qn>, <Qm>, #<imm4>","1|1|1|1|0|0|1|0|1|D|1|1|Vn:4|Vd:4|imm4:4|N|Q|M|0|Vm:4",""
|
||||
"0xfe800d10","0xf2000000","VH<ADD,SUB> <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|0|op|0|N|Q|M|0|Vm:4",""
|
||||
"0xffb00200","0xf4200200","VLD1.<size> <list4>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|0|D|1|0|Rn:4|Vd:4|type:4|size:2|align:2|Rm:4","SEE “Related encodings”"
|
||||
"0xffb00f00","0xf4a00c00","VLD1.<size> <list1>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|1|D|1|0|Rn:4|Vd:4|1|1|0|0|size:2|T|a|Rm:4",""
|
||||
"0xffb00300","0xf4a00000","VLD1.<size> <list1>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|1|D|1|0|Rn:4|Vd:4|size:2|0|0|index_align:4|Rm:4","SEE VLD1 (single element to all lanes)"
|
||||
"0xffb00400","0xf4200000","VLD2.<size> <list4>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|0|D|1|0|Rn:4|Vd:4|type:4|size:2|align:2|Rm:4","SEE “Related encodings”"
|
||||
"0xffb00f00","0xf4a00d00","VLD2.<size> <list1>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|1|D|1|0|Rn:4|Vd:4|1|1|0|1|size:2|T|a|Rm:4",""
|
||||
"0xffb00300","0xf4a00100","VLD2.<size> <list1>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|1|D|1|0|Rn:4|Vd:4|size:2|0|1|index_align:4|Rm:4","SEE VLD2 (single 2-element structure to all lanes)"
|
||||
"0xffb00f10","0xf4a00e00","VLD3.<size> <list3t>, [<Rn>]{!}","1|1|1|1|0|1|0|0|1|D|1|0|Rn:4|Vd:4|1|1|1|0|size:2|T|a|Rm:4",""
|
||||
"0xffb00310","0xf4a00200","VLD3.<size> <list3>, [<Rn>]{!}","1|1|1|1|0|1|0|0|1|D|1|0|Rn:4|Vd:4|size:2|1|0|index_align:4|Rm:4","SEE VLD3 (single 3-element structure to all lanes)"
|
||||
"0xffb00e20","0xf4200400","VLD3.<size> <list4>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|0|D|1|0|Rn:4|Vd:4|type:4|size:2|align:2|Rm:4","SEE “Related encodings”"
|
||||
"0xffb00f00","0xf4a00f00","VLD4.<size> <list4>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|1|D|1|0|Rn:4|Vd:4|1|1|1|1|size:2|T|a|Rm:4",""
|
||||
"0xffb00e00","0xf4200000","VLD4.<size> <list1>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|0|D|1|0|Rn:4|Vd:4|type:4|size:2|align:2|Rm:4","SEE “Related encodings”"
|
||||
"0xffb00300","0xf4a00300","VLD4.<size> <list1>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|1|D|1|0|Rn:4|Vd:4|size:2|1|1|index_align:4|Rm:4","SEE VLD4 (single 4-element structure to all lanes)"
|
||||
"0x0f300e00","0x0d100a00","VLDR<c> <Sd,Dd>, [<Rn>{,#+/-<imm8>}]","cond:4|1|1|0|1|U|D|0|1|Rn:4|Vd:4|1|0|1|sz|imm8:8","vfp"
|
||||
"0x0fe00fd0","0x0c400b10","VMOV<c> <Dm>, <Rt>, <Rt2>","cond:4|1|1|0|0|0|1|0|op|Rt2:4|Rt:4|1|0|1|1|0|0|M|1|Vm:4",""
|
||||
"0xffb00f10","0xf2200110","VMOV <Qd>, <Qm>","1|1|1|1|0|0|1|0|0|D|1|0|Vm:4|Vd:4|0|0|0|1|M|Q|M|1|Vm:4","SEE VORR (register)"
|
||||
"0x0fe00fd0","0x0c400a10","VMOV<c> <Sm>, <Sm1>, <Rt>, <Rt2>","cond:4|1|1|0|0|0|1|0|op|Rt2:4|Rt:4|1|0|1|0|0|0|M|1|Vm:4",""
|
||||
"0x0ff00f7f","0x0e000a10","VMOV<c> <Sn>, <Rt>","cond:4|1|1|1|0|0|0|0|0|Vn:4|Rt:4|1|0|1|0|N|0|0|1|0|0|0|0","vfp"
|
||||
"0x0ff00f7f","0x0e100a10","VMOV<c> <Rt>, <Sn>","cond:4|1|1|1|0|0|0|0|1|Vn:4|Rt:4|1|0|1|0|N|0|0|1|0|0|0|0","vfp"
|
||||
"0xfeb80090","0xf2800010","VMOV.<dt_simd> <Qd>, #<imm_simd>","1|1|1|1|0|0|1|i|1|D|0|0|0|imm3:3|Vd:4|cmode:4|0|Q|op|1|imm4:4","SEE VORR (immediate) SEE “Related encodings”"
|
||||
"0x0fd00f7f","0x0e100b10","VMOV<c>.32 <Rt>, <Dn[x]>","cond:4|1|1|1|0|0|0|opc1|1|Vn:4|Rt:4|1|0|1|1|N|0|0|1|0|0|0|0","vfp"
|
||||
"0x0fd00f7f","0x0e000b10","VMOV<c>.32 <Dd[x]>, <Rt>","cond:4|1|1|1|0|0|0|opc1|0|Vd:4|Rt:4|1|0|1|1|D|0|0|1|0|0|0|0","vfp"
|
||||
"0x0fb00ef0","0x0eb00a00","VMOV<c>.F<32,64> <Sd,Dd>, #<imm_vfp>","cond:4|1|1|1|0|1|D|1|1|imm4H:4|Vd:4|1|0|1|sz|0|0|0|0|imm4L:4","vfp"
|
||||
"0x0fbf0ed0","0x0eb00a40","VMOV<c>.F<32,64> <Sd,Dd>, <Sm,Dm>","cond:4|1|1|1|0|1|D|1|1|0|0|0|0|Vd:4|1|0|1|sz|0|1|M|0|Vm:4","vfp"
|
||||
"0xfe871fd0","0xf2800a10","VMOVL.<dt> <Qd>, <Dm>","1|1|1|1|0|0|1|U|1|D|imm3:3|0|0|0|Vd:4|1|0|1|0|0|0|M|1|Vm:4","SEE “Related encodings” SEE VSHLL"
|
||||
"0xffb30fd1","0xf3b20200","VMOVN.<dt> <Dd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|0|Vd:4|0|0|1|0|0|0|M|0|Vm:4",""
|
||||
"0x0fff0fff","0x0ef10a10","VMRS<c> <Rt_nzcv>, FPSCR","cond:4|1|1|1|0|1|1|1|1|0|0|0|1|Rt:4|1|0|1|0|0|0|0|1|0|0|0|0","vfp"
|
||||
"0x0fff0fff","0x0ee10a10","VMSR<c> FPSCR, <Rt>","cond:4|1|1|1|0|1|1|1|0|0|0|0|1|Rt:4|1|0|1|0|0|0|0|1|0|0|0|0","vfp"
|
||||
"0xfe800e50","0xf2800840","VMUL.<dt> <Qd>, <Qn>, <Dm[x]>","1|1|1|1|0|0|1|Q|1|D|size:2|Vn:4|Vd:4|1|0|0|F|N|1|M|0|Vm:4","SEE “Related encodings”"
|
||||
"0xfe800f10","0xf2000910","VMUL.<dt> <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|op|0|D|size:2|Vn:4|Vd:4|1|0|0|1|N|Q|M|1|Vm:4",""
|
||||
"0xffa00f10","0xf3000d10","VMUL.F32 <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|1|0|D|0|sz|Vn:4|Vd:4|1|1|0|1|N|Q|M|1|Vm:4",""
|
||||
"0x0fb00e50","0x0e200a00","VMUL<c>.F<32,64> <Sd,Dd>, <Sn,Dn>, <Sm,Dm>","cond:4|1|1|1|0|0|D|1|0|Vn:4|Vd:4|1|0|1|sz|N|0|M|0|Vm:4","vfp"
|
||||
"0xfe801d50","0xf2800c00","VMULL.<dt> <Qd>, <Dn>, <Dm>","1|1|1|1|0|0|1|U|1|D|size:2|Vn:4|Vd:4|1|1|op|0|N|0|M|0|Vm:4","SEE “Related encodings”"
|
||||
"0xfe801f50","0xf2800a40","VMULL.<dt> <Qd>, <Dn>, <Dm[x]>","1|1|1|1|0|0|1|U|1|D|size:2|Vn:4|Vd:4|1|0|1|0|N|1|M|0|Vm:4","SEE “Related encodings”"
|
||||
"0xffbf0f90","0xf3b00580","VMVN <Qd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|0|Vd:4|0|1|0|1|1|Q|M|0|Vm:4",""
|
||||
"0xfeb800b0","0xf2800030","VMVN.<dt> <Qd>, #<imm3>","1|1|1|1|0|0|1|i|1|D|0|0|0|imm3:3|Vd:4|cmode:4|0|Q|1|1|imm4:4","SEE “Related encodings”"
|
||||
"0xffb30b90","0xf3b10380","VNEG.<dt> <Qd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|1|Vd:4|0|F|1|1|1|Q|M|0|Vm:4",""
|
||||
"0x0fbf0ed0","0x0eb10a40","VNEG<c>.F<32,64> <Sd,Dd>, <Sm,Dm>","cond:4|1|1|1|0|1|D|1|1|0|0|0|1|Vd:4|1|0|1|sz|0|1|M|0|Vm:4","vfp"
|
||||
"0x0fb00e10","0x0e100a00","VN<MLS,MLA><c>.F<32,64> <Sd,Dd>, <Sn,Dn>, <Sm,Dm>","cond:4|1|1|1|0|0|D|0|1|Vn:4|Vd:4|1|0|1|sz|N|op|M|0|Vm:4","vfp"
|
||||
"0x0fb00e50","0x0e200a40","VNMUL<c>.F<32,64> <Sd,Dd>, <Sn,Dn>, <Sm,Dm>","cond:4|1|1|1|0|0|D|1|0|Vn:4|Vd:4|1|0|1|sz|N|1|M|0|Vm:4","vfp"
|
||||
"0xffb00f10","0xf2300110","VORN <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|0|0|D|1|1|Vn:4|Vd:4|0|0|0|1|N|Q|M|1|Vm:4",""
|
||||
"0xffb00f10","0xf2200110","VORR <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|0|0|D|1|0|Vn:4|Vd:4|0|0|0|1|N|Q|M|1|Vm:4","SEE VMOV (register)"
|
||||
"0xfeb800b0","0xf2800010","VORR.<dt> <Qd>, #<imm3>","1|1|1|1|0|0|1|i|1|D|0|0|0|imm3:3|Vd:4|cmode:4|0|Q|0|1|imm4:4","SEE VMOV (immediate)"
|
||||
"0xfe800f00","0xf2000a00","VP<MIN,MAX>.<dt> <Dd>, <Dn>, <Dm>","1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|1|0|1|0|N|Q|M|op|Vm:4",""
|
||||
"0xff800f10","0xf3000f00","VP<MIN,MAX>.F32 <Dd>, <Dn>, <Dm>","1|1|1|1|0|0|1|1|0|D|op|sz|Vn:4|Vd:4|1|1|1|1|N|Q|M|0|Vm:4",""
|
||||
"0xffb30f10","0xf3b00600","VPADAL.<dt> <Qd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|0|Vd:4|0|1|1|0|op|Q|M|0|Vm:4",""
|
||||
"0xff800f10","0xf2000b10","VPADD.<dt> <Dd>, <Dn>, <Dm>","1|1|1|1|0|0|1|0|0|D|size:2|Vn:4|Vd:4|1|0|1|1|N|Q|M|1|Vm:4",""
|
||||
"0xffa00f10","0xf3000d00","VPADD.F32 <Dd>, <Dn>, <Dm>","1|1|1|1|0|0|1|1|0|D|0|sz|Vn:4|Vd:4|1|1|0|1|N|Q|M|0|Vm:4",""
|
||||
"0xffb30f10","0xf3b00200","VPADDL.<dt> <Qd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|0|Vd:4|0|0|1|0|op|Q|M|0|Vm:4",""
|
||||
"0x0fbf0f00","0x0cbd0a00","VPOP <vlist32>","cond:4|1|1|0|0|1|D|1|1|1|1|0|1|Vd:4|1|0|1|0|imm8:8",""
|
||||
"0x0fbf0f00","0x0cbd0b00","VPOP <vlist64>","cond:4|1|1|0|0|1|D|1|1|1|1|0|1|Vd:4|1|0|1|1|imm8:8",""
|
||||
"0x0fbf0f00","0x0d2d0a00","VPUSH<c> <vlist32>","cond:4|1|1|0|1|0|D|1|0|1|1|0|1|Vd:4|1|0|1|0|imm8:8",""
|
||||
"0x0fbf0f00","0x0d2d0b00","VPUSH<c> <vlist64>","cond:4|1|1|0|1|0|D|1|0|1|1|0|1|Vd:4|1|0|1|1|imm8:8",""
|
||||
"0xffb30f90","0xf3b00700","VQABS.<dt> <Qd>,<Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|0|Vd:4|0|1|1|1|0|Q|M|0|Vm:4",""
|
||||
"0xfe800f10","0xf2000010","VQADD.<dt> <Qd>,<Qn>,<Qm>","1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|0|0|0|N|Q|M|1|Vm:4",""
|
||||
"0xff801d50","0xf2800900","VQD<MLAL,MLSL>.<dt> <Qd>,<Dn>,<Dm>","1|1|1|1|0|0|1|0|1|D|size:2|Vn:4|Vd:4|1|0|op|1|N|0|M|0|Vm:4","SEE “Related encodings”"
|
||||
"0xff801b50","0xf2800340","VQD<MLAL,MLSL>.<dt> <Qd>,<Dn>,<Dm[x]>","1|1|1|1|0|0|1|0|1|D|size:2|Vn:4|Vd:4|0|op|1|1|N|1|M|0|Vm:4","SEE “Related encodings”"
|
||||
"0xfe800f50","0xf2800c40","VQDMULH.<dt> <Qd>,<Qn>,<Dm[x]>","1|1|1|1|0|0|1|Q|1|D|size:2|Vn:4|Vd:4|1|1|0|0|N|1|M|0|Vm:4",""
|
||||
"0xff800f10","0xf2000b00","VQDMULH.<dt> <Qd>,<Qn>,<Qm>","1|1|1|1|0|0|1|0|0|D|size:2|Vn:4|Vd:4|1|0|1|1|N|Q|M|0|Vm:4",""
|
||||
"0xff801f50","0xf2800d00","VQDMULL.<dt> <Qd>,<Dn>,<Dm>","1|1|1|1|0|0|1|0|1|D|size:2|Vn:4|Vd:4|1|1|0|1|N|0|M|0|Vm:4","SEE “Related encodings”"
|
||||
"0xff801f50","0xf2800b40","VQDMULL.<dt> <Qd>,<Dn>,<Dm[x]>","1|1|1|1|0|0|1|0|1|D|size:2|Vn:4|Vd:4|1|0|1|1|N|1|M|0|Vm:4","SEE “Related encodings”"
|
||||
"0xffb30f11","0xf3b20200","VQMOV{U}N.<type><size> <Dd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|0|Vd:4|0|0|1|0|op:2|M|0|Vm:4","SEE VMOVN"
|
||||
"0xffb30f90","0xf3b00780","VQNEG.<dt> <Qd>,<Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|0|Vd:4|0|1|1|1|1|Q|M|0|Vm:4",""
|
||||
"0xfe800f50","0xf2800d40","VQRDMULH.<dt> <Qd>,<Qn>,<Dm[x]>","1|1|1|1|0|0|1|Q|1|D|size:2|Vn:4|Vd:4|1|1|0|1|N|1|M|0|Vm:4",""
|
||||
"0xff800f10","0xf3000b00","VQRDMULH.<dt> <Qd>,<Qn>,<Qm>","1|1|1|1|0|0|1|1|0|D|size:2|Vn:4|Vd:4|1|0|1|1|N|Q|M|0|Vm:4",""
|
||||
"0xfe800f10","0xf2000510","VQRSHL.<type><size> <Qd>,<Qm>,<Qn>","1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|1|0|1|N|Q|M|1|Vm:4",""
|
||||
"0xfe800ed1","0xf2800850","VQRSHR{U}N.<type><size> <Dd>,<Qm>,#<imm6>","1|1|1|1|0|0|1|U|1|D|imm6:6|Vd:4|1|0|0|op|0|1|M|1|Vm:4","SEE “Related encodings” SEE VRSHRN"
|
||||
"0xfe800f10","0xf2000410","VQSHL.<type><size> <Qd>,<Qm>,<Qn>","1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|1|0|0|N|Q|M|1|Vm:4",""
|
||||
"0xfe800e90","0xf2800690","VQSHL{U}.<type><size> <Qd>,<Qm>,#<imm6>","1|1|1|1|0|0|1|U|1|D|imm6:6|Vd:4|0|1|1|op|L|Q|M|1|Vm:4","SEE “Related encodings”"
|
||||
"0xfe800ed1","0xf2800810","VQSHR{U}N.<type><size> <Dd>,<Qm>,#<imm6>","1|1|1|1|0|0|1|U|1|D|imm6:6|Vd:4|1|0|0|op|0|0|M|1|Vm:4","SEE “Related encodings” SEE VSHRN"
|
||||
"0xfe800f10","0xf2000210","VQSUB.<type><size> <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|0|1|0|N|Q|M|1|Vm:4",""
|
||||
"0xff810f51","0xf3800400","VRADDHN.<dt> <Dd>, <Qn>, <Qm>","1|1|1|1|0|0|1|1|1|D|size:2|Vn:4|Vd:4|0|1|0|0|N|0|M|0|Vm:4","SEE “Related encodings”"
|
||||
"0xffbf0e90","0xf3bb0400","VRECPE.<dt> <Qd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|1|Vd:4|0|1|0|F|0|Q|M|0|Vm:4",""
|
||||
"0xffa00f10","0xf2000f10","VRECPS.F32 <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|0|0|D|0|sz|Vn:4|Vd:4|1|1|1|1|N|Q|M|1|Vm:4",""
|
||||
"0xffb30e10","0xf3b00000","VREV<n>.<size> <Qd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|0|Vd:4|0|0|0|op:2|Q|M|0|Vm:4",""
|
||||
"0xfe800f10","0xf2000100","VRHADD <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|0|0|1|N|Q|M|0|Vm:4",""
|
||||
"0xfe800f10","0xf2000500","VRSHL.<type><size> <Qd>, <Qm>, <Qn>","1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|1|0|1|N|Q|M|0|Vm:4",""
|
||||
"0xfe800f90","0xf2800290","VRSHR.<type><size> <Qd>, <Qm>, #<imm6>","1|1|1|1|0|0|1|U|1|D|imm6:6|Vd:4|0|0|1|0|L|Q|M|1|Vm:4","SEE “Related encodings”"
|
||||
"0xff800fd1","0xf2800850","VRSHRN.I<size> <Dd>, <Qm>, #<imm6>","1|1|1|1|0|0|1|0|1|D|imm6:6|Vd:4|1|0|0|0|0|1|M|1|Vm:4","SEE “Related encodings”"
|
||||
"0xffbf0e90","0xf3bb0480","VRSQRTE.<dt> <Qd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|1|Vd:4|0|1|0|F|1|Q|M|0|Vm:4",""
|
||||
"0xffa00f10","0xf2200f10","VRSQRTS.F32 <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|0|0|D|1|sz|Vn:4|Vd:4|1|1|1|1|N|Q|M|1|Vm:4",""
|
||||
"0xfe800f90","0xf2800390","VRSRA.<type><size> <Qd>, <Qm>, #<imm6>","1|1|1|1|0|0|1|U|1|D|imm6:6|Vd:4|0|0|1|1|L|Q|M|1|Vm:4","SEE “Related encodings”"
|
||||
"0xff810f51","0xf3800600","VRSUBHN.<dt> <Dd>, <Qn>, <Qm>","1|1|1|1|0|0|1|1|1|D|size:2|Vn:4|Vd:4|0|1|1|0|N|0|M|0|Vm:4","SEE “Related encodings”"
|
||||
"0xff800f90","0xf2800590","VSHL.I<size> <Qd>, <Qm>, #<imm6>","1|1|1|1|0|0|1|0|1|D|imm6:6|Vd:4|0|1|0|1|L|Q|M|1|Vm:4","SEE “Related encodings”"
|
||||
"0xfe800f10","0xf2000400","VSHL.I<size> <Qd>, <Qm>, <Qn>","1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|1|0|0|N|Q|M|0|Vm:4",""
|
||||
"0xffb31fd0","0xf3b20300","VSHLL.<type><size> <Qd>, <Dm>, #<immsize>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|0|Vd:4|0|0|1|1|0|0|M|0|Vm:4",""
|
||||
"0xfe801fd0","0xf2800a10","VSHLL.<type><size> <Qd>, <Dm>, #<imm6>","1|1|1|1|0|0|1|U|1|D|imm6:6|Vd:4|1|0|1|0|0|0|M|1|Vm:4","SEE “Related encodings” SEE VMOVL"
|
||||
"0xfe800f90","0xf2800090","VSHR.<type><size_vs> <Qd>, <Qm>, #<imm_vs>","1|1|1|1|0|0|1|U|1|D|imm6:6|Vd:4|0|0|0|0|L|Q|M|1|Vm:4","SEE “Related encodings”"
|
||||
"0xff800fd1","0xf2800810","VSHRN.I<size_vsn> <Dd>, <Qm>, #<imm_vsn>","1|1|1|1|0|0|1|0|1|D|imm6:6|Vd:4|1|0|0|0|0|0|M|1|Vm:4","SEE “Related encodings”"
|
||||
"0xff800f90","0xf3800590","VSLI.<size_vs> <Qd>, <Qm>, #<imm_vs>","1|1|1|1|0|0|1|1|1|D|imm6:6|Vd:4|0|1|0|1|L|Q|M|1|Vm:4","SEE “Related encodings”"
|
||||
"0x0fbf0ed0","0x0eb10ac0","VSQRT<c>.F<32,64> <Sd,Dd>, <Sm,Dm>","cond:4|1|1|1|0|1|D|1|1|0|0|0|1|Vd:4|1|0|1|sz|1|1|M|0|Vm:4","vfp"
|
||||
"0xfe800f90","0xf2800190","VSRA.<type_U><size_vs> <Qd>, <Qm>, #<imm_vs>","1|1|1|1|0|0|1|U|1|D|imm6:6|Vd:4|0|0|0|1|L|Q|M|1|Vm:4","SEE “Related encodings”"
|
||||
"0xff800f90","0xf3800490","VSRI.<size_vs> <Qd>, <Qm>, #<imm_vs>","1|1|1|1|0|0|1|1|1|D|imm6:6|Vd:4|0|1|0|0|L|Q|M|1|Vm:4","SEE “Related encodings”"
|
||||
"0xffb00200","0xf4000200","VST1.<size> <list4>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|0|D|0|0|Rn:4|Vd:4|type:4|size:2|align:2|Rm:4","SEE “Related encodings”"
|
||||
"0xffb00300","0xf4800000","VST1.<size> <list1>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|1|D|0|0|Rn:4|Vd:4|size:2|0|0|index_align:4|Rm:4",""
|
||||
"0xffb00400","0xf4000000","VST2.<size> <list4>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|0|D|0|0|Rn:4|Vd:4|type:4|size:2|align:2|Rm:4","SEE “Related encodings”"
|
||||
"0xffb00300","0xf4800100","VST2.<size> <list1>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|1|D|0|0|Rn:4|Vd:4|size:2|0|1|index_align:4|Rm:4",""
|
||||
"0xffb00300","0xf4800200","VST3.<size> <list4>, [<Rn>]{!}","1|1|1|1|0|1|0|0|1|D|0|0|Rn:4|Vd:4|size:2|1|0|index_align:4|Rm:4",""
|
||||
"0xffb00e20","0xf4000400","VST3.<size> <list1>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|0|D|0|0|Rn:4|Vd:4|type:4|size:2|align:2|Rm:4","SEE “Related encodings”"
|
||||
"0xffb00e00","0xf4000000","VST4.<size> <list4>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|0|D|0|0|Rn:4|Vd:4|type:4|size:2|align:2|Rm:4","SEE “Related encodings”"
|
||||
"0xffb00300","0xf4800300","VST4.<size> <list1>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|1|D|0|0|Rn:4|Vd:4|size:2|1|1|index_align:4|Rm:4",""
|
||||
"0x0f300e00","0x0d000a00","VSTR<c> <Sd,Dd>, [<Rn>{,#+/-<imm8>}]","cond:4|1|1|0|1|U|D|0|0|Rn:4|Vd:4|1|0|1|sz|imm8:8","vfp"
|
||||
"0xff800f10","0xf3000800","VSUB.<dt_Isize> <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|1|0|D|size:2|Vn:4|Vd:4|1|0|0|0|N|Q|M|0|Vm:4",""
|
||||
"0xffa00f10","0xf2200d00","VSUB.F32 <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|0|0|D|1|sz|Vn:4|Vd:4|1|1|0|1|N|Q|M|0|Vm:4",""
|
||||
"0x0fb00e50","0x0e300a40","VSUB<c>.F<32,64> <Sd,Dd>, <Sn,Dn>, <Sm,Dm>","cond:4|1|1|1|0|0|D|1|1|Vn:4|Vd:4|1|0|1|sz|N|1|M|0|Vm:4","vfp"
|
||||
"0xff810f51","0xf2800600","VSUBHN.<dt_Isize> <Dd>, <Qn>, <Qm>","1|1|1|1|0|0|1|0|1|D|size:2|Vn:4|Vd:4|0|1|1|0|N|0|M|0|Vm:4","SEE “Related encodings”"
|
||||
"0xfe801e50","0xf2800200","VSUBL.<dt_Usize> <Qd>, <Dn>, <Dm>","1|1|1|1|0|0|1|U|1|D|size:2|Vn:4|Vd:4|0|0|1|op|N|0|M|0|Vm:4","SEE “Related encodings”"
|
||||
"0xffbf0f90","0xf3b20000","VSWP <Qd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|0|Vd:4|0|0|0|0|0|Q|M|0|Vm:4",""
|
||||
"0xffb30f90","0xf3b20080","VTRN.<size_n> <Qd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|0|Vd:4|0|0|0|0|1|Q|M|0|Vm:4",""
|
||||
"0xff800f10","0xf2000810","VTST.<size_n> <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|0|0|D|size:2|Vn:4|Vd:4|1|0|0|0|N|Q|M|1|Vm:4",""
|
||||
"0xffb30f90","0xf3b20100","VUZP.<size_n> <Qd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|0|Vd:4|0|0|0|1|0|Q|M|0|Vm:4",""
|
||||
"0xffb30f90","0xf3b20180","VZIP.<size_n> <Qd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|0|Vd:4|0|0|0|1|1|Q|M|0|Vm:4",""
|
||||
"0x0fffffff","0x0320f002","WFE<c>","cond:4|0|0|1|1|0|0|1|0|0|0|0|0|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|0|0|0|0|0|1|0",""
|
||||
"0x0fffffff","0x0320f003","WFI<c>","cond:4|0|0|1|1|0|0|1|0|0|0|0|0|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|0|0|0|0|0|1|1",""
|
||||
"0x0fffffff","0x0320f001","YIELD<c>","cond:4|0|0|1|1|0|0|1|0|0|0|0|0|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|0|0|0|0|0|0|1",""
|
||||
"0xffffffff","0xf7fabcfd","UNDEF","1|1|1|1|0|1|1|1|1|1|1|1|1|0|1|0|1|0|1|1|1|1|0|0|1|1|1|1|1|1|0|1",""
|
||||
|
@@ -0,0 +1,2 @@
|
||||
tables.go: ../armmap/map.go ../arm.csv
|
||||
go run ../armmap/map.go -fmt=decoder ../arm.csv >_tables.go && gofmt _tables.go >tables.go && rm _tables.go
|
||||
@@ -0,0 +1,569 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package armasm
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// An instFormat describes the format of an instruction encoding.
|
||||
// An instruction with 32-bit value x matches the format if x&mask == value
|
||||
// and the condition matches.
|
||||
// The condition matches if x>>28 == 0xF && value>>28==0xF
|
||||
// or if x>>28 != 0xF and value>>28 == 0.
|
||||
// If x matches the format, then the rest of the fields describe how to interpret x.
|
||||
// The opBits describe bits that should be extracted from x and added to the opcode.
|
||||
// For example opBits = 0x1234 means that the value
|
||||
//
|
||||
// (2 bits at offset 1) followed by (4 bits at offset 3)
|
||||
//
|
||||
// should be added to op.
|
||||
// Finally the args describe how to decode the instruction arguments.
|
||||
// args is stored as a fixed-size array; if there are fewer than len(args) arguments,
|
||||
// args[i] == 0 marks the end of the argument list.
|
||||
type instFormat struct {
|
||||
mask uint32
|
||||
value uint32
|
||||
priority int8
|
||||
op Op
|
||||
opBits uint64
|
||||
args instArgs
|
||||
}
|
||||
|
||||
type instArgs [4]instArg
|
||||
|
||||
var (
|
||||
errMode = fmt.Errorf("unsupported execution mode")
|
||||
errShort = fmt.Errorf("truncated instruction")
|
||||
errUnknown = fmt.Errorf("unknown instruction")
|
||||
)
|
||||
|
||||
var decoderCover []bool
|
||||
|
||||
// Decode decodes the leading bytes in src as a single instruction.
|
||||
func Decode(src []byte, mode Mode) (inst Inst, err error) {
|
||||
if mode != ModeARM {
|
||||
return Inst{}, errMode
|
||||
}
|
||||
if len(src) < 4 {
|
||||
return Inst{}, errShort
|
||||
}
|
||||
|
||||
if decoderCover == nil {
|
||||
decoderCover = make([]bool, len(instFormats))
|
||||
}
|
||||
|
||||
x := binary.LittleEndian.Uint32(src)
|
||||
|
||||
// The instFormat table contains both conditional and unconditional instructions.
|
||||
// Considering only the top 4 bits, the conditional instructions use mask=0, value=0,
|
||||
// while the unconditional instructions use mask=f, value=f.
|
||||
// Prepare a version of x with the condition cleared to 0 in conditional instructions
|
||||
// and then assume mask=f during matching.
|
||||
const condMask = 0xf0000000
|
||||
xNoCond := x
|
||||
if x&condMask != condMask {
|
||||
xNoCond &^= condMask
|
||||
}
|
||||
var priority int8
|
||||
Search:
|
||||
for i := range instFormats {
|
||||
f := &instFormats[i]
|
||||
if xNoCond&(f.mask|condMask) != f.value || f.priority <= priority {
|
||||
continue
|
||||
}
|
||||
delta := uint32(0)
|
||||
deltaShift := uint(0)
|
||||
for opBits := f.opBits; opBits != 0; opBits >>= 16 {
|
||||
n := uint(opBits & 0xFF)
|
||||
off := uint((opBits >> 8) & 0xFF)
|
||||
delta |= (x >> off) & (1<<n - 1) << deltaShift
|
||||
deltaShift += n
|
||||
}
|
||||
op := f.op + Op(delta)
|
||||
|
||||
// Special case: BKPT encodes with condition but cannot have one.
|
||||
if op&^15 == BKPT_EQ && op != BKPT {
|
||||
continue Search
|
||||
}
|
||||
|
||||
var args Args
|
||||
for j, aop := range f.args {
|
||||
if aop == 0 {
|
||||
break
|
||||
}
|
||||
arg := decodeArg(aop, x)
|
||||
if arg == nil { // cannot decode argument
|
||||
continue Search
|
||||
}
|
||||
args[j] = arg
|
||||
}
|
||||
|
||||
decoderCover[i] = true
|
||||
|
||||
inst = Inst{
|
||||
Op: op,
|
||||
Args: args,
|
||||
Enc: x,
|
||||
Len: 4,
|
||||
}
|
||||
priority = f.priority
|
||||
continue Search
|
||||
}
|
||||
if inst.Op != 0 {
|
||||
return inst, nil
|
||||
}
|
||||
return Inst{}, errUnknown
|
||||
}
|
||||
|
||||
// An instArg describes the encoding of a single argument.
|
||||
// In the names used for arguments, _p_ means +, _m_ means -,
|
||||
// _pm_ means ± (usually keyed by the U bit).
|
||||
// The _W suffix indicates a general addressing mode based on the P and W bits.
|
||||
// The _offset and _postindex suffixes force the given addressing mode.
|
||||
// The rest should be somewhat self-explanatory, at least given
|
||||
// the decodeArg function.
|
||||
type instArg uint8
|
||||
|
||||
const (
|
||||
_ instArg = iota
|
||||
arg_APSR
|
||||
arg_FPSCR
|
||||
arg_Dn_half
|
||||
arg_R1_0
|
||||
arg_R1_12
|
||||
arg_R2_0
|
||||
arg_R2_12
|
||||
arg_R_0
|
||||
arg_R_12
|
||||
arg_R_12_nzcv
|
||||
arg_R_16
|
||||
arg_R_16_WB
|
||||
arg_R_8
|
||||
arg_R_rotate
|
||||
arg_R_shift_R
|
||||
arg_R_shift_imm
|
||||
arg_SP
|
||||
arg_Sd
|
||||
arg_Sd_Dd
|
||||
arg_Dd_Sd
|
||||
arg_Sm
|
||||
arg_Sm_Dm
|
||||
arg_Sn
|
||||
arg_Sn_Dn
|
||||
arg_const
|
||||
arg_endian
|
||||
arg_fbits
|
||||
arg_fp_0
|
||||
arg_imm24
|
||||
arg_imm5
|
||||
arg_imm5_32
|
||||
arg_imm5_nz
|
||||
arg_imm_12at8_4at0
|
||||
arg_imm_4at16_12at0
|
||||
arg_imm_vfp
|
||||
arg_label24
|
||||
arg_label24H
|
||||
arg_label_m_12
|
||||
arg_label_p_12
|
||||
arg_label_pm_12
|
||||
arg_label_pm_4_4
|
||||
arg_lsb_width
|
||||
arg_mem_R
|
||||
arg_mem_R_pm_R_W
|
||||
arg_mem_R_pm_R_postindex
|
||||
arg_mem_R_pm_R_shift_imm_W
|
||||
arg_mem_R_pm_R_shift_imm_offset
|
||||
arg_mem_R_pm_R_shift_imm_postindex
|
||||
arg_mem_R_pm_imm12_W
|
||||
arg_mem_R_pm_imm12_offset
|
||||
arg_mem_R_pm_imm12_postindex
|
||||
arg_mem_R_pm_imm8_W
|
||||
arg_mem_R_pm_imm8_postindex
|
||||
arg_mem_R_pm_imm8at0_offset
|
||||
arg_option
|
||||
arg_registers
|
||||
arg_registers1
|
||||
arg_registers2
|
||||
arg_satimm4
|
||||
arg_satimm5
|
||||
arg_satimm4m1
|
||||
arg_satimm5m1
|
||||
arg_widthm1
|
||||
)
|
||||
|
||||
// decodeArg decodes the arg described by aop from the instruction bits x.
|
||||
// It returns nil if x cannot be decoded according to aop.
|
||||
func decodeArg(aop instArg, x uint32) Arg {
|
||||
switch aop {
|
||||
default:
|
||||
return nil
|
||||
|
||||
case arg_APSR:
|
||||
return APSR
|
||||
case arg_FPSCR:
|
||||
return FPSCR
|
||||
|
||||
case arg_R_0:
|
||||
return Reg(x & (1<<4 - 1))
|
||||
case arg_R_8:
|
||||
return Reg((x >> 8) & (1<<4 - 1))
|
||||
case arg_R_12:
|
||||
return Reg((x >> 12) & (1<<4 - 1))
|
||||
case arg_R_16:
|
||||
return Reg((x >> 16) & (1<<4 - 1))
|
||||
|
||||
case arg_R_12_nzcv:
|
||||
r := Reg((x >> 12) & (1<<4 - 1))
|
||||
if r == R15 {
|
||||
return APSR_nzcv
|
||||
}
|
||||
return r
|
||||
|
||||
case arg_R_16_WB:
|
||||
mode := AddrLDM
|
||||
if (x>>21)&1 != 0 {
|
||||
mode = AddrLDM_WB
|
||||
}
|
||||
return Mem{Base: Reg((x >> 16) & (1<<4 - 1)), Mode: mode}
|
||||
|
||||
case arg_R_rotate:
|
||||
Rm := Reg(x & (1<<4 - 1))
|
||||
typ, count := decodeShift(x)
|
||||
// ROR #0 here means ROR #0, but decodeShift rewrites to RRX #1.
|
||||
if typ == RotateRightExt {
|
||||
return Rm
|
||||
}
|
||||
return RegShift{Rm, typ, count}
|
||||
|
||||
case arg_R_shift_R:
|
||||
Rm := Reg(x & (1<<4 - 1))
|
||||
Rs := Reg((x >> 8) & (1<<4 - 1))
|
||||
typ := Shift((x >> 5) & (1<<2 - 1))
|
||||
return RegShiftReg{Rm, typ, Rs}
|
||||
|
||||
case arg_R_shift_imm:
|
||||
Rm := Reg(x & (1<<4 - 1))
|
||||
typ, count := decodeShift(x)
|
||||
if typ == ShiftLeft && count == 0 {
|
||||
return Reg(Rm)
|
||||
}
|
||||
return RegShift{Rm, typ, count}
|
||||
|
||||
case arg_R1_0:
|
||||
return Reg((x & (1<<4 - 1)))
|
||||
case arg_R1_12:
|
||||
return Reg(((x >> 12) & (1<<4 - 1)))
|
||||
case arg_R2_0:
|
||||
return Reg((x & (1<<4 - 1)) | 1)
|
||||
case arg_R2_12:
|
||||
return Reg(((x >> 12) & (1<<4 - 1)) | 1)
|
||||
|
||||
case arg_SP:
|
||||
return SP
|
||||
|
||||
case arg_Sd_Dd:
|
||||
v := (x >> 12) & (1<<4 - 1)
|
||||
vx := (x >> 22) & 1
|
||||
sz := (x >> 8) & 1
|
||||
if sz != 0 {
|
||||
return D0 + Reg(vx<<4+v)
|
||||
} else {
|
||||
return S0 + Reg(v<<1+vx)
|
||||
}
|
||||
|
||||
case arg_Dd_Sd:
|
||||
return decodeArg(arg_Sd_Dd, x^(1<<8))
|
||||
|
||||
case arg_Sd:
|
||||
v := (x >> 12) & (1<<4 - 1)
|
||||
vx := (x >> 22) & 1
|
||||
return S0 + Reg(v<<1+vx)
|
||||
|
||||
case arg_Sm_Dm:
|
||||
v := (x >> 0) & (1<<4 - 1)
|
||||
vx := (x >> 5) & 1
|
||||
sz := (x >> 8) & 1
|
||||
if sz != 0 {
|
||||
return D0 + Reg(vx<<4+v)
|
||||
} else {
|
||||
return S0 + Reg(v<<1+vx)
|
||||
}
|
||||
|
||||
case arg_Sm:
|
||||
v := (x >> 0) & (1<<4 - 1)
|
||||
vx := (x >> 5) & 1
|
||||
return S0 + Reg(v<<1+vx)
|
||||
|
||||
case arg_Dn_half:
|
||||
v := (x >> 16) & (1<<4 - 1)
|
||||
vx := (x >> 7) & 1
|
||||
return RegX{D0 + Reg(vx<<4+v), int((x >> 21) & 1)}
|
||||
|
||||
case arg_Sn_Dn:
|
||||
v := (x >> 16) & (1<<4 - 1)
|
||||
vx := (x >> 7) & 1
|
||||
sz := (x >> 8) & 1
|
||||
if sz != 0 {
|
||||
return D0 + Reg(vx<<4+v)
|
||||
} else {
|
||||
return S0 + Reg(v<<1+vx)
|
||||
}
|
||||
|
||||
case arg_Sn:
|
||||
v := (x >> 16) & (1<<4 - 1)
|
||||
vx := (x >> 7) & 1
|
||||
return S0 + Reg(v<<1+vx)
|
||||
|
||||
case arg_const:
|
||||
v := x & (1<<8 - 1)
|
||||
rot := (x >> 8) & (1<<4 - 1) * 2
|
||||
if rot > 0 && v&3 == 0 {
|
||||
// could rotate less
|
||||
return ImmAlt{uint8(v), uint8(rot)}
|
||||
}
|
||||
if rot >= 24 && ((v<<(32-rot))&0xFF)>>(32-rot) == v {
|
||||
// could wrap around to rot==0.
|
||||
return ImmAlt{uint8(v), uint8(rot)}
|
||||
}
|
||||
return Imm(v>>rot | v<<(32-rot))
|
||||
|
||||
case arg_endian:
|
||||
return Endian((x >> 9) & 1)
|
||||
|
||||
case arg_fbits:
|
||||
return Imm((16 << ((x >> 7) & 1)) - ((x&(1<<4-1))<<1 | (x>>5)&1))
|
||||
|
||||
case arg_fp_0:
|
||||
return Imm(0)
|
||||
|
||||
case arg_imm24:
|
||||
return Imm(x & (1<<24 - 1))
|
||||
|
||||
case arg_imm5:
|
||||
return Imm((x >> 7) & (1<<5 - 1))
|
||||
|
||||
case arg_imm5_32:
|
||||
x = (x >> 7) & (1<<5 - 1)
|
||||
if x == 0 {
|
||||
x = 32
|
||||
}
|
||||
return Imm(x)
|
||||
|
||||
case arg_imm5_nz:
|
||||
x = (x >> 7) & (1<<5 - 1)
|
||||
if x == 0 {
|
||||
return nil
|
||||
}
|
||||
return Imm(x)
|
||||
|
||||
case arg_imm_4at16_12at0:
|
||||
return Imm((x>>16)&(1<<4-1)<<12 | x&(1<<12-1))
|
||||
|
||||
case arg_imm_12at8_4at0:
|
||||
return Imm((x>>8)&(1<<12-1)<<4 | x&(1<<4-1))
|
||||
|
||||
case arg_imm_vfp:
|
||||
x = (x>>16)&(1<<4-1)<<4 | x&(1<<4-1)
|
||||
return Imm(x)
|
||||
|
||||
case arg_label24:
|
||||
imm := (x & (1<<24 - 1)) << 2
|
||||
return PCRel(int32(imm<<6) >> 6)
|
||||
|
||||
case arg_label24H:
|
||||
h := (x >> 24) & 1
|
||||
imm := (x&(1<<24-1))<<2 | h<<1
|
||||
return PCRel(int32(imm<<6) >> 6)
|
||||
|
||||
case arg_label_m_12:
|
||||
d := int32(x & (1<<12 - 1))
|
||||
return Mem{Base: PC, Mode: AddrOffset, Offset: int16(-d)}
|
||||
|
||||
case arg_label_p_12:
|
||||
d := int32(x & (1<<12 - 1))
|
||||
return Mem{Base: PC, Mode: AddrOffset, Offset: int16(d)}
|
||||
|
||||
case arg_label_pm_12:
|
||||
d := int32(x & (1<<12 - 1))
|
||||
u := (x >> 23) & 1
|
||||
if u == 0 {
|
||||
d = -d
|
||||
}
|
||||
return Mem{Base: PC, Mode: AddrOffset, Offset: int16(d)}
|
||||
|
||||
case arg_label_pm_4_4:
|
||||
d := int32((x>>8)&(1<<4-1)<<4 | x&(1<<4-1))
|
||||
u := (x >> 23) & 1
|
||||
if u == 0 {
|
||||
d = -d
|
||||
}
|
||||
return PCRel(d)
|
||||
|
||||
case arg_lsb_width:
|
||||
lsb := (x >> 7) & (1<<5 - 1)
|
||||
msb := (x >> 16) & (1<<5 - 1)
|
||||
if msb < lsb || msb >= 32 {
|
||||
return nil
|
||||
}
|
||||
return Imm(msb + 1 - lsb)
|
||||
|
||||
case arg_mem_R:
|
||||
Rn := Reg((x >> 16) & (1<<4 - 1))
|
||||
return Mem{Base: Rn, Mode: AddrOffset}
|
||||
|
||||
case arg_mem_R_pm_R_postindex:
|
||||
// Treat [<Rn>],+/-<Rm> like [<Rn>,+/-<Rm>{,<shift>}]{!}
|
||||
// by forcing shift bits to <<0 and P=0, W=0 (postindex=true).
|
||||
return decodeArg(arg_mem_R_pm_R_shift_imm_W, x&^((1<<7-1)<<5|1<<24|1<<21))
|
||||
|
||||
case arg_mem_R_pm_R_W:
|
||||
// Treat [<Rn>,+/-<Rm>]{!} like [<Rn>,+/-<Rm>{,<shift>}]{!}
|
||||
// by forcing shift bits to <<0.
|
||||
return decodeArg(arg_mem_R_pm_R_shift_imm_W, x&^((1<<7-1)<<5))
|
||||
|
||||
case arg_mem_R_pm_R_shift_imm_offset:
|
||||
// Treat [<Rn>],+/-<Rm>{,<shift>} like [<Rn>,+/-<Rm>{,<shift>}]{!}
|
||||
// by forcing P=1, W=0 (index=false, wback=false).
|
||||
return decodeArg(arg_mem_R_pm_R_shift_imm_W, x&^(1<<21)|1<<24)
|
||||
|
||||
case arg_mem_R_pm_R_shift_imm_postindex:
|
||||
// Treat [<Rn>],+/-<Rm>{,<shift>} like [<Rn>,+/-<Rm>{,<shift>}]{!}
|
||||
// by forcing P=0, W=0 (postindex=true).
|
||||
return decodeArg(arg_mem_R_pm_R_shift_imm_W, x&^(1<<24|1<<21))
|
||||
|
||||
case arg_mem_R_pm_R_shift_imm_W:
|
||||
Rn := Reg((x >> 16) & (1<<4 - 1))
|
||||
Rm := Reg(x & (1<<4 - 1))
|
||||
typ, count := decodeShift(x)
|
||||
u := (x >> 23) & 1
|
||||
w := (x >> 21) & 1
|
||||
p := (x >> 24) & 1
|
||||
if p == 0 && w == 1 {
|
||||
return nil
|
||||
}
|
||||
sign := int8(+1)
|
||||
if u == 0 {
|
||||
sign = -1
|
||||
}
|
||||
mode := AddrMode(uint8(p<<1) | uint8(w^1))
|
||||
return Mem{Base: Rn, Mode: mode, Sign: sign, Index: Rm, Shift: typ, Count: count}
|
||||
|
||||
case arg_mem_R_pm_imm12_offset:
|
||||
// Treat [<Rn>,#+/-<imm12>] like [<Rn>{,#+/-<imm12>}]{!}
|
||||
// by forcing P=1, W=0 (index=false, wback=false).
|
||||
return decodeArg(arg_mem_R_pm_imm12_W, x&^(1<<21)|1<<24)
|
||||
|
||||
case arg_mem_R_pm_imm12_postindex:
|
||||
// Treat [<Rn>],#+/-<imm12> like [<Rn>{,#+/-<imm12>}]{!}
|
||||
// by forcing P=0, W=0 (postindex=true).
|
||||
return decodeArg(arg_mem_R_pm_imm12_W, x&^(1<<24|1<<21))
|
||||
|
||||
case arg_mem_R_pm_imm12_W:
|
||||
Rn := Reg((x >> 16) & (1<<4 - 1))
|
||||
u := (x >> 23) & 1
|
||||
w := (x >> 21) & 1
|
||||
p := (x >> 24) & 1
|
||||
if p == 0 && w == 1 {
|
||||
return nil
|
||||
}
|
||||
sign := int8(+1)
|
||||
if u == 0 {
|
||||
sign = -1
|
||||
}
|
||||
imm := int16(x & (1<<12 - 1))
|
||||
mode := AddrMode(uint8(p<<1) | uint8(w^1))
|
||||
return Mem{Base: Rn, Mode: mode, Offset: int16(sign) * imm}
|
||||
|
||||
case arg_mem_R_pm_imm8_postindex:
|
||||
// Treat [<Rn>],#+/-<imm8> like [<Rn>{,#+/-<imm8>}]{!}
|
||||
// by forcing P=0, W=0 (postindex=true).
|
||||
return decodeArg(arg_mem_R_pm_imm8_W, x&^(1<<24|1<<21))
|
||||
|
||||
case arg_mem_R_pm_imm8_W:
|
||||
Rn := Reg((x >> 16) & (1<<4 - 1))
|
||||
u := (x >> 23) & 1
|
||||
w := (x >> 21) & 1
|
||||
p := (x >> 24) & 1
|
||||
if p == 0 && w == 1 {
|
||||
return nil
|
||||
}
|
||||
sign := int8(+1)
|
||||
if u == 0 {
|
||||
sign = -1
|
||||
}
|
||||
imm := int16((x>>8)&(1<<4-1)<<4 | x&(1<<4-1))
|
||||
mode := AddrMode(uint8(p<<1) | uint8(w^1))
|
||||
return Mem{Base: Rn, Mode: mode, Offset: int16(sign) * imm}
|
||||
|
||||
case arg_mem_R_pm_imm8at0_offset:
|
||||
Rn := Reg((x >> 16) & (1<<4 - 1))
|
||||
u := (x >> 23) & 1
|
||||
sign := int8(+1)
|
||||
if u == 0 {
|
||||
sign = -1
|
||||
}
|
||||
imm := int16(x&(1<<8-1)) << 2
|
||||
return Mem{Base: Rn, Mode: AddrOffset, Offset: int16(sign) * imm}
|
||||
|
||||
case arg_option:
|
||||
return Imm(x & (1<<4 - 1))
|
||||
|
||||
case arg_registers:
|
||||
return RegList(x & (1<<16 - 1))
|
||||
|
||||
case arg_registers2:
|
||||
x &= 1<<16 - 1
|
||||
n := 0
|
||||
for i := 0; i < 16; i++ {
|
||||
if x>>uint(i)&1 != 0 {
|
||||
n++
|
||||
}
|
||||
}
|
||||
if n < 2 {
|
||||
return nil
|
||||
}
|
||||
return RegList(x)
|
||||
|
||||
case arg_registers1:
|
||||
Rt := (x >> 12) & (1<<4 - 1)
|
||||
return RegList(1 << Rt)
|
||||
|
||||
case arg_satimm4:
|
||||
return Imm((x >> 16) & (1<<4 - 1))
|
||||
|
||||
case arg_satimm5:
|
||||
return Imm((x >> 16) & (1<<5 - 1))
|
||||
|
||||
case arg_satimm4m1:
|
||||
return Imm((x>>16)&(1<<4-1) + 1)
|
||||
|
||||
case arg_satimm5m1:
|
||||
return Imm((x>>16)&(1<<5-1) + 1)
|
||||
|
||||
case arg_widthm1:
|
||||
return Imm((x>>16)&(1<<5-1) + 1)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// decodeShift decodes the shift-by-immediate encoded in x.
|
||||
func decodeShift(x uint32) (Shift, uint8) {
|
||||
count := (x >> 7) & (1<<5 - 1)
|
||||
typ := Shift((x >> 5) & (1<<2 - 1))
|
||||
switch typ {
|
||||
case ShiftRight, ShiftRightSigned:
|
||||
if count == 0 {
|
||||
count = 32
|
||||
}
|
||||
case RotateRight:
|
||||
if count == 0 {
|
||||
typ = RotateRightExt
|
||||
count = 1
|
||||
}
|
||||
}
|
||||
return typ, uint8(count)
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package armasm
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDecode(t *testing.T) {
|
||||
data, err := ioutil.ReadFile("testdata/decode.txt")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
all := string(data)
|
||||
for strings.Contains(all, "\t\t") {
|
||||
all = strings.Replace(all, "\t\t", "\t", -1)
|
||||
}
|
||||
for _, line := range strings.Split(all, "\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
f := strings.SplitN(line, "\t", 4)
|
||||
i := strings.Index(f[0], "|")
|
||||
if i < 0 {
|
||||
t.Errorf("parsing %q: missing | separator", f[0])
|
||||
continue
|
||||
}
|
||||
if i%2 != 0 {
|
||||
t.Errorf("parsing %q: misaligned | separator", f[0])
|
||||
}
|
||||
size := i / 2
|
||||
code, err := hex.DecodeString(f[0][:i] + f[0][i+1:])
|
||||
if err != nil {
|
||||
t.Errorf("parsing %q: %v", f[0], err)
|
||||
continue
|
||||
}
|
||||
mode, err := strconv.Atoi(f[1])
|
||||
if err != nil {
|
||||
t.Errorf("invalid mode %q in: %s", f[1], line)
|
||||
continue
|
||||
}
|
||||
syntax, asm := f[2], f[3]
|
||||
inst, err := Decode(code, Mode(mode))
|
||||
var out string
|
||||
if err != nil {
|
||||
out = "error: " + err.Error()
|
||||
} else {
|
||||
switch syntax {
|
||||
case "gnu":
|
||||
out = GNUSyntax(inst)
|
||||
case "plan9": // [sic]
|
||||
out = GoSyntax(inst, 0, nil, nil)
|
||||
default:
|
||||
t.Errorf("unknown syntax %q", syntax)
|
||||
continue
|
||||
}
|
||||
}
|
||||
if out != asm || inst.Len != size {
|
||||
t.Errorf("Decode(%s) [%s] = %s, %d, want %s, %d", f[0], syntax, out, inst.Len, asm, size)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,615 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Support for testing against external disassembler program.
|
||||
// Copied and simplified from ../../x86/x86asm/ext_test.go.
|
||||
|
||||
package armasm
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
printTests = flag.Bool("printtests", false, "print test cases that exercise new code paths")
|
||||
dumpTest = flag.Bool("dump", false, "dump all encodings")
|
||||
mismatch = flag.Bool("mismatch", false, "log allowed mismatches")
|
||||
longTest = flag.Bool("long", false, "long test")
|
||||
keep = flag.Bool("keep", false, "keep object files around")
|
||||
debug = false
|
||||
)
|
||||
|
||||
// An ExtInst represents a single decoded instruction parsed
|
||||
// from an external disassembler's output.
|
||||
type ExtInst struct {
|
||||
addr uint32
|
||||
enc [4]byte
|
||||
nenc int
|
||||
text string
|
||||
}
|
||||
|
||||
func (r ExtInst) String() string {
|
||||
return fmt.Sprintf("%#x: % x: %s", r.addr, r.enc, r.text)
|
||||
}
|
||||
|
||||
// An ExtDis is a connection between an external disassembler and a test.
|
||||
type ExtDis struct {
|
||||
Arch Mode
|
||||
Dec chan ExtInst
|
||||
File *os.File
|
||||
Size int
|
||||
KeepFile bool
|
||||
Cmd *exec.Cmd
|
||||
}
|
||||
|
||||
// Run runs the given command - the external disassembler - and returns
|
||||
// a buffered reader of its standard output.
|
||||
func (ext *ExtDis) Run(cmd ...string) (*bufio.Reader, error) {
|
||||
if *keep {
|
||||
log.Printf("%s\n", strings.Join(cmd, " "))
|
||||
}
|
||||
ext.Cmd = exec.Command(cmd[0], cmd[1:]...)
|
||||
out, err := ext.Cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("stdoutpipe: %v", err)
|
||||
}
|
||||
if err := ext.Cmd.Start(); err != nil {
|
||||
return nil, fmt.Errorf("exec: %v", err)
|
||||
}
|
||||
|
||||
b := bufio.NewReaderSize(out, 1<<20)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Wait waits for the command started with Run to exit.
|
||||
func (ext *ExtDis) Wait() error {
|
||||
return ext.Cmd.Wait()
|
||||
}
|
||||
|
||||
// testExtDis tests a set of byte sequences against an external disassembler.
|
||||
// The disassembler is expected to produce the given syntax and be run
|
||||
// in the given architecture mode (16, 32, or 64-bit).
|
||||
// The extdis function must start the external disassembler
|
||||
// and then parse its output, sending the parsed instructions on ext.Dec.
|
||||
// The generate function calls its argument f once for each byte sequence
|
||||
// to be tested. The generate function itself will be called twice, and it must
|
||||
// make the same sequence of calls to f each time.
|
||||
// When a disassembly does not match the internal decoding,
|
||||
// allowedMismatch determines whether this mismatch should be
|
||||
// allowed, or else considered an error.
|
||||
func testExtDis(
|
||||
t *testing.T,
|
||||
syntax string,
|
||||
arch Mode,
|
||||
extdis func(ext *ExtDis) error,
|
||||
generate func(f func([]byte)),
|
||||
allowedMismatch func(text string, size int, inst *Inst, dec ExtInst) bool,
|
||||
) {
|
||||
start := time.Now()
|
||||
ext := &ExtDis{
|
||||
Dec: make(chan ExtInst),
|
||||
Arch: arch,
|
||||
}
|
||||
errc := make(chan error)
|
||||
|
||||
// First pass: write instructions to input file for external disassembler.
|
||||
file, f, size, err := writeInst(generate)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ext.Size = size
|
||||
ext.File = f
|
||||
defer func() {
|
||||
f.Close()
|
||||
if !*keep {
|
||||
os.Remove(file)
|
||||
}
|
||||
}()
|
||||
|
||||
// Second pass: compare disassembly against our decodings.
|
||||
var (
|
||||
totalTests = 0
|
||||
totalSkips = 0
|
||||
totalErrors = 0
|
||||
|
||||
errors = make([]string, 0, 100) // sampled errors, at most cap
|
||||
)
|
||||
go func() {
|
||||
errc <- extdis(ext)
|
||||
}()
|
||||
generate(func(enc []byte) {
|
||||
dec, ok := <-ext.Dec
|
||||
if !ok {
|
||||
t.Errorf("decoding stream ended early")
|
||||
return
|
||||
}
|
||||
inst, text := disasm(syntax, arch, pad(enc))
|
||||
totalTests++
|
||||
if *dumpTest {
|
||||
fmt.Printf("%x -> %s [%d]\n", enc[:len(enc)], dec.text, dec.nenc)
|
||||
}
|
||||
if text != dec.text || inst.Len != dec.nenc {
|
||||
suffix := ""
|
||||
if allowedMismatch(text, size, &inst, dec) {
|
||||
totalSkips++
|
||||
if !*mismatch {
|
||||
return
|
||||
}
|
||||
suffix += " (allowed mismatch)"
|
||||
}
|
||||
totalErrors++
|
||||
if len(errors) >= cap(errors) {
|
||||
j := rand.Intn(totalErrors)
|
||||
if j >= cap(errors) {
|
||||
return
|
||||
}
|
||||
errors = append(errors[:j], errors[j+1:]...)
|
||||
}
|
||||
errors = append(errors, fmt.Sprintf("decode(%x) = %q, %d, want %q, %d%s", enc, text, inst.Len, dec.text, dec.nenc, suffix))
|
||||
}
|
||||
})
|
||||
|
||||
if *mismatch {
|
||||
totalErrors -= totalSkips
|
||||
}
|
||||
|
||||
for _, b := range errors {
|
||||
t.Log(b)
|
||||
}
|
||||
|
||||
if totalErrors > 0 {
|
||||
t.Fail()
|
||||
}
|
||||
t.Logf("%d test cases, %d expected mismatches, %d failures; %.0f cases/second", totalTests, totalSkips, totalErrors, float64(totalTests)/time.Since(start).Seconds())
|
||||
|
||||
if err := <-errc; err != nil {
|
||||
t.Fatalf("external disassembler: %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const start = 0x8000 // start address of text
|
||||
|
||||
// writeInst writes the generated byte sequences to a new file
|
||||
// starting at offset start. That file is intended to be the input to
|
||||
// the external disassembler.
|
||||
func writeInst(generate func(func([]byte))) (file string, f *os.File, size int, err error) {
|
||||
f, err = ioutil.TempFile("", "armasm")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
file = f.Name()
|
||||
|
||||
f.Seek(start, io.SeekStart)
|
||||
w := bufio.NewWriter(f)
|
||||
defer w.Flush()
|
||||
size = 0
|
||||
generate(func(x []byte) {
|
||||
if len(x) > 4 {
|
||||
x = x[:4]
|
||||
}
|
||||
if debug {
|
||||
fmt.Printf("%#x: %x%x\n", start+size, x, zeros[len(x):])
|
||||
}
|
||||
w.Write(x)
|
||||
w.Write(zeros[len(x):])
|
||||
size += len(zeros)
|
||||
})
|
||||
return file, f, size, nil
|
||||
}
|
||||
|
||||
var zeros = []byte{0, 0, 0, 0}
|
||||
|
||||
// pad pads the code sequence with pops.
|
||||
func pad(enc []byte) []byte {
|
||||
if len(enc) < 4 {
|
||||
enc = append(enc[:len(enc):len(enc)], zeros[:4-len(enc)]...)
|
||||
}
|
||||
return enc
|
||||
}
|
||||
|
||||
// disasm returns the decoded instruction and text
|
||||
// for the given source bytes, using the given syntax and mode.
|
||||
func disasm(syntax string, mode Mode, src []byte) (inst Inst, text string) {
|
||||
// If printTests is set, we record the coverage value
|
||||
// before and after, and we write out the inputs for which
|
||||
// coverage went up, in the format expected in testdata/decode.text.
|
||||
// This produces a fairly small set of test cases that exercise nearly
|
||||
// all the code.
|
||||
var cover float64
|
||||
if *printTests {
|
||||
cover -= coverage()
|
||||
}
|
||||
|
||||
inst, err := Decode(src, mode)
|
||||
if err != nil {
|
||||
text = "error: " + err.Error()
|
||||
} else {
|
||||
text = inst.String()
|
||||
switch syntax {
|
||||
//case "arm":
|
||||
// text = ARMSyntax(inst)
|
||||
case "gnu":
|
||||
text = GNUSyntax(inst)
|
||||
//case "plan9": // [sic]
|
||||
// text = GoSyntax(inst, 0, nil)
|
||||
default:
|
||||
text = "error: unknown syntax " + syntax
|
||||
}
|
||||
}
|
||||
|
||||
if *printTests {
|
||||
cover += coverage()
|
||||
if cover > 0 {
|
||||
max := len(src)
|
||||
if max > 4 && inst.Len <= 4 {
|
||||
max = 4
|
||||
}
|
||||
fmt.Printf("%x|%x\t%d\t%s\t%s\n", src[:inst.Len], src[inst.Len:max], mode, syntax, text)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// coverage returns a floating point number denoting the
|
||||
// test coverage until now. The number increases when new code paths are exercised,
|
||||
// both in the Go program and in the decoder byte code.
|
||||
func coverage() float64 {
|
||||
/*
|
||||
testing.Coverage is not in the main distribution.
|
||||
The implementation, which must go in package testing, is:
|
||||
|
||||
// Coverage reports the current code coverage as a fraction in the range [0, 1].
|
||||
func Coverage() float64 {
|
||||
var n, d int64
|
||||
for _, counters := range cover.Counters {
|
||||
for _, c := range counters {
|
||||
if c > 0 {
|
||||
n++
|
||||
}
|
||||
d++
|
||||
}
|
||||
}
|
||||
if d == 0 {
|
||||
return 0
|
||||
}
|
||||
return float64(n) / float64(d)
|
||||
}
|
||||
*/
|
||||
|
||||
var f float64
|
||||
f += testing.Coverage()
|
||||
f += decodeCoverage()
|
||||
return f
|
||||
}
|
||||
|
||||
func decodeCoverage() float64 {
|
||||
n := 0
|
||||
for _, t := range decoderCover {
|
||||
if t {
|
||||
n++
|
||||
}
|
||||
}
|
||||
return float64(1+n) / float64(1+len(decoderCover))
|
||||
}
|
||||
|
||||
// Helpers for writing disassembler output parsers.
|
||||
|
||||
// hasPrefix reports whether any of the space-separated words in the text s
|
||||
// begins with any of the given prefixes.
|
||||
func hasPrefix(s string, prefixes ...string) bool {
|
||||
for _, prefix := range prefixes {
|
||||
for s := s; s != ""; {
|
||||
if strings.HasPrefix(s, prefix) {
|
||||
return true
|
||||
}
|
||||
i := strings.Index(s, " ")
|
||||
if i < 0 {
|
||||
break
|
||||
}
|
||||
s = s[i+1:]
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// contains reports whether the text s contains any of the given substrings.
|
||||
func contains(s string, substrings ...string) bool {
|
||||
for _, sub := range substrings {
|
||||
if strings.Contains(s, sub) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isHex reports whether b is a hexadecimal character (0-9A-Fa-f).
|
||||
func isHex(b byte) bool { return b == '0' || unhex[b] > 0 }
|
||||
|
||||
// parseHex parses the hexadecimal byte dump in hex,
|
||||
// appending the parsed bytes to raw and returning the updated slice.
|
||||
// The returned bool signals whether any invalid hex was found.
|
||||
// Spaces and tabs between bytes are okay but any other non-hex is not.
|
||||
func parseHex(hex []byte, raw []byte) ([]byte, bool) {
|
||||
hex = trimSpace(hex)
|
||||
for j := 0; j < len(hex); {
|
||||
for hex[j] == ' ' || hex[j] == '\t' {
|
||||
j++
|
||||
}
|
||||
if j >= len(hex) {
|
||||
break
|
||||
}
|
||||
if j+2 > len(hex) || !isHex(hex[j]) || !isHex(hex[j+1]) {
|
||||
return nil, false
|
||||
}
|
||||
raw = append(raw, unhex[hex[j]]<<4|unhex[hex[j+1]])
|
||||
j += 2
|
||||
}
|
||||
return raw, true
|
||||
}
|
||||
|
||||
var unhex = [256]byte{
|
||||
'0': 0,
|
||||
'1': 1,
|
||||
'2': 2,
|
||||
'3': 3,
|
||||
'4': 4,
|
||||
'5': 5,
|
||||
'6': 6,
|
||||
'7': 7,
|
||||
'8': 8,
|
||||
'9': 9,
|
||||
'A': 10,
|
||||
'B': 11,
|
||||
'C': 12,
|
||||
'D': 13,
|
||||
'E': 14,
|
||||
'F': 15,
|
||||
'a': 10,
|
||||
'b': 11,
|
||||
'c': 12,
|
||||
'd': 13,
|
||||
'e': 14,
|
||||
'f': 15,
|
||||
}
|
||||
|
||||
// index is like bytes.Index(s, []byte(t)) but avoids the allocation.
|
||||
func index(s []byte, t string) int {
|
||||
i := 0
|
||||
for {
|
||||
j := bytes.IndexByte(s[i:], t[0])
|
||||
if j < 0 {
|
||||
return -1
|
||||
}
|
||||
i = i + j
|
||||
if i+len(t) > len(s) {
|
||||
return -1
|
||||
}
|
||||
for k := 1; k < len(t); k++ {
|
||||
if s[i+k] != t[k] {
|
||||
goto nomatch
|
||||
}
|
||||
}
|
||||
return i
|
||||
nomatch:
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
// fixSpace rewrites runs of spaces, tabs, and newline characters into single spaces in s.
|
||||
// If s must be rewritten, it is rewritten in place.
|
||||
func fixSpace(s []byte) []byte {
|
||||
s = trimSpace(s)
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] == '\t' || s[i] == '\n' || i > 0 && s[i] == ' ' && s[i-1] == ' ' {
|
||||
goto Fix
|
||||
}
|
||||
}
|
||||
return s
|
||||
|
||||
Fix:
|
||||
b := s
|
||||
w := 0
|
||||
for i := 0; i < len(s); i++ {
|
||||
c := s[i]
|
||||
if c == '\t' || c == '\n' {
|
||||
c = ' '
|
||||
}
|
||||
if c == ' ' && w > 0 && b[w-1] == ' ' {
|
||||
continue
|
||||
}
|
||||
b[w] = c
|
||||
w++
|
||||
}
|
||||
if w > 0 && b[w-1] == ' ' {
|
||||
w--
|
||||
}
|
||||
return b[:w]
|
||||
}
|
||||
|
||||
// trimSpace trims leading and trailing space from s, returning a subslice of s.
|
||||
func trimSpace(s []byte) []byte {
|
||||
j := len(s)
|
||||
for j > 0 && (s[j-1] == ' ' || s[j-1] == '\t' || s[j-1] == '\n') {
|
||||
j--
|
||||
}
|
||||
i := 0
|
||||
for i < j && (s[i] == ' ' || s[i] == '\t') {
|
||||
i++
|
||||
}
|
||||
return s[i:j]
|
||||
}
|
||||
|
||||
// pcrel matches instructions using relative addressing mode.
|
||||
var (
|
||||
pcrel = regexp.MustCompile(`^((?:.* )?(?:b|bl)x?(?:eq|ne|cs|cc|mi|pl|vs|vc|hi|ls|ge|lt|gt|le)?) 0x([0-9a-f]+)$`)
|
||||
)
|
||||
|
||||
// Generators.
|
||||
//
|
||||
// The test cases are described as functions that invoke a callback repeatedly,
|
||||
// with a new input sequence each time. These helpers make writing those
|
||||
// a little easier.
|
||||
|
||||
// condCases generates conditional instructions.
|
||||
func condCases(t *testing.T) func(func([]byte)) {
|
||||
return func(try func([]byte)) {
|
||||
// All the strides are relatively prime to 2 and therefore to 2²⁸,
|
||||
// so we will not repeat any instructions until we have tried all 2²⁸.
|
||||
// Using a stride other than 1 is meant to visit the instructions in a
|
||||
// pseudorandom order, which gives better variety in the set of
|
||||
// test cases chosen by -printtests.
|
||||
stride := uint32(10007)
|
||||
n := 1 << 28 / 7
|
||||
if testing.Short() {
|
||||
stride = 100003
|
||||
n = 1 << 28 / 1001
|
||||
} else if *longTest {
|
||||
stride = 200000033
|
||||
n = 1 << 28
|
||||
}
|
||||
x := uint32(0)
|
||||
for i := 0; i < n; i++ {
|
||||
enc := (x%15)<<28 | x&(1<<28-1)
|
||||
try([]byte{byte(enc), byte(enc >> 8), byte(enc >> 16), byte(enc >> 24)})
|
||||
x += stride
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// uncondCases generates unconditional instructions.
|
||||
func uncondCases(t *testing.T) func(func([]byte)) {
|
||||
return func(try func([]byte)) {
|
||||
condCases(t)(func(enc []byte) {
|
||||
enc[3] |= 0xF0
|
||||
try(enc)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func countBits(x uint32) int {
|
||||
n := 0
|
||||
for ; x != 0; x >>= 1 {
|
||||
n += int(x & 1)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func expandBits(x, m uint32) uint32 {
|
||||
var out uint32
|
||||
for i := uint(0); i < 32; i++ {
|
||||
out >>= 1
|
||||
if m&1 != 0 {
|
||||
out |= (x & 1) << 31
|
||||
x >>= 1
|
||||
}
|
||||
m >>= 1
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func tryCondMask(mask, val uint32, try func([]byte)) {
|
||||
n := countBits(^mask)
|
||||
bits := uint32(0)
|
||||
for i := 0; i < 1<<uint(n); i++ {
|
||||
bits += 848251 // arbitrary prime
|
||||
x := val | expandBits(bits, ^mask) | uint32(i)%15<<28
|
||||
try([]byte{byte(x), byte(x >> 8), byte(x >> 16), byte(x >> 24)})
|
||||
}
|
||||
}
|
||||
|
||||
// vfpCases generates VFP instructions.
|
||||
func vfpCases(t *testing.T) func(func([]byte)) {
|
||||
const (
|
||||
vfpmask uint32 = 0xFF00FE10
|
||||
vfp uint32 = 0x0E009A00
|
||||
)
|
||||
return func(try func([]byte)) {
|
||||
tryCondMask(0xff00fe10, 0x0e009a00, try) // standard VFP instruction space
|
||||
tryCondMask(0xffc00f7f, 0x0e000b10, try) // VFP MOV core reg to/from float64 half
|
||||
tryCondMask(0xffe00f7f, 0x0e000a10, try) // VFP MOV core reg to/from float32
|
||||
tryCondMask(0xffef0fff, 0x0ee10a10, try) // VFP MOV core reg to/from cond codes
|
||||
}
|
||||
}
|
||||
|
||||
// hexCases generates the cases written in hexadecimal in the encoded string.
|
||||
// Spaces in 'encoded' separate entire test cases, not individual bytes.
|
||||
func hexCases(t *testing.T, encoded string) func(func([]byte)) {
|
||||
return func(try func([]byte)) {
|
||||
for _, x := range strings.Fields(encoded) {
|
||||
src, err := hex.DecodeString(x)
|
||||
if err != nil {
|
||||
t.Errorf("parsing %q: %v", x, err)
|
||||
}
|
||||
try(src)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// testdataCases generates the test cases recorded in testdata/decode.txt.
|
||||
// It only uses the inputs; it ignores the answers recorded in that file.
|
||||
func testdataCases(t *testing.T) func(func([]byte)) {
|
||||
var codes [][]byte
|
||||
data, err := ioutil.ReadFile("testdata/decode.txt")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, line := range strings.Split(string(data), "\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
f := strings.Fields(line)[0]
|
||||
i := strings.Index(f, "|")
|
||||
if i < 0 {
|
||||
t.Errorf("parsing %q: missing | separator", f)
|
||||
continue
|
||||
}
|
||||
if i%2 != 0 {
|
||||
t.Errorf("parsing %q: misaligned | separator", f)
|
||||
}
|
||||
code, err := hex.DecodeString(f[:i] + f[i+1:])
|
||||
if err != nil {
|
||||
t.Errorf("parsing %q: %v", f, err)
|
||||
continue
|
||||
}
|
||||
codes = append(codes, code)
|
||||
}
|
||||
|
||||
return func(try func([]byte)) {
|
||||
for _, code := range codes {
|
||||
try(code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func caller(skip int) string {
|
||||
pc, _, _, _ := runtime.Caller(skip)
|
||||
f := runtime.FuncForPC(pc)
|
||||
name := "?"
|
||||
if f != nil {
|
||||
name = f.Name()
|
||||
if i := strings.LastIndex(name, "."); i >= 0 {
|
||||
name = name[i+1:]
|
||||
}
|
||||
}
|
||||
return name
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package armasm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var saveDot = strings.NewReplacer(
|
||||
".F16", "_dot_F16",
|
||||
".F32", "_dot_F32",
|
||||
".F64", "_dot_F64",
|
||||
".S32", "_dot_S32",
|
||||
".U32", "_dot_U32",
|
||||
".FXS", "_dot_S",
|
||||
".FXU", "_dot_U",
|
||||
".32", "_dot_32",
|
||||
)
|
||||
|
||||
// GNUSyntax returns the GNU assembler syntax for the instruction, as defined by GNU binutils.
|
||||
// This form typically matches the syntax defined in the ARM Reference Manual.
|
||||
func GNUSyntax(inst Inst) string {
|
||||
var buf bytes.Buffer
|
||||
op := inst.Op.String()
|
||||
op = saveDot.Replace(op)
|
||||
op = strings.Replace(op, ".", "", -1)
|
||||
op = strings.Replace(op, "_dot_", ".", -1)
|
||||
op = strings.ToLower(op)
|
||||
buf.WriteString(op)
|
||||
sep := " "
|
||||
for i, arg := range inst.Args {
|
||||
if arg == nil {
|
||||
break
|
||||
}
|
||||
text := gnuArg(&inst, i, arg)
|
||||
if text == "" {
|
||||
continue
|
||||
}
|
||||
buf.WriteString(sep)
|
||||
sep = ", "
|
||||
buf.WriteString(text)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func gnuArg(inst *Inst, argIndex int, arg Arg) string {
|
||||
switch inst.Op &^ 15 {
|
||||
case LDRD_EQ, LDREXD_EQ, STRD_EQ:
|
||||
if argIndex == 1 {
|
||||
// second argument in consecutive pair not printed
|
||||
return ""
|
||||
}
|
||||
case STREXD_EQ:
|
||||
if argIndex == 2 {
|
||||
// second argument in consecutive pair not printed
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
switch arg := arg.(type) {
|
||||
case Imm:
|
||||
switch inst.Op &^ 15 {
|
||||
case BKPT_EQ:
|
||||
return fmt.Sprintf("%#04x", uint32(arg))
|
||||
case SVC_EQ:
|
||||
return fmt.Sprintf("%#08x", uint32(arg))
|
||||
}
|
||||
return fmt.Sprintf("#%d", int32(arg))
|
||||
|
||||
case ImmAlt:
|
||||
return fmt.Sprintf("#%d, %d", arg.Val, arg.Rot)
|
||||
|
||||
case Mem:
|
||||
R := gnuArg(inst, -1, arg.Base)
|
||||
X := ""
|
||||
if arg.Sign != 0 {
|
||||
X = ""
|
||||
if arg.Sign < 0 {
|
||||
X = "-"
|
||||
}
|
||||
X += gnuArg(inst, -1, arg.Index)
|
||||
if arg.Shift == ShiftLeft && arg.Count == 0 {
|
||||
// nothing
|
||||
} else if arg.Shift == RotateRightExt {
|
||||
X += ", rrx"
|
||||
} else {
|
||||
X += fmt.Sprintf(", %s #%d", strings.ToLower(arg.Shift.String()), arg.Count)
|
||||
}
|
||||
} else {
|
||||
X = fmt.Sprintf("#%d", arg.Offset)
|
||||
}
|
||||
|
||||
switch arg.Mode {
|
||||
case AddrOffset:
|
||||
if X == "#0" {
|
||||
return fmt.Sprintf("[%s]", R)
|
||||
}
|
||||
return fmt.Sprintf("[%s, %s]", R, X)
|
||||
case AddrPreIndex:
|
||||
return fmt.Sprintf("[%s, %s]!", R, X)
|
||||
case AddrPostIndex:
|
||||
return fmt.Sprintf("[%s], %s", R, X)
|
||||
case AddrLDM:
|
||||
if X == "#0" {
|
||||
return R
|
||||
}
|
||||
case AddrLDM_WB:
|
||||
if X == "#0" {
|
||||
return R + "!"
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("[%s Mode(%d) %s]", R, int(arg.Mode), X)
|
||||
|
||||
case PCRel:
|
||||
return fmt.Sprintf(".%+#x", int32(arg)+4)
|
||||
|
||||
case Reg:
|
||||
switch inst.Op &^ 15 {
|
||||
case LDREX_EQ:
|
||||
if argIndex == 0 {
|
||||
return fmt.Sprintf("r%d", int32(arg))
|
||||
}
|
||||
}
|
||||
switch arg {
|
||||
case R10:
|
||||
return "sl"
|
||||
case R11:
|
||||
return "fp"
|
||||
case R12:
|
||||
return "ip"
|
||||
}
|
||||
|
||||
case RegList:
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprintf(&buf, "{")
|
||||
sep := ""
|
||||
for i := 0; i < 16; i++ {
|
||||
if arg&(1<<uint(i)) != 0 {
|
||||
fmt.Fprintf(&buf, "%s%s", sep, gnuArg(inst, -1, Reg(i)))
|
||||
sep = ", "
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(&buf, "}")
|
||||
return buf.String()
|
||||
|
||||
case RegShift:
|
||||
if arg.Shift == ShiftLeft && arg.Count == 0 {
|
||||
return gnuArg(inst, -1, arg.Reg)
|
||||
}
|
||||
if arg.Shift == RotateRightExt {
|
||||
return gnuArg(inst, -1, arg.Reg) + ", rrx"
|
||||
}
|
||||
return fmt.Sprintf("%s, %s #%d", gnuArg(inst, -1, arg.Reg), strings.ToLower(arg.Shift.String()), arg.Count)
|
||||
|
||||
case RegShiftReg:
|
||||
return fmt.Sprintf("%s, %s %s", gnuArg(inst, -1, arg.Reg), strings.ToLower(arg.Shift.String()), gnuArg(inst, -1, arg.RegCount))
|
||||
|
||||
}
|
||||
return strings.ToLower(arg.String())
|
||||
}
|
||||
@@ -0,0 +1,438 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package armasm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// A Mode is an instruction execution mode.
|
||||
type Mode int
|
||||
|
||||
const (
|
||||
_ Mode = iota
|
||||
ModeARM
|
||||
ModeThumb
|
||||
)
|
||||
|
||||
func (m Mode) String() string {
|
||||
switch m {
|
||||
case ModeARM:
|
||||
return "ARM"
|
||||
case ModeThumb:
|
||||
return "Thumb"
|
||||
}
|
||||
return fmt.Sprintf("Mode(%d)", int(m))
|
||||
}
|
||||
|
||||
// An Op is an ARM opcode.
|
||||
type Op uint16
|
||||
|
||||
// NOTE: The actual Op values are defined in tables.go.
|
||||
// They are chosen to simplify instruction decoding and
|
||||
// are not a dense packing from 0 to N, although the
|
||||
// density is high, probably at least 90%.
|
||||
|
||||
func (op Op) String() string {
|
||||
if op >= Op(len(opstr)) || opstr[op] == "" {
|
||||
return fmt.Sprintf("Op(%d)", int(op))
|
||||
}
|
||||
return opstr[op]
|
||||
}
|
||||
|
||||
// An Inst is a single instruction.
|
||||
type Inst struct {
|
||||
Op Op // Opcode mnemonic
|
||||
Enc uint32 // Raw encoding bits.
|
||||
Len int // Length of encoding in bytes.
|
||||
Args Args // Instruction arguments, in ARM manual order.
|
||||
}
|
||||
|
||||
func (i Inst) String() string {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(i.Op.String())
|
||||
for j, arg := range i.Args {
|
||||
if arg == nil {
|
||||
break
|
||||
}
|
||||
if j == 0 {
|
||||
buf.WriteString(" ")
|
||||
} else {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
buf.WriteString(arg.String())
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// An Args holds the instruction arguments.
|
||||
// If an instruction has fewer than 4 arguments,
|
||||
// the final elements in the array are nil.
|
||||
type Args [4]Arg
|
||||
|
||||
// An Arg is a single instruction argument, one of these types:
|
||||
// Endian, Imm, Mem, PCRel, Reg, RegList, RegShift, RegShiftReg.
|
||||
type Arg interface {
|
||||
IsArg()
|
||||
String() string
|
||||
}
|
||||
|
||||
type Float32Imm float32
|
||||
|
||||
func (Float32Imm) IsArg() {}
|
||||
|
||||
func (f Float32Imm) String() string {
|
||||
return fmt.Sprintf("#%v", float32(f))
|
||||
}
|
||||
|
||||
type Float64Imm float32
|
||||
|
||||
func (Float64Imm) IsArg() {}
|
||||
|
||||
func (f Float64Imm) String() string {
|
||||
return fmt.Sprintf("#%v", float64(f))
|
||||
}
|
||||
|
||||
// An Imm is an integer constant.
|
||||
type Imm uint32
|
||||
|
||||
func (Imm) IsArg() {}
|
||||
|
||||
func (i Imm) String() string {
|
||||
return fmt.Sprintf("#%#x", uint32(i))
|
||||
}
|
||||
|
||||
// An ImmAlt is an alternate encoding of an integer constant.
|
||||
type ImmAlt struct {
|
||||
Val uint8
|
||||
Rot uint8
|
||||
}
|
||||
|
||||
func (ImmAlt) IsArg() {}
|
||||
|
||||
func (i ImmAlt) Imm() Imm {
|
||||
v := uint32(i.Val)
|
||||
r := uint(i.Rot)
|
||||
return Imm(v>>r | v<<(32-r))
|
||||
}
|
||||
|
||||
func (i ImmAlt) String() string {
|
||||
return fmt.Sprintf("#%#x, %d", i.Val, i.Rot)
|
||||
}
|
||||
|
||||
// A Label is a text (code) address.
|
||||
type Label uint32
|
||||
|
||||
func (Label) IsArg() {}
|
||||
|
||||
func (i Label) String() string {
|
||||
return fmt.Sprintf("%#x", uint32(i))
|
||||
}
|
||||
|
||||
// A Reg is a single register.
|
||||
// The zero value denotes R0, not the absence of a register.
|
||||
type Reg uint8
|
||||
|
||||
const (
|
||||
R0 Reg = iota
|
||||
R1
|
||||
R2
|
||||
R3
|
||||
R4
|
||||
R5
|
||||
R6
|
||||
R7
|
||||
R8
|
||||
R9
|
||||
R10
|
||||
R11
|
||||
R12
|
||||
R13
|
||||
R14
|
||||
R15
|
||||
|
||||
S0
|
||||
S1
|
||||
S2
|
||||
S3
|
||||
S4
|
||||
S5
|
||||
S6
|
||||
S7
|
||||
S8
|
||||
S9
|
||||
S10
|
||||
S11
|
||||
S12
|
||||
S13
|
||||
S14
|
||||
S15
|
||||
S16
|
||||
S17
|
||||
S18
|
||||
S19
|
||||
S20
|
||||
S21
|
||||
S22
|
||||
S23
|
||||
S24
|
||||
S25
|
||||
S26
|
||||
S27
|
||||
S28
|
||||
S29
|
||||
S30
|
||||
S31
|
||||
|
||||
D0
|
||||
D1
|
||||
D2
|
||||
D3
|
||||
D4
|
||||
D5
|
||||
D6
|
||||
D7
|
||||
D8
|
||||
D9
|
||||
D10
|
||||
D11
|
||||
D12
|
||||
D13
|
||||
D14
|
||||
D15
|
||||
D16
|
||||
D17
|
||||
D18
|
||||
D19
|
||||
D20
|
||||
D21
|
||||
D22
|
||||
D23
|
||||
D24
|
||||
D25
|
||||
D26
|
||||
D27
|
||||
D28
|
||||
D29
|
||||
D30
|
||||
D31
|
||||
|
||||
APSR
|
||||
APSR_nzcv
|
||||
FPSCR
|
||||
|
||||
SP = R13
|
||||
LR = R14
|
||||
PC = R15
|
||||
)
|
||||
|
||||
func (Reg) IsArg() {}
|
||||
|
||||
func (r Reg) String() string {
|
||||
switch r {
|
||||
case APSR:
|
||||
return "APSR"
|
||||
case APSR_nzcv:
|
||||
return "APSR_nzcv"
|
||||
case FPSCR:
|
||||
return "FPSCR"
|
||||
case SP:
|
||||
return "SP"
|
||||
case PC:
|
||||
return "PC"
|
||||
case LR:
|
||||
return "LR"
|
||||
}
|
||||
if R0 <= r && r <= R15 {
|
||||
return fmt.Sprintf("R%d", int(r-R0))
|
||||
}
|
||||
if S0 <= r && r <= S31 {
|
||||
return fmt.Sprintf("S%d", int(r-S0))
|
||||
}
|
||||
if D0 <= r && r <= D31 {
|
||||
return fmt.Sprintf("D%d", int(r-D0))
|
||||
}
|
||||
return fmt.Sprintf("Reg(%d)", int(r))
|
||||
}
|
||||
|
||||
// A RegX represents a fraction of a multi-value register.
|
||||
// The Index field specifies the index number,
|
||||
// but the size of the fraction is not specified.
|
||||
// It must be inferred from the instruction and the register type.
|
||||
// For example, in a VMOV instruction, RegX{D5, 1} represents
|
||||
// the top 32 bits of the 64-bit D5 register.
|
||||
type RegX struct {
|
||||
Reg Reg
|
||||
Index int
|
||||
}
|
||||
|
||||
func (RegX) IsArg() {}
|
||||
|
||||
func (r RegX) String() string {
|
||||
return fmt.Sprintf("%s[%d]", r.Reg, r.Index)
|
||||
}
|
||||
|
||||
// A RegList is a register list.
|
||||
// Bits at indexes x = 0 through 15 indicate whether the corresponding Rx register is in the list.
|
||||
type RegList uint16
|
||||
|
||||
func (RegList) IsArg() {}
|
||||
|
||||
func (r RegList) String() string {
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprintf(&buf, "{")
|
||||
sep := ""
|
||||
for i := 0; i < 16; i++ {
|
||||
if r&(1<<uint(i)) != 0 {
|
||||
fmt.Fprintf(&buf, "%s%s", sep, Reg(i).String())
|
||||
sep = ","
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(&buf, "}")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// An Endian is the argument to the SETEND instruction.
|
||||
type Endian uint8
|
||||
|
||||
const (
|
||||
LittleEndian Endian = 0
|
||||
BigEndian Endian = 1
|
||||
)
|
||||
|
||||
func (Endian) IsArg() {}
|
||||
|
||||
func (e Endian) String() string {
|
||||
if e != 0 {
|
||||
return "BE"
|
||||
}
|
||||
return "LE"
|
||||
}
|
||||
|
||||
// A Shift describes an ARM shift operation.
|
||||
type Shift uint8
|
||||
|
||||
const (
|
||||
ShiftLeft Shift = 0 // left shift
|
||||
ShiftRight Shift = 1 // logical (unsigned) right shift
|
||||
ShiftRightSigned Shift = 2 // arithmetic (signed) right shift
|
||||
RotateRight Shift = 3 // right rotate
|
||||
RotateRightExt Shift = 4 // right rotate through carry (Count will always be 1)
|
||||
)
|
||||
|
||||
var shiftName = [...]string{
|
||||
"LSL", "LSR", "ASR", "ROR", "RRX",
|
||||
}
|
||||
|
||||
func (s Shift) String() string {
|
||||
if s < 5 {
|
||||
return shiftName[s]
|
||||
}
|
||||
return fmt.Sprintf("Shift(%d)", int(s))
|
||||
}
|
||||
|
||||
// A RegShift is a register shifted by a constant.
|
||||
type RegShift struct {
|
||||
Reg Reg
|
||||
Shift Shift
|
||||
Count uint8
|
||||
}
|
||||
|
||||
func (RegShift) IsArg() {}
|
||||
|
||||
func (r RegShift) String() string {
|
||||
return fmt.Sprintf("%s %s #%d", r.Reg, r.Shift, r.Count)
|
||||
}
|
||||
|
||||
// A RegShiftReg is a register shifted by a register.
|
||||
type RegShiftReg struct {
|
||||
Reg Reg
|
||||
Shift Shift
|
||||
RegCount Reg
|
||||
}
|
||||
|
||||
func (RegShiftReg) IsArg() {}
|
||||
|
||||
func (r RegShiftReg) String() string {
|
||||
return fmt.Sprintf("%s %s %s", r.Reg, r.Shift, r.RegCount)
|
||||
}
|
||||
|
||||
// A PCRel describes a memory address (usually a code label)
|
||||
// as a distance relative to the program counter.
|
||||
// TODO(rsc): Define which program counter (PC+4? PC+8? PC?).
|
||||
type PCRel int32
|
||||
|
||||
func (PCRel) IsArg() {}
|
||||
|
||||
func (r PCRel) String() string {
|
||||
return fmt.Sprintf("PC%+#x", int32(r))
|
||||
}
|
||||
|
||||
// An AddrMode is an ARM addressing mode.
|
||||
type AddrMode uint8
|
||||
|
||||
const (
|
||||
_ AddrMode = iota
|
||||
AddrPostIndex // [R], X – use address R, set R = R + X
|
||||
AddrPreIndex // [R, X]! – use address R + X, set R = R + X
|
||||
AddrOffset // [R, X] – use address R + X
|
||||
AddrLDM // R – [R] but formats as R, for LDM/STM only
|
||||
AddrLDM_WB // R! - [R], X where X is instruction-specific amount, for LDM/STM only
|
||||
)
|
||||
|
||||
// A Mem is a memory reference made up of a base R and index expression X.
|
||||
// The effective memory address is R or R+X depending on AddrMode.
|
||||
// The index expression is X = Sign*(Index Shift Count) + Offset,
|
||||
// but in any instruction either Sign = 0 or Offset = 0.
|
||||
type Mem struct {
|
||||
Base Reg
|
||||
Mode AddrMode
|
||||
Sign int8
|
||||
Index Reg
|
||||
Shift Shift
|
||||
Count uint8
|
||||
Offset int16
|
||||
}
|
||||
|
||||
func (Mem) IsArg() {}
|
||||
|
||||
func (m Mem) String() string {
|
||||
R := m.Base.String()
|
||||
X := ""
|
||||
if m.Sign != 0 {
|
||||
X = "+"
|
||||
if m.Sign < 0 {
|
||||
X = "-"
|
||||
}
|
||||
X += m.Index.String()
|
||||
if m.Shift != ShiftLeft || m.Count != 0 {
|
||||
X += fmt.Sprintf(", %s #%d", m.Shift, m.Count)
|
||||
}
|
||||
} else {
|
||||
X = fmt.Sprintf("#%d", m.Offset)
|
||||
}
|
||||
|
||||
switch m.Mode {
|
||||
case AddrOffset:
|
||||
if X == "#0" {
|
||||
return fmt.Sprintf("[%s]", R)
|
||||
}
|
||||
return fmt.Sprintf("[%s, %s]", R, X)
|
||||
case AddrPreIndex:
|
||||
return fmt.Sprintf("[%s, %s]!", R, X)
|
||||
case AddrPostIndex:
|
||||
return fmt.Sprintf("[%s], %s", R, X)
|
||||
case AddrLDM:
|
||||
if X == "#0" {
|
||||
return R
|
||||
}
|
||||
case AddrLDM_WB:
|
||||
if X == "#0" {
|
||||
return R + "!"
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("[%s Mode(%d) %s]", R, int(m.Mode), X)
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package armasm
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestObjdumpARMTestdata(t *testing.T) { testObjdumpARM(t, testdataCases(t)) }
|
||||
func TestObjdumpARMManual(t *testing.T) { testObjdumpARM(t, hexCases(t, objdumpManualTests)) }
|
||||
func TestObjdumpARMCond(t *testing.T) { testObjdumpARM(t, condCases(t)) }
|
||||
func TestObjdumpARMUncond(t *testing.T) { testObjdumpARM(t, uncondCases(t)) }
|
||||
func TestObjdumpARMVFP(t *testing.T) { testObjdumpARM(t, vfpCases(t)) }
|
||||
|
||||
// objdumpManualTests holds test cases that will be run by TestObjdumpARMManual.
|
||||
// If you are debugging a few cases that turned up in a longer run, it can be useful
|
||||
// to list them here and then use -run=Manual, particularly with tracing enabled.
|
||||
// Note that these are byte sequences, so they must be reversed from the usual
|
||||
// word presentation.
|
||||
var objdumpManualTests = `
|
||||
002a9b1d
|
||||
001b9bed
|
||||
020b8ded
|
||||
003a9b1d
|
||||
060b8ded
|
||||
fcde1100
|
||||
b4de1100
|
||||
bc480000
|
||||
0b008de7
|
||||
0b00ade7
|
||||
fdbcfaf7
|
||||
`
|
||||
|
||||
// allowedMismatchObjdump reports whether the mismatch between text and dec
|
||||
// should be allowed by the test.
|
||||
func allowedMismatchObjdump(text string, size int, inst *Inst, dec ExtInst) bool {
|
||||
if hasPrefix(text, "error:") {
|
||||
if hasPrefix(dec.text, unsupported...) || strings.Contains(dec.text, "invalid:") || strings.HasSuffix(dec.text, "^") || strings.Contains(dec.text, "f16.f64") || strings.Contains(dec.text, "f64.f16") {
|
||||
return true
|
||||
}
|
||||
// word 4320F02C: libopcodes says 'nopmi {44}'.
|
||||
if hasPrefix(dec.text, "nop") && strings.Contains(dec.text, "{") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if hasPrefix(dec.text, "error:") && text == "undef" && inst.Enc == 0xf7fabcfd {
|
||||
return true
|
||||
}
|
||||
|
||||
// word 00f02053: libopcodes says 'noppl {0}'.
|
||||
if hasPrefix(dec.text, "nop") && hasPrefix(text, "nop") && dec.text == text+" {0}" {
|
||||
return true
|
||||
}
|
||||
|
||||
// word F57FF04F. we say 'dsb #15', libopcodes says 'dsb sy'.
|
||||
if hasPrefix(text, "dsb") && hasPrefix(dec.text, "dsb") {
|
||||
return true
|
||||
}
|
||||
// word F57FF06F. we say 'isb #15', libopcodes says 'isb sy'.
|
||||
if hasPrefix(text, "isb") && hasPrefix(dec.text, "isb") {
|
||||
return true
|
||||
}
|
||||
// word F57FF053. we say 'dmb #3', libopcodes says 'dmb osh'.
|
||||
if hasPrefix(text, "dmb") && hasPrefix(dec.text, "dmb") {
|
||||
return true
|
||||
}
|
||||
|
||||
// word 992D0000. push/stmdb with no registers (undefined).
|
||||
// we say 'stmdbls sp!, {}', libopcodes says 'pushls {}'.
|
||||
if hasPrefix(text, "stmdb") && hasPrefix(dec.text, "push") && strings.Contains(text, "{}") && strings.Contains(dec.text, "{}") {
|
||||
return true
|
||||
}
|
||||
|
||||
// word 28BD0000. pop/ldm with no registers (undefined).
|
||||
// we say 'ldmcs sp!, {}', libopcodes says 'popcs {}'.
|
||||
if hasPrefix(text, "ldm") && hasPrefix(dec.text, "pop") && strings.Contains(text, "{}") && strings.Contains(dec.text, "{}") {
|
||||
return true
|
||||
}
|
||||
|
||||
// word 014640F0.
|
||||
// libopcodes emits #-0 for negative zero; we don't.
|
||||
if strings.Replace(dec.text, "#-0", "#0", -1) == text || strings.Replace(dec.text, ", #-0", "", -1) == text {
|
||||
return true
|
||||
}
|
||||
|
||||
// word 91EF90F0. we say 'strdls r9, [pc, #0]!' but libopcodes says 'strdls r9, [pc]'.
|
||||
// word D16F60F0. we say 'strdle r6, [pc, #0]!' but libopcodes says 'strdle r6, [pc, #-0]'.
|
||||
if strings.Replace(text, ", #0]!", "]", -1) == strings.Replace(dec.text, ", #-0]", "]", -1) {
|
||||
return true
|
||||
}
|
||||
|
||||
// word 510F4000. we say apsr, libopcodes says CPSR.
|
||||
if strings.Replace(dec.text, "CPSR", "apsr", -1) == text {
|
||||
return true
|
||||
}
|
||||
|
||||
// word 06A4B059.
|
||||
// for ssat and usat, libopcodes decodes asr #0 as asr #0 but the manual seems to say it should be asr #32.
|
||||
// There is never an asr #0.
|
||||
if strings.Replace(dec.text, ", asr #0", ", asr #32", -1) == text {
|
||||
return true
|
||||
}
|
||||
|
||||
if len(dec.enc) >= 4 {
|
||||
raw := binary.LittleEndian.Uint32(dec.enc[:4])
|
||||
|
||||
// word 21FFF0B5.
|
||||
// the manual is clear that this is pre-indexed mode (with !) but libopcodes generates post-index (without !).
|
||||
if raw&0x01200000 == 0x01200000 && strings.Replace(text, "!", "", -1) == dec.text {
|
||||
return true
|
||||
}
|
||||
|
||||
// word C100543E: libopcodes says tst, but no evidence for that.
|
||||
if strings.HasPrefix(dec.text, "tst") && raw&0x0ff00000 != 0x03100000 && raw&0x0ff00000 != 0x01100000 {
|
||||
return true
|
||||
}
|
||||
|
||||
// word C3203CE8: libopcodes says teq, but no evidence for that.
|
||||
if strings.HasPrefix(dec.text, "teq") && raw&0x0ff00000 != 0x03300000 && raw&0x0ff00000 != 0x01300000 {
|
||||
return true
|
||||
}
|
||||
|
||||
// word D14C552E: libopcodes says cmp but no evidence for that.
|
||||
if strings.HasPrefix(dec.text, "cmp") && raw&0x0ff00000 != 0x03500000 && raw&0x0ff00000 != 0x01500000 {
|
||||
return true
|
||||
}
|
||||
|
||||
// word 2166AA4A: libopcodes says cmn but no evidence for that.
|
||||
if strings.HasPrefix(dec.text, "cmn") && raw&0x0ff00000 != 0x03700000 && raw&0x0ff00000 != 0x01700000 {
|
||||
return true
|
||||
}
|
||||
|
||||
// word E70AEEEF: libopcodes says str but no evidence for that.
|
||||
if strings.HasPrefix(dec.text, "str") && len(dec.text) >= 5 && (dec.text[3] == ' ' || dec.text[5] == ' ') && raw&0x0e500018 != 0x06000000 && raw&0x0e500000 != 0x0400000 {
|
||||
return true
|
||||
}
|
||||
|
||||
// word B0AF48F4: libopcodes says strd but P=0,W=1 which is unpredictable.
|
||||
if hasPrefix(dec.text, "ldr", "str") && raw&0x01200000 == 0x00200000 {
|
||||
return true
|
||||
}
|
||||
|
||||
// word B6CC1C76: libopcodes inexplicably says 'uxtab16lt r1, ip, r6, ROR #24' instead of 'uxtab16lt r1, ip, r6, ror #24'
|
||||
if strings.ToLower(dec.text) == text {
|
||||
return true
|
||||
}
|
||||
|
||||
// word F410FDA1: libopcodes says PLDW but the manual is clear that PLDW is F5/F7, not F4.
|
||||
// word F7D0FB17: libopcodes says PLDW but the manual is clear that PLDW has 0x10 clear
|
||||
if hasPrefix(dec.text, "pld") && raw&0xfd000010 != 0xf5000000 {
|
||||
return true
|
||||
}
|
||||
|
||||
// word F650FE14: libopcodes says PLI but the manual is clear that PLI has 0x10 clear
|
||||
if hasPrefix(dec.text, "pli") && raw&0xff000010 != 0xf6000000 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Instructions known to libopcodes (or xed) but not to us.
|
||||
// Most of these are floating point coprocessor instructions.
|
||||
var unsupported = strings.Fields(`
|
||||
abs
|
||||
acs
|
||||
adf
|
||||
aes
|
||||
asn
|
||||
atn
|
||||
cdp
|
||||
cf
|
||||
cmf
|
||||
cnf
|
||||
cos
|
||||
cps
|
||||
crc32
|
||||
dvf
|
||||
eret
|
||||
exp
|
||||
fadd
|
||||
fcmp
|
||||
fcpy
|
||||
fcvt
|
||||
fdiv
|
||||
fdv
|
||||
fix
|
||||
fld
|
||||
flt
|
||||
fmac
|
||||
fmd
|
||||
fml
|
||||
fmr
|
||||
fms
|
||||
fmul
|
||||
fmx
|
||||
fneg
|
||||
fnm
|
||||
frd
|
||||
fsit
|
||||
fsq
|
||||
fst
|
||||
fsu
|
||||
fto
|
||||
fui
|
||||
hlt
|
||||
hvc
|
||||
lda
|
||||
ldc
|
||||
ldf
|
||||
lfm
|
||||
lgn
|
||||
log
|
||||
mar
|
||||
mcr
|
||||
mcrr
|
||||
mia
|
||||
mnf
|
||||
mra
|
||||
mrc
|
||||
mrrc
|
||||
mrs
|
||||
msr
|
||||
msr
|
||||
muf
|
||||
mvf
|
||||
nrm
|
||||
pol
|
||||
pow
|
||||
rdf
|
||||
rfc
|
||||
rfe
|
||||
rfs
|
||||
rmf
|
||||
rnd
|
||||
rpw
|
||||
rsf
|
||||
sdiv
|
||||
sev
|
||||
sfm
|
||||
sha1
|
||||
sha256
|
||||
sin
|
||||
smc
|
||||
sqt
|
||||
srs
|
||||
stc
|
||||
stf
|
||||
stl
|
||||
suf
|
||||
tan
|
||||
udf
|
||||
udiv
|
||||
urd
|
||||
vfma
|
||||
vfms
|
||||
vfnma
|
||||
vfnms
|
||||
vrint
|
||||
wfc
|
||||
wfs
|
||||
`)
|
||||
@@ -0,0 +1,259 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Copied and simplified from ../../x86/x86asm/objdumpext_test.go.
|
||||
|
||||
package armasm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/elf"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const objdumpPath = "/usr/local/bin/arm-linux-elf-objdump"
|
||||
|
||||
func testObjdumpARM(t *testing.T, generate func(func([]byte))) {
|
||||
testObjdumpArch(t, generate, ModeARM)
|
||||
}
|
||||
|
||||
func testObjdumpArch(t *testing.T, generate func(func([]byte)), arch Mode) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping objdump test in short mode")
|
||||
}
|
||||
if _, err := os.Stat(objdumpPath); err != nil {
|
||||
t.Skip(err)
|
||||
}
|
||||
|
||||
testExtDis(t, "gnu", arch, objdump, generate, allowedMismatchObjdump)
|
||||
}
|
||||
|
||||
func objdump(ext *ExtDis) error {
|
||||
// File already written with instructions; add ELF header.
|
||||
if ext.Arch == ModeARM {
|
||||
if err := writeELF32(ext.File, ext.Size); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
panic("unknown arch")
|
||||
}
|
||||
|
||||
b, err := ext.Run(objdumpPath, "-d", "-z", ext.File.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
nmatch int
|
||||
reading bool
|
||||
next uint32 = start
|
||||
addr uint32
|
||||
encbuf [4]byte
|
||||
enc []byte
|
||||
text string
|
||||
)
|
||||
flush := func() {
|
||||
if addr == next {
|
||||
if m := pcrel.FindStringSubmatch(text); m != nil {
|
||||
targ, _ := strconv.ParseUint(m[2], 16, 64)
|
||||
text = fmt.Sprintf("%s .%+#x", m[1], int32(uint32(targ)-addr-uint32(len(enc))))
|
||||
}
|
||||
if strings.HasPrefix(text, "stmia") {
|
||||
text = "stm" + text[5:]
|
||||
}
|
||||
if strings.HasPrefix(text, "stmfd") {
|
||||
text = "stmdb" + text[5:]
|
||||
}
|
||||
if strings.HasPrefix(text, "ldmfd") {
|
||||
text = "ldm" + text[5:]
|
||||
}
|
||||
text = strings.Replace(text, "#0.0", "#0", -1)
|
||||
if text == "undefined" && len(enc) == 4 {
|
||||
text = "error: unknown instruction"
|
||||
enc = nil
|
||||
}
|
||||
if len(enc) == 4 {
|
||||
// prints as word but we want to record bytes
|
||||
enc[0], enc[3] = enc[3], enc[0]
|
||||
enc[1], enc[2] = enc[2], enc[1]
|
||||
}
|
||||
ext.Dec <- ExtInst{addr, encbuf, len(enc), text}
|
||||
encbuf = [4]byte{}
|
||||
enc = nil
|
||||
next += 4
|
||||
}
|
||||
}
|
||||
var textangle = []byte("<.text>:")
|
||||
for {
|
||||
line, err := b.ReadSlice('\n')
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return fmt.Errorf("reading objdump output: %v", err)
|
||||
}
|
||||
if bytes.Contains(line, textangle) {
|
||||
reading = true
|
||||
continue
|
||||
}
|
||||
if !reading {
|
||||
continue
|
||||
}
|
||||
if debug {
|
||||
os.Stdout.Write(line)
|
||||
}
|
||||
if enc1 := parseContinuation(line, encbuf[:len(enc)]); enc1 != nil {
|
||||
enc = enc1
|
||||
continue
|
||||
}
|
||||
flush()
|
||||
nmatch++
|
||||
addr, enc, text = parseLine(line, encbuf[:0])
|
||||
if addr > next {
|
||||
return fmt.Errorf("address out of sync expected <= %#x at %q in:\n%s", next, line, line)
|
||||
}
|
||||
}
|
||||
flush()
|
||||
if next != start+uint32(ext.Size) {
|
||||
return fmt.Errorf("not enough results found [%d %d]", next, start+ext.Size)
|
||||
}
|
||||
if err := ext.Wait(); err != nil {
|
||||
return fmt.Errorf("exec: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
undefined = []byte("<UNDEFINED>")
|
||||
unpredictable = []byte("<UNPREDICTABLE>")
|
||||
illegalShifter = []byte("<illegal shifter operand>")
|
||||
)
|
||||
|
||||
func parseLine(line []byte, encstart []byte) (addr uint32, enc []byte, text string) {
|
||||
oline := line
|
||||
i := index(line, ":\t")
|
||||
if i < 0 {
|
||||
log.Fatalf("cannot parse disassembly: %q", oline)
|
||||
}
|
||||
x, err := strconv.ParseUint(string(trimSpace(line[:i])), 16, 32)
|
||||
if err != nil {
|
||||
log.Fatalf("cannot parse disassembly: %q", oline)
|
||||
}
|
||||
addr = uint32(x)
|
||||
line = line[i+2:]
|
||||
i = bytes.IndexByte(line, '\t')
|
||||
if i < 0 {
|
||||
log.Fatalf("cannot parse disassembly: %q", oline)
|
||||
}
|
||||
enc, ok := parseHex(line[:i], encstart)
|
||||
if !ok {
|
||||
log.Fatalf("cannot parse disassembly: %q", oline)
|
||||
}
|
||||
line = trimSpace(line[i:])
|
||||
if bytes.Contains(line, undefined) {
|
||||
text = "undefined"
|
||||
return
|
||||
}
|
||||
if bytes.Contains(line, illegalShifter) {
|
||||
text = "undefined"
|
||||
return
|
||||
}
|
||||
if false && bytes.Contains(line, unpredictable) {
|
||||
text = "unpredictable"
|
||||
return
|
||||
}
|
||||
if i := bytes.IndexByte(line, ';'); i >= 0 {
|
||||
line = trimSpace(line[:i])
|
||||
}
|
||||
text = string(fixSpace(line))
|
||||
return
|
||||
}
|
||||
|
||||
func parseContinuation(line []byte, enc []byte) []byte {
|
||||
i := index(line, ":\t")
|
||||
if i < 0 {
|
||||
return nil
|
||||
}
|
||||
line = line[i+1:]
|
||||
enc, _ = parseHex(line, enc)
|
||||
return enc
|
||||
}
|
||||
|
||||
// writeELF32 writes an ELF32 header to the file,
|
||||
// describing a text segment that starts at start
|
||||
// and extends for size bytes.
|
||||
func writeELF32(f *os.File, size int) error {
|
||||
f.Seek(0, io.SeekStart)
|
||||
var hdr elf.Header32
|
||||
var prog elf.Prog32
|
||||
var sect elf.Section32
|
||||
var buf bytes.Buffer
|
||||
binary.Write(&buf, binary.LittleEndian, &hdr)
|
||||
off1 := buf.Len()
|
||||
binary.Write(&buf, binary.LittleEndian, &prog)
|
||||
off2 := buf.Len()
|
||||
binary.Write(&buf, binary.LittleEndian, §)
|
||||
off3 := buf.Len()
|
||||
buf.Reset()
|
||||
data := byte(elf.ELFDATA2LSB)
|
||||
hdr = elf.Header32{
|
||||
Ident: [16]byte{0x7F, 'E', 'L', 'F', 1, data, 1},
|
||||
Type: 2,
|
||||
Machine: uint16(elf.EM_ARM),
|
||||
Version: 1,
|
||||
Entry: start,
|
||||
Phoff: uint32(off1),
|
||||
Shoff: uint32(off2),
|
||||
Flags: 0x05000002,
|
||||
Ehsize: uint16(off1),
|
||||
Phentsize: uint16(off2 - off1),
|
||||
Phnum: 1,
|
||||
Shentsize: uint16(off3 - off2),
|
||||
Shnum: 3,
|
||||
Shstrndx: 2,
|
||||
}
|
||||
binary.Write(&buf, binary.LittleEndian, &hdr)
|
||||
prog = elf.Prog32{
|
||||
Type: 1,
|
||||
Off: start,
|
||||
Vaddr: start,
|
||||
Paddr: start,
|
||||
Filesz: uint32(size),
|
||||
Memsz: uint32(size),
|
||||
Flags: 5,
|
||||
Align: start,
|
||||
}
|
||||
binary.Write(&buf, binary.LittleEndian, &prog)
|
||||
binary.Write(&buf, binary.LittleEndian, §) // NULL section
|
||||
sect = elf.Section32{
|
||||
Name: 1,
|
||||
Type: uint32(elf.SHT_PROGBITS),
|
||||
Addr: start,
|
||||
Off: start,
|
||||
Size: uint32(size),
|
||||
Flags: uint32(elf.SHF_ALLOC | elf.SHF_EXECINSTR),
|
||||
Addralign: 4,
|
||||
}
|
||||
binary.Write(&buf, binary.LittleEndian, §) // .text
|
||||
sect = elf.Section32{
|
||||
Name: uint32(len("\x00.text\x00")),
|
||||
Type: uint32(elf.SHT_STRTAB),
|
||||
Addr: 0,
|
||||
Off: uint32(off2 + (off3-off2)*3),
|
||||
Size: uint32(len("\x00.text\x00.shstrtab\x00")),
|
||||
Addralign: 1,
|
||||
}
|
||||
binary.Write(&buf, binary.LittleEndian, §)
|
||||
buf.WriteString("\x00.text\x00.shstrtab\x00")
|
||||
f.Write(buf.Bytes())
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,401 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package armasm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GoSyntax returns the Go assembler syntax for the instruction.
|
||||
// The syntax was originally defined by Plan 9.
|
||||
// The pc is the program counter of the instruction, used for expanding
|
||||
// PC-relative addresses into absolute ones.
|
||||
// The symname function queries the symbol table for the program
|
||||
// being disassembled. Given a target address it returns the name and base
|
||||
// address of the symbol containing the target, if any; otherwise it returns "", 0.
|
||||
// The reader r should read from the text segment using text addresses
|
||||
// as offsets; it is used to display pc-relative loads as constant loads.
|
||||
func GoSyntax(inst Inst, pc uint64, symname func(uint64) (string, uint64), text io.ReaderAt) string {
|
||||
if symname == nil {
|
||||
symname = func(uint64) (string, uint64) { return "", 0 }
|
||||
}
|
||||
|
||||
var args []string
|
||||
for _, a := range inst.Args {
|
||||
if a == nil {
|
||||
break
|
||||
}
|
||||
args = append(args, plan9Arg(&inst, pc, symname, a))
|
||||
}
|
||||
|
||||
op := inst.Op.String()
|
||||
|
||||
switch inst.Op &^ 15 {
|
||||
case LDR_EQ, LDRB_EQ, LDRH_EQ, LDRSB_EQ, LDRSH_EQ, VLDR_EQ:
|
||||
// Check for RET
|
||||
reg, _ := inst.Args[0].(Reg)
|
||||
mem, _ := inst.Args[1].(Mem)
|
||||
if inst.Op&^15 == LDR_EQ && reg == R15 && mem.Base == SP && mem.Sign == 0 && mem.Mode == AddrPostIndex {
|
||||
return fmt.Sprintf("RET%s #%d", op[3:], mem.Offset)
|
||||
}
|
||||
|
||||
// Check for PC-relative load.
|
||||
if mem.Base == PC && mem.Sign == 0 && mem.Mode == AddrOffset && text != nil {
|
||||
addr := uint32(pc) + 8 + uint32(mem.Offset)
|
||||
buf := make([]byte, 8)
|
||||
switch inst.Op &^ 15 {
|
||||
case LDRB_EQ, LDRSB_EQ:
|
||||
if _, err := text.ReadAt(buf[:1], int64(addr)); err != nil {
|
||||
break
|
||||
}
|
||||
args[1] = fmt.Sprintf("$%#x", buf[0])
|
||||
|
||||
case LDRH_EQ, LDRSH_EQ:
|
||||
if _, err := text.ReadAt(buf[:2], int64(addr)); err != nil {
|
||||
break
|
||||
}
|
||||
args[1] = fmt.Sprintf("$%#x", binary.LittleEndian.Uint16(buf))
|
||||
|
||||
case LDR_EQ:
|
||||
if _, err := text.ReadAt(buf[:4], int64(addr)); err != nil {
|
||||
break
|
||||
}
|
||||
x := binary.LittleEndian.Uint32(buf)
|
||||
if s, base := symname(uint64(x)); s != "" && uint64(x) == base {
|
||||
args[1] = fmt.Sprintf("$%s(SB)", s)
|
||||
} else {
|
||||
args[1] = fmt.Sprintf("$%#x", x)
|
||||
}
|
||||
|
||||
case VLDR_EQ:
|
||||
switch {
|
||||
case strings.HasPrefix(args[0], "D"): // VLDR.F64
|
||||
if _, err := text.ReadAt(buf, int64(addr)); err != nil {
|
||||
break
|
||||
}
|
||||
args[1] = fmt.Sprintf("$%f", math.Float64frombits(binary.LittleEndian.Uint64(buf)))
|
||||
case strings.HasPrefix(args[0], "S"): // VLDR.F32
|
||||
if _, err := text.ReadAt(buf[:4], int64(addr)); err != nil {
|
||||
break
|
||||
}
|
||||
args[1] = fmt.Sprintf("$%f", math.Float32frombits(binary.LittleEndian.Uint32(buf)))
|
||||
default:
|
||||
panic(fmt.Sprintf("wrong FP register: %v", inst))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Move addressing mode into opcode suffix.
|
||||
suffix := ""
|
||||
switch inst.Op &^ 15 {
|
||||
case PLD, PLI, PLD_W:
|
||||
if mem, ok := inst.Args[0].(Mem); ok {
|
||||
args[0], suffix = memOpTrans(mem)
|
||||
} else {
|
||||
panic(fmt.Sprintf("illegal instruction: %v", inst))
|
||||
}
|
||||
case LDR_EQ, LDRB_EQ, LDRSB_EQ, LDRH_EQ, LDRSH_EQ, STR_EQ, STRB_EQ, STRH_EQ, VLDR_EQ, VSTR_EQ, LDREX_EQ, LDREXH_EQ, LDREXB_EQ:
|
||||
if mem, ok := inst.Args[1].(Mem); ok {
|
||||
args[1], suffix = memOpTrans(mem)
|
||||
} else {
|
||||
panic(fmt.Sprintf("illegal instruction: %v", inst))
|
||||
}
|
||||
case SWP_EQ, SWP_B_EQ, STREX_EQ, STREXB_EQ, STREXH_EQ:
|
||||
if mem, ok := inst.Args[2].(Mem); ok {
|
||||
args[2], suffix = memOpTrans(mem)
|
||||
} else {
|
||||
panic(fmt.Sprintf("illegal instruction: %v", inst))
|
||||
}
|
||||
}
|
||||
|
||||
// Reverse args, placing dest last.
|
||||
for i, j := 0, len(args)-1; i < j; i, j = i+1, j-1 {
|
||||
args[i], args[j] = args[j], args[i]
|
||||
}
|
||||
// For MLA-like instructions, the addend is the third operand.
|
||||
switch inst.Op &^ 15 {
|
||||
case SMLAWT_EQ, SMLAWB_EQ, MLA_EQ, MLA_S_EQ, MLS_EQ, SMMLA_EQ, SMMLS_EQ, SMLABB_EQ, SMLATB_EQ, SMLABT_EQ, SMLATT_EQ, SMLAD_EQ, SMLAD_X_EQ, SMLSD_EQ, SMLSD_X_EQ:
|
||||
args = []string{args[1], args[2], args[0], args[3]}
|
||||
}
|
||||
// For STREX like instructions, the memory operands comes first.
|
||||
switch inst.Op &^ 15 {
|
||||
case STREX_EQ, STREXB_EQ, STREXH_EQ, SWP_EQ, SWP_B_EQ:
|
||||
args = []string{args[1], args[0], args[2]}
|
||||
}
|
||||
|
||||
// special process for FP instructions
|
||||
op, args = fpTrans(&inst, op, args)
|
||||
|
||||
// LDR/STR like instructions -> MOV like
|
||||
switch inst.Op &^ 15 {
|
||||
case MOV_EQ:
|
||||
op = "MOVW" + op[3:]
|
||||
case LDR_EQ, MSR_EQ, MRS_EQ:
|
||||
op = "MOVW" + op[3:] + suffix
|
||||
case VMRS_EQ, VMSR_EQ:
|
||||
op = "MOVW" + op[4:] + suffix
|
||||
case LDRB_EQ, UXTB_EQ:
|
||||
op = "MOVBU" + op[4:] + suffix
|
||||
case LDRSB_EQ:
|
||||
op = "MOVBS" + op[5:] + suffix
|
||||
case SXTB_EQ:
|
||||
op = "MOVBS" + op[4:] + suffix
|
||||
case LDRH_EQ, UXTH_EQ:
|
||||
op = "MOVHU" + op[4:] + suffix
|
||||
case LDRSH_EQ:
|
||||
op = "MOVHS" + op[5:] + suffix
|
||||
case SXTH_EQ:
|
||||
op = "MOVHS" + op[4:] + suffix
|
||||
case STR_EQ:
|
||||
op = "MOVW" + op[3:] + suffix
|
||||
args[0], args[1] = args[1], args[0]
|
||||
case STRB_EQ:
|
||||
op = "MOVB" + op[4:] + suffix
|
||||
args[0], args[1] = args[1], args[0]
|
||||
case STRH_EQ:
|
||||
op = "MOVH" + op[4:] + suffix
|
||||
args[0], args[1] = args[1], args[0]
|
||||
case VSTR_EQ:
|
||||
args[0], args[1] = args[1], args[0]
|
||||
default:
|
||||
op = op + suffix
|
||||
}
|
||||
|
||||
if args != nil {
|
||||
op += " " + strings.Join(args, ", ")
|
||||
}
|
||||
|
||||
return op
|
||||
}
|
||||
|
||||
// assembler syntax for the various shifts.
|
||||
// @x> is a lie; the assembler uses @> 0
|
||||
// instead of @x> 1, but i wanted to be clear that it
|
||||
// was a different operation (rotate right extended, not rotate right).
|
||||
var plan9Shift = []string{"<<", ">>", "->", "@>", "@x>"}
|
||||
|
||||
func plan9Arg(inst *Inst, pc uint64, symname func(uint64) (string, uint64), arg Arg) string {
|
||||
switch a := arg.(type) {
|
||||
case Endian:
|
||||
|
||||
case Imm:
|
||||
return fmt.Sprintf("$%d", uint32(a))
|
||||
|
||||
case Mem:
|
||||
|
||||
case PCRel:
|
||||
addr := uint32(pc) + 8 + uint32(a)
|
||||
if s, base := symname(uint64(addr)); s != "" && uint64(addr) == base {
|
||||
return fmt.Sprintf("%s(SB)", s)
|
||||
}
|
||||
return fmt.Sprintf("%#x", addr)
|
||||
|
||||
case Reg:
|
||||
if a < 16 {
|
||||
return fmt.Sprintf("R%d", int(a))
|
||||
}
|
||||
|
||||
case RegList:
|
||||
var buf bytes.Buffer
|
||||
start := -2
|
||||
end := -2
|
||||
fmt.Fprintf(&buf, "[")
|
||||
flush := func() {
|
||||
if start >= 0 {
|
||||
if buf.Len() > 1 {
|
||||
fmt.Fprintf(&buf, ",")
|
||||
}
|
||||
if start == end {
|
||||
fmt.Fprintf(&buf, "R%d", start)
|
||||
} else {
|
||||
fmt.Fprintf(&buf, "R%d-R%d", start, end)
|
||||
}
|
||||
start = -2
|
||||
end = -2
|
||||
}
|
||||
}
|
||||
for i := 0; i < 16; i++ {
|
||||
if a&(1<<uint(i)) != 0 {
|
||||
if i == end+1 {
|
||||
end++
|
||||
continue
|
||||
}
|
||||
start = i
|
||||
end = i
|
||||
} else {
|
||||
flush()
|
||||
}
|
||||
}
|
||||
flush()
|
||||
fmt.Fprintf(&buf, "]")
|
||||
return buf.String()
|
||||
|
||||
case RegShift:
|
||||
return fmt.Sprintf("R%d%s$%d", int(a.Reg), plan9Shift[a.Shift], int(a.Count))
|
||||
|
||||
case RegShiftReg:
|
||||
return fmt.Sprintf("R%d%sR%d", int(a.Reg), plan9Shift[a.Shift], int(a.RegCount))
|
||||
}
|
||||
return strings.ToUpper(arg.String())
|
||||
}
|
||||
|
||||
// convert memory operand from GNU syntax to Plan 9 syntax, for example,
|
||||
// [r5] -> (R5)
|
||||
// [r6, #4080] -> 0xff0(R6)
|
||||
// [r2, r0, ror #1] -> (R2)(R0@>1)
|
||||
// inst [r2, -r0, ror #1] -> INST.U (R2)(R0@>1)
|
||||
// input:
|
||||
//
|
||||
// a memory operand
|
||||
//
|
||||
// return values:
|
||||
//
|
||||
// corresponding memory operand in Plan 9 syntax
|
||||
// .W/.P/.U suffix
|
||||
func memOpTrans(mem Mem) (string, string) {
|
||||
suffix := ""
|
||||
switch mem.Mode {
|
||||
case AddrOffset, AddrLDM:
|
||||
// no suffix
|
||||
case AddrPreIndex, AddrLDM_WB:
|
||||
suffix = ".W"
|
||||
case AddrPostIndex:
|
||||
suffix = ".P"
|
||||
}
|
||||
off := ""
|
||||
if mem.Offset != 0 {
|
||||
off = fmt.Sprintf("%#x", mem.Offset)
|
||||
}
|
||||
base := fmt.Sprintf("(R%d)", int(mem.Base))
|
||||
index := ""
|
||||
if mem.Sign != 0 {
|
||||
sign := ""
|
||||
if mem.Sign < 0 {
|
||||
suffix += ".U"
|
||||
}
|
||||
shift := ""
|
||||
if mem.Count != 0 {
|
||||
shift = fmt.Sprintf("%s%d", plan9Shift[mem.Shift], mem.Count)
|
||||
}
|
||||
index = fmt.Sprintf("(%sR%d%s)", sign, int(mem.Index), shift)
|
||||
}
|
||||
return off + base + index, suffix
|
||||
}
|
||||
|
||||
type goFPInfo struct {
|
||||
op Op
|
||||
transArgs []int // indexes of arguments which need transformation
|
||||
gnuName string // instruction name in GNU syntax
|
||||
goName string // instruction name in Plan 9 syntax
|
||||
}
|
||||
|
||||
var fpInst []goFPInfo = []goFPInfo{
|
||||
{VADD_EQ_F32, []int{2, 1, 0}, "VADD", "ADDF"},
|
||||
{VADD_EQ_F64, []int{2, 1, 0}, "VADD", "ADDD"},
|
||||
{VSUB_EQ_F32, []int{2, 1, 0}, "VSUB", "SUBF"},
|
||||
{VSUB_EQ_F64, []int{2, 1, 0}, "VSUB", "SUBD"},
|
||||
{VMUL_EQ_F32, []int{2, 1, 0}, "VMUL", "MULF"},
|
||||
{VMUL_EQ_F64, []int{2, 1, 0}, "VMUL", "MULD"},
|
||||
{VNMUL_EQ_F32, []int{2, 1, 0}, "VNMUL", "NMULF"},
|
||||
{VNMUL_EQ_F64, []int{2, 1, 0}, "VNMUL", "NMULD"},
|
||||
{VMLA_EQ_F32, []int{2, 1, 0}, "VMLA", "MULAF"},
|
||||
{VMLA_EQ_F64, []int{2, 1, 0}, "VMLA", "MULAD"},
|
||||
{VMLS_EQ_F32, []int{2, 1, 0}, "VMLS", "MULSF"},
|
||||
{VMLS_EQ_F64, []int{2, 1, 0}, "VMLS", "MULSD"},
|
||||
{VNMLA_EQ_F32, []int{2, 1, 0}, "VNMLA", "NMULAF"},
|
||||
{VNMLA_EQ_F64, []int{2, 1, 0}, "VNMLA", "NMULAD"},
|
||||
{VNMLS_EQ_F32, []int{2, 1, 0}, "VNMLS", "NMULSF"},
|
||||
{VNMLS_EQ_F64, []int{2, 1, 0}, "VNMLS", "NMULSD"},
|
||||
{VDIV_EQ_F32, []int{2, 1, 0}, "VDIV", "DIVF"},
|
||||
{VDIV_EQ_F64, []int{2, 1, 0}, "VDIV", "DIVD"},
|
||||
{VNEG_EQ_F32, []int{1, 0}, "VNEG", "NEGF"},
|
||||
{VNEG_EQ_F64, []int{1, 0}, "VNEG", "NEGD"},
|
||||
{VABS_EQ_F32, []int{1, 0}, "VABS", "ABSF"},
|
||||
{VABS_EQ_F64, []int{1, 0}, "VABS", "ABSD"},
|
||||
{VSQRT_EQ_F32, []int{1, 0}, "VSQRT", "SQRTF"},
|
||||
{VSQRT_EQ_F64, []int{1, 0}, "VSQRT", "SQRTD"},
|
||||
{VCMP_EQ_F32, []int{1, 0}, "VCMP", "CMPF"},
|
||||
{VCMP_EQ_F64, []int{1, 0}, "VCMP", "CMPD"},
|
||||
{VCMP_E_EQ_F32, []int{1, 0}, "VCMP.E", "CMPF"},
|
||||
{VCMP_E_EQ_F64, []int{1, 0}, "VCMP.E", "CMPD"},
|
||||
{VLDR_EQ, []int{1}, "VLDR", "MOV"},
|
||||
{VSTR_EQ, []int{1}, "VSTR", "MOV"},
|
||||
{VMOV_EQ_F32, []int{1, 0}, "VMOV", "MOVF"},
|
||||
{VMOV_EQ_F64, []int{1, 0}, "VMOV", "MOVD"},
|
||||
{VMOV_EQ_32, []int{1, 0}, "VMOV", "MOVW"},
|
||||
{VMOV_EQ, []int{1, 0}, "VMOV", "MOVW"},
|
||||
{VCVT_EQ_F64_F32, []int{1, 0}, "VCVT", "MOVFD"},
|
||||
{VCVT_EQ_F32_F64, []int{1, 0}, "VCVT", "MOVDF"},
|
||||
{VCVT_EQ_F32_U32, []int{1, 0}, "VCVT", "MOVWF.U"},
|
||||
{VCVT_EQ_F32_S32, []int{1, 0}, "VCVT", "MOVWF"},
|
||||
{VCVT_EQ_S32_F32, []int{1, 0}, "VCVT", "MOVFW"},
|
||||
{VCVT_EQ_U32_F32, []int{1, 0}, "VCVT", "MOVFW.U"},
|
||||
{VCVT_EQ_F64_U32, []int{1, 0}, "VCVT", "MOVWD.U"},
|
||||
{VCVT_EQ_F64_S32, []int{1, 0}, "VCVT", "MOVWD"},
|
||||
{VCVT_EQ_S32_F64, []int{1, 0}, "VCVT", "MOVDW"},
|
||||
{VCVT_EQ_U32_F64, []int{1, 0}, "VCVT", "MOVDW.U"},
|
||||
}
|
||||
|
||||
// convert FP instructions from GNU syntax to Plan 9 syntax, for example,
|
||||
// vadd.f32 s0, s3, s4 -> ADDF F0, S3, F2
|
||||
// vsub.f64 d0, d2, d4 -> SUBD F0, F2, F4
|
||||
// vldr s2, [r11] -> MOVF (R11), F1
|
||||
// inputs: instruction name and arguments in GNU syntax
|
||||
// return values: corresponding instruction name and arguments in Plan 9 syntax
|
||||
func fpTrans(inst *Inst, op string, args []string) (string, []string) {
|
||||
for _, fp := range fpInst {
|
||||
if inst.Op&^15 == fp.op {
|
||||
// remove gnu syntax suffixes
|
||||
op = strings.Replace(op, ".F32", "", -1)
|
||||
op = strings.Replace(op, ".F64", "", -1)
|
||||
op = strings.Replace(op, ".S32", "", -1)
|
||||
op = strings.Replace(op, ".U32", "", -1)
|
||||
op = strings.Replace(op, ".32", "", -1)
|
||||
// compose op name
|
||||
if fp.op == VLDR_EQ || fp.op == VSTR_EQ {
|
||||
switch {
|
||||
case strings.HasPrefix(args[fp.transArgs[0]], "D"):
|
||||
op = "MOVD" + op[len(fp.gnuName):]
|
||||
case strings.HasPrefix(args[fp.transArgs[0]], "S"):
|
||||
op = "MOVF" + op[len(fp.gnuName):]
|
||||
default:
|
||||
panic(fmt.Sprintf("wrong FP register: %v", inst))
|
||||
}
|
||||
} else {
|
||||
op = fp.goName + op[len(fp.gnuName):]
|
||||
}
|
||||
// transform registers
|
||||
for ix, ri := range fp.transArgs {
|
||||
switch {
|
||||
case strings.HasSuffix(args[ri], "[1]"): // MOVW Rx, Dy[1]
|
||||
break
|
||||
case strings.HasSuffix(args[ri], "[0]"): // Dx[0] -> Fx
|
||||
args[ri] = strings.Replace(args[ri], "[0]", "", -1)
|
||||
fallthrough
|
||||
case strings.HasPrefix(args[ri], "D"): // Dx -> Fx
|
||||
args[ri] = "F" + args[ri][1:]
|
||||
case strings.HasPrefix(args[ri], "S"):
|
||||
if inst.Args[ix].(Reg)&1 == 0 { // Sx -> Fy, y = x/2, if x is even
|
||||
args[ri] = fmt.Sprintf("F%d", (inst.Args[ix].(Reg)-S0)/2)
|
||||
}
|
||||
case strings.HasPrefix(args[ri], "$"): // CMPF/CMPD $0, Fx
|
||||
break
|
||||
case strings.HasPrefix(args[ri], "R"): // MOVW Rx, Dy[1]
|
||||
break
|
||||
default:
|
||||
panic(fmt.Sprintf("wrong FP register: %v", inst))
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return op, args
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
||||
newdecode.txt:
|
||||
cd ..; go test -cover -run 'ObjdumpARMCond' -v -timeout 10h -printtests -long 2>&1 | tee log
|
||||
cd ..; go test -cover -run 'ObjdumpARMUncond' -v -timeout 10h -printtests -long 2>&1 | tee -a log
|
||||
egrep ' (gnu|plan9) ' ../log |sort >newdecode.txt
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,757 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Armmap constructs the ARM opcode map from the instruction set CSV file.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// armmap [-fmt=format] arm.csv
|
||||
//
|
||||
// The known output formats are:
|
||||
//
|
||||
// text (default) - print decoding tree in text form
|
||||
// decoder - print decoding tables for the armasm package
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/csv"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var format = flag.String("fmt", "text", "output format: text, decoder")
|
||||
|
||||
var inputFile string
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintf(os.Stderr, "usage: armmap [-fmt=format] x86.csv\n")
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.SetFlags(0)
|
||||
log.SetPrefix("armmap: ")
|
||||
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
if flag.NArg() != 1 {
|
||||
usage()
|
||||
}
|
||||
|
||||
inputFile = flag.Arg(0)
|
||||
|
||||
var print func(*Prog)
|
||||
switch *format {
|
||||
default:
|
||||
log.Fatalf("unknown output format %q", *format)
|
||||
case "text":
|
||||
print = printText
|
||||
case "decoder":
|
||||
print = printDecoder
|
||||
}
|
||||
|
||||
p, err := readCSV(flag.Arg(0))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
print(p)
|
||||
}
|
||||
|
||||
// readCSV reads the CSV file and returns the corresponding Prog.
|
||||
// It may print details about problems to standard error using the log package.
|
||||
func readCSV(file string) (*Prog, error) {
|
||||
// Read input.
|
||||
// Skip leading blank and # comment lines.
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b := bufio.NewReader(f)
|
||||
for {
|
||||
c, err := b.ReadByte()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if c == '\n' {
|
||||
continue
|
||||
}
|
||||
if c == '#' {
|
||||
b.ReadBytes('\n')
|
||||
continue
|
||||
}
|
||||
b.UnreadByte()
|
||||
break
|
||||
}
|
||||
table, err := csv.NewReader(b).ReadAll()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing %s: %v", file, err)
|
||||
}
|
||||
if len(table) == 0 {
|
||||
return nil, fmt.Errorf("empty csv input")
|
||||
}
|
||||
if len(table[0]) < 5 {
|
||||
return nil, fmt.Errorf("csv too narrow: need at least five columns")
|
||||
}
|
||||
|
||||
p := &Prog{}
|
||||
for _, row := range table {
|
||||
add(p, row[0], row[1], row[2], row[3], row[4])
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
type Prog struct {
|
||||
Inst []Inst
|
||||
OpRanges map[string]string
|
||||
}
|
||||
|
||||
type Inst struct {
|
||||
Text string
|
||||
Encoding string
|
||||
Mask uint32
|
||||
Value uint32
|
||||
Priority int
|
||||
OpBase string
|
||||
OpBits uint64
|
||||
Args []string
|
||||
}
|
||||
|
||||
type Arg struct {
|
||||
Name string
|
||||
Bits uint64
|
||||
}
|
||||
|
||||
// add adds the entry from the CSV described by maskstr, valuestr, text, encoding, tags
|
||||
// to the program p.
|
||||
func add(p *Prog, maskstr, valuestr, text, encoding, tags string) {
|
||||
if strings.Contains(tags, "pseudo") {
|
||||
return
|
||||
}
|
||||
|
||||
// For now, ignore the VFP floating point instructions.
|
||||
if strings.HasPrefix(text, "V") && !strings.Contains(tags, "vfp") {
|
||||
// TODO
|
||||
return
|
||||
}
|
||||
|
||||
mask, err := strconv.ParseUint(maskstr, 0, 32)
|
||||
if err != nil {
|
||||
log.Printf("invalid mask %q", maskstr)
|
||||
return
|
||||
}
|
||||
value, err := strconv.ParseUint(valuestr, 0, 32)
|
||||
if err != nil {
|
||||
log.Printf("invalid value %q", valuestr)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse encoding, building size and offset of each field.
|
||||
// The first field in the encoding is the largest offset.
|
||||
fuzzy := uint32(0) // mask of 'should be' bits
|
||||
fieldOffset := map[string]int{}
|
||||
fieldWidth := map[string]int{}
|
||||
off := 32
|
||||
for _, f := range strings.Split(encoding, "|") {
|
||||
n := 1
|
||||
if i := strings.Index(f, ":"); i >= 0 {
|
||||
n, _ = strconv.Atoi(f[i+1:])
|
||||
}
|
||||
off -= n
|
||||
fieldOffset[f] = off
|
||||
fieldWidth[f] = n
|
||||
if f == "(0)" || f == "(1)" {
|
||||
fuzzy |= 1 << uint(off)
|
||||
}
|
||||
}
|
||||
if off != 0 {
|
||||
fmt.Fprintf(os.Stderr, "%s: counted %d bits in %s\n", text, 32-off, encoding)
|
||||
}
|
||||
|
||||
// Track which encoding fields we found uses for.
|
||||
// If we do not find a use for a field, that's an error in the input tables.
|
||||
fieldUsed := map[string]bool{}
|
||||
|
||||
// Split text into opcode and arguments.
|
||||
var op, argstr string
|
||||
if i := strings.Index(text, " "); i >= 0 {
|
||||
op = text[:i]
|
||||
argstr = text[i:]
|
||||
} else {
|
||||
op = text
|
||||
}
|
||||
op = strings.TrimSpace(op)
|
||||
argstr = strings.TrimSpace(argstr)
|
||||
|
||||
// Parse opcode suffixes.
|
||||
i := strings.Index(op, "<")
|
||||
if i < 0 {
|
||||
i = len(op)
|
||||
}
|
||||
if j := strings.Index(op, "{"); j >= 0 && j < i {
|
||||
i = j
|
||||
}
|
||||
op, suffix := op[:i], op[i:]
|
||||
if suffix != "" && opSuffix[suffix] == "" {
|
||||
fmt.Fprintf(os.Stderr, "%s: invalid op suffix %q in %s\n", text, suffix, op+suffix)
|
||||
}
|
||||
|
||||
// Make sure fields needed by opcode suffix are available.
|
||||
for _, f := range strings.Split(opSuffix[suffix], ",") {
|
||||
if f != "" && fieldWidth[f] == 0 {
|
||||
fmt.Fprintf(os.Stderr, "%s: opsuffix %s missing %s in encoding %s\n", text, suffix, f, encoding)
|
||||
}
|
||||
fieldUsed[f] = true
|
||||
}
|
||||
|
||||
// Build list of opcodes that can be generated by this suffix.
|
||||
// For example, the opcodes generated by ADD<c> are ADD.EQ, ADD.NE, etc.
|
||||
// To simplify the decoding of instruction opcodes, we arrange that this
|
||||
// sequence aligns with the encoding, so that decoding amounts to extracting
|
||||
// the right bits, concatenating them, and adding them to the first opcode in
|
||||
// the sequence. If the condition code is present, we always place it in the
|
||||
// low order bits, so that x&^15 == FOO_EQ tests whether x is any of the
|
||||
// conditional FOO instructions.
|
||||
ops := []string{op}
|
||||
opBits := uint64(0) // record of bits to extract and add to opcode base
|
||||
opFields := strings.Split(opSuffix[suffix], ",")
|
||||
// First the optional elements, like {S} meaning "" or ".S".
|
||||
for strings.HasPrefix(suffix, "{") {
|
||||
i := strings.Index(suffix, "}")
|
||||
var f, option string
|
||||
option, suffix = suffix[1:i], suffix[i+1:]
|
||||
f, opFields = opFields[0], opFields[1:]
|
||||
if option == "W" {
|
||||
// The {W} option on PLD{W} uses the R bit which is !W.
|
||||
ops = cross(ops, "."+option, "")
|
||||
} else {
|
||||
ops = cross(ops, "", "."+option)
|
||||
}
|
||||
if fieldWidth[f] != 1 {
|
||||
fmt.Fprintf(os.Stderr, "%s: have %d bits for {%s}\n", text, fieldWidth[f], option)
|
||||
}
|
||||
// opBits is a sequence of 16-bit chunks describing contiguous bit sections.
|
||||
// Each chunk is 8-bit offset followed by 8-bit size.
|
||||
opBits = opBits<<16 | uint64(fieldOffset[f])<<8 | 1
|
||||
}
|
||||
// Then the true field substitutions.
|
||||
haveCond := false
|
||||
for strings.Contains(suffix, "<") {
|
||||
var f, literal, x string
|
||||
if len(opFields) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "%s: ran out of suffix fields for <%s>\n", text, x)
|
||||
break
|
||||
}
|
||||
f, opFields = opFields[0], opFields[1:]
|
||||
i := strings.Index(suffix, "<")
|
||||
j := strings.Index(suffix, ">")
|
||||
literal, x, suffix = suffix[:i], suffix[i+1:j], suffix[j+1:]
|
||||
|
||||
// Add leading literal text to all opcodes.
|
||||
ops = cross(ops, literal)
|
||||
|
||||
// The <c> condition can happen anywhere in the opcode text
|
||||
// but we want to generate the actual variation in the low bits
|
||||
// of the list index. Remember when and where we've seen <c> and apply
|
||||
// it after the loop has finished.
|
||||
if x == "c" && f == "cond:4" {
|
||||
haveCond = true
|
||||
ops = cross(ops, "_COND_")
|
||||
continue
|
||||
}
|
||||
|
||||
// Otherwise, choices[x] lists the possible expansions of <x>.
|
||||
// If <x> is of the form <A,B,C> the choices are A, B, and C.
|
||||
expand := choices[x]
|
||||
if expand == nil && strings.Contains(x, ",") {
|
||||
expand = strings.Split(x, ",")
|
||||
}
|
||||
if expand == nil {
|
||||
fmt.Fprintf(os.Stderr, "%s: unknown choices for <%s>\n", text, x)
|
||||
expand = []string{x}
|
||||
} else if len(expand) != 1<<uint(fieldWidth[f]) {
|
||||
fmt.Fprintf(os.Stderr, "%s: have %d choices for <%s> but %d bits\n", text, len(expand), x, fieldWidth[f])
|
||||
}
|
||||
opBits = opBits<<16 | uint64(fieldOffset[f])<<8 | uint64(fieldWidth[f])
|
||||
ops = cross(ops, expand...)
|
||||
}
|
||||
if haveCond {
|
||||
// Apply condtional suffix last.
|
||||
opBits = opBits<<16 | 28<<8 | 4
|
||||
ops = crossCond(ops)
|
||||
}
|
||||
ops = cross(ops, suffix)
|
||||
|
||||
// Now ops is a list of opcodes generated by this opcode pattern.
|
||||
// We want to make sure that we can arrange for those opcodes to
|
||||
// happen consecutively in the final opcode numbering.
|
||||
// Record in p.OpRanges[op] the required consecutive sequence of
|
||||
// opcode that includes op. To make searches easier, we record
|
||||
// the sequence as a comma-separated list of strings with commas
|
||||
// on both ends: [A, B] encodes as ",A,B,".
|
||||
if p.OpRanges == nil {
|
||||
p.OpRanges = make(map[string]string)
|
||||
}
|
||||
opstr := "," + strings.Join(ops, ",") + ","
|
||||
for _, op := range ops {
|
||||
if old := p.OpRanges[op]; old != "" && old != opstr {
|
||||
if strings.Contains(old, opstr) {
|
||||
opstr = old
|
||||
} else if strings.Contains(opstr, old) {
|
||||
// great, do nothing
|
||||
} else {
|
||||
// It would also be okay if there is some subsequence s such that
|
||||
// old = x+s and opstr = s+y (or vice versa), in which case we should
|
||||
// record opstr = x+s+y. However, this has not come up in practice.
|
||||
// Failing that, we can't satisfy the sequencing requirements.
|
||||
fmt.Fprintf(os.Stderr, "%s: %s appears in both %s and %s\n", text, op, old, opstr)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, op := range strings.Split(opstr, ",") {
|
||||
if op != "" {
|
||||
p.OpRanges[op] = opstr
|
||||
}
|
||||
}
|
||||
|
||||
// Process the arguments, building a list of argument descriptions.
|
||||
// Each argument description has the form <argument>|field@off|field@off...
|
||||
// where the |field@off suffixes give the name and location of the fields
|
||||
// needed by the argument. Each such string maps to a different decoding
|
||||
// type in the generated table, according to the argOps map.
|
||||
var args []string
|
||||
for argstr != "" {
|
||||
// Find longest match among argSuffixes pieces.
|
||||
best := 0
|
||||
for a := range argSuffixes {
|
||||
if argstr == a || strings.HasPrefix(argstr, a+",") {
|
||||
if best < len(a) {
|
||||
best = len(a)
|
||||
}
|
||||
}
|
||||
}
|
||||
if best == 0 {
|
||||
fmt.Fprintf(os.Stderr, "%s: unknown arg %s\n", text, argstr)
|
||||
break
|
||||
}
|
||||
|
||||
var arg, desc string
|
||||
arg, argstr = argstr[:best], strings.TrimSpace(strings.TrimLeft(argstr[best:], ","))
|
||||
desc = arg
|
||||
for _, f := range strings.Split(argSuffixes[desc], ",") {
|
||||
if f == "" {
|
||||
continue
|
||||
}
|
||||
if fieldWidth[f] == 0 {
|
||||
fmt.Fprintf(os.Stderr, "%s: arg %s missing %s in encoding %s\n", text, arg, f, encoding)
|
||||
}
|
||||
fieldUsed[f] = true
|
||||
desc += fmt.Sprintf("|%s@%d", f, fieldOffset[f])
|
||||
}
|
||||
args = append(args, desc)
|
||||
}
|
||||
|
||||
// Check that all encoding fields were used by suffix or argument decoding.
|
||||
for f := range fieldWidth {
|
||||
switch f {
|
||||
case "0", "1", "(0)", "(1)":
|
||||
// ok
|
||||
default:
|
||||
if !fieldUsed[f] {
|
||||
fmt.Fprintf(os.Stderr, "%s: encoding field %s not used in %s\n", text, f, encoding)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine decoding priority. Instructions that say 'SEE X' in the tag
|
||||
// are considered lower priority than ones that don't. In theory the
|
||||
// structure described by the SEE tags might be richer than that, but
|
||||
// in practice it only has those two levels.
|
||||
// We leave space for two more priorities according to whether the
|
||||
// fuzzy bits are set correctly. The full set of priorities then is:
|
||||
//
|
||||
// 4 - no SEE tag, fuzzy bits all match
|
||||
// 3 - no SEE tag, some fuzzy bits don't match
|
||||
// 2 - SEE tag, fuzzy bits all match
|
||||
// 1 - SEE tag, some fuzzy bits don't match
|
||||
//
|
||||
// You could argue for swapping the middle two levels but so far
|
||||
// it has not been an issue.
|
||||
pri := 4
|
||||
if strings.Contains(tags, "SEE") {
|
||||
pri = 2
|
||||
}
|
||||
|
||||
inst := Inst{
|
||||
Text: text,
|
||||
Encoding: encoding,
|
||||
Mask: uint32(mask),
|
||||
Value: uint32(value),
|
||||
Priority: pri,
|
||||
OpBase: ops[0],
|
||||
OpBits: opBits,
|
||||
Args: args,
|
||||
}
|
||||
p.Inst = append(p.Inst, inst)
|
||||
|
||||
if fuzzy != 0 {
|
||||
inst.Mask &^= fuzzy
|
||||
inst.Priority--
|
||||
p.Inst = append(p.Inst, inst)
|
||||
}
|
||||
}
|
||||
|
||||
// opSuffix describes the encoding fields used to resolve a given opcode suffix.
|
||||
var opSuffix = map[string]string{
|
||||
"<ADD,SUB>": "op",
|
||||
"<BIF,BIT,BSL>": "op:2",
|
||||
"<MLA,MLS><c>.F<32,64>": "op,cond:4,sz",
|
||||
"<MLS,MLA><c>.F<32,64>": "op,cond:4,sz",
|
||||
"<BT,TB><c>": "tb,cond:4",
|
||||
"<TBL,TBX>.8": "op",
|
||||
"<c>": "cond:4",
|
||||
"<c>.32": "cond:4",
|
||||
"<c>.F<32,64>": "cond:4,sz",
|
||||
"<x><y><c>": "N,M,cond:4",
|
||||
"<y><c>": "M,cond:4",
|
||||
"{B}<c>": "B,cond:4",
|
||||
"{E}<c>.F<32,64>": "E,cond:4,sz",
|
||||
"{R}<c>": "R,cond:4",
|
||||
"<c>.F<32,64>.<U,S>32": "cond:4,sz,op",
|
||||
"<R,><c>.<U,S>32.F<32,64>": "op,cond:4,signed,sz",
|
||||
"{S}<c>": "S,cond:4",
|
||||
"{W}": "R",
|
||||
"{X}<c>": "M,cond:4",
|
||||
"<B,T><c>.<F32.F16,F16.F32>": "T,cond:4,op",
|
||||
"<c>.<F64.F32,F32.F64>": "cond:4,sz",
|
||||
"<c>.FX<S,U><16,32>.F<32,64>": "cond:4,U,sx,sz",
|
||||
"<c>.F<32,64>.FX<S,U><16,32>": "cond:4,sz,U,sx",
|
||||
}
|
||||
|
||||
// choices[x] describes the choices for filling in "<"+x+">" in an opcode suffix.
|
||||
// Opcodes that end up containing ZZ take up a numeric sequence value but are
|
||||
// not exported in the package API.
|
||||
var choices = map[string][]string{
|
||||
"c": {".EQ", ".NE", ".CS", ".CC", ".MI", ".PL", ".VS", ".VC", ".HI", ".LS", ".GE", ".LT", ".GT", ".LE", "", ".ZZ"},
|
||||
"x": {"B", "T"},
|
||||
"y": {"B", "T"},
|
||||
}
|
||||
|
||||
// argOps maps from argument descriptions to internal decoder name.
|
||||
var argOps = map[string]string{
|
||||
// 4-bit register encodings
|
||||
"<Rm>|Rm:4@0": "arg_R_0",
|
||||
"<Rn>|Rn:4@0": "arg_R_0",
|
||||
"<Rt>|Rt:4@0": "arg_R_0",
|
||||
"<Rm>|Rm:4@8": "arg_R_8",
|
||||
"<Ra>|Ra:4@12": "arg_R_12",
|
||||
"<Rd>|Rd:4@12": "arg_R_12",
|
||||
"<RdLo>|RdLo:4@12": "arg_R_12",
|
||||
"<Rt>|Rt:4@12": "arg_R_12",
|
||||
"<Rt_nzcv>|Rt:4@12": "arg_R_12_nzcv",
|
||||
"<Rd>|Rd:4@16": "arg_R_16",
|
||||
"<RdHi>|RdHi:4@16": "arg_R_16",
|
||||
"<Rn>|Rn:4@16": "arg_R_16",
|
||||
|
||||
// first and second of consecutive register pair
|
||||
"<Rt1>|Rt:4@0": "arg_R1_0",
|
||||
"<Rt1>|Rt:4@12": "arg_R1_12",
|
||||
"<Rt2>|Rt:4@0": "arg_R2_0",
|
||||
"<Rt2>|Rt:4@12": "arg_R2_12",
|
||||
|
||||
// register arithmetic
|
||||
"<Rm>,<type> <Rs>|Rm:4@0|Rs:4@8|type:2@5": "arg_R_shift_R",
|
||||
"<Rm>{,<shift>}|Rm:4@0|imm5:5@7|type:2@5": "arg_R_shift_imm",
|
||||
"<Rn>{,<shift>}|Rn:4@0|imm5:5@7|sh@6": "arg_R_shift_imm",
|
||||
"<Rm>{,LSL #<imm5>}|Rm:4@0|imm5:5@7": "arg_R_shift_imm",
|
||||
"<Rm>{,<rotation>}|Rm:4@0|rotate:2@10": "arg_R_rotate",
|
||||
|
||||
// memory references
|
||||
"<Rn>{!}|Rn:4@16|W@21": "arg_R_16_WB",
|
||||
"[<Rn>]|Rn:4@16": "arg_mem_R",
|
||||
"[<Rn>,+/-<Rm>{, <shift>}]{!}|Rn:4@16|U@23|Rm:4@0|type:2@5|imm5:5@7|P@24|W@21": "arg_mem_R_pm_R_shift_imm_W",
|
||||
"[<Rn>{,#+/-<imm8>}]{!}|Rn:4@16|P@24|U@23|W@21|imm4H:4@8|imm4L:4@0": "arg_mem_R_pm_imm8_W",
|
||||
"[<Rn>] {,#+/-<imm8>}|Rn:4@16|U@23|imm4H:4@8|imm4L:4@0": "arg_mem_R_pm_imm8_postindex",
|
||||
"[<Rn>{,#+/-<imm12>}]{!}|Rn:4@16|P@24|U@23|W@21|imm12:12@0": "arg_mem_R_pm_imm12_W",
|
||||
"[<Rn>],#+/-<imm12>|Rn:4@16|imm12:12@0|U@23": "arg_mem_R_pm_imm12_postindex",
|
||||
"[<Rn>,#+/-<imm12>]|Rn:4@16|U@23|imm12:12@0": "arg_mem_R_pm_imm12_offset",
|
||||
"[<Rn>] {,#+/-<imm12>}|Rn:4@16|U@23|imm12:12@0": "arg_mem_R_pm_imm12_postindex",
|
||||
"[<Rn>], +/-<Rm>|Rn:4@16|U@23|Rm:4@0": "arg_mem_R_pm_R_postindex",
|
||||
"[<Rn>,+/-<Rm>]{!}|Rn:4@16|U@23|Rm:4@0|P@24|W@21": "arg_mem_R_pm_R_W",
|
||||
"[<Rn>],+/-<Rm>{, <shift>}|Rn:4@16|Rm:4@0|imm5:5@7|type:2@5|U@23": "arg_mem_R_pm_R_shift_imm_postindex",
|
||||
"[<Rn>,+/-<Rm>{, <shift>}]|Rn:4@16|U@23|Rm:4@0|type:2@5|imm5:5@7": "arg_mem_R_pm_R_shift_imm_offset",
|
||||
"[<Rn>{,#+/-<imm8>}]|Rn:4@16|U@23|imm8:8@0": "arg_mem_R_pm_imm8at0_offset",
|
||||
|
||||
// pc-relative constants
|
||||
"<label+12>|imm12:12@0": "arg_label_p_12",
|
||||
"<label-12>|imm12:12@0": "arg_label_m_12",
|
||||
"<label+/-12>|imm12:12@0|U@23": "arg_label_pm_12",
|
||||
"<label+/-4+4>|imm4H:4@8|imm4L:4@0|U@23": "arg_label_pm_4_4",
|
||||
|
||||
// constants
|
||||
"#<const>|imm12:12@0": "arg_const",
|
||||
"#<imm5>|imm5:5@7": "arg_imm5",
|
||||
"#<imm5_nz>|imm5:5@7": "arg_imm5_nz",
|
||||
"#<imm5_32>|imm5:5@7": "arg_imm5_32",
|
||||
"<label24>|imm24:24@0": "arg_label24",
|
||||
"#<lsb>|lsb:5@7": "arg_imm5",
|
||||
"#<width>|lsb:5@7|msb:5@16": "arg_lsb_width",
|
||||
"#<imm12+4>|imm12:12@8|imm4:4@0": "arg_imm_12at8_4at0",
|
||||
"#<imm12+4>|imm12:12@0|imm4:4@16": "arg_imm_4at16_12at0",
|
||||
"<label24H>|imm24:24@0|H@24": "arg_label24H",
|
||||
"#<option>|option:4@0": "arg_option",
|
||||
"#<widthm1>|widthm1:5@16": "arg_widthm1",
|
||||
"#<sat_imm4>|sat_imm:4@16": "arg_satimm4",
|
||||
"#<sat_imm5>|sat_imm:5@16": "arg_satimm5",
|
||||
"#<sat_imm4m1>|sat_imm:4@16": "arg_satimm4m1",
|
||||
"#<sat_imm5m1>|sat_imm:5@16": "arg_satimm5m1",
|
||||
"#<imm24>|imm24:24@0": "arg_imm24",
|
||||
|
||||
// special
|
||||
"<registers>|register_list:16@0": "arg_registers",
|
||||
"<registers2>|register_list:16@0": "arg_registers2",
|
||||
"<registers1>|Rt:4@12": "arg_registers1",
|
||||
"<endian_specifier>|E@9": "arg_endian",
|
||||
|
||||
"SP": "arg_SP",
|
||||
"APSR": "arg_APSR",
|
||||
"FPSCR": "arg_FPSCR",
|
||||
|
||||
// VFP floating point registers
|
||||
"<Sd>|Vd:4@12|D@22": "arg_Sd",
|
||||
"<Sd,Dd>|Vd:4@12|D@22|sz@8": "arg_Sd_Dd",
|
||||
"<Dd,Sd>|Vd:4@12|D@22|sz@8": "arg_Dd_Sd",
|
||||
"<Sn>|Vn:4@16|N@7": "arg_Sn",
|
||||
"<Sn,Dn>|Vn:4@16|N@7|sz@8": "arg_Sn_Dn",
|
||||
"<Sm>|Vm:4@0|M@5": "arg_Sm",
|
||||
"<Sm,Dm>|Vm:4@0|M@5|sz@8": "arg_Sm_Dm",
|
||||
"#0.0": "arg_fp_0",
|
||||
"#<imm_vfp>|imm4H:4@16|imm4L:4@0|sz@8": "arg_imm_vfp",
|
||||
"#<fbits>|sx@7|imm4:4@0|i@5": "arg_fbits",
|
||||
"<Dn[x]>|N@7|Vn:4@16|opc1@21": "arg_Dn_half",
|
||||
"<Dd[x]>|D@7|Vd:4@16|opc1@21": "arg_Dn_half",
|
||||
}
|
||||
|
||||
// argSuffixes describes the encoding fields needed for a particular suffix.
|
||||
// The set of keys in argSuffixes also drives the identification of suffix pieces.
|
||||
// For example, <Rm> and <Rm>{, <type> <Rs>} are both keys in the map
|
||||
// and matching is done 'longest first', so "<Rm>, <Rm>{, <type> <Rs>}" is
|
||||
// parsed as just two arguments despite the extra ", ".
|
||||
// The field order in the map values must match the order expected in
|
||||
// the argument descriptions in argOps.
|
||||
var argSuffixes = map[string]string{
|
||||
"#0": "",
|
||||
"#0.0": "",
|
||||
"#<const>": "imm12:12",
|
||||
"#<fbits>": "sx,imm4:4,i",
|
||||
"#<imm12+4>": "imm12:12,imm4:4",
|
||||
"#<imm24>": "imm24:24",
|
||||
"#<imm3>": "imm3:3",
|
||||
"#<imm4>": "imm4:4",
|
||||
"#<imm5>": "imm5:5",
|
||||
"#<imm5_nz>": "imm5:5",
|
||||
"#<imm5_32>": "imm5:5",
|
||||
"#<imm6>": "imm6:6",
|
||||
"#<immsize>": "size:2",
|
||||
"#<imm_vfp>": "imm4H:4,imm4L:4,sz",
|
||||
"#<sat_imm4>": "sat_imm:4",
|
||||
"#<sat_imm5>": "sat_imm:5",
|
||||
"#<sat_imm4m1>": "sat_imm:4",
|
||||
"#<sat_imm5m1>": "sat_imm:5",
|
||||
"#<lsb>": "lsb:5",
|
||||
"#<option>": "option:4",
|
||||
"#<width>": "lsb:5,msb:5",
|
||||
"#<widthm1>": "widthm1:5",
|
||||
"+/-<Rm>": "Rm:4,U",
|
||||
"<Dd>": "D,Vd:4",
|
||||
"<Dd[x]>": "D,Vd:4,opc1",
|
||||
"<Dm>": "M,Vm:4",
|
||||
"<Dm[x]>": "M,Vm:4,size:2",
|
||||
"<Dn>": "N,Vn:4",
|
||||
"<Dn[x]>": "N,Vn:4,opc1",
|
||||
"<Dm[size_x]>": "imm4:4",
|
||||
"<Qd>": "D,Vd:4",
|
||||
"<Qm>": "M,Vm:4",
|
||||
"<Qn>": "N,Vn:4",
|
||||
"<Ra>": "Ra:4",
|
||||
"<Rd>": "Rd:4",
|
||||
"<RdHi>": "RdHi:4",
|
||||
"<RdLo>": "RdLo:4",
|
||||
"<Rm>": "Rm:4",
|
||||
"<Rm>{,<rotation>}": "Rm:4,rotate:2",
|
||||
"<Rm>{,<shift>}": "Rm:4,imm5:5,type:2",
|
||||
"<Rm>{,LSL #<imm5>}": "Rm:4,imm5:5",
|
||||
"<Rn>": "Rn:4",
|
||||
"<Rn>{!}": "Rn:4,W",
|
||||
"<Rn>{,<shift>}": "Rn:4,imm5:5,sh",
|
||||
"<Rs>": "Rs:4",
|
||||
"<Rt1>": "Rt:4",
|
||||
"<Rt2>": "Rt:4",
|
||||
"<Rt>": "Rt:4",
|
||||
"<Rt_nzcv>": "Rt:4",
|
||||
"<Sd>": "Vd:4,D",
|
||||
"<Sm1>": "Vm:4,M",
|
||||
"<Sm>": "Vm:4,M",
|
||||
"<Sn>": "Vn:4,N",
|
||||
"<Sd,Dd>": "Vd:4,D,sz",
|
||||
"<Dd,Sd>": "Vd:4,D,sz",
|
||||
"<Sn,Dn>": "Vn:4,N,sz",
|
||||
"<Sm,Dm>": "Vm:4,M,sz",
|
||||
"<endian_specifier>": "E",
|
||||
"<label+/-12>": "imm12:12,U",
|
||||
"<label+12>": "imm12:12",
|
||||
"<label-12>": "imm12:12",
|
||||
"<label24>": "imm24:24",
|
||||
"<label24H>": "imm24:24,H",
|
||||
"<label+/-4+4>": "imm4H:4,imm4L:4,U",
|
||||
"<list4>": "D,Vd:4,type:4",
|
||||
"<list3>": "D,Vd:4,index_align:4",
|
||||
"<list3t>": "D,Vd:4,T",
|
||||
"<list1>": "D,Vd:4",
|
||||
"<list_len>": "N,Vn:4,len:2",
|
||||
"<vlist32>": "D,Vd:4,imm8:8",
|
||||
"<vlist64>": "D,Vd:4,imm8:8",
|
||||
"<registers>": "register_list:16",
|
||||
"<registers2>": "register_list:16",
|
||||
"<registers1>": "Rt:4",
|
||||
"APSR": "",
|
||||
"<Rm>,<type> <Rs>": "Rm:4,Rs:4,type:2",
|
||||
"FPSCR": "",
|
||||
"SP": "",
|
||||
"[<Rn>,#+/-<imm12>]": "Rn:4,U,imm12:12",
|
||||
"[<Rn>,+/-<Rm>]{!}": "Rn:4,U,Rm:4,P,W",
|
||||
"[<Rn>,+/-<Rm>{, <shift>}]": "Rn:4,U,Rm:4,type:2,imm5:5",
|
||||
"[<Rn>,+/-<Rm>{, <shift>}]{!}": "Rn:4,U,Rm:4,type:2,imm5:5,P,W",
|
||||
"[<Rn>] {,#+/-<imm12>}": "Rn:4,U,imm12:12",
|
||||
"[<Rn>] {,#+/-<imm8>}": "Rn:4,U,imm4H:4,imm4L:4",
|
||||
"[<Rn>]": "Rn:4",
|
||||
"[<Rn>],#+/-<imm12>": "Rn:4,imm12:12,U",
|
||||
"[<Rn>],+/-<Rm>{, <shift>}": "Rn:4,Rm:4,imm5:5,type:2,U",
|
||||
"[<Rn>]{!}": "Rn:4,Rm:4",
|
||||
"[<Rn>{@<align>}]{!}": "XXX",
|
||||
"[<Rn>{,#+/-<imm12>}]{!}": "Rn:4,P,U,W,imm12:12",
|
||||
"[<Rn>{,#+/-<imm8>}]{!}": "Rn:4,P,U,W,imm4H:4,imm4L:4",
|
||||
"[<Rn>{,#+/-<imm8>}]": "Rn:4,U,imm8:8",
|
||||
"[<Rn>], +/-<Rm>": "Rn:4,U,Rm:4",
|
||||
"#<imm_simd1>": "i,imm3:3,imm4:4,cmode:4",
|
||||
"#<imm_simd>": "op,i,imm3:3,imm4:4,cmode:4",
|
||||
"#<imm_vs>": "L,imm6:6",
|
||||
"#<imm_vsn>": "imm6:6",
|
||||
}
|
||||
|
||||
// cross returns the string concatenation cross product of xs and ys.
|
||||
func cross(xs []string, ys ...string) []string {
|
||||
var xys []string
|
||||
|
||||
for _, x := range xs {
|
||||
for _, y := range ys {
|
||||
xys = append(xys, x+y)
|
||||
}
|
||||
}
|
||||
return xys
|
||||
}
|
||||
|
||||
// crossCond returns the cross product of xs with all the possible
|
||||
// conditional execution suffixes. It is assumed that each string x in xs
|
||||
// contains a substring _COND_ marking where the conditional suffix
|
||||
// should be placed.
|
||||
func crossCond(xs []string) []string {
|
||||
ys := choices["c"]
|
||||
var xys []string
|
||||
|
||||
for _, x := range xs {
|
||||
i := strings.Index(x, "_COND_")
|
||||
pre, post := x[:i], x[i+6:]
|
||||
for _, y := range ys {
|
||||
xys = append(xys, pre+y+post)
|
||||
}
|
||||
}
|
||||
return xys
|
||||
}
|
||||
|
||||
// printText implements the -fmt=text mode, which is not implemented (yet?).
|
||||
func printText(p *Prog) {
|
||||
log.Fatal("-fmt=text not implemented")
|
||||
}
|
||||
|
||||
// printDecoder implements the -fmt=decoder mode.
|
||||
// It emits the tables.go for package armasm's decoder.
|
||||
func printDecoder(p *Prog) {
|
||||
fmt.Printf("package armasm\n\n")
|
||||
|
||||
// Build list of opcodes sorted by name
|
||||
// but preserving the sequential ranges needed for opcode decoding.
|
||||
haveRange := make(map[string]string)
|
||||
for _, r := range p.OpRanges {
|
||||
haveRange[r] = r
|
||||
}
|
||||
var ranges []string
|
||||
for _, r := range haveRange {
|
||||
ranges = append(ranges, r)
|
||||
}
|
||||
sort.Strings(ranges)
|
||||
|
||||
// Emit const definitions for opcodes.
|
||||
fmt.Printf("const (\n")
|
||||
iota := 0
|
||||
fmt.Printf("\t_ Op = iota\n")
|
||||
iota++
|
||||
for _, r := range ranges {
|
||||
for _, op := range strings.Split(r, ",") {
|
||||
if op == "" {
|
||||
continue
|
||||
}
|
||||
// Assume if opcode says .EQ it is the start of a 16-wide
|
||||
// iteration through the conditional suffixes. If so, emit
|
||||
// blank names until the assigned value is 16-aligned.
|
||||
if strings.Contains(op, ".EQ") {
|
||||
for iota&15 != 0 {
|
||||
fmt.Printf("\t_\n")
|
||||
iota++
|
||||
}
|
||||
}
|
||||
fmt.Printf("\t%s\n", strings.Replace(op, ".", "_", -1))
|
||||
iota++
|
||||
}
|
||||
}
|
||||
fmt.Printf(")\n")
|
||||
|
||||
// Emit slice mapping opcode number to name string.
|
||||
fmt.Printf("\nvar opstr = [...]string{\n")
|
||||
for _, r := range ranges {
|
||||
for _, op := range strings.Split(r, ",") {
|
||||
if op == "" {
|
||||
continue
|
||||
}
|
||||
fmt.Printf("\t%s: %q,\n", strings.Replace(op, ".", "_", -1), op)
|
||||
}
|
||||
}
|
||||
fmt.Printf("}\n")
|
||||
|
||||
// Emit decoding table.
|
||||
unknown := map[string]bool{}
|
||||
fmt.Printf("\nvar instFormats = [...]instFormat{\n")
|
||||
for _, inst := range p.Inst {
|
||||
fmt.Printf("\t{%#08x, %#08x, %d, %s, %#x, instArgs{", inst.Mask, inst.Value, inst.Priority, strings.Replace(inst.OpBase, ".", "_", -1), inst.OpBits)
|
||||
for i, a := range inst.Args {
|
||||
if i > 0 {
|
||||
fmt.Printf(", ")
|
||||
}
|
||||
str := argOps[a]
|
||||
if str == "" && !unknown[a] {
|
||||
fmt.Fprintf(os.Stderr, "%s: unknown arg %s\n", inst.Text, a)
|
||||
unknown[a] = true
|
||||
}
|
||||
fmt.Printf("%s", str)
|
||||
}
|
||||
fmt.Printf("}}, // %s %s\n", inst.Text, inst.Encoding)
|
||||
}
|
||||
fmt.Printf("}\n")
|
||||
}
|
||||
@@ -0,0 +1,610 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Armspec reads the “ARM Architecture Reference Manual”
|
||||
// to collect instruction encoding details and writes those details to standard output
|
||||
// in JSON format.
|
||||
//
|
||||
// # Warning Warning Warning
|
||||
//
|
||||
// This program is unfinished. It is being published in this incomplete form
|
||||
// for interested readers, but do not expect it to be runnable or useful.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"math"
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"rsc.io/pdf"
|
||||
)
|
||||
|
||||
type Inst struct {
|
||||
Name string
|
||||
ID string
|
||||
Bits string
|
||||
Arch string
|
||||
Syntax []string
|
||||
Code string
|
||||
}
|
||||
|
||||
const debugPage = 0
|
||||
|
||||
var stdout *bufio.Writer
|
||||
|
||||
func main() {
|
||||
log.SetFlags(0)
|
||||
log.SetPrefix("armspec: ")
|
||||
|
||||
if len(os.Args) != 2 {
|
||||
fmt.Fprintf(os.Stderr, "usage: armspec file.pdf\n")
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
f, err := pdf.Open(os.Args[1])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Find instruction set reference in outline, to build instruction list.
|
||||
instList := instHeadings(f.Outline())
|
||||
if len(instList) < 200 {
|
||||
log.Fatalf("only found %d instructions in table of contents", len(instList))
|
||||
}
|
||||
|
||||
stdout = bufio.NewWriter(os.Stdout)
|
||||
fmt.Fprintf(stdout, "[")
|
||||
numTable := 0
|
||||
defer stdout.Flush()
|
||||
|
||||
// Scan document looking for instructions.
|
||||
// Must find exactly the ones in the outline.
|
||||
n := f.NumPage()
|
||||
PageLoop:
|
||||
for pageNum := 1; pageNum <= n; pageNum++ {
|
||||
if debugPage > 0 && pageNum != debugPage {
|
||||
continue
|
||||
}
|
||||
if pageNum > 1127 {
|
||||
break
|
||||
}
|
||||
p := f.Page(pageNum)
|
||||
name, table := parsePage(pageNum, p)
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
if len(table) < 1 {
|
||||
if false {
|
||||
fmt.Fprintf(os.Stderr, "no encodings for instruction %q (page %d)\n", name, pageNum)
|
||||
}
|
||||
continue
|
||||
}
|
||||
for _, inst := range table {
|
||||
if numTable > 0 {
|
||||
fmt.Fprintf(stdout, ",")
|
||||
}
|
||||
numTable++
|
||||
js, _ := json.Marshal(inst)
|
||||
fmt.Fprintf(stdout, "\n%s", jsFix.Replace(string(js)))
|
||||
}
|
||||
for j, headline := range instList {
|
||||
if name == headline {
|
||||
instList[j] = ""
|
||||
continue PageLoop
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "unexpected instruction %q (page %d)\n", name, pageNum)
|
||||
}
|
||||
|
||||
fmt.Fprintf(stdout, "\n]\n")
|
||||
stdout.Flush()
|
||||
|
||||
if debugPage == 0 {
|
||||
for _, headline := range instList {
|
||||
if headline != "" {
|
||||
switch headline {
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "missing instruction %q\n", headline)
|
||||
case "CHKA": // ThumbEE
|
||||
case "CPS": // system instruction
|
||||
case "CPY": // synonym for MOV
|
||||
case "ENTERX": // ThumbEE
|
||||
case "F* (former VFP instruction mnemonics)": // synonyms
|
||||
case "HB, HBL, HBLP, HBP": // ThumbEE
|
||||
case "LEAVEX": // ThumbEE
|
||||
case "MOV (shifted register)": // pseudo instruction for ASR, LSL, LSR, ROR, and RRX
|
||||
case "NEG": // synonym for RSB
|
||||
case "RFE": // system instruction
|
||||
case "SMC (previously SMI)": // system instruction
|
||||
case "SRS": // system instruction
|
||||
case "SUBS PC, LR and related instructions": // system instruction
|
||||
case "VAND (immediate)": // pseudo instruction
|
||||
case "VCLE (register)": // pseudo instruction
|
||||
case "VCLT (register)": // pseudo instruction
|
||||
case "VORN (immediate)": // pseudo instruction
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func instHeadings(outline pdf.Outline) []string {
|
||||
return appendInstHeadings(outline, nil)
|
||||
}
|
||||
|
||||
var instRE = regexp.MustCompile(`A[\d.]+ Alphabetical list of instructions`)
|
||||
var childRE = regexp.MustCompile(`A[\d.]+ (.+)`)
|
||||
var sectionRE = regexp.MustCompile(`^A[\d.]+$`)
|
||||
var bitRE = regexp.MustCompile(`^( |[01]|\([01]\))*$`)
|
||||
|
||||
func appendInstHeadings(outline pdf.Outline, list []string) []string {
|
||||
if instRE.MatchString(outline.Title) {
|
||||
for _, child := range outline.Child {
|
||||
m := childRE.FindStringSubmatch(child.Title)
|
||||
if m == nil {
|
||||
fmt.Fprintf(os.Stderr, "cannot parse section title: %s\n", child.Title)
|
||||
continue
|
||||
}
|
||||
list = append(list, m[1])
|
||||
}
|
||||
}
|
||||
for _, child := range outline.Child {
|
||||
list = appendInstHeadings(child, list)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
const inch = 72.0
|
||||
|
||||
func parsePage(num int, p pdf.Page) (name string, table []Inst) {
|
||||
content := p.Content()
|
||||
|
||||
var text []pdf.Text
|
||||
for _, t := range content.Text {
|
||||
if match(t, "Times-Roman", 7.2, "") {
|
||||
t.FontSize = 9
|
||||
}
|
||||
if match(t, "Times-Roman", 6.72, "") && '0' <= t.S[0] && t.S[0] <= '9' {
|
||||
t.S = string([]rune("⁰¹²³⁴⁵⁶⁷⁸⁹")[t.S[0]-'0'])
|
||||
t.FontSize = 9
|
||||
t.Y -= 2.28
|
||||
}
|
||||
if t.Font == "Gen_Arial" {
|
||||
continue
|
||||
}
|
||||
text = append(text, t)
|
||||
}
|
||||
|
||||
text = findWords(text)
|
||||
|
||||
for i, t := range text {
|
||||
if t.Font == "Times" {
|
||||
t.Font = "Times-Roman"
|
||||
text[i] = t
|
||||
}
|
||||
}
|
||||
|
||||
if debugPage > 0 {
|
||||
for _, t := range text {
|
||||
fmt.Println(t)
|
||||
}
|
||||
for _, r := range content.Rect {
|
||||
fmt.Println(r)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove text we should ignore.
|
||||
out := text[:0]
|
||||
skip := false
|
||||
for _, t := range text {
|
||||
// skip page footer
|
||||
if match(t, "Helvetica", 8, "A") || match(t, "Helvetica", 8, "ARM DDI") || match(t, "Helvetica-Oblique", 8, "Copyright") {
|
||||
continue
|
||||
}
|
||||
// skip section header and body text
|
||||
if match(t, "Helvetica-Bold", 12, "") && (sectionRE.MatchString(t.S) || t.S == "Alphabetical list of instructions") {
|
||||
skip = true
|
||||
continue
|
||||
}
|
||||
if skip && match(t, "Times-Roman", 9, "") {
|
||||
continue
|
||||
}
|
||||
skip = false
|
||||
out = append(out, t)
|
||||
}
|
||||
text = out
|
||||
|
||||
// Page header must say Instruction Details.
|
||||
if len(text) == 0 || !match(text[0], "Helvetica-Oblique", 8, "Instruction Details") && !match(text[0], "Times-Roman", 9, "Instruction Details") {
|
||||
return "", nil
|
||||
}
|
||||
text = text[1:]
|
||||
|
||||
isSection := func(text []pdf.Text, i int) int {
|
||||
if i+2 <= len(text) && match(text[i], "Helvetica-Bold", 10, "") && sectionRE.MatchString(text[i].S) && match(text[i+1], "Helvetica-Bold", 10, "") {
|
||||
return 2
|
||||
}
|
||||
if i+1 <= len(text) && match(text[i], "Helvetica-Bold", 10, "") && childRE.MatchString(text[i].S) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Skip dummy headlines and sections.
|
||||
for d := isSection(text, 0); d != 0; d = isSection(text, 0) {
|
||||
i := d
|
||||
for i < len(text) && !match(text[i], "Helvetica-Bold", 9, "Encoding") && !match(text[i], "Helvetica-Bold", 10, "") {
|
||||
i++
|
||||
}
|
||||
if isSection(text, i) == 0 {
|
||||
break
|
||||
}
|
||||
text = text[i:]
|
||||
}
|
||||
|
||||
// Next line is headline. Can wrap to multiple lines.
|
||||
d := isSection(text, 0)
|
||||
if d == 0 {
|
||||
if debugPage > 0 {
|
||||
fmt.Printf("non-inst-headline: %v\n", text[0])
|
||||
}
|
||||
checkNoEncodings(num, text)
|
||||
return "", nil
|
||||
}
|
||||
if d == 2 {
|
||||
name = text[1].S
|
||||
text = text[2:]
|
||||
} else if d == 1 {
|
||||
m := childRE.FindStringSubmatch(text[0].S)
|
||||
name = m[1]
|
||||
text = text[1:]
|
||||
}
|
||||
for len(text) > 0 && match(text[0], "Helvetica-Bold", 10, "") {
|
||||
name += " " + text[0].S
|
||||
text = text[1:]
|
||||
}
|
||||
|
||||
// Skip description.
|
||||
for len(text) > 0 && (match(text[0], "Times-Roman", 9, "") || match(text[0], "LucidaSansTypewriteX", 6.48, "") || match(text[0], "Times-Bold", 10, "Note")) {
|
||||
text = text[1:]
|
||||
}
|
||||
|
||||
// Encodings follow.
|
||||
warned := false
|
||||
for i := 0; i < len(text); {
|
||||
if match(text[i], "Helvetica-Bold", 10, "Assembler syntax") ||
|
||||
match(text[i], "Helvetica-Bold", 9, "Modified operation in ThumbEE") ||
|
||||
match(text[i], "Helvetica-Bold", 9, "Unallocated memory hints") ||
|
||||
match(text[i], "Helvetica-Bold", 9, "Related encodings") ||
|
||||
match(text[i], "Times-Roman", 9, "Figure A") ||
|
||||
match(text[i], "Helvetica-Bold", 9, "Table A") ||
|
||||
match(text[i], "Helvetica-Bold", 9, "VFP Instructions") ||
|
||||
match(text[i], "Helvetica-Bold", 9, "VFP instructions") ||
|
||||
match(text[i], "Helvetica-Bold", 9, "VFP vectors") ||
|
||||
match(text[i], "Helvetica-Bold", 9, "FLDMX") ||
|
||||
match(text[i], "Helvetica-Bold", 9, "FSTMX") ||
|
||||
match(text[i], "Helvetica-Bold", 9, "Advanced SIMD and VFP") {
|
||||
checkNoEncodings(num, text[i:])
|
||||
break
|
||||
}
|
||||
if match(text[i], "Helvetica-Bold", 9, "Figure A") {
|
||||
y := text[i].Y
|
||||
i++
|
||||
for i < len(text) && math.Abs(text[i].Y-y) < 2 {
|
||||
i++
|
||||
}
|
||||
continue
|
||||
}
|
||||
if !match(text[i], "Helvetica-Bold", 9, "Encoding") {
|
||||
if !warned {
|
||||
warned = true
|
||||
fmt.Fprintln(os.Stderr, "page", num, ": unexpected:", text[i])
|
||||
}
|
||||
i++
|
||||
continue
|
||||
}
|
||||
inst := Inst{
|
||||
Name: name,
|
||||
}
|
||||
enc := text[i].S
|
||||
x := text[i].X
|
||||
i++
|
||||
// Possible subarchitecture notes.
|
||||
for i < len(text) && text[i].X > x+36 {
|
||||
if inst.Arch != "" {
|
||||
inst.Arch += " "
|
||||
}
|
||||
inst.Arch += text[i].S
|
||||
i++
|
||||
}
|
||||
// Encoding syntaxes.
|
||||
for i < len(text) && (match(text[i], "LucidaSansTypewriteX", 6.48, "") || text[i].X > x+36) {
|
||||
if text[i].X < x+0.25*inch {
|
||||
inst.Syntax = append(inst.Syntax, text[i].S)
|
||||
} else {
|
||||
s := inst.Syntax[len(inst.Syntax)-1]
|
||||
if !strings.Contains(s, "\t") {
|
||||
s += "\t"
|
||||
} else {
|
||||
s += " "
|
||||
}
|
||||
s += text[i].S
|
||||
inst.Syntax[len(inst.Syntax)-1] = s
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
var bits, abits, aenc string
|
||||
bits, i = readBitBox(inst.Name, inst.Syntax, content, text, i)
|
||||
if strings.Contains(enc, " / ") {
|
||||
if i < len(text) && match(text[i], "Times-Roman", 8, "") {
|
||||
abits, i = readBitBox(inst.Name, inst.Syntax, content, text, i)
|
||||
} else {
|
||||
abits = bits
|
||||
}
|
||||
slash := strings.Index(enc, " / ")
|
||||
aenc = "Encoding " + enc[slash+len(" / "):]
|
||||
enc = enc[:slash]
|
||||
}
|
||||
|
||||
// pseudocode
|
||||
y0 := -1 * inch
|
||||
tab := 0.0
|
||||
for i < len(text) && match(text[i], "LucidaSansTypewriteX", 6.48, "") {
|
||||
t := text[i]
|
||||
i++
|
||||
if math.Abs(t.Y-y0) < 3 {
|
||||
// same line as last fragment, probably just two spaces
|
||||
inst.Code += " " + t.S
|
||||
continue
|
||||
}
|
||||
if inst.Code != "" {
|
||||
inst.Code += "\n"
|
||||
}
|
||||
if t.X > x+0.1*inch {
|
||||
if tab == 0 {
|
||||
tab = t.X - x
|
||||
}
|
||||
inst.Code += strings.Repeat("\t", int((t.X-x)/tab+0.5))
|
||||
} else {
|
||||
tab = 0
|
||||
}
|
||||
inst.Code += t.S
|
||||
y0 = t.Y
|
||||
}
|
||||
|
||||
inst.ID = strings.TrimPrefix(enc, "Encoding ")
|
||||
inst.Bits = bits
|
||||
table = append(table, inst)
|
||||
if abits != "" {
|
||||
inst.ID = strings.TrimPrefix(aenc, "Encoding ")
|
||||
inst.Bits = abits
|
||||
table = append(table, inst)
|
||||
}
|
||||
|
||||
}
|
||||
return name, table
|
||||
}
|
||||
|
||||
func readBitBox(name string, syntax []string, content pdf.Content, text []pdf.Text, i int) (string, int) {
|
||||
// bit headings
|
||||
y2 := 0.0
|
||||
x1 := 0.0
|
||||
x2 := 0.0
|
||||
for i < len(text) && match(text[i], "Times-Roman", 8, "") {
|
||||
if y2 == 0 {
|
||||
y2 = text[i].Y
|
||||
}
|
||||
if x1 == 0 {
|
||||
x1 = text[i].X
|
||||
}
|
||||
i++
|
||||
}
|
||||
// bit fields in box
|
||||
y1 := 0.0
|
||||
dy1 := 0.0
|
||||
for i < len(text) && match(text[i], "Times-Roman", 9, "") {
|
||||
if x2 < text[i].X+text[i].W {
|
||||
x2 = text[i].X + text[i].W
|
||||
}
|
||||
y1 = text[i].Y
|
||||
dy1 = text[i].FontSize
|
||||
i++
|
||||
}
|
||||
|
||||
if debugPage > 0 {
|
||||
fmt.Println("encoding box", x1, y1, x2, y2)
|
||||
}
|
||||
|
||||
// Find lines (thin rectangles) separating bit fields.
|
||||
var bottom, top pdf.Rect
|
||||
const (
|
||||
yMargin = 0.25 * 72
|
||||
xMargin = 2 * 72
|
||||
)
|
||||
for _, r := range content.Rect {
|
||||
if r.Max.Y-r.Min.Y < 2 && x1-xMargin < r.Min.X && r.Min.X < x1 && x2 < r.Max.X && r.Max.X < x2+xMargin {
|
||||
if y1-yMargin < r.Min.Y && r.Min.Y < y1 {
|
||||
bottom = r
|
||||
}
|
||||
if y1+dy1 < r.Min.Y && r.Min.Y < y2 {
|
||||
top = r
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if debugPage > 0 {
|
||||
fmt.Println("top", top, "bottom", bottom)
|
||||
}
|
||||
|
||||
const ε = 0.1 * 72
|
||||
var bars []pdf.Rect
|
||||
for _, r := range content.Rect {
|
||||
if r.Max.X-r.Min.X < 2 && math.Abs(r.Min.Y-bottom.Min.Y) < ε && math.Abs(r.Max.Y-top.Min.Y) < ε {
|
||||
bars = append(bars, r)
|
||||
}
|
||||
}
|
||||
sort.Sort(RectHorizontal(bars))
|
||||
|
||||
// There are 16-bit and 32-bit encodings.
|
||||
// In practice, they are about 2.65 and 5.3 inches wide, respectively.
|
||||
// Use 4 inches as a cutoff.
|
||||
nbit := 32
|
||||
dx := top.Max.X - top.Min.X
|
||||
if top.Max.X-top.Min.X < 4*72 {
|
||||
nbit = 16
|
||||
}
|
||||
|
||||
total := 0
|
||||
var buf bytes.Buffer
|
||||
for i := 0; i < len(bars)-1; i++ {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(&buf, "|")
|
||||
}
|
||||
var sub []pdf.Text
|
||||
x1, x2 := bars[i].Min.X, bars[i+1].Min.X
|
||||
for _, t := range content.Text {
|
||||
tx := t.X + t.W/2
|
||||
ty := t.Y + t.FontSize/2
|
||||
if x1 < tx && tx < x2 && y1 < ty && ty < y2 {
|
||||
sub = append(sub, t)
|
||||
}
|
||||
}
|
||||
var str []string
|
||||
for _, t := range findWords(sub) {
|
||||
str = append(str, t.S)
|
||||
}
|
||||
s := strings.Join(str, " ")
|
||||
s = strings.Replace(s, ")(", ") (", -1)
|
||||
n := len(strings.Fields(s))
|
||||
b := int(float64(nbit)*(x2-x1)/dx + 0.5)
|
||||
if n == b {
|
||||
for j, f := range strings.Fields(s) {
|
||||
if j > 0 {
|
||||
fmt.Fprintf(&buf, "|")
|
||||
}
|
||||
fmt.Fprintf(&buf, "%s", f)
|
||||
}
|
||||
} else {
|
||||
if n != 1 {
|
||||
fmt.Fprintf(os.Stderr, "%s - %s - multi-field %d-bit encoding: %s\n", name, syntax, n, s)
|
||||
}
|
||||
fmt.Fprintf(&buf, "%s:%d", s, b)
|
||||
}
|
||||
total += b
|
||||
}
|
||||
|
||||
if total != nbit || total == 0 {
|
||||
fmt.Fprintf(os.Stderr, "%s - %s - %d-bit encoding\n", name, syntax, total)
|
||||
}
|
||||
return buf.String(), i
|
||||
}
|
||||
|
||||
type RectHorizontal []pdf.Rect
|
||||
|
||||
func (x RectHorizontal) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||
func (x RectHorizontal) Less(i, j int) bool { return x[i].Min.X < x[j].Min.X }
|
||||
func (x RectHorizontal) Len() int { return len(x) }
|
||||
|
||||
func checkNoEncodings(num int, text []pdf.Text) {
|
||||
for _, t := range text {
|
||||
if match(t, "Helvetica-Bold", 9, "Encoding") {
|
||||
fmt.Fprintf(os.Stderr, "page %d: unexpected encoding: %s\n", num, t.S)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func match(t pdf.Text, font string, size float64, substr string) bool {
|
||||
return t.Font == font && math.Abs(t.FontSize-size) < 0.1 && strings.Contains(t.S, substr)
|
||||
}
|
||||
|
||||
func findWords(chars []pdf.Text) (words []pdf.Text) {
|
||||
// Sort by Y coordinate and normalize.
|
||||
const nudge = 1
|
||||
sort.Sort(pdf.TextVertical(chars))
|
||||
old := -100000.0
|
||||
for i, c := range chars {
|
||||
if c.Y != old && math.Abs(old-c.Y) < nudge {
|
||||
chars[i].Y = old
|
||||
} else {
|
||||
old = c.Y
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by Y coordinate, breaking ties with X.
|
||||
// This will bring letters in a single word together.
|
||||
sort.Sort(pdf.TextVertical(chars))
|
||||
|
||||
// Loop over chars.
|
||||
for i := 0; i < len(chars); {
|
||||
// Find all chars on line.
|
||||
j := i + 1
|
||||
for j < len(chars) && chars[j].Y == chars[i].Y {
|
||||
j++
|
||||
}
|
||||
var end float64
|
||||
// Split line into words (really, phrases).
|
||||
for k := i; k < j; {
|
||||
ck := &chars[k]
|
||||
s := ck.S
|
||||
end = ck.X + ck.W
|
||||
charSpace := ck.FontSize / 6
|
||||
wordSpace := ck.FontSize * 2 / 3
|
||||
l := k + 1
|
||||
for l < j {
|
||||
// Grow word.
|
||||
cl := &chars[l]
|
||||
if sameFont(cl.Font, ck.Font) && math.Abs(cl.FontSize-ck.FontSize) < 0.1 && cl.X <= end+charSpace {
|
||||
s += cl.S
|
||||
end = cl.X + cl.W
|
||||
l++
|
||||
continue
|
||||
}
|
||||
// Add space to phrase before next word.
|
||||
if sameFont(cl.Font, ck.Font) && math.Abs(cl.FontSize-ck.FontSize) < 0.1 && cl.X <= end+wordSpace {
|
||||
s += " " + cl.S
|
||||
end = cl.X + cl.W
|
||||
l++
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
f := ck.Font
|
||||
f = strings.TrimSuffix(f, ",Italic")
|
||||
f = strings.TrimSuffix(f, "-Italic")
|
||||
words = append(words, pdf.Text{f, ck.FontSize, ck.X, ck.Y, end - ck.X, s})
|
||||
k = l
|
||||
}
|
||||
i = j
|
||||
}
|
||||
|
||||
return words
|
||||
}
|
||||
|
||||
func sameFont(f1, f2 string) bool {
|
||||
f1 = strings.TrimSuffix(f1, ",Italic")
|
||||
f1 = strings.TrimSuffix(f1, "-Italic")
|
||||
f2 = strings.TrimSuffix(f1, ",Italic")
|
||||
f2 = strings.TrimSuffix(f1, "-Italic")
|
||||
return strings.TrimSuffix(f1, ",Italic") == strings.TrimSuffix(f2, ",Italic") || f1 == "Symbol" || f2 == "Symbol" || f1 == "TimesNewRoman" || f2 == "TimesNewRoman"
|
||||
}
|
||||
|
||||
var jsFix = strings.NewReplacer(
|
||||
// `\u003c`, `<`,
|
||||
// `\u003e`, `>`,
|
||||
// `\u0026`, `&`,
|
||||
// `\u0009`, `\t`,
|
||||
)
|
||||
|
||||
func printTable(name string, table []Inst) {
|
||||
_ = strconv.Atoi
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var _ = os.Stdout
|
||||
var _ = fmt.Sprintf
|
||||
|
||||
type Inst struct {
|
||||
Name string
|
||||
ID string
|
||||
Bits string
|
||||
Arch string
|
||||
Syntax []string
|
||||
Code string
|
||||
Base uint32
|
||||
Mask uint32
|
||||
Prog []*Stmt
|
||||
}
|
||||
|
||||
func main() {
|
||||
data, err := ioutil.ReadFile("spec.json")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
var insts []Inst
|
||||
if err := json.Unmarshal(data, &insts); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var out []Inst
|
||||
for _, inst := range insts {
|
||||
inst.Prog = parse(inst.Name+" "+inst.ID, inst.Code)
|
||||
if inst.ID[0] == 'A' && !strings.HasPrefix(inst.Syntax[0], "MSR<c>") && !strings.Contains(inst.Syntax[0], "<coproc>") && !strings.Contains(inst.Syntax[0], "VLDM") && !strings.Contains(inst.Syntax[0], "VSTM") {
|
||||
out = append(out, inst)
|
||||
}
|
||||
}
|
||||
insts = out
|
||||
|
||||
for i := range insts {
|
||||
dosize(&insts[i])
|
||||
}
|
||||
|
||||
var cond, special []Inst
|
||||
for _, inst := range insts {
|
||||
if inst.Base>>28 == 0xF {
|
||||
special = append(special, inst)
|
||||
} else {
|
||||
cond = append(cond, inst)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("special:\n")
|
||||
split(special, 0xF0000000, 1)
|
||||
fmt.Printf("cond:\n")
|
||||
split(cond, 0xF0000000, 1)
|
||||
}
|
||||
|
||||
func dosize(inst *Inst) {
|
||||
var base, mask uint32
|
||||
off := 0
|
||||
for _, f := range strings.Split(inst.Bits, "|") {
|
||||
if i := strings.Index(f, ":"); i >= 0 {
|
||||
n, _ := strconv.Atoi(f[i+1:])
|
||||
off += n
|
||||
continue
|
||||
}
|
||||
for _, bit := range strings.Fields(f) {
|
||||
switch bit {
|
||||
case "0", "(0)":
|
||||
mask |= 1 << uint(31-off)
|
||||
case "1", "(1)":
|
||||
base |= 1 << uint(31-off)
|
||||
}
|
||||
off++
|
||||
}
|
||||
}
|
||||
if off != 16 && off != 32 {
|
||||
log.Printf("incorrect bit count for %s %s: have %d", inst.Name, inst.Bits, off)
|
||||
}
|
||||
if off == 16 {
|
||||
mask >>= 16
|
||||
base >>= 16
|
||||
}
|
||||
mask |= base
|
||||
inst.Mask = mask
|
||||
inst.Base = base
|
||||
}
|
||||
|
||||
func split(insts []Inst, used uint32, depth int) {
|
||||
Again:
|
||||
if len(insts) <= 1 {
|
||||
for _, inst := range insts {
|
||||
fmt.Printf("%*s%#08x %#08x %s %s %v\n", depth*2+2, "", inst.Mask, inst.Base, inst.Syntax[0], inst.Bits, seeRE.FindAllString(inst.Code, -1))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
m := ^used
|
||||
for _, inst := range insts {
|
||||
m &= inst.Mask
|
||||
}
|
||||
if m == 0 {
|
||||
fmt.Printf("«%*s%#08x masked out (%d)\n", depth*2, "", used, len(insts))
|
||||
for _, inst := range insts {
|
||||
fmt.Printf("%*s%#08x %#08x %s %s %v\n", depth*2+2, "", inst.Mask, inst.Base, inst.Syntax[0], inst.Bits, seeRE.FindAllString(inst.Code, -1))
|
||||
}
|
||||
updated := false
|
||||
for i := range insts {
|
||||
if updateMask(&insts[i]) {
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
fmt.Printf("»\n")
|
||||
if updated {
|
||||
goto Again
|
||||
}
|
||||
fmt.Printf("%*s%#08x masked out (%d)\n", depth*2, "", used, len(insts))
|
||||
for _, inst := range insts {
|
||||
fmt.Printf("%*s%#08x %#08x %s %s %v\n", depth*2+2, "", inst.Mask, inst.Base, inst.Syntax[0], inst.Bits, seeRE.FindAllString(inst.Code, -1))
|
||||
}
|
||||
//checkOverlap(used, insts)
|
||||
return
|
||||
}
|
||||
for i := 31; i >= 0; i-- {
|
||||
if m&(1<<uint(i)) != 0 {
|
||||
m = 1 << uint(i)
|
||||
break
|
||||
}
|
||||
}
|
||||
var bit [2][]Inst
|
||||
for _, inst := range insts {
|
||||
b := (inst.Base / m) & 1
|
||||
bit[b] = append(bit[b], inst)
|
||||
}
|
||||
|
||||
for b, list := range bit {
|
||||
if len(list) > 0 {
|
||||
suffix := ""
|
||||
if len(bit[1-b]) == 0 {
|
||||
suffix = " (only)"
|
||||
}
|
||||
fmt.Printf("%*sbit %#08x = %d%s\n", depth*2, "", m, b, suffix)
|
||||
split(list, used|m, depth+1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var seeRE = regexp.MustCompile(`SEE ([^;\n]+)`)
|
||||
|
||||
func updateMask(inst *Inst) bool {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
fmt.Println("PANIC:", err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
print(".")
|
||||
println(inst.Name, inst.ID, inst.Bits)
|
||||
println(inst.Code)
|
||||
wiggle := ^inst.Mask &^ 0xF0000000
|
||||
n := countbits(wiggle)
|
||||
m1 := ^uint32(0)
|
||||
m2 := ^uint32(0)
|
||||
for i := uint32(0); i < 1<<uint(n); i++ {
|
||||
w := inst.Base | expand(i, wiggle)
|
||||
if !isValid(inst, w) {
|
||||
continue
|
||||
}
|
||||
m1 &= w
|
||||
m2 &= ^w
|
||||
}
|
||||
m := m1 | m2
|
||||
m &^= 0xF0000000
|
||||
m |= 0xF0000000 & inst.Mask
|
||||
if m&^inst.Mask != 0 {
|
||||
fmt.Printf("%s %s: mask=%#x but decided %#x\n", inst.Name, inst.ID, inst.Mask, m)
|
||||
inst.Mask = m
|
||||
inst.Base = m1
|
||||
return true
|
||||
}
|
||||
if inst.Mask&^m != 0 {
|
||||
fmt.Printf("%s %s: mask=%#x but got %#x\n", inst.Name, inst.ID, inst.Mask, m)
|
||||
panic("bad updateMask")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func countbits(x uint32) int {
|
||||
n := 0
|
||||
for ; x != 0; x >>= 1 {
|
||||
n += int(x & 1)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func expand(x, m uint32) uint32 {
|
||||
var out uint32
|
||||
for i := uint(0); i < 32; i++ {
|
||||
out >>= 1
|
||||
if m&1 != 0 {
|
||||
out |= (x & 1) << 31
|
||||
x >>= 1
|
||||
}
|
||||
m >>= 1
|
||||
}
|
||||
return out
|
||||
}
|
||||
Reference in New Issue
Block a user