{
    Copyright (c) 2018 by Nikolay Nikolov

    This units contains support for Microsoft CodeView debug info generation

    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.

 ****************************************************************************
}
{ documentation for the format, available on the internet:

  earlier versions:
    http://pagesperso-orange.fr/pierrelib/exec_formats/MS_Symbol_Type_v1.0.pdf
    http://ftp.openwatcom.org/devel/docs/CodeView.pdf

  modern versions:
    https://llvm.org/docs/PDB/index.html
    https://llvm.org/devmtg/2016-11/Slides/Kleckner-CodeViewInLLVM.pdf
}

unit dbgcodeview;

{$i fpcdefs.inc}

interface

    uses
      aasmdata,
      dbgbase;

    type
      TSymbolIndex = (
        S_COMPILE    = $0001, { Compile flags symbol }
        S_REGISTER   = $0002, { Register variable }
        S_CONSTANT   = $0003, { Constant symbol }
        S_UDT        = $0004, { User-defined Type }
        S_SSEARCH    = $0005, { Start search }
        S_END        = $0006, { End block, procedure, with, or thunk }
        S_SKIP       = $0007, { Skip - Reserve symbol space }
        S_CVRESERVE  = $0008, { Reserved for internal use by the Microsoft debugger }
        S_OBJNAME    = $0009, { Specify name of object file }
        S_ENDARG     = $000a, { Specify end of arguments in function symbols }
        S_COBOLUDT   = $000b, { Microfocus COBOL user-defined type }
        S_MANYREG    = $000c, { Many register symbol }
        S_RETURN     = $000d, { Function return description }
        S_ENTRYTHIS  = $000e, { Description of this pointer at entry }
        S_BPREL16    = $0100, { BP relative 16:16 }
        S_LDATA16    = $0101, { Local data 16:16 }
        S_GDATA16    = $0102, { Global data 16:16 }
        S_PUB16      = $0103, { Public symbol 16:16 }
        S_LPROC16    = $0104, { Local procedure start 16:16 }
        S_GPROC16    = $0105, { Global procedure start 16:16 }
        S_THUNK16    = $0106, { Thunk start 16:16 }
        S_BLOCK16    = $0107, { Block start 16:16 }
        S_WITH16     = $0108, { With start 16:16 }
        S_LABEL16    = $0109, { Code label 16:16 }
        S_CEXMODEL16 = $010a, { Change execution model 16:16 }
        S_VFTPATH16  = $010b, { Virtual function table path descriptor 16:16 }
        S_REGREL16   = $010c, { Specify 16:16 offset relative to arbitrary register }
        S_BPREL32    = $0200, { BP relative 16:32 }
        S_LDATA32    = $0201, { Local data 16:32 }
        S_GDATA32    = $0202, { Global data 16:32 }
        S_PUB32      = $0203, { Public symbol 16:32 }
        S_LPROC32    = $0204, { Local procedure start 16:32 }
        S_GPROC32    = $0205, { Global procedure start 16:32 }
        S_THUNK32    = $0206, { Thunk start 16:32 }
        S_BLOCK32    = $0207, { Block start 16:32 }
        S_VFTPATH32  = $020b, { Virtual function table path descriptor 16:32 }
        S_REGREL32   = $020c, { 16:32 offset relative to arbitrary register }
        S_LTHREAD32  = $020d, { Local Thread Storage data }
        S_GTHREAD32  = $020e, { Global Thread Storage data }
        S_LPROCMIPS  = $0300, { Local procedure start MIPS }
        S_GPROCMIPS  = $0301, { Global procedure start MIPS }
        S_PROCREF    = $0400, { Reference to a procedure }
        S_DATAREF    = $0401, { Reference to data }
        S_ALIGN      = $0402  { Page align symbols }
      );
      TLeafIndex=(
        LF_MODIFIER   = $0001, { Type Modifier (const, volatile, unaligned) }
        LF_POINTER    = $0002, { Pointer }
        LF_ARRAY      = $0003, { Simple Array }
        LF_CLASS      = $0004, { Class (C++ class declaration) }
        LF_STRUCTURE  = $0005, { Structure (C and C++ struct declaration) }
        LF_UNION      = $0006, { Union }
        LF_ENUM       = $0007, { Enumeration }
        LF_PROCEDURE  = $0008, { Procedure }
        LF_MFUNCTION  = $0009, { Member Function }
        LF_VTSHAPE    = $000a, { Virtual Function Table Shape }
        LF_COBOL0     = $000b, { reserved for Microfocus COBOL }
        LF_COBOL1     = $000c, { reserved for Microfocus COBOL }
        LF_BARRAY     = $000d, { Basic Array }
        LF_LABEL      = $000e, { Label }
        LF_NULL       = $000f, { Null }
        LF_NOTTRAN    = $0010, { Not Translated }
        LF_DIMARRAY   = $0011, { Multiply Dimensioned Array }
        LF_VFTPATH    = $0012, { Path to Virtual Function Table }
        LF_PRECOMP    = $0013, { Reference Precompiled Types }
        LF_ENDPRECOMP = $0014, { End of Precompiled Types }
        LF_OEM        = $0015, { OEM Generic Type }
        LF_Reserved   = $0016, { Reserved }

        LF_PAD0       = $f0,
        LF_PAD1       = $f1,
        LF_PAD2       = $f2,
        LF_PAD3       = $f3,
        LF_PAD4       = $f4,
        LF_PAD5       = $f5,
        LF_PAD6       = $f6,
        LF_PAD7       = $f7,
        LF_PAD8       = $f8,
        LF_PAD9       = $f9,
        LF_PAD10      = $fa,
        LF_PAD11      = $fb,
        LF_PAD12      = $fc,
        LF_PAD13      = $fc,
        LF_PAD14      = $fe,
        LF_PAD15      = $ff,

        LF_SKIP       = $0200, { Skip (used by incremental compilers to reserve space for future indexes) }
        LF_ARGLIST    = $0201, { Argument List }
        LF_DEFARG     = $0202, { Default Argument }
        LF_LIST       = $0203, { Arbitrary List }
        LF_FIELDLIST  = $0204, { Field List }
        LF_DERIVED    = $0205, { Derived Classes }
        LF_BITFIELD   = $0206, { Bit Fields }
        LF_METHODLIST = $0207, { Method List }
        LF_DIMCONU    = $0208, { Dimensioned Array with Constant Upper Bound }
        LF_DIMCONLU   = $0209, { Dimensioned Array with Constant Lower and Upper Bounds }
        LF_DIMVARU    = $020a, { Dimensioned Array with Variable Upper Bound }
        LF_DIMVARLU   = $020b, { Dimensioned Array with Variable Lower and Upper Bounds }
        LF_REFSYM     = $020c, { Referenced Symbol }

        LF_BCLASS     = $0400, { Real Base Class }
        LF_VBCLASS    = $0401, { Direct Virtual Base Class }
        LF_IVBCLASS   = $0402, { Indirect Virtual Base Class }
        LF_ENUMERATE  = $0403, { Enumeration Name and Value }
        LF_FRIENDFCN  = $0404, { Friend Function }
        LF_INDEX      = $0405, { Index to Another Type Record }
        LF_MEMBER     = $0406, { Data Member }
        LF_STMEMBER   = $0407, { Static Data Member }
        LF_METHOD     = $0408, { Method }
        LF_NESTTYPE   = $0409, { Nested Type Definition }
        LF_VFUNCTAB   = $040a, { Virtual Function Table Pointer }
        LF_FRIENDCLS  = $040b, { Friend Class }
        LF_ONEMETHOD  = $040c, { One Method }
        LF_VFUNCOFF   = $040d, { Virtual Function Offset }

        LF_CHAR       = $8000, { Signed Char (8-bit value) }
        LF_SHORT      = $8001, { Signed Short (16-bit signed value) }
        LF_USHORT     = $8002, { Unsigned Short (16-bit unsigned value) }
        LF_LONG       = $8003, { Signed Long (32-bit signed value) }
        LF_ULONG      = $8004, { Unsigned Long (32-bit unsigned value) }
        LF_REAL32     = $8005, { 32-bit Float }
        LF_REAL64     = $8006, { 64-bit Float }
        LF_REAL80     = $8007, { 80-bit Float }
        LF_REAL128    = $8008, { 128-bit Float }
        LF_QUADWORD   = $8009, { Signed Quad Word (64-bit signed value) }
        LF_UQUADWORD  = $800a, { Unsigned Quad Word (64-bit unsigned value) }
        LF_REAL48     = $800b, { 48-bit Float }
        LF_COMPLEX32  = $800c, { 32-bit Complex }
        LF_COMPLEX64  = $800d, { 64-bit Complex }
        LF_COMPLEX80  = $800e, { 80-bit Complex }
        LF_COMPLEX128 = $800f, { 128-bit Complex }
        LF_VARSTRING  = $8010  { Variable-length String }
      );

    const
      LF_NUMERIC = LF_CHAR;

    type

      { TDebugInfoCodeView }

      TDebugInfoCodeView = class(TDebugInfo)
      public
        procedure insertlineinfo(list:TAsmList);override;
      end;

    procedure InsertLineInfo_OMF_LINNUM_MsLink(list: TAsmList);

implementation

    uses
      globtype,
      cutils,
      aasmtai,
      fmodule,
      systems;

    procedure InsertLineInfo_OMF_LINNUM_MsLink(list: TAsmList);
      var
        currfileinfo,
        lastfileinfo : tfileposinfo;
        nolineinfolevel : Integer;
        currfuncname : pshortstring;
        hp : tai;
      begin
        FillChar(lastfileinfo,sizeof(lastfileinfo),0);
        hp:=Tai(list.first);
        nolineinfolevel:=0;
        while assigned(hp) do
          begin
            case hp.typ of
              ait_function_name :
                begin
                  currfuncname:=tai_function_name(hp).funcname;
                  list.concat(tai_comment.Create(strpnew('function: '+currfuncname^)));
                end;
              ait_force_line :
                begin
                  lastfileinfo.line:=-1;
                end;
              ait_marker :
                begin
                  case tai_marker(hp).kind of
                    mark_NoLineInfoStart:
                      inc(nolineinfolevel);
                    mark_NoLineInfoEnd:
                      dec(nolineinfolevel);
                  end;
                end;
            end;

            { OMF LINNUM records do not support multiple source files }
            if (hp.typ=ait_instruction) and
               (nolineinfolevel=0) and
               (tailineinfo(hp).fileinfo.fileindex=main_module.unit_index) then
              begin
                currfileinfo:=tailineinfo(hp).fileinfo;

                { line changed ? }
                if (lastfileinfo.line<>currfileinfo.line) and (currfileinfo.line<>0) then
                  begin
                    { line directive }
                    list.insertbefore(tai_directive.Create(asd_omf_linnum_line,tostr(currfileinfo.line)),hp);
                  end;
                lastfileinfo:=currfileinfo;
              end;

            hp:=tai(hp.next);
          end;
      end;


{****************************************************************************
                             TDebugInfoCodeView
****************************************************************************}

    procedure TDebugInfoCodeView.insertlineinfo(list: TAsmList);
      begin
        InsertLineInfo_OMF_LINNUM_MsLink(list);
      end;

{****************************************************************************
****************************************************************************}
    const
      dbg_codeview_info : tdbginfo =
         (
           id     : dbg_codeview;
           idtxt  : 'CODEVIEW';
         );

initialization
  RegisterDebugInfo(dbg_codeview_info,TDebugInfoCodeView);

end.

