{
    Copyright (c) 1999-2009 by Florian Klaempfl and David Zhang

    This unit implements an asmoutput class for MIPS assembly syntax

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

{$i fpcdefs.inc}

  interface

    uses
      cpubase,
       aasmtai, aasmcpu, assemble, aggas;

    type
      TMIPSGNUAssembler = class(TGNUassembler)
        constructor create(smart: boolean); override;
      end;

      TMIPSInstrWriter = class(TCPUInstrWriter)
        procedure WriteInstruction(hp : tai);override;
      end;

  implementation

    uses
      cutils, systems,
      verbose, itcpugas, cgbase, cgutils;

{****************************************************************************}
{                         GNU MIPS  Assembler writer                           }
{****************************************************************************}

    constructor TMIPSGNUAssembler.create(smart: boolean);
      begin
        inherited create(smart);
        InstrWriter := TMIPSInstrWriter.create(self);
      end;


{****************************************************************************}
{                  Helper routines for Instruction Writer                    }
{****************************************************************************}

    function GetReferenceString(var ref: TReference): string;
      begin
        GetReferenceString := '';
        with ref do
        begin
          if (base = NR_NO) and (index = NR_NO) then
          begin
            if assigned(symbol) then
              GetReferenceString := symbol.Name;
            if offset > 0 then
              GetReferenceString := GetReferenceString + '+' + ToStr(offset)
            else if offset < 0 then
              GetReferenceString := GetReferenceString + ToStr(offset);
            case refaddr of
              addr_high:
                GetReferenceString := '%hi(' + GetReferenceString + ')';
              addr_low:
                GetReferenceString := '%lo(' + GetReferenceString + ')';
            end;
          end
          else
          begin
      {$ifdef extdebug}
            if assigned(symbol) and
              not(refaddr in [addr_pic,addr_lo]) then
              internalerror(2003052601);
      {$endif extdebug}
            if base <> NR_NO then
              GetReferenceString := GetReferenceString + '(' + gas_regname(base) + ')';
            if index = NR_NO then
            begin
              if offset <> 0 then
                GetReferenceString := ToStr(offset) + GetReferenceString;
              if assigned(symbol) then
              begin
                if refaddr = addr_low then
                  GetReferenceString := '%lo(' + symbol.Name + ')' + GetReferenceString
                else
                  GetReferenceString := symbol.Name + {'+' +} GetReferenceString;
              end;
            end
            else
            begin
  {$ifdef extdebug}
              if (Offset<>0) or assigned(symbol) then
                internalerror(2003052603);
  {$endif extdebug}
              GetReferenceString := GetReferenceString + '(' + gas_regname(index) + ')';

            end;
          end;
        end;
      end;


    function getopstr(const Oper: TOper): string;
      begin
        with Oper do
          case typ of
            top_reg:
              getopstr := gas_regname(reg);
            top_const:
              getopstr := tostr(longint(val));
            top_ref:
              if (oper.ref^.refaddr in [addr_no, addr_pic]) or ((oper.ref^.refaddr = addr_low) and ((oper.ref^.base <> NR_NO) or
                (oper.ref^.index <> NR_NO))) then
                getopstr := getreferencestring(ref^)
              else
                getopstr := getreferencestring(ref^);
            else
              internalerror(10001);
          end;
      end;

      function getopstr_4(const Oper: TOper): string;
      var
        tmpref: treference;
      begin
        with Oper do
          case typ of
            top_ref:
            begin
              tmpref := ref^;
              Inc(tmpref.offset, 4);
              getopstr_4 := getreferencestring(tmpref);
            end;
            else
              internalerror(2007050403);
          end;
      end;


    procedure TMIPSInstrWriter.WriteInstruction(hp: Tai);
      var
        Op: TAsmOp;
        s,s1:  string;
        i:  integer;
        tmpfpu: string;
        tmpfpu_len: longint;
      begin
        if hp.typ <> ait_instruction then
          exit;
        op := taicpu(hp).opcode;

        case op of
          A_P_STK2:
          begin
            s1 := getopstr(taicpu(hp).oper[2]^);
            STK2_LocalSize := align(STK2_LocalSize, 8);
            if s1[1] = '-' then
              str(-STK2_LocalSize, s1)
            else
              str(STK2_LocalSize, s1);
            s := #9 + gas_op2str[A_ADDIU] + #9 + getopstr(taicpu(hp).oper[0]^)+ ',' + getopstr(taicpu(hp).oper[1]^) + ',' + s1;
            owner.AsmWriteLn(s);
          end;
          A_P_FRAME:
          begin
          end;
          A_P_SET_MACRO:
          begin
            s := #9 + '.set' + #9 + 'macro';
            owner.AsmWriteLn(s);
          end;
          A_P_SET_REORDER:
          begin
            s := #9 + '.set' + #9 + 'reorder';
            owner.AsmWriteLn(s);
          end;
          A_P_SET_NOMACRO:
          begin
            s := #9 + '.set' + #9 + 'nomacro';
            owner.AsmWriteLn(s);
          end;
          A_P_SET_NOREORDER:
          begin
            s := #9 + '.set' + #9 + 'noreorder';
            owner.AsmWriteLn(s);
          end;
          A_P_SW:
          begin
            s := #9 + gas_op2str[A_SW] + #9 + getopstr(taicpu(hp).oper[0]^)+ ',' + getopstr(taicpu(hp).oper[2]^) + '(' + getopstr(taicpu(hp).oper[1]^) + ')';
            owner.AsmWriteLn(s);
          end;
          A_P_LW:
          begin
            s := #9 + gas_op2str[A_LW] + #9 + getopstr(taicpu(hp).oper[0]^)+ ',' + getopstr(taicpu(hp).oper[2]^) + '(' + getopstr(taicpu(hp).oper[1]^) + ')';
            owner.AsmWriteLn(s);
          end;
          A_LDC1:
          begin
            tmpfpu := getopstr(taicpu(hp).oper[0]^);
            s := #9 + gas_op2str[A_LWC1] + #9 + tmpfpu + ',' + getopstr(taicpu(hp).oper[1]^); // + '(' + getopstr(taicpu(hp).oper[1]^) + ')';
            owner.AsmWriteLn(s);

            tmpfpu_len := length(tmpfpu);
            tmpfpu[tmpfpu_len] := succ(tmpfpu[tmpfpu_len]);
            s := #9 + gas_op2str[A_LWC1] + #9 + tmpfpu + ',' + getopstr_4(taicpu(hp).oper[1]^); // + '(' + getopstr(taicpu(hp).oper[1]^) + ')';
            owner.AsmWriteLn(s);
          end;
          A_SDC1:
          begin
            tmpfpu := getopstr(taicpu(hp).oper[0]^);
            s := #9 + gas_op2str[A_SWC1] + #9 + tmpfpu + ',' + getopstr(taicpu(hp).oper[1]^); //+ ',' + getopstr(taicpu(hp).oper[2]^) + '(' + getopstr(taicpu(hp).oper[1]^) + ')';
            owner.AsmWriteLn(s);

            tmpfpu_len := length(tmpfpu);
            tmpfpu[tmpfpu_len] := succ(tmpfpu[tmpfpu_len]);
            s := #9 + gas_op2str[A_SWC1] + #9 + tmpfpu + ',' + getopstr_4(taicpu(hp).oper[1]^); //+ ',' + getopstr(taicpu(hp).oper[2]^) + '(' + getopstr(taicpu(hp).oper[1]^) + ')';
            owner.AsmWriteLn(s);
          end;
          else
          begin
            s := #9 + gas_op2str[op] + cond2str[taicpu(hp).condition];
            if taicpu(hp).delayslot_annulled then
              s := s + ',a';
            if taicpu(hp).ops > 0 then
            begin
              s := s + #9 + getopstr(taicpu(hp).oper[0]^);
              for i := 1 to taicpu(hp).ops - 1 do
                s := s + ',' + getopstr(taicpu(hp).oper[i]^);
            end;
            owner.AsmWriteLn(s);
          end;
        end;
      end;


    const
      as_MIPSEL_as_info: tasminfo =
        (
        id: as_gas;
        idtxt: 'AS';
        asmbin: 'as';
        asmcmd: '-mips2 -W -EL -o $OBJ $ASM';
        supported_targets: [system_mips_linux,system_mipsel_linux];
        flags: [af_allowdirect, af_needar, af_smartlink_sections];
        labelprefix: '.L';
        comment: '# ';
        );

begin
  RegisterAssembler(as_MIPSEL_as_info, TMIPSGNUAssembler);
end.
