Page 1 of 1

Assembler : Our First Windows Application A simnple frame window using FASM

#1 Martyn.Rae  Icon User is offline

  • The programming dinosaur
  • member icon

Reputation: 540
  • View blog
  • Posts: 1,406
  • Joined: 22-August 09

Posted 07 April 2010 - 05:19 PM

Assembler : Our First Windows Application

Switching allegance

I have chosen to switch allegance to FASM from NASM due to a closer proximity to MASM which I have been using for more years than I care to remember. Also, FASM comes with a set of Windows API include files, so you don't have to write them youself.

Introduction

You may think that writing a Windows application in assembler is completely crazy, but oddly enough it isn't. Agreed, there is slightly more typing involved, but other than that it is pretty much the same.

The Code

                    format              MS COFF
                    include             'c:\fasm\include\win32ax.inc'

                    ;==================================================
                    ; This is a simple window application that draws a
                    ; frame window whose client area is painted white.
                    ; The frame window can be resized and closed.
                    ;==================================================

                    public              WinMainCRTStartup
                    ;==================================================
                    ; A list of external definitiona that we need
                    ;==================================================
                    extrn               _DefWindowProcA@16:dword
                    extrn               _BeginPaint@8:dword
                    extrn               _CreateSolidBrush@4:dword
                    extrn               _GetClientRect@8:dword
                    extrn               _FillRect@12:dword
                    extrn               _DeleteObject@4:dword
                    extrn               _EndPaint@8:dword
                    extrn               _InvalidateRect@12:dword
                    extrn               _PostQuitMessage@4:dword
                    extrn               _GetModuleHandleA@4:dword
                    extrn               _RegisterClassA@4:dword
                    extrn               _CreateWindowExA@48:dword
                    extrn               _ShowWindow@8:dword
                    extrn               _GetMessageA@16:dword
                    extrn               _TranslateMessage@4:dword
                    extrn               _DispatchMessageA@4:dword
                    extrn               _ExitProcess@4:dword

                    ;==================================================
                    ; Application data
                    ;==================================================
                    .data
app_instance        dd                  0
window_handle       dd                  0
class_name          db                  'main window class', 0
window_title        db                  'Assembler Frame Window', 0 

message             MSG                 <>
main_class          WNDCLASS            CS_OWNDC, main_window_proc, \
                                        0, 0, NULL, NULL, \
                                        NULL, NULL,NULL, class_name

                    ;==================================================
                    ; Start of the code section
                    ;==================================================
                    .code
                    ;==================================================
                    ; The main window message processing procedure
                    ;==================================================
proc                main_window_proc    hwnd, wmsg, wparam, lparam
                    local               hDC:DWORD, hBrush:DWORD, 
                    local               paint_struct:PAINTSTRUCT
                    local               client_rect:RECT
                    ;==================================================
                    ; Process the windows message
                    ;==================================================
                    mov                 eax, [wmsg]
                    cmp                 eax, WM_CLOSE
                    je                  .close_window
                    cmp                 eax, WM_PAINT
                    je                  .paint_window
                    cmp                 eax, WM_SIZE
                    je                  .size_window
                    ;==================================================
                    ; Any message not processed above falls through 
                    ; to the default window message handler
                    ;==================================================
.defwndproc:        push                [lparam]
                    push                [wparam]
                    push                [wmsg]
                    push                [hwnd]
                    call                _DefWindowProcA@16
                    jmp                 .finish
                    ;==================================================
                    ; Paint the window
                    ;==================================================
.paint_window:      lea                 eax, [paint_struct]
                    push                eax
                    push                [hwnd]
                    call                _BeginPaint@8
                    mov                 [hDC], eax
                    push                0FFFFFFH
                    call                _CreateSolidBrush@4 
                    mov                 [hBrush], eax
                    lea                 eax, [client_rect]
                    push                eax
                    push                [hwnd]
                    call                _GetClientRect@8
                    push                [hBrush]
                    lea                 eax, [client_rect]
                    push                eax
                    push                [hDC]
                    call                _FillRect@12
                    push                [hBrush]
                    call                _DeleteObject@4
                    lea                 eax, [paint_struct]
                    push                eax
                    push                [hwnd]
                    call                _EndPaint@8 
                    xor                 eax, eax
                    jmp                 .finish
                    ;==================================================
                    ; Ensure that when the window is ressized, 
                    ; it is repainted
                    ;==================================================
.size_window:       push                TRUE
                    push                NULL
                    push                [hwnd]
                    call                _InvalidateRect@12
                    xor                 eax, eax
                    jmp                 .finish
                    ;==================================================
                    ; Post the quit message when the window is
                    ; closed
                    ;==================================================
.close_window:      push                0
                    call                _PostQuitMessage@4
                    xor                 eax, eax
.finish:            ret
                    endp

                    ;==================================================
                    ; Windows main entry point
                    ;==================================================
WinMainCRTStartup:  push                NULL
                    call                _GetModuleHandleA@4
                    mov                 [app_instance], eax
                    ;==================================================
                    ; Set up the WNDClASS structure
                    ;==================================================
                    mov                 [main_class.hInstance], eax
                    ;==================================================
                    ; Register the window class
                    ;==================================================
                    push                main_class
                    call                _RegisterClassA@4
                    ;==================================================
                    ; Create the frame window
                    ;==================================================
                    push                NULL
                    push                [app_instance]
                    push                NULL
                    push                NULL
                    push                600
                    push                800
                    push                100
                    push                100
                    push                WS_OVERLAPPEDWINDOW
                    push                window_title
                    push                class_name
                    push                WS_EX_WINDOWEDGE or \
                                        WS_EX_CLIENTEDGE
                    call                _CreateWindowExA@48
                    mov                 [window_handle], eax
                    ;==================================================
                    ; Make the window visible to the user
                    ;==================================================
                    push                SW_SHOW
                    push                eax
                    call                _ShowWindow@8
                    ;==================================================
                    ; Windows message loop
                    ;==================================================
message_loop:       push                0
                    push                0
                    push                NULL
                    push                message
                    call                _GetMessageA@16
                    ;==================================================
                    ; Check to see if we are closing
                    ;==================================================
                    or                    eax, eax
                    je                    message_loop_ended
                    ;==================================================
                    ; Translate the message
                    ;==================================================
                    push                message
                    call                _TranslateMessage@4
                    ;==================================================
                    ; Dispatch the message
                    ;==================================================
                    push                message
                    call                _DispatchMessageA@4
                    jmp                 message_loop
                    ;==================================================
                    ; Exit the process and return control back 
                    ; to the system
                    ;==================================================
message_loop_ended: push                0
                    call                _ExitProcess@4



Development Notes

There are a few tricky things that you need to be careful with when writing any assembler code and passing parameters to functions in FASM. If the parameter being passed to a function is an address, and the data exists in the data section such as exampled in the code below.

app_instance        dd                  0
...
                    push                app_instance



The push will place the address of the variable app_instance onto the stack (whereas if we had said [app_instance] then the contents at the address of app_instance would be pushed onto the stack). If however you want to push the address of a local variable onto the stack, you cannot use this method. Instead you have to use the load the effective address (lea) instruction as shown in the example below.

                   local               client_rect:RECT
...
                   lea                 eax, [client_rect]
                   push                eax



Failure to observe this will provide you with many happy hours of head-scratching trying to ascertain why the assembler will not compile the code.

Also, so not be tempted to use the invoke macro to reduce the code you are producing as unlike the version of invoke provided by MASM which is inbuilt into the assembler, the FASM version is a macro that as I discovered to my detriment does not work very well.

This post has been edited by Martyn.Rae: 08 April 2010 - 10:06 AM


Is This A Good Question/Topic? 0
  • +

Page 1 of 1