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

ADD ADC and AND are also sign extended nasm output OK (program still crashes at end and creates wrong assembler files !!) procsym types sym in tdef removed !!
3488 lines
137 KiB
ObjectPascal
3488 lines
137 KiB
ObjectPascal
{
|
|
$Id$
|
|
Copyright (c) 1997-98 by Carl Eric Codere
|
|
|
|
Does the parsing process for the intel 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 Rai386;
|
|
|
|
{**********************************************************************}
|
|
{ WARNING }
|
|
{**********************************************************************}
|
|
{ Any modification in the order or removal of terms in the tables }
|
|
{ in i386.pas and intasmi3.pas will BREAK the code in this unit, }
|
|
{ unless the appropriate changes are made to this unit. Addition }
|
|
{ of terms though, will not change the code herein. }
|
|
{**********************************************************************}
|
|
|
|
{--------------------------------------------------------------------}
|
|
{ LEFT TO DO: }
|
|
{--------------------------------------------------------------------}
|
|
{ o Add support for floating point opcodes. }
|
|
{ o Handle module overrides also... such as crt.white or }
|
|
{ crt.delay and local typed constants. }
|
|
{ o Handle label references }
|
|
{ o Add support for TP styled segment overrides, when the opcode }
|
|
{ table will be completed. }
|
|
{ o Add imul,shld and shrd support with references and CL }
|
|
{ i386.pas requires to be updated to do this. }
|
|
{ o Support for (* *) tp styled comments, this support should be }
|
|
{ added in asmgetchar in scanner.pas (it cannot be implemented }
|
|
{ here without causing errors such as in : }
|
|
{ (* "openbrace" AComment *) }
|
|
{ (presently an infinite loop will be created if a (* styled }
|
|
{ comment is found). }
|
|
{ o Bugfix of ao_imm8s for IMUL. (Currently the 3 operand imul will }
|
|
{ be considered as invalid because I use ao_imm8 and the table }
|
|
{ uses ao_imm8s). }
|
|
{--------------------------------------------------------------------}
|
|
|
|
Interface
|
|
|
|
uses
|
|
tree,i386;
|
|
|
|
function assemble: ptree;
|
|
|
|
const
|
|
{ this variable is TRUE if the lookup tables have already been setup }
|
|
{ for fast access. On the first call to assemble the tables are setup }
|
|
{ and stay set up. }
|
|
_asmsorted: boolean = FALSE;
|
|
firstreg = R_EAX;
|
|
lastreg = R_ST7;
|
|
|
|
type
|
|
tiasmops = array[firstop..lastop] of string[7];
|
|
piasmops = ^tiasmops;
|
|
|
|
var
|
|
{ sorted tables of opcodes }
|
|
iasmops: piasmops;
|
|
{ uppercased tables of registers }
|
|
iasmregs: array[firstreg..lastreg] of string[6];
|
|
|
|
|
|
Implementation
|
|
|
|
Uses
|
|
aasm,globals,AsmUtils,strings,hcodegen,scanner,
|
|
cobjects,verbose,types;
|
|
|
|
|
|
type
|
|
tinteltoken = (
|
|
AS_NONE,AS_LABEL,AS_LLABEL,AS_STRING,AS_HEXNUM,AS_OCTALNUM,
|
|
AS_BINNUM,AS_COMMA,AS_LBRACKET,AS_RBRACKET,AS_LPAREN,
|
|
AS_RPAREN,AS_COLON,AS_DOT,AS_PLUS,AS_MINUS,AS_STAR,AS_INTNUM,
|
|
AS_SEPARATOR,AS_ID,AS_REGISTER,AS_OPCODE,AS_SLASH,
|
|
{------------------ Assembler directives --------------------}
|
|
AS_DB,AS_DW,AS_DD,AS_END,
|
|
{------------------ Assembler Operators --------------------}
|
|
AS_BYTE,AS_WORD,AS_DWORD,AS_QWORD,AS_TBYTE,AS_NEAR,AS_FAR,
|
|
AS_HIGH,AS_LOW,AS_OFFSET,AS_SEG,AS_TYPE,AS_PTR,AS_MOD,AS_SHL,AS_SHR,AS_NOT,
|
|
AS_AND,AS_OR,AS_XOR);
|
|
|
|
tasmkeyword = string[6];
|
|
const
|
|
{ These tokens should be modified accordingly to the modifications }
|
|
{ in the different enumerations. }
|
|
firstdirective = AS_DB;
|
|
lastdirective = AS_END;
|
|
firstoperator = AS_BYTE;
|
|
lastoperator = AS_XOR;
|
|
firstsreg = R_CS;
|
|
lastsreg = R_SS;
|
|
{ this is a hack to accept all opcodes }
|
|
{ in the opcode table. }
|
|
{ check is done until A_POPFD }
|
|
{ otherwise no check. }
|
|
lastop_in_table = A_POPFD;
|
|
|
|
_count_asmdirectives = longint(lastdirective)-longint(firstdirective);
|
|
_count_asmoperators = longint(lastoperator)-longint(firstoperator);
|
|
_count_asmprefixes = 5;
|
|
_count_asmspecialops = 25;
|
|
_count_asmoverrides = 3;
|
|
|
|
_asmdirectives : array[0.._count_asmdirectives] of tasmkeyword =
|
|
('DB','DW','DD','END');
|
|
|
|
{ problems with shl,shr,not,and,or and xor, they are }
|
|
{ context sensitive. }
|
|
_asmoperators : array[0.._count_asmoperators] of tasmkeyword = (
|
|
'BYTE','WORD','DWORD','QWORD','TBYTE','NEAR','FAR','HIGH',
|
|
'LOW','OFFSET','SEG','TYPE','PTR','MOD','SHL','SHR','NOT','AND',
|
|
'OR','XOR');
|
|
|
|
{------------------ Missing opcodes from std list ----------------}
|
|
_asmprefixes: array[0.._count_asmprefixes] of tasmkeyword = (
|
|
'REPNE','REPE','REP','REPZ','REPNZ','LOCK');
|
|
|
|
_asmoverrides: array[0.._count_asmoverrides] of tasmkeyword =
|
|
('SEGCS','SEGDS','SEGES','SEGSS');
|
|
|
|
_overridetokens: array[0.._count_asmoverrides] of tregister =
|
|
(R_CS,R_DS,R_ES,R_SS);
|
|
|
|
_prefixtokens: array[0.._count_asmprefixes] of tasmop = (
|
|
A_REPNE,A_REPE,A_REP,A_REPE,A_REPNE,A_LOCK);
|
|
|
|
_specialops: array[0.._count_asmspecialops] of tasmkeyword = (
|
|
'CMPSB','CMPSW','CMPSD','INSB','INSW','INSD','OUTSB','OUTSW','OUTSD',
|
|
'SCASB','SCASW','SCASD','STOSB','STOSW','STOSD','MOVSB','MOVSW','MOVSD',
|
|
'LODSB','LODSW','LODSD','LOCK','SEGCS','SEGDS','SEGES','SEGSS');
|
|
|
|
_specialopstokens: array[0.._count_asmspecialops] of tasmop = (
|
|
A_CMPS,A_CMPS,A_CMPS,A_INS,A_INS,A_INS,A_OUTS,A_OUTS,A_OUTS,
|
|
A_SCAS,A_SCAS,A_SCAS,A_STOS,A_STOS,A_STOS,A_MOVS,A_MOVS,A_MOVS,
|
|
A_LODS,A_LODS,A_LODS,A_LOCK,A_NONE,A_NONE,A_NONE,A_NONE);
|
|
{------------------------------------------------------------------}
|
|
{ register type definition table for easier searching }
|
|
_regtypes:array[firstreg..lastreg] of longint =
|
|
(ao_reg32,ao_reg32,ao_reg32,ao_reg32,ao_reg32,ao_reg32,ao_reg32,ao_reg32,
|
|
ao_reg16,ao_reg16,ao_reg16,ao_reg16,ao_reg16,ao_reg16,ao_reg16,ao_reg16,
|
|
ao_reg8,ao_reg8,ao_reg8,ao_reg8,ao_reg8,ao_reg8,ao_reg8,ao_reg8,
|
|
ao_none,ao_sreg2,ao_sreg2,ao_sreg2,ao_sreg3,ao_sreg3,ao_sreg2,
|
|
ao_floatacc,ao_floatacc,ao_floatreg,ao_floatreg,ao_floatreg,ao_floatreg,
|
|
ao_floatreg,ao_floatreg,ao_floatreg);
|
|
|
|
_regsizes: array[firstreg..lastreg] of topsize =
|
|
(S_L,S_L,S_L,S_L,S_L,S_L,S_L,S_L,
|
|
S_W,S_W,S_W,S_W,S_W,S_W,S_W,S_W,
|
|
S_B,S_B,S_B,S_B,S_B,S_B,S_B,S_B,
|
|
{ segment register }
|
|
S_W,S_W,S_W,S_W,S_W,S_W,S_W,
|
|
{ can also be S_S or S_T - must be checked at run-time }
|
|
S_Q,S_Q,S_Q,S_Q,S_Q,S_Q,S_Q,S_Q,S_Q);
|
|
|
|
_constsizes: array[S_NO..S_S] of longint =
|
|
(0,ao_imm8,ao_imm16,ao_imm32,0,0,0,0,ao_imm32);
|
|
|
|
|
|
|
|
|
|
const
|
|
newline = #10;
|
|
firsttoken : boolean = TRUE;
|
|
operandnum : byte = 0;
|
|
var
|
|
{ context for SHL,SHR,AND,NOT,OR,XOR operators }
|
|
{ if set to true GetToken will return these }
|
|
{ as operators, otherwise will return these as }
|
|
{ opcodes. }
|
|
inexpression: boolean;
|
|
p : paasmoutput;
|
|
actasmtoken: tinteltoken;
|
|
actasmpattern: string;
|
|
c: char;
|
|
Instr: TInstruction;
|
|
labellist: TAsmLabelList;
|
|
old_exit : pointer;
|
|
|
|
|
|
Procedure SetupTables;
|
|
{ creates uppercased symbol tables for speed access }
|
|
var
|
|
i: tasmop;
|
|
j: tregister;
|
|
Begin
|
|
Message(assem_d_creating_lookup_tables);
|
|
{ opcodes }
|
|
new(iasmops);
|
|
for i:=firstop to lastop do
|
|
iasmops^[i] := upper(int_op2str[i]);
|
|
{ opcodes }
|
|
for j:=firstreg to lastreg do
|
|
iasmregs[j] := upper(int_reg2str[j]);
|
|
end;
|
|
|
|
|
|
procedure rai386_exit;{$ifndef FPC}far;{$endif}
|
|
|
|
begin
|
|
if assigned(iasmops) then
|
|
dispose(iasmops);
|
|
exitproc:=old_exit;
|
|
end;
|
|
|
|
|
|
{---------------------------------------------------------------------}
|
|
{ Routines for the tokenizing }
|
|
{---------------------------------------------------------------------}
|
|
|
|
|
|
function is_asmopcode(const s: string):Boolean;
|
|
{*********************************************************************}
|
|
{ FUNCTION is_asmopcode(s: string):Boolean }
|
|
{ Description: Determines if the s string is a valid opcode }
|
|
{ if so returns TRUE otherwise returns FALSE. }
|
|
{*********************************************************************}
|
|
var
|
|
i: tasmop;
|
|
j: byte;
|
|
Begin
|
|
is_asmopcode := FALSE;
|
|
for i:=firstop to lastop do
|
|
begin
|
|
if s = iasmops^[i] then
|
|
begin
|
|
is_asmopcode:=TRUE;
|
|
exit;
|
|
end;
|
|
end;
|
|
{ not found yet, search for extended opcodes }
|
|
for j:=0 to _count_asmspecialops do
|
|
Begin
|
|
if s = _specialops[j] then
|
|
Begin
|
|
is_asmopcode:=TRUE;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
Procedure is_asmdirective(const s: string; var token: tinteltoken);
|
|
{*********************************************************************}
|
|
{ FUNCTION is_asmdirective(s: string; var token: tinteltoken):Boolean }
|
|
{ Description: Determines if the s string is a valid directive }
|
|
{ (an operator can occur in operand fields, while a directive cannot) }
|
|
{ if so returns the directive token, otherwise does not change token.}
|
|
{*********************************************************************}
|
|
var
|
|
i:byte;
|
|
Begin
|
|
for i:=0 to _count_asmdirectives do
|
|
begin
|
|
if s=_asmdirectives[i] then
|
|
begin
|
|
token := tinteltoken(longint(firstdirective)+i);
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
Procedure is_asmoperator(const s: string; var token: tinteltoken);
|
|
{*********************************************************************}
|
|
{ FUNCTION is_asmoperator(s: string; var token: tinteltoken): Boolean}
|
|
{ Description: Determines if the s string is a valid operator }
|
|
{ (an operator can occur in operand fields, while a directive cannot) }
|
|
{ if so returns the operator token, otherwise does not change token. }
|
|
{*********************************************************************}
|
|
var
|
|
i:longint;
|
|
Begin
|
|
for i:=0 to _count_asmoperators do
|
|
begin
|
|
if s=_asmoperators[i] then
|
|
begin
|
|
token := tinteltoken(longint(firstoperator)+i);
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
Procedure is_register(const s: string; var token: tinteltoken);
|
|
{*********************************************************************}
|
|
{ PROCEDURE is_register(s: string; var token: tinteltoken); }
|
|
{ Description: Determines if the s string is a valid register, if }
|
|
{ so return token equal to A_REGISTER, otherwise does not change token}
|
|
{*********************************************************************}
|
|
Var
|
|
i: tregister;
|
|
Begin
|
|
for i:=firstreg to lastreg do
|
|
begin
|
|
if s=iasmregs[i] then
|
|
begin
|
|
token := AS_REGISTER;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
|
|
Function GetToken: tinteltoken;
|
|
{*********************************************************************}
|
|
{ FUNCTION GetToken: tinteltoken; }
|
|
{ Description: This routine returns intel assembler tokens and }
|
|
{ does some minor syntax error checking. }
|
|
{*********************************************************************}
|
|
var
|
|
j: integer;
|
|
token: tinteltoken;
|
|
forcelabel: boolean;
|
|
errorflag : boolean;
|
|
begin
|
|
errorflag := FALSE;
|
|
forcelabel := FALSE;
|
|
actasmpattern :='';
|
|
{* INIT TOKEN TO NOTHING *}
|
|
token := AS_NONE;
|
|
{ while space and tab , continue scan... }
|
|
while (c in [' ',#9]) do
|
|
c := asmgetchar;
|
|
{ Possiblities for first token in a statement: }
|
|
{ Local Label, Label, Directive, Prefix or Opcode.... }
|
|
if firsttoken and not (c in [newline,#13,'{',';']) then
|
|
begin
|
|
firsttoken := FALSE;
|
|
if c = '@' then
|
|
begin
|
|
token := AS_LLABEL; { this is a local label }
|
|
{ Let us point to the next character }
|
|
c := asmgetchar;
|
|
end;
|
|
|
|
|
|
|
|
while c in ['A'..'Z','a'..'z','0'..'9','_','@'] do
|
|
begin
|
|
{ if there is an at_sign, then this must absolutely be a label }
|
|
if c = '@' then forcelabel:=TRUE;
|
|
actasmpattern := actasmpattern + c;
|
|
c := asmgetchar;
|
|
end;
|
|
|
|
uppervar(actasmpattern);
|
|
|
|
if c = ':' then
|
|
begin
|
|
case token of
|
|
AS_NONE: token := AS_LABEL;
|
|
AS_LLABEL: ; { do nothing }
|
|
end; { end case }
|
|
{ let us point to the next character }
|
|
c := asmgetchar;
|
|
gettoken := token;
|
|
exit;
|
|
end;
|
|
|
|
{ Are we trying to create an identifier with }
|
|
{ an at-sign...? }
|
|
if forcelabel then
|
|
Message(assem_e_none_label_contain_at);
|
|
|
|
If is_asmopcode(actasmpattern) then
|
|
Begin
|
|
gettoken := AS_OPCODE;
|
|
{ check if we are in an expression }
|
|
{ then continue with asm directives }
|
|
if not inexpression then
|
|
exit;
|
|
end;
|
|
is_asmdirective(actasmpattern, token);
|
|
if (token <> AS_NONE) then
|
|
Begin
|
|
gettoken := token;
|
|
exit
|
|
end
|
|
else
|
|
begin
|
|
gettoken := AS_NONE;
|
|
Message1(assem_e_invalid_operand,actasmpattern);
|
|
end;
|
|
end
|
|
else { else firsttoken }
|
|
{ Here we must handle all possible cases }
|
|
begin
|
|
case c of
|
|
|
|
'@': { possiblities : - local label reference , such as in jmp @local1 }
|
|
{ - @Result, @Code or @Data special variables. }
|
|
begin
|
|
actasmpattern := c;
|
|
c:= asmgetchar;
|
|
while c in ['A'..'Z','a'..'z','0'..'9','_','@'] do
|
|
begin
|
|
actasmpattern := actasmpattern + c;
|
|
c := asmgetchar;
|
|
end;
|
|
uppervar(actasmpattern);
|
|
gettoken := AS_ID;
|
|
exit;
|
|
end;
|
|
{ identifier, register, opcode, prefix or directive }
|
|
'A'..'Z','a'..'z','_': begin
|
|
actasmpattern := c;
|
|
c:= asmgetchar;
|
|
while c in ['A'..'Z','a'..'z','0'..'9','_'] do
|
|
begin
|
|
actasmpattern := actasmpattern + c;
|
|
c := asmgetchar;
|
|
end;
|
|
uppervar(actasmpattern);
|
|
|
|
If is_asmopcode(actasmpattern) then
|
|
Begin
|
|
gettoken := AS_OPCODE;
|
|
{ if we are not in a constant }
|
|
{ expression than this is an }
|
|
{ opcode. }
|
|
if not inexpression then
|
|
exit;
|
|
end;
|
|
is_register(actasmpattern, token);
|
|
is_asmoperator(actasmpattern,token);
|
|
is_asmdirective(actasmpattern,token);
|
|
{ if found }
|
|
if (token <> AS_NONE) then
|
|
begin
|
|
gettoken := token;
|
|
exit;
|
|
end
|
|
{ this is surely an identifier }
|
|
else
|
|
token := AS_ID;
|
|
gettoken := token;
|
|
exit;
|
|
end;
|
|
{ override operator... not supported }
|
|
'&': begin
|
|
Message(assem_w_override_op_not_supported);
|
|
c:=asmgetchar;
|
|
gettoken := AS_NONE;
|
|
end;
|
|
{ string or character }
|
|
'''' :
|
|
begin
|
|
actasmpattern:='';
|
|
while true do
|
|
begin
|
|
if c = '''' then
|
|
begin
|
|
c:=asmgetchar;
|
|
if c=newline then
|
|
begin
|
|
Message(scan_f_string_exceeds_line);
|
|
break;
|
|
end;
|
|
repeat
|
|
if c=''''then
|
|
begin
|
|
c:=asmgetchar;
|
|
if c='''' then
|
|
begin
|
|
actasmpattern:=actasmpattern+'''';
|
|
c:=asmgetchar;
|
|
if c=newline then
|
|
begin
|
|
Message(scan_f_string_exceeds_line);
|
|
break;
|
|
end;
|
|
end
|
|
else break;
|
|
end
|
|
else
|
|
begin
|
|
actasmpattern:=actasmpattern+c;
|
|
c:=asmgetchar;
|
|
if c=newline then
|
|
begin
|
|
Message(scan_f_string_exceeds_line);
|
|
break
|
|
end;
|
|
end;
|
|
until false; { end repeat }
|
|
end
|
|
else break; { end if }
|
|
end; { end while }
|
|
token:=AS_STRING;
|
|
gettoken := token;
|
|
exit;
|
|
end;
|
|
{ string or character }
|
|
'"' :
|
|
begin
|
|
actasmpattern:='';
|
|
while true do
|
|
begin
|
|
if c = '"' then
|
|
begin
|
|
c:=asmgetchar;
|
|
if c=newline then
|
|
begin
|
|
Message(scan_f_string_exceeds_line);
|
|
break;
|
|
end;
|
|
repeat
|
|
if c='"'then
|
|
begin
|
|
c:=asmgetchar;
|
|
if c='"' then
|
|
begin
|
|
actasmpattern:=actasmpattern+'"';
|
|
c:=asmgetchar;
|
|
if c=newline then
|
|
begin
|
|
Message(scan_f_string_exceeds_line);
|
|
break;
|
|
end;
|
|
end
|
|
else break;
|
|
|
|
end
|
|
else
|
|
begin
|
|
actasmpattern:=actasmpattern+c;
|
|
c:=asmgetchar;
|
|
if c=newline then
|
|
begin
|
|
Message(scan_f_string_exceeds_line);
|
|
break
|
|
end;
|
|
end;
|
|
until false; { end repeat }
|
|
end
|
|
else break; { end if }
|
|
end; { end while }
|
|
token := AS_STRING;
|
|
gettoken := token;
|
|
exit;
|
|
end;
|
|
'$' : begin
|
|
c:=asmgetchar;
|
|
while c in ['0'..'9','A'..'F','a'..'f'] do
|
|
begin
|
|
actasmpattern := actasmpattern + c;
|
|
c := asmgetchar;
|
|
end;
|
|
gettoken := AS_HEXNUM;
|
|
exit;
|
|
end;
|
|
',' : begin
|
|
gettoken := AS_COMMA;
|
|
c:=asmgetchar;
|
|
exit;
|
|
end;
|
|
'[' : begin
|
|
gettoken := AS_LBRACKET;
|
|
c:=asmgetchar;
|
|
exit;
|
|
end;
|
|
']' : begin
|
|
gettoken := AS_RBRACKET;
|
|
c:=asmgetchar;
|
|
exit;
|
|
end;
|
|
'(' : begin
|
|
gettoken := AS_LPAREN;
|
|
c:=asmgetchar;
|
|
exit;
|
|
end;
|
|
')' : begin
|
|
gettoken := AS_RPAREN;
|
|
c:=asmgetchar;
|
|
exit;
|
|
end;
|
|
':' : begin
|
|
gettoken := AS_COLON;
|
|
c:=asmgetchar;
|
|
exit;
|
|
end;
|
|
'.' : begin
|
|
gettoken := AS_DOT;
|
|
c:=asmgetchar;
|
|
exit;
|
|
end;
|
|
'+' : begin
|
|
gettoken := AS_PLUS;
|
|
c:=asmgetchar;
|
|
exit;
|
|
end;
|
|
'-' : begin
|
|
gettoken := AS_MINUS;
|
|
c:=asmgetchar;
|
|
exit;
|
|
end;
|
|
'*' : begin
|
|
gettoken := AS_STAR;
|
|
c:=asmgetchar;
|
|
exit;
|
|
end;
|
|
'/' : begin
|
|
gettoken := AS_SLASH;
|
|
c:=asmgetchar;
|
|
exit;
|
|
end;
|
|
'0'..'9': begin
|
|
{ this flag indicates if there was an error }
|
|
{ if so, then we use a default value instead.}
|
|
errorflag := false;
|
|
actasmpattern := c;
|
|
c := asmgetchar;
|
|
{ Get the possible characters }
|
|
while c in ['0'..'9','A'..'F','a'..'f'] do
|
|
begin
|
|
actasmpattern := actasmpattern + c;
|
|
c:= asmgetchar;
|
|
end;
|
|
{ Get ending character }
|
|
uppervar(actasmpattern);
|
|
c:=upcase(c);
|
|
{ possibly a binary number. }
|
|
if (actasmpattern[length(actasmpattern)] = 'B') and (c <> 'H') then
|
|
Begin
|
|
{ Delete the last binary specifier }
|
|
delete(actasmpattern,length(actasmpattern),1);
|
|
for j:=1 to length(actasmpattern) do
|
|
if not (actasmpattern[j] in ['0','1']) then
|
|
begin
|
|
Message1(assem_e_error_in_binary_const,actasmpattern);
|
|
errorflag := TRUE;
|
|
end;
|
|
{ if error, then suppose a binary value of zero. }
|
|
if errorflag then
|
|
actasmpattern := '0';
|
|
gettoken := AS_BINNUM;
|
|
exit;
|
|
end
|
|
else
|
|
Begin
|
|
case c of
|
|
'O': Begin
|
|
for j:=1 to length(actasmpattern) do
|
|
if not (actasmpattern[j] in ['0'..'7']) then
|
|
begin
|
|
Message1(assem_e_error_in_octal_const,actasmpattern);
|
|
errorflag := TRUE;
|
|
end;
|
|
{ if error, then suppose an octal value of zero. }
|
|
if errorflag then
|
|
actasmpattern := '0';
|
|
gettoken := AS_OCTALNUM;
|
|
c := asmgetchar;
|
|
exit;
|
|
end;
|
|
'H': Begin
|
|
for j:=1 to length(actasmpattern) do
|
|
if not (actasmpattern[j] in ['0'..'9','A'..'F']) then
|
|
begin
|
|
Message1(assem_e_error_in_hex_const,actasmpattern);
|
|
errorflag := TRUE;
|
|
end;
|
|
{ if error, then suppose an hex value of zero. }
|
|
if errorflag then
|
|
actasmpattern := '0';
|
|
gettoken := AS_HEXNUM;
|
|
c := asmgetchar;
|
|
exit;
|
|
end;
|
|
else { must be an integer number }
|
|
begin
|
|
for j:=1 to length(actasmpattern) do
|
|
if not (actasmpattern[j] in ['0'..'9']) then
|
|
begin
|
|
Message1(assem_e_error_in_integer_const,actasmpattern);
|
|
errorflag := TRUE;
|
|
end;
|
|
{ if error, then suppose an int value of zero. }
|
|
if errorflag then
|
|
actasmpattern := '0';
|
|
gettoken := AS_INTNUM;
|
|
exit;
|
|
end;
|
|
end; { end case }
|
|
end; { end if }
|
|
end;
|
|
';','{',#13,newline : begin
|
|
c:=asmgetchar;
|
|
firsttoken := TRUE;
|
|
gettoken:=AS_SEPARATOR;
|
|
end;
|
|
else
|
|
Begin
|
|
Message(scan_f_illegal_char);
|
|
end;
|
|
|
|
end; { end case }
|
|
end; { end else if }
|
|
end;
|
|
|
|
{---------------------------------------------------------------------}
|
|
{ Routines for the output }
|
|
{---------------------------------------------------------------------}
|
|
|
|
{ returns an appropriate ao_xxxx flag indicating the type }
|
|
{ of operand. }
|
|
function findtype(Var Opr: TOperand): longint;
|
|
Begin
|
|
With Opr do
|
|
Begin
|
|
case operandtype of
|
|
OPR_REFERENCE: Begin
|
|
if assigned(ref.symbol) then
|
|
{ check if in local label list }
|
|
{ if so then it is considered }
|
|
{ as a displacement. }
|
|
Begin
|
|
if labellist.search(ref.symbol^) <> nil then
|
|
findtype := ao_disp
|
|
else
|
|
findtype := ao_mem; { probably a mem ref. }
|
|
end
|
|
else
|
|
findtype := ao_mem;
|
|
end;
|
|
OPR_CONSTANT: Begin
|
|
{ check if there is not already a default size }
|
|
if opr.size <> S_NO then
|
|
Begin
|
|
findtype := _constsizes[opr.size];
|
|
exit;
|
|
end;
|
|
if val < $ff then
|
|
Begin
|
|
findtype := ao_imm8;
|
|
opr.size := S_B;
|
|
end
|
|
else if val < $ffff then
|
|
Begin
|
|
findtype := ao_imm16;
|
|
opr.size := S_W;
|
|
end
|
|
else
|
|
Begin
|
|
findtype := ao_imm32;
|
|
opr.size := S_L;
|
|
end
|
|
end;
|
|
OPR_REGISTER: Begin
|
|
findtype := _regtypes[reg];
|
|
exit;
|
|
end;
|
|
OPR_NONE: Begin
|
|
findtype := 0;
|
|
end;
|
|
else
|
|
Begin
|
|
Message(assem_f_internal_error_in_findtype);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
Procedure ConcatLabeledInstr(var instr: TInstruction);
|
|
Begin
|
|
if (instr.getinstruction in [A_JO,A_JNO,A_JB,A_JC,A_JNAE,
|
|
A_JNB,A_JNC,A_JAE,A_JE,A_JZ,A_JNE,A_JNZ,A_JBE,A_JNA,A_JNBE,
|
|
A_JA,A_JS,A_JNS,A_JP,A_JPE,A_JNP,A_JPO,A_JL,A_JNGE,A_JNL,A_JGE,
|
|
A_JLE,A_JNG,A_JNLE,A_JG,A_JCXZ,A_JECXZ,A_LOOP,A_LOOPZ,A_LOOPE,
|
|
A_LOOPNZ,A_LOOPNE,A_MOV,A_JMP,A_CALL]) then
|
|
Begin
|
|
if instr.numops > 1 then
|
|
Message(assem_e_invalid_labeled_opcode)
|
|
else if instr.operands[1].operandtype <> OPR_LABINSTR then
|
|
Message(assem_e_invalid_labeled_opcode)
|
|
else if (instr.operands[1].operandtype = OPR_LABINSTR) and
|
|
(instr.numops = 1) then
|
|
if assigned(instr.operands[1].hl) then
|
|
ConcatLabel(p,instr.getinstruction, instr.operands[1].hl)
|
|
else
|
|
Message(assem_f_internal_error_in_findtype);
|
|
end
|
|
else if instr.getinstruction = A_MOV then
|
|
Begin
|
|
{ MOV to rel8 }
|
|
end
|
|
else
|
|
Message(assem_e_invalid_operand);
|
|
end;
|
|
|
|
|
|
|
|
|
|
Procedure HandleExtend(var instr: TInstruction);
|
|
{ Handles MOVZX, MOVSX ... }
|
|
var
|
|
instruc: tasmop;
|
|
opsize: topsize;
|
|
Begin
|
|
instruc:=instr.getinstruction;
|
|
{ return the old types ..}
|
|
{ these tokens still point to valid intel strings, }
|
|
{ but we must convert them to TRUE intel tokens }
|
|
if instruc in [A_MOVSB,A_MOVSBL,A_MOVSBW,A_MOVSWL] then
|
|
instruc := A_MOVSX;
|
|
if instruc in [A_MOVZB,A_MOVZWL] then
|
|
instruc := A_MOVZX;
|
|
|
|
With instr do
|
|
|
|
Begin
|
|
if operands[1].size = S_B then
|
|
Begin
|
|
if operands[2].size = S_L then
|
|
opsize := S_BL
|
|
else
|
|
if operands[2].size = S_W then
|
|
opsize := S_BW
|
|
else
|
|
begin
|
|
Message(assem_e_invalid_size_movzx);
|
|
exit;
|
|
end;
|
|
|
|
end
|
|
else
|
|
if operands[1].size = S_W then
|
|
Begin
|
|
if operands[2].size = S_L then
|
|
opsize := S_WL
|
|
else
|
|
begin
|
|
Message(assem_e_invalid_size_movzx);
|
|
exit;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
Message(assem_e_invalid_size_movzx);
|
|
exit;
|
|
end;
|
|
|
|
|
|
if operands[1].operandtype = OPR_REGISTER then
|
|
Begin
|
|
if operands[2].operandtype <> OPR_REGISTER then
|
|
Message(assem_e_invalid_opcode)
|
|
else
|
|
p^.concat(new(pai386,op_reg_reg(instruc,opsize,
|
|
operands[1].reg,operands[2].reg)));
|
|
end
|
|
else
|
|
if operands[1].operandtype = OPR_REFERENCE then
|
|
Begin
|
|
if operands[2].operandtype <> OPR_REGISTER then
|
|
Message(assem_e_invalid_opcode)
|
|
else
|
|
p^.concat(new(pai386,op_ref_reg(instruc,opsize,
|
|
newreference(operands[1].ref),operands[2].reg)));
|
|
end
|
|
end; { end with }
|
|
end;
|
|
|
|
|
|
Procedure ConcatOpCode(var instr: TInstruction);
|
|
{*********************************************************************}
|
|
{ First Pass: }
|
|
{ if instr = Lxxx with a 16bit offset, we emit an error. }
|
|
{ If the instruction is INS,IN,OUT,OUTS,RCL,ROL,RCR,ROR, }
|
|
{ SAL,SAR,SHL,SHR,SHLD,SHRD,DIV,IDIV,BT,BTC,BTR,BTS,INT, }
|
|
{ RET,ENTER,SCAS,CMPS,STOS,LODS,FNSTSW,FSTSW. }
|
|
{ set up the optypes variables manually, as well as setting }
|
|
{ operand sizes. }
|
|
{ Second pass: }
|
|
{ Check if the combination of opcodes and operands are valid, using }
|
|
{ the opcode table. }
|
|
{ Third pass: }
|
|
{ If there was no error on the 2nd pass , then we check the }
|
|
{ following: }
|
|
{ - If this is a 0 operand opcode }
|
|
{ we verify if it is a string opcode, if so we emit a size also}
|
|
{ otherwise simply emit the opcode by itself. }
|
|
{ - If this is a 1 operand opcode, and it is a reference, we make }
|
|
{ sure that the operand size is valid; we emit the opcode. }
|
|
{ - If this is a two operand opcode }
|
|
{ o if the opcode is MOVSX or MOVZX then we handle it specially }
|
|
{ o we check the operand types (most important combinations): }
|
|
{ if reg,reg we make sure that both registers are of the }
|
|
{ same size. }
|
|
{ if reg,ref or ref,reg we check if the symbol name is }
|
|
{ assigned, if so a size must be specified and compared }
|
|
{ to the register size, both must be equal. If there is }
|
|
{ no symbol name, then we check : }
|
|
{ if refsize = NO_SIZE then OPCODE_SIZE = regsize }
|
|
{ else if refsize = regsize then OPCODE_SIZE = regsize}
|
|
{ else error. }
|
|
{ if no_error emit the opcode. }
|
|
{ if ref,const or const,ref if ref does not have any size }
|
|
{ then error, otherwise emit the opcode. }
|
|
{ - If this is a three operand opcode: }
|
|
{ imul,shld,and shrd -> check them manually. }
|
|
{*********************************************************************}
|
|
var
|
|
fits : boolean;
|
|
i: longint;
|
|
opsize: topsize;
|
|
optyp1, optyp2, optyp3: longint;
|
|
instruc: tasmop;
|
|
Begin
|
|
fits := FALSE;
|
|
for i:=1 to instr.numops do
|
|
Begin
|
|
case instr.operands[i].operandtype of
|
|
OPR_REGISTER: instr.operands[i].size :=
|
|
_regsizes[instr.operands[i].reg];
|
|
end; { end case }
|
|
end; { endif }
|
|
{ setup specific instructions for first pass }
|
|
instruc := instr.getinstruction;
|
|
if (instruc in [A_LEA,A_LDS,A_LSS,A_LES,A_LFS,A_LGS]) then
|
|
Begin
|
|
if instr.operands[1].size <> S_L then
|
|
Begin
|
|
Message(assem_e_16bit_base_in_32bit_segment);
|
|
exit;
|
|
end; { endif }
|
|
end;
|
|
|
|
With instr do
|
|
Begin
|
|
|
|
|
|
for i:=1 to numops do
|
|
Begin
|
|
With operands[i] do
|
|
Begin
|
|
{ check for 16-bit bases/indexes and emit an error. }
|
|
{ we cannot only emit a warning since gas does not }
|
|
{ accept 16-bit indexes and bases. }
|
|
if (operandtype = OPR_REFERENCE) and
|
|
((ref.base <> R_NO) or
|
|
(ref.index <> R_NO)) then
|
|
Begin
|
|
{ index or base defined. }
|
|
if (ref.base <> R_NO) then
|
|
Begin
|
|
if not (ref.base in
|
|
[R_EAX,R_EBX,R_ECX,R_EDX,R_EBP,R_ESI,R_EDI,R_ESP]) then
|
|
Message(assem_e_16bit_base_in_32bit_segment);
|
|
end;
|
|
{ index or base defined. }
|
|
if (ref.index <> R_NO) then
|
|
Begin
|
|
if not (ref.index in
|
|
[R_EAX,R_EBX,R_ECX,R_EDX,R_EBP,R_ESI,R_EDI,R_ESP]) then
|
|
Message(assem_e_16bit_index_in_32bit_segment);
|
|
end;
|
|
end;
|
|
{ Check for constants without bases/indexes in memory }
|
|
{ references. }
|
|
if (operandtype = OPR_REFERENCE) and
|
|
(ref.base = R_NO) and
|
|
(ref.index = R_NO) and
|
|
(ref.symbol = nil) and
|
|
(ref.offset <> 0) then
|
|
Begin
|
|
ref.isintvalue := TRUE;
|
|
Message(assem_e_const_ref_not_allowed);
|
|
end;
|
|
|
|
opinfo := findtype(operands[i]);
|
|
|
|
end; { end with }
|
|
end; {endfor}
|
|
|
|
|
|
|
|
|
|
{ TAKE CARE OF SPECIAL OPCODES, TAKE CARE OF THEM INDIVUALLY. }
|
|
{ ALL THE REST ARE TAKEN CARE BY OPCODE TABLE AND THIRD PASS. }
|
|
if instruc = A_FST then
|
|
Begin
|
|
end
|
|
else
|
|
if instruc = A_FILD then
|
|
Begin
|
|
end
|
|
else
|
|
if instruc = A_FLD then
|
|
Begin
|
|
{A_FLDS,A_FLDL,A_FLDT}
|
|
end
|
|
else
|
|
if instruc = A_FIST then
|
|
Begin
|
|
{A_FISTQ,A_FISTS,A_FISTL}
|
|
end
|
|
else
|
|
if instruc = A_FWAIT then
|
|
FWaitWarning
|
|
else
|
|
if instruc = A_MOVSX then
|
|
Begin
|
|
{ change the instruction to conform to GAS }
|
|
if operands[1].size = S_W then
|
|
Begin
|
|
addinstr(A_MOVSBW)
|
|
end
|
|
else
|
|
if operands[1].size = S_L then
|
|
Begin
|
|
if operands[2].size = S_B then
|
|
addinstr(A_MOVSBL)
|
|
else
|
|
addinstr(A_MOVSWL);
|
|
end;
|
|
instruc := getinstruction; { reload instruction }
|
|
end
|
|
else
|
|
if instruc = A_MOVZX then
|
|
Begin
|
|
{ change the instruction to conform to GAS }
|
|
if operands[1].size = S_W then
|
|
Begin
|
|
addinstr(A_MOVZB)
|
|
end
|
|
else
|
|
if operands[1].size = S_L then
|
|
Begin
|
|
if operands[2].size = S_B then
|
|
addinstr(A_MOVZB)
|
|
else
|
|
addinstr(A_MOVZWL);
|
|
end;
|
|
instruc := getinstruction; { reload instruction }
|
|
end
|
|
else
|
|
if (instruc in [A_BT,A_BTC,A_BTR,A_BTS]) then
|
|
Begin
|
|
if numops = 2 then
|
|
Begin
|
|
if (operands[2].operandtype = OPR_CONSTANT)
|
|
and (operands[2].val <= $ff) then
|
|
Begin
|
|
operands[2].opinfo := ao_imm8;
|
|
{ no operand size if using constant. }
|
|
operands[2].size := S_NO;
|
|
fits := TRUE;
|
|
end
|
|
end
|
|
else
|
|
Begin
|
|
Message(assem_e_invalid_opcode_and_operand);
|
|
exit;
|
|
end;
|
|
end
|
|
else
|
|
if instruc = A_ENTER then
|
|
Begin
|
|
if numops =2 then
|
|
Begin
|
|
if (operands[1].operandtype = OPR_CONSTANT) and
|
|
(operands[1].val <= $ffff) then
|
|
Begin
|
|
operands[1].opinfo := ao_imm16;
|
|
end { endif }
|
|
end { endif }
|
|
else
|
|
Begin
|
|
Message(assem_e_invalid_opcode_and_operand);
|
|
exit;
|
|
end
|
|
end { endif }
|
|
else
|
|
{ Handle special opcodes for the opcode }
|
|
{ table. Set them up correctly. }
|
|
if (instruc in [A_IN,A_INS]) then
|
|
Begin
|
|
if numops =2 then
|
|
Begin
|
|
if (operands[2].operandtype = OPR_REGISTER) and (operands[2].reg = R_DX)
|
|
then
|
|
Begin
|
|
operands[2].opinfo := ao_inoutportreg;
|
|
if (operands[1].operandtype = OPR_REGISTER) and
|
|
(operands[1].reg in [R_EAX,R_AX,R_AL]) and
|
|
(instruc = A_IN) then
|
|
Begin
|
|
operands[1].opinfo := ao_acc;
|
|
end
|
|
end
|
|
else
|
|
if (operands[2].operandtype = OPR_CONSTANT) and (operands[2].val <= $ff)
|
|
and (instruc = A_IN) then
|
|
Begin
|
|
operands[2].opinfo := ao_imm8;
|
|
operands[2].size := S_B;
|
|
if (operands[1].operandtype = OPR_REGISTER) and
|
|
(operands[1].reg in [R_EAX,R_AX,R_AL]) and
|
|
(instruc = A_IN) then
|
|
Begin
|
|
operands[1].opinfo := ao_acc;
|
|
end
|
|
end;
|
|
end
|
|
else
|
|
if not ((numops=0) and (instruc=A_INS)) then
|
|
Begin
|
|
Message(assem_e_invalid_opcode_and_operand);
|
|
exit;
|
|
end;
|
|
end
|
|
else
|
|
if (instruc in [A_OUT,A_OUTS]) then
|
|
Begin
|
|
if numops =2 then
|
|
Begin
|
|
if (operands[1].operandtype = OPR_REGISTER) and (operands[1].reg = R_DX)
|
|
then
|
|
Begin
|
|
operands[1].opinfo := ao_inoutportreg;
|
|
if (operands[2].operandtype = OPR_REGISTER) and
|
|
(operands[2].reg in [R_EAX,R_AX,R_AL]) and
|
|
(instruc = A_OUT) then
|
|
Begin
|
|
operands[2].opinfo := ao_acc;
|
|
fits := TRUE;
|
|
end
|
|
end
|
|
else
|
|
if (operands[1].operandtype = OPR_CONSTANT) and (operands[1].val <= $ff)
|
|
and (instruc = A_OUT) then
|
|
Begin
|
|
operands[1].opinfo := ao_imm8;
|
|
operands[1].size := S_B;
|
|
if (operands[2].operandtype = OPR_REGISTER) and
|
|
(operands[2].reg in [R_EAX,R_AX,R_AL]) and
|
|
(instruc = A_OUT) then
|
|
Begin
|
|
operands[2].opinfo := ao_acc;
|
|
fits := TRUE;
|
|
end
|
|
end;
|
|
end
|
|
else
|
|
if not ((numops=0) and (instruc=A_OUTS)) then
|
|
Begin
|
|
Message(assem_e_invalid_opcode_and_operand);
|
|
exit;
|
|
end;
|
|
end
|
|
else
|
|
if instruc in [A_RCL,A_RCR,A_ROL,A_ROR,A_SAL,A_SAR,A_SHL,A_SHR] then
|
|
{ if RCL,ROL,... }
|
|
Begin
|
|
if numops =2 then
|
|
Begin
|
|
if (operands[2].operandtype = OPR_REGISTER) and (operands[2].reg = R_CL)
|
|
then
|
|
Begin
|
|
operands[2].opinfo := ao_shiftcount
|
|
end
|
|
else
|
|
if (operands[2].operandtype = OPR_CONSTANT) and
|
|
(operands[2].val <= $ff) then
|
|
Begin
|
|
operands[2].opinfo := ao_imm8;
|
|
operands[2].size := S_B;
|
|
end;
|
|
end
|
|
else { if numops = 2 }
|
|
Begin
|
|
Message(assem_e_invalid_opcode_and_operand);
|
|
exit;
|
|
end;
|
|
end
|
|
{ endif ROL,RCL ... }
|
|
else
|
|
if instruc in [A_DIV, A_IDIV] then
|
|
Begin
|
|
if (operands[1].operandtype = OPR_REGISTER) and
|
|
(operands[1].reg in [R_AL,R_AX,R_EAX]) then
|
|
operands[1].opinfo := ao_acc;
|
|
end
|
|
else
|
|
if (instruc = A_FNSTSW) or (instruc = A_FSTSW) then
|
|
Begin
|
|
if numops = 1 then
|
|
Begin
|
|
if (operands[1].operandtype = OPR_REGISTER) and
|
|
(operands[1].reg = R_AX) then
|
|
operands[1].opinfo := ao_acc;
|
|
end
|
|
else
|
|
Begin
|
|
Message(assem_e_invalid_opcode_and_operand);
|
|
exit;
|
|
end;
|
|
end
|
|
else
|
|
if (instruc = A_SHLD) or (instruc = A_SHRD) then
|
|
{ these instruction are fully parsed individually on pass three }
|
|
{ so we just do a summary checking here. }
|
|
Begin
|
|
if numops = 3 then
|
|
Begin
|
|
if (operands[3].operandtype = OPR_CONSTANT)
|
|
and (operands[3].val <= $ff) then
|
|
Begin
|
|
operands[3].opinfo := ao_imm8;
|
|
operands[3].size := S_B;
|
|
end;
|
|
end
|
|
else
|
|
Begin
|
|
Message(assem_e_invalid_opcode_and_operand);
|
|
exit;
|
|
end;
|
|
end
|
|
else
|
|
if instruc = A_INT then
|
|
Begin
|
|
if numops = 1 then
|
|
Begin
|
|
if (operands[1].operandtype = OPR_CONSTANT) and
|
|
(operands[1].val <= $ff) then
|
|
operands[1].opinfo := ao_imm8;
|
|
end
|
|
end
|
|
else
|
|
if instruc = A_RET then
|
|
Begin
|
|
if numops =1 then
|
|
Begin
|
|
if (operands[1].operandtype = OPR_CONSTANT) and
|
|
(operands[1].val <= $ffff) then
|
|
operands[1].opinfo := ao_imm16;
|
|
end
|
|
end; { endif }
|
|
|
|
{ all string instructions have default memory }
|
|
{ location which are ignored. Take care of }
|
|
{ those. }
|
|
{ Here could be added the code for segment }
|
|
{ overrides. }
|
|
if instruc in [A_SCAS,A_CMPS,A_STOS,A_LODS] then
|
|
Begin
|
|
if numops =1 then
|
|
Begin
|
|
if (operands[1].operandtype = OPR_REFERENCE) and
|
|
(assigned(operands[1].ref.symbol)) then
|
|
Freemem(operands[1].ref.symbol,length(operands[1].ref.symbol^)+1);
|
|
operands[1].operandtype := OPR_NONE;
|
|
numops := 0;
|
|
end;
|
|
end; { endif }
|
|
if instruc in [A_INS,A_MOVS,A_OUTS] then
|
|
Begin
|
|
if numops =2 then
|
|
Begin
|
|
if (operands[1].operandtype = OPR_REFERENCE) and
|
|
(assigned(operands[1].ref.symbol)) then
|
|
Freemem(operands[1].ref.symbol,length(operands[1].ref.symbol^)+1);
|
|
if (operands[2].operandtype = OPR_REFERENCE) and
|
|
(assigned(operands[2].ref.symbol)) then
|
|
Freemem(operands[2].ref.symbol,length(operands[1].ref.symbol^)+1);
|
|
operands[1].operandtype := OPR_NONE;
|
|
operands[2].operandtype := OPR_NONE;
|
|
numops := 0;
|
|
end;
|
|
end;
|
|
{ handle parameter for segment overrides }
|
|
if instruc = A_XLAT then
|
|
Begin
|
|
{ handle special TP syntax case for XLAT }
|
|
{ here we accept XLAT, XLATB and XLAT m8 }
|
|
if (numops = 1) or (numops = 0) then
|
|
Begin
|
|
if (operands[1].operandtype = OPR_REFERENCE) and
|
|
(assigned(operands[1].ref.symbol)) then
|
|
Freemem(operands[1].ref.symbol,length(operands[1].ref.symbol^)+1);
|
|
operands[1].operandtype := OPR_NONE;
|
|
numops := 0;
|
|
{ always a byte for XLAT }
|
|
instr.stropsize := S_B;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
{ swap the destination and source }
|
|
{ to put in AT&T style direction }
|
|
{ only if there are 2/3 operand }
|
|
{ numbers. }
|
|
if (instruc <> A_ENTER) then
|
|
SwapOperands(instr);
|
|
{ copy them to local variables }
|
|
{ for faster access }
|
|
optyp1:=operands[1].opinfo;
|
|
optyp2:=operands[2].opinfo;
|
|
optyp3:=operands[3].opinfo;
|
|
|
|
end; { end with }
|
|
|
|
{ after reading the operands }
|
|
{ search the instruction }
|
|
{ setup startvalue from cache }
|
|
if ins_cache[instruc]<>-1 then
|
|
i:=ins_cache[instruc]
|
|
else i:=0;
|
|
|
|
|
|
{ this makes cpu.pp uncompilable, but i think this code should be }
|
|
{ inserted in the system unit anyways. }
|
|
if (instruc >= lastop_in_table) and
|
|
((cs_compilesystem in aktswitches) or (opt_processors > globals.i386)) then
|
|
begin
|
|
Message(assem_w_opcode_not_in_table);
|
|
fits:=true;
|
|
end
|
|
else while not(fits) do
|
|
begin
|
|
{ set the instruction cache, if the instruction }
|
|
{ occurs the first time }
|
|
if (it[i].i=instruc) and (ins_cache[instruc]=-1) then
|
|
ins_cache[instruc]:=i;
|
|
|
|
if (it[i].i=instruc) and (instr.numops=it[i].ops) then
|
|
begin
|
|
{ first fit }
|
|
case instr.numops of
|
|
0 : begin
|
|
fits:=true;
|
|
break;
|
|
end;
|
|
1 :
|
|
Begin
|
|
if (optyp1 and it[i].o1)<>0 then
|
|
Begin
|
|
fits:=true;
|
|
break;
|
|
end;
|
|
{ I consider sign-extended 8bit value to }
|
|
{ be equal to immediate 8bit therefore }
|
|
{ convert... }
|
|
if (optyp1 = ao_imm8) then
|
|
Begin
|
|
{ check if this is a simple sign extend. }
|
|
if (it[i].o1<>ao_imm8s) then
|
|
Begin
|
|
fits:=true;
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
2 : if ((optyp1 and it[i].o1)<>0) and
|
|
((optyp2 and it[i].o2)<>0) then
|
|
Begin
|
|
fits:=true;
|
|
break;
|
|
end
|
|
{ if the operands can be swaped }
|
|
{ then swap them }
|
|
else if ((it[i].m and af_d)<>0) and
|
|
((optyp1 and it[i].o2)<>0) and
|
|
((optyp2 and it[i].o1)<>0) then
|
|
begin
|
|
{ swap the destination and source }
|
|
{ to put in AT&T style direction }
|
|
{ What does this mean !!!! ???????????????????????? }
|
|
{ if (output_format in [of_o,of_att]) then }
|
|
{ ???????????? }
|
|
{ SwapOperands(instr); }
|
|
fits:=true;
|
|
break;
|
|
end;
|
|
3 : if ((optyp1 and it[i].o1)<>0) and
|
|
((optyp2 and it[i].o2)<>0) and
|
|
((optyp3 and it[i].o3)<>0) then
|
|
Begin
|
|
fits:=true;
|
|
break;
|
|
end;
|
|
end; { end case }
|
|
end; { endif }
|
|
if it[i].i=A_NONE then
|
|
begin
|
|
{ NO MATCH! }
|
|
Message(assem_e_invalid_opcode_and_operand);
|
|
exit;
|
|
end;
|
|
inc(i);
|
|
end; { end while }
|
|
|
|
{ We add the opcode to the opcode linked list }
|
|
if fits then
|
|
Begin
|
|
if instr.getprefix <> A_NONE then
|
|
Begin
|
|
p^.concat(new(pai386,op_none(instr.getprefix,S_NO)));
|
|
end;
|
|
case instr.numops of
|
|
0:
|
|
if instr.stropsize <> S_NO then
|
|
{ is this a string operation opcode or xlat then check }
|
|
{ the size of the operation. }
|
|
p^.concat(new(pai386,op_none(instruc,instr.stropsize)))
|
|
else
|
|
p^.concat(new(pai386,op_none(instruc,S_NO)));
|
|
1: Begin
|
|
case instr.operands[1].operandtype of
|
|
{ all one operand opcodes with constant have no defined sizes }
|
|
{ at least that is what it seems in the tasm 2.0 manual. }
|
|
OPR_CONSTANT: p^.concat(new(pai386,op_const(instruc,
|
|
S_NO, instr.operands[1].val)));
|
|
{ the size of the operand can be determined by the as,nasm and }
|
|
{ tasm. }
|
|
{ Even though normally gas should not be trusted, v2.8.1 }
|
|
{ has been *extensively* tested to assure that the output }
|
|
{ is indeed correct with the following opcodes: push,pop,inc,dec}
|
|
{ neg and not. }
|
|
OPR_REGISTER: p^.concat(new(pai386,op_reg(instruc,
|
|
S_NO,instr.operands[1].reg)));
|
|
{ this is where it gets a bit more complicated... }
|
|
OPR_REFERENCE:
|
|
if instr.operands[1].size <> S_NO then
|
|
Begin
|
|
p^.concat(new(pai386,op_ref(instruc,
|
|
instr.operands[1].size,newreference(instr.operands[1].ref))));
|
|
end
|
|
else
|
|
Begin
|
|
{ special jmp and call case with }
|
|
{ symbolic references. }
|
|
if instruc in [A_CALL,A_JMP] then
|
|
Begin
|
|
p^.concat(new(pai386,op_ref(instruc,
|
|
S_NO,newreference(instr.operands[1].ref))));
|
|
end
|
|
else
|
|
Message(assem_e_invalid_opcode_and_operand);
|
|
end;
|
|
OPR_NONE: Begin
|
|
Message(assem_f_internal_error_in_concatopcode);
|
|
end;
|
|
else
|
|
Begin
|
|
Message(assem_f_internal_error_in_concatopcode);
|
|
end;
|
|
end;
|
|
end;
|
|
2:
|
|
Begin
|
|
if instruc in [A_MOVSX,A_MOVZX,A_MOVSB,A_MOVSBL,A_MOVSBW,
|
|
A_MOVSWL,A_MOVZB,A_MOVZWL] then
|
|
{ movzx and movsx }
|
|
HandleExtend(instr)
|
|
else
|
|
{ other instructions }
|
|
Begin
|
|
With instr do
|
|
Begin
|
|
{ source }
|
|
opsize := operands[1].size;
|
|
case operands[1].operandtype of
|
|
{ reg,reg }
|
|
{ reg,ref }
|
|
OPR_REGISTER:
|
|
Begin
|
|
case operands[2].operandtype of
|
|
OPR_REGISTER:
|
|
{ see info in ratti386.pas, about the problem }
|
|
{ which can cause gas here. }
|
|
if (opsize = operands[2].size) then
|
|
begin
|
|
p^.concat(new(pai386,op_reg_reg(instruc,
|
|
opsize,operands[1].reg,operands[2].reg)));
|
|
end
|
|
else
|
|
{ these do not require any size specification. }
|
|
if (instruc in [A_IN,A_OUT,A_SAL,A_SAR,A_SHL,A_SHR,A_ROL,
|
|
A_ROR,A_RCR,A_RCL]) then
|
|
{ outs and ins are already taken care by }
|
|
{ the first pass. }
|
|
p^.concat(new(pai386,op_reg_reg(instruc,
|
|
S_NO,operands[1].reg,operands[2].reg)))
|
|
else
|
|
Begin
|
|
Message(assem_e_invalid_opcode_and_operand);
|
|
end;
|
|
OPR_REFERENCE:
|
|
{ variable name. }
|
|
{ here we must check the instruction type }
|
|
{ before deciding if to use and compare }
|
|
{ any sizes. }
|
|
if assigned(operands[2].ref.symbol) then
|
|
Begin
|
|
if (opsize = operands[2].size) or (instruc in
|
|
[A_RCL,A_RCR,A_ROL,A_ROR,A_SAL,A_SAR,A_SHR,A_SHL]) then
|
|
p^.concat(new(pai386,op_reg_ref(instruc,
|
|
opsize,operands[1].reg,newreference(operands[2].ref))))
|
|
else
|
|
Message(assem_e_invalid_size_in_ref);
|
|
end
|
|
else
|
|
Begin
|
|
{ register reference }
|
|
{ possiblities:1) local variable which }
|
|
{ has been replaced by bp and offset }
|
|
{ in this case size should be valid }
|
|
{ 2) Indirect register }
|
|
{ adressing, 1st operand determines }
|
|
{ size. }
|
|
if (opsize = operands[2].size) or (operands[2].size = S_NO) then
|
|
p^.concat(new(pai386,op_reg_ref(instruc,
|
|
opsize,operands[1].reg,newreference(operands[2].ref))))
|
|
else
|
|
Message(assem_e_invalid_size_in_ref);
|
|
end;
|
|
OPR_CONSTANT: { const,reg }
|
|
Begin { OUT const,reg }
|
|
if (instruc = A_OUT) and (opsize = S_B) then
|
|
p^.concat(new(pai386,op_reg_const(instruc,
|
|
opsize,operands[1].reg,operands[2].val)))
|
|
else
|
|
Message(assem_e_invalid_size_in_ref);
|
|
end;
|
|
else { else case }
|
|
Begin
|
|
Message(assem_f_internal_error_in_concatopcode);
|
|
end;
|
|
end; { end inner case }
|
|
end;
|
|
{ const,reg }
|
|
{ const,const }
|
|
{ const,ref }
|
|
OPR_CONSTANT:
|
|
case instr.operands[2].operandtype of
|
|
{ constant, constant does not have a specific size. }
|
|
OPR_CONSTANT:
|
|
p^.concat(new(pai386,op_const_const(instruc,
|
|
S_NO,operands[1].val,operands[2].val)));
|
|
OPR_REFERENCE:
|
|
Begin
|
|
if (operands[1].val <= $ff) and
|
|
(operands[2].size in [S_B,S_W,S_L,S_Q,S_S]) then
|
|
p^.concat(new(pai386,op_const_ref(instruc,
|
|
operands[2].size,operands[1].val,
|
|
newreference(operands[2].ref))))
|
|
else
|
|
if (operands[1].val <= $ffff) and
|
|
(operands[2].size in [S_W,S_L,S_Q,S_S]) then
|
|
p^.concat(new(pai386,op_const_ref(instruc,
|
|
operands[2].size,operands[1].val,
|
|
newreference(operands[2].ref))))
|
|
else
|
|
if (operands[1].val <= $7fffffff) and
|
|
(operands[2].size in [S_L,S_Q,S_S]) then
|
|
p^.concat(new(pai386,op_const_ref(instruc,
|
|
operands[2].size,operands[1].val,
|
|
newreference(operands[2].ref))))
|
|
else
|
|
Message(assem_e_invalid_size_in_ref);
|
|
end;
|
|
OPR_REGISTER:
|
|
Begin
|
|
{ size of opcode determined by register }
|
|
if (operands[1].val <= $ff) and
|
|
(operands[2].size in [S_B,S_W,S_L,S_Q,S_S]) then
|
|
p^.concat(new(pai386,op_const_reg(instruc,
|
|
operands[2].size,operands[1].val,
|
|
operands[2].reg)))
|
|
else
|
|
if (operands[1].val <= $ffff) and
|
|
(operands[2].size in [S_W,S_L,S_Q,S_S]) then
|
|
p^.concat(new(pai386,op_const_reg(instruc,
|
|
operands[2].size,operands[1].val,
|
|
operands[2].reg)))
|
|
else
|
|
if (operands[1].val <= $7fffffff) and
|
|
(operands[2].size in [S_L,S_Q,S_S]) then
|
|
p^.concat(new(pai386,op_const_reg(instruc,
|
|
operands[2].size,operands[1].val,
|
|
operands[2].reg)))
|
|
else
|
|
Message(assem_e_invalid_opcode_size);
|
|
end;
|
|
else
|
|
Begin
|
|
Message(assem_f_internal_error_in_concatopcode);
|
|
end;
|
|
end; { end case }
|
|
{ ref,reg }
|
|
{ ref,ref }
|
|
OPR_REFERENCE:
|
|
case instr.operands[2].operandtype of
|
|
OPR_REGISTER:
|
|
if assigned(operands[1].ref.symbol) then
|
|
{ global variable }
|
|
Begin
|
|
if instruc in [A_LEA,A_LDS,A_LES,A_LFS,A_LGS,A_LSS]
|
|
then
|
|
p^.concat(new(pai386,op_ref_reg(instruc,
|
|
S_NO,newreference(operands[1].ref),
|
|
operands[2].reg)))
|
|
else
|
|
if (opsize = operands[2].size) then
|
|
p^.concat(new(pai386,op_ref_reg(instruc,
|
|
opsize,newreference(operands[1].ref),
|
|
operands[2].reg)))
|
|
else
|
|
Begin
|
|
Message(assem_e_invalid_opcode_and_operand);
|
|
end;
|
|
end
|
|
else
|
|
Begin
|
|
{ register reference }
|
|
{ possiblities:1) local variable which }
|
|
{ has been replaced by bp and offset }
|
|
{ in this case size should be valid }
|
|
{ 2) Indirect register }
|
|
{ adressing, 2nd operand determines }
|
|
{ size. }
|
|
if (opsize = operands[2].size) or (opsize = S_NO) then
|
|
Begin
|
|
p^.concat(new(pai386,op_ref_reg(instruc,
|
|
operands[2].size,newreference(operands[1].ref),
|
|
operands[2].reg)));
|
|
end
|
|
else
|
|
Message(assem_e_invalid_size_in_ref);
|
|
end;
|
|
OPR_REFERENCE: { special opcodes }
|
|
p^.concat(new(pai386,op_ref_ref(instruc,
|
|
opsize,newreference(operands[1].ref),
|
|
newreference(operands[2].ref))));
|
|
else
|
|
Begin
|
|
Message(assem_f_internal_error_in_concatopcode);
|
|
end;
|
|
end; { end inner case }
|
|
end; { end case }
|
|
end; { end with }
|
|
end; {end if movsx... }
|
|
end;
|
|
3: Begin
|
|
{ only imul, shld and shrd }
|
|
{ middle must be a register }
|
|
if (instruc in [A_SHLD,A_SHRD]) and (instr.operands[2].operandtype =
|
|
OPR_REGISTER) then
|
|
Begin
|
|
case instr.operands[2].size of
|
|
S_W: if instr.operands[1].operandtype = OPR_CONSTANT then
|
|
Begin
|
|
if instr.operands[1].val <= $ff then
|
|
Begin
|
|
if instr.operands[3].size in [S_W] then
|
|
Begin
|
|
case instr.operands[3].operandtype of
|
|
OPR_REFERENCE: { MISSING !!!! } ;
|
|
OPR_REGISTER: p^.concat(new(pai386,
|
|
op_const_reg_reg(instruc, S_W,
|
|
instr.operands[1].val, instr.operands[2].reg,
|
|
instr.operands[3].reg)));
|
|
else
|
|
Message(assem_e_invalid_opcode_and_operand);
|
|
Message(assem_e_invalid_opcode_and_operand);
|
|
end;
|
|
end
|
|
else
|
|
Message(assem_e_invalid_opcode_and_operand);
|
|
end;
|
|
end
|
|
else
|
|
Message(assem_e_invalid_opcode_and_operand);
|
|
S_L: if instr.operands[1].operandtype = OPR_CONSTANT then
|
|
Begin
|
|
if instr.operands[1].val <= $ff then
|
|
Begin
|
|
if instr.operands[3].size in [S_L] then
|
|
Begin
|
|
case instr.operands[3].operandtype of
|
|
OPR_REFERENCE: { MISSING !!!! } ;
|
|
OPR_REGISTER: p^.concat(new(pai386,
|
|
op_const_reg_reg(instruc, S_L,
|
|
instr.operands[1].val, instr.operands[2].reg,
|
|
instr.operands[3].reg)));
|
|
else
|
|
Message(assem_e_invalid_opcode_and_operand);
|
|
end;
|
|
end
|
|
else
|
|
Message(assem_e_invalid_opcode_and_operand);
|
|
end;
|
|
end
|
|
else
|
|
Message(assem_e_invalid_opcode_and_operand);
|
|
else
|
|
Message(assem_e_invalid_opcode_and_operand);
|
|
end; { end case }
|
|
end
|
|
else
|
|
if (instruc in [A_IMUL]) and (instr.operands[3].operandtype
|
|
= OPR_REGISTER) then
|
|
Begin
|
|
case instr.operands[3].size of
|
|
S_W: if instr.operands[1].operandtype = OPR_CONSTANT then
|
|
Begin
|
|
if instr.operands[1].val <= $ffff then
|
|
Begin
|
|
if instr.operands[2].size in [S_W] then
|
|
Begin
|
|
case instr.operands[2].operandtype of
|
|
OPR_REFERENCE: { MISSING !!!! } ;
|
|
OPR_REGISTER: p^.concat(new(pai386,
|
|
op_const_reg_reg(instruc, S_W,
|
|
instr.operands[1].val, instr.operands[2].reg,
|
|
instr.operands[3].reg)));
|
|
else
|
|
Message(assem_e_invalid_opcode_and_operand);
|
|
end; { end case }
|
|
end
|
|
else
|
|
Message(assem_e_invalid_opcode_and_operand);
|
|
end;
|
|
end
|
|
else
|
|
Message(assem_e_invalid_opcode_and_operand);
|
|
S_L: if instr.operands[1].operandtype = OPR_CONSTANT then
|
|
Begin
|
|
if instr.operands[1].val <= $7fffffff then
|
|
Begin
|
|
if instr.operands[2].size in [S_L] then
|
|
Begin
|
|
case instr.operands[2].operandtype of
|
|
OPR_REFERENCE: { MISSING !!!! } ;
|
|
OPR_REGISTER: p^.concat(new(pai386,
|
|
op_const_reg_reg(instruc, S_L,
|
|
instr.operands[1].val, instr.operands[2].reg,
|
|
instr.operands[3].reg)));
|
|
else
|
|
Message(assem_e_invalid_opcode_and_operand);
|
|
end; { end case }
|
|
end
|
|
else
|
|
Message(assem_e_invalid_opcode_and_operand);
|
|
end;
|
|
end
|
|
else
|
|
Message(assem_e_invalid_opcode_and_operand);
|
|
else
|
|
Message(assem_e_invalid_middle_sized_operand);
|
|
end; { end case }
|
|
end { endif }
|
|
else
|
|
Message(assem_e_invalid_three_operand_opcode);
|
|
end;
|
|
end; { end case }
|
|
end;
|
|
end;
|
|
|
|
{---------------------------------------------------------------------}
|
|
{ Routines for the parsing }
|
|
{---------------------------------------------------------------------}
|
|
|
|
procedure consume(t : tinteltoken);
|
|
|
|
begin
|
|
if t<>actasmtoken then
|
|
Message(assem_e_syntax_error);
|
|
actasmtoken:=gettoken;
|
|
{ if the token must be ignored, then }
|
|
{ get another token to parse. }
|
|
if actasmtoken = AS_NONE then
|
|
actasmtoken := gettoken;
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function findregister(const s : string): tregister;
|
|
{*********************************************************************}
|
|
{ FUNCTION findregister(s: string):tasmop; }
|
|
{ Description: Determines if the s string is a valid register, }
|
|
{ if so returns correct tregister token, or R_NO if not found. }
|
|
{*********************************************************************}
|
|
var
|
|
i: tregister;
|
|
begin
|
|
findregister := R_NO;
|
|
for i:=firstreg to lastreg do
|
|
if s = iasmregs[i] then
|
|
Begin
|
|
findregister := i;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
|
|
function findoverride(const s: string; var reg:tregister): boolean;
|
|
var
|
|
i: byte;
|
|
begin
|
|
findoverride := FALSE;
|
|
reg := R_NO;
|
|
for i:=0 to _count_asmoverrides do
|
|
Begin
|
|
if s = _asmoverrides[i] then
|
|
begin
|
|
reg := _overridetokens[i];
|
|
findoverride := TRUE;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function findprefix(const s: string; var token: tasmop): boolean;
|
|
var i: byte;
|
|
Begin
|
|
findprefix := FALSE;
|
|
for i:=0 to _count_asmprefixes do
|
|
Begin
|
|
if s = _asmprefixes[i] then
|
|
begin
|
|
token := _prefixtokens[i];
|
|
findprefix := TRUE;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
function findsegment(const s:string): tregister;
|
|
{*********************************************************************}
|
|
{ FUNCTION findsegment(s: string):tasmop; }
|
|
{ Description: Determines if the s string is a valid segment register}
|
|
{ if so returns correct tregister token, or R_NO if not found. }
|
|
{*********************************************************************}
|
|
var
|
|
i: tregister;
|
|
Begin
|
|
findsegment := R_DEFAULT_SEG;
|
|
for i:=firstsreg to lastsreg do
|
|
if s = iasmregs[i] then
|
|
Begin
|
|
findsegment := i;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
function findopcode(const s: string): tasmop;
|
|
{*********************************************************************}
|
|
{ FUNCTION findopcode(s: string): tasmop; }
|
|
{ Description: Determines if the s string is a valid opcode }
|
|
{ if so returns correct tasmop token. }
|
|
{*********************************************************************}
|
|
var
|
|
i: tasmop;
|
|
j: byte;
|
|
Begin
|
|
findopcode := A_NONE;
|
|
for i:=firstop to lastop do
|
|
if s = iasmops^[i] then
|
|
begin
|
|
findopcode:=i;
|
|
exit;
|
|
end;
|
|
{ not found yet, search for extended opcodes }
|
|
{ now, in this case, we must use the suffix }
|
|
{ to determine the size of the instruction }
|
|
for j:=0 to _count_asmspecialops do
|
|
Begin
|
|
if s = _specialops[j] then
|
|
Begin
|
|
findopcode := _specialopstokens[j];
|
|
{ set the size }
|
|
case s[length(s)] of
|
|
'B': instr.stropsize := S_B;
|
|
'D': instr.stropsize := S_L;
|
|
'W': instr.stropsize := S_W;
|
|
end;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Function CheckPrefix(prefix: tasmop; opcode:tasmop): Boolean;
|
|
{ Checks if the prefix is valid with the following instruction }
|
|
{ return false if not, otherwise true }
|
|
Begin
|
|
CheckPrefix := TRUE;
|
|
Case prefix of
|
|
A_REP,A_REPNE,A_REPE: if not (opcode in [A_SCAS,A_INS,A_OUTS,A_MOVS,
|
|
A_CMPS,A_LODS,A_STOS]) then
|
|
Begin
|
|
CheckPrefix := FALSE;
|
|
exit;
|
|
end;
|
|
A_LOCK: if not (opcode in [A_BT,A_BTS,A_BTR,A_BTC,A_XCHG,A_ADD,A_OR,
|
|
A_ADC,A_SBB,A_AND,A_SUB,A_XOR,A_NOT,A_NEG,A_INC,A_DEC]) then
|
|
Begin
|
|
CheckPrefix := FALSE;
|
|
Exit;
|
|
end;
|
|
A_NONE: exit; { no prefix here }
|
|
|
|
else
|
|
CheckPrefix := FALSE;
|
|
end; { end case }
|
|
end;
|
|
|
|
|
|
Procedure InitAsmRef(var instr: TInstruction);
|
|
{*********************************************************************}
|
|
{ Description: This routine first check if the instruction is of }
|
|
{ type OPR_NONE, or OPR_REFERENCE , if not it gives out an error. }
|
|
{ If the operandtype = OPR_NONE or <> OPR_REFERENCE then it sets up }
|
|
{ the operand type to OPR_REFERENCE, as well as setting up the ref }
|
|
{ to point to the default segment. }
|
|
{*********************************************************************}
|
|
Begin
|
|
With instr do
|
|
Begin
|
|
case operands[operandnum].operandtype of
|
|
OPR_REFERENCE: exit;
|
|
OPR_NONE: ;
|
|
else
|
|
Message(assem_e_invalid_operand_type);
|
|
end;
|
|
operands[operandnum].operandtype := OPR_REFERENCE;
|
|
operands[operandnum].ref.segment := R_DEFAULT_SEG;
|
|
end;
|
|
end;
|
|
|
|
Function CheckOverride(segreg: tregister; var instr: TInstruction): Boolean;
|
|
{ Check if the override is valid, and if so then }
|
|
{ update the instr variable accordingly. }
|
|
Begin
|
|
CheckOverride := FALSE;
|
|
if instr.getinstruction in [A_MOVS,A_XLAT,A_CMPS] then
|
|
Begin
|
|
CheckOverride := TRUE;
|
|
Message(assem_e_segment_override_not_supported);
|
|
end
|
|
end;
|
|
|
|
|
|
|
|
|
|
Function CalculateExpression(expression: string): longint;
|
|
var
|
|
expr: TExprParse;
|
|
Begin
|
|
expr.Init;
|
|
CalculateExpression := expr.Evaluate(expression);
|
|
expr.Done;
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Function BuildRefExpression: longint;
|
|
{*********************************************************************}
|
|
{ FUNCTION BuildExpression: longint }
|
|
{ Description: This routine calculates a constant expression to }
|
|
{ a given value. The return value is the value calculated from }
|
|
{ the expression. }
|
|
{ The following tokens (not strings) are recognized: }
|
|
{ (,),SHL,SHR,/,*,NOT,OR,XOR,AND,MOD,+/-,numbers,ID to constants. }
|
|
{*********************************************************************}
|
|
{ ENTRY: On entry the token should be any valid expression token. }
|
|
{ EXIT: On Exit the token points to any token after the closing }
|
|
{ RBRACKET }
|
|
{ ERROR RECOVERY: Tries to find COMMA or SEPARATOR token by consuming }
|
|
{ invalid tokens. }
|
|
{*********************************************************************}
|
|
var tempstr: string;
|
|
expr: string;
|
|
l : longint;
|
|
errorflag : boolean;
|
|
Begin
|
|
errorflag := FALSE;
|
|
tempstr := '';
|
|
expr := '';
|
|
{ tell tokenizer that we are in }
|
|
{ an expression. }
|
|
inexpression := TRUE;
|
|
Repeat
|
|
Case actasmtoken of
|
|
AS_LPAREN: Begin
|
|
Consume(AS_LPAREN);
|
|
expr := expr + '(';
|
|
end;
|
|
AS_RPAREN: Begin
|
|
Consume(AS_RPAREN);
|
|
expr := expr + ')';
|
|
end;
|
|
AS_SHL: Begin
|
|
Consume(AS_SHL);
|
|
expr := expr + '<';
|
|
end;
|
|
AS_SHR: Begin
|
|
Consume(AS_SHR);
|
|
expr := expr + '>';
|
|
end;
|
|
AS_SLASH: Begin
|
|
Consume(AS_SLASH);
|
|
expr := expr + '/';
|
|
end;
|
|
AS_MOD: Begin
|
|
Consume(AS_MOD);
|
|
expr := expr + '%';
|
|
end;
|
|
AS_STAR: Begin
|
|
Consume(AS_STAR);
|
|
expr := expr + '*';
|
|
end;
|
|
AS_PLUS: Begin
|
|
Consume(AS_PLUS);
|
|
expr := expr + '+';
|
|
end;
|
|
AS_MINUS: Begin
|
|
Consume(AS_MINUS);
|
|
expr := expr + '-';
|
|
end;
|
|
AS_AND: Begin
|
|
Consume(AS_AND);
|
|
expr := expr + '&';
|
|
end;
|
|
AS_NOT: Begin
|
|
Consume(AS_NOT);
|
|
expr := expr + '~';
|
|
end;
|
|
AS_XOR: Begin
|
|
Consume(AS_XOR);
|
|
expr := expr + '^';
|
|
end;
|
|
AS_OR: Begin
|
|
Consume(AS_OR);
|
|
expr := expr + '|';
|
|
end;
|
|
{ End of reference }
|
|
AS_RBRACKET: Begin
|
|
if not ErrorFlag then
|
|
BuildRefExpression := CalculateExpression(expr)
|
|
else
|
|
BuildRefExpression := 0;
|
|
Consume(AS_RBRACKET);
|
|
{ no longer in an expression }
|
|
inexpression := FALSE;
|
|
exit;
|
|
end;
|
|
AS_ID:
|
|
Begin
|
|
if NOT SearchIConstant(actasmpattern,l) then
|
|
Begin
|
|
Message1(assem_e_invalid_const_symbol,actasmpattern);
|
|
l := 0;
|
|
end;
|
|
str(l, tempstr);
|
|
expr := expr + tempstr;
|
|
Consume(AS_ID);
|
|
end;
|
|
AS_INTNUM: Begin
|
|
expr := expr + actasmpattern;
|
|
Consume(AS_INTNUM);
|
|
end;
|
|
AS_BINNUM: Begin
|
|
tempstr := BinaryToDec(actasmpattern);
|
|
if tempstr = '' then
|
|
Message(assem_f_error_converting_bin);
|
|
expr:=expr+tempstr;
|
|
Consume(AS_BINNUM);
|
|
end;
|
|
|
|
AS_HEXNUM: Begin
|
|
tempstr := HexToDec(actasmpattern);
|
|
if tempstr = '' then
|
|
Message(assem_f_error_converting_hex);
|
|
expr:=expr+tempstr;
|
|
Consume(AS_HEXNUM);
|
|
end;
|
|
AS_OCTALNUM: Begin
|
|
tempstr := OctalToDec(actasmpattern);
|
|
if tempstr = '' then
|
|
Message(assem_f_error_converting_octal);
|
|
expr:=expr+tempstr;
|
|
Consume(AS_OCTALNUM);
|
|
end;
|
|
else
|
|
Begin
|
|
{ write error only once. }
|
|
if not errorflag then
|
|
Message(assem_e_invalid_constant_expression);
|
|
BuildRefExpression := 0;
|
|
if actasmtoken in [AS_COMMA,AS_SEPARATOR] then exit;
|
|
{ consume tokens until we find COMMA or SEPARATOR }
|
|
Consume(actasmtoken);
|
|
errorflag := TRUE;
|
|
end;
|
|
end;
|
|
Until false;
|
|
end;
|
|
|
|
|
|
|
|
Procedure BuildRecordOffset(var instr: TInstruction; varname: string);
|
|
{*********************************************************************}
|
|
{ PROCEDURE BuildRecordOffset(var Instr: TInstruction) }
|
|
{ Description: This routine takes care of field specifiers of records }
|
|
{ and/or variables in asm operands. It updates the offset accordingly}
|
|
{*********************************************************************}
|
|
{ ENTRY: On entry the token should be DOT. }
|
|
{ name: should be the name of the variable to be expanded. '' if }
|
|
{ no variabled specified. }
|
|
{ EXIT: On Exit the token points to SEPARATOR or COMMA. }
|
|
{ ERROR RECOVERY: Tries to find COMMA or SEPARATOR token by consuming }
|
|
{ invalid tokens. }
|
|
{*********************************************************************}
|
|
var
|
|
firstpass: boolean;
|
|
offset: longint;
|
|
basetypename : string;
|
|
Begin
|
|
basetypename := '';
|
|
firstpass := TRUE;
|
|
{ // .ID[REG].ID ... // }
|
|
{ // .ID.ID... // }
|
|
Consume(AS_DOT);
|
|
Repeat
|
|
case actasmtoken of
|
|
AS_ID: Begin
|
|
InitAsmRef(instr);
|
|
{ // var_name.typefield.typefield // }
|
|
if (varname <> '') then
|
|
Begin
|
|
if not GetVarOffset(varname,actasmpattern,offset) then
|
|
Begin
|
|
Message1(assem_e_unknown_id,actasmpattern);
|
|
end
|
|
else
|
|
Inc(instr.operands[operandnum].ref.offset,Offset);
|
|
end
|
|
else
|
|
{ [ref].var_name.typefield.typefield ... }
|
|
{ [ref].var_name[reg] }
|
|
if not assigned(instr.operands[operandnum].ref.symbol) and
|
|
firstpass then
|
|
Begin
|
|
if not CreateVarInstr(instr,actasmpattern,operandnum) then
|
|
Begin
|
|
{ type field ? }
|
|
basetypename := actasmpattern;
|
|
end
|
|
else
|
|
varname := actasmpattern;
|
|
end
|
|
else
|
|
if firstpass then
|
|
{ [ref].typefield.typefield ... }
|
|
{ where the first typefield must specifiy the base }
|
|
{ object or record type. }
|
|
Begin
|
|
basetypename := actasmpattern;
|
|
end
|
|
else
|
|
{ [ref].typefield.typefield ... }
|
|
{ basetpyename is already set up... now look for fields. }
|
|
Begin
|
|
if not GetTypeOffset(basetypename,actasmpattern,Offset) then
|
|
Begin
|
|
Message1(assem_e_unknown_id,actasmpattern);
|
|
end
|
|
else
|
|
Inc(instr.operands[operandnum].ref.offset,Offset);
|
|
end;
|
|
Consume(AS_ID);
|
|
{ Take care of index register on this variable }
|
|
if actasmtoken = AS_LBRACKET then
|
|
Begin
|
|
Consume(AS_LBRACKET);
|
|
Case actasmtoken of
|
|
AS_REGISTER: Begin
|
|
if instr.operands[operandnum].ref.index <> R_NO then
|
|
Message(assem_e_defining_index_more_than_once);
|
|
instr.operands[operandnum].ref.index :=
|
|
findregister(actasmpattern);
|
|
Consume(AS_REGISTER);
|
|
end;
|
|
else
|
|
Begin
|
|
{ add offsets , assuming these are constant expressions... }
|
|
Inc(instr.operands[operandnum].ref.offset,BuildRefExpression);
|
|
end;
|
|
end;
|
|
Consume(AS_RBRACKET);
|
|
end;
|
|
{ Here we should either have AS_DOT, AS_SEPARATOR or AS_COMMA }
|
|
if actasmtoken = AS_DOT then
|
|
Consume(AS_DOT);
|
|
firstpass := FALSE;
|
|
Offset := 0;
|
|
end;
|
|
AS_SEPARATOR: exit;
|
|
AS_COMMA: exit;
|
|
else
|
|
Begin
|
|
Message(assem_e_invalid_field_specifier);
|
|
Consume(actasmtoken);
|
|
firstpass := FALSE;
|
|
end;
|
|
end; { end case }
|
|
Until (actasmtoken = AS_SEPARATOR) or (actasmtoken = AS_COMMA);
|
|
end;
|
|
|
|
|
|
Function BuildExpression: longint;
|
|
{*********************************************************************}
|
|
{ FUNCTION BuildExpression: longint }
|
|
{ Description: This routine calculates a constant expression to }
|
|
{ a given value. The return value is the value calculated from }
|
|
{ the expression. }
|
|
{ The following tokens (not strings) are recognized: }
|
|
{ (,),SHL,SHR,/,*,NOT,OR,XOR,AND,MOD,+/-,numbers,ID to constants. }
|
|
{*********************************************************************}
|
|
{ ENTRY: On entry the token should be any valid expression token. }
|
|
{ EXIT: On Exit the token points to either COMMA or SEPARATOR }
|
|
{ ERROR RECOVERY: Tries to find COMMA or SEPARATOR token by consuming }
|
|
{ invalid tokens. }
|
|
{*********************************************************************}
|
|
var expr: string;
|
|
tempstr: string;
|
|
l : longint;
|
|
errorflag: boolean;
|
|
Begin
|
|
errorflag := FALSE;
|
|
expr := '';
|
|
tempstr := '';
|
|
{ tell tokenizer that we are in an expression. }
|
|
inexpression := TRUE;
|
|
Repeat
|
|
Case actasmtoken of
|
|
AS_LPAREN: Begin
|
|
Consume(AS_LPAREN);
|
|
expr := expr + '(';
|
|
end;
|
|
AS_RPAREN: Begin
|
|
Consume(AS_RPAREN);
|
|
expr := expr + ')';
|
|
end;
|
|
AS_SHL: Begin
|
|
Consume(AS_SHL);
|
|
expr := expr + '<';
|
|
end;
|
|
AS_SHR: Begin
|
|
Consume(AS_SHR);
|
|
expr := expr + '>';
|
|
end;
|
|
AS_SLASH: Begin
|
|
Consume(AS_SLASH);
|
|
expr := expr + '/';
|
|
end;
|
|
AS_MOD: Begin
|
|
Consume(AS_MOD);
|
|
expr := expr + '%';
|
|
end;
|
|
AS_STAR: Begin
|
|
Consume(AS_STAR);
|
|
expr := expr + '*';
|
|
end;
|
|
AS_PLUS: Begin
|
|
Consume(AS_PLUS);
|
|
expr := expr + '+';
|
|
end;
|
|
AS_MINUS: Begin
|
|
Consume(AS_MINUS);
|
|
expr := expr + '-';
|
|
end;
|
|
AS_AND: Begin
|
|
Consume(AS_AND);
|
|
expr := expr + '&';
|
|
end;
|
|
AS_NOT: Begin
|
|
Consume(AS_NOT);
|
|
expr := expr + '~';
|
|
end;
|
|
AS_XOR: Begin
|
|
Consume(AS_XOR);
|
|
expr := expr + '^';
|
|
end;
|
|
AS_OR: Begin
|
|
Consume(AS_OR);
|
|
expr := expr + '|';
|
|
end;
|
|
AS_ID: Begin
|
|
if NOT SearchIConstant(actasmpattern,l) then
|
|
Begin
|
|
Message1(assem_e_invalid_const_symbol,actasmpattern);
|
|
l := 0;
|
|
end;
|
|
str(l, tempstr);
|
|
expr := expr + tempstr;
|
|
Consume(AS_ID);
|
|
end;
|
|
AS_INTNUM: Begin
|
|
expr := expr + actasmpattern;
|
|
Consume(AS_INTNUM);
|
|
end;
|
|
AS_BINNUM: Begin
|
|
tempstr := BinaryToDec(actasmpattern);
|
|
if tempstr = '' then
|
|
Message(assem_f_error_converting_bin);
|
|
expr:=expr+tempstr;
|
|
Consume(AS_BINNUM);
|
|
end;
|
|
|
|
AS_HEXNUM: Begin
|
|
tempstr := HexToDec(actasmpattern);
|
|
if tempstr = '' then
|
|
Message(assem_f_error_converting_hex);
|
|
expr:=expr+tempstr;
|
|
Consume(AS_HEXNUM);
|
|
end;
|
|
AS_OCTALNUM: Begin
|
|
tempstr := OctalToDec(actasmpattern);
|
|
if tempstr = '' then
|
|
Message(assem_f_error_converting_octal);
|
|
expr:=expr+tempstr;
|
|
Consume(AS_OCTALNUM);
|
|
end;
|
|
{ go to next term }
|
|
AS_COMMA: Begin
|
|
if not ErrorFlag then
|
|
BuildExpression := CalculateExpression(expr)
|
|
else
|
|
BuildExpression := 0;
|
|
inexpression := FALSE;
|
|
Exit;
|
|
end;
|
|
{ go to next symbol }
|
|
AS_SEPARATOR: Begin
|
|
if not ErrorFlag then
|
|
BuildExpression := CalculateExpression(expr)
|
|
else
|
|
BuildExpression := 0;
|
|
inexpression := FALSE;
|
|
Exit;
|
|
end;
|
|
else
|
|
Begin
|
|
{ only write error once. }
|
|
if not errorflag then
|
|
Message(assem_e_invalid_constant_expression);
|
|
{ consume tokens until we find COMMA or SEPARATOR }
|
|
Consume(actasmtoken);
|
|
errorflag := TRUE;
|
|
End;
|
|
end;
|
|
Until false;
|
|
end;
|
|
|
|
|
|
|
|
|
|
Procedure BuildScaling(Var instr: TInstruction);
|
|
{*********************************************************************}
|
|
{ Takes care of parsing expression starting from the scaling value }
|
|
{ up to and including possible field specifiers. }
|
|
{ EXIT CONDITION: On exit the routine should point to AS_SEPARATOR }
|
|
{ or AS_COMMA. On entry should point to AS_STAR token. }
|
|
{*********************************************************************}
|
|
var str:string;
|
|
l: longint;
|
|
code: integer;
|
|
Begin
|
|
Consume(AS_STAR);
|
|
if (instr.operands[operandnum].ref.scalefactor <> 0)
|
|
and (instr.operands[operandnum].ref.scalefactor <> 1) then
|
|
Begin
|
|
Message(assem_f_internal_error_in_buildscale);
|
|
end;
|
|
case actasmtoken of
|
|
AS_INTNUM: str := actasmpattern;
|
|
AS_HEXNUM: str := HexToDec(actasmpattern);
|
|
AS_BINNUM: str := BinaryToDec(actasmpattern);
|
|
AS_OCTALNUM: str := OctalToDec(actasmpattern);
|
|
else
|
|
Message(assem_e_syntax_error);
|
|
end;
|
|
val(str, l, code);
|
|
if code <> 0 then
|
|
Message(assem_e_invalid_scaling_factor);
|
|
if ((l = 2) or (l = 4) or (l = 8) or (l = 1)) and (code = 0) then
|
|
begin
|
|
instr.operands[operandnum].ref.scalefactor := l;
|
|
end
|
|
else
|
|
Begin
|
|
Message(assem_e_invalid_scaling_value);
|
|
instr.operands[operandnum].ref.scalefactor := 0;
|
|
end;
|
|
if instr.operands[operandnum].ref.index = R_NO then
|
|
Begin
|
|
Message(assem_e_scaling_value_only_allowed_with_index);
|
|
instr.operands[operandnum].ref.scalefactor := 0;
|
|
end;
|
|
{ Consume the scaling number }
|
|
Consume(actasmtoken);
|
|
case actasmtoken of
|
|
{ // [...*SCALING-expr] ... // }
|
|
AS_MINUS: Begin
|
|
if instr.operands[operandnum].ref.offset <> 0 then
|
|
Message(assem_f_internal_error_in_buildscale);
|
|
instr.operands[operandnum].ref.offset :=
|
|
BuildRefExpression;
|
|
end;
|
|
{ // [...*SCALING+expr] ... // }
|
|
AS_PLUS: Begin
|
|
if instr.operands[operandnum].ref.offset <> 0 then
|
|
Message(assem_f_internal_error_in_buildscale);
|
|
instr.operands[operandnum].ref.offset :=
|
|
BuildRefExpression;
|
|
end;
|
|
{ // [...*SCALING] ... // }
|
|
AS_RBRACKET: Consume(AS_RBRACKET);
|
|
else
|
|
Message(assem_e_invalid_scaling_value);
|
|
end;
|
|
{ // .Field.Field ... or separator/comma // }
|
|
Case actasmtoken of
|
|
AS_DOT: BuildRecordOffset(instr,'');
|
|
AS_COMMA, AS_SEPARATOR: ;
|
|
else
|
|
Message(assem_e_syntax_error);
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
Procedure BuildReference(var instr: TInstruction);
|
|
{*********************************************************************}
|
|
{ EXIT CONDITION: On exit the routine should point to either the }
|
|
{ AS_COMMA or AS_SEPARATOR token. }
|
|
{ On entry: contains the register after the opening bracket if any. }
|
|
{*********************************************************************}
|
|
var
|
|
reg:string;
|
|
segreg: boolean;
|
|
negative: boolean;
|
|
expr: string;
|
|
Begin
|
|
expr := '';
|
|
if instr.operands[operandnum].operandtype <> OPR_REFERENCE then
|
|
Begin
|
|
Message(assem_e_syn_no_ref_with_brackets);
|
|
InitAsmRef(instr);
|
|
consume(AS_REGISTER);
|
|
end
|
|
else
|
|
Begin
|
|
{ save the reg }
|
|
reg := actasmpattern;
|
|
{ is the syntax of the form: [REG:REG...] }
|
|
consume(AS_REGISTER);
|
|
if actasmtoken = AS_COLON then
|
|
begin
|
|
segreg := TRUE;
|
|
Message(assem_e_expression_form_not_supported);
|
|
if instr.operands[operandnum].ref.segment <> R_NO then
|
|
Message(assem_e_defining_seg_more_than_once);
|
|
instr.operands[operandnum].ref.segment := findsegment(reg);
|
|
{ Here we should process the syntax of the form }
|
|
{ [reg:reg...] }
|
|
{!!!!!!!!!!!!!!!!!!!!!!!! }
|
|
end
|
|
{ This is probably of the following syntax: }
|
|
{ SREG:[REG...] where SReg: is optional. }
|
|
{ Therefore we immediately say that reg }
|
|
{ is the base. }
|
|
else
|
|
Begin
|
|
if instr.operands[operandnum].ref.base <> R_NO then
|
|
Message(assem_e_defining_base_more_than_once);
|
|
instr.operands[operandnum].ref.base := findregister(reg);
|
|
end;
|
|
{ we process this type of syntax immediately... }
|
|
case actasmtoken of
|
|
|
|
{ // REG:[REG].Field.Field ... // }
|
|
{ // REG:[REG].Field[REG].Field... // }
|
|
AS_RBRACKET: Begin
|
|
Consume(AS_RBRACKET);
|
|
{ check for record fields }
|
|
if actasmtoken = AS_DOT then
|
|
BuildRecordOffset(instr,'');
|
|
if (actasmtoken = AS_SEPARATOR) or (actasmtoken = AS_COMMA) then
|
|
exit
|
|
else
|
|
Message(assem_e_syn_reference);
|
|
end;
|
|
{ // REG:[REG +/- ...].Field.Field ... // }
|
|
AS_PLUS,AS_MINUS: Begin
|
|
if actasmtoken = AS_MINUS then
|
|
Begin
|
|
expr := '-';
|
|
negative := TRUE
|
|
end
|
|
else
|
|
Begin
|
|
negative := FALSE;
|
|
expr := '+';
|
|
end;
|
|
Consume(actasmtoken);
|
|
{ // REG:[REG+REG+/-...].Field.Field // }
|
|
if actasmtoken = AS_REGISTER then
|
|
Begin
|
|
if negative then
|
|
Message(assem_e_negative_index_register);
|
|
if instr.operands[operandnum].ref.index <> R_NO then
|
|
Message(assem_e_defining_index_more_than_once);
|
|
instr.operands[operandnum].ref.index := findregister(actasmpattern);
|
|
Consume(AS_REGISTER);
|
|
case actasmtoken of
|
|
AS_RBRACKET: { // REG:[REG+REG].Field.Field... // }
|
|
Begin
|
|
Consume(AS_RBRACKET);
|
|
Case actasmtoken of
|
|
AS_DOT: BuildRecordOffset(instr,'');
|
|
AS_COMMA,AS_SEPARATOR: exit;
|
|
else
|
|
Message(assem_e_syntax_error);
|
|
end
|
|
end;
|
|
AS_PLUS,AS_MINUS: { // REG:[REG+REG+/-expr].Field.Field... // }
|
|
Begin
|
|
if instr.operands[operandnum].ref.offset <> 0 then
|
|
Message(assem_f_internal_error_in_buildreference);
|
|
instr.operands[operandnum].ref.offset :=
|
|
BuildRefExpression;
|
|
case actasmtoken of
|
|
AS_DOT: BuildRecordOffset(instr,'');
|
|
AS_COMMA,AS_SEPARATOR: ;
|
|
else
|
|
Message(assem_e_syntax_error);
|
|
end; { end case }
|
|
end;
|
|
AS_STAR: Begin { // REG:[REG+REG*SCALING...].Field.Field... // }
|
|
BuildScaling(instr);
|
|
end;
|
|
else
|
|
Begin
|
|
Message(assem_e_syntax_error);
|
|
end;
|
|
end; { end case }
|
|
end
|
|
else if actasmtoken = AS_STAR then
|
|
{ // REG:[REG*SCALING ... ] // }
|
|
Begin
|
|
BuildScaling(instr);
|
|
end
|
|
else
|
|
{ // REG:[REG+expr].Field.Field // }
|
|
Begin
|
|
if instr.operands[operandnum].ref.offset <> 0 then
|
|
Message(assem_f_internal_error_in_buildreference);
|
|
instr.operands[operandnum].ref.offset := BuildRefExpression;
|
|
case actasmtoken of
|
|
AS_DOT: BuildRecordOffset(instr,'');
|
|
AS_COMMA,AS_SEPARATOR: ;
|
|
else
|
|
Message(assem_e_syntax_error);
|
|
end; { end case }
|
|
end; { end if }
|
|
end; { end this case }
|
|
{ // REG:[REG*scaling] ... // }
|
|
AS_STAR: Begin
|
|
BuildScaling(instr);
|
|
end;
|
|
end;
|
|
end; { end outer if }
|
|
end;
|
|
|
|
|
|
Procedure BuildBracketExpression(var Instr: TInstruction; var_prefix: boolean);
|
|
{*********************************************************************}
|
|
{ PROCEDURE BuildBracketExpression }
|
|
{ Description: This routine builds up an expression after a LBRACKET }
|
|
{ token is encountered. }
|
|
{ On entry actasmtoken should be equal to AS_LBRACKET. }
|
|
{ var_prefix : Should be set to true if variable identifier has }
|
|
{ been defined, such as in ID[ }
|
|
{*********************************************************************}
|
|
{ EXIT CONDITION: On exit the routine should point to either the }
|
|
{ AS_COMMA or AS_SEPARATOR token. }
|
|
{*********************************************************************}
|
|
var
|
|
l:longint;
|
|
Begin
|
|
Consume(AS_LBRACKET);
|
|
initAsmRef(instr);
|
|
Case actasmtoken of
|
|
{ // Constant reference expression OR variable reference expression // }
|
|
AS_ID: Begin
|
|
if actasmpattern[1] = '@' then
|
|
Message(assem_e_local_symbol_not_allowed_as_ref);
|
|
if SearchIConstant(actasmpattern,l) then
|
|
Begin
|
|
{ if there was a variable prefix then }
|
|
{ add to offset }
|
|
If var_prefix then
|
|
Begin
|
|
Inc(instr.operands[operandnum].ref.offset, BuildRefExpression);
|
|
end
|
|
else
|
|
instr.operands[operandnum].ref.offset :=BuildRefExpression;
|
|
if not (actasmtoken in [AS_SEPARATOR,AS_COMMA]) then
|
|
Message(assem_e_invalid_operand_in_bracket_expression);
|
|
end
|
|
else if NOT var_prefix then
|
|
Begin
|
|
InitAsmRef(instr);
|
|
if not CreateVarInstr(instr,actasmpattern,operandnum) then
|
|
Message1(assem_e_unknown_id,actasmpattern);
|
|
Consume(AS_ID);
|
|
{ is there a constant expression following }
|
|
{ the variable name? }
|
|
if actasmtoken <> AS_RBRACKET then
|
|
Begin
|
|
Inc(instr.operands[operandnum].ref.offset, BuildRefExpression);
|
|
end
|
|
else
|
|
Consume(AS_RBRACKET);
|
|
end
|
|
else
|
|
Message1(assem_e_invalid_symbol_name,actasmpattern);
|
|
end;
|
|
{ Here we handle the special case in tp where }
|
|
{ the + operator is allowed with reg and var }
|
|
{ references, such as in mov al, byte ptr [+bx] }
|
|
AS_PLUS: Begin
|
|
Consume(AS_PLUS);
|
|
Case actasmtoken of
|
|
AS_REGISTER: Begin
|
|
BuildReference(instr);
|
|
end;
|
|
AS_ID: Begin
|
|
if actasmpattern[1] = '@' then
|
|
Message(assem_e_local_symbol_not_allowed_as_ref);
|
|
if SearchIConstant(actasmpattern,l) then
|
|
Begin
|
|
{ if there was a variable prefix then }
|
|
{ add to offset }
|
|
If var_prefix then
|
|
Begin
|
|
Inc(instr.operands[operandnum].ref.offset,
|
|
BuildRefExpression);
|
|
end
|
|
else
|
|
instr.operands[operandnum].ref.offset :=
|
|
BuildRefExpression;
|
|
if not (actasmtoken in [AS_SEPARATOR,AS_COMMA]) then
|
|
Message(assem_e_invalid_operand_in_bracket_expression);
|
|
end
|
|
else if NOT var_prefix then
|
|
Begin
|
|
InitAsmRef(instr);
|
|
if not CreateVarInstr(instr,actasmpattern,operandnum) then
|
|
Message1(assem_e_unknown_id,actasmpattern);
|
|
Consume(AS_ID);
|
|
{ is there a constant expression following }
|
|
{ the variable name? }
|
|
if actasmtoken <> AS_RBRACKET then
|
|
Begin
|
|
Inc(instr.operands[operandnum].ref.offset,
|
|
BuildRefExpression);
|
|
end
|
|
else
|
|
Consume(AS_RBRACKET);
|
|
end
|
|
else
|
|
Message1(assem_e_invalid_symbol_name,actasmpattern);
|
|
end;
|
|
{ // Constant reference expression // }
|
|
AS_INTNUM,AS_BINNUM,AS_OCTALNUM,
|
|
AS_HEXNUM: Begin
|
|
{ if there was a variable prefix then }
|
|
{ add to offset instead. }
|
|
If var_prefix then
|
|
Begin
|
|
Inc(instr.operands[operandnum].ref.offset, BuildRefExpression);
|
|
end
|
|
else
|
|
Begin
|
|
instr.operands[operandnum].ref.offset :=BuildRefExpression;
|
|
end;
|
|
if not (actasmtoken in [AS_SEPARATOR,AS_COMMA]) then
|
|
Message(assem_e_invalid_operand_in_bracket_expression);
|
|
end;
|
|
else
|
|
Message(assem_e_syntax_error);
|
|
end;
|
|
end;
|
|
{ // Constant reference expression // }
|
|
AS_MINUS,AS_NOT,AS_LPAREN:
|
|
Begin
|
|
{ if there was a variable prefix then }
|
|
{ add to offset instead. }
|
|
If var_prefix then
|
|
Begin
|
|
Inc(instr.operands[operandnum].ref.offset, BuildRefExpression);
|
|
end
|
|
else
|
|
Begin
|
|
instr.operands[operandnum].ref.offset :=BuildRefExpression;
|
|
end;
|
|
if not (actasmtoken in [AS_SEPARATOR,AS_COMMA]) then
|
|
Message(assem_e_invalid_operand_in_bracket_expression);
|
|
end;
|
|
{ // Constant reference expression // }
|
|
AS_INTNUM,AS_OCTALNUM,AS_BINNUM,AS_HEXNUM: Begin
|
|
{ if there was a variable prefix then }
|
|
{ add to offset instead. }
|
|
If var_prefix then
|
|
Begin
|
|
Inc(instr.operands[operandnum].ref.offset, BuildRefExpression);
|
|
end
|
|
else
|
|
Begin
|
|
instr.operands[operandnum].ref.offset :=BuildRefExpression;
|
|
end;
|
|
if not (actasmtoken in [AS_SEPARATOR,AS_COMMA]) then
|
|
Message(assem_e_invalid_operand_in_bracket_expression);
|
|
end;
|
|
{ // Variable reference expression // }
|
|
AS_REGISTER: BuildReference(instr);
|
|
else
|
|
Begin
|
|
Message(assem_e_invalid_reference_syntax);
|
|
while (actasmtoken <> AS_SEPARATOR) do
|
|
Consume(actasmtoken);
|
|
end;
|
|
end; { end case }
|
|
end;
|
|
|
|
|
|
Procedure BuildOperand(var instr: TInstruction);
|
|
{*********************************************************************}
|
|
{ EXIT CONDITION: On exit the routine should point to either the }
|
|
{ AS_COMMA or AS_SEPARATOR token. }
|
|
{*********************************************************************}
|
|
var
|
|
tempstr: string;
|
|
expr: string;
|
|
lab: Pasmlabel;
|
|
l : longint;
|
|
hl: plabel;
|
|
Begin
|
|
tempstr := '';
|
|
expr := '';
|
|
case actasmtoken of
|
|
{ // Constant expression // }
|
|
AS_PLUS,AS_MINUS,AS_NOT,AS_LPAREN:
|
|
Begin
|
|
if not (instr.operands[operandnum].operandtype in [OPR_NONE,OPR_CONSTANT]) then
|
|
Message(assem_e_invalid_operand_type);
|
|
instr.operands[operandnum].operandtype := OPR_CONSTANT;
|
|
instr.operands[operandnum].val :=BuildExpression;
|
|
end;
|
|
{ // Constant expression // }
|
|
AS_STRING: Begin
|
|
if not (instr.operands[operandnum].operandtype in [OPR_NONE]) then
|
|
Message(assem_e_invalid_operand_type);
|
|
instr.operands[operandnum].operandtype := OPR_CONSTANT;
|
|
if not PadZero(actasmpattern,4) then
|
|
Message1(assem_e_invalid_string_as_opcode_operand,actasmpattern);
|
|
instr.operands[operandnum].val :=
|
|
ord(actasmpattern[4]) + ord(actasmpattern[3]) shl 8 +
|
|
Ord(actasmpattern[2]) shl 16 + ord(actasmpattern[1])
|
|
shl 24;
|
|
Consume(AS_STRING);
|
|
Case actasmtoken of
|
|
AS_COMMA, AS_SEPARATOR: ;
|
|
else
|
|
Message(assem_e_invalid_string_expression);
|
|
end; { end case }
|
|
end;
|
|
{ // Constant expression // }
|
|
AS_INTNUM,AS_BINNUM,
|
|
AS_OCTALNUM,
|
|
AS_HEXNUM: Begin
|
|
if not (instr.operands[operandnum].operandtype in [OPR_NONE,OPR_CONSTANT]) then
|
|
Message(assem_e_invalid_operand_type);
|
|
instr.operands[operandnum].operandtype := OPR_CONSTANT;
|
|
instr.operands[operandnum].val :=BuildExpression;
|
|
end;
|
|
{ // A constant expression, or a Variable ref. // }
|
|
AS_ID: Begin
|
|
if actasmpattern[1] = '@' then
|
|
{ // Label or Special symbol reference // }
|
|
Begin
|
|
if actasmpattern = '@RESULT' then
|
|
Begin
|
|
InitAsmRef(instr);
|
|
SetUpResult(instr,operandnum);
|
|
end
|
|
else
|
|
if (actasmpattern = '@CODE') or (actasmpattern = '@DATA') then
|
|
Message(assem_w_CODE_and_DATA_not_supported)
|
|
else
|
|
Begin
|
|
delete(actasmpattern,1,1);
|
|
if actasmpattern = '' then
|
|
Message(assem_e_null_label_ref_not_allowed);
|
|
lab := labellist.search(actasmpattern);
|
|
{ check if the label is already defined }
|
|
{ if so, we then check if the plabel is }
|
|
{ non-nil, if so we add it to instruction }
|
|
if assigned(lab) then
|
|
Begin
|
|
if assigned(lab^.lab) then
|
|
Begin
|
|
instr.operands[operandnum].operandtype := OPR_LABINSTR;
|
|
instr.operands[operandnum].hl := lab^.lab;
|
|
instr.labeled := TRUE;
|
|
end;
|
|
end
|
|
else
|
|
{ the label does not exist, create it }
|
|
{ emit the opcode, but set that the }
|
|
{ label has not been emitted }
|
|
Begin
|
|
getlabel(hl);
|
|
labellist.insert(actasmpattern,hl,FALSE);
|
|
instr.operands[operandnum].operandtype := OPR_LABINSTR;
|
|
instr.operands[operandnum].hl := hl;
|
|
instr.labeled := TRUE;
|
|
end;
|
|
end;
|
|
Consume(AS_ID);
|
|
if not (actasmtoken in [AS_SEPARATOR,AS_COMMA]) then
|
|
Begin
|
|
Message(assem_e_syntax_error);
|
|
end;
|
|
end
|
|
{ probably a variable or normal expression }
|
|
{ or a procedure (such as in CALL ID) }
|
|
else
|
|
Begin
|
|
{ is it a constant ? }
|
|
if SearchIConstant(actasmpattern,l) then
|
|
Begin
|
|
if not (instr.operands[operandnum].operandtype in [OPR_NONE,OPR_CONSTANT]) then
|
|
Message(assem_e_invalid_operand_type);
|
|
instr.operands[operandnum].operandtype := OPR_CONSTANT;
|
|
instr.operands[operandnum].val :=BuildExpression;
|
|
end
|
|
else { is it a label variable ? }
|
|
Begin
|
|
{ // ID[ , ID.Field.Field or simple ID // }
|
|
{ check if this is a label, if so then }
|
|
{ emit it as a label. }
|
|
if SearchLabel(actasmpattern,hl) then
|
|
Begin
|
|
instr.operands[operandnum].operandtype := OPR_LABINSTR;
|
|
instr.operands[operandnum].hl := hl;
|
|
instr.labeled := TRUE;
|
|
Consume(AS_ID);
|
|
if not (actasmtoken in [AS_SEPARATOR,AS_COMMA]) then
|
|
Message(assem_e_syntax_error);
|
|
end
|
|
else
|
|
{ is it a normal variable ? }
|
|
Begin
|
|
initAsmRef(instr);
|
|
if not CreateVarInstr(instr,actasmpattern,operandnum) then
|
|
Begin
|
|
{ not a variable.. }
|
|
{ check special variables.. }
|
|
if actasmpattern = 'SELF' then
|
|
{ special self variable }
|
|
Begin
|
|
if assigned(procinfo._class) then
|
|
Begin
|
|
instr.operands[operandnum].ref.offset := procinfo.ESI_offset;
|
|
instr.operands[operandnum].ref.base := procinfo.framepointer;
|
|
end
|
|
else
|
|
Message(assem_e_cannot_use_SELF_outside_a_method);
|
|
end
|
|
else
|
|
Message1(assem_e_unknown_id,actasmpattern);
|
|
end;
|
|
expr := actasmpattern;
|
|
Consume(AS_ID);
|
|
case actasmtoken of
|
|
AS_LBRACKET: { indexing }
|
|
BuildBracketExpression(instr,TRUE);
|
|
AS_DOT: BuildRecordOffset(instr,expr);
|
|
|
|
AS_SEPARATOR,AS_COMMA: ;
|
|
else
|
|
Message(assem_e_syntax_error);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
{ // Register, a variable reference or a constant reference // }
|
|
AS_REGISTER: Begin
|
|
{ save the type of register used. }
|
|
tempstr := actasmpattern;
|
|
Consume(AS_REGISTER);
|
|
if actasmtoken = AS_COLON then
|
|
Begin
|
|
Consume(AS_COLON);
|
|
if actasmtoken <> AS_LBRACKET then
|
|
Message(assem_e_syn_start_with_bracket)
|
|
else
|
|
Begin
|
|
initAsmRef(instr);
|
|
instr.operands[operandnum].ref.segment := findsegment(tempstr);
|
|
BuildBracketExpression(instr,false);
|
|
end;
|
|
end
|
|
{ // Simple register // }
|
|
else if (actasmtoken = AS_SEPARATOR) or (actasmtoken = AS_COMMA) then
|
|
Begin
|
|
if not (instr.operands[operandnum].operandtype in [OPR_NONE,OPR_REGISTER]) then
|
|
Message(assem_e_invalid_operand_type);
|
|
instr.operands[operandnum].operandtype := OPR_REGISTER;
|
|
instr.operands[operandnum].reg := findregister(tempstr);
|
|
end
|
|
else
|
|
Message1(assem_e_syn_register,tempstr);
|
|
end;
|
|
{ // a variable reference, register ref. or a constant reference // }
|
|
AS_LBRACKET: Begin
|
|
BuildBracketExpression(instr,false);
|
|
end;
|
|
{ // Unsupported // }
|
|
AS_SEG,AS_OFFSET: Begin
|
|
Message(assem_e_SEG_and_OFFSET_not_supported);
|
|
Consume(actasmtoken);
|
|
{ error recovery }
|
|
While not (actasmtoken in [AS_SEPARATOR,AS_COMMA]) do
|
|
Consume(actasmtoken);
|
|
end;
|
|
AS_SEPARATOR, AS_COMMA: ;
|
|
else
|
|
Message(assem_e_syn_opcode_operand);
|
|
end; { end case }
|
|
end;
|
|
|
|
|
|
Procedure BuildConstant(maxvalue: longint);
|
|
{*********************************************************************}
|
|
{ PROCEDURE BuildConstant }
|
|
{ Description: This routine takes care of parsing a DB,DD,or DW }
|
|
{ line and adding those to the assembler node. Expressions, range- }
|
|
{ checking are fullly taken care of. }
|
|
{ maxvalue: $ff -> indicates that this is a DB node. }
|
|
{ $ffff -> indicates that this is a DW node. }
|
|
{ $ffffffff -> indicates that this is a DD node. }
|
|
{*********************************************************************}
|
|
{ EXIT CONDITION: On exit the routine should point to AS_SEPARATOR. }
|
|
{*********************************************************************}
|
|
var
|
|
strlength: byte;
|
|
expr: string;
|
|
value : longint;
|
|
Begin
|
|
strlength := 0; { assume it is a DB }
|
|
Repeat
|
|
Case actasmtoken of
|
|
AS_STRING: Begin
|
|
if maxvalue = $ffff then
|
|
strlength := 2
|
|
else if maxvalue = $ffffffff then
|
|
strlength := 4;
|
|
if strlength <> 0 then
|
|
{ DD and DW cases }
|
|
Begin
|
|
if Not PadZero(actasmpattern,strlength) then
|
|
Message(scan_f_string_exceeds_line);
|
|
end;
|
|
expr := actasmpattern;
|
|
Consume(AS_STRING);
|
|
Case actasmtoken of
|
|
AS_COMMA: Consume(AS_COMMA);
|
|
AS_SEPARATOR: ;
|
|
else
|
|
Message(assem_e_invalid_string_expression);
|
|
end; { end case }
|
|
ConcatString(p,expr);
|
|
end;
|
|
AS_INTNUM,AS_BINNUM,
|
|
AS_OCTALNUM,AS_HEXNUM:
|
|
Begin
|
|
value:=BuildExpression;
|
|
ConcatConstant(p,value,maxvalue);
|
|
end;
|
|
AS_ID:
|
|
Begin
|
|
value:=BuildExpression;
|
|
if value > maxvalue then
|
|
Begin
|
|
Message(assem_e_expression_out_of_bounds);
|
|
{ assuming a value of maxvalue }
|
|
value := maxvalue;
|
|
end;
|
|
ConcatConstant(p,value,maxvalue);
|
|
end;
|
|
{ These terms can start an assembler expression }
|
|
AS_PLUS,AS_MINUS,AS_LPAREN,AS_NOT: Begin
|
|
value := BuildExpression;
|
|
ConcatConstant(p,value,maxvalue);
|
|
end;
|
|
AS_COMMA: BEGIN
|
|
Consume(AS_COMMA);
|
|
END;
|
|
AS_SEPARATOR: ;
|
|
|
|
else
|
|
Begin
|
|
Message(assem_f_internal_error_in_buildconstant);
|
|
end;
|
|
end; { end case }
|
|
Until actasmtoken = AS_SEPARATOR;
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Procedure BuildOpCode;
|
|
{*********************************************************************}
|
|
{ PROCEDURE BuildOpcode; }
|
|
{ Description: Parses the intel opcode and operands, and writes it }
|
|
{ in the TInstruction object. }
|
|
{*********************************************************************}
|
|
{ EXIT CONDITION: On exit the routine should point to AS_SEPARATOR. }
|
|
{ On ENTRY: Token should point to AS_OPCODE }
|
|
{*********************************************************************}
|
|
var asmtok: tasmop;
|
|
op: tasmop;
|
|
expr: string;
|
|
segreg: tregister;
|
|
Begin
|
|
expr := '';
|
|
asmtok := A_NONE; { assmume no prefix }
|
|
segreg := R_NO; { assume no segment override }
|
|
|
|
{ // prefix seg opcode // }
|
|
{ // prefix opcode // }
|
|
if findprefix(actasmpattern,asmtok) then
|
|
Begin
|
|
{ standard opcode prefix }
|
|
if asmtok <> A_NONE then
|
|
instr.addprefix(asmtok);
|
|
Consume(AS_OPCODE);
|
|
if findoverride(actasmpattern,segreg) then
|
|
Begin
|
|
Consume(AS_OPCODE);
|
|
Message(assem_w_repeat_prefix_and_seg_override);
|
|
end;
|
|
end
|
|
else
|
|
{ // seg prefix opcode // }
|
|
{ // seg opcode // }
|
|
if findoverride(actasmpattern,segreg) then
|
|
Begin
|
|
Consume(AS_OPCODE);
|
|
if findprefix(actasmpattern,asmtok) then
|
|
Begin
|
|
{ standard opcode prefix }
|
|
Message(assem_w_repeat_prefix_and_seg_override);
|
|
if asmtok <> A_NONE then
|
|
instr.addprefix(asmtok);
|
|
Consume(AS_OPCODE);
|
|
end;
|
|
end;
|
|
{ // opcode // }
|
|
if (actasmtoken <> AS_OPCODE) then
|
|
Begin
|
|
Message(assem_e_invalid_or_missing_opcode);
|
|
{ error recovery }
|
|
While not (actasmtoken in [AS_SEPARATOR,AS_COMMA]) do
|
|
Consume(actasmtoken);
|
|
exit;
|
|
end
|
|
else
|
|
Begin
|
|
op := findopcode(actasmpattern);
|
|
instr.addinstr(op);
|
|
{ // Valid combination of prefix and instruction ? // }
|
|
if (asmtok <> A_NONE) and (NOT CheckPrefix(asmtok,op)) then
|
|
Message1(assem_e_invalid_prefix_and_opcode,actasmpattern);
|
|
{ // Valid combination of segment override // }
|
|
if (segreg <> R_NO) and (NOT CheckOverride(segreg,instr)) then
|
|
Message1(assem_e_invalid_override_and_opcode,actasmpattern);
|
|
Consume(AS_OPCODE);
|
|
{ // Zero operand opcode ? // }
|
|
if actasmtoken = AS_SEPARATOR then
|
|
exit
|
|
else
|
|
operandnum := 1;
|
|
end;
|
|
|
|
While actasmtoken <> AS_SEPARATOR do
|
|
Begin
|
|
case actasmtoken of
|
|
{ // Operand delimiter // }
|
|
AS_COMMA: Begin
|
|
if operandnum > MaxOperands then
|
|
Message(assem_e_too_many_operands)
|
|
else
|
|
Inc(operandnum);
|
|
Consume(AS_COMMA);
|
|
end;
|
|
{ // Typecast, Constant Expression, Type Specifier // }
|
|
AS_DWORD,AS_BYTE,AS_WORD,AS_TBYTE,AS_QWORD: Begin
|
|
Case actasmtoken of
|
|
AS_DWORD: instr.operands[operandnum].size := S_L;
|
|
AS_WORD: instr.operands[operandnum].size := S_W;
|
|
AS_BYTE: instr.operands[operandnum].size := S_B;
|
|
AS_QWORD: instr.operands[operandnum].size := S_Q;
|
|
AS_TBYTE: instr.operands[operandnum].size := S_X;
|
|
end;
|
|
Consume(actasmtoken);
|
|
Case actasmtoken of
|
|
{ // Reference // }
|
|
AS_PTR: Begin
|
|
initAsmRef(instr);
|
|
Consume(AS_PTR);
|
|
BuildOperand(instr);
|
|
end;
|
|
{ // Possibly a typecast or a constant // }
|
|
{ // expression. // }
|
|
AS_LPAREN: Begin
|
|
if actasmtoken = AS_ID then
|
|
Begin
|
|
{ Case vartype of }
|
|
{ LOCAL: Replace by offset and }
|
|
{ BP in treference. }
|
|
{ GLOBAL: Replace by mangledname}
|
|
{ in symbol of treference }
|
|
{ Check if next token = RPAREN }
|
|
{ otherwise syntax error. }
|
|
initAsmRef(instr);
|
|
if not CreateVarInstr(instr,actasmpattern,
|
|
operandnum) then
|
|
Begin
|
|
Message1(assem_e_unknown_id,actasmpattern);
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
instr.operands[operandnum].operandtype := OPR_CONSTANT;
|
|
instr.operands[operandnum].val := BuildExpression;
|
|
end;
|
|
end;
|
|
else
|
|
BuildOperand(instr);
|
|
end; { end case }
|
|
end;
|
|
{ // Type specifier // }
|
|
AS_NEAR,AS_FAR: Begin
|
|
if actasmtoken = AS_NEAR then
|
|
Message(assem_w_near_ignored)
|
|
else
|
|
Message(assem_w_far_ignored);
|
|
Consume(actasmtoken);
|
|
if actasmtoken = AS_PTR then
|
|
begin
|
|
initAsmRef(instr);
|
|
Consume(AS_PTR);
|
|
end;
|
|
BuildOperand(instr);
|
|
end;
|
|
{ // End of asm operands for this opcode // }
|
|
AS_SEPARATOR: ;
|
|
{ // Constant expression // }
|
|
AS_LPAREN: Begin
|
|
instr.operands[operandnum].operandtype := OPR_CONSTANT;
|
|
instr.operands[operandnum].val := BuildExpression;
|
|
end;
|
|
else
|
|
BuildOperand(instr);
|
|
end; { end case }
|
|
end; { end while }
|
|
end;
|
|
|
|
|
|
Function Assemble: Ptree;
|
|
{*********************************************************************}
|
|
{ PROCEDURE Assemble; }
|
|
{ Description: Parses the intel assembler syntax, parsing is done }
|
|
{ according to the rules in the Turbo Pascal manual. }
|
|
{*********************************************************************}
|
|
Var
|
|
hl: plabel;
|
|
labelptr: pasmlabel;
|
|
Begin
|
|
Message(assem_d_start_intel);
|
|
inexpression := FALSE;
|
|
firsttoken := TRUE;
|
|
operandnum := 0;
|
|
if assigned(procinfo.retdef) and
|
|
(is_fpu(procinfo.retdef) or
|
|
ret_in_acc(procinfo.retdef)) then
|
|
procinfo.funcret_is_valid:=true;
|
|
{ sets up all opcode and register tables in uppercase }
|
|
if not _asmsorted then
|
|
Begin
|
|
SetupTables;
|
|
_asmsorted := TRUE;
|
|
end;
|
|
p:=new(paasmoutput,init);
|
|
{ setup label linked list }
|
|
labellist.init;
|
|
c:=asmgetchar;
|
|
actasmtoken:=gettoken;
|
|
while actasmtoken<>AS_END do
|
|
Begin
|
|
case actasmtoken of
|
|
AS_LLABEL: Begin
|
|
labelptr := labellist.search(actasmpattern);
|
|
if not assigned(labelptr) then
|
|
Begin
|
|
getlabel(hl);
|
|
labellist.insert(actasmpattern,hl,TRUE);
|
|
ConcatLabel(p,A_LABEL,hl);
|
|
end
|
|
else
|
|
{ the label has already been inserted into the }
|
|
{ label list, either as an intruction label (in }
|
|
{ this case it has not been emitted), or as a }
|
|
{ duplicate local symbol (in this case it has }
|
|
{ already been emitted). }
|
|
Begin
|
|
if labelptr^.emitted then
|
|
Message1(assem_e_dup_local_sym,'@'+labelptr^.name^)
|
|
else
|
|
Begin
|
|
if assigned(labelptr^.lab) then
|
|
ConcatLabel(p,A_LABEL,labelptr^.lab);
|
|
labelptr^.emitted := TRUE;
|
|
end;
|
|
end;
|
|
Consume(AS_LLABEL);
|
|
end;
|
|
AS_LABEL: Begin
|
|
if SearchLabel(actasmpattern,hl) then
|
|
ConcatLabel(p,A_LABEL, hl)
|
|
else
|
|
Message1(assem_e_unknown_label_identifer,actasmpattern);
|
|
Consume(AS_LABEL);
|
|
end;
|
|
AS_DW: Begin
|
|
Consume(AS_DW);
|
|
BuildConstant($ffff);
|
|
end;
|
|
|
|
AS_DB: Begin
|
|
Consume(AS_DB);
|
|
BuildConstant($ff);
|
|
end;
|
|
AS_DD: Begin
|
|
Consume(AS_DD);
|
|
BuildConstant($ffffffff);
|
|
end;
|
|
AS_OPCODE: Begin
|
|
instr.init;
|
|
BuildOpcode;
|
|
instr.numops := operandnum;
|
|
if instr.labeled then
|
|
ConcatLabeledInstr(instr)
|
|
else
|
|
ConcatOpCode(instr);
|
|
end;
|
|
AS_SEPARATOR:Begin
|
|
Consume(AS_SEPARATOR);
|
|
{ let us go back to the first operand }
|
|
operandnum := 0;
|
|
end;
|
|
AS_END: ; { end assembly block }
|
|
else
|
|
Begin
|
|
Message(assem_e_assemble_node_syntax_error);
|
|
{ error recovery }
|
|
Consume(actasmtoken);
|
|
end;
|
|
end; { end case }
|
|
end; { end while }
|
|
{ check if there were undefined symbols. }
|
|
{ if so, then list each of those undefined }
|
|
{ labels. }
|
|
if assigned(labellist.First) then
|
|
Begin
|
|
labelptr := labellist.First;
|
|
if labellist.First <> nil then
|
|
Begin
|
|
{ first label }
|
|
if not labelptr^.emitted then
|
|
Message1(assem_e_unknown_local_sym,'@'+labelptr^.name^);
|
|
{ other labels ... }
|
|
While (labelptr^.Next <> nil) do
|
|
Begin
|
|
labelptr := labelptr^.Next;
|
|
if not labelptr^.emitted then
|
|
Message1(assem_e_unknown_local_sym,'@'+labelptr^.name^);
|
|
end;
|
|
end;
|
|
end;
|
|
assemble := genasmnode(p);
|
|
labellist.done;
|
|
Message(assem_d_finish_intel);
|
|
end;
|
|
|
|
|
|
Begin
|
|
old_exit:=exitproc;
|
|
exitproc:=@rai386_exit;
|
|
end.
|
|
{
|
|
$Log$
|
|
Revision 1.3 1998-04-08 16:58:06 pierre
|
|
* several bugfixes
|
|
ADD ADC and AND are also sign extended
|
|
nasm output OK (program still crashes at end
|
|
and creates wrong assembler files !!)
|
|
procsym types sym in tdef removed !!
|
|
|
|
Revision 1.2 1998/03/31 15:21:01 florian
|
|
* fix of out (intel syntax) applied
|
|
|
|
Revision 1.1.1.1 1998/03/25 11:18:15 root
|
|
* Restored version
|
|
|
|
Revision 1.19 1998/03/24 21:48:34 florian
|
|
* just a couple of fixes applied:
|
|
- problem with fixed16 solved
|
|
- internalerror 10005 problem fixed
|
|
- patch for assembler reading
|
|
- small optimizer fix
|
|
- mem is now supported
|
|
|
|
Revision 1.18 1998/03/10 01:17:26 peter
|
|
* all files have the same header
|
|
* messages are fully implemented, EXTDEBUG uses Comment()
|
|
+ AG... files for the Assembler generation
|
|
|
|
Revision 1.17 1998/03/09 12:58:12 peter
|
|
* FWait warning is only showed for Go32V2 and $E+
|
|
* opcode tables moved to i386.pas/m68k.pas to reduce circular uses (and
|
|
for m68k the same tables are removed)
|
|
+ $E for i386
|
|
|
|
Revision 1.16 1998/03/04 17:33:56 michael
|
|
+ Changed ifdef FPK to ifdef FPC
|
|
|
|
Revision 1.15 1998/03/03 22:38:26 peter
|
|
* the last 3 files
|
|
|
|
Revision 1.14 1998/03/02 01:49:15 peter
|
|
* renamed target_DOS to target_GO32V1
|
|
+ new verbose system, merged old errors and verbose units into one new
|
|
verbose.pas, so errors.pas is obsolete
|
|
|
|
Revision 1.13 1998/02/13 10:35:38 daniel
|
|
* Made Motorola version compilable.
|
|
* Fixed optimizer
|
|
|
|
Revision 1.12 1998/02/12 11:50:36 daniel
|
|
Yes! Finally! After three retries, my patch!
|
|
|
|
Changes:
|
|
|
|
Complete rewrite of psub.pas.
|
|
Added support for DLL's.
|
|
Compiler requires less memory.
|
|
Platform units for each platform.
|
|
|
|
Revision 1.11 1998/02/07 18:02:36 carl
|
|
+ fwait warning for emulation
|
|
|
|
Revision 1.10 1998/01/19 03:11:40 carl
|
|
* bugfix number 78
|
|
|
|
Revision 1.9 1998/01/09 19:22:51 carl
|
|
* bugfix of __ID variable names
|
|
|
|
Revision 1.8 1997/12/09 14:00:25 carl
|
|
* bugfix of intr reg,reg instructions, size must always be specified
|
|
under gas (ref: DJGPP FAQ)
|
|
* bugfix of concatopcode with fits init twice!
|
|
+ unknown instr. only poermitted when compiling system unit and/or
|
|
target processor > i386
|
|
|
|
Revision 1.7 1997/12/04 12:20:50 pierre
|
|
+* MMX instructions added to att output with a warning that
|
|
GNU as version >= 2.81 is needed
|
|
bug in reading of reals under att syntax corrected
|
|
|
|
Revision 1.6 1997/11/28 18:14:45 pierre
|
|
working version with several bug fixes
|
|
|
|
Revision 1.5 1997/11/28 15:43:20 florian
|
|
Fixed stack ajustment bug, 0.9.8 compiles now 0.9.8 without problems.
|
|
|
|
Revision 1.4 1997/11/28 15:31:59 carl
|
|
* uncommented firstop and lastop. (otherwise can cause bugs)
|
|
|
|
Revision 1.3 1997/11/28 14:26:22 florian
|
|
Fixed some bugs
|
|
|
|
Revision 1.2 1997/11/28 12:03:53 michael
|
|
Changed comment delimiters to braces, causes problems with 0.9.1
|
|
Changed use of ord to typecast with longint.
|
|
Made boolean expressions non-redundant.
|
|
|
|
Revision 1.1.1.1 1997/11/27 08:33:00 michael
|
|
FPC Compiler CVS start
|
|
|
|
|
|
Pre-CVS log:
|
|
|
|
CEC Carl-Eric Codere
|
|
FK Florian Klaempfl
|
|
PM Pierre Muller
|
|
+ feature added
|
|
- removed
|
|
* bug fixed or changed
|
|
|
|
9th november 1997:
|
|
+ first working version with main distribution line of FPC (CEC)
|
|
12th november 1997:
|
|
* bugfix of CALL and JMP with symbolic references. (CEC)
|
|
13th november 1997:
|
|
* too many bugfixes/improvements to name... (CEC)
|
|
* Fixed range check, line numbering, missing operand checking
|
|
bugs - range checking must be off to compile under tp. (CEC)
|
|
+ speed improvement of 30% over old version with global look up tables.
|
|
14th november 1997:
|
|
+ added support for record/object offsets. (CEC)
|
|
* fixed bug regarding ENTER and push imm8 instruction(CEC)
|
|
+ fixed conflicts with fpu instructions. (CEC).
|
|
|
|
}
|