{
    Copyright (c) 2014 by Florian Klaempfl

    Symbol table overrides for i8086

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

{$i fpcdefs.inc}

interface

uses
  globtype,
  symconst,symtype,symdef,symsym,symx86,symi86;

type
  { defs }
  tcpufiledef = class(tfiledef)
  end;
  tcpufiledefclass = class of tcpufiledef;

  tcpuvariantdef = class(tvariantdef)
  end;
  tcpuvariantdefclass = class of tcpuvariantdef;

  tcpuformaldef = class(tformaldef)
  end;
  tcpuformaldefclass = class of tcpuformaldef;

  tcpuforwarddef = class(tforwarddef)
  end;
  tcpuforwarddefclass = class of tcpuforwarddef;

  tcpuundefineddef = class(tundefineddef)
  end;
  tcpuundefineddefclass = class of tcpuundefineddef;

  tcpuerrordef = class(terrordef)
  end;
  tcpuerrordefclass = class of tcpuerrordef;

  tcpupointerdef = class(tx86pointerdef)
    class function default_x86_data_pointer_type: tx86pointertyp; override;
    function alignment:shortint;override;
    function pointer_arithmetic_int_type:tdef; override;
    function pointer_arithmetic_uint_type:tdef; override;
    function pointer_subtraction_result_type:tdef; override;
    function converted_pointer_to_array_range_type: tdef; override;
  end;
  tcpupointerdefclass = class of tcpupointerdef;

  tcpurecorddef = class(trecorddef)
  end;
  tcpurecorddefclass = class of tcpurecorddef;

  tcpuimplementedinterface = class(timplementedinterface)
  end;
  tcpuimplementedinterfaceclass = class of tcpuimplementedinterface;

  tcpuobjectdef = class(tobjectdef)
  end;
  tcpuobjectdefclass = class of tcpuobjectdef;

  tcpuclassrefdef = class(tclassrefdef)
    function alignment:shortint;override;
  end;
  tcpuclassrefdefclass = class of tcpuclassrefdef;

  { tcpuarraydef }

  tcpuarraydef = class(tarraydef)
   private
    huge: Boolean;
   protected
    procedure ppuload_platform(ppufile: tcompilerppufile); override;
    procedure ppuwrite_platform(ppufile: tcompilerppufile); override;
   public
    constructor create_from_pointer(def:tpointerdef);override;
    function getcopy: tstoreddef; override;
    function GetTypeName:string;override;
    property is_huge: Boolean read huge write huge;
  end;
  tcpuarraydefclass = class of tcpuarraydef;

  tcpuorddef = class(torddef)
  end;
  tcpuorddefclass = class of tcpuorddef;

  tcpufloatdef = class(tfloatdef)
  end;
  tcpufloatdefclass = class of tcpufloatdef;

  { tcpuprocvardef }

  tcpuprocvardef = class(ti86procvardef)
    constructor create(level:byte);override;
    function getcopyas(newtyp:tdeftyp;copytyp:tproccopytyp;const paraprefix:string):tstoreddef;override;
    function address_type:tdef;override;
    function ofs_address_type:tdef;override;
    function size:asizeint;override;
    procedure declared_far;override;
    procedure declared_near;override;
    function is_far:boolean;
  end;
  tcpuprocvardefclass = class of tcpuprocvardef;

  { tcpuprocdef }

  tcpuprocdef = class(ti86procdef)
   private
    { returns whether the function is far by default, i.e. whether it would be
      far if _all_ of the following conditions are true:
      - we're in a far code memory model
      - it has no 'near' or 'far' specifiers
      - it is compiled in a $F- state }
    function default_far:boolean;
    procedure Setinterfacedef(AValue: boolean);override;
   public
    constructor create(level:byte;doregister:boolean);override;
    function getcopyas(newtyp:tdeftyp;copytyp:tproccopytyp;const paraprefix:string):tstoreddef;override;
    function address_type:tdef;override;
    function ofs_address_type:tdef;override;
    function size:asizeint;override;
    procedure declared_far;override;
    procedure declared_near;override;
    function is_far:boolean;
  end;
  tcpuprocdefclass = class of tcpuprocdef;

  tcpustringdef = class(tstringdef)
  end;
  tcpustringdefclass = class of tcpustringdef;

  tcpuenumdef = class(tenumdef)
  end;
  tcpuenumdefclass = class of tcpuenumdef;

  tcpusetdef = class(tsetdef)
  end;
  tcpusetdefclass = class of tcpusetdef;

  { syms }
  tcpulabelsym = class(tlabelsym)
  end;
  tcpulabelsymclass = class of tcpulabelsym;

  tcpuunitsym = class(tunitsym)
  end;
  tcpuunitsymclass = class of tcpuunitsym;

  tcpuprogramparasym = class(tprogramparasym)
  end;
  tcpuprogramparasymclass = class(tprogramparasym);

  tcpunamespacesym = class(tnamespacesym)
  end;
  tcpunamespacesymclass = class of tcpunamespacesym;

  tcpuprocsym = class(tprocsym)
  end;
  tcpuprocsymclass = class of tcpuprocsym;

  tcputypesym = class(ttypesym)
  end;
  tcpuypesymclass = class of tcputypesym;

  tcpufieldvarsym = class(tfieldvarsym)
  end;
  tcpufieldvarsymclass = class of tcpufieldvarsym;

  tcpulocalvarsym = class(tlocalvarsym)
  end;
  tcpulocalvarsymclass = class of tcpulocalvarsym;

  tcpuparavarsym = class(tparavarsym)
  end;
  tcpuparavarsymclass = class of tcpuparavarsym;

  tcpustaticvarsym = class(tstaticvarsym)
  end;
  tcpustaticvarsymclass = class of tcpustaticvarsym;

  tcpuabsolutevarsym = class(ti86absolutevarsym)
   protected
    procedure ppuload_platform(ppufile: tcompilerppufile); override;
    procedure ppuwrite_platform(ppufile: tcompilerppufile); override;
   public
    addrsegment : aword;
  end;
  tcpuabsolutevarsymclass = class of tcpuabsolutevarsym;

  tcpupropertysym = class(tpropertysym)
  end;
  tcpupropertysymclass = class of tcpupropertysym;

  tcpuconstsym = class(tconstsym)
  end;
  tcpuconstsymclass = class of tcpuconstsym;

  tcpuenumsym = class(tenumsym)
  end;
  tcpuenumsymclass = class of tcpuenumsym;

  tcpusyssym = class(tsyssym)
  end;
  tcpusyssymclass = class of tcpusyssym;


const
   pbestrealtype : ^tdef = @s80floattype;


  function is_proc_far(p: tabstractprocdef): boolean;

  {# Returns true if p is a far proc var }
  function is_farprocvar(p : tdef): boolean;

  {# Returns true if p is a far pointer def }
  function is_farpointer(p : tdef) : boolean;

  {# Returns true if p is a huge pointer def }
  function is_hugepointer(p : tdef) : boolean;

implementation

  uses
    globals, cpuinfo, verbose, fmodule;


  function is_proc_far(p: tabstractprocdef): boolean;
  begin
    if p is tcpuprocdef then
      result:=tcpuprocdef(p).is_far
    else if p is tcpuprocvardef then
      result:=tcpuprocvardef(p).is_far
    else
      internalerror(2014041301);
  end;

  { true if p is a far proc var }
  function is_farprocvar(p : tdef): boolean;
    begin
      result:=(p.typ=procvardef) and tcpuprocvardef(p).is_far;
    end;

  { true if p is a far pointer def }
  function is_farpointer(p : tdef) : boolean;
    begin
      result:=(p.typ=pointerdef) and (tcpupointerdef(p).x86pointertyp=x86pt_far);
    end;

  { true if p is a huge pointer def }
  function is_hugepointer(p : tdef) : boolean;
    begin
      result:=(p.typ=pointerdef) and (tcpupointerdef(p).x86pointertyp=x86pt_huge);
    end;

{****************************************************************************
                               tcpuclassrefdef
****************************************************************************}

  function tcpuclassrefdef.alignment:shortint;
    begin
      Result:=2;
    end;

{****************************************************************************
                               tcpuarraydef
****************************************************************************}

  constructor tcpuarraydef.create_from_pointer(def: tpointerdef);
    begin
      huge:=tcpupointerdef(def).x86pointertyp=x86pt_huge;
      inherited create_from_pointer(def);
    end;


  function tcpuarraydef.getcopy: tstoreddef;
    begin
      result:=inherited;
      tcpuarraydef(result).huge:=huge;
    end;


  function tcpuarraydef.GetTypeName: string;
    begin
      Result:=inherited;
      if is_huge then
        Result:='Huge '+Result;
    end;


  procedure tcpuarraydef.ppuload_platform(ppufile: tcompilerppufile);
    begin
      inherited;
      huge:=(ppufile.getbyte<>0);
    end;


  procedure tcpuarraydef.ppuwrite_platform(ppufile: tcompilerppufile);
    begin
      inherited;
      ppufile.putbyte(byte(huge));
    end;


{****************************************************************************
                             tcpuprocdef
****************************************************************************}

  constructor tcpuprocdef.create(level: byte;doregister:boolean);
    begin
      inherited create(level,doregister);
      if (current_settings.x86memorymodel in x86_far_code_models) and
         ((cs_huge_code in current_settings.moduleswitches) or
          (cs_force_far_calls in current_settings.localswitches)) then
        procoptions:=procoptions+[po_far];
    end;


  function tcpuprocdef.getcopyas(newtyp:tdeftyp;copytyp:tproccopytyp;const paraprefix:string):tstoreddef;
    begin
      result:=inherited;
      if is_far then
        include(tabstractprocdef(result).procoptions,po_far)
      else
        exclude(tabstractprocdef(result).procoptions,po_far);
    end;


  function tcpuprocdef.address_type: tdef;
    begin
      if is_far then
        result:=voidfarpointertype
      else
        result:=voidnearpointertype;
    end;


  function tcpuprocdef.ofs_address_type:tdef;
    begin
      result:=voidnearpointertype;
    end;


  function tcpuprocdef.size: asizeint;
    begin
      result:=address_type.size;
    end;


  procedure tcpuprocdef.declared_far;
    begin
      include(procoptions,po_far);
      include(procoptions,po_hasnearfarcallmodel);
    end;


  procedure tcpuprocdef.declared_near;
    begin
      if not (cs_huge_code in current_settings.moduleswitches) then
        begin
          exclude(procoptions,po_far);
          include(procoptions,po_hasnearfarcallmodel);
        end
      else
        inherited declared_near;
    end;


  function tcpuprocdef.default_far: boolean;
    begin
      if proctypeoption in [potype_proginit,potype_unitinit,potype_unitfinalize,
                            potype_constructor,potype_destructor,
                            potype_class_constructor,potype_class_destructor,
                            potype_propgetter,potype_propsetter] then
        exit(true);
      if (procoptions*[po_classmethod,po_virtualmethod,po_abstractmethod,
                       po_finalmethod,po_staticmethod,po_overridingmethod,
                       po_external,po_public,po_interrupt])<>[] then
        exit(true);
      if is_methodpointer then
        exit(true);
      result:=not (visibility in [vis_private,vis_hidden]);
    end;


  procedure tcpuprocdef.Setinterfacedef(AValue: boolean);
    begin
      inherited;
      if (current_settings.x86memorymodel in x86_far_code_models) and AValue then
        include(procoptions,po_far);
    end;


  function tcpuprocdef.is_far: boolean;
    begin
      result:=(po_exports in procoptions) or
              (po_far in procoptions) or
              ((current_settings.x86memorymodel in x86_far_code_models) and default_far);
    end;

{****************************************************************************
                             tcpuprocvardef
****************************************************************************}

  constructor tcpuprocvardef.create(level: byte);
    begin
      inherited create(level);
      if current_settings.x86memorymodel in x86_far_code_models then
        procoptions:=procoptions+[po_far];
    end;


  function tcpuprocvardef.getcopyas(newtyp:tdeftyp;copytyp:tproccopytyp;const paraprefix:string):tstoreddef;
    begin
      result:=inherited;
      if is_far then
        include(tabstractprocdef(result).procoptions,po_far)
      else
        exclude(tabstractprocdef(result).procoptions,po_far);
    end;


  function tcpuprocvardef.address_type:tdef;
    begin
      if is_addressonly then
        if is_far then
          result:=voidfarpointertype
        else
          begin
            { near }
            if current_settings.x86memorymodel=mm_tiny then
              result:=voidnearpointertype
            else
              result:=voidnearcspointertype;
          end
      else
        result:=inherited;
    end;


  function tcpuprocvardef.ofs_address_type:tdef;
    begin
      result:=voidnearpointertype;
    end;


  function tcpuprocvardef.size:asizeint;
    begin
      if is_addressonly then
        if is_far then
          result:=4
        else
          result:=2
      else
        result:=inherited;
    end;


  procedure tcpuprocvardef.declared_far;
    begin
      if is_addressonly then
        begin
          include(procoptions,po_far);
          include(procoptions,po_hasnearfarcallmodel);
        end
      else
        inherited;
    end;


  procedure tcpuprocvardef.declared_near;
    begin
      if is_addressonly then
        begin
          exclude(procoptions,po_far);
          include(procoptions,po_hasnearfarcallmodel);
        end
      else
        inherited;
    end;


  function tcpuprocvardef.is_far: boolean;
    begin
      if is_addressonly then
        result:=po_far in procoptions
      else
        result:=current_settings.x86memorymodel in x86_far_code_models;
    end;

{****************************************************************************
                             tcpupointerdef
****************************************************************************}

    class function tcpupointerdef.default_x86_data_pointer_type: tx86pointertyp;
      begin
        if current_settings.x86memorymodel in x86_far_data_models then
          result:=x86pt_far
        else
          result:=inherited;
      end;


    function tcpupointerdef.alignment:shortint;
      begin
        { on i8086, we use 16-bit alignment for all pointer types, even far and
          huge (which are 4 bytes long) }
        result:=2;
      end;


    function tcpupointerdef.pointer_arithmetic_int_type:tdef;
      begin
        case x86pointertyp of
          x86pt_huge:
            result:=s32inttype;
          x86pt_far,
          x86pt_near,
          x86pt_near_cs,
          x86pt_near_ds,
          x86pt_near_ss,
          x86pt_near_es,
          x86pt_near_fs,
          x86pt_near_gs:
            result:=s16inttype;
          else
            internalerror(2016100403);
        end;
      end;


    function tcpupointerdef.pointer_arithmetic_uint_type:tdef;
      begin
        case x86pointertyp of
          x86pt_huge:
            result:=u32inttype;
          x86pt_far,
          x86pt_near,
          x86pt_near_cs,
          x86pt_near_ds,
          x86pt_near_ss,
          x86pt_near_es,
          x86pt_near_fs,
          x86pt_near_gs:
            result:=u16inttype;
          else
            internalerror(2016100403);
        end;
      end;


    function tcpupointerdef.pointer_subtraction_result_type:tdef;
      begin
        case x86pointertyp of
          x86pt_huge:
            result:=s32inttype;
          x86pt_far:
            result:=u16inttype;
          x86pt_near,
          x86pt_near_cs,
          x86pt_near_ds,
          x86pt_near_ss,
          x86pt_near_es,
          x86pt_near_fs,
          x86pt_near_gs:
            result:=s16inttype;
          else
            internalerror(2016100402);
        end;
      end;


    function tcpupointerdef.converted_pointer_to_array_range_type: tdef;
      begin
        case x86pointertyp of
          x86pt_huge:
            result:=s32inttype;
          x86pt_far,
          x86pt_near,
          x86pt_near_cs,
          x86pt_near_ds,
          x86pt_near_ss,
          x86pt_near_es,
          x86pt_near_fs,
          x86pt_near_gs:
            result:=s16inttype;
          else
            internalerror(2016100401);
        end;
      end;


{****************************************************************************
                             tcpuabsolutevarsym
****************************************************************************}

  procedure tcpuabsolutevarsym.ppuload_platform(ppufile: tcompilerppufile);
    begin
      inherited;
      if absseg then
        addrsegment:=ppufile.getaword;
    end;


  procedure tcpuabsolutevarsym.ppuwrite_platform(ppufile: tcompilerppufile);
    begin
      inherited;
      if absseg then
        ppufile.putaword(addrsegment);
    end;

begin
  { used tdef classes }
  cfiledef:=tcpufiledef;
  cvariantdef:=tcpuvariantdef;
  cformaldef:=tcpuformaldef;
  cforwarddef:=tcpuforwarddef;
  cundefineddef:=tcpuundefineddef;
  cerrordef:=tcpuerrordef;
  cpointerdef:=tcpupointerdef;
  crecorddef:=tcpurecorddef;
  cimplementedinterface:=tcpuimplementedinterface;
  cobjectdef:=tcpuobjectdef;
  cclassrefdef:=tcpuclassrefdef;
  carraydef:=tcpuarraydef;
  corddef:=tcpuorddef;
  cfloatdef:=tcpufloatdef;
  cprocvardef:=tcpuprocvardef;
  cprocdef:=tcpuprocdef;
  cstringdef:=tcpustringdef;
  cenumdef:=tcpuenumdef;
  csetdef:=tcpusetdef;

  { used tsym classes }
  clabelsym:=tcpulabelsym;
  cunitsym:=tcpuunitsym;
  cprogramparasym:=tcpuprogramparasym;
  cnamespacesym:=tcpunamespacesym;
  cprocsym:=tcpuprocsym;
  ctypesym:=tcputypesym;
  cfieldvarsym:=tcpufieldvarsym;
  clocalvarsym:=tcpulocalvarsym;
  cparavarsym:=tcpuparavarsym;
  cstaticvarsym:=tcpustaticvarsym;
  cabsolutevarsym:=tcpuabsolutevarsym;
  cpropertysym:=tcpupropertysym;
  cconstsym:=tcpuconstsym;
  cenumsym:=tcpuenumsym;
  csyssym:=tcpusyssym;
end.

