• Welcome to Powerbasic Museum 2020-B.
 

News:

Forum in repository mode. No new members allowed.

Main Menu

Assembler Programming with Gas

Started by Charles Pegge, February 11, 2008, 12:32:50 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Charles Pegge

The title is misleading in as much as assembler programming is not a painful surgical procedure (if done correctly) nor does it require the inhalation of intoxicating fumes to attain altered states of consciousness.

Gas is the GNU assembler used by the GCC C++ compiler (and other languages like FreeBasic) to generate executable code.  While it is mainly used as part of the compilation suite, invisible to the programer. it is friendly enough to be used in its own right.

It is distributed as part of the binutils. For MS Windows this is part of the  MinGW package. It is also present in the FreeBasic bin folder.

To create executables, as.exe, the assembler and ld.exe the linker is all that you need.

Here is a Hello World Message Box:
The source code is in t.asm

.intel_syntax noprefix

.section .data

caption: .asciz "GAS Greeting"
message: .asciz "Hello World!"
.align 4,0

.section .text
.balign 16
.globl _mainCRTStartup
_mainCRTStartup:


push 3                 # style
push offset caption    # caption
push offset message    # message
push 0                 # window handle
call _MessageBoxA@16   # MessageBox

ret 16



Assembling and Linking.
This can be put into a batch file like this:
go.bat

..\bin\win32\as t.asm
..\bin\win32\ld --subsystem  windows a.out ..\lib\win32\libuser32.dll.a
pause



Some useful reference material for programming with GAS


Linux assemblers: A comparison of GAS and NASM
http://www.ibm.com/developerworks/library/l-gas-nasm.html

Using 'as': GNU Assembler Reference
http://sourceware.org/binutils/docs-2.17/as/index.html
alt
http://www.gnu.org/software/binutils/manual/gas-2.9.1/html_chapter/as_toc.html

Intel Architecture Software developer's manual Vol 2
http://developer.intel.com/design/pentiumii/manuals/243191.htm

Freebasic (comes with AS and LD)
http://www.freebasic.net

If you have installed Freebasic then you can extract the folder below and put it into the main directory.
It contains the source code and  batchfile (and precompiled executable).



Charles Pegge

#1
In minGW, the directory structure and lib file name conventions are slightly different so comple commands look like this:

go.bat
..\bin\as t.asm
..\bin\ld --subsystem  windows a.out ..\lib\libuser32.a
pause



Another difference is the name of the entry point WinMainCRTStartup instead of mainCRTStartup:


.intel_syntax noprefix

.section .data

caption: .asciz "GAS Greeting"
message: .asciz "Hello World!"
.align 4,0

.section .text
.balign 16
.globl _WinMainCRTStartup
_WinMainCRTStartup:


.set ABC,42

push 3                 # style
push offset caption    # caption
push offset message    # message
push 0                 # window handle
call _MessageBoxA@16   # MessageBox

ret 16





The linker has innumerable command line switches for various systems but the --subsystem Windows suppresses the appearance of a console window when running Windows GUI programs.

http://sourceware.org/binutils/docs-2.17/ld/index.html

a.out is the default output of the assembler and any library files required by the executable come after this.

When Gas is used as the inline Assemble from within FreeBasic. Some ot its features are disrupted. I found that Basic's use of the single quote mark as a comment is not really compatible, and an important feature: relative labels is also partially disrupted.

These labels are an elegant solution to block-structured programming. Labels 0..9 can be used:
To jump forward to the next matching label the operand is 0f 1f 2f etc. To jmp back to the previous match the operand is 0b 1b 2b .. 9b. This is all you need to construct multiple nested loops, conditionals and case blocks with the need to invent unique labels every time.



Petr Schreiber

Thanks for sharing Charles,

very interesting, please continue :)


Petr
AMD Sempron 3400+ | 1GB RAM @ 533MHz | GeForce 6200 / GeForce 9500GT | 32bit Windows XP SP3

psch.thinbasic.com

Kent Sarikaya

Very interesting indeed, thanks Charles. I will soon buy one of these low cost 7" screen notebooks that are under $400 with a linux OS installed.
I hope to finally start to develop under linux while out and about and finally learn linux better. I will make use of this thread as well as Donald's linux adventure threads.

Charles Pegge

#4
Okay, this is an intermediate step for a Windows prog which will be written in pure Gas.

Hello World

Using FreeBasic Inline Assembler

Minimal Windows SDK prog with message loop etc


' inline Assembler "Hello World"
' FreeBasic 0.18.3
' 09:30 12/02/2008

#include once "windows.bi"

'type MSG
' hwnd as HWND
' message as UINT
' wParam as WPARAM
' lParam as LPARAM
' time as DWORD
' pt as POINT
'end type

'type WNDCLASSA
' style as UINT
' lpfnWndProc as WNDPROC
' cbClsExtra as integer
' cbWndExtra as integer
' hInstance as HINSTANCE
' hIcon as HICON
' hCursor as HCURSOR
' hbrBackground as HBRUSH
' lpszMenuName as LPCSTR
' lpszClassName as LPCSTR
'end type

'type RECT
' left as LONG
' top as LONG
' right as LONG
' bottom as LONG
'end type

'type PAINTSTRUCT
'  hDC AS DWORD
'  fErase AS LONG
'  rcPaint AS RECT
'  fRestore AS LONG
'  fIncUpdate AS LONG
'  rgbReserved(0 TO 31) AS BYTE
'end type



declare function WndProc ( byval hWnd as HWND, _
                   byval wMsg as UINT, _
                   byval wParam as WPARAM, _
                   byval lParam as LPARAM ) as LRESULT




'':::::
function WinMain ( byval hInstance as HINSTANCE, _
                   byval hPrevInstance as HINSTANCE, _
                   byval szCmdLine as string, _
                   byval iCmdShow as integer ) as integer   
     
    dim wMsg as MSG
    dim wcls as WNDCLASS     
    dim hWnd as HWND
     
    function = 0
     
dim as any ptr p,q,r,s,t,u,v
q=@WndProc
p=@wcls
r=@"HelloWin"
s=@"Failed to register wcls"
t=@"Error"
u=@"The Hello Program"
v=@wMsg
asm
    mov eax,[p] ' redundant?
    mov dword ptr [eax],CS_HREDRAW
    or  dword ptr [eax],CS_VREDRAW
    mov edx,[q]
    mov [eax+4],edx
    mov dword ptr [eax+8],0
    mov dword ptr [eax+12],0
    mov  edx,[hInstance]
    mov  [eax+16],edx
    push 32512 'IDI_APPLICATION
    push 0
    call LoadIcon
    mov edx,eax
    mov eax,[p]
    mov [eax+20],edx
    '
    push 32512 'IDC_ARROW
    push 0
    call LoadCursor
    mov edx,eax
    mov eax,[p]
    mov [eax+24],edx
    '
    push 0 'WHITE_BRUSH
    call GetStockObject
    mov edx,eax
    mov eax,[p]
    mov [eax+28],edx
    '
    mov dword ptr [eax+32],0
    mov edx,[r]
    mov [eax+36],edx
    '
    push eax
    call RegisterClass
    cmp eax,0
    jnz Reg_ok
        push 16  ' MB_ICONERROR
        push [t] ' heading
        push [ s ] ' message
        push 0   ' hWnd or null
        call MessageBox
        xor eax,eax
        jmp xitwm
    Reg_ok:
    push 0
    push [hInstance]
    push 0
    push 0
    push 480 'CW_USEDEFAULT
    push 640 'CW_USEDEFAULT
    push 0x80000000 'CW_USEDEFAULT
    push 0x80000000 'CW_USEDEFAULT
    push 0x00cf0000 'WS_OVERLAPPEDWINDOW
    push [ u ]
    push [r]
    push 0
    call CreateWindowEx
    mov [hWnd],eax
    push [iCmdShow]
    push [hWnd]
    call ShowWindow
    push [hWnd]
    call UpdateWindow

    MessageLoop:
       push 0
       push 0
       push 0
       push [v]
       call GetMessage
       cmp eax,0
       jz xit
       push [v]
       call TranslateMessage
       push [v]
       call DisPatchMessage
    jmp MessageLoop     
    xit:
    mov eax,[v]
    mov eax,[eax+8] ' wMsg.wParam
    mov [function],eax
xitwm:   
end asm
   
end function


                                 
end WinMain( GetModuleHandle( null ), null, Command( ), SW_NORMAL )



function WndProc ( byval hWnd as HWND, _
                   byval wMsg as UINT, _
                   byval wParam as WPARAM, _
                   byval lParam as LPARAM ) as LRESULT
    dim rct as RECT
    dim pnt as PAINTSTRUCT
    dim hDC as HDC
    dim as any ptr p,r,s
    p=@pnt
    r=@rct
    s=@"Hello, World!"

    asm
        mov eax,[wMsg]
        cmp eax,WM_CREATE
        jnz nx1
        xor eax,eax
        jmp xitwp
    nx1:
        cmp eax,WM_PAINT
        jnz nx2
        push [p]
        push [hWnd]
        call BeginPaint
        mov [hDC],eax
        push [r]
        push [hWnd]
        call GetClientRect
        mov eax,DT_SINGLELINE ' 0x20
        or eax,DT_VCENTER ' 04
        or eax,DT_CENTER  ' 01
        push 37 'push eax
        push [r]
        push -1
        push [ s ]
        push [hDC]
        call DrawText
        push [p]
        push [hWnd]
        call EndPaint
        xor eax,eax
        jmp xitwp
    nx2:
        cmp eax,WM_KEYDOWN
        jnz nx3
        mov eax,[wParam]
        cmp al,27
        jnz cntnu
            push 0
            push 0
            push WM_CLOSE
            push [hWnd]
            call PostMessage
           
        cntnu:
        xor eax,eax
        jmp xitwp
    nx3:
    'case WM_DESTROY

        cmp eax,WM_DESTROY
        jnz nx4
        push 0
        call PostQuitMessage
        xor eax,eax
        jmp xitwp
    nx4:
        push [lparam]
        push [wparam]
        push [wMsg]
        push [hWnd]
        call DefWindowProc
        'jmp xitwp
    xitwp:
    mov [function],eax
    end asm
end function   




The Original FreeBasic "Hello"




#include once "windows.bi"

declare function        WinMain     ( byval hInstance as HINSTANCE, _
                                      byval hPrevInstance as HINSTANCE, _
                                      byval szCmdLine as string, _
                                      byval iCmdShow as integer ) as integer
                                 
                                 
end WinMain( GetModuleHandle( null ), null, Command( ), SW_NORMAL )

'':::::
function WndProc ( byval hWnd as HWND, _
                   byval wMsg as UINT, _
                   byval wParam as WPARAM, _
                   byval lParam as LPARAM ) as LRESULT
   
    function = 0
   
    select case( wMsg )
        case WM_CREATE           
            exit function

        case WM_PAINT
    dim rct as RECT
    dim pnt as PAINTSTRUCT
    dim hDC as HDC
         
            hDC = BeginPaint( hWnd, @pnt )
            GetClientRect( hWnd, @rct )
           
            DrawText( hDC, _
              "Hello, World!", _
              -1, _
                      @rct, _
                      DT_SINGLELINE or DT_CENTER or DT_VCENTER )
           
            EndPaint( hWnd, @pnt )
           
            exit function           
       
case WM_KEYDOWN
if( lobyte( wParam ) = 27 ) then
PostMessage( hWnd, WM_CLOSE, 0, 0 )
end if

    case WM_DESTROY
            PostQuitMessage( 0 )
            exit function
    end select
   
    function = DefWindowProc( hWnd, wMsg, wParam, lParam )   
   
end function

'':::::
function WinMain ( byval hInstance as HINSTANCE, _
                   byval hPrevInstance as HINSTANCE, _
                   byval szCmdLine as string, _
                   byval iCmdShow as integer ) as integer   
     
    dim wMsg as MSG
    dim wcls as WNDCLASS     
    dim hWnd as HWND
     
    function = 0
     
    with wcls
    .style         = CS_HREDRAW or CS_VREDRAW
    .lpfnWndProc   = @WndProc
    .cbClsExtra    = 0
    .cbWndExtra    = 0
    .hInstance     = hInstance
    .hIcon         = LoadIcon( NULL, IDI_APPLICATION )
    .hCursor       = LoadCursor( NULL, IDC_ARROW )
    .hbrBackground = GetStockObject( WHITE_BRUSH )
    .lpszMenuName  = NULL
    .lpszClassName = @"HelloWin"
    end with
         
    if( RegisterClass( @wcls ) = FALSE ) then
       MessageBox( null, "Failed to register wcls", "Error", MB_ICONERROR )
       exit function
    end if
   
    hWnd = CreateWindowEx( 0, _
       @"HelloWin", _
                           "The Hello Program", _
                           WS_OVERLAPPEDWINDOW, _
                           CW_USEDEFAULT, _
                           CW_USEDEFAULT, _
                           CW_USEDEFAULT, _
                           CW_USEDEFAULT, _
                           NULL, _
                           NULL, _
                           hInstance, _
                           NULL )
                         

    ShowWindow( hWnd, iCmdShow )
    UpdateWindow( hWnd )
     
    while( GetMessage( @wMsg, NULL, 0, 0 ) <> FALSE )   
        TranslateMessage( @wMsg )
        DispatchMessage( @wMsg )
    wend
   
    function = wMsg.wParam

end function


Charles Pegge


The Hello Progtam rewritten in standalone Gas:

You have to allocate space on the stack and manage the data structures manually. But local labels 0: 1: 2: etc work properly, making block structured programming easy.




    # Hello World
    # MSWindows SDK using Gas Assembler
    # 05:18 15/02/2008
    # Charles E V Pegge



    .intel_syntax noprefix

    .section .data

    #========================================================#
        Caption:    .asciz "Test Info"
        Message:    .asciz "Hello World!"
        Title:      .asciz "The Hello Program"
        ClassName:  .asciz "HelloWin"
        Error:      .asciz "Error"
        RegFail:    .asciz "Unable to register"
        CreateFail: .asciz "Unable to Create Window"
        Buf:        .fill 40,0
    #========================================================#

    .balign 16,0


#TYPE WNDCLASS 40 bytes
#    STYLE AS DWORD
#    lpfnwndproc AS DWORD
#    cbClsextra AS LONG
#    cbWndExtra AS LONG
#    hInstance AS DWORD
#    hIcon AS DWORD
#    hCursor AS DWORD
#    hbrBackground AS DWORD
#    lpszMenuName AS ASCIIZ PTR
#    lpszClassName AS ASCIIZ PTR
#END TYPE

#TYPE WNDCLASSEX
#    cbSize AS DWORD
#    STYLE AS DWORD
#    lpfnWndProc AS LONG
#    cbClsExtra AS LONG
#    cbWndExtra AS LONG
#    hInstance AS DWORD
#    hIcon AS DWORD
#    hCursor AS DWORD
#    hbrBackground AS DWORD
#    lpszMenuName AS ASCIIZ PTR
#    lpszClassName AS ASCIIZ PTR
#    hIconSm AS DWORD
#END TYPE

#typedef struct { 28 bytes
#    HWND hwnd;
#    UINT message;
#    WPARAM wParam;
#    LPARAM lParam;
#    DWORD time;
#    POINT pt;
#} MSG, *PMSG;


    #===========================#
    .section .text              #
    #===========================#

    #===========================#
                                #
    .globl _WinMainCRTStartup   #
    _WinMainCRTStartup:         #

    .set SW_NORMAL,1
    .set SW_SHOWDEFAULT, 10

    #===========================#
    push SW_NORMAL              # iCmdShow as integer
    #---------------------------#
    call _GetCommandLineA@0     #
    push eax                    # szCmdLine as string
    #---------------------------#
    push 0                      # hPrevInstance as HINSTANCE
    #---------------------------#
    push 0                      #
    call _GetModuleHandleA@4    #
    push eax                    # hInstance as HINSTANCE
    #---------------------------#
    call WinMain                #
    #===========================#
    push eax                    #
    call _ExitProcess@4         #
    #===========================#


    .balign 16,0



    #===========================#
    WinMain:                    #
    #===========================#
                                #
                                #

    .set CS_VREDRAW,     1
    .set CS_HREDRAW,     2
    .set IDI_APPLICATION,32512
    .set IDC_ARROW,      32512
    .set WHITE_BRUSH,    0
    .set MB_ICONERROR,   16
    .set SW_NORMAL,      2

    .set CW_USEDEFAULT,      0x80000000
    .set WS_OVERLAPPEDWINDOW,0x00cf0000


    #---------------------------#
    sub esp,100                 #
    mov ebp,esp                 #
    #===========================#
    mov eax,CS_HREDRAW          #
    or  eax,CS_VREDRAW          #
    mov [ebp],eax               # 01 Style
    mov dword ptr [ebp+4], offset WndProc # 02
    mov dword ptr [ebp+8],0     # 03 cbClsExtra
    mov dword ptr [ebp+12],0    # 04 cbWndExtra
    mov  eax,[ebp+104]          #
    mov  [ebp+16],eax           # 05 hInstance
    #---------------------------#
    push IDI_APPLICATION        #
    push 0                      #
    call _LoadIconA@8           #
    mov [ebp+20],eax            # 06 icon
    #---------------------------#
    push IDC_ARROW              #
    push 0                      #
    call _LoadCursorA@8         #
    mov [ebp+24],eax            # 07 cursor
    #---------------------------#
    push WHITE_BRUSH            #
    call _GetStockObject@4      #
    mov [ebp+28],eax            # 08 brush
    #---------------------------#
    mov dword ptr [ebp+32],0    # 09 lpMenuName
    mov dword ptr [ebp+36],offset ClassName # 10
    #===========================#
    push ebp                    # pClass
    call _RegisterClassA@4      #
    #---------------------------#
    cmp eax,0                   #
    jnz 0f                      #
        push MB_ICONERROR       #
        push offset Error       # heading
        push offset RegFail     # message
        push 0                  # hWnd or null
        call _MessageBoxA@16    #
        xor eax,eax             # zero eax
        jmp end_app             # jmp xitwm
    0:                          #
    #===========================#
    push 0                      # 12 lpParam
    push [ebp+104]              # 11 hInstance
    push 0                      # 10 hMenu
    push 0                      # 09 hWndParent
    push 480                    # 08 bottom
    push 640                    # 07 right
    push CW_USEDEFAULT          # 06 top
    push CW_USEDEFAULT          # 05 left
    push WS_OVERLAPPEDWINDOW    # 04 dwStyle
    push offset Title           # 03 lpWindowName Title
    push offset ClassName       # 02 lpClassName
    push 0                      # 01 dwExStyle
    call _CreateWindowExA@48    # 00 Create Window
    mov [ebp+40],eax            # window handle hWnd
    #---------------------------#
    cmp eax,0                   #
    jnz 0f                      #
        push MB_ICONERROR       # 16
        push offset Error       # heading
        push offset CreateFail  # message
        push 0                  # hWnd or null
        call _MessageBoxA@16    #
        xor eax,eax             # zero eax
        jmp end_app             # jmp xitwm
    0:                          #
    #---------------------------#
    push [ebp+116]              # iCmdShow
    push [ebp+40]               # hWnd
    call _ShowWindow@8          # show
    #---------------------------#
    push [ebp+40]               # hWnd
    call _UpdateWindow@4        # update
    #===========================#
    #                           #
    #     MESSAGE LOOP          #
    #                           #
    #===========================#
    add ebp,44                  # Msg offset
    0:                          #
    #---------------------------#
       push 0                   #
       push 0                   #
       push 0                   #
       push ebp                 #
       call _GetMessageA@16     #
    #---------------------------#
       cmp eax,0                #
       jz 1f                    #
    #---------------------------#
       push ebp                 #
       call _TranslateMessage@4 #
    #---------------------------#
       push ebp                 #
       call _DispatchMessageA@4 #
    #---------------------------#
    jmp 0b                      #
    1:                          #
    #===========================#
    mov eax,[ebp+8]            # Msg.wParam 3rd param
    sub ebp,44                  # original base
    jmp end_app                 #
    #===========================#
    #                           #
    #    TERMINATE APP          #
    #                           #
    #===========================#

    end_app:

    #===========================#
    add ebp,100                 #
    add esp,100                 #
    ret 16                      #
    #===========================#


    .balign 16

    #'type PAINTSTRUCT 0 64
    #00'  hDC AS DWORD
    #04'  fErase AS LONG
    #08'  rcPaint AS RECT
    #24'  fRestore AS LONG
    #28'  fIncUpdate AS LONG
    #32'  rgbReserved(0 TO 31) AS BYTE
    #64'end type

    #'type RECT 64 16
    #00' left as LONG
    #04' top as LONG
    #08' right as LONG
    #0C' bottom as LONG
    #10'end type

    #' hDC 80 4



    #===========================#
    WndProc:                    #
    #===========================#
        push ebp                #
        mov ebp,esp             #
        mov eax,[ebp+12]        #
    #===========================#
        cmp eax, 1 #WM_CREATE   #
        jnz 0f                  #
        xor eax,eax             # zero eax
        jmp 9f                  #
    #===========================#
    0:                          #
        cmp eax,2 #WM_DESTROY   #
        jnz 0f                  #
        push 0                  #
        call _PostQuitMessage@4 #
        xor eax,eax             # zero
        jmp 9f                  #
    #===========================#
    0:                          #
        cmp eax,0xf #WM_PAINT   #
        jnz 0f                  #
    #---------------------------#
        sub esp,100
        mov ebp,esp
    #---------------------------#
        push ebp                # 04 paint struc
        push [ebp+108]          # 00 hWnd
        call _BeginPaint@8      #
        mov [ebp+80],eax        # hDC
    #---------------------------#
        lea eax,[ebp+64]        #
        push eax                # 04 rect pointer
    #---------------------------#
        push [ebp+108]          # 00 hWnd
        call _GetClientRect@8   #
    #---------------------------#
        mov eax,0x20 #DT_SINGLELINE
        or eax,4 #DT_VCENTER    #
        or eax,1 #DT_CENTER     #
        push eax                # 10  37
    #---------------------------#
        lea eax,[ebp+64]        # 0C rect pointer
        push eax                #
    #---------------------------#
        push -1                 # 08
    #---------------------------#
        push offset Message     # 04 Hello World
    #---------------------------#
        push [ebp+80]           # 00 hDC
    #---------------------------#
        call _DrawTextA@20      #
    #---------------------------#
        push ebp                # 04 paint struc pointer
        push [ebp+108]          # 00 hWnd
        call _EndPaint@8        #
    #---------------------------#
        add esp,100             #
        mov ebp,esp             #
        xor eax,eax             #
        jmp 9f                  #
    #===========================#
    0:                          #
        cmp eax,0x100 #WM_KEYDOWN
        jnz 0f                  #
        mov eax,[ebp+16]        # wParam
        cmp al,27               # esc key
        jnz 1f                  #
            push 0              #
            push 0              #
            push 0x10 #WM_CLOSE #
            push [ebp+8]        # hWnd
            call _PostMessageA@16
        1:                      #
        xor eax,eax             # zero
        jmp 9f                  #
    #===========================#
    0:
        push [ebp+20]           # 0C lParam
        push [ebp+16]           # 08 wParam
        push [ebp+12]           # 04 wMsg
        push [ebp+8]            # 00 hWnd
        call _DefWindowProcA@16 #
    #===========================#
    9:                          #
        pop ebp                 #
        ret 16                  #
    #===========================#




    .balign 16



   
# .include "diagnostics.asm"


Code for error checking:

Using Message boxes to display registers, stack contents, data structures

Diagnostics.asm
    #...........................#
    call tester                 #
    #...........................#


    #===========================#
    tester1:                    # dword on stack
    #===========================#
    push eax                    #
    mov eax,[esp+8]             #
    call tester                 #
    pop eax                     #
    ret

    #===========================#
    tester2:                    # Error Code in eax
    #===========================#
    pusha                       #
    call _GetLastError@0        #
    call tester                 #
    popa                        #
    ret


    #===========================#
    tester3:                    # display stack (descending order)
    #===========================#
    push ebp                    #
    sub esp,292                 # +300 eq stack
    mov ebp,esp                 #
    pusha                       #
    mov esi,ebp                 #
    mov edi,ebp                 #
    add edi,344 # 11..0         # point to stack data top
    mov ebx,ebp                 #
    add ebx,300 # boundary      # point to stack data lower limit
    0:                          # loop start
        mov eax,[edi]           # get data
        call TextHexLong        # create Hexadecimal text
        mov dword ptr [esi+9],0x0a0d # crlf
        add esi,10              # ready for next line
        sub edi,4               # next data
        cmp edi,ebx             # boundary check
    jge 0b                      # repeat for next data
    #---------------------------#
    call Display                #
    #---------------------------#
    popa                        #
    add esp,292                 #
    pop ebp                     #
    ret                         #
    #===========================#


    #===========================#
    tester4:                    # data structures (with pointer in eax)
    #===========================#
    push ebp                    #
    sub esp,300                 # +300 eq stack
    mov ebp,esp                 #
    pusha                       #
    mov esi,ebp                 #
    mov edi,eax                 #
    mov ebx,eax                 #
    add ebx,40 # boundary       #
    0:                          # loop start
        mov eax,[edi]           # get data
        call TextHexLong        # create Hexadecimal text
        mov dword ptr [esi+9],0x0a0d # crlf
        add esi,10              # ready for next line
        add edi,4               # next data
        cmp edi,ebx             # boundary check
    jl 0b                       # repeat for next data
    #---------------------------#
    call Display                #
    #---------------------------#
    popa                        #
    add esp,300                 #
    pop ebp                     #
    ret                         #
    #===========================#



    #===========================#
    Display:                    #
    #===========================#
    push 1                      #
    push offset Caption         #
    push ebp                    #
    push 0                      #
    call _MessageBoxA@16        # MessageBox
    ret
    #===========================#

    #===========================#
    tester:                     # dword in eax
    #===========================#
    pusha                       #
    push 1                      #
    push offset Caption         # caption
    call text_eax               #
    push offset Buf             # message
    push 0                      # window handle
    call _MessageBoxA@16        # MessageBox
    popa                        #
    ret                         #
    #===========================#


    #===========================#
    text_eax:                   #
    #===========================#
    # Hexadecimal               #
    #---------------------------#
    push esi                    #
    mov esi,offset Buf          #
    call TextHexLong            #
    pop esi                     #
    ret                         #
    #===========================#


    #===========================#
    TextHexLong:
    #===========================#
    add esi,7                   #
    mov edx,eax                 #
    mov ecx, 8                  #
    1:                          #
        and al,0xf              #
        add al,0x30             #
        cmp al,0x39             #
        jle 0f                  #
            add al,7            #
        0:                      #
        mov [esi],al            #
        dec esi                 #
        shr edx,4               #
        mov al,dl               #
        dec ecx                 #
    jg 1b                       #
    mov byte ptr [esi+9],0x20   #
    ret                         #
    #===========================#




Batch File to Assemble and Link:



..\bin\win32\as hw.asm
..\bin\win32\ld --subsystem  windows -entry=_WinMainCRTStartup a.out ..\lib\win32\libkernel32.dll.a ..\lib\win32\libuser32.dll.a ..\lib\win32\libgdi32.dll.a ..\lib\win32\libshell32.dll.a
pause