uglobal
align 4
  LastAddr      dd ?
  LastPtrLine   dd ?
  LastVirt      db ?

align 4
  ptrMainFilename dd ?  ; pointer to a copy of the main filename.

  ptrDebugInfo  dd ?   ; Dynamic array with TDebugLine elements.
  ptrListing    dd ?   ; Pointer to TMemoryStream structures with the listing.

  AsmLines      dd ?   ; count of the lines assembled to code
  TotalLines    dd ?   ; count of all compiled lines
endg

cInitListingSize = 65536

proc InitDebugInfo
begin
        stdcall ClearDebugInfo

        stdcall StrLen, [input_file]
        add     eax, 1
        push    eax

        stdcall GetMem, eax
        mov     [ptrMainFilename], eax
        mov     edi, eax
        mov     esi, [input_file]
        pop     ecx
        rep movsb

        stdcall CreateArray, sizeof.TDebugLine
        mov     [ptrDebugInfo], eax

        stdcall CreateMemoryStream, ptrListing, cInitListingSize
        return
endp



proc  ClearDebugInfo
begin
        cmp     [ptrDebugInfo], 0
        je      .debug_info_ok

        stdcall FreeMem, [ptrDebugInfo]
        mov     [ptrDebugInfo], 0

.debug_info_ok:
        cmp     [ptrListing], 0
        je      .listing_ok

        stdcall FreeMem, [ptrListing]
        mov     [ptrListing], 0

.listing_ok:
        cmp     [ptrMainFilename], 0
        je      .clearok

        stdcall FreeMem, [ptrMainFilename]
        mov     [ptrMainFilename], 0

.clearok:
        return
endp




proc CaptureDebugInfo, .ptrFAS
.asmlen dd ?
begin
        pushad

        stdcall InitDebugInfo

        mov     ebx, [.ptrFAS]
        mov     esi, [ebx+TDumpHeader.ofsAsmDump]
        add     esi, ebx

        mov     eax, [ebx+TDumpHeader.lenAsmDump]
        mov     [.asmlen], eax

        mov     [TotalLines], 0
        mov     [AsmLines], 0

.linesloop:
        cmp     [.asmlen], 4
        jbe     .endofdump

        inc     [TotalLines]

        cmp     [esi+TAsmDumpElement.flagVirtual], 0
        jne     .nextline

        cmp     [esi+TAsmDumpElement.extSIB],0
        jne     .nextline

        cmp     [.asmlen], 4+sizeof.TAsmDumpElement       ; last line of the code...???
        ja      .notlast

        mov     eax, dword [esi+TAsmDumpElement.ofsBinary]
        cmp     eax, dword [esi+sizeof.TAsmDumpElement]
        je      .nextline
        jmp     .thisline

.notlast:
        cmp     [esi+TAsmDumpElement.extSIB+sizeof.TAsmDumpElement], 0
        jne     .nextline

        cmp     [esi+TAsmDumpElement.flagVirtual+sizeof.TAsmDumpElement], 0
        jne     .nextline

        mov     eax, dword [esi+TAsmDumpElement.ofsBinary]
        cmp     eax, dword [esi+TAsmDumpElement.ofsBinary+sizeof.TAsmDumpElement]
        je      .nextline

.thisline:
        inc     [AsmLines]

        mov     ebx, [esi+TAsmDumpElement.ofsPreprocessed]
        add     ebx, [ptrPreprocessed]

; process one line, pointed by ebx
        stdcall CreateLineString, ebx
        push    eax

.findbaseline:
        test    byte [ebx+TIntLine.LineNumber+3], $80
        jz      .lineok

        mov     eax, [ebx+TIntLine.ofsGenerator]
        add     eax, [ptrPreprocessed]
        cmp     word [eax+1], '__'      ; is it internal macro definition?
        je      .searchdown

        mov     ebx, [ebx+TIntLine.ofsSourceLine] ; get the line where the macro was invoked.
        add     ebx, [ptrPreprocessed]
        jmp     .findbaseline

.searchdown:
        mov     ebx, [ebx+TIntLine.ofsMacroLine]
        add     ebx, [ptrPreprocessed]
        test    byte [ebx+TIntLine.LineNumber+3], $80
        jnz     .searchdown

.lineok:
        stdcall AddArrayItems, [ptrDebugInfo], 1
        mov     [ptrDebugInfo], edx
        mov     edi, eax

        pop     [edi+TDebugLine.strLine]

        mov     eax, dword [esi+TAsmDumpElement.Address]
        mov     edx, dword [esi+TAsmDumpElement.Address+4]
        mov     dword [edi+TDebugLine.Address], eax
        mov     dword [edi+TDebugLine.Address+4], edx

        mov     ecx, dword [ebx+TIntLine.LineNumber] ; line number.
        mov     [edi+TDebugLine.LineNum], ecx

        mov     ecx, [ebx+TIntLine.ofsGenerator]
        test    ecx, ecx
        jnz     .fileok

        mov     ecx, [ptrMainFilename]
        sub     ecx, [ptrPreprocessed]

.fileok:
        add     ecx, [ptrPreprocessed]
        mov     [edi+TDebugLine.ptrFile], ecx

.nextline:
        add     esi, sizeof.TAsmDumpElement
        sub     [.asmlen], sizeof.TAsmDumpElement
        jmp     .linesloop

.endofdump:
        stdcall VacuumArray, [ptrDebugInfo]

        popad
        return
endp








proc CreateLineString, .ptrLine
.char dd ?
begin
        push    esi edi ebx

        stdcall GetStreamRoom, ptrListing, $1000
        push    edi                             ; this will be returned.
        add     edi, ebx

        mov     esi, [.ptrLine]
        lea     esi, [esi+TIntLine.PreprocessedLine]

        mov     [.char], $09

.getloop:
        lodsb
        test    al, al
        jz      .eol
        cmp     al, $3b
        je      .ignore
        cmp     al, $1a         ; prefix of token
        je      .token
        cmp     al, $22         ; quoted text.
        je      .quoted

        stosb
        cmp     al,','
        jne     .getloop
        mov     al,$20
        stosb
        jmp     .getloop

.quoted:
        mov     al, "'"
        stosb

        lodsd   ; length
        mov     ecx, eax
        rep movsb

        mov     al, "'"
        stosb
        jmp     .getloop

.token:
        movzx   ecx, byte [esi]
        inc     esi
        rep movsb               ; copy the token to the output string.

        cmp     byte [esi], '['
        je      .tab
        cmp     byte [esi], $1a
        jne     .getloop
.tab:
        mov     al, byte [.char]
        mov     byte [.char], $20
        stosb
        jmp     .getloop

.ignore:
        mov     al, 0

.eol:
        stosb

        sub     edi, ebx
        mov     [ebx+TMemoryStream.offset], edi

        pop     eax
        pop     ebx edi esi
        return
endp




;--------------------------------------------------------------
; Find the file and line of the source, where some address
; is generate.
; Returns:
;   eax: pointer to the TDebugLine record - 0 means not found
;--------------------------------------------------------
proc FindAddressPosition, .addr
begin
        push    ecx edx

        mov     eax, [ptrDebugInfo]
        test    eax, eax
        jz      .notfound

        mov     ecx, [eax+TArray.count]
        lea     eax, [eax+TArray.array]
        mov     edx, [.addr]

.search:
        jecxz   .notfound
        cmp     edx, dword [eax+TDebugLine.Address]
        jbe     .finish
        add     eax, sizeof.TDebugLine
        dec     ecx
        jmp     .search

.notfound:
        xor     eax, eax

.finish:
        pop     edx ecx
        return
endp







;------------------------------------------------------------------
; Positions the editor to the file/line
; This procedure can remember the browsing history and then to
; quick navigate the user to the previously browsed source.
;------------------------------------------------------------------
uglobal
  fSetCurrent dd ?
endg


proc PosEditor, .hFileName, .LineNum, .fLogit
.aepos AEPOS
.name  dd ?
begin
        push    eax ebx ecx edx

        stdcall ConvertPath, [.hFileName]
        mov     [.name], eax

; log the current file in the history.
;        cmp     [.fLogit], 0
;        je      .logok
;        stdcall LogInHistory
;.logok:

        stdcall OpenFileByName, eax, NULL, TRUE
        test    eax, eax
        jz      .finish

        mov     ebx, [eax+TOpenFile.hEditor]
        mov     [fSetCurrent], ebx

        invoke  SendMessageA, ebx, AEM_SETFOCUSLINE, [.LineNum], 0
        invoke  RedrawWindow, ebx, NULL, NULL, RDW_UPDATENOW

        invoke  SetForegroundWindow, ebx

; workaround, because AEM_SETFOCUSLINE do not move the caret.
        mov     [.aepos.selectionPosition], 1
        mov     [.aepos.caretPosition], 1
        mov     eax, [.LineNum]
        mov     [.aepos.selectionLine], eax
        mov     [.aepos.caretLine], eax
        lea     eax, [.aepos]
        invoke  SendMessageA, ebx, AEM_SETPOS, eax, 0

; Work around, because of some bug in AsmEdit, that make caret to dissapear, even when
; the control have an input focus.
        invoke  SetFocus, 0
        invoke  SetFocus, ebx

        mov     [fSetCurrent], 0
.finish:
        stdcall StrDel, [.name]
        pop     edx ecx ebx eax
        return
endp



;----------------------------------------------------------
; Returns the address in binary of some line from file
;----------------------------------------------------------
proc FindAddress, .hFilename, .linenum
begin

endp