;-----------------------------------------------------------------------------------------------
;
; flat assembler interface for Fresh RAD IDE
;
; Copyright (c) 1999-2003, Tomasz Grysztar.
; Copyright (c) 2003, John Found
; All rights reserved.
;
;   This file is modified version of the fasm.inc file from
; FASMW package.
;
;-----------------------------------------------------------------------------------------------

cMinimalCompilerMemory = 8*1024    ; 8MB
cDefaultCompilerMemory = 128*1024  ; 128MB

iglobal
  _section_environment db 'Environment',0
endg


uglobal
  bytes_count           dd ?
  systime               SYSTEMTIME

  start_time            dd ?
  preprocess_time       dd ?
  parsing_time          dd ?
  capturing_time        dd ?
  assembling_time       dd ?
  formating_time        dd ?
  collect_time          dd ?

  total_time            dd ?

  exit_code             dd ?
  buffer                rb 1000h
endg

;
; Fresh internal variables not related to FASM compiler
uglobal
  CompiledFileName      dd ?

  hfile                 dd ?

  error_data_size       dd ?

  save_stack_pointer    dd ?
endg



align 16

include 'fasm\version.inc'

include 'fasm\errors.inc'
include 'fasm\symbdump.inc'
include 'fasm\preproce.inc'
include 'fasm\parser.inc'
include 'fasm\exprpars.inc'

include 'fasm\assemble.inc'

fasm_instructions:
include 'fasm\x86_64.inc'
include 'fasm\avx.inc'

include 'fasm\exprcalc.inc'
include 'fasm\formats.inc'


iglobal
  include 'fasm\messages.inc'
  include 'fasm\tables.inc'
endg

uglobal
  include 'fasm\variable.inc'
endg


uglobal
 fmsg TFreshMsg
 hStrFileName   dd ?
endg


;----------------------------------------------------------
; Prepares the compiler for compilation, allocating needed
; memory, preparing filename, path, etc.
; It is good to call from main thread.
;
; Returns:
;   eax - TRUE if compiler is ready.
;         FALSE if there is no free memory.
;----------------------------------------------------------
proc PrepareForCompilation, .hFileName
begin
; Init virtual cash with current files in the editor.
        stdcall InitVCash

; Init display strings.
        stdcall StrNew
        mov     [strDisplay], eax

        mov     [iLastIcon], -1
        mov     [hLastMsg], TVI_ROOT

; Init filename string
        cmp     [hStrFileName], 0
        je      .strnameok

        stdcall StrDel, [hStrFileName]

.strnameok:
        stdcall ConvertAliasString, [.hFileName]
        mov     [hStrFileName], eax

        stdcall StrPtr, eax
        mov     [input_file], eax

        stdcall ChangeDir, [hStrFileName]

        mov     [hfile], 0

        mov     [error_data_size], 0
        mov     [memory_start], 0

        mov     eax, [poMemory]
        shl     eax, 10               ; KB -> bytes
        jz      .out_of_memory        ; ???????

        mov     edx, eax
        shr     edx, 3
        mov     ecx, eax
        sub     ecx, edx
        mov     [memory_end],ecx
        mov     [additional_memory_end],edx

        mov     eax, [ptrMemoryAllocated]
        test    eax, eax
        jz      .out_of_memory

        mov     [memory_start], eax
        mov     [code_start], eax
        add     eax, [memory_end]
        mov     [memory_end], eax
        mov     [additional_memory], eax
        add     [additional_memory_end], eax

        xor     eax, eax
        inc     eax
        return

.out_of_memory:

        stdcall StrDel, [strDisplay]
        mov     [strDisplay], 0

        xor     eax, eax
        return
endp


;----------------------------------------------------------
; Free compiling resources.
; Frees all memory used by compiler, dynamic strings, etc.
; It is good to be called from main thread. (also in case of
; canceling compilation.
;----------------------------------------------------------
proc CleanupAfterCompilation
begin
        mov     ebx, [hfile]
        test    ebx,ebx
        jz      .handle_ok

        call    close

.handle_ok:
        stdcall StrDel, [strDisplay]
        stdcall DestroyVCash

        return
endp



;---------------------------------------------
; Compiles the file with filename in lParam
; You can use this procedure with stdcall
; or with CreateThread.
;---------------------------------------------
align 16
proc flat_assembler, .dummy
begin
        pushad

        mov     [save_stack_pointer], esp

        invoke  GetTickCount
        mov     [start_time], eax

; Preprocessing

        stdcall DisplayBlock, cMsgPreprocess
  call preprocessor
        stdcall ProgressWndStepIt
        invoke  GetTickCount
        mov     [preprocess_time], eax

; Parsing

        stdcall DisplayBlock, cMsgParsing
  call parser
        stdcall ProgressWndStepIt
        invoke  GetTickCount
        mov     [parsing_time], eax

; Assembling

        stdcall DisplayBlock, cMsgAssembling
  call assembler
        stdcall ProgressWndStepIt
        invoke  GetTickCount
        mov     [assembling_time], eax

; Format output file.

        stdcall DisplayBlock, cMsgFormating
  call    formatter
        stdcall ProgressWndStepIt
        invoke  GetTickCount
        mov     [formating_time], eax

; finalize the compilation.

        cmp     [CompiledFileName], 0
        je      @f
        stdcall StrDel, [CompiledFileName]
        mov     [CompiledFileName], 0
@@:
        cmp     [output_file], 0
        je      @f
        stdcall StrDup, [output_file]
        mov     [CompiledFileName], eax
@@:
        call    show_display_buffer

        invoke  GetTickCount
        mov     [total_time], eax

; Capture debug information
        stdcall DisplayBlock, cMsgDebugInfo

; close the modal progress window
        invoke  SendMessageW,[hProgressWnd], WM_COMMAND, MR_OK, 0

        cmp     [output_file], 0
        je      .fasok

        stdcall LoadBinaryFile, cDumpFilename
        pushf
        stdcall FileDelete, cDumpFilename
        popf
        jc      .fasok

        mov     esi, eax

;; debug +
;        stdcall GetTimestamp
;        mov     ebx, eax
;; debug -

        stdcall CopyPreprocessedSource, esi

;; debug +
;        stdcall GetTimestamp
;        sub     eax, ebx
;        stdcall Output, 'Copy preprocessed source: '
;        stdcall OutputNumber, eax, 10, 6
;        stdcall Output, cCRLF
;
;        stdcall GetTimestamp
;        mov     ebx, eax
;; debug -

        stdcall SortCrossReference, esi

;; debug +
;        stdcall GetTimestamp
;        sub     eax, ebx
;        stdcall Output, 'Sort cross reference: '
;        stdcall OutputNumber, eax, 10, 6
;        stdcall Output, cCRLF
;
;        stdcall GetTimestamp
;        mov     ebx, eax
;; debug -

        stdcall CreateLabelsTree, esi

;; debug +
;        stdcall GetTimestamp
;        sub     eax, ebx
;        stdcall Output, 'Create labels tree: '
;        stdcall OutputNumber, eax, 10, 6
;        stdcall Output, cCRLF
;
;        stdcall GetTimestamp
;        mov     ebx, eax
;; debug -

        stdcall CaptureDebugInfo, esi
        stdcall FreeMem, esi

;; debug +
;        stdcall GetTimestamp
;        sub     eax, ebx
;        stdcall Output, 'Capture debug info: '
;        stdcall OutputNumber, eax, 10, 6
;        stdcall Output, cCRLF
;
;        stdcall GetTimestamp
;        mov     ebx, eax
;; debug -

        stdcall CreateGlobalIndex           ; creates the index of the global labels.

;; debug +
;        stdcall GetTimestamp
;        sub     eax, ebx
;        stdcall Output, 'Create globals index: '
;        stdcall OutputNumber, eax, 10, 6
;        stdcall Output, cCRLF
;        stdcall Output, cCRLF
;; debug -
                                                                        ;cCRLF text 13, 10
        stdcall UpdateStatusbarCompiler

.fasok:
        xor     al,al

.exit_program:
        movzx   eax, al
        mov     [exit_code], eax

        test    eax, eax
        jz      @f
        invoke  SendMessageW,[hProgressWnd], WM_COMMAND, MR_ABORT, 0
@@:
        mov     esp, [save_stack_pointer]
        popad
        mov     eax, [exit_code]

        invoke  CloseHandle, [comp_handle]
        mov     [comp_handle], 0
        return
endp

iglobal
;  cTotalLines    db  'Total lines: ', 0
;  cAsmLines      db  'Binary code lines: ', 0

  cCompileTime db 'Total time: ',0
  cPreprocessTime db 'Preprocessing: ', 0
  cParsingTime    db 'Parsing:', 0
  cAssemblingTime db 'Assembling: ', 0
  cFormatTime     db 'Formating: ',0

  cTimeUnits   db ' seconds',0
  cCompilePasses db 'Passes: ', 0
  cCompileSize   db 'Compiled: ', 0
  cSizeUnits     db ' bytes',0
  cSummary       db 'Compilation summary:', 0

  cCanceled     db 'Canceled by the user.',0

endg




proc AddCompilationStatistics
begin
        pushad

        invoke  LockWindowUpdate, [tvMessagesWin]

; Compilation summary display message:
        mov     [fmsg.link.hFileName], 0
        mov     [fmsg.type], mtInfo
        mov     [fmsg.hMessage], cSummary
        invoke  SendMessageW, [frmMsg], MWM_ADDMESSAGE, fmsg, TVI_ROOT
        mov     ebx, eax

; display times
        mov     eax, [total_time]
        sub     eax, [start_time]
        stdcall FormatTime, eax, cCompileTime
        mov     [fmsg.hMessage], eax
        invoke  SendMessageW, [frmMsg], MWM_ADDMESSAGE, fmsg, ebx
        stdcall StrDel, [fmsg.hMessage]

        push    ebx
        mov     ebx, eax

        mov     eax, [preprocess_time]
        sub     eax, [start_time]
        stdcall FormatTime, eax, cPreprocessTime
        mov     [fmsg.hMessage], eax
        invoke  SendMessageW, [frmMsg], MWM_ADDMESSAGE, fmsg, ebx
        stdcall StrDel, [fmsg.hMessage]

        mov     eax, [parsing_time]
        sub     eax, [preprocess_time]
        stdcall FormatTime, eax, cParsingTime
        mov     [fmsg.hMessage], eax
        invoke  SendMessageW, [frmMsg], MWM_ADDMESSAGE, fmsg, ebx
        stdcall StrDel, [fmsg.hMessage]

        mov     eax, [assembling_time]
        sub     eax, [parsing_time]
        stdcall FormatTime, eax, cAssemblingTime
        mov     [fmsg.hMessage], eax
        invoke  SendMessageW, [frmMsg], MWM_ADDMESSAGE, fmsg, ebx
        stdcall StrDel, [fmsg.hMessage]

        mov     eax, [formating_time]
        sub     eax, [assembling_time]
        stdcall FormatTime, eax, cFormatTime
        mov     [fmsg.hMessage], eax
        invoke  SendMessageW, [frmMsg], MWM_ADDMESSAGE, fmsg, ebx
        stdcall StrDel, [fmsg.hMessage]

        invoke  SendMessageW, [tvMessagesWin], TVM_EXPAND, TVE_COLLAPSE, ebx

        pop     ebx

        movzx   eax, [current_pass]
        inc     eax
        stdcall NumToStr, eax, ntsUnsigned or ntsDec
        mov     esi, eax
        mov     [fmsg.hMessage], eax
        stdcall StrInsert, esi, cCompilePasses, 0
        invoke  SendMessageW, [frmMsg], MWM_ADDMESSAGE, fmsg, ebx
        stdcall StrDel,esi

        stdcall NumToStr, [written_size], ntsUnsigned or ntsDec
        mov     esi, eax
        mov     [fmsg.hMessage], eax
        stdcall StrInsert, esi, cCompileSize, 0
        stdcall StrCat, esi, cSizeUnits
        invoke  SendMessageW, [frmMsg], MWM_ADDMESSAGE, fmsg, ebx
        stdcall StrDel, esi

        invoke  LockWindowUpdate, NULL

        popad
        return
endp








proc FormatTime, .time, .header
begin
        push    esi
        stdcall NumToStr, [.time], ntsUnsigned or ntsDec
        mov     esi, eax

.addzero:
        stdcall StrLen, esi
        cmp     eax, 4
        jae     .insertdot

        stdcall StrCharInsert, esi, '0', 0
        jmp     .addzero

.insertdot:
        sub     eax, 3
        stdcall StrCharInsert, esi, '.', eax
        stdcall StrInsert, esi, [.header], 0
        stdcall StrCat, esi, cTimeUnits

        mov     eax, esi
        pop     esi
        return
endp


;---------------------------------------------------------------------
; This is part of interface subroutines since FASM1.52
;---------------------------------------------------------------------

get_environment_variable:

        push    esi

        stdcall StrNew
        push    eax
        stdcall StrCharCat, eax, '%'
        stdcall StrCat, eax, esi
        stdcall StrCharCat, eax, '%'

        stdcall ConvertAliasString, eax
        stdcall StrDel ; from the stack

; copy the string
        push    ecx
        push    eax
        push    eax

        stdcall StrLen, eax
        mov     ecx, eax

        stdcall StrPtr ; from the stack
        mov     esi, eax

        rep movsb
        stdcall StrDel ; from the stack

        pop     ecx

        pop     esi
        retn



;_________________________________________________________________________________
;
; Searches an alias name and returns its value.
; The search happens in the following order:
; 1. The project aliases.
; 2. The global IDE aliases. (From the section "Environment" of the config file.)
; 3. OS environment variables.
; 4. Predefined constants list (for now it is empty list actually).
;
; Returns:
;   EAX<>0 string handle of the value
;   EAX==0 the alias was not found
;_________________________________________________________________________________

proc ___SearchAlias, .hName
begin
        pushad

; first look in the project defined aliases...

        mov     esi, [poVarArray]
        test    esi, esi
        jz      .no_proj_var

        mov     ecx, [esi+TArray.count]
        lea     esi, [esi+TArray.array]

        jecxz   .no_proj_var

.loop:
        stdcall StrCompNoCase, [esi+TEnvVarType.pName], [.hName]
        jc      .found_proj_var

        lea     esi, [esi+sizeof.TEnvVarType]
        loop    .loop

; project variable was not found ...
; ... then check for a global IDE  aliases ...
.no_proj_var:

        stdcall CfgGetStr, [hCfgFileName], _section_environment, [.hName]
        jnc     .value_found

; ... global IDE variable not found, so check the OS environment ...

        stdcall GetEnvVariable, ebx
        jnc     .value_found

; ... Predefined internal constants to put here ...

;        stdcall StrCompNoCase, [.hName], 'ExeDir'       ; the path where the Fresh.exe file is located.
;        jnc     @f
;
;        stdcall StrDup, []
;        jmp     .value_found
;
;@@:

; ... nothing suitable found, so return NULL ...
        xor     ebx, ebx
        jmp     .finish

.found_proj_var:
        stdcall StrDup, [esi+TEnvVarType.pValue]

.value_found:
        mov     ebx, eax

        stdcall StrCharPos, ebx, '|'  ; check for the multi-value aliases.
        test    eax, eax
        jz      .finish

        mov     dword [eax], 0
        stdcall StrFixLen, ebx

.finish:
        mov     [esp+4*regEAX], ebx
        popad
        return
endp



;____________________________________________________________________________
;
; Recursively searches the string for an alias name (%alias%) and
; replaces it with the value, found by ___SearchAlias procedure.
; The missing aliases, are replaced by an empty string.
;
; Returns: EAX - handle to the converted string. The original string is not
;          changed. Can return empty string.
;____________________________________________________________________________

proc ConvertAliasString, .hString
begin
        pushad

        stdcall StrDup, [.hString]
        mov     edx, eax

.main_loop:
        stdcall StrNew
        mov     ebx, eax

; split. Every odd element of the array is an alias name.
        stdcall StrSplitList, edx, '%', TRUE
        stdcall StrDel, edx

        mov     esi, eax
        xor     ecx, ecx
        xor     edx, edx

.join:
        cmp     ecx, [esi+TArray.count]
        jae     .end_string

        mov     eax, [esi+4*ecx+TArray.array]

        test    ecx, 1
        jz      .text

        push    eax
        stdcall ___SearchAlias, eax
        stdcall StrDel ; from the stack

        test    eax, eax
        jz      .next

.text:
        stdcall StrCat, ebx, eax
        stdcall StrDel, eax

.next:
        inc     ecx
        jmp     .join

.end_string:
        stdcall FreeMem, esi

        cmp     ecx, 1
        je      .converted

        mov     edx, ebx
        jmp     .main_loop

.converted:
        mov     [esp+4*regEAX], ebx
        popad
        return
endp





;------------------------------------------------------------------------------------------------
; Interface routines needed by FASM code
;------------------------------------------------------------------------------------------------
;-----------------------------------------------------------------------------------------------
; Opens "virtual file" and returns pointer to the structure TVFile
; If the file is in the editor - load the virtual file from editor.
; if the file is on the disk, reads the entire file in the virtual file.
; Arguments:
;   EDX - pointer to the filename.
; Returns:
;   EBX - pointer to TVFile structure or handle of open file.
;-----------------------------------------------------------------------------------------------

open:
        cmp     [flagFakeCompile], 0
        je      .opennormal

        stdcall StrCompCase, edx, [input_file]
        jc      .openmain

.opennormal:
        stdcall CheckCash, edx
        test    eax, eax
        jnz     .openfromcash

        stdcall FileOpen, edx
        jc      .file_error

        mov     [hfile],eax
        mov     ebx, eax
        clc
        ret

.openfromcash:
        mov     ebx, eax
        stdcall VSetFilePointer, ebx, 0, FILE_BEGIN
        clc
        ret

.file_error:
        stc
        ret

.openmain:
        push    esi edi ebp

        mov     ebp, edx
        stdcall GetMem, sizeof.TVFile
        jc      .erroropenmain

        mov     [ptrMainFile], eax
        mov     edx, ebp
        mov     [eax+TVFile.hFileName], edx

        mov     ebx, eax

        stdcall CheckCash, edx
        test    eax, eax
        jnz     .copyfromcash

        stdcall LoadBinaryFile, edx
        jc      .erroropenmain

        mov     esi, eax
        mov     ebp, eax
        jmp     .copyit

.copyfromcash:
        mov     esi, [eax+TVFile.buffer]
        mov     ecx, [eax+TVFile.size]
        xor     ebp, ebp

.copyit:
        mov     [ebx+TVFile.size], ecx
        push    esi

        add     ecx, cFakeProlog.size + cFakeEpilog.size
        stdcall GetMem, ecx
        mov     [ebx+TVFile.buffer], eax
        mov     edi, eax

        mov     ecx, cFakeProlog.size
        mov     esi, cFakeProlog
        rep movsb

        pop     esi
        mov     ecx, [ebx+TVFile.size]
        rep movsb

        mov     ecx, cFakeEpilog.size
        mov     esi, cFakeEpilog
        rep movsb

        add     [ebx+TVFile.size],  cFakeProlog.size + cFakeEpilog.size
        stdcall VSetFilePointer, ebx, 0, FILE_BEGIN

        test    ebp, ebp
        jz      .endopenmain

        stdcall FreeMem, ebp

.endopenmain:
        pop     ebp edi esi
        clc
        retn

.erroropenmain:
        pop     ebp edi esi
        stc
        retn

uglobal
  flagFakeCompile dd 0
  ptrMainFile     dd 0
endg


iglobal
  cFakeCompileSymbol db 15,'___FAKE_COMPILETRUE', 0, 0, 0, 0  ; specific internal format. The first number determines the length of the symbol name.

  cFakeProlog db 'if 0', 13, 10
  .size = $ - cFakeProlog

  cFakeEpilog db 13, 10, 'end if', 13, 10
  .size = $-cFakeEpilog
endg


;________________________________________________________________________
;
; Creates file on disk and returns handle to the file.
; Arguments:
;   edx - pointer to the filename.
; Returns:
;   CF=0; ebx=file_handle -> in case the file is created.
;   CF=1 in case there is an error.
;________________________________________________________________________


label create
        stdcall FileCreate, edx
        jc      open.file_error
        mov     ebx, eax
        retn

;________________________________________________________________________________________________
;
;  Arguments:
;    ebx - handle to the file.
;    edx - pointer to a buffer with data.
;    ecx - byte count to write.
; Returns:
;   CF=0 - no error.
;   CF=1 - error.
;________________________________________________________________________________________________

write:
        stdcall FileWrite, ebx, edx, ecx
        jc      open.file_error
        retn

;________________________________________________________________________________________________
; Reads data from file or virtual file.
; Arguments:
;   ebx - file handle or pointer to TVFile strructure.
;   edx - buffer to put data.
;   ecx - bytes read count.
;________________________________________________________________________________________________

read:
        test    ebx, $ffff0000
        jnz     .readfromcash

        mov     ebp, ecx
        stdcall FileRead, ebx, edx, ecx
        jc      open.file_error

        cmp     ebp, eax
        jne     open.file_error

        clc
        retn

.readfromcash:
        mov     ebp, ecx
        stdcall VReadFile, ebx, edx, ecx
        cmp     ebp, eax
        jne     open.file_error
        clc
        retn

;------------------------------------------------------------------------------------------------

close:
        cmp     ebx, [hfile]
        jne     .close_handle
        mov     [hfile], 0

.close_handle:
        test    ebx, $ffff0000
        jnz     .closevirtual

        stdcall FileClose, ebx
        retn

.closevirtual:
        cmp     [ptrMainFile], ebx
        jne     .endclose

        mov     [ptrMainFile], 0
        stdcall FreeMem, [ebx+TVFile.buffer]
        stdcall FreeMem, ebx

.endclose:
        retn


;------------------------------------------------------------------------------------------------

lseek:
        test    ebx, $ffff0000
        jnz     .seekcash

        movzx   eax,al
        stdcall FileSeek, ebx, edx, eax
        jc      open.file_error
        retn

.seekcash:
        movzx   eax, al
        stdcall VSetFilePointer, ebx, edx, eax
        clc
        retn

;------------------------------------------------------------------------------------------------

uglobal
  strDisplay dd ?
  iLastIcon  dd ?
  hLastMsg   dd ?
endg


proc display_block
.fmsg TFreshMsg
begin
        push    ebx
        mov     byte [esi+ecx], 0

.loopskip:
        mov     edx, esi

.lineloop:
; Check for eol.
        lodsb
        test    al,al
        jz      .eol
        cmp     al,$0d
        je      .eol
        cmp     al,$0a
        je      .eol
        cmp     al,$09
        je      .eol
        cmp     al,$08
        je      .eol

        cmp     al, 5
        ja      .lineloop

.eol:
        mov     bl,al
        mov     byte [esi-1], 0

        stdcall StrCat, [strDisplay], edx

        test    bl, bl
        jz      .finish
        cmp     bl, $0d
        je      .eol2
        cmp     bl, $0a
        je      .eol2
        cmp     bl, $09
        je      .eol2
        cmp     bl, $08
        je      .eol2

        movzx   ebx, bl
        dec     ebx
        mov     [iLastIcon], ebx
        jmp     .loopskip

.eol2:
        stdcall StrPtr, [strDisplay]
        cmp     byte [eax], 0
        je      .loopskip               ; Don't append empty messages.

        push    [iLastIcon] [strDisplay]
        pop     [.fmsg.hMessage] [.fmsg.type]
        and     [.fmsg.link.hFileName], 0
        lea     eax, [.fmsg]
        invoke  SendMessageW, [frmMsg], MWM_ADDMESSAGE, eax, [hLastMsg]

        cmp     bl, $0d
        je      .cr
        cmp     bl, $0a
        je      .end
        cmp     bl, $09
        je      .set

.back:
        invoke  SendMessageW, [tvMessagesWin], TVM_GETNEXTITEM, TVGN_PARENT, [hLastMsg]
        jmp     .set
.cr:
        mov     eax, TVI_ROOT
.set:
        mov     [hLastMsg], eax
.end:
        stdcall StrPtr, [strDisplay]
        mov     byte [eax], 0
        mov     [eax+string.len], 0
        mov     [iLastIcon], -1
        jmp     .loopskip

.finish:
        pop     ebx
        return
endp




proc DisplayBlock, .hString
begin
        pushad

        stdcall StrDup, [.hString]
        push    eax

        stdcall StrPtr, eax
        mov     esi, eax

        stdcall StrLen, [.hString]
        mov     ecx, eax

        call    display_block

        stdcall StrDel  ; arg is in the stack.
        popad
        return
endp



proc AppendErrorMessage, .filename, .linenum, .parent, .emsg
.FMsg TFreshMsg
.res  dd ?
begin
        pushad

        mov     [.FMsg.type], mtError
        push    [.linenum]
        push    [.linenum]
        pop     [.FMsg.link.pos.caretLine]
        pop     [.FMsg.link.pos.selectionLine]
        mov     [.FMsg.link.hFileName], 0
        mov     [.FMsg.link.pos.caretPosition], 1
        mov     [.FMsg.link.pos.selectionPosition], 257

        stdcall StrNew
        mov     [.FMsg.hMessage], eax
        mov     esi, eax

        cmp     [.emsg], NULL
        je      .nomessage

        stdcall StrCopy, esi, cErrorPrefix
        stdcall StrCat, esi, [.emsg]

.nomessage:
        cmp     [.filename], NULL
        je      .messageready

        stdcall GetFullPathNameUtf8, [.filename]
        mov     ebx, eax

        stdcall StrDup, eax
        push    eax
        stdcall StrSplitFilename, eax
        stdcall StrDel ; from the stack
        mov     edi, eax

        mov     [.FMsg.link.hFileName], ebx

        stdcall StrCat, esi, edi
        stdcall StrCat, esi, cLMiddleBracket
        stdcall NumToStr, [.linenum], ntsUnsigned or ntsDec
        stdcall StrCat, esi, eax
        stdcall StrDel, eax
        stdcall StrCat, esi, cRMiddleBracket

.messageready:
        lea     eax, [.FMsg]
        invoke  SendMessageW, [frmMsg], MWM_ADDMESSAGE, eax, [.parent]

        mov     [.res], eax

        stdcall StrDel, esi
        stdcall StrDel, edi
        stdcall StrDel, ebx

        popad
        mov     eax, [.res]
        return
endp


iglobal
  cMsgPreprocess db 3, 'Preprocessing...',13,0
  cMsgParsing    db 3, 'Parsing...',13,0
  cMsgAssembling db 3, 'Assembling...',13,0
  cMsgFormating  db 3, 'Formating...',13,0
  cMsgDebugInfo  db 3, 'Collect debug info...',13,0

  cLMiddleBracket db ' [',0
  cRMiddleBracket db ']',0

  cErrorPrefix   db 'Error: ', 0
endg


;------------------------------------------------------------------------------------------------

fatal_error:
        call    show_display_buffer

        stdcall AppendErrorMessage, NULL, -1, TVI_ROOT    ; the error message address is in the stack.
        mov     al,0FFh
        jmp     flat_assembler.exit_program

assembler_error:
        call    show_display_buffer

        pop     esi
        mov     edi, buffer

.copymessage:
        lodsb
        stosb
        test    al,al
        jnz     .copymessage

        dec     edi
        mov     eax, ' << '
        stosd

        mov     esi,[current_line]
        add     esi,16
        xor     dl,dl


      convert_instruction:
        lodsb
        cmp     al,1Ah
        je      copy_symbol
        cmp     al,22h
        je      copy_symbol
        cmp     al,3Bh
        je      copy_symbol     ;ignore_preprocessor_symbols
        stosb
        or      al,al
        jz      instruction_converted
        xor     dl,dl
        jmp     convert_instruction
      copy_symbol:
        or      dl,dl
        jz      space_ok
        mov     byte [edi],20h
        inc     edi
      space_ok:
        cmp     al,22h
        je      quoted
        lodsb
        movzx   ecx,al
        rep     movsb
        or      dl,-1
        jmp     convert_instruction
      quoted:
        mov     al,27h
        stosb
        lodsd
        mov     ecx,eax
        jecxz   quoted_copied
      copy_quoted:
        lodsb
        stosb
        cmp     al,27h
        jne     quote_ok
        stosb
      quote_ok:
        loop    copy_quoted
      quoted_copied:
        mov     al,27h
        stosb
        or      dl,-1
        jmp     convert_instruction

;      ignore_preprocessor_symbols:
;        xor     al,al
;        stosb

      instruction_converted:

        dec     edi
        mov     eax, ' >> '
        stosd
        mov     byte [edi], 0

        mov     edi, buffer

        push    dword 0
        mov     ebx,[current_line]

        mov     ecx, TVI_ROOT

  get_error_lines:
        push    ebx
        test    byte [ebx+TIntLine.LineNumber+3], 80h       ; First if not set ???
        jz      display_error_line

        mov     edx, [ebx+TIntLine.ofsGenerator]
        cmp     word [edx+1], '__'      ; is it internal macro definition?
        jne     .notinternal

        pop     ebx

.searchdown:
        mov     ebx, [ebx+TIntLine.ofsMacroLine]
        test    byte [ebx+TIntLine.LineNumber+3], $80
        jnz     .searchdown
        push    ebx
        jmp     display_error_line

.notinternal:
        pop     ebx
        mov     edx,ebx

  find_definition_origin:
  ; check the macro name
        mov     edx,[edx+TIntLine.ofsMacroLine]
        test    byte [edx+TIntLine.LineNumber+3],80h
        jnz     find_definition_origin

        push    edx
        mov     ebx,[ebx+TIntLine.ofsSourceLine]
        jmp     get_error_lines

 display_error_line:
        pop     ebx
        test    ebx, ebx
        jz      finish_error
        mov     esi,[ebx]
        mov     eax,[ebx+4]
        stdcall AppendErrorMessage, [ebx], [ebx+4], ecx, edi
; uncomment if you want only one level of nesting error messages.
; Maybe it's interesting if the style of the message treeview
; includes TVS_SINGLEEXPAND style.
;        cmp     ecx, TVI_ROOT
;        jne     display_error_line

        xor     edi, edi
        mov     ecx, eax
        jmp     display_error_line

  finish_error:
        mov     al,2
        jmp     flat_assembler.exit_program

;------------------------------------------------------------------------------------------------

make_timestamp:
        stdcall GetTime
        retn

;------------------------------------------------------------------------------------------------

