Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / NASM

Simple Window With Assembly

5.00/5 (5 votes)
2 Oct 2015CPOL5 min read 47.8K   3K  
Here, we will create a simple Window using Assembly Programming Language and also gather knowledge about various assembly language instructions.

Introduction

In this tip, we will learn how to create a simple Window using Assembly language. We will also gather knowledge about various assembly language instructions. As assembler, we will use the NASM assembler.

Background

I will assume that you are familiar with Intel x86 assembly syntax.

Why Assembly Language?

Studying and learning assembly language will give you a better idea about how a program actually executes in a computer. And also, you can learn assembly language to build up your efficiency on programming.

Why NASM Assembler?

NASM is an Open-Source 80x86 assembler designed for portability. That means it supports many platforms. Its syntax is very simple and easy to understand. The syntax is similar to Intel's but less complex. And that's why I have chosen this assembler.

For compiling the source-code of the project, you will need the NASM assembler. I used the NASM v2.11.05 for this project. So make sure you have it. If you don't have it, then you can easily download it from their website.

For creating an executable file from the object file produced by NASM, you will need a linker. There are several linkers available on the internet such as GoLink, ALink, JWlink, etc. I've used GoLink for this project.

Using NASM to Create the Window

First of all, we will define our program entry point and then we will create our window.

Entry Point

Entry point is where the code execution begins. In NASM, the entry point is top-level global function of .text section, if you don't manually specify the entry point symbol name to your Linker.

We can write the entry point in .text section in this way:

ASM
section .text
global START
START:
    ; Here is your codes ...
            ; . .  . . . . .
    ret

Window

Inside the entry point function ( START ), we will create our window. In Windows, we use the CreateWindowExA function for creating a window. As class name of the window, we will use 'MyClass'. But before creating the window, we need to register the class name. We will use the RegisterClassExA function for that purpose.

In the following codes, first we initialize a 48 bytes structure called OurWindowClass declared in .bss section. Then, we pass it to the RegisterClassExA function parameter. And finally, we create our main window through calling the CreateWindowExA function.

ASM
mov dword[OurWindowclass], 48 ; .cbSize
mov dword[OurWindowclass+4], CS_OWNDC|CS_HREDRAW|CS_VREDRAW 
mov dword[OurWindowclass+8], WindowProc    ; lpfnWndProc
mov dword[OurWindowclass+12], 0
mov dword[OurWindowclass+16], 0
mov eax, dword[Instance]
mov dword[OurWindowclass+20], eax ; .hInstance
;.. ... .... .. ...... .. .. .

At offset 32, we set the background brush for our window:

ASM
mov dword[OurWindowclass+32], 5 ; .hbrBackground, 5 is COLOR_WINDOW

Here we assign the window class name called 'MyClass' at offset 40 of OurWindowClass structure:

ASM
mov dword[OurWindowclass+40], Windowclassname ; .lpszClassName
; . . . . .

Here, we will register our class. We will use the LEA instruction to get pointer of OurWindowclass into EAX register and, push EAX into system stack and then call the RegisterClassExA function using CALL instruction to register our window class:

ASM
lea eax, [OurWindowclass]
push eax
call RegisterClassExA

Here, we call the CreateWindowExA function to create the main window:

ASM
push 0
push dword[Instance]
push 0
push 0
push 382    ;  Height
push 362    ;  Width
push 140    ;  Top
push 360    ;  Left
push WS_VISIBLE+WS_OVERLAPPEDWINDOW ; Style
push Windowname
push Windowclassname
push 0        ; Extended style
call CreateWindowExA

In assembly language, to call a function, we push all the parameters in backwards order.

We know that CreateWindowExA returns created window handle. In assembly language, return value generally exists on EAX register. So, it's better we save the value before the EAX register get modified by another operation:

ASM
mov dword[Windowhandle], eax

Windowhandle is a variable declared in the .data section of the program.

Window Message Handler

You may have noticed that we have assigned address of a function called WindowProc at offset 8 of OurWindowClass structure. This function will handle all the common messages such as WM_PAINT, WM_SIZE, WM_CLOSE for our main window.

Here is our WindowProc function:

ASM
global WindowProc
WindowProc:
    push ebp    ; Saves the base pointer
    mov ebp, esp    ; Saves the stack pointer

In the above codes, we use the PUSH instruction to save EBP register value. Then we save ESP register by putting it into EBP.

The System will call this WindowProc function to let us handle massages like WM_PAINT, WM_CLOSE, etc. for our window. But before calling the function, the system will push 4 parameters on stack. So that means WindowProc function has 4 parameters.

The 1st parameter of WindowProc is handle of sender window. The 2nd parameter is the message. The 3rd and 4th parameters contain additional message information. Each parameter is 4 bytes in size. So we can make a list of all the parameters in this way:

ASM
; dword[ebp+20]    ; LPARAM
; dword[ebp+16]    ; WPARAM
; dword[ebp+12]    ; Msg
; dword[ebp+8]    ; hWnd

You might be wondering why parameter starts at 8 bytes ahead of EBP pointer?

Well, when any function is called, the return instruction pointer is pushed to the stack, that is 4 bytes. And then, when we set up the stack frame, the EBP register is pushed to the stack, that is more 4 bytes. So we have to skip over 8 bytes, to reach the first parameter.

Now, we will handle the WM_CLOSE message to make sure that our window will close properly:

ASM
; case WM_CLOSE
cmp dword[ebp+12], WM_CLOSE
jne Ldefault
;{  begin WM_CLOSE

    push 0
    call PostQuitMessage

;}  end WM_CLOSE

The CMP instruction compares the Message parameter value with WM_CLOSE constant and instead of storing the result into a register or something, it will affect the CPU FLAGS. The JNE is a conditional jump instruction. Here it will check the CPU FLAGS whether the Message parameter is equal to WM_CLOSE or not. If not equal, then it will jump to a label called Ldefault. And that's how we do conditional operation in assembly language.

In the Ldefault label, we call a function named DefWindowProcA. DefWindowProcA is a window procedure like our WindowProc function. This function processes window messages that our application does not process. This function makes sure that every message is processed for our window.

ASM
Ldefault:
    push dword[ebp+20]    ; LPARAM
    push dword[ebp+16]    ; WPARAM
    push dword[ebp+12]    ; Msg
    push dword[ebp+8]    ; hWnd
    call DefWindowProcA

Finally, we restore the ESP register again from EBP, recover the previous EBP and end the function with RET instruction.

ASM
mov esp, ebp
pop ebp    ; Restore the base pointer
ret

That's it!

Conclusion

The main purpose behind writing this tip is to give you information on GUI programming with assembly language. Although, assembly isn't an easy programming language at all. In fact, it is the hardest programming language to learn and it's very tough to remember the instructions set of assembly language. But practicing more and more will help you to learn this low-level programming language.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)