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




MultiQuote


|