mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-07 11:08:02 +02:00

- Change spelling of AM_PREDRECEMENT to AM_PREDECREMENT - Fix multiline comment style to use {} - Fix indenting of code in method tcgavr.g_concatcopy git-svn-id: trunk@49474 -
384 lines
20 KiB
ObjectPascal
384 lines
20 KiB
ObjectPascal
{
|
|
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.
|