123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383 |
- {
- Copyright (c) 1998-2003 by Carl Eric Codere and Peter Vreman
- Handles the common arm assembler reader routines
- 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 raavr;
- {$i fpcdefs.inc}
- interface
- uses
- cpubase,
- aasmtai,aasmdata,
- rautils,
- globtype;
- type
- TAVROperand=class(TOperand)
- end;
- TAVRInstruction=class(TInstruction)
- function ConcatInstruction(p:TAsmList) : tai;override;
- end;
- TRegType = (rt_all, rt_even, rt_16_31, rt_even_24_30, rt_16_23, rt_XYZ, rt_YZ, rt_X, rt_Y, rt_Z);
- TAVROpConstraint = record
- case typ : toptype of
- top_none : ();
- top_reg : (rt: TRegType; am: set of taddressmode; minconst, maxconst: tcgint);
- top_ref,
- top_const : (max, min: tcgint);
- end;
- // Constraints of operands per opcode
- // Encode variable number of operands by bit position, e.g.
- // 2 operands = 1 shl 2 = 4
- // 1 or 2 operands = 1 shl 1 + 1 shl 2 = 6
- TAVRInstrConstraint = record
- numOperands: byte;
- Operands: array[0..max_operands-1] of TAVROpConstraint;
- end;
- {$PUSH}
- {$WARN 3177 off : Some fields coming after "$1" were not initialized}
- const
- AVRInstrConstraint: array[TAsmOp] of TAVRInstrConstraint =
- // A_NONE
- ((numOperands: 0; Operands: ((typ: top_none), (typ: top_none))),
- // A_ADD
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_all), (typ: top_reg; rt: rt_all))),
- // A_ADC
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_all), (typ: top_reg; rt: rt_all))),
- // A_ADIW
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_even_24_30), (typ: top_const; max: 63; min: 0))),
- // A_SUB
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_all), (typ: top_reg; rt: rt_all))),
- // A_SUBI
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_16_31), (typ: top_const; max: 255; min: -128))),
- // A_SBC
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_all), (typ: top_reg; rt: rt_all))),
- // A_SBCI
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_16_31), (typ: top_const; max: 255; min: -128))),
- // A_SBRC
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_all), (typ: top_const; max: 7; min: 0))),
- // A_SBRS
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_all), (typ: top_const; max: 7; min: 0))),
- // A_SBIW
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_even_24_30), (typ: top_const; max: 63; min: 0))),
- // A_AND
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_all), (typ: top_reg; rt: rt_all))),
- // A_ANDI
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_16_31), (typ: top_const; max: 255; min: -128))),
- // A_OR
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_all), (typ: top_reg; rt: rt_all))),
- // A_ORI
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_16_31), (typ: top_const; max: 255; min: -128))),
- // A_EOR
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_all), (typ: top_reg; rt: rt_all))),
- // A_COM
- (numOperands: (1 shl 1); Operands: ((typ: top_reg; rt: rt_all), (typ: top_none))),
- // A_NEG
- (numOperands: (1 shl 1); Operands: ((typ: top_reg; rt: rt_all), (typ: top_none))),
- // A_SBR
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_16_31), (typ: top_const; max: 255; min: -128))),
- // A_CBR
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_16_31), (typ: top_const; max: 255; min: -128))),
- // A_INC
- (numOperands: (1 shl 1); Operands: ((typ: top_reg; rt: rt_all), (typ: top_none))),
- // A_DEC
- (numOperands: (1 shl 1); Operands: ((typ: top_reg; rt: rt_all), (typ: top_none))),
- // A_TST
- (numOperands: (1 shl 1); Operands: ((typ: top_reg; rt: rt_all), (typ: top_none))),
- // A_MUL
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_all), (typ: top_reg; rt: rt_all))),
- // A_MULS
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_16_31), (typ: top_reg; rt: rt_16_31))),
- // A_MULSU
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_16_23), (typ: top_reg; rt: rt_16_23))),
- // A_FMUL
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_16_23), (typ: top_reg; rt: rt_16_23))),
- // A_FMULS
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_16_23), (typ: top_reg; rt: rt_16_23))),
- // A_FMULSU
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_16_23), (typ: top_reg; rt: rt_16_23))),
- // A_RJMP
- (numOperands: (1 shl 1); Operands: ((typ: top_const; max: 2047; min: -2048), (typ: top_reg; rt: rt_all))),
- // A_IJMP
- (numOperands: (1 shl 0); Operands: ((typ: top_none), (typ: top_none))),
- // A_EIJMP
- (numOperands: (1 shl 0); Operands: ((typ: top_none), (typ: top_none))),
- // A_JMP, max size depends on size op PC
- (numOperands: (1 shl 1); Operands: ((typ: top_const; max: (1 shl 22 - 1); min: 0), (typ: top_none))),
- // A_RCALL
- (numOperands: (1 shl 1); Operands: ((typ: top_const; max: 2047; min: -2048), (typ: top_none))),
- // A_ICALL
- (numOperands: (1 shl 0); Operands: ((typ: top_none), (typ: top_none))),
- // A_EICALL
- (numOperands: (1 shl 0); Operands: ((typ: top_none), (typ: top_none))),
- // A_CALL, max size depends on size op PC
- (numOperands: (1 shl 1); Operands: ((typ: top_const; max: (1 shl 22 - 1); min: 0), (typ: top_none))),
- // A_RET
- (numOperands: (1 shl 0); Operands: ((typ: top_none), (typ: top_none))),
- // A_IRET
- (numOperands: (1 shl 0); Operands: ((typ: top_none), (typ: top_none))),
- // A_CPSE
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_all), (typ: top_reg; rt: rt_all))),
- // A_CP
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_all), (typ: top_reg; rt: rt_all))),
- // A_CPC
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_all), (typ: top_reg; rt: rt_all))),
- // A_CPI
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_16_31), (typ: top_const; max: 255; min: -128))),
- // A_SBIC
- (numOperands: (1 shl 2); Operands: ((typ: top_const; max: 31; min: 0), (typ: top_const; max: 7; min: 0))),
- // A_SBIS
- (numOperands: (1 shl 2); Operands: ((typ: top_const; max: 31; min: 0), (typ: top_const; max: 7; min: 0))),
- // A_BRxx
- (numOperands: (1 shl 1); Operands: ((typ: top_const; max: 63; min: -64), (typ: top_none))),
- // A_MOV
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_all), (typ: top_reg; rt: rt_all))),
- // A_MOVW
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_even), (typ: top_reg; rt: rt_even))),
- // A_LDI
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_16_31), (typ: top_const; max: 255; min: -128))),
- // A_LDS TODO: There are 2 versions with different machine codes and constant ranges. Could possibly distinguish based on size of PC?
- // Perhaps handle separately with a check on sub-architecture? Range check only important if smaller instruction code selected on larger arch
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_all), (typ: top_const; max: 65535; min: 0))),
- // A_LD
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_all), (typ: top_reg; rt: rt_XYZ; am: [AM_UNCHANGED, AM_POSTINCREMENT, AM_PREDECREMENT]))),
- // A_LDD
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_all), (typ: top_reg; rt: rt_YZ; am: [AM_UNCHANGED]; minconst: 0; maxconst: 63))),
- // A_STS TODO: See LDS above
- (numOperands: (1 shl 2); Operands: ((typ: top_const; max: 65535; min: 0), (typ: top_reg; rt: rt_all))),
- // A_ST
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_XYZ; am: [AM_UNCHANGED, AM_POSTINCREMENT, AM_PREDECREMENT]), (typ: top_reg; rt: rt_all))),
- // A_STD
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_YZ; am: [AM_UNCHANGED]; minconst: 0; maxconst: 63), (typ: top_reg; rt: rt_all))),
- // A_LPM
- (numOperands: (1 shl 0 + 1 shl 2); Operands: ((typ: top_reg; rt: rt_all), (typ: top_reg; rt: rt_Z; am: [AM_UNCHANGED, AM_POSTINCREMENT]))),
- // A_ELPM
- (numOperands: (1 shl 0 + 1 shl 2); Operands: ((typ: top_reg; rt: rt_all), (typ: top_reg; rt: rt_Z; am: [AM_UNCHANGED, AM_POSTINCREMENT]))),
- // A_SPM
- (numOperands: (1 shl 0 + 1 shl 1); Operands: ((typ: top_reg; rt: rt_Z; am: [AM_UNCHANGED, AM_POSTINCREMENT]), (typ: top_none))),
- // A_IN
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_all), (typ: top_const; max: 63; min: 0))),
- // A_OUT
- (numOperands: (1 shl 2); Operands: ((typ: top_const; max: 63; min: 0), (typ: top_reg; rt: rt_all))),
- // A_PUSH
- (numOperands: (1 shl 1); Operands: ((typ: top_reg; rt: rt_all), (typ: top_none))),
- // A_POP
- (numOperands: (1 shl 1); Operands: ((typ: top_reg; rt: rt_all), (typ: top_none))),
- // A_LSL
- (numOperands: (1 shl 1); Operands: ((typ: top_reg; rt: rt_all), (typ: top_none))),
- // A_LSR
- (numOperands: (1 shl 1); Operands: ((typ: top_reg; rt: rt_all), (typ: top_none))),
- // A_ROL
- (numOperands: (1 shl 1); Operands: ((typ: top_reg; rt: rt_all), (typ: top_none))),
- // A_ROR
- (numOperands: (1 shl 1); Operands: ((typ: top_reg; rt: rt_all), (typ: top_none))),
- // A_ASR
- (numOperands: (1 shl 1); Operands: ((typ: top_reg; rt: rt_all), (typ: top_none))),
- // A_SWAP
- (numOperands: (1 shl 1); Operands: ((typ: top_reg; rt: rt_all), (typ: top_none))),
- // A_BSET
- (numOperands: (1 shl 1); Operands: ((typ: top_const; max: 7; min: 0), (typ: top_none))),
- // A_BCLR
- (numOperands: (1 shl 1); Operands: ((typ: top_const; max: 7; min: 0), (typ: top_none))),
- // A_SBI
- (numOperands: (1 shl 2); Operands: ((typ: top_const; max: 32; min: 0), (typ: top_const; max: 7; min: 0))),
- // A_CBI
- (numOperands: (1 shl 2); Operands: ((typ: top_const; max: 32; min: 0), (typ: top_const; max: 7; min: 0))),
- // A_SEC
- (numOperands: (1 shl 0); Operands: ((typ: top_none), (typ: top_none))),
- // A_SEH
- (numOperands: (1 shl 0); Operands: ((typ: top_none), (typ: top_none))),
- // A_SEI
- (numOperands: (1 shl 0); Operands: ((typ: top_none), (typ: top_none))),
- // A_SEN
- (numOperands: (1 shl 0); Operands: ((typ: top_none), (typ: top_none))),
- // A_SER
- (numOperands: (1 shl 1); Operands: ((typ: top_reg; rt: rt_16_31), (typ: top_none))),
- // A_SES
- (numOperands: (1 shl 0); Operands: ((typ: top_none), (typ: top_none))),
- // A_SET
- (numOperands: (1 shl 0); Operands: ((typ: top_none), (typ: top_none))),
- // A_SEV
- (numOperands: (1 shl 0); Operands: ((typ: top_none), (typ: top_none))),
- // A_SEZ
- (numOperands: (1 shl 0); Operands: ((typ: top_none), (typ: top_none))),
- // A_CLC
- (numOperands: (1 shl 0); Operands: ((typ: top_none), (typ: top_none))),
- // A_CLH
- (numOperands: (1 shl 0); Operands: ((typ: top_none), (typ: top_none))),
- // A_CLI
- (numOperands: (1 shl 0); Operands: ((typ: top_none), (typ: top_none))),
- // A_CLN
- (numOperands: (1 shl 0); Operands: ((typ: top_none), (typ: top_none))),
- // A_CLR
- (numOperands: (1 shl 1); Operands: ((typ: top_reg; rt: rt_all), (typ: top_none))),
- // A_CLS
- (numOperands: (1 shl 0); Operands: ((typ: top_none), (typ: top_none))),
- // A_CLT
- (numOperands: (1 shl 0); Operands: ((typ: top_none), (typ: top_none))),
- // A_CLV
- (numOperands: (1 shl 0); Operands: ((typ: top_none), (typ: top_none))),
- // A_CLZ
- (numOperands: (1 shl 0); Operands: ((typ: top_none), (typ: top_none))),
- // A_BST
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_all), (typ: top_const; max: 7; min: 0))),
- // A_BLD
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_all), (typ: top_const; max: 7; min: 0))),
- // A_BREAK
- (numOperands: (1 shl 0); Operands: ((typ: top_none), (typ: top_none))),
- // A_NOP
- (numOperands: (1 shl 0); Operands: ((typ: top_none), (typ: top_none))),
- // A_SLEEP
- (numOperands: (1 shl 0); Operands: ((typ: top_none), (typ: top_none))),
- // A_WDR
- (numOperands: (1 shl 0); Operands: ((typ: top_none), (typ: top_none))),
- // A_XCH
- (numOperands: (1 shl 2); Operands: ((typ: top_reg; rt: rt_Z), (typ: top_reg; rt: rt_all))),
- // A_DES
- (numOperands: (1 shl 1); Operands: ((typ: top_const; max: 15; min: 0), (typ: top_none)))
- );
- {$POP}
- implementation
- uses
- aasmcpu, verbose, cgbase, itcpugas;
- function TAVRInstruction.ConcatInstruction(p:TAsmList) : tai;
- var
- i: integer;
- err, opregasref: boolean;
- s1, s2: string;
- reg: tregister;
- begin
- result:=inherited ConcatInstruction(p);
- if Result.typ = ait_instruction then
- begin
- // check if number of operands fall inside the allowed set
- if ((1 shl taicpu(Result).ops) and AVRInstrConstraint[opcode].numOperands = 0) then
- Message(asmr_e_invalid_opcode_and_operand)
- else
- begin
- for i := 0 to taicpu(Result).ops-1 do
- begin
- err := true;
- opregasref := false;
- // Type check
- if (taicpu(Result).oper[i]^.typ = AVRInstrConstraint[opcode].Operands[i].typ) then
- err := false
- // if label was specified check if const is expected
- else if (AVRInstrConstraint[opcode].Operands[i].typ = top_const) and (taicpu(Result).oper[i]^.typ = top_ref) then
- err := false
- // Also expressions such as X+, -Z and X+8 are classified as TOP_REF
- else if (AVRInstrConstraint[opcode].Operands[i].typ = top_reg) and (taicpu(Result).oper[i]^.typ = top_ref) and
- (int64(taicpu(Result).oper[i]^.ref^.base) and $01030000 > 0) then
- begin
- err := false;
- opregasref := true;
- reg := taicpu(Result).oper[i]^.ref^.base;
- end
- // LDD r1, a or STD a, r1 where a is local variable => type is set to top_local
- // also mov r10, TypeCastToByte(procParam) returns top_local for typecasted param
- // but no further information is available at this point to check
- else if (AVRInstrConstraint[opcode].Operands[i].typ = top_reg) and (taicpu(Result).oper[i]^.typ = top_local) and
- (opcode in [A_LDD, A_STD, A_MOV]) then
- begin
- err := false;
- end;
- if err then
- begin
- WriteStr(s1, taicpu(Result).oper[i]^.typ);
- WriteStr(s2, AVRInstrConstraint[opcode].Operands[i].typ);
- Message2(type_e_incompatible_types, s1, s2);
- end
- else if (taicpu(Result).oper[i]^.typ = top_reg) or opregasref then
- begin
- err := false;
- if not opregasref then
- reg := taicpu(Result).oper[i]^.reg;
- case AVRInstrConstraint[opcode].Operands[i].rt of
- rt_all: err := int64(reg) > $1030000; // excluding pointer registers X, Y, Z
- rt_even: err := int64(reg) mod 2 = 1;
- rt_even_24_30: err := not(word(reg) in [24, 26, 28, 30]);
- rt_16_31: err := not(word(reg) in [16..31]);
- rt_16_23: err := not(word(reg) in [16..23]);
- rt_Z: err := (int64(reg) <> $0103001e);
- rt_YZ: err := not((int64(reg) = $0103001c) or
- (int64(reg) =$0103001e));
- rt_XYZ: err := not((int64(reg) = $0103001a) or
- (int64(reg) = $0103001c) or
- (int64(reg) = $0103001e));
- end;
- // Catch erroneous z if it should be z+k or something similar
- // By checking if .am is not empty (implying a reference is expected)
- // ldd R20, z should not pass => opregasref = true
- // but ldd R20, z+1 should pass => opregasref = false
- if not (err) and (AVRInstrConstraint[opcode].Operands[i].am = [AM_UNCHANGED]) then
- err := not opregasref;
- if not (err) and not(AM_UNCHANGED in AVRInstrConstraint[opcode].Operands[i].am) and
- ((AM_POSTINCREMENT in AVRInstrConstraint[opcode].Operands[i].am) or
- (AM_PREDECREMENT in AVRInstrConstraint[opcode].Operands[i].am)) then
- err := not opregasref;
- if not(err) and opregasref then
- begin
- if (taicpu(Result).oper[i]^.ref^.addressmode = AM_UNCHANGED) and
- (AM_UNCHANGED in AVRInstrConstraint[opcode].Operands[i].am) then
- err := ((taicpu(Result).oper[i]^.ref^.offset > AVRInstrConstraint[opcode].Operands[i].maxconst) or
- (taicpu(Result).oper[i]^.ref^.offset < AVRInstrConstraint[opcode].Operands[i].minconst))
- else if (taicpu(Result).oper[i]^.ref^.addressmode in AVRInstrConstraint[opcode].Operands[i].am) then
- err := false
- else
- err := true;
- end;
- if err then
- Message1(asmr_e_invalid_ref_register, gas_regname(reg));
- end
- else if taicpu(Result).oper[i]^.typ = top_const then
- begin
- // Need to check if original value was signed or simply sign overflowed in 16 bit?
- if ((taicpu(Result).oper[i]^.val > AVRInstrConstraint[opcode].Operands[i].max) or
- (taicpu(Result).oper[i]^.val < AVRInstrConstraint[opcode].Operands[i].min)) then
- Message(asmr_e_constant_out_of_bounds);
- end;
- end;
- end;
- end;
- end;
- end.
|