{
    $Id$
    Copyright (c) 1997-98 by Carl Eric Codere

    Does the parsing for the AT&T 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 Ratti386;
{**********************************************************************}
{ 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 Handle record offsets                                            }
{ o Add support imul,shld and shrd.                                  }
{ o Add support for nor operators.                                   }
{ 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).                                                  }
{ o In ConcatOpCode add more checking regarding suffixes and         }
{   destination registers. (started but unfinished).                 }
{--------------------------------------------------------------------}
Interface

uses
  i386,tree;

   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;
 { Hack to support all opcodes in the i386 table    }
 { only tokens up to and including lastop_in_table  }
 { are checked for validity, otherwise...           }
 lastop_in_table = A_POPFD;

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,symtable;

type
 tinteltoken = (
   AS_NONE,AS_LABEL,AS_LLABEL,AS_STRING,AS_HEXNUM,AS_OCTALNUM,
   AS_BINNUM,AS_REALNUM,AS_COMMA,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,AS_DOLLAR,
     {------------------ Assembler directives --------------------}
   AS_DB,AS_DW,AS_DD,AS_DQ,AS_GLOBAL,AS_ALIGN,AS_ASCII,
   AS_ASCIIZ,AS_LCOMM,AS_COMM,AS_SINGLE,AS_DOUBLE,AS_EXTENDED,
   AS_DATA,AS_TEXT,AS_END,
     {------------------ Assembler Operators  --------------------}
   AS_MOD,AS_SHL,AS_SHR,AS_NOT,AS_AND,AS_OR,AS_XOR,AS_NOR);

   tasmkeyword = string[8];
const
   { These tokens should be modified accordingly to the modifications }
   { in the different enumerations.                                   }
   firstdirective = AS_DB;
   lastdirective  = AS_END;
   firstsreg      = R_CS;
   lastsreg       = R_SS;

       _count_asmdirectives = longint(lastdirective)-longint(firstdirective);
       _count_asmprefixes   = 5;
       _count_asmspecialops = 25;
       _count_asmoverrides  = 3;

       _asmdirectives : array[0.._count_asmdirectives] of tasmkeyword =
       ('.byte','.word','.long','.quad','.globl','.align','.ascii',
        '.asciz','.lcomm','.comm','.single','.double','.tfloat',
        '.data','.text','END');

     {------------------ Missing opcodes from std list  ----------------}
       _asmprefixes: array[0.._count_asmprefixes] of tasmkeyword = (
       'REPNE','REPE','REP','REPZ','REPNZ','LOCK');

       _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','CMPSL','INSB','INSW','INSL','OUTSB','OUTSW','OUTSL',
       'SCASB','SCASW','SCASL','STOSB','STOSW','STOSL','MOVSB','MOVSW','MOVSL',
       'LODSB','LODSW','LODSL','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);


       { converts from AT&T style to non-specific style... }
      {'fildq','filds',
     'fildl','fldl','fldt','fistq','fists','fistl','fstl','fsts',
      'fstps','fistpl','fstpl','fistps','fistpq','fstpt','fcomps',
      'ficompl','fcompl','ficomps','fcoms','ficoml','fcoml','ficoms',
      'fiaddl','faddl','fiadds','fisubl','fsubl','fisubs','fsubs',
      'fsubr','fsubrs','fisubrl','fsubrl','fisubrs','fmuls','fimull',
      'fmull','fimuls','fdivs','fidivl','fdivl','fidivs','fdivrs',
      'fidivrl','fdivrl','fidivrs','repe','repne','fadds','popfl', }
       _fpusizes:array[A_FILDQ..A_FIDIVRS] of topsize = (
                 S_Q,S_S,S_L,S_L,S_X,S_Q,S_S,S_L,S_L,S_S,
                 S_S,S_L,S_L,S_S,S_Q,S_X,
                 S_S,S_L,S_L,S_S,
                 S_S,S_L,S_L,S_S,S_L,S_L,S_S,
                 S_L,S_L,S_S,S_S,S_NO,S_S,S_L,
                 S_L,S_S,S_S,S_L,S_L,S_S,S_S,S_L,
                 S_L,S_S,S_S,S_L,S_L,S_S);
       _fpuopcodes:array[A_FILDQ..A_FIDIVRS] of tasmop = (
       A_FILD,A_FILD,A_FILD,A_FLD,A_FLD,A_FIST,A_FIST,A_FIST,A_FST,A_FST,
       A_FSTP,A_FISTP,A_FSTP,A_FISTP,A_FISTP,A_FSTP,
       A_FCOMP,A_FICOMP,A_FCOMP,A_FICOMP,
       A_FCOM,A_FICOM,A_FCOM,A_FICOM,A_FIADD,A_FADD,A_FIADD,
       A_FISUB,A_FSUB,A_FISUB,A_FSUB,A_FSUB,A_FSUBR,A_FISUBR,
       A_FSUBR,A_FISUBR,A_FMUL,A_FIMUL,A_FMUL,A_FIMUL,A_FDIV,A_FIDIV,
       A_FDIV,A_FIDIV,A_FDIVR,A_FIDIVR,A_FDIVR,A_FIDIVR);

 const
  newline = #10;
  firsttoken : boolean = TRUE;
  operandnum : byte = 0;
 charcount: byte = 0;
 var
 p : paasmoutput;
 actasmtoken: tinteltoken;
 actasmpattern: string;
 c: char;
 Instr: TInstruction;
 labellist: TAsmLabelList;
 line: string; { CHanged from const to var, there is a bug in 0.9.1 which
                 doesn't allow 255-char constant strings. MVC}

   Procedure SetupTables;
   { creates uppercased symbol tables. }
   var
     i: tasmop;
     j: tregister;
   Begin
     Message(assem_d_creating_lookup_tables);
     { opcodes }
     new(iasmops);
     for i:=firstop to lastop do
      iasmops^[i] := upper(att_op2str[i]);
     { opcodes }
     for j:=firstreg to lastreg do
      iasmregs[j] := upper(att_reg2str[j]);
   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;
    hs: topsize;
    hid: string;
   Begin
     is_asmopcode := FALSE;
     { first search for extended opcodes }
     for j:=0 to _count_asmspecialops do
     Begin
       if s = _specialops[j] then
       Begin
         is_asmopcode:=TRUE;
         exit;
       end;
     end;

     for i:=firstop to lastop do
     Begin
            if s=iasmops^[i] then
             begin
               is_asmopcode := TRUE;
               exit
             end;
     end;
     { not found yet ... }
     { search for all possible suffixes }
     for hs:=S_WL downto S_B do
        if copy(s,length(s)-length(att_opsize2str[hs])+1,
          length(att_opsize2str[hs]))=upper(att_opsize2str[hs]) then
        begin
           { here we search the entire table... }
           hid:=copy(s,1,length(s)-length(att_opsize2str[hs]));
           for i:=firstop to lastop do
              if (length(hid) > 0) and (hid=iasmops^[i]) 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_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
   token: tinteltoken;
   forcelabel: boolean;
   errorflag : boolean;
   temp: string;
   code: integer;
   value: byte;
  begin
    errorflag := FALSE;
    forcelabel := FALSE;
    actasmpattern :='';
    {* INIT TOKEN TO NOTHING *}
    token := AS_NONE;
    { while space and tab , continue scan... }
    while (c = ' ') or (c = #9) do
    begin
      c := asmgetchar;
    end;
    { 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;
      { directive or local labe }
      if c = '.' then
      begin
        actasmpattern := c;
        { Let us point to the next character }
        c := asmgetchar;
        while c in ['A'..'Z','a'..'z','0'..'9','_','$'] do
        begin
         actasmpattern := actasmpattern + c;
         c := asmgetchar;
        end;

        { this is a local label... }
        if (actasmpattern[2] = 'L') and (c = ':') then
        Begin
          { local variables are case sensitive }
          gettoken := AS_LLABEL;
          { delete .L }
          delete(actasmpattern,1,1);
          delete(actasmpattern,1,1);
          { point to next character ... }
          c := asmgetchar;
          exit;
        end
        { must be a directive }
        else
        Begin
         { directives are case sensitive!! }
         is_asmdirective(actasmpattern, token);
         if (token <> AS_NONE) then
          Begin
            gettoken := token;
            exit;
          end
         else
           Message1(assem_e_not_directive_or_local_symbol,actasmpattern);
        end;
      end; { endif }


      if c='/' then
        begin
           c:=asmgetchar;
           { att styled comment }
           if c='/' then
             begin
                repeat
                   c:=asmgetchar;
                until c=newline;
                firsttoken := TRUE;
                gettoken:=AS_SEPARATOR;
                c:=asmgetchar;
                exit;
             end
           else
             Message(assem_e_slash_at_begin_of_line_not_allowed);
        end;
      { only opcodes and global labels are allowed now. }
      while c in ['A'..'Z','a'..'z','0'..'9','_'] do
      begin
         actasmpattern := actasmpattern + c;
         c := asmgetchar;
      end;

      if c = ':' then
      begin
           uppervar(actasmpattern);
           token := AS_LABEL;
           { let us point to the next character }
           c := asmgetchar;
           gettoken := token;
           exit;
      end;


      If is_asmopcode(upper(actasmpattern)) then
      Begin
       uppervar(actasmpattern);
       gettoken := AS_OPCODE;
       exit;
      end
      else
      if upper(actasmpattern) = 'END' then
      begin
         gettoken := AS_END;
         exit;
      end
      else
      begin
         gettoken := AS_NONE;
         Message(assem_e_invalid_operand);
      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 }
                {                - directive.                                     }
                            begin
                             actasmpattern := c;
                             c:= asmgetchar;
                             while c in  ['A'..'Z','a'..'z','0'..'9','_','$'] do
                             begin
                               actasmpattern := actasmpattern + c;
                               c := asmgetchar;
                             end;
                             is_asmdirective(actasmpattern,token);
                             { if directive }
                             if (token <> AS_NONE) then
                             begin
                               gettoken := token;
                               exit;
                             end;
                             { local label references and directives }
                             { are case sensitive                    }
                             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;
                             { pascal is not case sensitive!    }
                             { therefore variables which are    }
                             { outside the scope of the asm     }
                             { block, should not be made case   }
                             { sensitive...  !!!!!              }
                             uppervar(actasmpattern);

                             If is_asmopcode(actasmpattern) then
                             Begin
                                    gettoken := AS_OPCODE;
                                    exit;
                             end;
                             { we handle this directive separately from }
                             { others.                                  }
                             if actasmpattern = 'END' then
                             Begin
                                 gettoken := AS_END;
                                 exit;
                             end;

                             { 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;
           '&':       begin
                         c:=asmgetchar;
                         gettoken := AS_AND;
                      end;
           { character }
           '''' :     begin
                         c:=asmgetchar;
                         if c = '\' then
                         Begin
                           { escape sequence }
                           c:=asmgetchar;
                           case c of
                         newline: Message(scan_f_string_exceeds_line);
                             't': actasmpattern:=#09;
                             'b': actasmpattern:=#08;
                             '\': actasmpattern:='\';
                             'f': actasmpattern:=#12;
                             'n': actasmpattern:=#10;
                             'r': actasmpattern:=#13;
                             '"': actasmpattern:='"';
                             { octal number }
                             '0'..'7':
                                begin
                                   temp:=c;
                                   temp:=temp+asmgetchar;
                                   temp:=temp+asmgetchar;
                                   val(octaltodec(temp),value,code);
                                   if (code <> 0) then
                                    Message1(assem_e_error_in_octal_const,temp);
                                   actasmpattern:=chr(value);
                                end;
                             { hexadecimal number }
                             'x':
                                 begin
                                   temp:=asmgetchar;
                                   temp:=temp+asmgetchar;
                                   val(hextodec(temp),value,code);
                                   if (code <> 0) then
                                    Message1(assem_e_error_in_hex_const,temp);
                                   actasmpattern:=chr(value);
                                 end;
                             else
                              Begin
                                Message(assem_e_escape_seq_ignored);
                                actasmpattern:=c;
                              end
                           end; { end case }
                         end
                         else
                           actasmpattern:=c;

                         gettoken := AS_STRING;
                         c:=asmgetchar;
                         exit;

                      end;
           { string }
           '"' :
                      begin
                         actasmpattern:='';
                         while true do
                         Begin
                           c:=asmgetchar;
                           case c of
                            '\': Begin
                                  { escape sequences }
                                  c:=asmgetchar;
                                  case c of
                                   newline: Message(scan_f_string_exceeds_line);
                                   't': actasmpattern:=actasmpattern+#09;
                                   'b': actasmpattern:=actasmpattern+#08;
                                   '\': actasmpattern:=actasmpattern+'\';
                                   'f': actasmpattern:=actasmpattern+#12;
                                   'n': actasmpattern:=actasmpattern+#10;
                                   'r': actasmpattern:=actasmpattern+#13;
                                   '"': actasmpattern:=actasmpattern+'"';
                                   { octal number }
                                   '0'..'7':
                                      begin
                                           temp:=c;
                                           temp:=temp+asmgetchar;
                                           temp:=temp+asmgetchar;
                                           val(octaltodec(temp),value,code);
                                           if (code <> 0) then
                                            Message1(assem_e_error_in_octal_const,temp);
                                           actasmpattern:=actasmpattern+chr(value);
                                      end;
                                   { hexadecimal number }
                                   'x':
                                     begin
                                       temp:=asmgetchar;
                                       temp:=temp+asmgetchar;
                                       val(hextodec(temp),value,code);
                                       if (code <> 0) then
                                        Message1(assem_e_error_in_hex_const,temp);
                                       actasmpattern:=actasmpattern+chr(value);
                                     end;
                                   else
                                     Begin
                                       Message(assem_e_escape_seq_ignored);
                                       actasmpattern:=actasmpattern+c;
                                     end
                                   end; { end case }
                                 end;
                            '"': begin
                                  c:=asmgetchar;
                                  break;
                                 end;
                            newline: Message(scan_f_string_exceeds_line);
                           else
                             actasmpattern:=actasmpattern+c;
                           end;
                         end; { end case }
                   token := AS_STRING;
                   gettoken := token;
                   exit;
                 end;
           '$' :  begin
                   gettoken := AS_DOLLAR;
                   c:=asmgetchar;
                   exit;
                  end;
           ',' : begin
                   gettoken := AS_COMMA;
                   c:=asmgetchar;
                   exit;
                 end;
           '<' : begin
                   gettoken := AS_SHL;
                   c := asmgetchar;
                   if c = '<' then
                     c := asmgetchar;
                   exit;
                 end;
           '>' : begin
                   gettoken := AS_SHL;
                   c := asmgetchar;
                   if c = '>' then
                     c := asmgetchar;
                   exit;
                 end;
           '|' : begin
                   gettoken := AS_OR;
                   c := asmgetchar;
                   exit;
                 end;
           '^' : begin
                  gettoken := AS_XOR;
                  c := asmgetchar;
                  exit;
                 end;
           '!' : begin
                  Message(assem_e_nor_not_supported);
                  c := asmgetchar;
                  gettoken := AS_NONE;
                  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_PLUS;
                   c:=asmgetchar;
                   exit;
                 end;
           '-' : begin
                   gettoken := AS_MINUS;
                   c:=asmgetchar;
                   exit;
                 end;
           '*' : begin
                   gettoken := AS_STAR;
                   c:=asmgetchar;
                   exit;
                 end;
           '/' : begin
                   c:=asmgetchar;
                   { att styled comment }
                   if c='/' then
                     begin
                        repeat
                           c:=asmgetchar;
                        until c=newline;
                        firsttoken := TRUE;
                        gettoken:=AS_SEPARATOR;
                        c:=asmgetchar;
                        exit;
                     end
                   else
                     begin
                        gettoken := AS_SLASH;
                        c:=asmgetchar;
                        exit;
                     end;
                 end;
           { register or modulo      }
           { only register supported }
           { for the moment.         }
           '%' : begin
                     actasmpattern := c;
                     c:=asmgetchar;
                     while c in ['a'..'z','A'..'Z','0'..'9'] do
                     Begin
                        actasmpattern := actasmpattern + c;
                        c:=asmgetchar;
                     end;
                     token := AS_NONE;
                     uppervar(actasmpattern);
                     if (actasmpattern = '%ST') and (c='(') then
                     Begin
                        actasmpattern:=actasmpattern+c;
                        c:=asmgetchar;
                        if c in ['0'..'9'] then
                          actasmpattern := actasmpattern + c
                        else
                          Message(assem_e_invalid_fpu_register);
                        c:=asmgetchar;
                        if c <> ')' then
                          Message(assem_e_invalid_fpu_register)
                        else
                        Begin
                          actasmpattern := actasmpattern + c;
                          c:=asmgetchar; { let us point to next character. }
                        end;
                     end;
                     is_register(actasmpattern, token);
                     { if found }
                     if (token <> AS_NONE) then
                     begin
                        gettoken := token;
                        exit;
                     end
                     else
                       Message(assem_w_modulo_not_supported);
                 end;
           { integer number }
           '1'..'9': begin
                        actasmpattern := c;
                        c := asmgetchar;
                        while c in ['0'..'9'] do
                          Begin
                             actasmpattern := actasmpattern + c;
                             c:= asmgetchar;
                          end;
                        gettoken := AS_INTNUM;
                        exit;
                     end;
           '0': begin
                { octal,hexa,real or binary number. }
                 actasmpattern := c;
                 c:=asmgetchar;
                 case upcase(c) of
                   { binary }
                   'B': Begin
                          c:=asmgetchar;
                          while c in ['0','1'] do
                          Begin
                            actasmpattern := actasmpattern + c;
                            c := asmgetchar;
                          end;
                          gettoken := AS_BINNUM;
                          exit;
                        end;
                   { real }
                   'D': Begin
                          c:=asmgetchar;
                          { get ridd of the 0d }
                          if (c='+') or (c='-') then
                            begin
                               actasmpattern:=c;
                               c:=asmgetchar;
                            end
                          else
                            actasmpattern:='';
                        while c in ['0'..'9'] do
                          Begin
                             actasmpattern := actasmpattern + c;
                             c:= asmgetchar;
                          end;
                        if c='.' then
                          begin
                             actasmpattern := actasmpattern + c;
                             c:=asmgetchar;
                             while c in ['0'..'9'] do
                               Begin
                                  actasmpattern := actasmpattern + c;
                                  c:= asmgetchar;
                               end;
                             if upcase(c) = 'E' then
                               begin
                                  actasmpattern := actasmpattern + c;
                                  c:=asmgetchar;
                                  if (c = '+') or (c = '-') then
                                    begin
                                       actasmpattern := actasmpattern + c;
                                       c:=asmgetchar;
                                    end;
                                  while c in ['0'..'9'] do
                                    Begin
                                       actasmpattern := actasmpattern + c;
                                       c:= asmgetchar;
                                    end;
                               end;
                             gettoken := AS_REALNUM;
                             exit;
                          end
                        else
                            Message1(assem_e_invalid_float_const,actasmpattern+c);
                        end;
                   { hexadecimal }
                   'X': 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;
                   { octal }
                   '1'..'7': begin
                               actasmpattern := actasmpattern + c;
                               while c in ['0'..'7'] do
                               Begin
                                 actasmpattern := actasmpattern + c;
                                 c := asmgetchar;
                               end;
                               gettoken := AS_OCTALNUM;
                               exit;
                             end;
                    else { octal number zero value...}
                      Begin
                         gettoken := AS_OCTALNUM;
                         exit;
                      end;
                   end; { end case }
                end;

         '{',#13,newline,';' : begin
                            { the comment is read by asmgetchar }
                            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                         }
  {---------------------------------------------------------------------}


  { looks for internal names of variables and routines }
  Function SearchDirectVar(var Instr: TInstruction; const hs:string;operandnum:byte): Boolean;
  var
    p : pai_external;
  Begin
     { search in the list of internals }
     p:=search_assembler_symbol(internals,hs,EXT_ANY);
       if p=nil then
         p:=search_assembler_symbol(externals,hs,EXT_ANY);
     if p<>nil then
       begin
         { get symbol name                                  }
         { free the memory before changing the symbol name. }
         if assigned(instr.operands[operandnum].ref.symbol) then
           FreeMem(instr.operands[operandnum].ref.symbol,
               length(instr.operands[operandnum].ref.symbol^)+1);
         instr.operands[operandnum].ref.symbol:=newpasstr(strpas(p^.name));
           case p^.exttyp of
             EXT_BYTE   : instr.operands[operandnum].size := S_B;
             EXT_WORD   : instr.operands[operandnum].size := S_W;
             EXT_NEAR,EXT_FAR,EXT_PROC,EXT_DWORD,EXT_CODEPTR,EXT_DATAPTR:
             instr.operands[operandnum].size := S_L;
             EXT_QWORD  : instr.operands[operandnum].size := S_Q;
             EXT_TBYTE  : instr.operands[operandnum].size := S_X;
           else
             { this is in the case where the instruction is LEA }
             { or something like that, in that case size is not }
             { important.                                       }
               instr.operands[operandnum].size := S_NO;
           end;
         SearchDirectVar := TRUE;
         Exit;
       end;
  end;


   { 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 HandleExtend(var instr: TInstruction);
   { Handles MOVZX, MOVSX ... }
   var
     instruc: tasmop;
     opsize: topsize;
   Begin
      instruc:=instr.getinstruction;
      { if we have A_MOVZX/A_MOVSX here, there is a big problem }
      { it should never happen, because it is already replaced  }
      { by ConcatOpcode!                                        }
      if (instruc in [A_MOVZX,A_MOVSX]) then
       Message(assem_f_internal_error_in_handleextend)
      else
      if (instruc = A_MOVSB) or (instruc = A_MOVSBL)
      or (instruc = A_MOVSBW) or (instruc = A_MOVSWL) then
        instruc := A_MOVSX
      else
      if (instruc = A_MOVZB) or (instruc = 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) { exit...}
              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) {exit...}
              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 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[2].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.    }
       { is this right for ratti386 ? (PM) }
       { almost... here we check for the size of registers and references }
       { to determine the correct gas opcode to use, because if the token }
       { is A_MOVSX or A_MOVZX then that means that the person forgot to  }
       { specify the size..                                               }
       { if memory size is not specified, will of course give out an error}
       if instruc = A_MOVSX then
       Begin
         if numops = 2 then
         begin
           if stropsize = S_BL then
           begin
               operands[1].size := S_B;
               stropsize := S_NO;
               operands[2].size := S_L;
               addinstr(A_MOVSBL)
           end
           else
           if stropsize = S_WL then
           begin
               operands[1].size := S_W;
               stropsize := S_NO;
               operands[2].size := S_L;
               addinstr(A_MOVSWL)
           end
           else
           if stropsize = S_BW then
           begin
               operands[1].size := S_B;
               stropsize := S_NO;
               operands[2].size := S_W;
               addinstr(A_MOVSBW)
           end
           else
           if (operands[1].size = S_B) and (operands[2].size = S_W) then
               addinstr(A_MOVSBW)
           else
           if (operands[1].size = S_B) and (operands[2].size = S_L) then
               addinstr(A_MOVSBL)
           else
           if (operands[1].size = S_W) and (operands[2].size = S_L) then
               addinstr(A_MOVSWL)
           else
           begin
             Message(assem_e_invalid_size_movzx);
             exit;
           end;
           instruc := getinstruction; { reload instruction }
         end
         else
         begin
           Message(assem_e_too_many_operands);
           exit;
         end;
       end
       else
       if instruc = A_MOVZX then
       Begin
         if numops = 2 then
         Begin
           if stropsize = S_BW then
           begin
               operands[1].size := S_B;
               stropsize := S_NO;
               operands[2].size := S_W;
               addinstr(A_MOVZB)
           end
           else
           if stropsize = S_BL then
           begin
               operands[1].size := S_B;
               stropsize := S_NO;
               operands[2].size := S_L;
               addinstr(A_MOVZB)
           end
           else
           if stropsize = S_WL then
           begin
               operands[1].size := S_W;
               stropsize := S_NO;
               operands[2].size := S_L;
               addinstr(A_MOVZWL)
           end
           else
           { change the instruction to conform to GAS }
           if (operands[1].size = S_B) and (operands[2].size in [S_W,S_L]) then
               addinstr(A_MOVZB)
           else
           if (operands[1].size = S_W) and (operands[2].size = S_L) then
               addinstr(A_MOVZWL)
           else
           begin
             Message(assem_e_invalid_size_movzx);
             exit;
           end;
           instruc := getinstruction;  { reload instruction }
         end
         else
         Begin
           Message(assem_e_too_many_operands);
           exit;
         end;
       end
       else
       if instruc = A_FWAIT then
        FWaitWarning
       else
       if (instruc in [A_BT,A_BTC,A_BTR,A_BTS]) then
       Begin
          if numops = 2 then
            Begin
                if (operands[1].operandtype = OPR_CONSTANT)
                and (operands[1].val <= $ff) then
                  Begin
                     operands[1].opinfo := ao_imm8;
                     { no operand size if using constant. }
                     operands[1].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_INS,A_IN]) 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_IN) then
                    Begin
                       operands[2].opinfo := ao_acc;
                    end
               end
              else
              if (operands[1].operandtype = OPR_CONSTANT) and (operands[1].val <= $ff)
                and (instruc = A_IN) 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_IN) then
                    Begin
                       operands[2].opinfo := ao_acc;
                    end
                end;
            end
          else
            Begin
              Message(assem_e_invalid_opcode_and_operand);
              exit;
            end;
       end
       else
       if (instruc in [A_OUTS,A_OUT]) 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_OUT) then
                     Begin
                       operands[1].opinfo := ao_acc;
                       fits := TRUE;
                     end
               end
              else
              if (operands[2].operandtype = OPR_CONSTANT) and (operands[2].val <= $ff)
                and (instruc = A_OUT) 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_OUT) then
                     Begin
                       operands[1].opinfo := ao_acc;
                       fits := TRUE;
                     end
                end;
            end
          else
            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[1].operandtype = OPR_REGISTER) and (operands[1].reg = R_CL)
              then
              Begin
                operands[1].opinfo := ao_shiftcount
              end
              else
              if (operands[1].operandtype = OPR_CONSTANT) and
                (operands[1].val <= $ff) then
                Begin
                   operands[1].opinfo := ao_imm8;
                   operands[1].size := S_B;
                end;
            end
          else { if numops = 2 }
            Begin
                Message(assem_e_invalid_opcode_and_operand);
                exit;
            end;
       end
       { endif ROL,RCL ... }
       else
       { this did not work  (PM) }
       if instruc in [A_DIV, A_IDIV] then
       Begin
          if (operands[2].operandtype = OPR_REGISTER) and
            (operands[2].reg in [R_AL,R_AX,R_EAX]) then
                operands[2].opinfo := ao_acc;
       end
       else
       if (instruc = A_FNSTSW) or (instruc = A_FSTSW) then
       Begin
         { %ax can be omitted in ATT syntax }
          if numops = 0 then
            Begin
               numops:=1;
               operands[1].operandtype:=OPR_REGISTER;
               operands[1].reg:=R_AX;
               operands[1].opinfo := ao_acc;
            end
          else 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[2].operandtype = OPR_REFERENCE) and
                 (assigned(operands[2].ref.symbol)) then
                 Freemem(operands[2].ref.symbol,length(operands[2].ref.symbol^)+1);
               if (operands[1].operandtype = OPR_REFERENCE) and
                 (assigned(operands[1].ref.symbol)) then
                 Freemem(operands[1].ref.symbol,length(operands[2].ref.symbol^)+1);
               operands[2].operandtype := OPR_NONE;
               operands[1].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
     else
     { ------------------------------------------------------------------- }
     { ------------------------- SIZE CHECK ------------------------------ }
     { ------------- presently done only for most used opcodes  ---------- }
     {  Checks if the suffix concords with the destination size    , if    }
     {  not gives out an error. (This check is stricter then gas but is    }
     {  REQUIRED for intasmi3)                                             }
     if instruc in [A_MOV,A_ADD,A_SUB,A_ADC,A_SBB,A_CMP,A_AND,A_OR,A_TEST,A_XOR] then
     begin
       if (instr.stropsize <> S_NO) and (instr.operands[2].size <> S_NO) then
         if (instr.stropsize <> instr.operands[2].size) then
         begin
            Message(assem_e_size_suffix_and_dest_reg_dont_match);
            exit;
         end;
     end
     else
     if instruc in [A_DEC,A_INC,A_NOT,A_NEG] then
     begin
       if (instr.stropsize <> S_NO) and (instr.operands[1].size <> S_NO) then
         if (instr.stropsize <> instr.operands[1].size) then
         begin
            Message(assem_e_size_suffix_and_dest_reg_dont_match);
            exit;
         end;
     end;
     { ------------------------------------------------------------------- }


    { 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;

    { I think this is too dangerous for me therefore i decided that for }
    { the att version only if the processor > i386 or we are compiling  }
    { the system unit then this will be allowed...                      }
    if (instruc >= lastop_in_table) and
       ((cs_compilesystem in aktswitches) or (opt_processors > globals.i386)) then
      begin
         Message1(assem_w_opcode_not_in_table,att_op2str[instruc]);
         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
                 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;
    { change from AT&T styled floating point to   }
    { intel styled floating point with valid size }
    { we use these instructions so it does not    }
    { mess up intasmi3                            }
    if (instruc >= A_FILDQ) and (instruc <= A_FIDIVRS) then
    Begin
      instr.stropsize := _fpusizes[instruc];
      instr.addinstr(_fpuopcodes[instruc]);
      instruc := instr.getinstruction;
    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 -- see note in rai386.pas                               }
           OPR_REGISTER:  p^.concat(new(pai386,op_reg(instruc,
                            S_NO,instr.operands[1].reg)));
           OPR_REFERENCE:
               { now first check suffix ... }
                          if instr.stropsize <> S_NO then
                          Begin
                                p^.concat(new(pai386,op_ref(instruc,
                                  instr.stropsize,newreference(instr.operands[1].ref))));
                          end
               { no suffix... therefore resort using intel styled checking .. }
                          else
                          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]) or
                                 (instruc = A_FNSTCW) or
                                 (instruc = A_FSTCW) or
                                 (instruc = A_FLDCW) or
                                 (instruc = A_FNSTSW) or
                                 (instruc = A_FSTSW) or
                                 (instruc = A_FLDENV) or
                                 (instruc = A_FSTENV) or
                                 (instruc = A_FNSAVE) or
                                 (instruc = A_FSAVE) 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;
{ This either crashed the compiler or the symbol would always be nil! }
{ The problem is here is I didn't see any way of adding the labeled   }
{ symbol in the internal list, since i think from what i see in aasm  }
{ that these will automatically be declared as external ??            }
{                              if (instruc 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_JMP,A_CALL]) then
                              Begin
                                if assigned(instr.operands[1].ref.symbol) then
                                   p^.concat(new(pai386,op_csymbol(instruc,
                                     S_NO,newcsymbol(instr.operands[1].ref.symbol^,instr.operands[1].ref.offset))))
                                else
                                  Message(assem_e_invalid_opcode_and_operand);
                              end
                              else
                              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     }
                  { const,reg -- IN/OUT }
                   OPR_REGISTER:
                     Begin
                       case operands[2].operandtype of
                         OPR_REGISTER:
                            { correction: according to the DJGPP FAQ, gas }
                            { doesn't even check correctly the size of    }
                            { operands, therefore let us specify a size!  }
                            { as in the GAS docs... destination tells us  }
                            { the size! This might give out invalid output }
                            { in some very rare cases (because the size   }
                            { checking is still not perfect).             }
                            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
                            if stropsize <> S_NO then
                            Begin
                               p^.concat(new(pai386,op_reg_reg(instruc,
                               stropsize,operands[1].reg,operands[2].reg)))
                            end
                            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 stropsize <> S_NO then
                              Begin
                               p^.concat(new(pai386,op_reg_ref(instruc,
                               stropsize,operands[1].reg,newreference(operands[2].ref))))
                              end
                              else
                              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 }
                              if stropsize <> S_NO then
                              Begin
                               p^.concat(new(pai386,op_reg_ref(instruc,
                               stropsize,operands[1].reg,newreference(operands[2].ref))))
                              end
                              else
                              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:  { OUT }
                           begin
                              { determine first with suffix }
                              if instruc = A_OUT then
                              begin
                               if instr.stropsize <> S_NO then
                                  p^.concat(new(pai386,op_reg_const(instruc,stropsize,
                                    instr.operands[1].reg, instr.operands[2].val)))
                               else
                                  p^.concat(new(pai386,op_reg_const(instruc,S_NO,
                                    instr.operands[1].reg, instr.operands[2].val)));
                              end
                              else
                                Message(assem_e_invalid_opcode);
                           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
                           { check for suffix first ... }
                              if (instr.stropsize <> S_NO) then
                              Begin
                                 p^.concat(new(pai386,op_const_ref(instruc,
                                 stropsize,operands[1].val,
                                 newreference(operands[2].ref))))
                              end
                              else
                           { resort to intel styled checking ... }
                              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 (stropsize <> S_NO) then
                              Begin
                                 p^.concat(new(pai386,op_ref_reg(instruc,
                                 stropsize,newreference(operands[1].ref),
                                 operands[2].reg)))
                              end
                              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 (stropsize <> S_NO) then
                              Begin
                                 p^.concat(new(pai386,op_ref_reg(instruc,
                                 stropsize,newreference(operands[1].ref),
                                 operands[2].reg)))
                              end
                              else
                              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);
                                 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;

    Procedure ConcatLabeledInstr(var instr: TInstruction);

      Var instruct : tasmop;
          i : longint;
    Begin
       instruct:=instr.getinstruction;
       if (instruct 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 assigned(instr.operands[1].hl) then
          ConcatLabel(p,instruct, instr.operands[1].hl)
        else
          Begin
            Message(assem_f_internal_error_in_concatlabeledinstr);
          end;
       end
       else
       if (cs_compilesystem in aktswitches) then
       begin
        for i:=1 to instr.numops do
          if instr.operands[i].operandtype=OPR_LABINSTR then
            begin
              instr.operands[i].operandtype:=OPR_REFERENCE;
              instr.operands[i].ref.symbol:=newpasstr(lab2str(instr.operands[i].hl) );
              instr.operands[i].opinfo:=ao_mem;
              instr.operands[i].ref.base:=R_NO;
              instr.operands[i].ref.index:=R_NO;
              instr.operands[i].ref.segment:=R_DEFAULT_SEG;
              instr.operands[i].ref.offset:=0;
            end;
        { handle now as an ordinary opcode }
        concatopcode(instr);
       end
       else
         Message(assem_e_invalid_operand);
    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 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;
    hs: topsize;
    hid: string;
   Begin
     findopcode := A_NONE;
     { first 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;
         'L': instr.stropsize := S_L;
         'W': instr.stropsize := S_W;
         end;
         exit;
       end;
     end;
     for i:=firstop to lastop do
     Begin
            if s=iasmops^[i] then
             begin
               findopcode := i;
               instr.stropsize := S_NO;
               exit;
             end;
     end;
     { not found yet ... }
     { search for all possible suffixes }
     for hs:=S_WL downto S_B do
        if copy(s,length(s)-length(att_opsize2str[hs])+1,
          length(att_opsize2str[hs]))=upper(att_opsize2str[hs]) then
        begin
           hid:=copy(s,1,length(s)-length(att_opsize2str[hs]));
           for i:=firstop to lastop do
              if (length(hid) > 0) and (hid=iasmops^[i]) then
              begin
                findopcode := i;
                instr.stropsize := hs;
                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 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 := '';
    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;
                  Exit;
               end;
      { go to next symbol }
      AS_SEPARATOR: Begin
                      if not ErrorFlag then
                        BuildExpression := CalculateExpression(expr)
                      else
                        BuildExpression := 0;
                      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 BuildRealConstant(typ : tfloattype);
  {*********************************************************************}
  { PROCEDURE BuilRealConst                                             }
  {  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:                  }
  {    +/-,numbers and real numbers                                     }
  {*********************************************************************}
  { 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;
      r : extended;
      code : word;
      negativ : boolean;
      errorflag: boolean;
  Begin
    errorflag := FALSE;
    Repeat
    negativ:=false;
    expr := '';
    tempstr := '';
    if actasmtoken=AS_PLUS then Consume(AS_PLUS)
    else if actasmtoken=AS_MINUS then
      begin
         negativ:=true;
         consume(AS_MINUS);
      end;
    Case actasmtoken of
      AS_INTNUM:  Begin
                   expr := actasmpattern;
                   Consume(AS_INTNUM);
                 end;
      AS_REALNUM:  Begin
                   expr := actasmpattern;
                   { in ATT syntax you have 0d in front of the real }
                   { should this be forced ?  yes i think so, as to }
                   { conform to gas as much as possible.            }
                   if (expr[1]='0') and (upper(expr[2])='D') then
                     expr:=copy(expr,3,255);
                   Consume(AS_REALNUM);
                 end;
      AS_BINNUM:  Begin
                      { checking for real constants with this should use  }
                      { real DECODING otherwise the compiler will crash!  }
                      Message(assem_w_float_bin_ignored);
                      Consume(AS_BINNUM);
                 end;

      AS_HEXNUM: Begin
                      { checking for real constants with this should use  }
                      { real DECODING otherwise the compiler will crash!  }
                    Message(assem_w_float_hex_ignored);
                    Consume(AS_HEXNUM);
                end;
      AS_OCTALNUM: Begin
                      { checking for real constants with this should use    }
                      { real DECODING otherwise the compiler will crash!    }
                      { xxxToDec using reals could be a solution, but the   }
                      { problem is that these will crash the m68k compiler  }
                      { when compiling -- because of lack of good fpu       }
                      { support.                                           }
                    Message(assem_w_float_octal_ignored);
                    Consume(AS_OCTALNUM);
                  end;
         else
           Begin
             { only write error once. }
             if not errorflag then
              Message(assem_e_invalid_real_const);
             { consume tokens until we find COMMA or SEPARATOR }
             Consume(actasmtoken);
             errorflag := TRUE;
           End;

         end;
      { go to next term }
      if (actasmtoken=AS_COMMA) or (actasmtoken=AS_SEPARATOR) then
        Begin
          if negativ then expr:='-'+expr;
          val(expr,r,code);
          if code<>0 then
            Begin
               r:=0;
               Message(assem_e_invalid_real_const);
               ConcatRealConstant(p,r,typ);
            End
          else
            Begin
              ConcatRealConstant(p,r,typ);
            End;
        end
      else
       Message(assem_e_invalid_real_const);
    Until actasmtoken=AS_SEPARATOR;
  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 the AS_COMMA token.           }
  {*********************************************************************}
  var str:string;
      l: longint;
      code: integer;
  Begin
     Consume(AS_COMMA);
     if (instr.operands[operandnum].ref.scalefactor <> 0)
     and (instr.operands[operandnum].ref.scalefactor <> 1) then
      Message(assem_f_internal_error_in_buildscale);
     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);
    if actasmtoken = AS_RPAREN then
        Consume(AS_RPAREN)
    else
       Message(assem_e_invalid_scaling_value);
    { // .Field.Field ... or separator/comma // }
    if actasmtoken in [AS_COMMA,AS_SEPARATOR] then
    Begin
    end
    else
      Message(assem_e_syntax_error);
  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 the LPAREN token.                }
  { 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 := '';
    Repeat
      Case actasmtoken of
      AS_RPAREN: Begin
                   Message(assem_e_parenthesis_are_not_allowed);
                   Consume(AS_RPAREN);
                 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_LPAREN: Begin
                     if not ErrorFlag then
                        BuildRefExpression := CalculateExpression(expr)
                     else
                        BuildRefExpression := 0;
                     { no longer in an expression }
                     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 BuildReference(var Instr: TInstruction);
  {*********************************************************************}
  { PROCEDURE BuildBracketExpression                                    }
  {  Description: This routine builds up an expression after a LPAREN   }
  {  token is encountered.                                              }
  {   On entry actasmtoken should be equal to AS_LPAREN                 }
  {*********************************************************************}
  { EXIT CONDITION:  On exit the routine should point to either the     }
  {       AS_COMMA or AS_SEPARATOR token.                               }
  {*********************************************************************}
  var
    l:longint;
    code: integer;
    str: string;
  Begin
     Consume(AS_LPAREN);
     initAsmRef(instr);
     Case actasmtoken of
        { // (reg ... // }
        AS_REGISTER: Begin
                        instr.operands[operandnum].ref.base :=
                           findregister(actasmpattern);
                        Consume(AS_REGISTER);
                        { can either be a register or a right parenthesis }
                         { // (reg)       // }
                         if actasmtoken=AS_RPAREN then  Begin
                                       Consume(AS_RPAREN);
                                       if not (actasmtoken in [AS_COMMA,
                                         AS_SEPARATOR]) then
                                       Begin
                                         Message(assem_e_invalid_reference);
                                         { error recovery ... }
                                         while actasmtoken <> AS_SEPARATOR do
                                           Consume(actasmtoken);
                                       end;
                                         exit;
                                     end;
                       { // (reg,reg .. // }
                       { we need a comman here !! }
                       { oops..                   }
                        Consume(AS_COMMA);

                        Case actasmtoken of
                         AS_REGISTER: Begin
                                        instr.operands[operandnum].ref.index :=
                                           findregister(actasmpattern);
                                        Consume(AS_REGISTER);
                                        { check for scaling ... }
                                        case actasmtoken of
                                         AS_RPAREN:
                                               Begin
                                                 Consume(AS_RPAREN);
                                                 if not (actasmtoken in [AS_COMMA,
                                                    AS_SEPARATOR]) then
                                                  Begin
                                                    { error recovery ... }
                                                    Message(assem_e_invalid_reference);
                                                    while actasmtoken <> AS_SEPARATOR do
                                                    Consume(actasmtoken);
                                                  end;
                                                   exit;
                                               end;
                                         AS_COMMA:
                                               Begin
                                                 BuildScaling(instr);
                                               end;
                                         else
                                          Begin
                                             Message(assem_e_invalid_reference_syntax);
                                             while (actasmtoken <> AS_SEPARATOR) do
                                             Consume(actasmtoken);
                                          end;
                                         end; { end case }
                                        end;
                         else
                          Begin
                            Message(assem_e_invalid_reference_syntax);
                            while (actasmtoken <> AS_SEPARATOR) do
                                Consume(actasmtoken);
                          end;
                         end; {end case }
                     end;
        { // (, ...   // }
        AS_COMMA:  { can either be scaling, or index }
                   Begin
                     Consume(AS_COMMA);
                     case actasmtoken of
                       AS_REGISTER: Begin
                                      instr.operands[operandnum].ref.index :=
                                         findregister(actasmpattern);
                                      Consume(AS_REGISTER);
                                        { check for scaling ... }
                                        case actasmtoken of
                                         AS_RPAREN:
                                               Begin
                                                 Consume(AS_RPAREN);
                                                 if not (actasmtoken in [AS_COMMA,
                                                    AS_SEPARATOR]) then
                                                  Begin
                                                    { error recovery ... }
                                                    Message(assem_e_invalid_reference);
                                                    while actasmtoken <> AS_SEPARATOR do
                                                    Consume(actasmtoken);
                                                  end;
                                                   exit;
                                               end;
                                         AS_COMMA:
                                               Begin
                                                 BuildScaling(instr);
                                               end;
                                         else
                                          Begin
                                             Message(assem_e_invalid_reference_syntax);
                                             while (actasmtoken <> AS_SEPARATOR) do
                                             Consume(actasmtoken);
                                          end;
                                         end; {end case }
                                    end;
                       AS_HEXNUM,AS_INTNUM,   { we have to process the scaling }
                       AS_BINNUM,AS_OCTALNUM: { directly here...               }
                                              Begin
                                                  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; { end case }
                                                  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;
                                                  Consume(actasmtoken);
                                                  if actasmtoken <> AS_RPAREN then
                                                  Begin
                                                    Message(assem_e_invalid_scaling_value);
                                                    while actasmtoken <> AS_SEPARATOR do
                                                      Consume(actasmtoken);
                                                  end
                                                  else
                                                  Begin
                                                    Consume(AS_RPAREN);
                                                    if not (actasmtoken in [AS_COMMA,
                                                       AS_SEPARATOR]) then
                                                     Begin
                                                      { error recovery ... }
                                                      Message(assem_e_invalid_reference);
                                                      while actasmtoken <> AS_SEPARATOR do
                                                        Consume(actasmtoken);
                                                     end;
                                                    exit;
                                                  end;
                                              end;
                     else
                       Begin
                          Message(assem_e_invalid_reference_syntax);
                          while (actasmtoken <> AS_SEPARATOR) do
                          Consume(actasmtoken);
                       end;
                     end; { end case }
                   end;

     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;
    hl: plabel;
  Begin
   tempstr := '';
   expr := '';
   case actasmtoken of
   { // Memory reference //  }
     AS_LPAREN:
               Begin
                  initAsmRef(instr);
                  BuildReference(instr);
               end;
   { // Constant expression //  }
     AS_DOLLAR:  Begin
                      Consume(AS_DOLLAR);
                      if not (instr.operands[operandnum].operandtype in [OPR_NONE,OPR_CONSTANT]) then
                       Message(assem_e_invalid_operand_type);
                      { identifiers are handled by BuildExpression }
                      instr.operands[operandnum].operandtype := OPR_CONSTANT;
                      instr.operands[operandnum].val :=BuildExpression;
                 end;
   { // Constant memory offset .              // }
   { // This must absolutely be followed by ( // }
     AS_HEXNUM,AS_INTNUM,AS_MINUS,
     AS_BINNUM,AS_OCTALNUM,AS_PLUS:
                   Begin
                      InitAsmRef(instr);
                      instr.operands[operandnum].ref.offset:=BuildRefExpression;
                      BuildReference(instr);
                   end;
   { // A constant expression, or a Variable ref. // }
     AS_ID:  Begin
              { // Local label.                      // }
              if (actasmpattern[1] ='.') and (actasmpattern[2] = 'L') then
              Begin
                  Begin
                    delete(actasmpattern,1,1);
                    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
                 { 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
                         { look for special symbols ... }
                         if actasmpattern = '__RESULT' then
                             SetUpResult(instr,operandnum)
                         else
                         if actasmpattern = '__SELF' then
                         Begin
                           if assigned(procinfo._class) then
                             Begin
                               instr.operands[operandnum].operandtype := OPR_REFERENCE;
                               instr.operands[operandnum].ref.offset :=
                                 procinfo.ESI_offset;
                               instr.operands[operandnum].ref.base :=
                                 procinfo.framepointer;
                             end
                           else
                             Message(assem_e_cannot_use___SELF_outside_methode);
                         end
                         else
                         if actasmpattern = '__OLDEBP' then
                         Begin
                           if lexlevel>2 then
                             Begin
                               instr.operands[operandnum].operandtype := OPR_REFERENCE;
                               instr.operands[operandnum].ref.offset :=
                                 procinfo.framepointer_offset;
                               instr.operands[operandnum].ref.base :=
                                 procinfo.framepointer;
                             end
                           else
                             Message(assem_e_cannot_use___OLDEBP_outside_nested_procedure);
                         end { endif actasmpattern = '__OLDEBP' }
                         else
                         { check for direct symbolic names   }
                         { only if compiling the system unit }
                         if (cs_compilesystem in aktswitches) then
                         begin
                           if not SearchDirectVar(instr,actasmpattern,operandnum) then
                           Begin
                            { not found, finally ... add it anyways ... }
                            Message1(assem_w_id_supposed_external,actasmpattern);
                            instr.operands[operandnum].ref.symbol := newpasstr(actasmpattern);
                           end;
                         end
                         else
                          Message1(assem_e_unknown_id,actasmpattern);
                      end;
                     expr := actasmpattern;
                     Consume(AS_ID);
                       case actasmtoken of
                           AS_LPAREN: { indexing }
                                        BuildReference(instr);
                           AS_SEPARATOR,AS_COMMA: ;
                       else
                           Message(assem_e_syntax_error);
                       end; { end case }
                   end; { end if }
               end; { end if }
             end; { end this case }
   { // 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);
                      initAsmRef(instr);
                      instr.operands[operandnum].ref.segment := findsegment(tempstr);
                      { here we can have either an identifier }
                      { or a constant, where either can be    }
                      { followed by a parenthesis...          }
                      { // Constant memory offset .              // }
                      { // This must absolutely be followed by ( // }
                      case actasmtoken of
                        AS_HEXNUM,AS_INTNUM,AS_MINUS,
                        AS_BINNUM,AS_OCTALNUM,AS_PLUS
                        :  Begin
                                       instr.operands[operandnum].
                                       ref.offset:=BuildRefExpression;
                                       BuildReference(instr);
                                      end;
                        AS_LPAREN: BuildReference(instr);
                        { only a variable is allowed ... }
                        AS_ID: Begin
                                 { is it a normal variable ? }
                                 if not CreateVarInstr(instr,actasmpattern,operandnum)
                                 then
                                 begin
                                  {  check for direct symbolic names   }
                                   { only if compiling the system unit }
                                   if (cs_compilesystem in aktswitches) then
                                   begin
                                     if not SearchDirectVar(instr,actasmpattern,operandnum) then
                                        Message(assem_e_invalid_seg_override);
                                   end
                                   else
                                        Message(assem_e_invalid_seg_override);
                                 end;
                                 Consume(actasmtoken);
                                 case actasmtoken of
                                   AS_SEPARATOR,AS_COMMA: ;
                                   AS_LPAREN: BuildReference(instr);
                                 else
                                  Begin
                                   Message(assem_e_invalid_seg_override);
                                   Consume(actasmtoken);
                                  end;
                                 end; {end case }
                               end;
                      else
                          Begin
                            Message(assem_e_invalid_seg_override);
                            Consume(actasmtoken);
                          end;
                      end; { end case }
                   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;
     AS_SEPARATOR, AS_COMMA: ;
    else
     Begin
      Message(assem_e_syn_opcode_operand);
      Consume(actasmtoken);
     end;
  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
      Repeat
        Case actasmtoken of
          AS_STRING: Begin
                      if maxvalue = $ff then
                         strlength := 1
                      else
                         Message(assem_e_string_not_allowed_as_const);
                      expr := actasmpattern;
                      if length(expr) > 1 then
                       Message(assem_e_string_not_allowed_as_const);
                      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 BuildStringConstant(asciiz: boolean);
  {*********************************************************************}
  { PROCEDURE BuildStringConstant                                       }
  {  Description: Takes care of a ASCII, or ASCIIZ directive.           }
  {   asciiz: boolean -> if true then string will be null terminated.   }
  {*********************************************************************}
  { EXIT CONDITION:  On exit the routine should point to AS_SEPARATOR.  }
  { On ENTRY: Token should point to AS_STRING                           }
  {*********************************************************************}
  var
   expr: string;
   errorflag : boolean;
  Begin
      errorflag := FALSE;
      Repeat
        Case actasmtoken of
          AS_STRING: Begin
                      expr:=actasmpattern;
                      if asciiz then
                       expr:=expr+#0;
                      ConcatPasString(p,expr);
                      Consume(AS_STRING);
                    end;
          AS_COMMA:  BEGIN
                       Consume(AS_COMMA);
                     END;
          AS_SEPARATOR: ;
        else
         Begin
          Consume(actasmtoken);
          if not errorflag then
           Message(assem_e_invalid_string_expression);
          errorflag := TRUE;
         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);
    end;
    { //  opcode                          // }
    { allow for newline as in gas styled syntax }
    { under DOS you get two AS_SEPARATOR !! }
    while actasmtoken=AS_SEPARATOR do
      Consume(AS_SEPARATOR);
    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);
      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;
         { // End of asm operands for this opcode // }
         AS_SEPARATOR: ;
       else
         BuildOperand(instr);
     end; { end case }
    end; { end while }
  end;




  Function Assemble: Ptree;
  {*********************************************************************}
  { PROCEDURE Assemble;                                                 }
  {  Description: Parses the att assembler syntax, parsing is done      }
  {  according to GAs rules.                                            }
  {*********************************************************************}
  Var
   hl: plabel;
   labelptr,nextlabel : pasmlabel;
   commname : string;
   store_p : paasmoutput;

  Begin
    Message(assem_d_start_att);
    firsttoken := TRUE;
    operandnum := 0;
    { sets up all opcode and register tables in uppercase }
    if not _asmsorted then
    Begin
      SetupTables;
      _asmsorted := TRUE;
    end;
    p:=new(paasmoutput,init);
    { save pointer code section }
    store_p:=p;
    { 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 instruction 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,'.L'+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
                     { when looking for Pascal labels, these must }
                     { be in uppercase.                           }
                     if SearchLabel(upper(actasmpattern),hl) then
                       ConcatLabel(p,A_LABEL, hl)
                     else
                     Begin
                       if (cs_compilesystem in aktswitches) then
                       begin
                          Message1(assem_e_unknown_label_identifer,actasmpattern);
                          { once again we don't know what it represents }
                          { so we simply concatenate it                 }
                          ConcatLocal(p,actasmpattern);
                       end
                       else
                        Message1(assem_e_unknown_label_identifer,actasmpattern);
                     end;
                     Consume(AS_LABEL);
                 end;
        AS_DW:   Begin
                   Consume(AS_DW);
                   BuildConstant($ffff);
                 end;
        AS_DATA: Begin
                 { -- this should only be allowed for system development -- }
                 {    i think this should be fixed in the dos unit, and     }
                 {    not here.                                             }
                   if (cs_compilesystem in aktswitches) then
                       p:=datasegment
                   else
                       Message(assem_e_switching_sections_not_allowed);
                   Consume(AS_DATA);
                 end;
        AS_TEXT: Begin
                 { -- this should only be allowed for system development -- }
                 {    i think this should be fixed in the dos unit, and     }
                 {    not here.                                             }
                   if (cs_compilesystem in aktswitches) then
                        p:=store_p
                   else
                       Message(assem_e_switching_sections_not_allowed);
                   Consume(AS_TEXT);
                 end;
        AS_DB:   Begin
                  Consume(AS_DB);
                  BuildConstant($ff);
                end;
        AS_DD:   Begin
                 Consume(AS_DD);
                 BuildConstant($ffffffff);
                end;
        AS_DQ:  Begin
                 Consume(AS_DQ);
                 BuildRealConstant(s64bit);
                end;
        AS_SINGLE:   Begin
                 Consume(AS_SINGLE);
                 BuildRealConstant(s32real);
                end;
        AS_DOUBLE:   Begin
                 Consume(AS_DOUBLE);
                 BuildRealConstant(s64real);
                end;
        AS_EXTENDED:   Begin
                 Consume(AS_EXTENDED);
                 BuildRealConstant(s80real);
                end;
        AS_GLOBAL:
                  Begin
                   { normal units should not be able to declare }
                   { direct label names like this... anyhow     }
                   { procedural calls in asm blocks are         }
                   { supposedely replaced automatically         }
                   if (cs_compilesystem in aktswitches) then
                   begin
                     Consume(AS_GLOBAL);
                      if actasmtoken <> AS_ID then
                        Message(assem_e_invalid_global_def)
                      else
                        ConcatPublic(p,actasmpattern);
                      Consume(actasmtoken);
                      if actasmtoken <> AS_SEPARATOR then
                      Begin
                        Message(assem_e_line_separator_expected);
                        while actasmtoken <> AS_SEPARATOR do
                         Consume(actasmtoken);
                      end;
                   end
                   else
                   begin
                     Message(assem_w_globl_not_supported);
                     while actasmtoken <> AS_SEPARATOR do
                       Consume(actasmtoken);
                   end;
                  end;
        AS_ALIGN: Begin
                    Message(assem_w_align_not_supported);
                    while actasmtoken <> AS_SEPARATOR do
                     Consume(actasmtoken);
                  end;
        AS_ASCIIZ: Begin
                     Consume(AS_ASCIIZ);
                     BuildStringConstant(TRUE);
                   end;
        AS_ASCII: Begin
                    Consume(AS_ASCII);
                    BuildStringConstant(FALSE);
                  end;
        AS_LCOMM: Begin
                 { -- this should only be allowed for system development -- }
                 { -- otherwise may mess up future enhancements we might -- }
                 { -- add.                                               -- }
                   if (cs_compilesystem in aktswitches) then
                   begin
                     Consume(AS_LCOMM);
                      if actasmtoken <> AS_ID then
                        begin
                           Message(assem_e_invalid_lcomm_def);
                           { error recovery }
                           while actasmtoken <> AS_SEPARATOR do
                            Consume(actasmtoken);
                        end
                      else
                        begin
                           commname:=actasmpattern;
                           Consume(AS_COMMA);
                           ConcatLocalBss(actasmpattern,BuildExpression);
                           if actasmtoken <> AS_SEPARATOR then
                             Begin
                                Message(assem_e_line_separator_expected);
                                while actasmtoken <> AS_SEPARATOR do
                                  Consume(actasmtoken);
                             end;
                        end;
                   end
                   else
                   begin
                        Message(assem_w_lcomm_not_supported);
                        while actasmtoken <> AS_SEPARATOR do
                          Consume(actasmtoken);
                   end;
                  end;
        AS_COMM: Begin
                 { -- this should only be allowed for system development -- }
                 { -- otherwise may mess up future enhancements we might -- }
                 { -- add.                                               -- }
                   if (cs_compilesystem in aktswitches) then
                   begin
                     Consume(AS_LCOMM);
                      if actasmtoken <> AS_ID then
                        begin
                           Message(assem_e_invalid_comm_def);
                           { error recovery }
                           while actasmtoken <> AS_SEPARATOR do
                            Consume(actasmtoken);
                        end
                      else
                        begin
                           commname:=actasmpattern;
                           Consume(AS_COMMA);
                           ConcatGlobalBss(actasmpattern,BuildExpression);
                           if actasmtoken <> AS_SEPARATOR then
                           Begin
                             Message(assem_e_line_separator_expected);
                             while actasmtoken <> AS_SEPARATOR do
                              Consume(actasmtoken);
                           end;
                        end;
                   end
                   else
                   begin
                      Message(assem_w_comm_not_supported);
                      while actasmtoken <> AS_SEPARATOR do
                       Consume(actasmtoken);
                   end;
                 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;
    While labelptr <> nil do
      Begin
         nextlabel:=labelptr^.next;
         if not labelptr^.emitted  then
          Message1(assem_e_local_sym_not_found_in_asm_statement,'.L'+labelptr^.name^);
         labelptr:=nextlabel;
      end;
  end;
  if p<>store_p then
    begin
       Message(assem_e_assembler_code_not_returned_to_text);
       p:=store_p;
    end;
  assemble := genasmnode(p);
  labellist.done;
  Message(assem_d_finish_att);
end;


var
 old_exit: pointer;

    procedure ratti386_exit;{$ifndef FPC}far;{$endif}

      begin
         if assigned(iasmops) then
           dispose(iasmops);
         exitproc:=old_exit;
      end;


Begin
 line:=''; { Initialization of line variable.
             No 255 char coonst string in version 0.9.1 MVC}
 old_exit := exitproc;
 exitproc := @ratti386_exit;
end.

{
  $Log$
  Revision 1.1  1998-03-25 11:18:15  root
  Initial revision

  Revision 1.21  1998/03/10 16:27:44  pierre
    * better line info in stabs debug
    * symtabletype and lexlevel separated into two fields of tsymtable
    + ifdef MAKELIB for direct library output, not complete
    + ifdef CHAINPROCSYMS for overloaded seach across units, not fully
      working
    + ifdef TESTFUNCRET for setting func result in underfunction, not
      working

  Revision 1.20  1998/03/10 01:17:27  peter
    * all files have the same header
    * messages are fully implemented, EXTDEBUG uses Comment()
    + AG... files for the Assembler generation

  Revision 1.19  1998/03/09 12:58:13  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.18  1998/03/04 17:34:01  michael
  + Changed ifdef FPK to ifdef FPC

  Revision 1.17  1998/03/03 22:38:30  peter
    * the last 3 files

  Revision 1.16  1998/03/02 01:49:21  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.15  1998/02/13 10:35:42  daniel
  * Made Motorola version compilable.
  * Fixed optimizer

  Revision 1.14  1998/02/12 11:50:41  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.13  1998/02/07 18:03:55  carl
    + fwait warning for emulation

  Revision 1.12  1998/01/19 03:10:52  carl
    * bugfix number 78

  Revision 1.11  1998/01/09 19:24:00  carl
  + externals are now added if identifier is not found

  Revision 1.10  1997/12/14 22:43:25  florian
    + command line switch -Xs for DOS (passes -s to the linker to strip symbols from
      executable)
    * some changes of Carl-Eric implemented

  Revision 1.9  1997/12/09 14:07:14  carl
  + added better error size checkimg -- otherwise would cause problems
    with intasmi3
  * bugfixes as in rai386
  * BuildRealConstant gave out Overflow errors (hex/bin/octal should be
    directly decoded into real)
  * bugfix of MOVSX/MOVZX instruction
  * ConcatOpCode op_csymbol gave out a Runerrore 216 under each test
    I performed, or output a nil symbol -- so removed.
  * All identifiers must be in uppercase!!!
    (except local labels and directives)
  + supervisor stuff only possible when compiling the system unit

  Revision 1.7  1997/12/04 12:21:09  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/12/01 17:42:56  pierre
     + added some more functionnality to the assembler parser

  Revision 1.5  1997/11/28 15:43:23  florian
  Fixed stack ajustment bug, 0.9.8 compiles now 0.9.8 without problems.

  Revision 1.4  1997/11/28 15:39:46  carl
  - removed reference to WriteLn and replaced in inasmxxx
  * uncommented firstop and lastop (otherwise can cause bugs)

  Revision 1.3  1997/11/28 14:26:24  florian
  Fixed some bugs

  Revision 1.2  1997/11/28 12:05:44  michael
  Changed comment delimiter to braces
  CHanged use of ord to typecast with longint
  Changed line constant to variable. Added initialization. v0.9.1 chokes
  on 255 length constant strings.
  Boolean expressions are now non-redundant.

  Revision 1.1.1.1  1997/11/27 08:33:01  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

  14th november 1997:
   * fixed bug regarding ENTER and push imm8 instruction (CEC)
   + fixed conflicts with fpu instructions. (CEC).
   + adding real const support. (PM).

}

