flat assembler
Message board for the users of flat assembler.

Index > Compiler Internals > fasmg as DLL

Author
Thread Post new topic Reply to topic
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8356
Location: Kraków, Poland
Tomasz Grysztar 02 Oct 2019, 10:47
While experimental DLL version of fasmg has been available even in the official package for quite some time, the information on its usage has been buried in the other thread, therefore I'm posting this information again to make an official guide.

To get fasmg.DLL, simply assemble the source/windows/dll/fasmg.asm file from the official package. You can do it with either fasm or fasmg.

It defines only two functions:

fasmg_GetVersion() returns pointer to the version string.

fasmg_Assemble(source_string, source_path, output_region, output_path, stdout, stderr) assembles a given source and returns the number of reported errors (so 0 indicates a successful assembly). If the returned value is negative, it indicates a fatal error, like running out of memory or failing to write the output.

All arguments are optional - every one of them is allowed to be NULL.

  • source_string points to the text to be assembled directly from memory. Console versions of fasmg use this input channel for the instructions added from command line with -I switch, but it can just as well be used to assemble complete source not stored in any file.
  • source_path is the path to the source file. If both in-memory and file sources are provided, assembly proceeds on a combined text (the string from memory comes first).
  • output_region is a pointer to this simple structure:
    Code:
    struct MEMORY_REGION
            address dd ?
            size dd ?
    ends    
    It should be initially zeroed or may be filled with information about a block of memory allocated with VirtualAlloc. When the assembly is successful, the structure is filled with information about the size of generated output and the address where it is stored. If the structure provided a memory region but it was too small to hold the output, a region is re-allocated with VirtualAlloc, releasing the previous one with VirtualFree. This way you can keep calling the function and have it re-use the same region of memory for output. You can also initialize the address field to point to a region prepared with VirtualAlloc with MEM_RESERVE flag and set the initial size to zero, fasmg_Assemble should then be able to keep using the reserved address, committing as much memory as needed for the generated output.
  • output_path is the path to the output file. When not provided, the output path is generated from the input path (just like the command-line fasmg does it). When neither path is present, the output is not written to a file.
  • stdout should be a handle to file or pipe that receives the messages generated with DISPLAY command.
  • stderr should be a handle to file or pipe that receives the error messages. All the errors counted by the value returned from function are reported with console-like messages here.

This function can be used by multiple threads in parallel (as opposed to the DLL version of fasm).
Post 02 Oct 2019, 10:47
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8356
Location: Kraków, Poland
Tomasz Grysztar 02 Oct 2019, 10:48
Next, a simple example of how to use fasmg.DLL.

It is a calculator, a bit similar to the one built into fasmw IDE, but this one uses fasmg engine to compute expressions.

You can assemble this sample with either fasm or fasmg (see my instructional video on how to set up fasmg to assemble fasm-compatible Win32 programs).
Code:
format PE GUI 4.0
entry start

include 'win32ax.inc'


struct MEMORY_REGION
        address dd ?
        size dd ?
ends

EXPRESSION_MAX_LENGTH = 32767

IDR_CALCULATOR = 37

ID_EXPRESSION  = 100
ID_BINARY      = 102
ID_DECIMAL     = 110
ID_HEXADECIMAL = 116
ID_VERSION     = 199


section '.text' code readable executable

  start:

        invoke  VirtualAlloc,0,100000h,MEM_RESERVE,PAGE_READWRITE
        mov     [aout.address],eax
        mov     [aout.size],0
        invoke  VirtualAlloc,0,100000h,MEM_RESERVE,PAGE_READWRITE
        mov     [conv.address],eax
        mov     [conv.size],0

        invoke  GetModuleHandle,0
        invoke  DialogBoxParam,eax,IDR_CALCULATOR,HWND_DESKTOP,CalculatorDialog,0
        invoke  ExitProcess,0

proc CalculatorDialog hwnd,msg,wparam,lparam
        push    ebx esi edi
        cmp     [msg],WM_INITDIALOG
        je      init
        cmp     [msg],WM_COMMAND
        je      command
        cmp     [msg],WM_CLOSE
        je      close
        xor     eax,eax
        jmp     finish
    init:

        invoke  fasmg_GetVersion

        invoke  SetDlgItemText,[hwnd],ID_VERSION,eax

        jmp     processed
    command:
        cmp     [wparam],IDCANCEL
        je      close
        cmp     [wparam],IDOK
        je      processed

        cmp     [wparam],ID_EXPRESSION + EN_CHANGE shl 16
        jne     processed

        invoke  GetDlgItemText,[hwnd],ID_EXPRESSION,expression_buffer,EXPRESSION_MAX_LENGTH

        invoke  fasmg_Assemble,source_string,NULL,aout,NULL,NULL,NULL
        test    eax,eax
        jnz     error

        mov     eax,[aout.size]
        lea     eax,[eax*8+8]
        cmp     eax,[conv.size]
        jbe     convert_output
        mov     [conv.size],eax
        invoke  VirtualAlloc,[conv.address],eax,MEM_COMMIT,PAGE_READWRITE
        test    eax,eax
        jnz     convert_output
        invoke  VirtualFree,[conv.address],0,MEM_RELEASE
        invoke  VirtualAlloc,0,[conv.size],MEM_COMMIT,PAGE_READWRITE
        test    eax,eax
        jz      error
        mov     [conv.address],eax
    convert_output:

        mov     esi,[aout.address]
        mov     edx,[aout.size]
        xor     cl,cl
        mov     edi,[conv.address]
        add     edi,[conv.size]
        sub     edi,2
        mov     word [edi],'b'
      to_bin:
        mov     al,[esi]
        shr     al,cl
        and     al,1
        add     al,'0'
        dec     edi
        mov     [edi],al
        inc     cl
        and     cl,111b
        jnz     to_bin
        inc     esi
        dec     edx
        jnz     to_bin
        test    byte [esi-1],80h
        jz      bin_ok
        dec     edi
        mov     ecx,3
        mov     al,'.'
        std
        rep     stosb
        cld
        inc     edi
      bin_ok:
        invoke  SetDlgItemText,[hwnd],ID_BINARY,edi

        mov     esi,[aout.address]
        mov     ecx,[aout.size]
        mov     edi,[conv.address]
        add     edi,[conv.size]
        sub     edi,2
        mov     word [edi],'h'
      to_hex:
        mov     al,[esi]
        and     al,0Fh
        cmp     al,10
        sbb     al,69h
        das
        dec     edi
        mov     [edi],al
        lodsb
        shr     al,4
        cmp     al,10
        sbb     al,69h
        das
        dec     edi
        mov     [edi],al
        loop    to_hex
        test    byte [esi-1],80h
        jz      hex_ok
        dec     edi
        mov     ecx,3
        mov     al,'.'
        std
        rep     stosb
        cld
        inc     edi
      hex_ok:
        invoke  SetDlgItemText,[hwnd],ID_HEXADECIMAL,edi

        mov     esi,[aout.address]
        mov     ecx,[aout.size]
        mov     edi,[conv.address]
        rep     movsb
        test    byte [esi-1],80h
        jnz     negative
        xor     eax,eax
        stosd
        jmp     to_dec
      negative:
        or      eax,-1
        stosd
        mov     esi,[conv.address]
        mov     ecx,edi
        sub     ecx,esi
        stc
      negate:
        not     byte [esi]
        adc     byte [esi],0
        inc     esi
        loop    negate
      to_dec:
        mov     edi,[conv.address]
        add     edi,[conv.size]
        dec     edi
        and     byte [edi],0
        mov     esi,[conv.address]
        mov     ecx,[aout.size]
        dec     ecx
        and     ecx,not 11b
      obtain_digit:
        xor     edx,edx
      divide_highest_dwords:
        mov     eax,[esi+ecx]
        call    div10
        test    eax,eax
        jnz     more_digits_to_come
        sub     ecx,4
        jnc     divide_highest_dwords
      store_final_digit:
        add     dl,'0'
        dec     edi
        mov     [edi],dl
        mov     esi,[aout.address]
        add     esi,[aout.size]
        test    byte [esi-1],80h
        jz      dec_ok
        dec     edi
        mov     byte [edi],'-'
        jmp     dec_ok
      div10:
        push    ebx ecx
        push    eax
        mov     ebx,eax
        mov     ecx,edx
        shld    edx,eax,2
        sub     ebx,edx
        sbb     ecx,0
        mov     eax,ebx
        mov     ebx,1999999Ah
        mul     ebx
        mov     eax,ecx
        imul    eax,ebx
        add     eax,edx
        pop     edx
        imul    ecx,eax,10
        sub     edx,ecx
        cmp     edx,10
        jb      div10_done
        sub     edx,10
        inc     eax
      div10_done:
        pop     ecx ebx
        retn
      more_digits_to_come:
        mov     ebx,ecx
      divide_remaining_dwords:
        mov     [esi+ebx],eax
        sub     ebx,4
        jc      store_digit
        mov     eax,[esi+ebx]
        call    div10
        jmp     divide_remaining_dwords
      store_digit:
        add     dl,'0'
        dec     edi
        mov     [edi],dl
        jmp     obtain_digit
      dec_ok:
        invoke  SetDlgItemText,[hwnd],ID_DECIMAL,edi

        jmp     processed
    error:
        invoke  SetDlgItemText,[hwnd],ID_BINARY,error_string
        invoke  SetDlgItemText,[hwnd],ID_DECIMAL,error_string
        invoke  SetDlgItemText,[hwnd],ID_HEXADECIMAL,error_string
        jmp     processed
    close:
        invoke  EndDialog,[hwnd],0
    processed:
        mov     eax,1
    finish:
        pop     edi esi ebx
        ret
endp


section '.data' data readable writeable

    error_string:
        db 0

    source_string:
        db 10,'if $ < 0'
        db 10,'emit (bsr (1 or not $) + 1) shr 3 + 1: +$'
        db 10,'else'
        db 10,'emit (bsr (1 or $) + 1) shr 3 + 1: +$'
        db 10,'end if'
        db 10,'$ = '
    expression_buffer db EXPRESSION_MAX_LENGTH dup ?

    aout MEMORY_REGION
    conv MEMORY_REGION


section '.idata' import data readable writeable

  library kernel,'KERNEL32.DLL',\
          user,'USER32.DLL',\
          fasmg,'FASMG.DLL'

  import kernel,\
         GetModuleHandle,'GetModuleHandleA',\
         VirtualAlloc,'VirtualAlloc',\
         VirtualFree,'VirtualFree',\
         ExitProcess,'ExitProcess'

  import user,\
         DialogBoxParam,'DialogBoxParamA',\
         GetDlgItemText,'GetDlgItemTextA',\
         SetDlgItemText,'SetDlgItemTextA',\
         EndDialog,'EndDialog'

  import fasmg,\
         fasmg_GetVersion,'fasmg_GetVersion',\
         fasmg_Assemble,'fasmg_Assemble'


section '.rsrc' resource data readable

  directory RT_DIALOG,dialogs

  resource dialogs,\
           IDR_CALCULATOR,LANG_ENGLISH+SUBLANG_DEFAULT,calculator_dialog

  dialog calculator_dialog,'fasmg-powered calculator',100,120,380,64,WS_CAPTION+WS_POPUP+WS_SYSMENU+DS_MODALFRAME
    dialogitem 'STATIC','&Expression:',-1,4,8,44,8,WS_VISIBLE+SS_RIGHT
    dialogitem 'EDIT','',ID_EXPRESSION,52,6,320,12,WS_VISIBLE+WS_BORDER+WS_TABSTOP+ES_AUTOHSCROLL
    dialogitem 'STATIC','&Decimal:',-1,4,20,44,8,WS_VISIBLE+SS_RIGHT
    dialogitem 'EDIT','',ID_DECIMAL,52,18,320,12,WS_VISIBLE+WS_BORDER+WS_TABSTOP+ES_AUTOHSCROLL+ES_READONLY
    dialogitem 'STATIC','&Hexadecimal:',-1,4,32,44,8,WS_VISIBLE+SS_RIGHT
    dialogitem 'EDIT','',ID_HEXADECIMAL,52,30,320,12,WS_VISIBLE+WS_BORDER+WS_TABSTOP+ES_AUTOHSCROLL+ES_READONLY
    dialogitem 'STATIC','&Binary:',-1,4,44,44,8,WS_VISIBLE+SS_RIGHT
    dialogitem 'EDIT','',ID_BINARY,52,42,320,12,WS_VISIBLE+WS_BORDER+WS_TABSTOP+ES_AUTOHSCROLL+ES_READONLY
    dialogitem 'STATIC','fasm g .',-1,320,55,30,8,WS_VISIBLE+SS_RIGHT
    dialogitem 'STATIC','',ID_VERSION,350,55,20,8,WS_VISIBLE+SS_RIGHT
  enddialog    
(also available on GitHub).

As you can see, it calls fasmg_Assemble with most of the arguments set to NULL, to perform a memory-to-memory assembly without logging error messages (when there are errors, the result display is simply cleared). The entered expression is simply appended to an assembly snippet that converts it into a variable-length output - this has some side-effects, for example you can add comments on the expression line with help of semicolon (this side-effect can be eliminated by adding RETAINCOMMENTS directive to the source snippet), or you can use built-in variables like %t.

For simplicity, this example does not handle floating point results, but you can still use such values in the expression as long as you finally convert them to integer. For example you can compute an expression like trunc(1.2*70).


Description: Calculator in action
Filesize: 4.94 KB
Viewed: 8102 Time(s)

fasmg_calc.png


Post 02 Oct 2019, 10:48
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8356
Location: Kraków, Poland
Tomasz Grysztar 11 May 2020, 22:49
On a live stream I tweaked the calculator example to make a simple real-time assembler, capable of showing the codes of x86 instructions as soon as you type them.

You can watch the recording: Making a real-time assembler with fasmg.DLL on YouTube. There is also a thread on the forum with a subsequent discussion.

Unfortunately my microphone connection died in the second half of the session and I have not noticed it in time, so some of my commentary has been lost. Thank you to all the viewers that were there live!
Post 11 May 2020, 22:49
View user's profile Send private message Visit poster's website Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.