{
    $Id$
    Copyright (c) 1998-2000 by Florian Klaempfl and Peter Vreman

    Contains the basic declarations for the x86-64 architecture

    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.

 ****************************************************************************
}
{ This unit contains the basic declarations for the x86-64 architecture.
}
unit cpubase;

{$i fpcdefs.inc}

interface

uses
  globals,cutils,cclasses,
  aasmbase,
  cpuinfo,
  cginfo;

{*****************************************************************************
                                Assembler Opcodes
*****************************************************************************}

    type
      TAsmOp={$i x86_64op.inc}

      { This should define the array of instructions as string }
      op2strtable=array[tasmop] of string[11];

    Const
      { First value of opcode enumeration }
      firstop = low(tasmop);
      { Last value of opcode enumeration }
      lastop  = high(tasmop);


{*****************************************************************************
                                Operand Sizes
*****************************************************************************}

type
  topsize = (S_NO,
    S_B,S_W,S_L,S_BW,S_BL,S_WL,S_BQ,S_WQ,S_LQ,
    S_IS,S_IL,S_IQ,
    S_FS,S_FL,S_FX,S_D,S_Q,S_FV,S_FXX,
    S_NEAR,S_FAR,S_SHORT
  );

{*****************************************************************************
                                  Registers
*****************************************************************************}

type
  { don't change the order }
  { it's used by the register size conversions }
  { Enumeration of all registers of the CPU }
  tregister = (R_NO,
    R_RAX,R_RCX,R_RDX,R_RBX,R_RSP,R_RBP,R_RSI,R_RDI,
    R_R8,R_R9,R_R10,R_R11,R_R12,R_R13,R_R14,R_R15,R_RIP,
    R_EAX,R_ECX,R_EDX,R_EBX,R_ESP,R_EBP,R_ESI,R_EDI,
    R_R8D,R_R9D,R_R10D,R_R11D,R_R12D,R_R13D,R_R14D,R_R15D,
    R_AX,R_CX,R_DX,R_BX,R_SP,R_BP,R_SI,R_DI,
    R_R8W,R_R9W,R_R10W,R_R11W,R_R12W,R_R13W,R_R14W,R_R15W,
    R_AL,R_CL,R_DL,R_BL,R_SPL,R_BPL,R_SIL,R_DIL,
    R_R8B,R_R9B,R_R10B,R_R11B,R_R12B,R_R13B,R_R14B,R_R15B,
    R_AH,R_CH,R_BH,R_DH,
    R_CS,R_DS,R_ES,R_SS,R_FS,R_GS,
    R_ST,R_ST0,R_ST1,R_ST2,R_ST3,R_ST4,R_ST5,R_ST6,R_ST7,
    R_DR0,R_DR1,R_DR2,R_DR3,R_DR6,R_DR7,
    R_CR0,R_CR2,R_CR3,R_CR4,
    R_TR3,R_TR4,R_TR5,R_TR6,R_TR7,
    R_MM0,R_MM1,R_MM2,R_MM3,R_MM4,R_MM5,R_MM6,R_MM7,
    R_XMM0,R_XMM1,R_XMM2,R_XMM3,R_XMM4,R_XMM5,R_XMM6,R_XMM7,
    R_XMM8,R_XMM9,R_XMM10,R_XMM11,R_XMM12,R_XMM13,R_XMM14,R_XMM15
  );

   { A type to store register locations for 64 Bit values. }
      tregister64 = tregister;

   { alias for compact code }
   treg64 = tregister64;

   { Set type definition for registers }
    tregisterset = set of tregister;

   { Type definition for the array of string of register names }
   reg2strtable = array[tregister] of string[6];

const
  firstreg = low(tregister);
  lastreg  = high(tregister);

  firstsreg = R_CS;
  lastsreg  = R_GS;

  regset8bit  : tregisterset = [R_AL..R_DH];
  regset16bit : tregisterset = [R_AX..R_DI,R_CS..R_SS];
  regset32bit : tregisterset = [R_EAX..R_EDI];

  { Convert reg to opsize }
  reg2opsize:array[firstreg..lastreg] of topsize = (S_NO,
    S_Q,S_Q,S_Q,S_Q,S_Q,S_Q,S_Q,S_Q,
    S_Q,S_Q,S_Q,S_Q,S_Q,S_Q,S_Q,S_Q,S_Q,
    S_L,S_L,S_L,S_L,S_L,S_L,S_L,S_L,
    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_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,
    S_B,S_B,S_B,S_B,S_B,S_B,S_B,S_B,
    S_B,S_B,S_B,S_B,
    S_W,S_W,S_W,S_W,S_W,S_W,
    S_FL,S_FL,S_FL,S_FL,S_FL,S_FL,S_FL,S_FL,S_FL,
    S_L,S_L,S_L,S_L,S_L,S_L,
    S_L,S_L,S_L,S_L,
    S_L,S_L,S_L,S_L,S_L,
    S_D,S_D,S_D,S_D,S_D,S_D,S_D,S_D,
    S_D,S_D,S_D,S_D,S_D,S_D,S_D,S_D,
    S_D,S_D,S_D,S_D,S_D,S_D,S_D,S_D
  );

  { Standard opcode string table (for each tasmop enumeration). The
    opcode strings should conform to the names as defined by the
    processor manufacturer.
  }
  std_op2str:op2strtable={$i x86_64in.inc}

  { Standard register table (for each tregister enumeration). The
    register strings should conform to the the names as defined
    by the processor manufacturer
  }
  std_reg2str : reg2strtable = ('',
    'rax','rcx','rdx','rbx','rsp','rbp','rsi','rdi',
    'r8','r9','r10','r11','r12','r13','r14','r15','rip',
    'eax','ecx','edx','ebx','esp','ebp','esi','edi',
    'r8d','r9d','r10d','r11d','r12d','r13d','r14d','r15d',
    'ax','cx','dx','bx','sp','bp','si','di',
    'r8w','r9w','r10w','r11w','r12w','r13w','r14w','r15w',
    'al','cl','dl','bl','spl','bpl','sil','dil',
    'r8b','r9b','r10b','r11b','r12b','r13b','r14b','r15b',
    'ah','ch','bh','dh',
    'cs','ds','es','ss','fs','gs',
    'st','st(0)','st(1)','st(2)','st(3)','st(4)','st(5)','st(6)','st(7)',
    'dr0','dr1','dr2','dr3','dr6','dr7',
    'cr0','cr2','cr3','cr4',
    'tr3','tr4','tr5','tr6','tr7',
    'mm0','mm1','mm2','mm3','mm4','mm5','mm6','mm7',
    'xmm0','xmm1','xmm2','xmm3','xmm4','xmm5','xmm6','xmm7',
    'xmm8','xmm9','xmm10','xmm11','xmm12','xmm13','xmm14','xmm15');

{*****************************************************************************
                                Conditions
*****************************************************************************}

type
  TAsmCond=(C_None,
    C_A,C_AE,C_B,C_BE,C_C,C_E,C_G,C_GE,C_L,C_LE,C_NA,C_NAE,
    C_NB,C_NBE,C_NC,C_NE,C_NG,C_NGE,C_NL,C_NLE,C_NO,C_NP,
    C_NS,C_NZ,C_O,C_P,C_PE,C_PO,C_S,C_Z
  );

const
  cond2str:array[TAsmCond] of string[3]=('',
    'a','ae','b','be','c','e','g','ge','l','le','na','nae',
    'nb','nbe','nc','ne','ng','nge','nl','nle','no','np',
    'ns','nz','o','p','pe','po','s','z'
  );
  inverse_cond:array[TAsmCond] of TAsmCond=(C_None,
    C_NA,C_NAE,C_NB,C_NBE,C_NC,C_NE,C_NG,C_NGE,C_NL,C_NLE,C_A,C_AE,
    C_B,C_BE,C_C,C_E,C_G,C_GE,C_L,C_LE,C_O,C_P,
    C_S,C_Z,C_NO,C_NP,C_NP,C_P,C_NS,C_NZ
  );


{*****************************************************************************
                                   Flags
*****************************************************************************}

    type
       TResFlags = (F_E,F_NE,F_G,F_L,F_GE,F_LE,F_C,F_NC,F_A,F_AE,F_B,F_BE);

{*****************************************************************************
                                Reference
*****************************************************************************}

    type
      trefoptions=(ref_none,ref_parafixup,ref_localfixup,ref_selffixup);

      { immediate/reference record }
      preference = ^treference;
      treference = packed record
         offset      : longint;
         symbol      : tasmsymbol;
         offsetfixup : longint;
         segment,
         base,
         index       : tregister;
         scalefactor : byte;
         options     : trefoptions;
         alignment   : byte;
      end;

      { reference record }
      pparareference = ^tparareference;
      tparareference = packed record
         index       : tregister;
         offset      : longint;
      end;

{*****************************************************************************
                                Operands
*****************************************************************************}

       { Types of operand }
        toptype=(top_none,top_reg,top_ref,top_const,top_symbol);

        toper=record
          ot  : longint;
          case typ : toptype of
           top_none   : ();
           top_reg    : (reg:tregister);
           top_ref    : (ref:preference);
           top_const  : (val:longint);
           top_symbol : (sym:tasmsymbol;symofs:longint);
        end;

{*****************************************************************************
                               Generic Location
*****************************************************************************}

    type
       TLoc=(
         { added for tracking problems}
         LOC_INVALID,
         { contant }
         LOC_CONSTANT,
         { in a processor register }
         LOC_REGISTER,
         { in memory }
         LOC_CREFERENCE,
         { like LOC_MEM, but lvalue }
         LOC_REFERENCE,
         { boolean results only, jump to false or true label }
         LOC_JUMP,
         { boolean results only, flags are set }
         LOC_FLAGS,
         { Constant register which shouldn't be modified }
         LOC_CREGISTER,
         { MMX register }
         LOC_MMXREGISTER,
         { Constant MMX register }
         LOC_CMMXREGISTER,
         { FPU stack }
         LOC_FPUREGISTER,
         { if it is a FPU register variable on the fpu stack }
         LOC_CFPUREGISTER,
         LOC_MMREGISTER,
         LOC_CMMREGISTER
       );

      { tparamlocation describes where a parameter for a procedure is stored.
        References are given from the caller's point of view. The usual
        TLocation isn't used, because contains a lot of unnessary fields.
      }
      tparalocation = packed record
         loc  : TLoc;
         sp_fixup : longint;
         size : TCGSize;
         case TLoc of
            LOC_REFERENCE : (reference : tparareference);
            { segment in reference at the same place as in loc_register }
            LOC_REGISTER,LOC_CREGISTER : (
              case longint of
                1 : (register,registerhigh : tregister);
                { overlay a registerlow }
                2 : (registerlow : tregister);
                { overlay a 64 Bit register type }
                3 : (reg64 : tregister64);
                4 : (register64 : tregister64);
              );
            { it's only for better handling }
            LOC_MMXREGISTER,LOC_CMMXREGISTER : (mmxreg : tregister);
      end;

  plocation = ^tlocation;
  tlocation = packed record
     loc : tloc;
     size : TCGSize;
     case  TLoc of
        LOC_FLAGS : (resflags : tresflags);
        LOC_CONSTANT : (
          case longint of
             1 : (value : AWord);
             2 : (valuelow, valuehigh:AWord);
             { overlay a complete 64 Bit value }
             3 : (valueqword : qword);
        );
        LOC_CREFERENCE,LOC_REFERENCE : (reference : treference);
        { segment in reference at the same place as in loc_register }
        LOC_REGISTER,LOC_CREGISTER : (
        case longint of
          1 : (register,segment,registerhigh : tregister);
          { overlay a registerlow }
          2 : (registerlow : tregister);
          { overlay a 64 Bit register type }
          3 : (reg64 : tregister64);
          4 : (register64 : tregister64);
        );
        { it's only for better handling }
        LOC_MMXREGISTER,LOC_CMMXREGISTER : (mmxreg : tregister);
  end;

{*****************************************************************************
                                 Constants
*****************************************************************************}

    const
       max_operands = 3;

       lvaluelocations = [LOC_REFERENCE,LOC_CFPUREGISTER,
         LOC_CREGISTER,LOC_MMXREGISTER,LOC_CMMXREGISTER];

       ALL_REGISTERS = [R_EAX..R_XMM15];

       general_registers = [R_EAX,R_EBX,R_ECX,R_EDX];

       { low and high of the available maximum width integer general purpose }
       { registers                                                           }
       LoGPReg = R_EAX;
       HiGPReg = R_EDI;

     { low and high of every possible width general purpose register (same as }
     { above on most architctures apart from the 80x86)                       }
       LoReg = R_EAX;
       HiReg = R_BL;

       intregs = general_registers;

       maxvarregs = 4;
       varregs : array[1..maxvarregs] of tregister =
         (R_EBX,R_EDX,R_ECX,R_EAX);

       usableregsint = [R_EAX,R_EBX,R_ECX,R_EDX];
       c_countusableregsint = 4;

       maxfpuvarregs = 16;

       maxintregs = maxvarregs;
       maxfpuregs = maxfpuvarregs;

       fpuregs = [R_ST0..R_ST7];
       usableregsfpu = [];
       c_countusableregsfpu = 0;

       mmregs = [R_MM0..R_MM7];
       usableregsmm = [R_XMM0..R_XMM15];
       c_countusableregsmm  = 8;
       
      { no distinction on this platform }      
       maxaddrregs = 0;
       addrregs    = [];
       usableregsaddr = [];
       c_countusableregsaddr = 0;
       

       firstsaveintreg = R_EAX;
       lastsaveintreg  = R_R15;
       firstsavefpureg = R_NO;
       lastsavefpureg  = R_NO;
       firstsavemmreg  = R_XMM0;
       lastsavemmreg   = R_XMM15;

       registers_saved_on_cdecl = [R_ESI,R_EDI,R_EBX];

       scratch_regs : array[1..1] of tregister = (R_EDI);


{*****************************************************************************
                          Default generic sizes
*****************************************************************************}
      { Defines the default address size for a processor, }
      OS_ADDR = OS_32;
      { the natural int size for a processor,             }
      OS_INT = OS_32;
      { the maximum float size for a processor,           }
      OS_FLOAT = OS_F80;
      { the size of a vector register for a processor     }
      OS_VECTOR = OS_M64;

      cpuflags = [];

      { sizes }
      pointersize   = 8;
      extended_size = 10;
      sizepostfix_pointer = S_L;


{*****************************************************************************
                          Generic Register names
*****************************************************************************}

       { location of function results }

       stack_pointer_reg = R_RSP;
       frame_pointer_reg = R_RBP;
       self_pointer_reg  = R_RSI;
       accumulator   = R_RAX;
  {the return_result_reg, is used inside the called function to store its return
  value when that is a scalar value otherwise a pointer to the address of the
  result is placed inside it}
  return_result_reg   = accumulator;

  {the function_result_reg contains the function result after a call to a scalar
  function othewise it contains a pointer to the returned result}
  function_result_reg = accumulator;
       accumulatorhigh = R_RDX;
       { the register where the vmt offset is passed to the destructor }
       { helper routine                                                }
       vmt_offset_reg = R_RDI;

       resultreg = R_RAX;
       resultreg64 = R_RAX;
       fpu_result_reg = R_ST;

{*****************************************************************************
                       GCC /ABI linking information
*****************************************************************************}

    const
      { Registers which must be saved when calling a routine declared as
        cppdecl, cdecl, stdcall, safecall, palmossyscall. The registers
        saved should be the ones as defined in the target ABI and / or GCC.

        This value can be deduced from the CALLED_USED_REGISTERS array in the
        GCC source.
      }
      std_saved_registers = [R_ESI,R_EDI,R_EBX];
      { Required parameter alignment when calling a routine declared as
        stdcall and cdecl. The alignment value should be the one defined
        by GCC or the target ABI.

         The value of this constant is equal to the constant
         PARM_BOUNDARY / BITS_PER_UNIT in the GCC source.
      }
      std_param_align = 8;

{*****************************************************************************
                                  Helpers
*****************************************************************************}

    function is_calljmp(o:tasmop):boolean;

    function flags_to_cond(const f: TResFlags) : TAsmCond;


implementation

{*****************************************************************************
                                  Helpers
*****************************************************************************}

    function is_calljmp(o:tasmop):boolean;
      begin
        case o of
          A_CALL,
          A_JCXZ,
          A_JECXZ,
          A_JMP,
          A_LOOP,
          A_LOOPE,
          A_LOOPNE,
          A_LOOPNZ,
          A_LOOPZ,
          A_Jcc :
            is_calljmp:=true;
          else
            is_calljmp:=false;
        end;
      end;


    function flags_to_cond(const f: TResFlags) : TAsmCond;
      const
        flags_2_cond : array[TResFlags] of TAsmCond =
          (C_E,C_NE,C_G,C_L,C_GE,C_LE,C_C,C_NC,C_A,C_AE,C_B,C_BE);
      begin
        result := flags_2_cond[f];
      end;


end.
{
  $Log$
  Revision 1.6  2003-02-02 19:25:54  carl
    * Several bugfixes for m68k target (register alloc., opcode emission)
    + VIS target
    + Generic add more complete (still not verified)

  Revision 1.5  2003/01/05 13:36:54  florian
    * x86-64 compiles
    + very basic support for float128 type (x86-64 only)

  Revision 1.4  2002/11/17 18:26:16  mazen
  * fixed a compilation bug accmulator-->accumulator, in definition of return_result_reg

  Revision 1.3  2002/11/17 17:49:09  mazen
  + return_result_reg and function_result_reg are now used, in all plateforms, to pass functions result between called function and its caller. See the explanation of each one

  Revision 1.2  2002/07/25 22:55:33  florian
    * several fixes, small test units can be compiled

  Revision 1.1  2002/07/24 22:38:15  florian
    + initial release of x86-64 target code

}
