{
    $Id$
    Copyright (c) 1998 Carl Eric Codere

    This unit implements some support routines for assembler parsing

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

{*************************************************************************}
{  This unit implements some objects as well as utilities which will be   }
{  used by all inline assembler parsers (non-processor specific).         }
{                                                                         }
{  Main routines/objects herein:                                          }
{  o Object TExprParse is a simple expression parser to resolve assembler }
{    expressions. (Based generally on some code by Thai Tran from SWAG).  }
{  o Object TInstruction is a simple object used for instructions         }
{  o Record TOperand is a simple record used to store information on      }
{    each operand.                                                        }
{  o String conversion routines from octal,binary and hex to decimal.     }
{  o A linked list object/record for local labels                         }
{  o Routines for retrieving symbols (local and global)                   }
{  o Object for a linked list of strings (with duplicate strings not      }
{    allowed).                                                            }
{  o Non-processor dependant routines for adding instructions to the      }
{    instruction list.                                                    }
{*************************************************************************}


{--------------------------------------------------------------------}
{ LEFT TO DO:                                                        }
{ o Fix the remaining bugs in the expression parser, such as with    }
{     4+-3                                                           }
{ o Add support for local typed constants search.                    }
{ o Add support for private/protected fields in method assembler     }
{    routines.                                                       }
{--------------------------------------------------------------------}
Interface

Uses
  symtable,aasm,hcodegen,verbose,systems,globals,files,strings,
  cobjects,
{$ifdef i386}
  i386;
{$endif}
{$ifdef m68k}
   m68k;
{$endif}


Const
  RPNMax = 10;             { I think you only need 4, but just to be safe }
  OpMax  = 25;

  maxoperands = 3;         { Maximum operands for assembler instructions }


Type


  {---------------------------------------------------------------------}
  {                     Label Management types                          }
  {---------------------------------------------------------------------}


    PAsmLabel = ^TAsmLabel;
    PString = ^String;

    { Each local label has this structure associated with it }
    TAsmLabel = record
      name: PString;    { pointer to a pascal string name of label }
      lab: PLabel;      { pointer to a label as defined in FPC     }
      emitted: boolean; { as the label itself been emitted ?       }
      next: PAsmLabel;  { next node                                }
    end;

    TAsmLabelList = Object
    public
      First: PAsmLabel;
      Constructor Init;
      Destructor Done;
      Procedure Insert(s:string; lab: PLabel; emitted: boolean);
      Function Search(const s: string): PAsmLabel;
    private
      Last: PAsmLabel;
      Function NewPasStr(s:string): PString;
    end;



  {---------------------------------------------------------------------}
  {                 Instruction management types                        }
  {---------------------------------------------------------------------}

  toperandtype = (OPR_NONE,OPR_REFERENCE,OPR_CONSTANT,OPR_REGISTER,OPR_LABINSTR,
                  OPR_REGLIST);

    { When the TReference field isintvalue = TRUE }
    { then offset points to an ABSOLUTE address   }
    { otherwise isintvalue should always be false }

    { Special cases:                              }
    {   For the M68k Target, size is UNUSED, the  }
    {   opcode determines the size of the         }
    {   instruction.                              }
    {  DIVS/DIVU/MULS/MULU of the form dn,dn:dn   }
    {  is stored as three operands!!              }


    { Each instruction operand can be of this type }
    TOperand = record
      size: topsize;
      opinfo: longint; { ao_xxxx flags }
      case operandtype:toperandtype of
       { the size of the opr_none field should be at least equal to each }
       { other field as to facilitate initialization.                    }
       OPR_NONE: (l: array[1..sizeof(treference)] of byte);
       OPR_REFERENCE: (ref:treference);
       OPR_CONSTANT:  (val: longint);
       OPR_REGISTER:  (reg:tregister);
       OPR_LABINSTR: (hl: plabel);
       { Register list such as in the movem instruction }
       OPR_REGLIST:  (list: set of tregister);
    end;



    TInstruction = object
    public
      operands: array[1..maxoperands] of TOperand;
      { if numops = zero, a size may still be valid in operands[1] }
      { it still should be checked.                                }
      numops: byte;
      { set to TRUE if the instruction is labeled.                }
      labeled: boolean;
      { This is used for instructions such A_CMPSB... etc, to determine }
      { the size of the instruction.                                    }
      stropsize: topsize;
      procedure init;
      { sets up the prefix field with the instruction pointed to in s }
      procedure addprefix(tok: tasmop);
      { sets up the instruction with the instruction pointed to in s }
      procedure addinstr(tok: tasmop);
      { get the current instruction of this object }
      function getinstruction: tasmop;
      { get the current prefix of this instruction }
      function getprefix: tasmop;
    private
      prefix: tasmop;
      instruction: tasmop;
    end;




  {---------------------------------------------------------------------}
  {                   Expression parser types                           }
  {---------------------------------------------------------------------}

  { expression parser error codes }
  texpr_error =
  (zero_divide,       { divide by zero.     }
   stack_overflow,    { stack overflow.     }
   stack_underflow,   { stack underflow.    }
   invalid_number,    { invalid conversion  }
   invalid_op);       { invalid operator    }


   TExprOperator = record
    ch: char;           { operator }
    is_prefix: boolean; { was it a prefix, possible prefixes are +,- and not }
   end;

  String15 = String[15];
  {**********************************************************************}
  { The following operators are supported:                              }
  {  '+' : addition                                                     }
  {  '-' : subtraction                                                  }
  {  '*' : multiplication                                               }
  {  '/' : modulo division                                              }
  {  '^' : exclusive or                                                 }
  {  '<' : shift left                                                   }
  {  '>' : shift right                                                  }
  {  '&' : bitwise and                                                  }
  {  '|' : bitwise or                                                   }
  {  '~' : bitwise complement                                           }
  {  '%' : modulo division                                              }
  {  nnn: longint numbers                                               }
  {  ( and ) parenthesis                                                }
  {**********************************************************************}

  TExprParse = Object
    public
     Constructor Init;
     Destructor Done;
     Function Evaluate(Expr:  String): longint;
     Procedure Error(anerror: texpr_error); virtual;
     Function Priority(_Operator: Char): Integer; virtual;
    private
     RPNStack   : Array[1..RPNMax] of longint;        { Stack For RPN calculator }
     RPNTop     : Integer;
     OpStack    : Array[1..OpMax] of TExprOperator;    { Operator stack For conversion }
     OpTop      : Integer;
     Procedure RPNPush(Num: Longint);
     Function RPNPop: Longint;
     Procedure RPNCalc(token: String15; prefix: boolean);
     Procedure OpPush(_Operator: char; prefix: boolean);
     { In reality returns TExprOperaotr }
     Procedure OpPop(var _Operator:TExprOperator);
  end;


  {---------------------------------------------------------------------}
  {                     String routines                                 }
  {---------------------------------------------------------------------}


  {*********************************************************************}
  { PROCEDURE PadZero;                                                  }
  {  Description: Makes sure that the string specified is of the given  }
  {  length, by padding it with binary zeros, or truncating if necessary}
  {  Remark: The return value is determined BEFORE any eventual padding.}
  {  Return Value: TRUE  = if length of string s was <= then n          }
  {                FALSE = if length of string s was > then n           }
  {*********************************************************************}
  Function PadZero(Var s: String; n: byte): Boolean;

  { Converts an Hex digit string to a Decimal string                      }
  { Returns '' if there was an error.                                     }
  Function HexToDec(const S:String): String;

  { Converts a binary digit string to a Decimal string                    }
  { Returns '' if there was an error.                                     }
  Function BinaryToDec(const S:String): String;

  { Converts an octal digit string to a Decimal string                    }
  { Returns '' if there was an error.                                     }
  Function OctalToDec(const S:String): String;

  { Converts a string containing C styled escape sequences to }
  { a pascal style string.                                    }
  Function EscapeToPascal(const s:string): string;

  Procedure ConcatPasString(p : paasmoutput;s:string);
  { Writes the string s directly to the assembler output }
  Procedure ConcatDirect(p : paasmoutput;s:string);


  {---------------------------------------------------------------------}
  {                     Symbol helper routines                          }
  {---------------------------------------------------------------------}

  Function GetTypeOffset(const base: string; const field: string;
    Var Offset: longint):boolean;
  Function GetVarOffset(const base: string; const field: string;
    Var Offset: longint):boolean;
  Function SearchIConstant(const s:string; var l:longint): boolean;
  Function SearchLabel(const s: string; var hl: plabel): boolean;
  Function CreateVarInstr(var Instr: TInstruction; const hs:string;
     operandnum:byte):boolean;
  {*********************************************************************}
  { FUNCTION NewPasStr(s:string): PString                               }
  {  Description: This routine allocates a string on the heap and       }
  {  returns a pointer to the allocated string.                         }
  {                                                                     }
  {  Remarks: The string allocated should not be modified, since it's   }
  {  length will be less then 255.                                      }
  {  Remarks: It is assumed that HeapError will be called if an         }
  {  allocation fails.                                                  }
  {*********************************************************************}
  Function newpasstr(s: string): Pointer;
  Procedure SetupResult(Var Instr:TInstruction; operandnum: byte);
  Procedure FWaitWarning;

  {---------------------------------------------------------------------}
  {                  Instruction generation routines                    }
  {---------------------------------------------------------------------}

  { swaps in the case of a 2/3 operand opcode the destination and the    }
  { source as to put it in AT&T style instruction format.                }
  Procedure SwapOperands(Var instr: TInstruction);
  Procedure ConcatLabel(p : paasmoutput;op : tasmop;var l : plabel);
  Procedure ConcatConstant(p : paasmoutput;value: longint; maxvalue: longint);
  Procedure ConcatRealConstant(p : paasmoutput;value: bestreal; real_typ : tfloattype);
  Procedure ConcatString(p : paasmoutput;s:string);
  Procedure ConcatPublic(p:paasmoutput;const s : string);
  Procedure ConcatLocal(p:paasmoutput;const s : string);
  Procedure ConcatGlobalBss(const s : string;size : longint);
  Procedure ConcatLocalBss(const s : string;size : longint);
  { add to list of external labels }
  Procedure ConcatExternal(const s : string;typ : texternal_typ);
  { add to internal list of labels }
  Procedure ConcatInternal(const s : string;typ : texternal_typ);

Implementation

{*************************************************************************}
{                         Expression Parser                               }
{*************************************************************************}

Constructor TExprParse.Init;
Begin
end;

Procedure TExprParse.Error(anerror:texpr_error);
var
  t : tmsgconst;
Begin
  case anerror of
  zero_divide: t:=assem_f_ev_zero_divide;
  stack_overflow: t:=assem_f_ev_stack_overflow;
  stack_underflow: t:=assem_f_ev_stack_underflow;
  invalid_number: t:=assem_f_ev_invalid_number;
  invalid_op: t:=assem_f_ev_invalid_op;
  else
   t:=assem_f_ev_unknown;
  end;
  Message(t);
end;

Procedure TExprParse.RPNPush(Num : longint); { Add an operand to the top of the RPN stack }
begin
  if RPNTop < RPNMax then
  begin
    Inc(RPNTop);
    RPNStack[RPNTop] := Num;
  end
  else
    Error(stack_overflow); { Put some error handler here }
end;




Function TExprParse.RPNPop : longint;       { Get the operand at the top of the RPN stack }
begin
  if RPNTop > 0 then
  begin
    RPNPop := RPNStack[RPNTop];
    Dec(RPNTop);
  end
  else  { Put some error handler here }
   Error(stack_underflow);
end;

Procedure TExprParse.RPNCalc(Token : String15; prefix:boolean);                       { RPN Calculator }
Var
  Temp  : longint;
  LocalError : Integer;
begin
{  Write(Token, ' ');              This just outputs the RPN expression }

  if (Length(Token) = 1) and (Token[1] in ['+', '-', '*', '/','&','|','%','^','~','<','>']) then
  Case Token[1] of                                   { Handle operators }
    '+' : Begin
       if prefix then
       else
          RPNPush(RPNPop + RPNPop);
     end;
    '-' : Begin
      if prefix then
         RPNPush(-(RPNPop))
      else
         RPNPush(RPNPop - RPNPop);
     end;
    '*' : RPNPush(RPNPop * RPNPop);
    '&' : RPNPush(RPNPop AND RPNPop);
    '|' : RPNPush(RPNPop OR RPNPop);
    '~' : RPNPush(NOT RPNPop);
    '<' : RPNPush(RPNPop SHL RPNPop);
    '>' : RPNPush(RPNPop SHR RPNPop);
    '%' : begin
      Temp := RPNPop;
      if Temp <> 0 then
       RPNPush(RPNPop mod Temp)
      else Error(zero_divide); { Handle divide by zero error }
     end;
    '^' : RPNPush(RPNPop XOR RPNPop);
    '/' :
    begin
      Temp := RPNPop;
      if Temp <> 0 then
   RPNPush(RPNPop div Temp)
      else  Error(zero_divide);{ Handle divide by 0 error }
    end;
  end
  else
  begin                   { Convert String to number and add to stack }
    if token='-2147483648' then
      begin
         temp:=$80000000;
         localerror:=0;
      end
    else
      Val(Token, Temp, LocalError);
    if LocalError = 0 then
      RPNPush(Temp)
    else  Error(invalid_number);{ Handle error }
  end;
end;

Procedure TExprParse.OpPush(_Operator : char;prefix: boolean);  { Add an operator onto top of the stack }
begin
  if OpTop < OpMax then
  begin
    Inc(OpTop);
    OpStack[OpTop].ch := _Operator;
    OpStack[OpTop].is_prefix := prefix;
  end
  else Error(stack_overflow); { Put some error handler here }
end;

Procedure TExprParse.OpPop(var _Operator:TExprOperator);               { Get operator at the top of the stack }
begin
  if OpTop > 0 then
  begin
    _Operator := OpStack[OpTop];
    Dec(OpTop);
  end
  else Error(stack_underflow); { Put some error handler here }
end;

Function TExprParse.Priority(_Operator : Char) : Integer; { Return priority of operator }
{ The greater the priority, the higher the precedence }
begin
  Case _Operator OF
    '('      : Priority := 0;
    '+', '-' : Priority := 1;
    '*', '/','%','<','>' : Priority := 2;
    '|','&','^','~': Priority := 0;
    else  Error(invalid_op);{ More error handling }
  end;
end;

Function TExprParse.Evaluate(Expr : String):longint;
Var
  I     : Integer;
  Token : String15;
  opr: TExprOperator;
begin
  OpTop  := 0;                                              { Reset stacks }
  RPNTop := 0;
  Token  := '';

  For I := 1 to Length(Expr) DO

     if Expr[I] in ['0'..'9'] then
      begin       { Build multi-digit numbers }
   Token := Token + Expr[I];
   if I = Length(Expr) then          { Send last one to calculator }
      RPNCalc(Token,false);
      end
     else
     if Expr[I] in ['+', '-', '*', '/', '(', ')','^','&','|','%','~','<','>'] then
      begin
       if Token <> '' then
   begin        { Send last built number to calc. }
    RPNCalc(Token,false);
    Token := '';
   end;

     Case Expr[I] OF
       '(' : OpPush('(',false);
       ')' :
     begin
        While OpStack[OpTop].ch <> '(' DO
        Begin
          OpPop(opr);
          RPNCalc(opr.ch,opr.is_prefix);
        end;
        OpPop(opr);                          { Pop off and ignore the '(' }
     end;

   '+','-','~':  Begin
      While (OpTop > 0) AND
          (Priority(Expr[I]) <= Priority(OpStack[OpTop].ch)) DO
        Begin
             OpPop(opr);
             RPNCalc(opr.ch,opr.is_prefix);
        end;
       { if start of expression then surely a prefix }
       { or if previous char was also an operator    }
       { push it and don't evaluate normally         }
       { workaround for -2147483648 }
       if (expr[I]='-') and (expr[i+1] in ['0'..'9']) then
         begin
            token:='-';
            expr[i]:='+';
         end;
       if (I = 1) or (not (Expr[I-1] in ['0'..'9','(',')'])) then
         OpPush(Expr[I],true)
       else
         OpPush(Expr[I],false);
      end;

      '*', '/','^','|','&','%','<','>' :
    begin
      While (OpTop > 0) AND
          (Priority(Expr[I]) <= Priority(OpStack[OpTop].ch)) DO
        Begin
             OpPop(opr);
             RPNCalc(opr.ch,opr.is_prefix);
        end;
        OpPush(Expr[I],false);
    end;
     end; { Case }
  end
    else Error(invalid_op);
      { Handle bad input error }

  While OpTop > 0 do                     { Pop off the remaining operators }
  Begin
    OpPop(opr);
    RPNCalc(opr.ch,opr.is_prefix);
  end;

  { The result is stored on the top of the stack }
  Evaluate := RPNPop;
end;


Destructor TExprParse.Done;
Begin
end;


{*************************************************************************}
{                         String conversions/utils                        }
{*************************************************************************}

  Function newpasstr(s: string): Pointer;
  Var
   StrPtr: PString;
  Begin
    GetMem(StrPtr, length(s)+1);
    Move(s,StrPtr^,length(s)+1);
    newpasstr:= Strptr;
  end;


  Function EscapeToPascal(const s:string): string;
  { converts a C styled string - which contains escape }
  { characters to a pascal style string.               }
  var
   i,j: word;
   str: string;
   temp: string;
   value: byte;
   code: integer;
  Begin
   str:='';
   i:=1;
   j:=1;
   repeat
     if s[i] = '\' then
     Begin
      Inc(i);
      if i > 255 then
      Begin
       EscapeToPascal:=str;
       exit;
      end;
      case s[i] of
       '\': insert('\',str,j);
       'b': insert(#08,str,j);
       'f': insert(#12,str,j);
       'n': insert(#10,str,j);
       'r': insert(#13,str,j);
       't': insert(#09,str,j);
       '"': insert('"',str,j);
       { octal number }
       '0'..'7': Begin
                  temp:=s[i];
                  temp:=temp+s[i+1];
                  temp:=temp+s[i+2];
                  inc(i,2);
                  val(octaltodec(temp),value,code);
                  if (code <> 0) then
                   Message(assem_w_invalid_numeric);
                  insert(chr(value),str,j);
                 end;
     { hexadecimal number }
     'x': Begin
            temp:=s[i+1];
            temp:=temp+s[i+2];
            inc(i,2);
            val(hextodec(temp),value,code);
            if (code <> 0) then
             Message(assem_w_invalid_numeric);
            insert(chr(value),str,j);
          end;
     else
      Begin
         Message1(assem_e_escape_seq_ignored,s[i]);
         insert(s[i],str,j);
      end;
    end; {end case }
    Inc(i);
   end
   else
   Begin
    Insert(s[i],str,j);
    Inc(i);
    if i > 255 then
    Begin
     EscapeToPascal:=str;
     exit;
    end;
   end;
   Inc(j);
 until (i > length(s)) or (j > 255);
 EscapeToPascal:=str;
end;



  Function OctalToDec(const S:String): String;
  { Converts an octal string to a Decimal string }
  { Returns '' if there was an error.            }
  var vs: longint;
    c: byte;
    st: string;
  Begin
   vs := 0;
   for c:=1 to length(s) do
   begin
     case s[c] of
     '0': vs:=vs shl 3;
     '1': vs:=vs shl 3+1;
     '2': vs:=vs shl 3+2;
     '3': vs:=vs shl 3+3;
     '4': vs:=vs shl 3+4;
     '5': vs:=vs shl 3+5;
     '6': vs:=vs shl 3+6;
     '7': vs:=vs shl 3+7;
    else
      begin
        OctalToDec := '';
        exit;
      end;
    end;
   end;
     str(vs,st);
     OctalToDec := st;
  end;

  Function BinaryToDec(const S:String): String;
  { Converts a binary string to a Decimal string }
  { Returns '' if there was an error.            }
  var vs: longint;
    c: byte;
    st: string;
  Begin
   vs := 0;
   for c:=1 to length(s) do
   begin
     if s[c] = '0' then
       vs:=vs shl 1
     else
     if s[c]='1' then
       vs:=vs shl 1+1
     else
       begin
         BinaryToDec := '';
         exit;
       end;
   end;
     str(vs,st);
     BinaryToDec := st;
  end;


  Function HexToDec(const S:String): String;
  var vs: longint;
    c: byte;
    st: string;
  Begin
   vs := 0;
   for c:=1 to length(s) do
   begin
     case upcase(s[c]) of
     '0': vs:=vs shl 4;
     '1': vs:=vs shl 4+1;
     '2': vs:=vs shl 4+2;
     '3': vs:=vs shl 4+3;
     '4': vs:=vs shl 4+4;
     '5': vs:=vs shl 4+5;
     '6': vs:=vs shl 4+6;
     '7': vs:=vs shl 4+7;
     '8': vs:=vs shl 4+8;
     '9': vs:=vs shl 4+9;
     'A': vs:=vs shl 4+10;
     'B': vs:=vs shl 4+11;
     'C': vs:=vs shl 4+12;
     'D': vs:=vs shl 4+13;
     'E': vs:=vs shl 4+14;
     'F': vs:=vs shl 4+15;
    else
      begin
        HexToDec := '';
        exit;
      end;
    end;
   end;
     str(vs,st);
     HexToDec := st;
  end;

  Function PadZero(Var s: String; n: byte): Boolean;
  Begin
    PadZero := TRUE;
    { Do some error checking first }
    if Length(s) = n then
      exit
    else
    if Length(s) > n then
    Begin
      PadZero := FALSE;
      delete(s,n+1,length(s));
      exit;
    end
    else
      PadZero := TRUE;
    { Fill it up with the specified character }
    fillchar(s[length(s)+1],n-1,#0);
    s[0] := chr(n);
  end;

{*************************************************************************}
{                          Instruction utilities                          }
{*************************************************************************}

 Procedure TInstruction.init;
 var
  k: integer;
 Begin
  numops := 0;
  labeled := FALSE;
  stropsize := S_NO;
  prefix := A_NONE;
  instruction := A_NONE;
  for k:=1 to maxoperands do
  begin
    operands[k].size := S_NO;
    operands[k].operandtype := OPR_NONE;
    { init to zeros }
    fillchar(operands[k].l, sizeof(operands[k].l),#0);
  end;
 end;

 Procedure TInstruction.addprefix(tok: tasmop);
 Begin
   if tok = A_NONE then
    Message(assem_e_syn_prefix_not_found);
   if Prefix = A_NONE then
    Prefix := tok
   else
    Message(assem_e_syn_try_add_more_prefix);
 end;

 Procedure TInstruction.addinstr(tok: tasmop);
 Begin
   if tok = A_NONE then
    Message(assem_e_syn_opcode_not_found);
   Instruction := tok;
 end;

 function TInstruction.getinstruction: tasmop;
 Begin
   getinstruction := Instruction;
 end;
      { get the current prefix of this instruction }
 function TInstruction.getprefix: tasmop;
 Begin
   getprefix := prefix;
 end;

{*************************************************************************}
{                          Local label utilities                          }
{*************************************************************************}

  Constructor TAsmLabelList.Init;
  Begin
    First := nil;
    Last := nil;
  end;


  Procedure TAsmLabelList.Insert(s:string; lab: PLabel; emitted: boolean);
  {*********************************************************************}
  {  Description: Insert a node at the end of the list with lab and     }
  {  and the name in s. The name is allocated on the heap.              }
  {  Duplicates are not allowed.                                        }
  {  Indicate in emitted if this label itself has been emitted, or is it}
  {  a simple labeled instruction?                                      }
  {*********************************************************************}
  Begin
    if search(s) = nil then
    Begin
      if First = nil then
       Begin
          New(First);
          Last := First;
       end
      else
       Begin
          New(Last^.Next);
          Last := Last^.Next;
       end;
      Last^.name := NewPasStr(s);
      Last^.Lab := lab;
      Last^.Next := nil;
      Last^.emitted := emitted;
    end;
  end;



  Function TAsmLabelList.Search(const s: string): PAsmLabel;
  {*********************************************************************}
  {  Description: This routine searches for a label named s in the      }
  {  linked list, returns a pointer to the label if found, otherwise    }
  {  returns nil.                                                       }
  {*********************************************************************}
  Var
    asmlab: PAsmLabel;
  Begin
    asmlab := First;
    if First = nil then
    Begin
      Search := nil;
      exit;
    end;
    While (asmlab^.name^ <> s) and (asmlab^.Next <> nil) do
       asmlab := asmlab^.Next;
    if asmlab^.name^ = s then
       search := asmlab
    else
       search := nil;
  end;


  Destructor TAsmLabelList.Done;
  {*********************************************************************}
  {  Description: This routine takes care of deallocating all nodes     }
  {  in the linked list, as well as deallocating the string pointers    }
  {  of these nodes.                                                    }
  {                                                                     }
  {  Remark: The PLabel field is NOT freed, the compiler takes care of  }
  {  this.                                                              }
  {*********************************************************************}
  Var
    temp: PAsmLabel;
    temp1: PAsmLabel;
  Begin
    temp := First;
    while temp <> nil do
    Begin
      Freemem(Temp^.name, length(Temp^.name^)+1);
      Temp1 := Temp^.Next;
      Dispose(Temp);
      Temp := Temp1;
      { The plabel could be deleted here, but let us not do }
      { it, FPC will do it instead.                         }
    end;
  end;



  Function TAsmLabelList.newpasstr(s: string): PString;
  {*********************************************************************}
  { FUNCTION NewPasStr(s:string): PString                               }
  {  Description: This routine allocates a string on the heap and       }
  {  returns a pointer to the allocated string.                         }
  {                                                                     }
  {  Remarks: The string allocated should not be modified, since it's   }
  {  length will be less then 255.                                      }
  {  Remarks: It is assumed that HeapError will be called if an         }
  {  allocation fails.                                                  }
  {*********************************************************************}
  Var
   StrPtr: PString;
  Begin
    GetMem(StrPtr, length(s)+1);
    Move(s,StrPtr^,length(s)+1);
    newpasstr:= Strptr;
  end;

{*************************************************************************}
{                      Symbol table helper routines                       }
{*************************************************************************}


  Procedure SwapOperands(Var instr: TInstruction);
  Var
   tempopr: TOperand;
  Begin
    if instr.numops = 2 then
    Begin
      tempopr := instr.operands[1];
      instr.operands[1] := instr.operands[2];
      instr.operands[2] := tempopr;
    end
    else
    if instr.numops = 3 then
    Begin
      tempopr := instr.operands[1];
      instr.operands[1] := instr.operands[3];
      instr.operands[3] := tempopr;
    end;
  end;


  Function SearchIConstant(const s:string; var l:longint): boolean;
  {**********************************************************************}
  {  Description: Searches for a CONSTANT of name s in either the local  }
  {  symbol list, then in the global symbol list, and returns the value  }
  {  of that constant in l. Returns TRUE if successfull, if not found,   }
  {  or if the constant is not of correct type, then returns FALSE       }
  { Remarks: Also handle TRUE and FALSE returning in those cases 1 and 0 }
  {  respectively.                                                       }
  {**********************************************************************}
  var
    sym: psym;
  Begin
    SearchIConstant := FALSE;
    { check for TRUE or FALSE reserved words first }
    if s = 'TRUE' then
    Begin
       SearchIConstant := TRUE;
       l := 1;
    end
    else
    if s = 'FALSE' then
    Begin
       SearchIConstant := TRUE;
       l := 0;
    end
    else
    if assigned(aktprocsym) then
    Begin
      if assigned(aktprocsym^.definition) then
      Begin
   { Check the local constants }
    if assigned(aktprocsym^.definition^.localst) then
       sym := aktprocsym^.definition^.localst^.search(s)
    else
       sym := nil;
    if assigned(sym) then
    Begin
       if (sym^.typ = constsym) and (pconstsym(sym)^.consttype in
         [constord,constint,constchar,constbool]) then
       Begin
          l:=pconstsym(sym)^.value;
          SearchIConstant := TRUE;
          exit;
       end;
    end;
      end;
    end;
    { Check the global constants }
    getsym(s,false);
    if srsym <> nil then
    Begin
      if (srsym^.typ=constsym) and (pconstsym(srsym)^.consttype in
       [constord,constint,constchar,constbool]) then
      Begin
        l:=pconstsym(srsym)^.value;
        SearchIConstant := TRUE;
        exit;
      end;
    end;
  end;


  Procedure SetupResult(Var Instr:TInstruction; operandnum: byte);
  {**********************************************************************}
  {  Description: This routine changes the correct fields and correct    }
  {  offset in the reference, so that it points to the __RESULT or       }
  {  @Result variable (depending on the inline asm).                     }
  {  Resturns a reference with all correct offset correctly set up.      }
  {  The Operand should already point to a treference on entry.          }
  {**********************************************************************}
  Begin
    { replace by correct offset. }
    if assigned(procinfo.retdef) and
      (procinfo.retdef<>pdef(voiddef)) then
    begin
      instr.operands[operandnum].ref.offset := procinfo.retoffset;
      instr.operands[operandnum].ref.base :=  procinfo.framepointer;
      { always assume that the result is valid. }
      procinfo.funcret_is_valid:=true;
    end
    else
     Message(assem_e_invalid_symbol_ref);
  end;


  Procedure FWaitWarning;
  begin
    if (target_info.target=target_GO32V2) and (cs_fp_emulation in aktswitches) then
     Message(assem_w_fwait_emu_prob);
  end;




  Function GetVarOffset(const base: string; const field: string;
    Var Offset: longint):boolean;
  { search and returns the offset of records/objects of the base }
  { with field name setup in field.                              }
  { returns 0 if not found.                                      }
  { used when base is a variable or a typed constant name.       }
   var
    sym:psym;
    p: psym;
  Begin
     GetVarOffset := FALSE;
     Offset := 0;
     { local list }
     if assigned(aktprocsym) then
     begin
      if assigned(aktprocsym^.definition^.localst) then
        sym:=aktprocsym^.definition^.localst^.search(base)
      else
        sym:=nil;
      if assigned(sym) then
      begin
        { field of local record variable. }
        if (sym^.typ=varsym) and (pvarsym(sym)^.definition^.deftype=recorddef) then
          begin
             p:=pvarsym(precdef(pvarsym(sym)^.definition)^.symtable^.search(field));
             if assigned(pvarsym(p)) then
             Begin
                Offset := pvarsym(p)^.address;
                GetVarOffset := TRUE;
                Exit;
             end;
          end;
      end
      else
       begin
        { field of local record parameter to routine. }
         if assigned(aktprocsym^.definition^.parast) then
            sym:=aktprocsym^.definition^.parast^.search(base)
         else
           sym:=nil;
         if assigned(sym) then
         begin
           if (sym^.typ=varsym) and (pvarsym(sym)^.definition^.deftype=recorddef)
           then
           begin
             p:=pvarsym(precdef(pvarsym(sym)^.definition)^.symtable^.search(field));
             if assigned(p) then
             Begin
                Offset := pvarsym(p)^.address;
                GetVarOffset := TRUE;
                Exit;
             end;
           end; { endif }
         end; {endif }
       end; { endif }
     end;

     { not found.. .now look for global variables. }
     getsym(base,false);
     sym:=srsym;
     if assigned(sym) then
     Begin
        { field of global record variable. }
        if (sym^.typ=varsym) and (pvarsym(sym)^.definition^.deftype=recorddef) then
          begin
             p:=pvarsym(precdef(pvarsym(sym)^.definition)^.symtable^.search(field));
             if assigned(p) then
             Begin
                Offset := pvarsym(p)^.address;
                GetVarOffset := TRUE;
                Exit;
             end;
          end
        else
        { field of global record type constant. }
        if (sym^.typ=typedconstsym) and (ptypedconstsym(sym)^.definition^.deftype=recorddef)
          then
          begin
             p:=pvarsym(precdef(pvarsym(sym)^.definition)^.symtable^.search(field));
             if assigned(p) then
             Begin
                Offset := pvarsym(p)^.address;
                GetVarOffset := TRUE;
                Exit;
             end;
          end
     end; { end looking for global variables .. }
  end;



  Function GetTypeOffset(const base: string; const field: string;
    Var Offset: longint):boolean;
  { search and returns the offset of records/objects of the base }
  { with field name setup in field.                              }
  { returns 0 if not found.                                      }
  { used when base is a variable or a typed constant name.       }
   var
    sym:psym;
    p: psym;
  Begin
     Offset := 0;
     GetTypeOffset := FALSE;
     { local list }
     if assigned(aktprocsym) then
     begin
      if assigned(aktprocsym^.definition^.localst) then
        sym:=aktprocsym^.definition^.localst^.search(base)
      else
        sym:=nil;
      if assigned(sym) then
      begin
        { field of local record type. }
        if (sym^.typ=typesym) and (ptypesym(sym)^.definition^.deftype=recorddef) then
          begin
             p:=precdef(ptypesym(sym)^.definition)^.symtable^.search(field);
             if assigned(p) then
             Begin
                Offset := pvarsym(p)^.address;
                GetTypeOffset := TRUE;
                Exit;
             end;
          end;
      end
      else
       begin
        { field of local record type to routine. }
         if assigned(aktprocsym^.definition^.parast) then
            sym:=aktprocsym^.definition^.parast^.search(base)
         else
           sym:=nil;
         if assigned(sym) then
         begin
           if (sym^.typ=typesym) and (ptypesym(sym)^.definition^.deftype=recorddef)
           then
           begin
             p:=precdef(ptypesym(sym)^.definition)^.symtable^.search(field);
             if assigned(p) then
             Begin
                Offset := pvarsym(p)^.address;
                GetTypeOffset := TRUE;
                Exit;
             end;
           end; { endif }
         end; {endif }
       end; { endif }
     end;

     { not found.. .now look for global types. }
     getsym(base,false);
     sym:=srsym;
     if assigned(sym) then
     Begin
        { field of global record types. }
        if (sym^.typ=typesym) and (ptypesym(sym)^.definition^.deftype=recorddef) then
          begin
             p:=precdef(ptypesym(sym)^.definition)^.symtable^.search(field);
             if assigned(p) then
             Begin
                Offset := pvarsym(p)^.address;
                GetTypeOffset := TRUE;
                Exit;
             end
          end
        else
        { public field names of objects }
        if (sym^.typ=typesym) and (ptypesym(sym)^.definition^.deftype=objectdef)then
          begin
             if assigned(pobjectdef(ptypesym(sym)^.definition)^.publicsyms) then
             Begin
               p:=pobjectdef(ptypesym(sym)^.definition)^.publicsyms^.search(field);
               if assigned(p) then
               Begin
                  Offset := pvarsym(p)^.address;
                  GetTypeOffset := TRUE;
                  Exit;
               end
             end;
          end;
     end; { end looking for global variables .. }
  end;


  Function CreateVarInstr(var Instr: TInstruction; const hs:string;operandnum:byte): Boolean;
  { search and sets up the correct fields in the Instr record }
  { for the NON-constant identifier passed to the routine.    }
  { if not found returns FALSE.                               }
   var
    sym:psym;
    l: longint;
  Begin
     CreateVarInstr := FALSE;
     { are we in a routine ? }
     if assigned(aktprocsym) then
     begin
      if assigned(aktprocsym^.definition^.localst) then
      { search the local list for the name of this variable. }
        sym:=aktprocsym^.definition^.localst^.search(hs)
      else
        sym:=nil;
      if assigned(sym) then
      begin
        if sym^.typ=varsym then
          begin
           { we always assume in asm statements that     }
           { that the variable is valid.                 }
           pvarsym(sym)^.is_valid:=1;
           instr.operands[operandnum].ref.base := procinfo.framepointer;
           instr.operands[operandnum].ref.offset := - (pvarsym(sym)^.address);
           { the current size is NOT overriden if it already }
           { exists, such as in the case of a byte ptr, in   }
           { front of the identifier.                        }
           if instr.operands[operandnum].size = S_NO then
           Begin
             case pvarsym(sym)^.getsize of
              1: instr.operands[operandnum].size := S_B;
              2: instr.operands[operandnum].size := S_W;
              4: instr.operands[operandnum].size := S_L;
              8: instr.operands[operandnum].size := S_Q;
              extended_size: 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; { end case }
           end;
           { ok, finished for thir variable. }
           CreateVarInstr := TRUE;
           Exit;
          end
        else
        { call to local function }
        if (sym^.typ=procsym) then
          begin
            { 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(pprocsym(sym)^.definition^.mangledname);
            CreateVarInstr := TRUE;
            Exit;
          end
{        else
        if (sym^.typ = typedconstsym) then
        Begin}
           { UGH????? pprocsym??? }
{           instr.operands[operandnum].ref.symbol:=newpasstr(pprocsym(sym)^.definition^.mangledname);}
           {* the current size is NOT overriden if it already *}
           {* exists, such as in the case of a byte ptr, in   *}
           {* front of the identifier.                        *}
{           if instr.operands[operandnum].size = S_NO then
           Begin
             case ptypedconstsym(sym)^.definition^.size of
              1: instr.operands[operandnum].size := S_B;
              2: instr.operands[operandnum].size := S_W;
              4: instr.operands[operandnum].size := S_L;
              8: instr.operands[operandnum].size := S_Q;
              extended_size: 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;} {* end case *}
{           end;}
           {* ok, finished for this variable. *}
{           CreateVarInstr := TRUE;
           Exit;
        end }
      end;
      { now check for parameters passed to routine }
{      else}
       begin
         if assigned(aktprocsym^.definition^.parast) then
            sym:=aktprocsym^.definition^.parast^.search(hs)
         else
           sym:=nil;
         if assigned(sym) then
         begin
           if sym^.typ=varsym then
           begin
             l:=pvarsym(sym)^.address;
             { set offset }
             inc(l,aktprocsym^.definition^.parast^.call_offset);
             pvarsym(sym)^.is_valid:=1;
             instr.operands[operandnum].ref.base := procinfo.framepointer;
             instr.operands[operandnum].ref.offset := l;
             { the current size is NOT overriden if it already }
             { exists, such as in the case of a byte ptr, in   }
             { front of the identifier.                        }
             if instr.operands[operandnum].size = S_NO then
             Begin
               case pvarsym(sym)^.getsize of
                 1: instr.operands[operandnum].size := S_B;
                 2: instr.operands[operandnum].size := S_W;
                 4: instr.operands[operandnum].size := S_L;
                 8: instr.operands[operandnum].size := S_Q;
                 extended_size: 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; { end case }
             end; { endif }
             CreateVarInstr := TRUE;
             Exit;
           end; { endif }
         end; {endif }
       end; { endif }
     end;

     { not found.. .now look for global variables. }
     getsym(hs,false);
     sym:=srsym;
     if assigned(sym) then
     Begin
       if (sym^.typ = varsym) or (sym^.typ = typedconstsym) then
       Begin
       { 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(sym^.mangledname);
         { the current size is NOT overriden if it already }
         { exists, such as in the case of a byte ptr, in   }
         { front of the identifier.                        }
         if (instr.operands[operandnum].size = S_NO) and (sym^.typ = varsym) then
         Begin
           case pvarsym(sym)^.getsize of
             1: instr.operands[operandnum].size := S_B;
             2: instr.operands[operandnum].size := S_W;
             4: instr.operands[operandnum].size := S_L;
             8: instr.operands[operandnum].size := S_Q;
           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;
         end
         else
         if (instr.operands[operandnum].size = S_NO) and (sym^.typ = typedconstsym) then
         Begin
         { only these are valid sizes, otherwise prefixes are }
         { required.                                          }
            case ptypedconstsym(sym)^.definition^.size of
              1: instr.operands[operandnum].size := S_B;
              2: instr.operands[operandnum].size := S_W;
              4: instr.operands[operandnum].size := S_L;
              8: instr.operands[operandnum].size := S_Q;
            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;
         end; { endif }
         CreateVarInstr := TRUE;
         Exit;
       end;
       if (sym^.typ=procsym) then
       begin
         if assigned(pprocsym(sym)^.definition^.nextoverloaded) then
          Message(assem_w_calling_overload_func);
         { 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(pprocsym(sym)^.definition^.mangledname);
         CreateVarInstr := TRUE;
         Exit;
       end;
     end; { end looking for global variables .. }
  end;



  Function SearchLabel(const s: string; var hl: plabel): boolean;
  {**********************************************************************}
  {  Description: Searches for a pascal label definition, first in the   }
  {  local symbol list and then in the global symbol list. If found then }
  {  return pointer to label and return true, otherwise returns false.   }
  {**********************************************************************}
  var
    sym: psym;
  Begin
    SearchLabel := FALSE;
    if assigned(aktprocsym) then
    Begin
      { Check the local constants }
    if assigned(aktprocsym^.definition) then
    Begin
        if assigned(aktprocsym^.definition^.localst) then
          sym := aktprocsym^.definition^.localst^.search(s)
      else
       sym := nil;
      if assigned(sym) then
      Begin
       if (sym^.typ = labelsym) then
       Begin
          hl:=plabelsym(sym)^.number;
          SearchLabel := TRUE;
          exit;
       end;
      end;
    end;
  end;
    { Check the global label symbols... }
    getsym(s,false);
    if srsym <> nil then
    Begin
      if (srsym^.typ=labelsym) then
      Begin
        hl:=plabelsym(srsym)^.number;
        SearchLabel:= TRUE;
        exit;
      end;
    end;
  end;


 {*************************************************************************}
 {                   Instruction Generation Utilities                      }
 {*************************************************************************}


   Procedure ConcatString(p : paasmoutput;s:string);
  {*********************************************************************}
  { PROCEDURE ConcatString(s:string);                                   }
  {  Description: This routine adds the character chain pointed to in   }
  {  s to the instruction linked list.                                  }
  {*********************************************************************}
  Var
   pc: PChar;
  Begin
     getmem(pc,length(s)+1);
     p^.concat(new(pai_string,init_pchar(strpcopy(pc,s))));
  end;

  Procedure ConcatPasString(p : paasmoutput;s:string);
  {*********************************************************************}
  { PROCEDURE ConcatPasString(s:string);                                }
  {  Description: This routine adds the character chain pointed to in   }
  {  s to the instruction linked list, contrary to ConcatString it      }
  {  uses a pascal style string, so it conserves null characters.       }
  {*********************************************************************}
  Begin
     p^.concat(new(pai_string,init(s)));
  end;

  Procedure ConcatDirect(p : paasmoutput;s:string);
  {*********************************************************************}
  { PROCEDURE ConcatDirect(s:string)                                    }
  {  Description: This routine output the string directly to the asm    }
  {  output, it is only sed when writing special labels in AT&T mode,   }
  {  and should not be used without due consideration, since it may     }
  {  cause problems.                                                    }
  {*********************************************************************}
  Var
   pc: PChar;
  Begin
     getmem(pc,length(s)+1);
     p^.concat(new(pai_direct,init(strpcopy(pc,s))));
  end;




   Procedure ConcatConstant(p: paasmoutput; value: longint; maxvalue: longint);
  {*********************************************************************}
  { PROCEDURE ConcatConstant(value: longint; maxvalue: longint);        }
  {  Description: This routine adds the value constant to the current   }
  {  instruction linked list.                                           }
  {   maxvalue -> indicates the size of the data to initialize:         }
  {                  $ff -> create a byte node.                         }
  {                  $ffff -> create a word node.                       }
  {                  $ffffffff -> create a dword node.                  }
  {*********************************************************************}
  Begin
      if value > maxvalue then
      Begin
         Message(assem_e_constant_out_of_bounds);
         { assuming a value of maxvalue }
         value := maxvalue;
      end;
      if maxvalue = $ff then
          p^.concat(new(pai_const,init_8bit(byte(value))))
      else
      if maxvalue = $ffff then
          p^.concat(new(pai_const,init_16bit(word(value))))
      else
      if maxvalue = $ffffffff then
          p^.concat(new(pai_const,init_32bit(longint(value))));
  end;

  Procedure ConcatRealConstant(p : paasmoutput;value: bestreal; real_typ : tfloattype);
  {***********************************************************************}
  { PROCEDURE ConcatRealConstant(value: bestreal; real_typ : tfloattype); }
  {  Description: This routine adds the value constant to the current     }
  {  instruction linked list.                                             }
  {   real_typ -> indicates the type of the real data to initialize:      }
  {                  s32real -> create a single node.                     }
  {                  s64real -> create a double node.                     }
  {                  s80real -> create an extended node.                  }
  {                  s64bit ->  create a  comp node.                      }
  {                  f32bit ->  create a  fixed node. (not used normally) }
  {***********************************************************************}
    Begin
       case real_typ of
          s32real : p^.concat(new(pai_single,init(value)));
          s64real : p^.concat(new(pai_double,init(value)));
          s80real : p^.concat(new(pai_extended,init(value)));
          s64bit  : p^.concat(new(pai_comp,init(value)));
          f32bit  : p^.concat(new(pai_const,init_32bit(trunc(value*$10000))));
       end;
    end;

   Procedure ConcatLabel(p: paasmoutput;op : tasmop;var l : plabel);
  {*********************************************************************}
  { PROCEDURE ConcatLabel                                               }
  {  Description: This routine either emits a label or a labeled        }
  {  instruction to the linked list of instructions.                    }
  {*********************************************************************}
   begin
         if op=A_LABEL then
           p^.concat(new(pai_label,init(l)))
         else
           p^.concat(new(pai_labeled,init(op,l)))
   end;

   procedure ConcatPublic(p:paasmoutput;const s : string);
  {*********************************************************************}
  { PROCEDURE ConcatPublic                                              }
  {  Description: This routine emits an global   definition to the      }
  {  linked list of instructions.(used by AT&T styled asm)              }
  {*********************************************************************}
   begin
       p^.concat(new(pai_symbol,init_global(s)));
       { concat_internal(s,EXT_NEAR); done in aasm }
   end;

   procedure ConcatLocal(p:paasmoutput;const s : string);
  {*********************************************************************}
  { PROCEDURE ConcatLocal                                               }
  {  Description: This routine emits an local    definition to the      }
  {  linked list of instructions.                                       }
  {*********************************************************************}
   begin
       p^.concat(new(pai_symbol,init(s)));
       { concat_internal(s,EXT_NEAR); done in aasm }
   end;

  Procedure ConcatGlobalBss(const s : string;size : longint);
  {*********************************************************************}
  { PROCEDURE ConcatGlobalBss                                           }
  {  Description: This routine emits an global  datablock   to the      }
  {  linked list of instructions.                                       }
  {*********************************************************************}
   begin
       bsssegment^.concat(new(pai_datablock,init_global(s,size)));
       { concat_internal(s,EXT_NEAR); done in aasm }
   end;

  Procedure ConcatLocalBss(const s : string;size : longint);
  {*********************************************************************}
  { PROCEDURE ConcatLocalBss                                            }
  {  Description: This routine emits a local datablcok      to the      }
  {  linked list of instructions.                                       }
  {*********************************************************************}
   begin
       bsssegment^.concat(new(pai_datablock,init(s,size)));
       { concat_internal(s,EXT_NEAR); done in aasm }
   end;

  { add to list of external labels }
  Procedure ConcatExternal(const s : string;typ : texternal_typ);
  {*********************************************************************}
  { PROCEDURE ConcatExternal                                            }
  {  Description: This routine emits an external definition to the      }
  {  linked list of instructions.(used by AT&T styled asm)              }
  {*********************************************************************}
  { check if in internal list and remove it there                       }
  var p : pai_external;
   begin
       p:=search_assembler_symbol(internals,s,typ);
       if p<>nil then internals^.remove(p);
       concat_external(s,typ);
   end;

  { add to internal list of labels }
  Procedure ConcatInternal(const s : string;typ : texternal_typ);
  {*********************************************************************}
  { PROCEDURE ConcatInternal                                            }
  {  Description: This routine emits an internal definition of a symbol }
  {  (used by AT&T styled asm for undefined labels)                     }
  {*********************************************************************}
   begin
       concat_internal(s,typ);
   end;

end.
{
  $Log$
  Revision 1.1  1998-03-25 11:18:12  root
  Initial revision

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

  Revision 1.14  1998/03/09 12:58:10  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.13  1998/03/03 16:45:16  peter
    + message support for assembler parsers

  Revision 1.12  1998/03/02 01:48:02  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.11  1998/02/13 10:34:34  daniel
  * Made Motorola version compilable.
  * Fixed optimizer

  Revision 1.10  1998/01/09 19:21:19  carl
  + added support for m68k

  Revision 1.7  1997/12/14 22:43:17  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.5  1997/12/09 13:23:54  carl
  + less processor specific
  - moved searching for externals/internal symbols from CreateVarInstr to
    ratti386.pas (this would cause invalid stuff in rai386.pas!)

  Revision 1.4  1997/12/04 12:20:39  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.3  1997/12/01 17:42:49  pierre
     + added some more functionnality to the assembler parser

  Revision 1.2  1997/11/27 17:55:11  carl
  * made it compile under bp (one comment was nested)

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

 11th november 1997:
   * fixed problems when using reserved words TRUE and FALSE (CEC).
 22th november 1997:
   * changed operator (reserved word) into _operator (PM).

}
