mirror of
https://github.com/cheat-engine/cheat-engine
synced 2026-08-15 02:26:08 -04:00
2138 lines
53 KiB
ObjectPascal
Executable file
2138 lines
53 KiB
ObjectPascal
Executable file
unit DisassemblerArm;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
{$ifdef JNI}
|
|
uses unixporthelper, classes, sysutils, NewKernelHandler, LastDisassembleData, DisassemblerThumb;
|
|
{$endif}
|
|
|
|
{$ifdef windows}
|
|
uses
|
|
windows, Classes, SysUtils{$ifndef ARMDEV}, newkernelhandler, cefuncproc{$endif},
|
|
LastDisassembleData, DisassemblerThumb;
|
|
{$endif}
|
|
|
|
{$ifdef darwin}
|
|
uses macport, classes,SysUtils, newkernelhandler, cefuncproc,LastDisassembleData, DisassemblerThumb;
|
|
{$endif}
|
|
|
|
|
|
|
|
const ArmConditions: array [0..15] of string=('EQ','NE','CS', 'CC', 'MI', 'PL', 'VS', 'VC', 'HI', 'LS', 'GE', 'LT', 'GT', 'LE', '','NV');
|
|
const DataProcessingOpcodes: array [0..15] of string=('AND','EOR','SUB', 'RSB', 'ADD', 'ADC', 'SBC', 'RSC', 'TST', 'TEQ', 'CMP', 'CMN', 'ORR', 'MOV', 'BIC','MVN');
|
|
const ArmRegisters : array [0..15] of string= ('R0','R1','R2','R3','R4','R5','R6','R7','R8','R9','R10','FP', 'IP', 'SP', 'LR', 'PC');
|
|
const ArmRegistersNoName : array [0..15] of string=('R0','R1','R2','R3','R4','R5','R6','R7','R8','R9','R10','R11','R12','R13','R14','R15');
|
|
//const Arm64Registers : array [0..15] of string=('X0','X1','X2','X3','X4','X5','X6','X7','X8','X9','X10','X11','X12','X13','X14','X15','X16');
|
|
function SignExtend(value: int32; mostSignificantBit: integer): int32;
|
|
|
|
|
|
type
|
|
TArmDisassembler=object
|
|
|
|
private
|
|
opcode: uint32;
|
|
function Condition: string;
|
|
procedure Branch;
|
|
procedure BX;
|
|
procedure DataProcessing;
|
|
procedure MRS;
|
|
procedure MSR;
|
|
procedure MSR_flg;
|
|
procedure Mul;
|
|
procedure SingleDataTransfer;
|
|
procedure LDM_STM;
|
|
procedure SWP;
|
|
procedure SWI;
|
|
procedure CDP;
|
|
|
|
|
|
|
|
|
|
public
|
|
LastDisassembleData: TLastDisassembleData;
|
|
function disassemble(var address: ptrUint): string;
|
|
end;
|
|
|
|
|
|
|
|
|
|
implementation
|
|
|
|
{$ifndef ARMDEV}
|
|
uses processhandlerunit, disassemblerarm32, disassemblerarm64;
|
|
{$endif}
|
|
|
|
|
|
function SignExtend(value: int32; mostSignificantBit: integer): int32;
|
|
{
|
|
Signextends a given offset. mostSignificant bit defines what bit determines if it should be sign extended or not
|
|
}
|
|
begin
|
|
if (value shr mostSignificantBit)=1 then //needs to be sign extended
|
|
begin
|
|
//set bits 31 to mostSignificantBit to 1
|
|
result:=value or ($fffffff shl mostSignificantBit);
|
|
end
|
|
else
|
|
result:=value;
|
|
end;
|
|
|
|
function TArmDisassembler.Condition: string;
|
|
begin
|
|
result:=ArmConditions[opcode shr 28];
|
|
end;
|
|
|
|
procedure TArmDisassembler.Branch;
|
|
var offset: int32;
|
|
_condition: string;
|
|
begin
|
|
_condition:=condition;
|
|
if (opcode shr 24) and 1=1 then //Link bit set
|
|
begin
|
|
LastDisassembleData.opcode:='BL'+Condition;
|
|
LastDisassembleData.iscall:=true;
|
|
end
|
|
else
|
|
begin
|
|
LastDisassembleData.opcode:='B'+Condition;
|
|
LastDisassembleData.isjump:=true;
|
|
LastDisassembleData.isconditionaljump:=_condition<>'';
|
|
end;
|
|
|
|
offset:=signextend(opcode and $FFFFFF, 23) shl 2;
|
|
|
|
LastDisassembleData.parameters:=inttohex(dword(LastDisassembleData.address+8+offset),8);
|
|
end;
|
|
|
|
procedure TArmDisassembler.BX;
|
|
var
|
|
Rn: integer;
|
|
_Rn: string;
|
|
_condition: string;
|
|
begin
|
|
_condition:=condition;
|
|
LastDisassembleData.opcode:='BX'+Condition;
|
|
LastDisassembleData.isjump:=true;
|
|
LastDisassembleData.isconditionaljump:=_condition<>'';
|
|
|
|
Rn:=opcode and $F;
|
|
_Rn:=ArmRegisters[Rn];
|
|
|
|
LastDisassembleData.parameters:=_Rn;
|
|
end;
|
|
|
|
procedure TArmDisassembler.DataProcessing;
|
|
var
|
|
ImmediateOperand: integer;
|
|
OpcodeIndex: integer;
|
|
SetCondition: integer;
|
|
Operand1: integer;
|
|
Destination: integer;
|
|
Operand2: integer;
|
|
|
|
shift: integer;
|
|
rm: integer;
|
|
|
|
Rotate: integer;
|
|
Imm: integer;
|
|
|
|
shiftformat: integer;
|
|
shiftType: integer;
|
|
shiftammount: integer;
|
|
shiftRegister: integer;
|
|
|
|
shiftname: string='';
|
|
_shift: string;
|
|
|
|
|
|
_opcode: string;
|
|
_cond: string;
|
|
_S: string;
|
|
_Rd: string;
|
|
_Rn: string;
|
|
_rm: string;
|
|
_rs: string;
|
|
_op2: string;
|
|
begin
|
|
_opcode:='';
|
|
_cond:='';
|
|
_S:='';
|
|
_Rd:='';
|
|
_Rn:='';
|
|
_op2:='';
|
|
|
|
ImmediateOperand:=(opcode shr 25) and 1;
|
|
OpcodeIndex:=(opcode shr 21) and $f;
|
|
SetCondition:=(opcode shr 20) and 1;
|
|
Operand1:=(opcode shr 16) and $f;
|
|
Destination:=(opcode shr 12) and $f;
|
|
Operand2:=opcode and $fff;
|
|
|
|
_opcode:=DataProcessingOpcodes[OpcodeIndex];
|
|
_cond:=Condition;
|
|
if SetCondition=1 then
|
|
_s:='S';
|
|
|
|
|
|
_Rn:=ArmRegisters[Operand1];
|
|
_Rd:=ArmRegisters[Destination];
|
|
|
|
|
|
//build _op2
|
|
if ImmediateOperand=0 then //operand2 is a register
|
|
begin
|
|
shift:=Operand2 shr 4;
|
|
rm:=operand2 and $f;
|
|
|
|
_rm:=ArmRegisters[rm];
|
|
|
|
shiftformat:=shift and 1;
|
|
shiftType:=(shift shr 1) and 3;
|
|
|
|
case shifttype of
|
|
0: shiftname:='LSL';
|
|
1: shiftname:='LSR';
|
|
2: shiftname:='ASR';
|
|
3: shiftname:='ROR';
|
|
end;
|
|
|
|
_shift:=shiftname;
|
|
|
|
|
|
|
|
if shiftformat=0 then
|
|
begin
|
|
shiftAmmount:=(shift shr (7-4)) and $1f; //7-4 because the doc describes the shift field starting from bit 4
|
|
if shiftAmmount>0 then
|
|
_shift:=_shift+' '+inttohex(shiftammount,1)
|
|
else
|
|
_shift:='';
|
|
|
|
end
|
|
else
|
|
begin
|
|
shiftregister:=(shift shr (8-4)) and $f;
|
|
_rs:=ArmRegisters[shiftregister];
|
|
_shift:=_shift+' '+_rs;
|
|
end;
|
|
|
|
if _shift<>'' then
|
|
_op2:=_rm+','+_shift
|
|
else
|
|
_op2:=_rm;
|
|
|
|
end
|
|
else
|
|
begin
|
|
//operand2 is an immediate value
|
|
rotate:=Operand2 shr 8;
|
|
imm:=Operand2 and $ff;
|
|
|
|
|
|
_op2:=inttohex(RorDWord(imm, rotate*2),1);
|
|
end;
|
|
|
|
if _op2<>'' then
|
|
_op2:=','+_op2;
|
|
|
|
case OpcodeIndex of
|
|
13,15:
|
|
begin
|
|
LastDisassembleData.opcode:=_opcode+_cond+_S;
|
|
LastDisassembleData.parameters:=_Rd+_op2;
|
|
end;
|
|
|
|
|
|
8,9,10,11:
|
|
begin
|
|
LastDisassembleData.opcode:=_opcode+_cond;
|
|
LastDisassembleData.parameters:=_Rn+_op2;
|
|
end
|
|
else
|
|
begin
|
|
LastDisassembleData.opcode:=_opcode+_cond+_S;
|
|
LastDisassembleData.parameters:=_Rd+','+_Rn+_op2;
|
|
end;
|
|
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TArmDisassembler.MRS;
|
|
var
|
|
_cond: string;
|
|
_rd: string;
|
|
_psr: string;
|
|
|
|
Ps: integer;
|
|
Rd: integer;
|
|
begin
|
|
_cond:=Condition;
|
|
|
|
Ps:=(opcode shr 22) and 1;
|
|
if (Ps=0) then
|
|
_psr:='CPSR'
|
|
else
|
|
_psr:='SPSR';
|
|
|
|
Rd:=(opcode shr 12) and $F;
|
|
_rd:=ArmRegisters[rd];
|
|
|
|
LastDisassembleData.opcode:='MRS'+_cond;
|
|
LastDisassembleData.parameters:=_Rd+','+_psr;
|
|
end;
|
|
|
|
procedure TArmDisassembler.MSR;
|
|
var
|
|
_cond: string;
|
|
_psr: string;
|
|
_Rm: string;
|
|
|
|
Pd: integer;
|
|
Rm: integer;
|
|
|
|
I: integer;
|
|
imm, rotate: integer;
|
|
begin
|
|
_cond:=Condition;
|
|
|
|
Pd:=(opcode shr 22) and 1;
|
|
if (Pd=0) then
|
|
_psr:='CPSR'
|
|
else
|
|
_psr:='SPSR';
|
|
|
|
Rm:=opcode and $F;
|
|
_rm:=ArmRegisters[rm];
|
|
|
|
LastDisassembleData.opcode:='MSR'+_cond;
|
|
LastDisassembleData.parameters:=_psr+'_all,'+_Rm;
|
|
end;
|
|
|
|
procedure TArmDisassembler.MSR_flg;
|
|
var
|
|
_cond: string;
|
|
_psr: string;
|
|
_Rm: string;
|
|
|
|
Pd: integer;
|
|
Rm: integer;
|
|
|
|
I: integer;
|
|
imm, rotate: integer;
|
|
begin
|
|
_cond:=Condition;
|
|
|
|
Pd:=(opcode shr 22) and 1;
|
|
if (Pd=0) then
|
|
_psr:='CPSR'
|
|
else
|
|
_psr:='SPSR';
|
|
|
|
i:=(opcode shr 25) and 1;
|
|
if i=0 then
|
|
begin
|
|
Rm:=opcode and $F;
|
|
_rm:=ArmRegisters[rm];
|
|
|
|
LastDisassembleData.opcode:='MSR'+_cond;
|
|
LastDisassembleData.parameters:=_psr+'_flg,'+_Rm;
|
|
end
|
|
else
|
|
begin
|
|
rotate:=(opcode and $fff) shr 8;
|
|
imm:=(opcode and $fff) and $ff;
|
|
|
|
LastDisassembleData.opcode:='MSR'+_cond;
|
|
LastDisassembleData.parameters:=_psr+'_flg,'+inttohex(RorDWord(imm, rotate*2),1);
|
|
end;
|
|
end;
|
|
|
|
procedure TArmDisassembler.Mul;
|
|
var
|
|
_cond: string;
|
|
_S: string;
|
|
_rd: string;
|
|
_rm: string;
|
|
_rs: string;
|
|
_rn: string;
|
|
begin
|
|
if (opcode shl 20) and 1=1 then
|
|
_S:='S'
|
|
else
|
|
_S:='';
|
|
|
|
_cond:=Condition;
|
|
|
|
_rd:=ArmRegisters[opcode shl 16 and $f];
|
|
_rn:=ArmRegisters[opcode shl 12 and $f];
|
|
_rs:=ArmRegisters[opcode shl 8 and $f];
|
|
_rm:=ArmRegisters[opcode and $f];
|
|
|
|
|
|
if (opcode shl 21) and 1=1 then
|
|
begin
|
|
LastDisassembleData.opcode:='MLA'+_cond+_S;
|
|
LastDisassembleData.parameters:=_Rd+','+_Rm+','+_Rs+','+_Rn
|
|
end
|
|
else
|
|
begin
|
|
LastDisassembleData.opcode:='MUL'+_cond+_S;
|
|
LastDisassembleData.parameters:=_Rd+','+_Rm+','+_Rs;
|
|
end;
|
|
end;
|
|
|
|
procedure TArmDisassembler.SingleDataTransfer;
|
|
var
|
|
_cond: string;
|
|
_opcode: string;
|
|
_B: string;
|
|
_T: string;
|
|
_Rd: string;
|
|
_Rn: string;
|
|
_Rm: string;
|
|
_shift: string;
|
|
_U: string;
|
|
|
|
I: integer;
|
|
P: integer;
|
|
U: integer;
|
|
B: integer;
|
|
W: integer;
|
|
L: integer;
|
|
Rn: integer;
|
|
Rd: integer;
|
|
Rm: integer;
|
|
Offset: integer;
|
|
|
|
|
|
shift: integer;
|
|
shifttype: integer;
|
|
|
|
_shiftname: string='';
|
|
_shiftAmount: string;
|
|
shiftamount: integer;
|
|
|
|
|
|
_address: string;
|
|
begin
|
|
I:=(opcode shr 25) and 1;
|
|
P:=(opcode shr 24) and 1;
|
|
U:=(opcode shr 23) and 1;
|
|
B:=(opcode shr 22) and 1;
|
|
W:=(opcode shr 21) and 1;
|
|
L:=(opcode shr 20) and 1;
|
|
Rn:=(opcode shr 16) and $f;
|
|
Rd:=(opcode shr 12) and $f;
|
|
offset:=opcode and $fff;
|
|
|
|
if L=1 then
|
|
_opcode:='LDR'
|
|
else
|
|
_opcode:='STR';
|
|
|
|
if u=0 then
|
|
_U:='-'
|
|
else
|
|
_U:='';
|
|
|
|
_cond:=Condition;
|
|
|
|
if b=1 then
|
|
_B:='B'
|
|
else
|
|
_B:='';
|
|
|
|
if (W=1) and (p=0) then //w is set and p=0 (post indexed)
|
|
_T:='T'
|
|
else
|
|
_T:='';
|
|
|
|
_Rd:=ArmRegisters[Rd];
|
|
|
|
_Rn:=ArmRegisters[Rn];
|
|
|
|
if i=0 then
|
|
begin
|
|
//offset is an immediate value
|
|
if Rn=15 then //pc
|
|
begin
|
|
if u=0 then //decrease
|
|
_address:='['+inttohex(dword(LastDisassembleData.address-offset+8),8)+']'
|
|
else
|
|
_address:='['+inttohex(dword(LastDisassembleData.address+offset+8),8)+']'
|
|
end
|
|
else
|
|
begin
|
|
if offset=0 then
|
|
_address:='['+_Rn+']'
|
|
else
|
|
_address:='['+_Rn+','+_U+inttohex(dword(offset),8)+']';
|
|
end;
|
|
|
|
end
|
|
else
|
|
begin
|
|
//offset is a register
|
|
Rm:=offset and $f;
|
|
Shift:=offset shr 4;
|
|
|
|
_Rm:=ArmRegisters[Rm];
|
|
|
|
shiftType:=(shift shr 1) and 3;
|
|
|
|
case shifttype of
|
|
0: _shiftname:='LSL';
|
|
1: _shiftname:='LSR';
|
|
2: _shiftname:='ASR';
|
|
3: _shiftname:='ROR';
|
|
end;
|
|
|
|
|
|
shiftAmount:=(shift shr (7-4)) and $1f;
|
|
if shiftAmount>0 then
|
|
begin
|
|
_shiftAmount:=inttohex(shiftamount,1);
|
|
|
|
_shift:=', '+_U+_Rm+' '+_shiftname+' '+_shiftamount;
|
|
end
|
|
else
|
|
begin
|
|
_shiftAmount:='';
|
|
_shift:=', '+_Rm;
|
|
end;
|
|
|
|
if p=0 then //post index
|
|
_address:='['+_Rn+']'+_shift
|
|
else //preindexed
|
|
_address:='['+_Rn+_shift+']';
|
|
end;
|
|
|
|
if (w=1) and (p=1) then
|
|
_address:=_address+'!';
|
|
|
|
|
|
LastDisassembleData.opcode:=_opcode+_cond+_B+_T;
|
|
LastDisassembleData.parameters:=_Rd+','+_Address;
|
|
end;
|
|
|
|
procedure TArmDisassembler.LDM_STM;
|
|
var
|
|
P: integer;
|
|
U: integer;
|
|
S: integer;
|
|
W: integer;
|
|
L: integer;
|
|
Rn: integer;
|
|
RegisterList: integer;
|
|
|
|
_opcode: string;
|
|
_cond: string;
|
|
_addressingmode: string;
|
|
_rn: string;
|
|
_ex: string;
|
|
_exp: string;
|
|
_rlist: string;
|
|
|
|
|
|
i: integer;
|
|
rcount: integer;
|
|
|
|
|
|
|
|
begin
|
|
|
|
_cond:=Condition;
|
|
p:=(opcode shr 24) and 1;
|
|
u:=(opcode shr 23) and 1;
|
|
s:=(opcode shr 22) and 1;
|
|
w:=(opcode shr 21) and 1;
|
|
l:=(opcode shr 20) and 1;
|
|
Rn:=(opcode shr 16) and $f;
|
|
RegisterList:=opcode and $ffff;
|
|
|
|
|
|
_rlist:='{';
|
|
rcount:=0;
|
|
|
|
for i:=0 to 15 do
|
|
begin
|
|
if (RegisterList shr i) and 1=1 then
|
|
begin
|
|
if rcount=0 then
|
|
_rlist:=_rlist+ArmRegisters[i]
|
|
else
|
|
_rlist:=_rlist+', '+ArmRegisters[i];
|
|
|
|
inc(rcount);
|
|
end;
|
|
end;
|
|
|
|
_rlist:=_rlist+'}';
|
|
|
|
IF (rn=%1101) and (rcount>=2) then
|
|
begin
|
|
_opcode:='PUSH';
|
|
LastDisassembleData.opcode:=_opcode+_cond;
|
|
LastDisassembleData.parameters:=_rlist;
|
|
exit;
|
|
end
|
|
else
|
|
begin
|
|
if L=0 then
|
|
_opcode:='STM'
|
|
else
|
|
_opcode:='LDM';
|
|
end;
|
|
|
|
_rn:=ArmRegisters[Rn];
|
|
|
|
if w=1 then
|
|
_ex:='!'
|
|
else
|
|
_ex:='';
|
|
|
|
if s=1 then
|
|
_exp:='^'
|
|
else
|
|
_exp:='';
|
|
|
|
|
|
_addressingmode:='';
|
|
if (L=1) and (P=1) and (U=1) then
|
|
begin
|
|
if rn=13 then
|
|
_addressingmode:='ED'
|
|
else
|
|
_addressingmode:='IB';
|
|
end;
|
|
|
|
if (L=1) and (P=0) and (U=1) then
|
|
begin
|
|
if rn=13 then
|
|
_addressingmode:='FD'
|
|
else
|
|
_addressingmode:='IA';
|
|
end;
|
|
|
|
if (L=1) and (P=1) and (U=0) then
|
|
begin
|
|
if rn=13 then
|
|
_addressingmode:='EA'
|
|
else
|
|
_addressingmode:='DB';
|
|
end;
|
|
|
|
if (L=1) and (P=0) and (U=0) then
|
|
begin
|
|
if rn=13 then
|
|
_addressingmode:='FA'
|
|
else
|
|
_addressingmode:='DA';
|
|
end;
|
|
|
|
if (L=0) and (P=1) and (U=1) then
|
|
begin
|
|
if rn=13 then
|
|
_addressingmode:='FA'
|
|
else
|
|
_addressingmode:='IB';
|
|
end;
|
|
|
|
if (L=0) and (P=0) and (U=1) then
|
|
begin
|
|
if rn=13 then
|
|
_addressingmode:='EA'
|
|
else
|
|
_addressingmode:='IA';
|
|
end;
|
|
|
|
if (L=0) and (P=1) and (U=0) then
|
|
begin
|
|
if rn=13 then
|
|
_addressingmode:='FD'
|
|
else
|
|
_addressingmode:='DB';
|
|
end;
|
|
|
|
if (L=0) and (P=0) and (U=0) then
|
|
begin
|
|
if rn=13 then
|
|
_addressingmode:='ED'
|
|
else
|
|
_addressingmode:='DA';
|
|
end;
|
|
|
|
LastDisassembleData.opcode:=_opcode+_cond+_addressingmode;
|
|
LastDisassembleData.parameters:=_rn+_ex+','+_rlist+_exp;
|
|
end;
|
|
|
|
procedure TArmDisassembler.SWP;
|
|
var
|
|
_cond: string;
|
|
_B: string;
|
|
_rd: string;
|
|
_rm: string;
|
|
_rn: string;
|
|
begin
|
|
_cond:=Condition;
|
|
if (opcode shr 22) and 1=1 then
|
|
_B:='1'
|
|
else
|
|
_B:='';
|
|
|
|
_rd:=ArmRegisters[(opcode shr 12) and $f];
|
|
_rm:=ArmRegisters[opcode and $f];
|
|
_rn:=ArmRegisters[(opcode shr 16) and $f];
|
|
|
|
LastDisassembleData.opcode:='SWP'+_cond+_B;
|
|
LastDisassembleData.parameters:=_rd+','+_rm+',['+_rn+']';
|
|
end;
|
|
|
|
procedure TArmDisassembler.SWI;
|
|
begin
|
|
LastDisassembleData.opcode:='SWI'+Condition;
|
|
LastDisassembleData.parameters:=inttohex(opcode and $FFFFFF,1);
|
|
end;
|
|
|
|
procedure FloatingPointDataInstruction(var LastDisassembleData: TLastDisassembleData);
|
|
var
|
|
opcode: dword;
|
|
cond: byte;
|
|
t: byte;
|
|
|
|
opc1, opc2, opc3, opc4: byte;
|
|
condition: string;
|
|
|
|
D, Vn, Vd, sz, N, M, Vm: byte;
|
|
cc: byte;
|
|
begin
|
|
opcode:=pdword(LastDisassembleData.Bytes)^;
|
|
|
|
if lastdisassembledata.Disassembler=dcArm then
|
|
begin
|
|
cond:=(opcode shr 28) and $f;
|
|
if (cond=$f) then
|
|
t:=1
|
|
else
|
|
t:=0;
|
|
|
|
condition:=ArmConditions[cond];
|
|
end
|
|
else
|
|
begin
|
|
opcode:=(opcode shr 16) or (opcode shl 16);
|
|
t:=(opcode shr 28) and 1;
|
|
|
|
pdword(@lastdisassembledata.Bytes[0])^:=opcode;
|
|
condition:='';
|
|
end;
|
|
|
|
|
|
|
|
opc1:=(opcode shr 20) and $f;
|
|
opc2:=(opcode shr 16) and $f;
|
|
opc3:=(opcode shr 6) and 3;
|
|
opc4:=opcode and $f;
|
|
|
|
sz:=(opcode shr 8) and 1;
|
|
|
|
D:=(opcode shr 22) and 1;
|
|
Vn:=(opcode shr 16) and $f;
|
|
Vd:=(opcode shr 12) and $f;
|
|
N:=(opcode shr 7) and 1;
|
|
M:=(opcode shr 5) and 1;
|
|
Vm:=opcode and $f;
|
|
|
|
|
|
if sz=0 then
|
|
begin
|
|
Vn:=(Vn shl 1) or N;
|
|
Vm:=(Vm shl 1) or M;
|
|
Vd:=(Vd shl 1) or D;
|
|
LastDisassembleData.parameters:='S'+inttostr(vd)+', S'+inttostr(vn)+', S'+inttostr(Vm);
|
|
end
|
|
else
|
|
begin
|
|
Vn:=(N shl 4) or Vn;
|
|
Vm:=(M shl 4) or Vm;
|
|
Vd:=(D shl 4) or Vd;
|
|
LastDisassembleData.parameters:='D'+inttostr(vd)+', D'+inttostr(vn)+', D'+inttostr(Vm);
|
|
end;
|
|
|
|
case (t shl 4) or opc1 of
|
|
0,4: if (opc3 and 1)=1 then LastDisassembleData.opcode:='VMLS' else LastDisassembleData.opcode:='VMLA';
|
|
1,5: if (opc3 and 1)=1 then LastDisassembleData.opcode:='VNMLA' else LastDisassembleData.opcode:='VNMLS';
|
|
2,6: if (opc3 and 1)=1 then LastDisassembleData.opcode:='VNMUL' else LastDisassembleData.opcode:='VMUL';
|
|
3,7: if (opc3 and 1)=1 then LastDisassembleData.opcode:='VADD' else LastDisassembleData.opcode:='VSUB';
|
|
8,12: if (opc3 and 1)=0 then LastDisassembleData.opcode:='VDIV';
|
|
9,13: if (opc3 and 1)=1 then LastDisassembleData.opcode:='VFNMA' else LastDisassembleData.opcode:='VFNMS';
|
|
10,14: if (opc3 and 1)=1 then LastDisassembleData.opcode:='VFMS' else LastDisassembleData.opcode:='VFMA';
|
|
|
|
16..23:
|
|
begin
|
|
case (opcode shr 20) and 3 of
|
|
0: LastDisassembleData.opcode:='VSELEQ';
|
|
1: LastDisassembleData.opcode:='VSELVS';
|
|
2: LastDisassembleData.opcode:='VSELGE';
|
|
3: LastDisassembleData.opcode:='VSELGT';
|
|
end;
|
|
end;
|
|
|
|
24,28: if (opc3 and 1)=1 then LastDisassembleData.opcode:='VMINNM' else LastDisassembleData.opcode:='VMAXNM';
|
|
|
|
11,15, 27, 31:
|
|
begin
|
|
//other:
|
|
exit;
|
|
|
|
end;
|
|
end;
|
|
|
|
if sz=0 then
|
|
LastDisassembleData.opcode:=LastDisassembleData.opcode+condition+'.F32'
|
|
else
|
|
LastDisassembleData.opcode:=LastDisassembleData.opcode+condition+'.F64';
|
|
|
|
if lastdisassembledata.Disassembler=dcThumb then //undo the wordswap
|
|
begin
|
|
opcode:=(opcode shr 16) or (opcode shl 16);
|
|
pdword(@lastdisassembledata.Bytes[0])^:=opcode;
|
|
end;
|
|
end;
|
|
|
|
procedure TArmDisassembler.CDP;
|
|
var
|
|
CP_Opc: integer;
|
|
CRn: integer;
|
|
CRd: integer;
|
|
CPn: integer;
|
|
CP: integer;
|
|
CRm: integer;
|
|
|
|
_pn: string;
|
|
|
|
_cd,_cn,_cm: string;
|
|
|
|
_expression1,_expression2: string;
|
|
begin
|
|
|
|
CP_Opc:=(opcode shr 20) and $f; //
|
|
CRd:=(opcode shr 12) and $f; //
|
|
CRn:=(opcode shr 16) and $f; //
|
|
CRm:=opcode and $f; //
|
|
CPn:=(opcode shr 8) and $f;
|
|
CP:=(opcode shr 5) and $7; //
|
|
|
|
if (CPn in [10,11]) and ((opcode and $10)=$10) then
|
|
begin
|
|
FloatingPointDataInstruction(LastDisassembleData);
|
|
exit;
|
|
end;
|
|
|
|
|
|
_expression1:=inttohex(CP_OPC,1);
|
|
if cp<>0 then
|
|
_expression2:=inttohex(CP,1)
|
|
else
|
|
_expression2:='';
|
|
|
|
_pn:=inttohex(cp,1);
|
|
|
|
_cd:='c'+inttostr(CRd);
|
|
_cn:='c'+inttostr(CRn);
|
|
_cm:='c'+inttostr(CRm);
|
|
|
|
|
|
|
|
|
|
//CDP{cond} p#,<expression1>,cd,cn,cm{,<expression2>}
|
|
|
|
LastDisassembleData.opcode:='CDP'+Condition;
|
|
LastDisassembleData.parameters:=_pn+','+_expression1+','+_cd+','+_cn+','+_cm+_expression2;
|
|
end;
|
|
|
|
procedure ASIMD_3RegSL(var LastDisassembleData: TLastDisassembleData);
|
|
var
|
|
opcode: dword;
|
|
u,c,a,b: byte;
|
|
d: byte;
|
|
size: byte;
|
|
Vn, Vd, op, N, Q, M, Vm: byte;
|
|
_datatype: string;
|
|
begin
|
|
opcode:=pdword(@LastDisassembleData.Bytes[0])^;
|
|
|
|
if LastDisassembleData.Disassembler=dcThumb then
|
|
u:=(opcode shr 28) and 1
|
|
else
|
|
u:=(opcode shr 24) and 1;
|
|
|
|
a:=(opcode shr 8) and $f;
|
|
b:=(opcode shr 4) and 1;
|
|
c:=(opcode shr 20) and 3;
|
|
|
|
D:=(opcode shr 22) and 1;
|
|
size:=(opcode shr 20) and 3;
|
|
Vn:=(opcode shr 16) and $f;
|
|
Vd:=(opcode shr 12) and $f;
|
|
op:=(opcode shr 9) and 1;
|
|
N:=(opcode shr 7) and 1;
|
|
Q:=(opcode shr 6) and 1;
|
|
M:=(opcode shr 5) and 1;
|
|
Vm:=opcode and $f;
|
|
|
|
Vn:=(N shl 4) or Vn;
|
|
Vm:=(M shl 4) or Vm;
|
|
Vd:=(D shl 4) or Vd;
|
|
|
|
if u=0 then
|
|
_datatype:='S'
|
|
else
|
|
_datatype:='U';
|
|
|
|
case size of
|
|
0: _datatype:=_datatype+'8';
|
|
1: _datatype:=_datatype+'16';
|
|
2: _datatype:=_datatype+'32';
|
|
3: _datatype:=_datatype+'64'; //?
|
|
end;
|
|
|
|
|
|
if q=1 then
|
|
LastDisassembleData.parameters:='.'+_datatype+' '+'D'+inttostr(vd)+', D'+inttostr(vn)+', D'+inttostr(Vm)
|
|
else
|
|
LastDisassembleData.parameters:='.'+_datatype+' '+'Q'+inttostr(vd)+', Q'+inttostr(vn)+', Q'+inttostr(Vm);
|
|
|
|
|
|
case a of
|
|
0: if b=0 then LastDisassembleData.opcode:='VHADD' else LastDisassembleData.opcode:='VQADD';
|
|
1:
|
|
begin
|
|
if b=0 then LastDisassembleData.opcode:='VRHADD' else
|
|
begin
|
|
case (u shl 2) or c of
|
|
0: LastDisassembleData.opcode:='VAND';
|
|
1: LastDisassembleData.opcode:='VBIC';
|
|
2: LastDisassembleData.opcode:='VORR';
|
|
3: LastDisassembleData.opcode:='VORN';
|
|
4: LastDisassembleData.opcode:='VEOR';
|
|
5: LastDisassembleData.opcode:='VBSL';
|
|
6: LastDisassembleData.opcode:='VBIT';
|
|
7: LastDisassembleData.opcode:='VBIF';
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
2: if b=0 then LastDisassembleData.opcode:='VHSUB' else LastDisassembleData.opcode:='VQSUB';
|
|
3: if b=0 then LastDisassembleData.opcode:='VCGT' else LastDisassembleData.opcode:='VCGE';
|
|
4: if b=0 then LastDisassembleData.opcode:='VSHL' else LastDisassembleData.opcode:='VQSHL';
|
|
5: if b=0 then LastDisassembleData.opcode:='VRSHL' else LastDisassembleData.opcode:='VQRSHL';
|
|
6: if b=0 then LastDisassembleData.opcode:='VMAX' else LastDisassembleData.opcode:='VMIN';
|
|
7: if b=0 then LastDisassembleData.opcode:='VABD' else LastDisassembleData.opcode:='VABA';
|
|
8: if b=0 then
|
|
begin
|
|
if u=0 then LastDisassembleData.opcode:='VADD' else LastDisassembleData.opcode:='VSUB';
|
|
end
|
|
else
|
|
begin
|
|
if u=0 then LastDisassembleData.opcode:='VTST' else LastDisassembleData.opcode:='VCEQ';
|
|
end;
|
|
|
|
9: if b=0 then
|
|
begin
|
|
if u=0 then LastDisassembleData.opcode:='VMLA' else LastDisassembleData.opcode:='VMLS';
|
|
end
|
|
else LastDisassembleData.opcode:='VMUL';
|
|
|
|
10: if b=0 then LastDisassembleData.opcode:='VPMAX' else LastDisassembleData.opcode:='VPMIN';
|
|
11: if b=0 then
|
|
begin
|
|
if u=0 then LastDisassembleData.opcode:='VQDMULH' else LastDisassembleData.opcode:='VQRDMULH';
|
|
end
|
|
else LastDisassembleData.opcode:='VPADD';
|
|
|
|
12: case (b shl 3) or (u shl 2) or c of
|
|
0: LastDisassembleData.opcode:='SHA1C';
|
|
1: LastDisassembleData.opcode:='SHA1P';
|
|
2: LastDisassembleData.opcode:='SHA1M';
|
|
3: LastDisassembleData.opcode:='SHA1SU0';
|
|
4: LastDisassembleData.opcode:='SHA256H';
|
|
5: LastDisassembleData.opcode:='SHA256H2';
|
|
6: LastDisassembleData.opcode:='SHA256SU1';
|
|
8: LastDisassembleData.opcode:='VFMA';
|
|
10:LastDisassembleData.opcode:='VFMS';
|
|
end;
|
|
|
|
13: case (b shl 3) or (u shl 2) or c of
|
|
0,1: LastDisassembleData.opcode:='VADD';
|
|
2,3: LastDisassembleData.opcode:='VSUB';
|
|
4,5: LastDisassembleData.opcode:='VPADD';
|
|
6,7: LastDisassembleData.opcode:='VABD';
|
|
8: LastDisassembleData.opcode:='VMLA';
|
|
10: LastDisassembleData.opcode:='VMLS';
|
|
12,13: LastDisassembleData.opcode:='VMUL';
|
|
end;
|
|
|
|
14: case (b shl 3) or (u shl 2) or c of
|
|
0,1: LastDisassembleData.opcode:='VCEQ';
|
|
4,5: LastDisassembleData.opcode:='VCGE';
|
|
6,7: LastDisassembleData.opcode:='VCGT';
|
|
12: LastDisassembleData.opcode:='VACGE';
|
|
14: LastDisassembleData.opcode:='VACGT';
|
|
end;
|
|
|
|
15: case (b shl 3) or (u shl 2) or c of
|
|
0: LastDisassembleData.opcode:='VMAX';
|
|
2: LastDisassembleData.opcode:='VMIN';
|
|
4: LastDisassembleData.opcode:='VPMAX';
|
|
6: LastDisassembleData.opcode:='VPMIN';
|
|
8,9: LastDisassembleData.opcode:='VRECPS';
|
|
10,11: LastDisassembleData.opcode:='VRSQRTS';
|
|
12: LastDisassembleData.opcode:='VMAXNM';
|
|
14: LastDisassembleData.opcode:='VMINNM';
|
|
end;
|
|
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
procedure ASIMD_1Reg_ModImm(var LastDisassembleData: TLastDisassembleData);
|
|
var opcode: dword;
|
|
a,b,c,d,cmode,op,e,f,g,h: byte;
|
|
b2: byte;
|
|
|
|
q: byte;
|
|
|
|
Vd: byte;
|
|
|
|
|
|
abcdefgh: byte;
|
|
constant: qword;
|
|
_dt: string='';
|
|
|
|
a32: dword;
|
|
begin
|
|
opcode:=pdword(@LastDisassembleData.Bytes[0])^;
|
|
|
|
if LastDisassembleData.Disassembler=dcThumb then
|
|
a:=(opcode shr 28) and 1
|
|
else
|
|
a:=(opcode shr 24) and 1;
|
|
|
|
b:=(opcode shr 18) and 1;
|
|
c:=(opcode shr 17) and 1;
|
|
d:=(opcode shr 16) and 1;
|
|
|
|
cmode:=(opcode shr 8) and $f;
|
|
Q:=(opcode shr 6) and 1;
|
|
op:=(opcode shr 5) and 1;
|
|
|
|
e:=(opcode shr 3) and 1;
|
|
f:=(opcode shr 2) and 1;
|
|
g:=(opcode shr 1) and 1;
|
|
h:=opcode and 1;
|
|
|
|
Vd:=((opcode shr (22-4)) and $1) or (opcode shr 12) and $f;
|
|
|
|
|
|
abcdefgh:=(a shl 7) or (b shl 6) or (c shl 5) or (d shl 4) or (e shl 3) or (f shl 2) or (g shl 1) or h;
|
|
|
|
constant:=0;
|
|
case cmode of
|
|
0:
|
|
begin
|
|
constant:=(qword(abcdefgh) shl 32) or abcdefgh;
|
|
_dt:='I32';
|
|
end;
|
|
|
|
2:
|
|
begin
|
|
constant:=(qword(abcdefgh) shl 40) or (qword(abcdefgh) shl 8);
|
|
_dt:='I32';
|
|
end;
|
|
|
|
4:
|
|
begin
|
|
constant:=(qword(abcdefgh) shl 48) or (qword(abcdefgh) shl 16);
|
|
_dt:='I32';
|
|
end;
|
|
|
|
|
|
6:
|
|
begin
|
|
constant:=(qword(abcdefgh) shl 56) or (qword(abcdefgh) shl 24);
|
|
_dt:='I32';
|
|
end;
|
|
|
|
8:
|
|
begin
|
|
constant:=(qword(abcdefgh) shl 48) or (qword(abcdefgh) shl 32) or (qword(abcdefgh) shl 16) or abcdefgh;
|
|
_dt:='I16';
|
|
end;
|
|
|
|
10:
|
|
begin
|
|
constant:=(qword(abcdefgh) shl 56) or (qword(abcdefgh) shl 40) or (qword(abcdefgh) shl 24) or (abcdefgh shl 8);
|
|
_dt:='I16';
|
|
end;
|
|
|
|
12:
|
|
begin
|
|
constant:=(qword(abcdefgh) shl 40) or (qword($ff) shl 32) or (qword(abcdefgh) shl 8) or ($ff);
|
|
_dt:='I32';
|
|
end;
|
|
|
|
13:
|
|
begin
|
|
constant:=(qword(abcdefgh) shl 48) or (qword($ff) shl 40) or (qword($ff) shl 32) or (qword(abcdefgh) shl 16) or (qword($ff) shl 8) or ($ff);
|
|
_dt:='I32';
|
|
end;
|
|
|
|
14:
|
|
begin
|
|
if op=0 then
|
|
begin
|
|
constant:=(qword(abcdefgh) shl 56) or (qword(abcdefgh) shl 48) or (qword(abcdefgh) shl 40) or (qword(abcdefgh) shl 32) or (qword(abcdefgh) shl 24) or (abcdefgh shl 16) or (abcdefgh shl 8);
|
|
_dt:='I8';
|
|
end
|
|
else
|
|
begin
|
|
a:=(a shl 7) or (a shl 6) or (a shl 5) or (a shl 4) or (a shl 3) or (a shl 2) or (a shl 1) or a;
|
|
b:=(b shl 7) or (b shl 6) or (b shl 5) or (b shl 4) or (b shl 3) or (b shl 2) or (b shl 1) or b;
|
|
c:=(c shl 7) or (c shl 6) or (c shl 5) or (c shl 4) or (c shl 3) or (c shl 2) or (c shl 1) or c;
|
|
d:=(d shl 7) or (d shl 6) or (d shl 5) or (d shl 4) or (d shl 3) or (d shl 2) or (d shl 1) or d;
|
|
e:=(e shl 7) or (e shl 6) or (e shl 5) or (e shl 4) or (e shl 3) or (e shl 2) or (e shl 1) or e;
|
|
f:=(f shl 7) or (f shl 6) or (f shl 5) or (f shl 4) or (f shl 3) or (f shl 2) or (f shl 1) or f;
|
|
g:=(g shl 7) or (g shl 6) or (g shl 5) or (g shl 4) or (g shl 3) or (g shl 2) or (g shl 1) or g;
|
|
h:=(h shl 7) or (h shl 6) or (h shl 5) or (h shl 4) or (h shl 3) or (h shl 2) or (h shl 1) or h;
|
|
|
|
constant:=(qword(a) shl 56) or (qword(b) shl 48) or (qword(c) shl 40) or (qword(d) shl 32) or (qword(e) shl 24) or (qword(f) shl 16) or (qword(g) shl 16) or qword(h);
|
|
_dt:='I64';
|
|
|
|
end;
|
|
end;
|
|
|
|
15:
|
|
begin
|
|
if op=0 then
|
|
begin
|
|
b2:=(not b) and 1;
|
|
a32:=0;
|
|
a32:=(a shl 31) or (b2 shl 30) or (b shl 29) or (b shl 28) or (b shl 27) or (b shl 26) or (b shl 25) or (c shl 24) or (d shl 23) or (e shl 22) or (e shl 21) or (f shl 20) or (g shl 19) or (h shl 18);
|
|
constant:=(qword(a32) shl 32) or a32;
|
|
end
|
|
else
|
|
begin
|
|
constant:=qword($BADBADBADBADBAD)
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
case (op shl 4) or cmode of
|
|
0,2,4,6: LastDisassembleData.opcode:='VMOV.'+_dt;
|
|
1,3,5,7: LastDisassembleData.opcode:='VORR.'+_dt;
|
|
8,10: LastDisassembleData.opcode:='VMOV.'+_dt;
|
|
9,11: LastDisassembleData.opcode:='VORR.'+_dt;
|
|
12,13,14,15: LastDisassembleData.opcode:='VMOV.'+_dt;
|
|
|
|
16,18,20,22: LastDisassembleData.opcode:='VMVN.'+_dt;
|
|
17,19,21,23: LastDisassembleData.opcode:='VBIC.'+_dt;
|
|
24,26: LastDisassembleData.opcode:='VMVN.'+_dt;
|
|
25,27: LastDisassembleData.opcode:='VBIC.'+_dt;
|
|
28,29: LastDisassembleData.opcode:='VMVN.'+_dt;
|
|
30: LastDisassembleData.opcode:='VMOV.'+_dt;
|
|
end;
|
|
|
|
|
|
if Q=0 then
|
|
LastDisassembleData.parameters:='D'+inttostr(vd)+', '+inttohex(constant,1)
|
|
else
|
|
LastDisassembleData.parameters:='Q'+inttostr(vd)+', '+inttohex(constant,1);
|
|
|
|
|
|
end;
|
|
|
|
procedure ASIMD_2Reg_Shift(var LastDisassembleData: TLastDisassembleData);
|
|
var opcode: dword;
|
|
u: byte;
|
|
imm3, imm6: byte;
|
|
M, D,A,L,B,Q: byte;
|
|
Vd, Vm: byte;
|
|
size: integer;
|
|
_type, _size: string;
|
|
limm3: byte;
|
|
imm: byte;
|
|
esize: byte;
|
|
elements: byte;
|
|
|
|
op: byte;
|
|
|
|
_dt1: string='';
|
|
_dt2: string='';
|
|
begin
|
|
opcode:=pdword(@LastDisassembleData.Bytes[0])^;
|
|
imm:=0;
|
|
|
|
if LastDisassembleData.Disassembler=dcThumb then
|
|
u:=(opcode shr 28) and 1
|
|
else
|
|
u:=(opcode shr 24) and 1;
|
|
|
|
d:=(opcode shr 22) and 1;
|
|
imm3:=(opcode shr 19) and $7;
|
|
imm6:=(opcode shr 16) and $3f;
|
|
Vd:=(opcode shr 12) and $f;
|
|
a:=(opcode shr 8) and $f;
|
|
op:=(opcode shr 8) and 1;
|
|
l:=(opcode shr 7) and 1;
|
|
b:=(opcode shr 6) and 1;
|
|
q:=b;
|
|
m:=(opcode shr 5) and 1;
|
|
Vm:=opcode and $f;
|
|
|
|
Vm:=(m shl 4) or vm;
|
|
Vd:=(d shl 4) or vd;
|
|
|
|
|
|
limm3:=(l shl 3) or imm3;
|
|
|
|
if (limm3=0) then ASIMD_1Reg_ModImm(LastDisassembleData);
|
|
|
|
_type:='';
|
|
|
|
//size:
|
|
size:=8;
|
|
if (limm3 and 1)=1 then size:=8 else
|
|
if (limm3 and 2)=2 then size:=16 else
|
|
if (limm3 and 4)=4 then size:=32 else
|
|
if (limm3 and 8)=8 then size:=64;
|
|
|
|
esize:=size;
|
|
|
|
_size:=inttostr(size);
|
|
|
|
//imm:
|
|
if a in [0,1,2,3,4] then
|
|
begin
|
|
if size=32 then size:=64;
|
|
imm:=size-imm6;
|
|
end;
|
|
|
|
if a in [5,6,7] then
|
|
begin
|
|
case size of
|
|
8: imm:=imm6-8;
|
|
16: imm:=imm6-16;
|
|
32: imm:=imm6-32;
|
|
64: imm:=imm6;
|
|
end;
|
|
end;
|
|
|
|
|
|
//_type:
|
|
if u=0 then _type:='S' else _type:='U';
|
|
|
|
|
|
if Q=0 then
|
|
LastDisassembleData.parameters:='D'+inttostr(vd)+', D'+inttostr(vm)+', '+inttohex(imm,1)
|
|
else
|
|
LastDisassembleData.parameters:='Q'+inttostr(vd)+', Q'+inttostr(vm)+', '+inttohex(imm,1);
|
|
|
|
|
|
//instruction:
|
|
case A of
|
|
0: LastDisassembleData.opcode:='VSHR'+_type+_size;
|
|
1: LastDisassembleData.opcode:='VSRA'+_type+_size;
|
|
2: LastDisassembleData.opcode:='VRSHR'+_type+_size;
|
|
3: LastDisassembleData.opcode:='VRSRA'+_type+_size;
|
|
4: if u=1 then LastDisassembleData.opcode:='VSRI'+_size;
|
|
5: if u=0 then LastDisassembleData.opcode:='VSHL.I'+_size else LastDisassembleData.opcode:='VSLI'+_type+_size;
|
|
6: LastDisassembleData.opcode:='VQSHLU'+_type+_size;
|
|
7: LastDisassembleData.opcode:='VQSHL'+_type+_size;
|
|
8:
|
|
begin
|
|
size:=size*2;
|
|
_size:=inttostr(size);
|
|
imm:=size-imm6;
|
|
LastDisassembleData.parameters:='D'+inttostr(vd)+', Q'+inttostr(vm)+', '+inttohex(imm,1);
|
|
|
|
case (u shl 3) or (b shl 2) or l of
|
|
0: LastDisassembleData.opcode:='VSHRN.I'+_size;
|
|
2: LastDisassembleData.opcode:='VRSHRN.I'+_size;
|
|
4: LastDisassembleData.opcode:='VQSHRUN.I'+_type+_size;
|
|
6: LastDisassembleData.opcode:='VQRSHRUN.I'+_type+_size;
|
|
end;
|
|
end;
|
|
|
|
9:
|
|
begin
|
|
size:=size*2;
|
|
_size:=inttostr(size);
|
|
imm:=size-imm6;
|
|
LastDisassembleData.parameters:='D'+inttostr(vd)+', Q'+inttostr(vm)+', '+inttohex(imm,1);
|
|
|
|
case (b shl 2) or l of
|
|
0: LastDisassembleData.opcode:='VSHRN.I'+_size;
|
|
2: LastDisassembleData.opcode:='VRSHRN.I'+_size;
|
|
end;
|
|
end;
|
|
|
|
10:
|
|
begin
|
|
if (b=0) and (l=0) then
|
|
begin
|
|
imm:=imm6-esize;
|
|
if imm=0 then
|
|
begin
|
|
LastDisassembleData.parameters:='D'+inttostr(vd)+', Q'+inttostr(vm);
|
|
LastDisassembleData.opcode:='VMOVL';
|
|
end
|
|
else
|
|
begin
|
|
LastDisassembleData.parameters:='D'+inttostr(vd)+', Q'+inttostr(vm)+', '+inttohex(imm,1);
|
|
LastDisassembleData.opcode:='VSHLL'+_type+_size;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
14,15:
|
|
begin
|
|
case (op shl 1) or U of
|
|
0:
|
|
begin
|
|
_dt1:='F32';
|
|
_dt2:='S32';
|
|
end;
|
|
|
|
1:
|
|
begin
|
|
_dt1:='F32';
|
|
_dt2:='U32';
|
|
end;
|
|
|
|
2:
|
|
begin
|
|
_dt1:='S32';
|
|
_dt2:='F32';
|
|
end;
|
|
|
|
3:
|
|
begin
|
|
_dt1:='U32';
|
|
_dt2:='F32';
|
|
end;
|
|
end;
|
|
LastDisassembleData.opcode:='VCVT.'+_dt1+'.'+_dt2;
|
|
|
|
if Q=0 then
|
|
LastDisassembleData.parameters:='D'+inttostr(vd)+', D'+inttostr(vm)+', '+inttohex(64-imm6,1)
|
|
else
|
|
LastDisassembleData.parameters:='Q'+inttostr(vd)+', Q'+inttostr(vm)+', '+inttohex(64-imm6,1)
|
|
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
end;
|
|
|
|
procedure ASIMD_3Reg_DL(var LastDisassembleData: TLastDisassembleData);
|
|
var opcode: dword;
|
|
u,b,a: byte;
|
|
d, size, Vn, Vd, op, N,M, Vm: byte;
|
|
_datatype: string;
|
|
begin
|
|
opcode:=pdword(@LastDisassembleData.Bytes[0])^;
|
|
|
|
if LastDisassembleData.Disassembler=dcThumb then
|
|
u:=(opcode shr 28) and 1
|
|
else
|
|
u:=(opcode shr 24) and 1;
|
|
|
|
b:=(opcode shr 20) and 3;
|
|
a:=(opcode shr 8) and $f;
|
|
|
|
D:=(opcode shr 22) and 1;
|
|
size:=(opcode shr 20) and 3;
|
|
Vn:=(opcode shr 16) and $f;
|
|
Vd:=(opcode shr 12) and $f;
|
|
op:=(opcode shr 9) and 1;
|
|
N:=(opcode shr 7) and 1;
|
|
M:=(opcode shr 5) and 1;
|
|
Vm:=opcode and $f;
|
|
|
|
Vn:=(N shl 4) or Vn;
|
|
Vm:=(M shl 4) or Vm;
|
|
Vd:=(D shl 4) or Vd;
|
|
|
|
if u=0 then
|
|
_datatype:='S'
|
|
else
|
|
_datatype:='U';
|
|
|
|
case size of
|
|
0: _datatype:=_datatype+'8';
|
|
1: _datatype:=_datatype+'16';
|
|
2: _datatype:=_datatype+'32';
|
|
3: _datatype:=_datatype+'64'; //?
|
|
end;
|
|
|
|
LastDisassembleData.parameters:='.'+_datatype+' '+'Q'+inttostr(vd)+', D'+inttostr(vn)+', D'+inttostr(Vm);
|
|
|
|
case a of
|
|
0: LastDisassembleData.opcode:='VADDL';
|
|
1:
|
|
begin
|
|
LastDisassembleData.opcode:='VADDW';
|
|
LastDisassembleData.parameters:='.'+_datatype+' '+'Q'+inttostr(vd)+', Q'+inttostr(vn)+', D'+inttostr(Vm);
|
|
end;
|
|
2: LastDisassembleData.opcode:='VSUBL';
|
|
3:
|
|
begin
|
|
LastDisassembleData.opcode:='VSUBL';
|
|
LastDisassembleData.parameters:='.'+_datatype+' '+'Q'+inttostr(vd)+', Q'+inttostr(vn)+', D'+inttostr(Vm);
|
|
end;
|
|
|
|
4: if u=0 then LastDisassembleData.opcode:='VADDHN' else LastDisassembleData.opcode:='VRADDHN';
|
|
5: LastDisassembleData.opcode:='VABAL';
|
|
6: if u=0 then LastDisassembleData.opcode:='VSUBHN' else LastDisassembleData.opcode:='VRSUBHN';
|
|
7: LastDisassembleData.opcode:='VABDL';
|
|
8: LastDisassembleData.opcode:='VMLAL';
|
|
9: if u=0 then LastDisassembleData.opcode:='VQDMLAL';
|
|
10: LastDisassembleData.opcode:='VMLSL';
|
|
11: LastDisassembleData.opcode:='VQDMLSL';
|
|
12,14: LastDisassembleData.opcode:='VMULL';
|
|
13: LastDisassembleData.opcode:='VQDMULL';
|
|
end;
|
|
|
|
end;
|
|
|
|
procedure ASIMD_2Reg_Scalar(var LastDisassembleData: TLastDisassembleData);
|
|
var opcode: dword;
|
|
q,u,b,a: byte;
|
|
d, size, Vn, Vd, op, F,N,M, Vm: byte;
|
|
_datatype: string='';
|
|
begin
|
|
opcode:=pdword(@LastDisassembleData.Bytes[0])^;
|
|
|
|
if LastDisassembleData.Disassembler=dcThumb then
|
|
u:=(opcode shr 28) and 1
|
|
else
|
|
u:=(opcode shr 24) and 1;
|
|
|
|
q:=u;
|
|
b:=(opcode shr 20) and 3;
|
|
a:=(opcode shr 8) and $f;
|
|
|
|
D:=(opcode shr 22) and 1;
|
|
size:=(opcode shr 20) and 3;
|
|
Vn:=(opcode shr 16) and $f;
|
|
Vd:=(opcode shr 12) and $f;
|
|
op:=(opcode shr 9) and 1;
|
|
F:=(opcode shr 8) and 1;
|
|
N:=(opcode shr 7) and 1;
|
|
M:=(opcode shr 5) and 1;
|
|
Vm:=opcode and $f;
|
|
|
|
Vn:=(N shl 4) or Vn;
|
|
Vm:=(M shl 4) or Vm;
|
|
Vd:=(D shl 4) or Vd;
|
|
|
|
|
|
|
|
case size of
|
|
0: _datatype:='8';
|
|
1: _datatype:='16';
|
|
2: _datatype:='32';
|
|
3: _datatype:='64'; //?
|
|
end;
|
|
|
|
case a of
|
|
0,1:
|
|
begin
|
|
if F=0 then _datatype:='I'+_datatype else _datatype:='F'+_datatype;
|
|
|
|
LastDisassembleData.parameters:='.'+_datatype+' '+'Q'+inttostr(vd)+', D'+inttostr(vn)+', D'+inttostr(Vm);
|
|
LastDisassembleData.opcode:='VMLA';
|
|
end;
|
|
|
|
2:
|
|
begin
|
|
if U=0 then _datatype:='S'+_datatype else _datatype:='U'+_datatype;
|
|
|
|
LastDisassembleData.parameters:='.'+_datatype+' '+'Q'+inttostr(vd)+', D'+inttostr(vn)+', D'+inttostr(Vm);
|
|
LastDisassembleData.opcode:='VMLAL';
|
|
end;
|
|
|
|
3:
|
|
begin
|
|
_datatype:='S'+_datatype;
|
|
LastDisassembleData.parameters:='.'+_datatype+' '+'Q'+inttostr(vd)+', D'+inttostr(vn)+', D'+inttostr(Vm);
|
|
LastDisassembleData.opcode:='VQDMLAL';
|
|
end;
|
|
|
|
4,5:
|
|
begin
|
|
if F=0 then _datatype:='I'+_datatype else _datatype:='F'+_datatype;
|
|
|
|
LastDisassembleData.parameters:='.'+_datatype+' '+'Q'+inttostr(vd)+', D'+inttostr(vn)+', D'+inttostr(Vm);
|
|
LastDisassembleData.opcode:='VMLA';
|
|
end;
|
|
|
|
6:
|
|
begin
|
|
if U=0 then _datatype:='S'+_datatype else _datatype:='U'+_datatype;
|
|
|
|
LastDisassembleData.parameters:='.'+_datatype+' '+'Q'+inttostr(vd)+', D'+inttostr(vn)+', D'+inttostr(Vm);
|
|
LastDisassembleData.opcode:='VMLSL';
|
|
end;
|
|
|
|
7:
|
|
begin
|
|
_datatype:='S'+_datatype;
|
|
LastDisassembleData.parameters:='.'+_datatype+' '+'Q'+inttostr(vd)+', D'+inttostr(vn)+', D'+inttostr(Vm);
|
|
LastDisassembleData.opcode:='VQDMLSL';
|
|
end;
|
|
|
|
8,9:
|
|
begin
|
|
if F=0 then _datatype:='I'+_datatype else _datatype:='F'+_datatype;
|
|
|
|
if q=1 then
|
|
LastDisassembleData.parameters:='.'+_datatype+' '+'D'+inttostr(vd)+', D'+inttostr(vn)+', D'+inttostr(Vm)
|
|
else
|
|
LastDisassembleData.parameters:='.'+_datatype+' '+'Q'+inttostr(vd)+', Q'+inttostr(vn)+', D'+inttostr(Vm);
|
|
|
|
LastDisassembleData.opcode:='VMUL';
|
|
end;
|
|
|
|
10:
|
|
begin
|
|
if U=0 then _datatype:='S'+_datatype else _datatype:='U'+_datatype;
|
|
LastDisassembleData.parameters:='.'+_datatype+' '+'Q'+inttostr(vd)+', D'+inttostr(vn)+', D'+inttostr(Vm);
|
|
|
|
LastDisassembleData.opcode:='VMULL';
|
|
end;
|
|
|
|
11:
|
|
begin
|
|
_datatype:='S'+_datatype;
|
|
LastDisassembleData.parameters:='.'+_datatype+' '+'Q'+inttostr(vd)+', D'+inttostr(vn)+', D'+inttostr(Vm);
|
|
LastDisassembleData.opcode:='VQDMULL';
|
|
end;
|
|
|
|
12:
|
|
begin
|
|
_datatype:='S'+_datatype;
|
|
if q=1 then
|
|
LastDisassembleData.parameters:='.'+_datatype+' '+'D'+inttostr(vd)+', D'+inttostr(vn)+', D'+inttostr(Vm)
|
|
else
|
|
LastDisassembleData.parameters:='.'+_datatype+' '+'Q'+inttostr(vd)+', Q'+inttostr(vn)+', D'+inttostr(Vm);
|
|
LastDisassembleData.opcode:='VQDMULH';
|
|
end;
|
|
|
|
13:
|
|
begin
|
|
_datatype:='S'+_datatype;
|
|
if q=1 then
|
|
LastDisassembleData.parameters:='.'+_datatype+' '+'D'+inttostr(vd)+', D'+inttostr(vn)+', D'+inttostr(Vm)
|
|
else
|
|
LastDisassembleData.parameters:='.'+_datatype+' '+'Q'+inttostr(vd)+', Q'+inttostr(vn)+', D'+inttostr(Vm);
|
|
LastDisassembleData.opcode:='VQRDMULH';
|
|
end;
|
|
end;
|
|
|
|
|
|
end;
|
|
|
|
procedure VEXT(var LastDisassembleData: TLastDisassembleData);
|
|
var
|
|
opcode: dword;
|
|
D, Vn, Vd, imm4, N, Q, M, Vm: byte;
|
|
begin
|
|
opcode:=pdword(@LastDisassembleData.Bytes[0])^;
|
|
D:=(opcode shr 22) and 1;
|
|
Vn:=(opcode shr 16) and $f;
|
|
Vd:=(opcode shr 12) and $f;
|
|
|
|
imm4:=(opcode shr 8) and $f;
|
|
|
|
N:=(opcode shr 7) and 1;
|
|
Q:=(opcode shr 6) and 1;
|
|
M:=(opcode shr 5) and 1;
|
|
Vm:=opcode and $f;
|
|
|
|
Vn:=(N shl 4) or Vn;
|
|
Vm:=(M shl 4) or Vm;
|
|
Vd:=(D shl 4) or Vd;
|
|
|
|
if q=0 then
|
|
LastDisassembleData.parameters:='D'+inttostr(vd)+', D'+inttostr(Vn)+', D'+inttostr(Vm)+' '+inttohex(imm4,1)
|
|
else
|
|
LastDisassembleData.parameters:='D'+inttostr(vd)+', D'+inttostr(Vn)+', D'+inttostr(Vm)+' '+inttohex(imm4,1);
|
|
|
|
LastDisassembleData.opcode:='VEXT.8';
|
|
end;
|
|
|
|
procedure ASIMD_2Reg_Misc(var LastDisassembleData: TLastDisassembleData);
|
|
var opcode: dword;
|
|
u,b,a: byte;
|
|
q,d, size, Vn, Vd, op, F,N,M, Vm: byte;
|
|
sz: byte;
|
|
_dt: string='';
|
|
_dt2: string='';
|
|
|
|
_sU, _sf: string;
|
|
begin
|
|
opcode:=pdword(@LastDisassembleData.Bytes[0])^;
|
|
|
|
if LastDisassembleData.Disassembler=dcThumb then
|
|
u:=(opcode shr 28) and 1
|
|
else
|
|
u:=(opcode shr 24) and 1;
|
|
|
|
|
|
b:=(opcode shr 6) and $1f;
|
|
a:=(opcode shr 16) and $3;
|
|
|
|
D:=(opcode shr 22) and 1;
|
|
size:=(opcode shr 18) and 3;
|
|
Vn:=(opcode shr 16) and $f;
|
|
Vd:=(opcode shr 12) and $f;
|
|
op:=(opcode shr 7) and 1;
|
|
F:=(opcode shr 10) and 1;
|
|
N:=(opcode shr 7) and 1;
|
|
Q:=(opcode shr 6) and 1;
|
|
M:=(opcode shr 5) and 1;
|
|
Vm:=opcode and $f;
|
|
|
|
Vn:=(N shl 4) or Vn;
|
|
Vm:=(M shl 4) or Vm;
|
|
Vd:=(D shl 4) or Vd;
|
|
|
|
case size of
|
|
0: _dt:='8';
|
|
1: _dt:='16';
|
|
2: _dt:='32';
|
|
3: _dt:='64';
|
|
end;
|
|
|
|
case size of
|
|
0: _dt2:='16';
|
|
1: _dt2:='32';
|
|
2: _dt2:='64';
|
|
end;
|
|
|
|
|
|
|
|
if op=0 then _SU:='S' else _sU:='U';
|
|
if f=0 then _sf:='S' else _sf:='F';
|
|
|
|
|
|
case a of
|
|
0:
|
|
begin
|
|
case b of
|
|
0..11,
|
|
16..31: if q=0 then
|
|
LastDisassembleData.parameters:='D'+inttostr(vd)+', D'+inttostr(Vm)
|
|
else
|
|
LastDisassembleData.parameters:='Q'+inttostr(vd)+', Q'+inttostr(Vm);
|
|
|
|
12..15: LastDisassembleData.parameters:='Q'+inttostr(vd)+', Q'+inttostr(Vm);
|
|
end;
|
|
|
|
case b of
|
|
0,1: LastDisassembleData.opcode:='VREV64.'+_dt;
|
|
2,3: LastDisassembleData.opcode:='VREV32.'+_dt;
|
|
4,5: LastDisassembleData.opcode:='VREV16.'+_dt;
|
|
8..11: LastDisassembleData.opcode:='VPADDL.'+_sU+_dt;
|
|
12: LastDisassembleData.opcode:='AESE.'+_dt;
|
|
13: LastDisassembleData.opcode:='AESD.'+_dt;
|
|
14: LastDisassembleData.opcode:='AESMC.'+_dt;
|
|
15: LastDisassembleData.opcode:='AESIMC.'+_dt;
|
|
|
|
16,17: LastDisassembleData.opcode:='VCLS.S'+_dt;
|
|
18,19: LastDisassembleData.opcode:='VCLZ.I'+_dt;
|
|
20,21: LastDisassembleData.opcode:='VCNT.8';
|
|
22,23: LastDisassembleData.opcode:='VMVN';
|
|
24,27: LastDisassembleData.opcode:='VPADAL.'+_sU+_dt;
|
|
28,29: LastDisassembleData.opcode:='VQABS.S'+_dt;
|
|
30,31: LastDisassembleData.opcode:='VQNEG.S'+_dt;
|
|
end;
|
|
|
|
end;
|
|
|
|
1:
|
|
begin
|
|
case (b shr 1) and 7 of
|
|
0..4: if q=0 then
|
|
LastDisassembleData.parameters:='D'+inttostr(vd)+', D'+inttostr(Vm)+', 0'
|
|
else
|
|
LastDisassembleData.parameters:='Q'+inttostr(vd)+', Q'+inttostr(Vm)+', 0';
|
|
|
|
5: LastDisassembleData.parameters:='Q'+inttostr(vd)+', Q'+inttostr(Vm);
|
|
|
|
6,7: if q=0 then
|
|
LastDisassembleData.parameters:='D'+inttostr(vd)+', D'+inttostr(Vm)
|
|
else
|
|
LastDisassembleData.parameters:='Q'+inttostr(vd)+', Q'+inttostr(Vm);
|
|
|
|
|
|
|
|
end;
|
|
|
|
case (b shr 1) and 7 of
|
|
0: LastDisassembleData.opcode:='VCGT.'+_sf+_dt;
|
|
1: LastDisassembleData.opcode:='VCGE.'+_sf+_dt;
|
|
2: LastDisassembleData.opcode:='VCEQ.'+_sf+_dt;
|
|
3: LastDisassembleData.opcode:='VCLE.'+_sf+_dt;
|
|
4: LastDisassembleData.opcode:='VCLT.'+_sf+_dt;
|
|
5: LastDisassembleData.opcode:='SHA1H.32';
|
|
6: LastDisassembleData.opcode:='VABS'+_sf+_dt;
|
|
7: LastDisassembleData.opcode:='VNEG.'+_sf+_dt;
|
|
end;
|
|
|
|
end;
|
|
|
|
2:
|
|
begin
|
|
if q=0 then
|
|
LastDisassembleData.parameters:='D'+inttostr(vd)+', D'+inttostr(Vm)
|
|
else
|
|
LastDisassembleData.parameters:='Q'+inttostr(vd)+', Q'+inttostr(Vm);
|
|
|
|
case b of
|
|
0,1: LastDisassembleData.opcode:='VSWP';
|
|
2,3: LastDisassembleData.opcode:='VTRN.'+_dt;
|
|
4,5: LastDisassembleData.opcode:='VUZP.'+_dt;
|
|
6,7: LastDisassembleData.opcode:='VZIP.'+_dt;
|
|
8:
|
|
begin
|
|
LastDisassembleData.opcode:='VMOVN.I'+_dt;
|
|
LastDisassembleData.parameters:='D'+inttostr(vd)+', Q'+inttostr(Vm);
|
|
end;
|
|
|
|
9:
|
|
begin
|
|
LastDisassembleData.opcode:='VQMOVUN.S'+_dt2;
|
|
LastDisassembleData.parameters:='D'+inttostr(vd)+', Q'+inttostr(Vm);
|
|
end;
|
|
|
|
10:
|
|
begin
|
|
LastDisassembleData.opcode:='VQMOVN.S'+_dt2;
|
|
LastDisassembleData.parameters:='D'+inttostr(vd)+', Q'+inttostr(Vm);
|
|
end;
|
|
|
|
11:
|
|
begin
|
|
if size=0 then _dt:='8';
|
|
LastDisassembleData.opcode:='VQMOVN.U'+_dt2;
|
|
LastDisassembleData.parameters:='D'+inttostr(vd)+', Q'+inttostr(Vm);
|
|
end;
|
|
|
|
12:
|
|
begin
|
|
LastDisassembleData.opcode:='VSHLL';
|
|
LastDisassembleData.parameters:='Q'+inttostr(vd)+', D'+inttostr(Vm)+', '+inttohex(size,1);
|
|
end;
|
|
|
|
14:
|
|
begin
|
|
LastDisassembleData.opcode:='SHA1SU1.32';
|
|
LastDisassembleData.parameters:='Q'+inttostr(vd)+', Q'+inttostr(Vm);
|
|
end;
|
|
|
|
15:
|
|
begin
|
|
LastDisassembleData.opcode:='SHA1SU0.32';
|
|
LastDisassembleData.parameters:='Q'+inttostr(vd)+', Q'+inttostr(vn)+', Q'+inttostr(Vm);
|
|
end;
|
|
|
|
16, 17: LastDisassembleData.opcode:='VRINTN.F32.F32';
|
|
18, 19: LastDisassembleData.opcode:='VRINTX.F32.F32';
|
|
20, 21: LastDisassembleData.opcode:='VRINTA.F32.F32';
|
|
22, 23: LastDisassembleData.opcode:='VRINTZ.F32.F32';
|
|
26, 27: LastDisassembleData.opcode:='VRINTM.F32.F32';
|
|
30, 31: LastDisassembleData.opcode:='VRINTP.F32.F32';
|
|
24:
|
|
begin
|
|
LastDisassembleData.opcode:='VCVT.F16.F32';
|
|
LastDisassembleData.parameters:='D'+inttostr(vd)+', Q'+inttostr(Vm);
|
|
end;
|
|
|
|
28:
|
|
begin
|
|
LastDisassembleData.opcode:='VCVT.F32.F16';
|
|
LastDisassembleData.parameters:='Q'+inttostr(vd)+', D'+inttostr(Vm);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
3:
|
|
begin
|
|
if q=0 then
|
|
LastDisassembleData.parameters:='D'+inttostr(vd)+', D'+inttostr(Vm)
|
|
else
|
|
LastDisassembleData.parameters:='Q'+inttostr(vd)+', Q'+inttostr(Vm);
|
|
|
|
case b shr 2 of
|
|
0: LastDisassembleData.opcode:='VCVTA.F32';
|
|
1: LastDisassembleData.opcode:='VCVTN.F32';
|
|
2: LastDisassembleData.opcode:='VCVTP.F32';
|
|
3: LastDisassembleData.opcode:='VCVTN.F32';
|
|
4,5:
|
|
begin
|
|
if ((b shr 1) and 1)=0 then LastDisassembleData.opcode:='VRECPE' else LastDisassembleData.opcode:='VRSQRTE';
|
|
|
|
if f=1 then
|
|
LastDisassembleData.opcode:=LastDisassembleData.opcode+'F32'
|
|
else
|
|
LastDisassembleData.opcode:=LastDisassembleData.opcode+'S32';
|
|
end;
|
|
|
|
6,7:
|
|
begin
|
|
case (opcode shr 7) and 3 of
|
|
0: LastDisassembleData.opcode:='VCVT.F32.S32';
|
|
1: LastDisassembleData.opcode:='VCVT.F32.U32';
|
|
2: LastDisassembleData.opcode:='VCVT.S32.F32';
|
|
3: LastDisassembleData.opcode:='VCVT.U32.F32';
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
|
|
end;
|
|
|
|
procedure VTBL_VTBLX(var LastDisassembleData: TLastDisassembleData);
|
|
var
|
|
opcode: dword;
|
|
D, Vn, Vd, len, N, op, M, Vm: byte;
|
|
_list: string;
|
|
i: integer;
|
|
begin
|
|
_list:='';
|
|
opcode:=pdword(@LastDisassembleData.Bytes[0])^;
|
|
|
|
D:=(opcode shr 22) and 1;
|
|
Vn:=(opcode shr 16) and $f;
|
|
Vd:=(opcode shr 12) and $f;
|
|
len:=(opcode shr 8) and 3;
|
|
|
|
N:=(opcode shr 7) and 1;
|
|
op:=(opcode shr 6) and 1;
|
|
M:=(opcode shr 5) and 1;
|
|
Vm:=opcode and $f;
|
|
|
|
Vn:=(N shl 4) or Vn;
|
|
Vm:=(M shl 4) or Vm;
|
|
Vd:=(D shl 4) or Vd;
|
|
|
|
for i:=0 to len-1 do
|
|
begin
|
|
_list:=_list+'D'+inttostr(Vn+i);
|
|
if i<len-1 then
|
|
_list:=_list+', ';
|
|
end;
|
|
|
|
LastDisassembleData.parameters:='D'+inttostr(vd)+', {'+_list+'} D'+inttostr(Vm);
|
|
|
|
|
|
if op=0 then
|
|
LastDisassembleData.opcode:='VTBL.8'
|
|
else
|
|
LastDisassembleData.opcode:='VTBX.8';
|
|
end;
|
|
|
|
|
|
procedure VDUP_Scalar(var LastDisassembleData: TLastDisassembleData);
|
|
var
|
|
opcode: dword;
|
|
D, imm4, Vd, Q, M, Vm: byte;
|
|
|
|
size, index: byte;
|
|
begin
|
|
opcode:=pdword(@LastDisassembleData.Bytes[0])^;
|
|
|
|
D:=(opcode shr 22) and 1;
|
|
imm4:=(opcode shr 16) and $f;
|
|
Vd:=(opcode shr 12) and $f;
|
|
|
|
Q:=(opcode shr 6) and 1;
|
|
M:=(opcode shr 5) and 1;
|
|
Vm:=opcode and $f;
|
|
|
|
Vm:=(M shl 4) or Vm;
|
|
Vd:=(D shl 4) or Vd;
|
|
|
|
size:=0;
|
|
index:=0;
|
|
if (imm4 and 1)=1 then
|
|
begin
|
|
size:=8;
|
|
index:=imm4 shr 1;
|
|
end
|
|
else
|
|
if (imm4 and 2)=2 then
|
|
begin
|
|
size:=16;
|
|
index:=imm4 shr 2;
|
|
end
|
|
else
|
|
if (imm4 and 4)=4 then
|
|
begin
|
|
size:=32;
|
|
index:=imm4 shr 3;
|
|
end;
|
|
|
|
if q=0 then
|
|
LastDisassembleData.parameters:='D'+inttostr(vd)+', D'+inttostr(Vm)+'['+inttostr(index)+']'
|
|
else
|
|
LastDisassembleData.parameters:='Q'+inttostr(vd)+', Q'+inttostr(Vm)+'['+inttostr(index)+']';
|
|
|
|
LastDisassembleData.opcode:='VDUP.'+inttostr(size);
|
|
end;
|
|
|
|
procedure Advanced_SIMD_Data_Processing(var LastDisassembleData: TLastDisassembleData);
|
|
//called by both the arm and thumb disassembler
|
|
var opcode: dword; u,a,b,c:byte;
|
|
begin
|
|
opcode:=pdword(LastDisassembleData.Bytes)^;
|
|
|
|
if lastdisassembledata.Disassembler=dcArm then
|
|
u:=(opcode shr 24) and 1
|
|
else
|
|
begin
|
|
opcode:=(opcode shr 16) or (opcode shl 16);
|
|
u:=(opcode shr 28) and 1;
|
|
|
|
pdword(@lastdisassembledata.Bytes[0])^:=opcode;
|
|
end;
|
|
|
|
a:=(opcode shr 19) and $1f;
|
|
b:=(opcode shr 8) and $f;
|
|
c:=opcode and $f;
|
|
|
|
if (a shr 4) and 1=0 then ASIMD_3RegSL(LastDisassembleData)
|
|
else
|
|
begin
|
|
if ((c and 9)=1) then
|
|
begin
|
|
case a and $7 of
|
|
0: ASIMD_1Reg_ModImm(LastDisassembleData);
|
|
1..7: ASIMD_2Reg_Shift(LastDisassembleData);
|
|
end;
|
|
end
|
|
else
|
|
if ((c and 9)=9) then
|
|
begin
|
|
ASIMD_2Reg_Shift(LastDisassembleData);
|
|
end
|
|
else
|
|
if ((a and $7) in [0..5]) and ((c and 5)=0) then
|
|
begin
|
|
ASIMD_3Reg_DL(LastDisassembleData);
|
|
end
|
|
else
|
|
if ((a and $7) in [0..5]) and ((c and 5)=4) then
|
|
begin
|
|
ASIMD_2Reg_Scalar(LastDisassembleData);
|
|
end
|
|
else
|
|
if (u=0) and ((a and $16)=$16) and ((c and 1)=0) then
|
|
begin
|
|
VEXT(LastDisassembleData);
|
|
end
|
|
else
|
|
if (u=1) and ((a and $16)=$16) and ((c and 1)=0) then
|
|
begin
|
|
if (b and 8)=0 then
|
|
begin
|
|
ASIMD_2Reg_Misc(LastDisassembleData);
|
|
end
|
|
else
|
|
if (b and $c)=8 then
|
|
begin
|
|
VTBL_VTBLX(LastDisassembleData);
|
|
end
|
|
else
|
|
if (b=$c) then
|
|
begin
|
|
VDUP_Scalar(LastDisassembleData);
|
|
end;
|
|
|
|
end;
|
|
end;
|
|
|
|
|
|
if lastdisassembledata.Disassembler=dcThumb then //undo the wordswap
|
|
begin
|
|
opcode:=(opcode shr 16) or (opcode shl 16);
|
|
pdword(@lastdisassembledata.Bytes[0])^:=opcode;
|
|
end;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TArmDisassembler.Disassemble(var address: ptrUint): string;
|
|
var
|
|
i: integer;
|
|
x: ptruint;
|
|
thumbdisassembler: TThumbDisassembler;
|
|
arm32disassembler: TArm32Instructionset;
|
|
arm64disassembler: TArm64Instructionset;
|
|
begin
|
|
result:='';
|
|
|
|
if processhandler.is64Bit then
|
|
begin
|
|
result:=arm64disassembler.Disassemble(address);
|
|
lastdisassembledata:=arm64disassembler.LastDisassembleData;
|
|
exit;
|
|
end
|
|
else
|
|
begin
|
|
if (address and 1) = 1 then //thumb
|
|
begin
|
|
result:=thumbdisassembler.Disassemble(address);
|
|
LastDisassembleData:=thumbdisassembler.LastDisassembleData;
|
|
exit;
|
|
end;
|
|
|
|
if (address and 3) = 0 then
|
|
begin
|
|
result:=arm32disassembler.Disassemble(address);
|
|
lastdisassembledata:=arm32disassembler.LastDisassembleData;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
//obsolete:
|
|
{$ifdef ARMDEV}
|
|
opcode:=pdword(address)^;
|
|
setlength(LastDisassembleData.Bytes,4);
|
|
pdword(@LastDisassembleData.Bytes[0])^:=opcode;
|
|
|
|
address:=0;
|
|
x:=sizeof(opcode);
|
|
{$else}
|
|
x:=0;
|
|
setlength(LastDisassembleData.Bytes,4);
|
|
|
|
readprocessmemory(processhandle, pointer(address), @LastDisassembleData.Bytes[0], 4, x);
|
|
opcode:=pdword(@LastDisassembleData.Bytes[0])^;
|
|
{$endif}
|
|
|
|
LastDisassembleData.address:=address;
|
|
LastDisassembleData.SeperatorCount:=0;
|
|
LastDisassembleData.prefix:='';
|
|
LastDisassembleData.PrefixSize:=0;
|
|
LastDisassembleData.opcode:='';
|
|
LastDisassembleData.parameters:='';
|
|
lastdisassembledata.isjump:=false;
|
|
lastdisassembledata.iscall:=false;
|
|
lastdisassembledata.isret:=false;
|
|
lastdisassembledata.isconditionaljump:=false;
|
|
lastdisassembledata.modrmValueType:=dvtNone;
|
|
lastdisassembledata.parameterValueType:=dvtNone;
|
|
lastdisassembledata.Disassembler:=dcArm;
|
|
|
|
|
|
|
|
if (x=sizeof(opcode)) then
|
|
begin
|
|
|
|
|
|
if ((opcode shr 4) and $ffffff)=$12FFF1 then
|
|
BX
|
|
else
|
|
if (((opcode shr 2) and $3f)=0) and (((opcode shr 4) and $f)=$9) then
|
|
MUL
|
|
else
|
|
if (((opcode shr 23) and $1f)=2) and (((opcode shr 16) and $3f)=$f) and ((opcode and $FFF)=0) then
|
|
MRS
|
|
else
|
|
if (((opcode shr 23) and $1f)=2) and (((opcode shr 4) and $3FFFF)=$29F00) then
|
|
MSR
|
|
else
|
|
if (((opcode shr 23) and $3)=2) and (((opcode shr 26) and $3)=2) and (((opcode shr 12) and $3ff)=$28f) then
|
|
MSR_flg
|
|
else
|
|
if ((opcode shr 25) and %110)=%101 then
|
|
Branch
|
|
else
|
|
if (((opcode shr 26) and 3)=0) and (((opcode shr 4) and $f)<>9) then
|
|
DataProcessing
|
|
else
|
|
if (opcode shr 26) and 3=1 then
|
|
SingleDataTransfer
|
|
else
|
|
if (opcode shr 25) and %110=%100 then
|
|
LDM_STM
|
|
else
|
|
if ((opcode shr 23) and $1f=2) and ((opcode shr 20) and $3=0) and ((opcode shr 4) and $ff=9) then
|
|
SWP
|
|
else
|
|
if (opcode shr 24) and $F=$F then
|
|
SWI
|
|
else
|
|
if (((opcode shr 24) and $F)=$E) and (((opcode shr 4) and 1)=1) then
|
|
CDP
|
|
else
|
|
if (opcode shr 25) and $7f=$79 then
|
|
Advanced_SIMD_Data_Processing(LastDisassembleData);
|
|
|
|
//if (((opcode shr 24) and $f)=$E) and ((opcode shr 9) and $3=5) and ((opcode shr 4) and 1) = 1
|
|
|
|
end
|
|
else
|
|
LastDisassembleData.opcode:='??';
|
|
|
|
|
|
result:=inttohex(LastDisassembleData.address,8);
|
|
result:=result+' - ';
|
|
if x>0 then
|
|
begin
|
|
for i:=0 to length(LastDisassembleData.bytes)-1 do
|
|
result:=result+inttohex(LastDisassembleData.Bytes[i],2)+' ';
|
|
end
|
|
else
|
|
begin
|
|
for i:=0 to length(LastDisassembleData.bytes)-1 do
|
|
result:=result+'?? ';
|
|
end;
|
|
|
|
result:=result+' - ';
|
|
result:=result+LastDisassembleData.opcode;
|
|
result:=result+' ';
|
|
result:=result+LastDisassembleData.parameters;
|
|
|
|
inc(address,4);
|
|
|
|
end;
|
|
|
|
initialization
|
|
outputdebugstring('arm disassembler'); //runs
|
|
|
|
end.
|
|
|