{
    $Id$
    Copyright (c) 1998 by the FPC development team

    This unit handles the assemblerfile write and assembler calls of FPC

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

interface

uses
  dos,cobjects,globals,aasm;

const
{$ifdef tp}
  AsmOutSize=1024;
{$else}
  AsmOutSize=10000;
{$endif}


{$ifdef i386}
{ tof = (of_none,of_o,of_obj,of_masm,of_att,of_nasm,of_win32) }
  AsBin : array[tof] of string[8]=('','as','nasm','masm','as','nasm','asw');
{$endif}
{$ifdef m68k}
{ tof = (of_none,of_o,of_gas,of_mot,of_mit) }
  AsBin : array[tof] of string[8]=('','','','','');
{$endif}


type
  PAsmList=^TAsmList;
  TAsmList=object
    outcnt  : longint;
    outbuf  : array[0..AsmOutSize-1] of char;
    outfile : file;
    constructor Init;
    destructor Done;
    Procedure AsmFlush;
    Procedure AsmWrite(const s:string);
    Procedure AsmWritePChar(p:pchar);
    Procedure AsmWriteLn(const s:string);
    Procedure AsmLn;
    procedure OpenAsmList(const fn,fn2:string);
    procedure CloseAsmList;
    procedure WriteTree(p:paasmoutput);virtual;
    procedure WriteAsmList;virtual;
  end;

  PAsmFile=^TAsmFile;
  TAsmFile=object
    asmlist : pasmlist;
    path:dirstr;
    asmfile,
    objfile,
    srcfile,
    as_bin  : string;
    Constructor Init(const fn:string);
    Destructor Done;
    Function FindAssembler(curr_of:tof):string;
    Procedure WriteAsmSource;
    Function CallAssembler(const command,para:string):Boolean;
    Procedure RemoveAsm;
    Function DoAssemble:boolean;
  end;

Implementation

uses
  script,files,systems,verbose
{$ifdef linux}
  ,linux
{$endif}
  ,strings
{$ifdef i386}
  ,ag386att,ag386int
{$endif}
{$ifdef m68k}
  ,ag68kmot,ag68kgas,ag68kmit
{$endif}
  ;


Function DoPipe:boolean;
begin
  DoPipe:=use_pipe and (not writeasmfile) and (current_module^.output_format=of_o);
end;


{*****************************************************************************
                                  TASMLIST
*****************************************************************************}

Procedure TAsmList.AsmFlush;
begin
  if outcnt>0 then
   begin
     BlockWrite(outfile,outbuf,outcnt);
     outcnt:=0;
   end;
end;


Procedure TAsmList.AsmWrite(const s:string);
begin
  if OutCnt+length(s)>=AsmOutSize then
   AsmFlush;
  Move(s[1],OutBuf[OutCnt],length(s));
  inc(OutCnt,length(s));
end;


Procedure TAsmList.AsmWriteLn(const s:string);
begin
  AsmWrite(s);
  AsmWrite(target_info.newline);
end;


Procedure TAsmList.AsmWritePChar(p:pchar);
var
  i,j : longint;
begin
  i:=StrLen(p);
  j:=i;
  while j>0 do
   begin
     i:=min(j,AsmOutSize);
     if OutCnt+i>=AsmOutSize then
      AsmFlush;
     Move(p[0],OutBuf[OutCnt],i);
     inc(OutCnt,i);
     dec(j,i);
     p:=pchar(@p[i]);
   end;
end;




Procedure TAsmList.AsmLn;
begin
  AsmWrite(target_info.newline);
end;


procedure TAsmList.OpenAsmList(const fn,fn2:string);
begin
{$ifdef linux}
  if DoPipe then
   begin
     Message1(exec_i_assembling_pipe,fn);
     POpen(outfile,'as -o '+fn2,'W');
   end
  else
{$endif}
   begin
     Assign(outfile,fn);
     {$I-}
      Rewrite(outfile,1);
     {$I+}
     if ioresult<>0 then
      Message1(exec_d_cant_create_asmfile,fn);
   end;
  outcnt:=0;
end;


procedure TAsmList.CloseAsmList;
var
  f : file;
  l : longint;
begin
  AsmFlush;
{$ifdef linux}
  if DoPipe then
   Close(outfile)
  else
{$endif}
   begin
   {Touch Assembler time to ppu time is there is a ppufilename}
     if Assigned(current_module^.ppufilename) then
      begin
        Assign(f,current_module^.ppufilename^);
        reset(f,1);
        if ioresult=0 then
         begin
           getftime(f,l);
           close(f);
           reset(outfile,1);
           setftime(outfile,l);
         end;
      end;
     close(outfile);
   end;
end;


procedure TAsmList.WriteTree(p:paasmoutput);
begin
end;


procedure TAsmList.WriteAsmList;
begin
end;


constructor TAsmList.Init;
begin
  OutCnt:=0;
end;


destructor TAsmList.Done;
begin
end;


{*****************************************************************************
                                  TASMFILE
*****************************************************************************}

Constructor TAsmFile.Init(const fn:string);
var
  name:namestr;
  ext:extstr;
begin
{Create filenames for easier access}
  fsplit(fn,path,name,ext);
  srcfile:=fn;
  asmfile:=path+name+target_info.asmext;
  objfile:=path+name+target_info.objext;
{Init output format}
  case current_module^.output_format of
{$ifdef i386}
     of_o,
     of_win32,
     of_att:
       asmlist:=new(pi386attasmlist,Init);
     of_obj,
     of_masm,
     of_nasm:
       asmlist:=new(pi386intasmlist,Init);
{$endif}
{$ifdef m68k}
     of_o,
   of_gas : asmlist:=new(pm68kgasasmlist,Init);
   of_mot : asmlist:=new(pm68kmotasmlist,Init);
   of_mit : asmlist:=new(pm68kmitasmlist,Init);
{$endif}
  else
   internalerror(30000);
  end;
end;


Destructor TAsmFile.Done;
begin
end;


Procedure TAsmFile.WriteAsmSource;
begin
  asmlist^.OpenAsmList(asmfile,objfile);
  asmlist^.WriteAsmList;
  asmlist^.CloseAsmList;
end;


const
  last_of  : tof=of_none;
var
  LastASBin : string;
Function TAsmFile.FindAssembler(curr_of:tof):string;
var
  asfound : boolean;
begin
  if last_of<>curr_of then
   begin
     last_of:=curr_of;
     LastASBin:=FindExe(asbin[curr_of],asfound);
     if (not asfound) and (not externasm) then
      begin
        Message1(exec_w_assembler_not_found,LastASBin);
        externasm:=true;
      end;
     if asfound then
      Message1(exec_u_using_assembler,LastASBin);
   end;
  FindAssembler:=LastASBin;
end;


Function TAsmFile.CallAssembler(const command,para:string):Boolean;
begin
  if not externasm then
   begin
     swapvectors;
     exec(command,para);
     swapvectors;
     if (dosexitcode<>0) then
      begin
        Message(exec_w_error_while_assembling);
        callassembler:=false;
        exit;
      end
     else
      if (doserror<>0) then
       begin
         Message(exec_w_cant_call_assembler);
         externasm:=true;
       end;
   end;
  if externasm then
   AsmRes.AddAsmCommand(command,para,asmfile);
  callassembler:=true;
end;


procedure TAsmFile.RemoveAsm;
var
  g : file;
  i : word;
begin
  if writeasmfile then
   exit;
  if ExternAsm then
   AsmRes.AddDeleteCommand (AsmFile)
  else
   begin
     assign(g,asmfile);
     {$I-}
      erase(g);
     {$I+}
     i:=ioresult;
   end;
end;


Function TAsmFile.DoAssemble:boolean;
begin
  if DoPipe then
   exit;
  if not externasm then
   Message1(exec_i_assembling,asmfile);
  case current_module^.output_format of
{$ifdef i386}
   of_att : begin
              externasm:=true; {Force Extern Asm}
              if CallAssembler(FindAssembler(of_att),' -D -o '+objfile+' '+asmfile) then
               RemoveAsm;
            end;
     of_o : begin
              if CallAssembler(FindAssembler(of_o),'-D -o '+objfile+' '+asmfile) then
               RemoveAsm;
            end;
 of_win32 : begin
              if CallAssembler(FindAssembler(of_win32),'-D -o '+objfile+' '+asmfile) then
               RemoveAsm;
            end;
  of_nasm : begin
              if CallAssembler(FindAssembler(of_nasm),' -f coff -o '+objfile+' '+asmfile) then
               RemoveAsm;
            end;
   of_obj : begin
              if CallAssembler(FindAssembler(of_nasm),' -f obj -o '+objfile+' '+asmfile) then
               RemoveAsm;
            end;
  of_masm : begin
            { !! Nothing yet !! }
            end;
{$endif}
{$ifdef m68k}
     of_o,
   of_mot,
   of_mit,
   of_gas : begin
            { !! Nothing yet !! }
            end;
{$endif}
  else
   internalerror(30000);
  end;
  DoAssemble:=true;
end;

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

  Revision 1.17  1998/03/10 13:23:00  florian
    * small win32 problems fixed

  Revision 1.16  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.15  1998/03/09 10:37:41  peter
    * fixed very long pchar writing (> outbufsize)

  Revision 1.14  1998/03/05 22:43:45  florian
    * some win32 support stuff added

  Revision 1.13  1998/03/04 14:18:58  michael
  * modified messaging system

  Revision 1.12  1998/03/04 01:34:51  peter
    * messages for unit-handling and assembler/linker
    * the compiler compiles without -dGDB, but doesn't work yet
    + -vh for Hint

  Revision 1.11  1998/03/02 01:48:05  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.10  1998/02/26 11:57:00  daniel
  * New assembler optimizations commented out, because of bugs.
  * Use of dir-/name- and extstr.

  Revision 1.9  1998/02/24 10:29:12  peter
    * -a works again

  Revision 1.8  1998/02/21 03:31:40  carl
    + mit68k asm support.

  Revision 1.7  1998/02/18 14:18:16  michael
  + added log at end of file (retroactively)

  revision 1.6
  date: 1998/02/18 13:43:11;  author: michael;  state: Exp;  lines: +3 -19
  + Implemented an OS independent AsmRes object.
  ----------------------------
  revision 1.5
  date: 1998/02/17 21:20:28;  author: peter;  state: Exp;  lines: +60 -54
    + Script unit
    + __EXIT is called again to exit a program
    - target_info.link/assembler calls
    * linking works again for dos
    * optimized a few filehandling functions
    * fixed stabs generation for procedures
  ----------------------------
  revision 1.4
  date: 1998/02/16 12:51:27;  author: michael;  state: Exp;  lines: +2 -2
  + Implemented linker object
  ----------------------------
  revision 1.3
  date: 1998/02/15 21:15:58;  author: peter;  state: Exp;  lines: +8 -9
    * all assembler outputs supported by assemblerobject
    * cleanup with assembleroutputs, better .ascii generation
    * help_constructor/destructor are now added to the externals
    - generation of asmresponse is not outputformat depended
  ----------------------------
  revision 1.2
  date: 1998/02/14 01:45:04;  author: peter;  state: Exp;  lines: +3 -14
    * more fixes
    - pmode target is removed
    - search_as_ld is removed, this is done in the link.pas/assemble.pas
    + findexe() to search for an executable (linker,assembler,binder)
  ----------------------------
  revision 1.1
  date: 1998/02/13 22:28:16;  author: peter;  state: Exp;
    + Initial implementation
}
