mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-11-14 05:39:37 +01:00
* changed most of the variables in the assembler readers used to store constants from aint to tcgint as aint has only the size of the accumular while some CPUs (AVR) allow larger constants in instructions + allow access to absolute symbols with address type in inline assembler * allow absolute addresses in avr inline assembler + tests git-svn-id: trunk@37411 -
1529 lines
50 KiB
ObjectPascal
1529 lines
50 KiB
ObjectPascal
{
|
|
Copyright (c) 1998-2002 by Carl Eric Codere and Peter Vreman
|
|
|
|
Does the parsing for the ARM GNU AS styled inline assembler.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
****************************************************************************
|
|
}
|
|
Unit raarmgas;
|
|
|
|
{$i fpcdefs.inc}
|
|
|
|
Interface
|
|
|
|
uses
|
|
raatt,raarm,
|
|
cpubase;
|
|
|
|
type
|
|
|
|
tarmattreader = class(tattreader)
|
|
actoppostfix : TOpPostfix;
|
|
actwideformat : boolean;
|
|
function is_asmopcode(const s: string):boolean;override;
|
|
function is_register(const s:string):boolean;override;
|
|
function is_targetdirective(const s: string): boolean; override;
|
|
procedure handleopcode;override;
|
|
procedure BuildReference(oper : tarmoperand);
|
|
procedure BuildOperand(oper : tarmoperand);
|
|
procedure BuildSpecialreg(oper : tarmoperand);
|
|
function TryBuildShifterOp(oper : tarmoperand) : boolean;
|
|
procedure BuildOpCode(instr : tarminstruction);
|
|
procedure ReadSym(oper : tarmoperand);
|
|
procedure ConvertCalljmp(instr : tarminstruction);
|
|
procedure HandleTargetDirective; override;
|
|
protected
|
|
function is_unified: boolean; virtual;
|
|
end;
|
|
|
|
tarmunifiedattreader = class(tarmattreader)
|
|
protected
|
|
function is_unified: boolean; override;
|
|
end;
|
|
|
|
|
|
Implementation
|
|
|
|
uses
|
|
{ helpers }
|
|
cutils,
|
|
{ global }
|
|
globtype,globals,verbose,
|
|
systems,aasmbase,aasmtai,aasmdata,aasmcpu,
|
|
{ symtable }
|
|
symconst,symsym,symdef,
|
|
procinfo,
|
|
rabase,rautils,
|
|
cgbase,cgutils,paramgr;
|
|
|
|
|
|
function tarmunifiedattreader.is_unified: boolean;
|
|
begin
|
|
result:=true;
|
|
end;
|
|
|
|
|
|
function tarmattreader.is_register(const s:string):boolean;
|
|
type
|
|
treg2str = record
|
|
name : string[3];
|
|
reg : tregister;
|
|
end;
|
|
|
|
const
|
|
extraregs : array[0..19+16] of treg2str = (
|
|
(name: 'A1'; reg : NR_R0),
|
|
(name: 'A2'; reg : NR_R1),
|
|
(name: 'A3'; reg : NR_R2),
|
|
(name: 'A4'; reg : NR_R3),
|
|
(name: 'V1'; reg : NR_R4),
|
|
(name: 'V2'; reg : NR_R5),
|
|
(name: 'V3'; reg : NR_R6),
|
|
(name: 'V4'; reg : NR_R7),
|
|
(name: 'V5'; reg : NR_R8),
|
|
(name: 'V6'; reg : NR_R9),
|
|
(name: 'V7'; reg : NR_R10),
|
|
(name: 'V8'; reg : NR_R11),
|
|
(name: 'WR'; reg : NR_R7),
|
|
(name: 'SB'; reg : NR_R9),
|
|
(name: 'SL'; reg : NR_R10),
|
|
(name: 'FP'; reg : NR_R11),
|
|
(name: 'IP'; reg : NR_R12),
|
|
(name: 'SP'; reg : NR_R13),
|
|
(name: 'LR'; reg : NR_R14),
|
|
(name: 'PC'; reg : NR_R15),
|
|
|
|
(name: 'C0'; reg : NR_CR0),
|
|
(name: 'C1'; reg : NR_CR1),
|
|
(name: 'C2'; reg : NR_CR2),
|
|
(name: 'C3'; reg : NR_CR3),
|
|
(name: 'C4'; reg : NR_CR4),
|
|
(name: 'C5'; reg : NR_CR5),
|
|
(name: 'C6'; reg : NR_CR6),
|
|
(name: 'C7'; reg : NR_CR7),
|
|
(name: 'C8'; reg : NR_CR8),
|
|
(name: 'C9'; reg : NR_CR9),
|
|
(name: 'C10'; reg : NR_CR10),
|
|
(name: 'C11'; reg : NR_CR11),
|
|
(name: 'C12'; reg : NR_CR12),
|
|
(name: 'C13'; reg : NR_CR13),
|
|
(name: 'C14'; reg : NR_CR14),
|
|
(name: 'C15'; reg : NR_CR15)
|
|
);
|
|
|
|
var
|
|
i : longint;
|
|
|
|
begin
|
|
result:=inherited is_register(s);
|
|
{ reg found?
|
|
possible aliases are always 2 char
|
|
}
|
|
if result or (not (length(s) in [2,3])) then
|
|
exit;
|
|
for i:=low(extraregs) to high(extraregs) do
|
|
begin
|
|
if s=extraregs[i].name then
|
|
begin
|
|
actasmregister:=extraregs[i].reg;
|
|
result:=true;
|
|
actasmtoken:=AS_REGISTER;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function tarmattreader.is_targetdirective(const s: string): boolean;
|
|
begin
|
|
case s of
|
|
'.thumb_func',
|
|
'.code',
|
|
'.thumb_set':
|
|
result:=true
|
|
else
|
|
Result:=inherited is_targetdirective(s);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure tarmattreader.ReadSym(oper : tarmoperand);
|
|
var
|
|
tempstr, mangledname : string;
|
|
typesize,l,k : tcgint;
|
|
begin
|
|
tempstr:=actasmpattern;
|
|
Consume(AS_ID);
|
|
{ typecasting? }
|
|
if (actasmtoken=AS_LPAREN) and
|
|
SearchType(tempstr,typesize) then
|
|
begin
|
|
oper.hastype:=true;
|
|
Consume(AS_LPAREN);
|
|
BuildOperand(oper);
|
|
Consume(AS_RPAREN);
|
|
if oper.opr.typ in [OPR_REFERENCE,OPR_LOCAL] then
|
|
oper.SetSize(typesize,true);
|
|
end
|
|
else
|
|
if not oper.SetupVar(tempstr,false) then
|
|
Message1(sym_e_unknown_id,tempstr);
|
|
{ record.field ? }
|
|
if actasmtoken=AS_DOT then
|
|
begin
|
|
BuildRecordOffsetSize(tempstr,l,k,mangledname,false);
|
|
if (mangledname<>'') then
|
|
Message(asmr_e_invalid_reference_syntax);
|
|
inc(oper.opr.ref.offset,l);
|
|
end;
|
|
end;
|
|
|
|
|
|
Procedure tarmattreader.BuildReference(oper : tarmoperand);
|
|
|
|
procedure do_error;
|
|
begin
|
|
Message(asmr_e_invalid_reference_syntax);
|
|
RecoverConsume(false);
|
|
end;
|
|
|
|
|
|
procedure test_end(require_rbracket : boolean);
|
|
begin
|
|
if require_rbracket then begin
|
|
if not(actasmtoken=AS_RBRACKET) then
|
|
begin
|
|
do_error;
|
|
exit;
|
|
end
|
|
else
|
|
Consume(AS_RBRACKET);
|
|
if (actasmtoken=AS_NOT) then
|
|
begin
|
|
oper.opr.ref.addressmode:=AM_PREINDEXED;
|
|
Consume(AS_NOT);
|
|
end;
|
|
end;
|
|
if not(actasmtoken in [AS_SEPARATOR,AS_end]) then
|
|
do_error
|
|
else
|
|
begin
|
|
{$IFDEF debugasmreader}
|
|
writeln('TEST_end_FINAL_OK. Created the following ref:');
|
|
writeln('oper.opr.ref.shiftimm=',oper.opr.ref.shiftimm);
|
|
writeln('oper.opr.ref.shiftmode=',ord(oper.opr.ref.shiftmode));
|
|
writeln('oper.opr.ref.index=',ord(oper.opr.ref.index));
|
|
writeln('oper.opr.ref.base=',ord(oper.opr.ref.base));
|
|
writeln('oper.opr.ref.signindex=',ord(oper.opr.ref.signindex));
|
|
writeln('oper.opr.ref.addressmode=',ord(oper.opr.ref.addressmode));
|
|
writeln;
|
|
{$endIF debugasmreader}
|
|
end;
|
|
end;
|
|
|
|
|
|
function is_shifter_ref_operation(var a : tshiftmode) : boolean;
|
|
begin
|
|
a := SM_NONE;
|
|
if (actasmpattern='LSL') then
|
|
a := SM_LSL
|
|
else if (actasmpattern='LSR') then
|
|
a := SM_LSR
|
|
else if (actasmpattern='ASR') then
|
|
a := SM_ASR
|
|
else if (actasmpattern='ROR') then
|
|
a := SM_ROR
|
|
else if (actasmpattern='RRX') then
|
|
a := SM_RRX;
|
|
is_shifter_ref_operation := not(a=SM_NONE);
|
|
end;
|
|
|
|
|
|
procedure read_index_shift(require_rbracket : boolean);
|
|
var
|
|
shift : aint;
|
|
begin
|
|
case actasmtoken of
|
|
AS_COMMA :
|
|
begin
|
|
Consume(AS_COMMA);
|
|
if not(actasmtoken=AS_ID) then
|
|
do_error;
|
|
if is_shifter_ref_operation(oper.opr.ref.shiftmode) then
|
|
begin
|
|
Consume(AS_ID);
|
|
if not(oper.opr.ref.shiftmode=SM_RRX) then
|
|
begin
|
|
if not(actasmtoken=AS_HASH) then
|
|
do_error;
|
|
Consume(AS_HASH);
|
|
shift := BuildConstExpression(false,true);
|
|
if (shift<0) or (shift>32) then
|
|
do_error;
|
|
oper.opr.ref.shiftimm := shift;
|
|
test_end(require_rbracket);
|
|
end
|
|
else
|
|
test_end(require_rbracket);
|
|
end
|
|
else
|
|
begin
|
|
do_error;
|
|
exit;
|
|
end;
|
|
end;
|
|
AS_RBRACKET :
|
|
if require_rbracket then
|
|
test_end(require_rbracket)
|
|
else
|
|
begin
|
|
do_error;
|
|
exit;
|
|
end;
|
|
AS_SEPARATOR,AS_END :
|
|
if not require_rbracket then
|
|
test_end(false)
|
|
else
|
|
do_error;
|
|
else
|
|
begin
|
|
do_error;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure read_index(require_rbracket : boolean);
|
|
var
|
|
recname : string;
|
|
o_int,s_int : tcgint;
|
|
begin
|
|
case actasmtoken of
|
|
AS_REGISTER :
|
|
begin
|
|
oper.opr.ref.index:=actasmregister;
|
|
Consume(AS_REGISTER);
|
|
read_index_shift(require_rbracket);
|
|
exit;
|
|
end;
|
|
AS_PLUS,AS_MINUS :
|
|
begin
|
|
if actasmtoken=AS_PLUS then
|
|
begin
|
|
Consume(AS_PLUS);
|
|
end
|
|
else
|
|
begin
|
|
oper.opr.ref.signindex := -1;
|
|
Consume(AS_MINUS);
|
|
end;
|
|
if actasmtoken=AS_REGISTER then
|
|
begin
|
|
oper.opr.ref.index:=actasmregister;
|
|
Consume(AS_REGISTER);
|
|
read_index_shift(require_rbracket);
|
|
exit;
|
|
end
|
|
else
|
|
begin
|
|
do_error;
|
|
exit;
|
|
end;
|
|
test_end(require_rbracket);
|
|
exit;
|
|
end;
|
|
AS_HASH : // constant
|
|
begin
|
|
Consume(AS_HASH);
|
|
o_int := BuildConstExpression(false,true);
|
|
if (o_int>4095) or (o_int<-4095) then
|
|
begin
|
|
Message(asmr_e_constant_out_of_bounds);
|
|
RecoverConsume(false);
|
|
exit;
|
|
end
|
|
else
|
|
begin
|
|
inc(oper.opr.ref.offset,o_int);
|
|
test_end(require_rbracket);
|
|
exit;
|
|
end;
|
|
end;
|
|
AS_ID :
|
|
begin
|
|
recname := actasmpattern;
|
|
Consume(AS_ID);
|
|
BuildRecordOffsetSize(recname,o_int,s_int,recname,false);
|
|
if (o_int>4095)or(o_int<-4095) then
|
|
begin
|
|
Message(asmr_e_constant_out_of_bounds);
|
|
RecoverConsume(false);
|
|
exit;
|
|
end
|
|
else
|
|
begin
|
|
inc(oper.opr.ref.offset,o_int);
|
|
test_end(require_rbracket);
|
|
exit;
|
|
end;
|
|
end;
|
|
AS_AT:
|
|
begin
|
|
do_error;
|
|
exit;
|
|
end;
|
|
AS_DOT : // local label
|
|
begin
|
|
oper.opr.ref.signindex := BuildConstExpression(true,false);
|
|
test_end(require_rbracket);
|
|
exit;
|
|
end;
|
|
AS_RBRACKET :
|
|
begin
|
|
if require_rbracket then
|
|
begin
|
|
test_end(require_rbracket);
|
|
exit;
|
|
end
|
|
else
|
|
begin
|
|
do_error; // unexpected rbracket
|
|
exit;
|
|
end;
|
|
end;
|
|
AS_SEPARATOR,AS_end :
|
|
begin
|
|
if not require_rbracket then
|
|
begin
|
|
test_end(false);
|
|
exit;
|
|
end
|
|
else
|
|
begin
|
|
do_error;
|
|
exit;
|
|
end;
|
|
end;
|
|
else
|
|
begin
|
|
// unexpected token
|
|
do_error;
|
|
exit;
|
|
end;
|
|
end; // case
|
|
end;
|
|
|
|
|
|
procedure try_prepostindexed;
|
|
begin
|
|
Consume(AS_RBRACKET);
|
|
case actasmtoken of
|
|
AS_COMMA :
|
|
begin // post-indexed
|
|
Consume(AS_COMMA);
|
|
oper.opr.ref.addressmode:=AM_POSTINDEXED;
|
|
read_index(false);
|
|
exit;
|
|
end;
|
|
AS_NOT :
|
|
begin // pre-indexed
|
|
Consume(AS_NOT);
|
|
oper.opr.ref.addressmode:=AM_PREINDEXED;
|
|
test_end(false);
|
|
exit;
|
|
end;
|
|
else
|
|
begin
|
|
test_end(false);
|
|
exit;
|
|
end;
|
|
end; // case
|
|
end;
|
|
|
|
var
|
|
lab : TASMLABEL;
|
|
begin
|
|
Consume(AS_LBRACKET);
|
|
oper.opr.ref.addressmode:=AM_OFFSET; // assume "neither PRE nor POST inc"
|
|
if actasmtoken=AS_REGISTER then
|
|
begin
|
|
oper.opr.ref.base:=actasmregister;
|
|
Consume(AS_REGISTER);
|
|
case actasmtoken of
|
|
AS_RBRACKET :
|
|
begin
|
|
try_prepostindexed;
|
|
exit;
|
|
end;
|
|
AS_COMMA :
|
|
begin
|
|
Consume(AS_COMMA);
|
|
read_index(true);
|
|
exit;
|
|
end;
|
|
else
|
|
begin
|
|
Message(asmr_e_invalid_reference_syntax);
|
|
RecoverConsume(false);
|
|
end;
|
|
end;
|
|
end
|
|
else
|
|
{
|
|
if base isn't a register, r15=PC is implied base, so it must be a local label.
|
|
pascal constants don't make sense, because implied r15
|
|
record offsets probably don't make sense, too (a record offset of code?)
|
|
|
|
TODO: However, we could make the Stackpointer implied.
|
|
|
|
}
|
|
|
|
Begin
|
|
case actasmtoken of
|
|
AS_ID :
|
|
begin
|
|
if is_locallabel(actasmpattern) then
|
|
begin
|
|
CreateLocalLabel(actasmpattern,lab,false);
|
|
oper.opr.ref.symbol := lab;
|
|
oper.opr.ref.base := NR_PC;
|
|
Consume(AS_ID);
|
|
test_end(true);
|
|
exit;
|
|
end
|
|
else
|
|
begin
|
|
// TODO: Stackpointer implied,
|
|
Message(asmr_e_invalid_reference_syntax);
|
|
RecoverConsume(false);
|
|
exit;
|
|
end;
|
|
end;
|
|
else
|
|
begin // elsecase
|
|
Message(asmr_e_invalid_reference_syntax);
|
|
RecoverConsume(false);
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
function tarmattreader.TryBuildShifterOp(oper : tarmoperand) : boolean;
|
|
|
|
procedure handlepara(sm : tshiftmode);
|
|
begin
|
|
consume(AS_ID);
|
|
fillchar(oper.opr,sizeof(oper.opr),0);
|
|
oper.opr.typ:=OPR_SHIFTEROP;
|
|
oper.opr.shifterop.shiftmode:=sm;
|
|
if sm<>SM_RRX then
|
|
begin
|
|
case actasmtoken of
|
|
AS_REGISTER:
|
|
begin
|
|
oper.opr.shifterop.rs:=actasmregister;
|
|
consume(AS_REGISTER);
|
|
end;
|
|
AS_HASH:
|
|
begin
|
|
consume(AS_HASH);
|
|
oper.opr.shifterop.shiftimm:=BuildConstExpression(false,false);
|
|
end;
|
|
else
|
|
Message(asmr_e_illegal_shifterop_syntax);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
result:=true;
|
|
if (actasmtoken=AS_ID) then
|
|
begin
|
|
if (actasmpattern='LSL') then
|
|
handlepara(SM_LSL)
|
|
else if (actasmpattern='LSR') then
|
|
handlepara(SM_LSR)
|
|
else if (actasmpattern='ASR') then
|
|
handlepara(SM_ASR)
|
|
else if (actasmpattern='ROR') then
|
|
handlepara(SM_ROR)
|
|
else if (actasmpattern='RRX') then
|
|
handlepara(SM_RRX)
|
|
else
|
|
result:=false;
|
|
end
|
|
else
|
|
result:=false;
|
|
end;
|
|
|
|
|
|
Procedure tarmattreader.BuildOperand(oper : tarmoperand);
|
|
var
|
|
expr : string;
|
|
typesize,l : tcgint;
|
|
|
|
|
|
procedure AddLabelOperand(hl:tasmlabel);
|
|
begin
|
|
if not(actasmtoken in [AS_PLUS,AS_MINUS,AS_LPAREN]) and
|
|
is_calljmp(actopcode) then
|
|
begin
|
|
oper.opr.typ:=OPR_SYMBOL;
|
|
oper.opr.symbol:=hl;
|
|
end
|
|
else
|
|
begin
|
|
oper.InitRef;
|
|
oper.opr.ref.symbol:=hl;
|
|
oper.opr.ref.base:=NR_PC;
|
|
if (actasmtoken in [AS_PLUS, AS_MINUS]) then
|
|
begin
|
|
l:=BuildConstExpression(true,false);
|
|
oper.opr.ref.offset:=l;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure MaybeRecordOffset;
|
|
var
|
|
mangledname: string;
|
|
hasdot : boolean;
|
|
l,
|
|
toffset,
|
|
tsize : tcgint;
|
|
begin
|
|
if not(actasmtoken in [AS_DOT,AS_PLUS,AS_MINUS]) then
|
|
exit;
|
|
l:=0;
|
|
mangledname:='';
|
|
hasdot:=(actasmtoken=AS_DOT);
|
|
if hasdot then
|
|
begin
|
|
if expr<>'' then
|
|
begin
|
|
BuildRecordOffsetSize(expr,toffset,tsize,mangledname,false);
|
|
if (oper.opr.typ<>OPR_CONSTANT) and
|
|
(mangledname<>'') then
|
|
Message(asmr_e_wrong_sym_type);
|
|
inc(l,toffset);
|
|
oper.SetSize(tsize,true);
|
|
end;
|
|
end;
|
|
if actasmtoken in [AS_PLUS,AS_MINUS] then
|
|
inc(l,BuildConstExpression(true,false));
|
|
case oper.opr.typ of
|
|
OPR_LOCAL :
|
|
begin
|
|
{ don't allow direct access to fields of parameters, because that
|
|
will generate buggy code. Allow it only for explicit typecasting }
|
|
if hasdot and
|
|
(not oper.hastype) and
|
|
(oper.opr.localsym.typ=paravarsym) and
|
|
(not(po_assembler in current_procinfo.procdef.procoptions) or
|
|
(tparavarsym(oper.opr.localsym).paraloc[calleeside].location^.loc<>LOC_REGISTER) or
|
|
(not is_implicit_pointer_object_type(oper.opr.localsym.vardef) and
|
|
not paramanager.push_addr_param(oper.opr.localsym.varspez,oper.opr.localsym.vardef,current_procinfo.procdef.proccalloption))) then
|
|
Message(asmr_e_cannot_access_field_directly_for_parameters);
|
|
inc(oper.opr.localsymofs,l)
|
|
end;
|
|
OPR_CONSTANT :
|
|
inc(oper.opr.val,l);
|
|
OPR_REFERENCE :
|
|
if (mangledname<>'') then
|
|
begin
|
|
if (oper.opr.val<>0) then
|
|
Message(asmr_e_wrong_sym_type);
|
|
oper.opr.typ:=OPR_SYMBOL;
|
|
oper.opr.symbol:=current_asmdata.RefAsmSymbol(mangledname,AT_FUNCTION);
|
|
end
|
|
else
|
|
inc(oper.opr.val,l);
|
|
OPR_SYMBOL:
|
|
Message(asmr_e_invalid_symbol_ref);
|
|
else
|
|
internalerror(200309221);
|
|
end;
|
|
end;
|
|
|
|
|
|
function MaybeBuildReference:boolean;
|
|
{ Try to create a reference, if not a reference is found then false
|
|
is returned }
|
|
begin
|
|
MaybeBuildReference:=true;
|
|
case actasmtoken of
|
|
AS_INTNUM,
|
|
AS_MINUS,
|
|
AS_PLUS:
|
|
Begin
|
|
oper.opr.ref.offset:=BuildConstExpression(True,False);
|
|
if actasmtoken<>AS_LPAREN then
|
|
Message(asmr_e_invalid_reference_syntax)
|
|
else
|
|
BuildReference(oper);
|
|
end;
|
|
AS_LPAREN:
|
|
BuildReference(oper);
|
|
AS_ID: { only a variable is allowed ... }
|
|
Begin
|
|
ReadSym(oper);
|
|
case actasmtoken of
|
|
AS_end,
|
|
AS_SEPARATOR,
|
|
AS_COMMA: ;
|
|
AS_LPAREN:
|
|
BuildReference(oper);
|
|
else
|
|
Begin
|
|
Message(asmr_e_invalid_reference_syntax);
|
|
Consume(actasmtoken);
|
|
end;
|
|
end; {end case }
|
|
end;
|
|
else
|
|
MaybeBuildReference:=false;
|
|
end; { end case }
|
|
end;
|
|
|
|
|
|
function is_ConditionCode(hs: string): boolean;
|
|
var icond: tasmcond;
|
|
begin
|
|
is_ConditionCode := false;
|
|
|
|
case actopcode of
|
|
A_IT,A_ITE,A_ITT,
|
|
A_ITEE,A_ITTE,A_ITET,A_ITTT,
|
|
A_ITEEE,A_ITTEE,A_ITETE,A_ITTTE,A_ITEET,A_ITTET,A_ITETT,A_ITTTT:
|
|
begin
|
|
{ search for condition, conditions are always 2 chars }
|
|
if length(hs)>1 then
|
|
begin
|
|
for icond:=low(tasmcond) to high(tasmcond) do
|
|
begin
|
|
if copy(hs,1,2)=uppercond2str[icond] then
|
|
begin
|
|
//actcondition:=icond;
|
|
oper.opr.typ := OPR_COND;
|
|
oper.opr.cc := icond;
|
|
exit(true);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
function is_modeflag(hs : string): boolean;
|
|
var
|
|
i: longint;
|
|
flags: tcpumodeflags;
|
|
begin
|
|
is_modeflag := false;
|
|
|
|
flags:=[];
|
|
hs:=lower(hs);
|
|
|
|
if (actopcode in [A_CPSID,A_CPSIE]) and (length(hs) >= 1) then
|
|
begin
|
|
for i:=1 to length(hs) do
|
|
begin
|
|
case hs[i] of
|
|
'a':
|
|
Include(flags,mfA);
|
|
'f':
|
|
Include(flags,mfF);
|
|
'i':
|
|
Include(flags,mfI);
|
|
else
|
|
exit;
|
|
end;
|
|
end;
|
|
oper.opr.typ := OPR_MODEFLAGS;
|
|
oper.opr.flags := flags;
|
|
exit(true);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure BuildDirectRef;
|
|
|
|
function GetConstLabel(const symname: string; ofs: aint): TAsmLabel;
|
|
var
|
|
hp: tai;
|
|
newconst: tai_const;
|
|
lab: TAsmLabel;
|
|
begin
|
|
if symname<>'' then
|
|
newconst:=tai_const.Createname(symname,ofs)
|
|
else
|
|
newconst:=tai_const.Create_32bit(ofs);
|
|
|
|
hp:=tai(current_procinfo.aktlocaldata.First);
|
|
while assigned(hp) do
|
|
begin
|
|
if hp.typ=ait_const then
|
|
begin
|
|
if (tai_const(hp).sym=newconst.sym) and
|
|
(tai_const(hp).value=newconst.value) and
|
|
assigned(hp.Previous) and
|
|
(tai(hp.previous).typ=ait_label) then
|
|
begin
|
|
newconst.Free;
|
|
result:=tai_label(hp.Previous).labsym;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
hp:=tai(hp.Next);
|
|
end;
|
|
|
|
current_asmdata.getjumplabel(lab);
|
|
current_procinfo.aktlocaldata.concat(tai_align.create(4));
|
|
current_procinfo.aktlocaldata.concat(tai_label.create(lab));
|
|
current_procinfo.aktlocaldata.concat(newconst);
|
|
result:=lab;
|
|
end;
|
|
|
|
var
|
|
symtype: TAsmsymtype;
|
|
sym: string;
|
|
val: tcgint;
|
|
begin
|
|
case actasmtoken of
|
|
AS_INTNUM,
|
|
AS_ID:
|
|
begin
|
|
BuildConstSymbolExpression(true,false,false,val,sym,symtype);
|
|
|
|
if symtype=AT_NONE then
|
|
sym:='';
|
|
|
|
reference_reset(oper.opr.ref,4,[]);
|
|
oper.opr.ref.base:=NR_PC;
|
|
oper.opr.ref.symbol:=GetConstLabel(sym,val);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
function getregsetindex(reg: tregister): integer;
|
|
begin
|
|
if getsubreg(reg)=R_SUBFS then
|
|
begin
|
|
result:=getsupreg(reg)*2;
|
|
if result>32 then
|
|
result:=result-63;
|
|
end
|
|
else
|
|
result:=getsupreg(reg);
|
|
end;
|
|
|
|
var
|
|
tempreg : tregister;
|
|
ireg : tsuperregister;
|
|
regtype: tregistertype;
|
|
subreg: tsubregister;
|
|
hl : tasmlabel;
|
|
{ofs : longint;}
|
|
registerset : tcpuregisterset;
|
|
Begin
|
|
expr:='';
|
|
case actasmtoken of
|
|
AS_LBRACKET: { Memory reference or constant expression }
|
|
Begin
|
|
oper.InitRef;
|
|
BuildReference(oper);
|
|
end;
|
|
|
|
AS_HASH: { Constant expression }
|
|
Begin
|
|
Consume(AS_HASH);
|
|
BuildConstantOperand(oper);
|
|
end;
|
|
|
|
AS_EQUAL:
|
|
begin
|
|
case actopcode of
|
|
A_LDRBT,A_LDRB,A_LDR,A_LDRH,A_LDRSB,A_LDRSH,A_LDRT,
|
|
A_LDREX,A_LDREXB,A_LDREXD,A_LDREXH:
|
|
begin
|
|
consume(AS_EQUAL);
|
|
oper.InitRef;
|
|
BuildDirectRef;
|
|
end;
|
|
else
|
|
Message(asmr_e_invalid_opcode_and_operand);
|
|
end;
|
|
end;
|
|
|
|
(*
|
|
AS_INTNUM,
|
|
AS_MINUS,
|
|
AS_PLUS:
|
|
Begin
|
|
{ Constant memory offset }
|
|
{ This must absolutely be followed by ( }
|
|
oper.InitRef;
|
|
oper.opr.ref.offset:=BuildConstExpression(True,False);
|
|
if actasmtoken<>AS_LPAREN then
|
|
begin
|
|
ofs:=oper.opr.ref.offset;
|
|
BuildConstantOperand(oper);
|
|
inc(oper.opr.val,ofs);
|
|
end
|
|
else
|
|
BuildReference(oper);
|
|
end;
|
|
*)
|
|
AS_ID: { A constant expression, or a Variable ref. }
|
|
Begin
|
|
if is_modeflag(actasmpattern) then
|
|
begin
|
|
consume(AS_ID);
|
|
end
|
|
else
|
|
{ Condition code? }
|
|
if is_conditioncode(actasmpattern) then
|
|
begin
|
|
consume(AS_ID);
|
|
end
|
|
else
|
|
{ Local Label ? }
|
|
if is_locallabel(actasmpattern) then
|
|
begin
|
|
CreateLocalLabel(actasmpattern,hl,false);
|
|
Consume(AS_ID);
|
|
AddLabelOperand(hl);
|
|
end
|
|
else
|
|
{ Check for label }
|
|
if SearchLabel(actasmpattern,hl,false) then
|
|
begin
|
|
Consume(AS_ID);
|
|
AddLabelOperand(hl);
|
|
end
|
|
else
|
|
{ probably a variable or normal expression }
|
|
{ or a procedure (such as in CALL ID) }
|
|
Begin
|
|
{ is it a constant ? }
|
|
if SearchIConstant(actasmpattern,l) then
|
|
Begin
|
|
if not (oper.opr.typ in [OPR_NONE,OPR_CONSTANT]) then
|
|
Message(asmr_e_invalid_operand_type);
|
|
BuildConstantOperand(oper);
|
|
end
|
|
else
|
|
begin
|
|
expr:=actasmpattern;
|
|
Consume(AS_ID);
|
|
{ typecasting? }
|
|
if (actasmtoken=AS_LPAREN) and
|
|
SearchType(expr,typesize) then
|
|
begin
|
|
oper.hastype:=true;
|
|
Consume(AS_LPAREN);
|
|
BuildOperand(oper);
|
|
Consume(AS_RPAREN);
|
|
if oper.opr.typ in [OPR_REFERENCE,OPR_LOCAL] then
|
|
oper.SetSize(typesize,true);
|
|
end
|
|
else
|
|
begin
|
|
if not(oper.SetupVar(expr,false)) then
|
|
Begin
|
|
{ look for special symbols ... }
|
|
if expr= '__HIGH' then
|
|
begin
|
|
consume(AS_LPAREN);
|
|
if not oper.setupvar('high'+actasmpattern,false) then
|
|
Message1(sym_e_unknown_id,'high'+actasmpattern);
|
|
consume(AS_ID);
|
|
consume(AS_RPAREN);
|
|
end
|
|
else
|
|
if expr = '__RESULT' then
|
|
oper.SetUpResult
|
|
else
|
|
if expr = '__SELF' then
|
|
oper.SetupSelf
|
|
else
|
|
if expr = '__OLDEBP' then
|
|
oper.SetupOldEBP
|
|
else
|
|
Message1(sym_e_unknown_id,expr);
|
|
end;
|
|
end;
|
|
end;
|
|
if actasmtoken=AS_DOT then
|
|
MaybeRecordOffset;
|
|
{ add a constant expression? }
|
|
if (actasmtoken=AS_PLUS) then
|
|
begin
|
|
l:=BuildConstExpression(true,false);
|
|
case oper.opr.typ of
|
|
OPR_CONSTANT :
|
|
inc(oper.opr.val,l);
|
|
OPR_LOCAL :
|
|
inc(oper.opr.localsymofs,l);
|
|
OPR_REFERENCE :
|
|
inc(oper.opr.ref.offset,l);
|
|
else
|
|
internalerror(200309202);
|
|
end;
|
|
end
|
|
end;
|
|
{ Do we have a indexing reference, then parse it also }
|
|
if actasmtoken=AS_LPAREN then
|
|
BuildReference(oper);
|
|
end;
|
|
|
|
{ Register, a variable reference or a constant reference }
|
|
AS_REGISTER:
|
|
Begin
|
|
{ save the type of register used. }
|
|
tempreg:=actasmregister;
|
|
Consume(AS_REGISTER);
|
|
if (actasmtoken in [AS_end,AS_SEPARATOR,AS_COMMA]) then
|
|
Begin
|
|
if not (oper.opr.typ in [OPR_NONE,OPR_REGISTER]) then
|
|
Message(asmr_e_invalid_operand_type);
|
|
oper.opr.typ:=OPR_REGISTER;
|
|
oper.opr.reg:=tempreg;
|
|
end
|
|
else if (actasmtoken=AS_NOT) and (actopcode in [A_LDM,A_STM,A_FLDM,A_FSTM,A_VLDM,A_VSTM,A_SRS,A_RFE]) then
|
|
begin
|
|
consume(AS_NOT);
|
|
oper.opr.typ:=OPR_REFERENCE;
|
|
oper.opr.ref.addressmode:=AM_PREINDEXED;
|
|
oper.opr.ref.index:=tempreg;
|
|
end
|
|
else
|
|
Message(asmr_e_syn_operand);
|
|
end;
|
|
|
|
{ Registerset }
|
|
AS_LSBRACKET:
|
|
begin
|
|
consume(AS_LSBRACKET);
|
|
registerset:=[];
|
|
regtype:=R_INVALIDREGISTER;
|
|
subreg:=R_SUBNONE;
|
|
while actasmtoken<>AS_RSBRACKET do
|
|
begin
|
|
if actasmtoken=AS_REGISTER then
|
|
begin
|
|
include(registerset,getregsetindex(actasmregister));
|
|
if regtype<>R_INVALIDREGISTER then
|
|
begin
|
|
if (getregtype(actasmregister)<>regtype) or
|
|
(getsubreg(actasmregister)<>subreg) then
|
|
Message(asmr_e_mixing_regtypes);
|
|
end
|
|
else
|
|
begin
|
|
regtype:=getregtype(actasmregister);
|
|
subreg:=getsubreg(actasmregister);
|
|
end;
|
|
tempreg:=actasmregister;
|
|
consume(AS_REGISTER);
|
|
if actasmtoken=AS_MINUS then
|
|
begin
|
|
consume(AS_MINUS);
|
|
for ireg:=getregsetindex(tempreg) to getregsetindex(actasmregister) do
|
|
include(registerset,ireg);
|
|
consume(AS_REGISTER);
|
|
end;
|
|
end
|
|
else
|
|
consume(AS_REGISTER);
|
|
if actasmtoken=AS_COMMA then
|
|
consume(AS_COMMA)
|
|
else
|
|
break;
|
|
end;
|
|
consume(AS_RSBRACKET);
|
|
oper.opr.typ:=OPR_REGSET;
|
|
oper.opr.regtype:=regtype;
|
|
oper.opr.subreg:=subreg;
|
|
oper.opr.regset:=registerset;
|
|
if actasmtoken=AS_XOR then
|
|
begin
|
|
consume(AS_XOR);
|
|
oper.opr.usermode:=true;
|
|
end
|
|
else
|
|
oper.opr.usermode:=false;
|
|
if (registerset=[]) then
|
|
Message(asmr_e_empty_regset);
|
|
end;
|
|
AS_end,
|
|
AS_SEPARATOR,
|
|
AS_COMMA: ;
|
|
else
|
|
Begin
|
|
Message(asmr_e_syn_operand);
|
|
Consume(actasmtoken);
|
|
end;
|
|
end; { end case }
|
|
end;
|
|
|
|
procedure tarmattreader.BuildSpecialreg(oper: tarmoperand);
|
|
var
|
|
hs, reg : String;
|
|
ch : char;
|
|
i, t : longint;
|
|
hreg : tregister;
|
|
flags : tspecialregflags;
|
|
begin
|
|
hreg:=NR_NO;
|
|
case actasmtoken of
|
|
AS_REGISTER:
|
|
begin
|
|
oper.opr.typ:=OPR_REGISTER;
|
|
oper.opr.reg:=actasmregister;
|
|
Consume(AS_REGISTER);
|
|
end;
|
|
AS_ID:
|
|
begin
|
|
t := pos('_', actasmpattern);
|
|
if t > 0 then
|
|
begin
|
|
hs:=lower(actasmpattern);
|
|
reg:=copy(hs, 1, t-1);
|
|
delete(hs, 1, t);
|
|
|
|
if length(hs) < 1 then
|
|
Message(asmr_e_invalid_operand_type);
|
|
|
|
if reg = 'cpsr' then
|
|
hreg:=NR_CPSR
|
|
else if reg='spsr' then
|
|
hreg:=NR_SPSR
|
|
else
|
|
Message(asmr_e_invalid_register);
|
|
|
|
flags:=[];
|
|
for i := 1 to length(hs) do
|
|
begin
|
|
ch:=hs[i];
|
|
if ch='c' then
|
|
include(flags, srC)
|
|
else if ch='x' then
|
|
include(flags, srX)
|
|
else if ch='f' then
|
|
include(flags, srF)
|
|
else if ch='s' then
|
|
include(flags, srS)
|
|
else
|
|
message(asmr_e_invalid_operand_type);
|
|
end;
|
|
|
|
oper.opr.typ:=OPR_SPECIALREG;
|
|
oper.opr.specialreg:=hreg;
|
|
oper.opr.specialregflags:=flags;
|
|
|
|
consume(AS_ID);
|
|
end
|
|
else
|
|
Message(asmr_e_invalid_operand_type); // Otherwise it would have been seen as a AS_REGISTER
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
{*****************************************************************************
|
|
tarmattreader
|
|
*****************************************************************************}
|
|
|
|
procedure tarmattreader.BuildOpCode(instr : tarminstruction);
|
|
var
|
|
operandnum : longint;
|
|
Begin
|
|
{ opcode }
|
|
if (actasmtoken<>AS_OPCODE) then
|
|
Begin
|
|
Message(asmr_e_invalid_or_missing_opcode);
|
|
RecoverConsume(true);
|
|
exit;
|
|
end;
|
|
{ Fill the instr object with the current state }
|
|
with instr do
|
|
begin
|
|
Opcode:=ActOpcode;
|
|
condition:=ActCondition;
|
|
oppostfix:=actoppostfix;
|
|
wideformat:=actwideformat;
|
|
end;
|
|
|
|
{ We are reading operands, so opcode will be an AS_ID }
|
|
operandnum:=1;
|
|
Consume(AS_OPCODE);
|
|
{ Zero operand opcode ? }
|
|
if actasmtoken in [AS_SEPARATOR,AS_end] then
|
|
begin
|
|
operandnum:=0;
|
|
exit;
|
|
end;
|
|
{ Read the operands }
|
|
repeat
|
|
case actasmtoken of
|
|
AS_COMMA: { Operand delimiter }
|
|
Begin
|
|
if ((instr.opcode in [A_MOV,A_MVN,A_CMP,A_CMN,A_TST,A_TEQ,
|
|
A_UXTB,A_UXTH,A_UXTB16,
|
|
A_SXTB,A_SXTH,A_SXTB16]) and
|
|
(operandnum=2)) or
|
|
((operandnum=3) and not(instr.opcode in [A_UMLAL,A_UMULL,A_SMLAL,A_SMULL,A_MLA,A_UMAAL,A_MLS,
|
|
A_SMLABB,A_SMLABT,A_SMLATB,A_SMLATT,A_SMMLA,A_SMMLS,A_SMLAD,A_SMLALD,A_SMLSD,
|
|
A_SMLALBB,A_SMLALBT,A_SMLALTB,A_SMLALTT,A_SMLSLD,
|
|
A_SMLAWB,A_SMLAWT,
|
|
A_MRC,A_MCR,A_MCRR,A_MRRC,A_MRC2,A_MCR2,A_MCRR2,A_MRRC2,
|
|
A_STREXD,A_STRD,
|
|
A_USADA8,
|
|
A_VMOV,
|
|
A_SBFX,A_UBFX,A_BFI])) then
|
|
begin
|
|
Consume(AS_COMMA);
|
|
if not(TryBuildShifterOp(instr.Operands[operandnum+1] as tarmoperand)) then
|
|
Message(asmr_e_illegal_shifterop_syntax);
|
|
Inc(operandnum);
|
|
end
|
|
else
|
|
begin
|
|
if operandnum>Max_Operands then
|
|
Message(asmr_e_too_many_operands)
|
|
else
|
|
Inc(operandnum);
|
|
Consume(AS_COMMA);
|
|
end;
|
|
end;
|
|
AS_SEPARATOR,
|
|
AS_end : { End of asm operands for this opcode }
|
|
begin
|
|
break;
|
|
end;
|
|
else
|
|
if ((instr.opcode = A_MRS) and (operandnum = 2)) or
|
|
((instr.opcode = A_MSR) and (operandnum = 1)) then
|
|
BuildSpecialreg(instr.Operands[operandnum] as tarmoperand)
|
|
else
|
|
BuildOperand(instr.Operands[operandnum] as tarmoperand);
|
|
end; { end case }
|
|
until false;
|
|
instr.Ops:=operandnum;
|
|
end;
|
|
|
|
|
|
function tarmattreader.is_asmopcode(const s: string):boolean;
|
|
|
|
const
|
|
{ sorted by length so longer postfixes will match first }
|
|
postfix2strsorted : array[1..70] of string[9] = (
|
|
'.F32.S32','.F32.U32','.S32.F32','.U32.F32','.F64.S32','.F64.U32','.S32.F64','.U32.F64',
|
|
'.F32.S16','.F32.U16','.S16.F32','.U16.F32','.F64.S16','.F64.U16','.S16.F64','.U16.F64',
|
|
'.F32.F64','.F64.F32',
|
|
'.I16','.I32','.I64','.S16','.S32','.S64','.U16','.U32','.U64','.F32','.F64',
|
|
'IAD','DBD','FDD','EAD','IAS','DBS','FDS','EAS','IAX','DBX','FDX','EAX',
|
|
'.16','.32','.64','.I8','.S8','.U8','.P8',
|
|
'EP','SB','BT','SH','IA','IB','DA','DB','FD','FA','ED','EA',
|
|
'.8','S','D','E','P','X','R','B','H','T');
|
|
|
|
postfixsorted : array[1..70] of TOpPostfix = (
|
|
PF_F32S32,PF_F32U32,PF_S32F32,PF_U32F32,PF_F64S32,PF_F64U32,PF_S32F64,PF_U32F64,
|
|
PF_F32S16,PF_F32U16,PF_S16F32,PF_U16F32,PF_F64S16,PF_F64U16,PF_S16F64,PF_U16F64,
|
|
PF_F32F64,PF_F64F32,
|
|
PF_I16,PF_I32,
|
|
PF_I64,PF_S16,PF_S32,PF_S64,PF_U16,PF_U32,PF_U64,PF_F32,
|
|
PF_F64,PF_IAD,PF_DBD,PF_FDD,PF_EAD,
|
|
PF_IAS,PF_DBS,PF_FDS,PF_EAS,PF_IAX,
|
|
PF_DBX,PF_FDX,PF_EAX,PF_16,PF_32,
|
|
PF_64,PF_I8,PF_S8,PF_U8,PF_P8,
|
|
PF_EP,PF_SB,PF_BT,PF_SH,PF_IA,
|
|
PF_IB,PF_DA,PF_DB,PF_FD,PF_FA,
|
|
PF_ED,PF_EA,PF_8,PF_S,PF_D,PF_E,
|
|
PF_P,PF_X,PF_R,PF_B,PF_H,PF_T);
|
|
|
|
var
|
|
j, j2 : longint;
|
|
hs,hs2 : string;
|
|
maxlen : longint;
|
|
icond : tasmcond;
|
|
Begin
|
|
{ making s a value parameter would break other assembler readers }
|
|
hs:=s;
|
|
is_asmopcode:=false;
|
|
|
|
{ clear op code }
|
|
actopcode:=A_None;
|
|
|
|
actcondition:=C_None;
|
|
|
|
{ first, handle B else BLS is read wrong }
|
|
if ((hs[1]='B') and (length(hs)=3)) then
|
|
begin
|
|
for icond:=low(tasmcond) to high(tasmcond) do
|
|
begin
|
|
if copy(hs,2,3)=uppercond2str[icond] then
|
|
begin
|
|
actopcode:=A_B;
|
|
actasmtoken:=AS_OPCODE;
|
|
actcondition:=icond;
|
|
is_asmopcode:=true;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
maxlen:=min(length(hs),6);
|
|
actopcode:=A_NONE;
|
|
j2:=maxlen;
|
|
hs2:=hs;
|
|
while j2>=1 do
|
|
begin
|
|
hs:=hs2;
|
|
while j2>=1 do
|
|
begin
|
|
actopcode:=tasmop(PtrUInt(iasmops.Find(copy(hs,1,j2))));
|
|
if actopcode<>A_NONE then
|
|
begin
|
|
actasmtoken:=AS_OPCODE;
|
|
{ strip op code }
|
|
delete(hs,1,j2);
|
|
dec(j2);
|
|
break;
|
|
end;
|
|
dec(j2);
|
|
end;
|
|
|
|
if actopcode=A_NONE then
|
|
exit;
|
|
|
|
if is_unified then
|
|
begin
|
|
{ check for postfix }
|
|
if (length(hs)>0) and (actoppostfix=PF_None) then
|
|
begin
|
|
for j:=low(postfixsorted) to high(postfixsorted) do
|
|
begin
|
|
if copy(hs,1,length(postfix2strsorted[j]))=postfix2strsorted[j] then
|
|
begin
|
|
if not ((length(hs)-length(postfix2strsorted[j])) in [0,2,4]) then
|
|
continue;
|
|
|
|
actoppostfix:=postfixsorted[j];
|
|
{ strip postfix }
|
|
delete(hs,1,length(postfix2strsorted[j]));
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
{ search for condition, conditions are always 2 chars }
|
|
if length(hs)>1 then
|
|
begin
|
|
for icond:=low(tasmcond) to high(tasmcond) do
|
|
begin
|
|
if copy(hs,1,2)=uppercond2str[icond] then
|
|
begin
|
|
actcondition:=icond;
|
|
{ strip condition }
|
|
delete(hs,1,2);
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
{ check for postfix }
|
|
if (length(hs)>0) and (actoppostfix=PF_None) then
|
|
begin
|
|
for j:=low(postfixsorted) to high(postfixsorted) do
|
|
begin
|
|
if copy(hs,1,length(postfix2strsorted[j]))=postfix2strsorted[j] then
|
|
begin
|
|
if not ((length(hs)-length(postfix2strsorted[j])) = 0) then
|
|
continue;
|
|
|
|
actoppostfix:=postfixsorted[j];
|
|
{ strip postfix }
|
|
delete(hs,1,length(postfix2strsorted[j]));
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
{ search for condition, conditions are always 2 chars }
|
|
if length(hs)>1 then
|
|
begin
|
|
for icond:=low(tasmcond) to high(tasmcond) do
|
|
begin
|
|
if copy(hs,1,2)=uppercond2str[icond] then
|
|
begin
|
|
actcondition:=icond;
|
|
{ strip condition }
|
|
delete(hs,1,2);
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
{ check for postfix }
|
|
if (length(hs)>0) and (actoppostfix=PF_None) then
|
|
begin
|
|
for j:=low(postfixsorted) to high(postfixsorted) do
|
|
begin
|
|
if copy(hs,1,length(postfix2strsorted[j]))=postfix2strsorted[j] then
|
|
begin
|
|
actoppostfix:=postfixsorted[j];
|
|
{ strip postfix }
|
|
delete(hs,1,length(postfix2strsorted[j]));
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
{ check for format postfix }
|
|
if length(hs)>0 then
|
|
begin
|
|
if copy(hs,1,2) = '.W' then
|
|
begin
|
|
actwideformat:=true;
|
|
delete(hs,1,2);
|
|
end;
|
|
end;
|
|
{ if we stripped all postfixes, it's a valid opcode }
|
|
is_asmopcode:=length(hs)=0;
|
|
if is_asmopcode = true then
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure tarmattreader.ConvertCalljmp(instr : tarminstruction);
|
|
var
|
|
newopr : toprrec;
|
|
begin
|
|
if instr.Operands[1].opr.typ=OPR_REFERENCE then
|
|
begin
|
|
newopr.typ:=OPR_SYMBOL;
|
|
newopr.symbol:=instr.Operands[1].opr.ref.symbol;
|
|
newopr.symofs:=instr.Operands[1].opr.ref.offset;
|
|
if (instr.Operands[1].opr.ref.base<>NR_NO) or
|
|
(instr.Operands[1].opr.ref.index<>NR_NO) then
|
|
Message(asmr_e_syn_operand);
|
|
instr.Operands[1].opr:=newopr;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure tarmattreader.HandleTargetDirective;
|
|
var
|
|
symname,
|
|
symval : String;
|
|
val : tcgint;
|
|
symtyp : TAsmsymtype;
|
|
begin
|
|
case actasmpattern of
|
|
'.thumb_set':
|
|
begin
|
|
consume(AS_TARGET_DIRECTIVE);
|
|
BuildConstSymbolExpression(true,false,false, val,symname,symtyp);
|
|
Consume(AS_COMMA);
|
|
BuildConstSymbolExpression(true,false,false, val,symval,symtyp);
|
|
|
|
curList.concat(tai_symbolpair.create(spk_thumb_set,symname,symval));
|
|
end;
|
|
'.code':
|
|
begin
|
|
consume(AS_TARGET_DIRECTIVE);
|
|
val:=BuildConstExpression(false,false);
|
|
if not(val in [16,32]) then
|
|
Message(asmr_e_invalid_code_value);
|
|
curList.concat(tai_directive.create(asd_code,tostr(val)));
|
|
end;
|
|
'.thumb_func':
|
|
begin
|
|
consume(AS_TARGET_DIRECTIVE);
|
|
curList.concat(tai_directive.create(asd_thumb_func,''));
|
|
end
|
|
else
|
|
inherited HandleTargetDirective;
|
|
end;
|
|
end;
|
|
|
|
|
|
function tarmattreader.is_unified: boolean;
|
|
begin
|
|
result:=false;
|
|
end;
|
|
|
|
|
|
procedure tarmattreader.handleopcode;
|
|
var
|
|
instr : tarminstruction;
|
|
begin
|
|
instr:=TarmInstruction.Create(TarmOperand);
|
|
BuildOpcode(instr);
|
|
if is_calljmp(instr.opcode) then
|
|
ConvertCalljmp(instr);
|
|
{
|
|
instr.AddReferenceSizes;
|
|
instr.SetInstructionOpsize;
|
|
instr.CheckOperandSizes;
|
|
}
|
|
instr.ConcatInstruction(curlist);
|
|
instr.Free;
|
|
actoppostfix:=PF_None;
|
|
actwideformat:=false;
|
|
end;
|
|
|
|
|
|
{*****************************************************************************
|
|
Initialize
|
|
*****************************************************************************}
|
|
|
|
const
|
|
asmmode_arm_att_info : tasmmodeinfo =
|
|
(
|
|
id : asmmode_arm_gas;
|
|
idtxt : 'DIVIDED';
|
|
casmreader : tarmattreader;
|
|
);
|
|
|
|
asmmode_arm_att_unified_info : tasmmodeinfo =
|
|
(
|
|
id : asmmode_arm_gas_unified;
|
|
idtxt : 'UNIFIED';
|
|
casmreader : tarmunifiedattreader;
|
|
);
|
|
|
|
asmmode_arm_standard_info : tasmmodeinfo =
|
|
(
|
|
id : asmmode_standard;
|
|
idtxt : 'STANDARD';
|
|
casmreader : tarmattreader;
|
|
);
|
|
|
|
initialization
|
|
RegisterAsmMode(asmmode_arm_att_info);
|
|
RegisterAsmMode(asmmode_arm_att_unified_info);
|
|
RegisterAsmMode(asmmode_arm_standard_info);
|
|
end.
|