Assembly language examples using NASM on Windows
Basic Window Extended - 64 bit
Description
Creates a window with simple functionality, 64 bit.
It demonstrates the processing of several WM_ messages, and improves upon the basic window example.
Assemble
- nasm -f win64 BasicWindowExtended64.asm -o BasicWindowExtended64.obj
Link
- golink /entry:Start kernel32.dll user32.dll gdi32.dll BasicWindowExtended64.obj
- polink /ENTRY:Start /SUBSYSTEM:WINDOWS /LIBPATH:c:\lib64 kernel32.lib user32.lib gdi32.lib BasicWindowExtended64.obj
Notes
Double click the icon to run the executable.
Improvements over the basic window example
- The window will be centred on the screen
- The client area will now be the correct size (before resizing)
- The window has a background colour
- 2 static controls are created. The text changes colour when clicked
- 2 edit controls are created. The tab key can be used to change focus
- The static and edit controls have their font changed from the system default
- A black rectangle is blitted to the window
- An exit confirmation is displayed when closing the window
Optimisations
At the start of each stack frame, space is allocated on the stack for the Windows API call parameters and local variables. This avoids the
need to constantly allocate and deallocate stack space for every Windows API call.
However, make sure you know how many parameters you need space for, and keep the stack aligned to a multiple of 16 bytes before an
API call.
When a WM_ message has been processed, Windows usually expects a return value of 0. There is now a single point of return where
RAX is zeroed and the stack frame removed.
WM_CTLCOLOREDIT and WM_CTLCOLORSTATIC need a brush returned instead, in these cases the zeroing of RAX is skipped over.
Code
ANSI_CHARSET EQU 0
BLACKNESS EQU 42h
CLIP_DEFAULT_PRECIS EQU 0
CS_BYTEALIGNWINDOW EQU 2000h
CS_HREDRAW EQU 2
CS_VREDRAW EQU 1
DEFAULT_PITCH EQU 0
ES_AUTOHSCROLL EQU 80h
ES_CENTER EQU 1
FALSE EQU 0
GRAY_BRUSH EQU 2
IDC_ARROW EQU 7F00h
IDI_APPLICATION EQU 7F00h
IDNO EQU 7
IMAGE_CURSOR EQU 2
IMAGE_ICON EQU 1
LR_SHARED EQU 8000h
MB_DEFBUTTON2 EQU 100h
MB_YESNO EQU 4
NULL EQU 0
NULL_BRUSH EQU 5
OPAQUE EQU 2
PROOF_QUALITY EQU 2
SM_CXFULLSCREEN EQU 10h
SM_CYFULLSCREEN EQU 11h
SS_CENTER EQU 1
SS_NOTIFY EQU 100h
SW_SHOWNORMAL EQU 1
TRUE EQU 1
WM_CLOSE EQU 10h
WM_COMMAND EQU 111h
WM_CREATE EQU 1
WM_CTLCOLOREDIT EQU 133h
WM_CTLCOLORSTATIC EQU 138h
WM_DESTROY EQU 2
WM_PAINT EQU 0Fh
WM_SETFONT EQU 30h
OUT_DEFAULT_PRECIS EQU 0
WS_CHILD EQU 40000000h
WS_EX_COMPOSITED EQU 2000000h
WS_OVERLAPPEDWINDOW EQU 0CF0000h
WS_TABSTOP EQU 10000h
WS_VISIBLE EQU 10000000h
WindowWidth EQU 640
WindowHeight EQU 170
Static1ID EQU 100
Static2ID EQU 101
Edit1ID EQU 102
Edit2ID EQU 103
extern AdjustWindowRectEx
extern BeginPaint
extern BitBlt
extern CreateFontA
extern CreateSolidBrush
extern CreateWindowExA
extern DefWindowProcA
extern DeleteObject
extern DestroyWindow
extern DispatchMessageA
extern EndPaint
extern ExitProcess
extern GetDlgCtrlID
extern GetStockObject
extern GetMessageA
extern GetModuleHandleA
extern GetSystemMetrics
extern InvalidateRect
extern IsDialogMessageA
extern LoadImageA
extern MessageBoxA
extern PostQuitMessage
extern RegisterClassExA
extern SendMessageA
extern SetBkColor
extern SetBkMode
extern SetTextColor
extern ShowWindow
extern TranslateMessage
extern UpdateWindow
global Start
section .data
Static1Colour dd 0F0F0F0h
Static1ColourA dd 020A0F0h
Static2Colour dd 000FFFFh
Static2ColourA dd 08000FFh
Edit1TextColour dd 0F590F5h
Edit1BackColour dd 0A00000h
Edit2TextColour dd 0A56E3Bh
Edit2BackColour dd 0FEFE8Eh
BackgroundColour dd 0A56E3Bh
WindowName db "Basic Window Extended 64", 0
ClassName db "Window", 0
SegoeUI db "Segoe UI", 0
StaticClass db "STATIC", 0
EditClass db "EDIT", 0
Text1 db "ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789", 0
Text2 db "abcdefghijklmnopqrstuvwxyz_0123456789", 0
ExitText db "Do you want to exit?", 0
section .bss
alignb 8
hInstance resq 1
BackgroundBrush resq 1
Font resq 1
Static1 resq 1
Static2 resq 1
Edit1 resq 1
Edit2 resq 1
section .text
Start:
sub RSP, 8
sub RSP, 32
xor ECX, ECX
call GetModuleHandleA
mov qword [REL hInstance], RAX
add RSP, 32
call WinMain
.Exit:
xor ECX, ECX
call ExitProcess
WinMain:
push RBP
mov RBP, RSP
sub RSP, 160 + 64 + 32
%define Screen.Width RBP - 160
%define Screen.Height RBP - 156
%define ClientArea RBP - 152
%define ClientArea.left RBP - 152
%define ClientArea.top RBP - 148
%define ClientArea.right RBP - 144
%define ClientArea.bottom RBP - 140
%define wc RBP - 136
%define wc.cbSize RBP - 136
%define wc.style RBP - 132
%define wc.lpfnWndProc RBP - 128
%define wc.cbClsExtra RBP - 120
%define wc.cbWndExtra RBP - 116
%define wc.hInstance RBP - 112
%define wc.hIcon RBP - 104
%define wc.hCursor RBP - 96
%define wc.hbrBackground RBP - 88
%define wc.lpszMenuName RBP - 80
%define wc.lpszClassName RBP - 72
%define wc.hIconSm RBP - 64
%define msg RBP - 56
%define msg.hwnd RBP - 56
%define msg.message RBP - 48
%define msg.Padding1 RBP - 44
%define msg.wParam RBP - 40
%define msg.lParam RBP - 32
%define msg.time RBP - 24
%define msg.py.x RBP - 20
%define msg.pt.y RBP - 16
%define msg.Padding2 RBP - 12
%define hWnd RBP - 8
mov ECX, dword [REL BackgroundColour]
call CreateSolidBrush
mov qword [REL BackgroundBrush], RAX
mov dword [wc.cbSize], 80
mov dword [wc.style], CS_HREDRAW | CS_VREDRAW | CS_BYTEALIGNWINDOW
lea RAX, [REL WndProc]
mov qword [wc.lpfnWndProc], RAX
mov dword [wc.cbClsExtra], NULL
mov dword [wc.cbWndExtra], NULL
mov RAX, qword [REL hInstance]
mov qword [wc.hInstance], RAX
xor ECX, ECX
mov EDX, IDI_APPLICATION
mov R8D, IMAGE_ICON
xor R9D, R9D
mov qword [RSP + 4 * 8], NULL
mov qword [RSP + 5 * 8], LR_SHARED
call LoadImageA
mov qword [wc.hIcon], RAX
xor ECX, ECX
mov EDX, IDC_ARROW
mov R8D, IMAGE_CURSOR
xor R9D, R9D
mov qword [RSP + 4 * 8], NULL
mov qword [RSP + 5 * 8], LR_SHARED
call LoadImageA
mov qword [wc.hCursor], RAX
mov RAX, qword [REL BackgroundBrush]
mov qword [wc.hbrBackground], RAX
mov qword [wc.lpszMenuName], NULL
lea RAX, [REL ClassName]
mov qword [wc.lpszClassName], RAX
xor ECX, ECX
mov EDX, IDI_APPLICATION
mov R8D, IMAGE_ICON
xor R9D, R9D
mov qword [RSP + 4 * 8], NULL
mov qword [RSP + 5 * 8], LR_SHARED
call LoadImageA
mov qword [wc.hIconSm], RAX
lea RCX, [wc]
call RegisterClassExA
mov ECX, SM_CXFULLSCREEN
call GetSystemMetrics
mov dword [Screen.Width], EAX
mov ECX, SM_CYFULLSCREEN
call GetSystemMetrics
mov dword [Screen.Height], EAX
mov dword [ClientArea.left], 0
mov dword [ClientArea.top], 0
mov dword [ClientArea.right], WindowWidth
mov dword [ClientArea.bottom], WindowHeight
lea RCX, [ClientArea]
mov EDX, WS_OVERLAPPEDWINDOW
xor R8D, R8D
mov R9D, WS_EX_COMPOSITED
call AdjustWindowRectEx
mov EAX, dword [ClientArea.top]
sub dword [ClientArea.bottom], EAX
mov EAX, dword [ClientArea.left]
sub dword [ClientArea.right], EAX
mov ECX, WS_EX_COMPOSITED
lea RDX, [REL ClassName]
lea R8, [REL WindowName]
mov R9D, WS_OVERLAPPEDWINDOW
xor R10D, R10D
mov EAX, dword [Screen.Width]
sub EAX, dword [ClientArea.right]
cmovs EAX, R10D
shr EAX, 1
mov dword [RSP + 4 * 8], EAX
mov EAX, dword [Screen.Height]
sub EAX, dword [ClientArea.bottom]
cmovs EAX, R10D
shr EAX, 1
mov dword [RSP + 5 * 8], EAX
mov EAX, dword [ClientArea.right]
mov dword [RSP + 6 * 8], EAX
mov EAX, dword [ClientArea.bottom]
mov dword [RSP + 7 * 8], EAX
mov qword [RSP + 8 * 8], NULL
mov qword [RSP + 9 * 8], NULL
mov RAX, qword [REL hInstance]
mov qword [RSP + 10 * 8], RAX
mov qword [RSP + 11 * 8], NULL
call CreateWindowExA
mov qword [hWnd], RAX
mov RCX, qword [hWnd]
mov EDX, SW_SHOWNORMAL
call ShowWindow
mov RCX, qword [hWnd]
call UpdateWindow
.MessageLoop:
lea RCX, [msg]
xor EDX, EDX
xor R8D, R8D
xor R9D, R9D
call GetMessageA
cmp RAX, 0
je .Done
mov RCX, qword [hWnd]
lea RDX, [msg]
call IsDialogMessageA
cmp RAX, 0
jne .MessageLoop
lea RCX, [msg]
call TranslateMessage
lea RCX, [msg]
call DispatchMessageA
jmp .MessageLoop
.Done:
xor EAX, EAX
mov RSP, RBP
pop RBP
ret
WndProc:
push RBP
mov RBP, RSP
sub RSP, 80 + 80 + 32
%define hWnd RBP + 16
%define uMsg RBP + 24
%define wParam RBP + 32
%define lParam RBP + 40
%define ps RBP - 80
%define ps.hdc RBP - 80
%define ps.fErase RBP - 72
%define ps.rcPaint.left RBP - 68
%define ps.rcPaint.top RBP - 64
%define ps.rcPaint.right RBP - 60
%define ps.rcPaint.bottom RBP - 56
%define ps.Restore RBP - 52
%define ps.fIncUpdate RBP - 48
%define ps.rgbReserved RBP - 44
%define ps.Padding RBP - 12
%define hdc RBP - 8
mov qword [hWnd], RCX
mov qword [uMsg], RDX
mov qword [wParam], R8
mov qword [lParam], R9
cmp qword [uMsg], WM_CLOSE
je WMCLOSE
cmp qword [uMsg], WM_COMMAND
je WMCOMMAND
cmp qword [uMsg], WM_CREATE
je WMCREATE
cmp qword [uMsg], WM_CTLCOLOREDIT
je WMCTLCOLOREDIT
cmp qword [uMsg], WM_CTLCOLORSTATIC
je WMCTLCOLORSTATIC
cmp qword [uMsg], WM_DESTROY
je WMDESTROY
cmp qword [uMsg], WM_PAINT
je WMPAINT
DefaultMessage:
mov RCX, qword [hWnd]
mov RDX, qword [uMsg]
mov R8, qword [wParam]
mov R9, qword [lParam]
call DefWindowProcA
jmp Return
WMCLOSE:
mov RCX, qword [hWnd]
lea RDX, [REL ExitText]
lea R8, [REL WindowName]
mov R9D, MB_YESNO | MB_DEFBUTTON2
call MessageBoxA
cmp RAX, IDNO
je Return.WM_Processed
mov RCX, qword [hWnd]
call DestroyWindow
jmp Return.WM_Processed
WMCOMMAND:
mov RAX, qword [wParam]
cmp AX, Static1ID
je .Static1
cmp AX, Static2ID
je .Static2
jmp Return.WM_Processed
.Static1:
mov EAX, dword [REL Static1Colour]
mov ECX, dword [REL Static1ColourA]
mov dword [REL Static1Colour], ECX
mov dword [REL Static1ColourA], EAX
mov RCX, qword [lParam]
xor EDX, EDX
mov R8D, TRUE
call InvalidateRect
jmp Return.WM_Processed
.Static2:
mov EAX, dword [REL Static2Colour]
mov ECX, dword [REL Static2ColourA]
mov dword [REL Static2Colour], ECX
mov dword [REL Static2ColourA], EAX
mov RCX, qword [lParam]
xor EDX, EDX
mov R8D, TRUE
call InvalidateRect
jmp Return.WM_Processed
WMCREATE:
xor ECX, ECX
lea RDX, [REL StaticClass]
lea R8, [REL Text1]
mov R9D, WS_CHILD | WS_VISIBLE | SS_NOTIFY | SS_CENTER
mov qword [RSP + 4 * 8], 120
mov qword [RSP + 5 * 8], 10
mov qword [RSP + 6 * 8], 400
mov qword [RSP + 7 * 8], 20
mov RAX, qword [hWnd]
mov qword [RSP + 8 * 8], RAX
mov qword [RSP + 9 * 8], Static1ID
mov RAX, qword [REL hInstance]
mov qword [RSP + 10 * 8], RAX
mov qword [RSP + 11 * 8], NULL
call CreateWindowExA
mov qword [REL Static1], RAX
xor ECX, ECX
lea RDX, [REL StaticClass]
lea R8, [REL Text2]
mov R9D, WS_CHILD | WS_VISIBLE | SS_NOTIFY | SS_CENTER
mov qword [RSP + 4 * 8], 120
mov qword [RSP + 5 * 8], 40
mov qword [RSP + 6 * 8], 400
mov qword [RSP + 7 * 8], 20
mov RAX, qword [hWnd]
mov qword [RSP + 8 * 8], RAX
mov qword [RSP + 9 * 8], Static2ID
mov RAX, qword [REL hInstance]
mov qword [RSP + 10 * 8], RAX
mov qword [RSP + 11 * 8], NULL
call CreateWindowExA
mov qword [REL Static2], RAX
xor ECX, ECX
lea RDX, [REL EditClass]
lea R8, [REL Text1]
mov R9D, WS_CHILD | WS_VISIBLE | ES_CENTER | WS_TABSTOP | ES_AUTOHSCROLL
mov qword [RSP + 4 * 8], 120
mov qword [RSP + 5 * 8], 70
mov qword [RSP + 6 * 8], 400
mov qword [RSP + 7 * 8], 20
mov RAX, qword [hWnd]
mov qword [RSP + 8 * 8], RAX
mov qword [RSP + 9 * 8], Edit1ID
mov RAX, qword [REL hInstance]
mov qword [RSP + 10 * 8], RAX
mov qword [RSP + 11 * 8], NULL
call CreateWindowExA
mov qword [REL Edit1], RAX
xor ECX, ECX
lea RDX, [REL EditClass]
lea R8, [REL Text2]
mov R9D, WS_CHILD | WS_VISIBLE | ES_CENTER | WS_TABSTOP | ES_AUTOHSCROLL
mov qword [RSP + 4 * 8], 120
mov qword [RSP + 5 * 8], 100
mov qword [RSP + 6 * 8], 400
mov qword [RSP + 7 * 8], 20
mov RAX, qword [hWnd]
mov qword [RSP + 8 * 8], RAX
mov qword [RSP + 9 * 8], Edit2ID
mov RAX, qword [REL hInstance]
mov qword [RSP + 10 * 8], RAX
mov qword [RSP + 11 * 8], NULL
call CreateWindowExA
mov qword [REL Edit2], RAX
mov ECX, 20
xor EDX, EDX
xor R8D, R8D
xor R9D, R9D
mov qword [RSP + 4 * 8], 400
mov qword [RSP + 5 * 8], NULL
mov qword [RSP + 6 * 8], NULL
mov qword [RSP + 7 * 8], NULL
mov qword [RSP + 8 * 8], ANSI_CHARSET
mov qword [RSP + 9 * 8], OUT_DEFAULT_PRECIS
mov qword [RSP + 10 * 8], CLIP_DEFAULT_PRECIS
mov qword [RSP + 11 * 8], PROOF_QUALITY
mov qword [RSP + 12 * 8], DEFAULT_PITCH
lea RAX, [REL SegoeUI]
mov qword [RSP + 13 * 8], RAX
call CreateFontA
mov qword [REL Font], RAX
mov RCX, qword [REL Static1]
mov EDX, WM_SETFONT
mov R8, qword [REL Font]
xor R9D, R9D
call SendMessageA
mov RCX, qword [REL Static2]
mov EDX, WM_SETFONT
mov R8, qword [REL Font]
xor R9D, R9D
call SendMessageA
mov RCX, qword [REL Edit1]
mov EDX, WM_SETFONT
mov R8, qword [REL Font]
xor R9D, R9D
call SendMessageA
mov RCX, qword [REL Edit2]
mov EDX, WM_SETFONT
mov R8, qword [REL Font]
xor R9D, R9D
call SendMessageA
jmp Return.WM_Processed
WMCTLCOLOREDIT:
mov RCX, qword [lParam]
call GetDlgCtrlID
cmp RAX, Edit1ID
je .Edit1
cmp RAX, Edit2ID
je .Edit2
.Default:
mov ECX, NULL_BRUSH
call GetStockObject
jmp Return
.Edit1:
mov RCX, qword [wParam]
mov EDX, dword [REL Edit1TextColour]
call SetTextColor
mov RCX, qword [wParam]
mov EDX, OPAQUE
call SetBkMode
mov RCX, qword [wParam]
mov EDX, dword [REL Edit1BackColour]
call SetBkColor
mov ECX, NULL_BRUSH
call GetStockObject
jmp Return
.Edit2:
mov RCX, qword [wParam]
mov EDX, dword [REL Edit2TextColour]
call SetTextColor
mov RCX, qword [wParam]
mov EDX, OPAQUE
call SetBkMode
mov RCX, qword [wParam]
mov EDX, dword [REL Edit2BackColour]
call SetBkColor
mov ECX, NULL_BRUSH
call GetStockObject
jmp Return
WMCTLCOLORSTATIC:
mov RCX, qword [lParam]
call GetDlgCtrlID
cmp RAX, Static1ID
je .Static1
cmp RAX, Static2ID
je .Static2
.Default:
mov ECX, NULL_BRUSH
call GetStockObject
jmp Return
.Static1:
mov RCX, qword [wParam]
mov EDX, dword [REL Static1Colour]
call SetTextColor
mov RCX, qword [wParam]
mov EDX, OPAQUE
call SetBkMode
mov RCX, qword [wParam]
mov EDX, 0604060h
call SetBkColor
mov ECX, NULL_BRUSH
call GetStockObject
jmp Return
.Static2:
mov RCX, qword [wParam]
mov EDX, dword [REL Static2Colour]
call SetTextColor
mov RCX, qword [wParam]
mov EDX, OPAQUE
call SetBkMode
mov RCX, qword [wParam]
mov EDX, 0005000h
call SetBkColor
mov ECX, GRAY_BRUSH
call GetStockObject
jmp Return
WMDESTROY:
mov RCX, qword [REL BackgroundBrush]
call DeleteObject
mov RCX, qword [REL Font]
call DeleteObject
xor ECX, ECX
call PostQuitMessage
jmp Return.WM_Processed
WMPAINT:
mov RCX, qword [hWnd]
lea RDX, [ps]
call BeginPaint
mov qword [hdc], RAX
mov RCX, qword [hdc]
mov EDX, 120
mov R8D, 130
mov R9D, 400
mov qword [RSP + 4 * 8], 20
mov RAX, qword [hdc]
mov qword [RSP + 5 * 8], RAX
mov qword [RSP + 6 * 8], 0
mov qword [RSP + 7 * 8], 0
mov qword [RSP + 8 * 8], BLACKNESS
call BitBlt
mov RCX, qword [hWnd]
lea RDX, [ps]
call EndPaint
Return.WM_Processed:
xor EAX, EAX
Return:
mov RSP, RBP
pop RBP
ret