Page 1 of 1

Getting Started In Assembler A simple Hello World tutorial using NASM

#1 Martyn.Rae  Icon User is offline

  • The programming dinosaur
  • member icon

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

Posted 06 April 2010 - 01:18 AM

*
POPULAR

Getting Started In Assembler

Introduction

Assembler is a low-level programming language that has very few 'bells and whistles'. There is no concept of classes as such (although there are are few hybrid languages such as HLA that offer this type of feature), so object-oriented programming is not part of the assembler programmers repertoire. For all it's shortcomings however, it is an elegant programming language that is pure and natural.

I am going to assume that you have some knowledge of C++, because I think that it is important to be able to compare the various assembler statements with equivalent high-level language statements.

Obtaining an assembler

Before you can start programmming in assembler, you will of course need an assembler to compile with. There are many free assemblers out there and I shall name some of the more popular ones for the x86-64 processors and that are capable of running on both Windows and (with the exception of GoASM) Linux systems.

  • NASM - the Netwide Assembler
  • FASM - the Flat Assembler
  • GoASM - the Go Assembler (Windows ONLY)


I am got going to mention assemblers such as TASM or MASM, as these a proprietary products from Borland and Microsoft. As such code produced by said assemblers is under license and subject to restrictions, so we will leave them alone.

Of the three most popular assemblers listed above, I would strongly recommend using the Netwide Assembler as it provides the best features.

Other resources you will need

All of the Intel software developer manuals may be found here.

Getting started with a 'Hello World' example

Let's dive in at the deep end and provide you with the code for our 'Hello World' application.

                    ; This is a console application that writes the infamous 'Hello World'
                    ; text to the console and then exits

                    global      _mainCRTStartup			; This is the main program entry point
                    extern      _ExitProcess@4			; Windows API call to exit the process
                    extern      _GetStdHandle@4			; Windows API call to get the standard output handle
                    extern      _WriteFile@20			; Windows API call to write to a handle
                    
                    section     .data                           ; Start of the data segment
hello_world         db          'Hello World', 10, 0            ; The hello world message
bytes_written       dd           0                              ; Return 32-bit word from WriteFile

                    section      .code                          ; Start of the code segment
                    
                    ; We need to get hold of the standard output handle so we can write
                    ; our 'Hello World' text to it. This is provided by the GetStdHandle 
                    ; windows API call
                    
_mainCRTStartup:    
                    push        -11                             ; We want the standard output handle
                    call        _GetStdHandle@4			; Call the Windows API GetStdHandle to retrieve it
                    
                    ; The EAX register now contains the handle we need to write to. As
                    ; we are just going to stack the parameters needed for the WriteFile
                    ; API call, and the push instruction does not affect the registers, 
                    ; we can leave the returned handle in EAX for the moment
                    
                    push        0                               ; We do not want overlapped I/O
                    push        dword bytes_written             ; The address of the number of bytes written
                    push        13                              ; The length of the text we are writing
                    push        dword hello_world		; The address of the text we are writing
                    push        eax                             ; The handle returned from GetStdHandle call
                    call        _WriteFile@20			; Write the text to the standard output handle
                    
                    ; The text has been written, so all we need to do now is exit the
                    ; process and return control back to the console
                    
                    push        0                               ; Stack the exit code
                    call        _ExitProcess@4                  ; Exit the process



Right, now let's look at it in some detail making comparisions with C++. The first line

                   global      _mainCRTStartup			; This is the main program entry point



does not directly have a C++ equivalent statement as most top-level symbols are automatically made global (i.e. other modules can reference the symbols declared in any given module. This statement tells the assembler that the label _mainCRTStartup in the code is made visible to the outside world.

The next three lines

                    extern      _ExitProcess@4			; Windows API call to exit the process
                    extern      _GetStdHandle@4			; Windows API call to get the standard output handle
                    extern      _WriteFile@20			; Windows API call to write to a handle



would be equivalent to

extern "C" {
VOID WINAPI ExitProcess(UINT uExitCode);
HANDLE WINAPI GetStdHandle(DWORD nStdHandle);
BOOL WINAPI WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nBytesToWrite, LPDWORD lpBytesWritten, LPOVERLAPPED lpOverlapped);
}



The C++ compiler actually adds an underscore to the names you are prototyping and counts the number of bytes used as parameter space appending the @ sign and the number calculated.

The next line

                    section     .data                           ; Start of the data segment



tells the assembler that the lines following this statement are to be assembled into the data segment. Again, there is no C++ equivalent as it is taken care of by the compiler. (The closest in VC++ is the #pragma data_seg(".data") statement - which all but the most experienced programmers would use).

The next two lines

hello_world         db          'Hello World', 10, 0            ; The hello world message
bytes_written       dd           0                              ; Return 32-bit word from WriteFile



define the data that the program needs to run. These two statements would be written as

char hello_world[] = "Hello World\n";
int bytes_written;



The next line

                    section      .code                          ; Start of the code segment



tells the assembler that we are now switching to the code segment, has no C++ equivalent as it is taken care of by the compiler. (The closest in VC++ is the #pragma code_seg(".code") statement - which all but the most experienced programmers would use).

The next line

mainCRTStartup:



is the global label that we declared in the first line of the program. This is the default entry point for the program, and needs to be visible to the outside world otherwise the linker will not know where the start point is, and the executable will not be produced. Notice the colon ( : ) at the end of the label. This says it is a code label.

The next two lines are pushing the value of -11 onto the stack and calling the GetStdHandle windows API call to obtain the handle for standard output.

                    push        -11                             ; We want the standard output handle
                    call        _GetStdHandle@4			; Call the Windows API GetStdHandle to retrieve it



The C++ equivalent would be

    HANDLE hFile = GetStdHandle(-11);



Notice that the result from the C++ call is being placed into a variable hFile. In assembler, all API functions (that is routines that return a value) return the result of the call in the EAX register.

The next six lines actually push the parameters for the WriteFile API call onto the stack ready to be used.

                    push        0                               ; We do not want overlapped I/O
                    push        dword bytes_written             ; The address of the number of bytes written
                    push        13                              ; The length of the text we are writing
                    push        dword hello_world		; The address of the text we are writing
                    push        eax                             ; The handle returned from GetStdHandle call
                    call        _WriteFile@20			; Write the text to the standard output handle



The C++ equivalent would be

    WriteFile(hFile, hello_world, 13, &bytes_written, 0);



Notice that for windows API calls, parameters are stacked last parameter first, next to last parameter next etc so the first parameter is always at the top of the stack.

Finally, the last two lines stack the exit code and call ExitProcess.

                    push        0                               ; Stack the exit code
                    call        _ExitProcess@4                  ; Exit the process



The equivalent C++ code would be

    return 0;



Compiling and linking

To compile the assembler source into an object file ready for linking, you would use the command line

nasm -f win32 HelloWorld.asm

You would then link the object file to create an executable (I am not going to detail that part of the process because it depends on which IDE you are using if any, which linker you chose to use etc.

Conclusion

This tutorial provides a small insight into the world of assembler programming. We have used two instructions push and call to write our 'Hello World' application (all the other statements are assembler control statements). In future tutorials, we will look at some more basic assembler instructions and how they can be used to build applications.

The next tutorial is here.

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


Is This A Good Question/Topic? 10
  • +

Replies To: Getting Started In Assembler

#2 sarmanu  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 966
  • View blog
  • Posts: 2,362
  • Joined: 04-December 09

Posted 06 April 2010 - 08:59 AM

Looking forward to learn ASM. Gonna be a pain to learn it by myself, but I'm gonna give it a try :) Thanks for the tutorial, I really needed one.
Was This Post Helpful? 0
  • +
  • -

#3 Dogstopper  Icon User is offline

  • The Ninjaducky
  • member icon



Reputation: 2870
  • View blog
  • Posts: 11,021
  • Joined: 15-July 08

Posted 08 April 2010 - 10:09 PM

Wow! Great tutorial Martyn.Rae! I started to learn NASM a while back and might think about picking it up again (for Linux though)
Was This Post Helpful? 0
  • +
  • -

#4 nathanpc  Icon User is offline

  • SCIENCE!
  • member icon

Reputation: 112
  • View blog
  • Posts: 1,171
  • Joined: 31-July 09

Posted 26 May 2010 - 02:36 AM

Great tutorial, but it isn't Assembler, it's Assembly. Assembler is the Assembly compiler. ;)
Was This Post Helpful? 1
  • +
  • -

#5 athlon32  Icon User is offline

  • D.I.C Regular
  • member icon

Reputation: 116
  • View blog
  • Posts: 363
  • Joined: 20-August 08

Posted 30 May 2010 - 01:08 PM

View Postnathanpc, on 26 May 2010 - 01:36 AM, said:

Great tutorial, but it isn't Assembler, it's Assembly. Assembler is the Assembly compiler. ;)


That's true. Of course assembler language would be the language of the assembler :sweatdrop:

Anyways, great tutorial Martyn.Rae! :D
Was This Post Helpful? 0
  • +
  • -

#6 JITHU  Icon User is offline

  • D.I.C Head
  • member icon

Reputation: 62
  • View blog
  • Posts: 201
  • Joined: 02-July 07

Posted 01 June 2010 - 10:37 AM

Nice tutorial!

I think, it would be great if you could also show us how to link this programs, maybe using the gcc linker. I'm getting some reference errors.

I tried importing kernel32.dll like this:
extern _ExitProcess@4
import _ExitProcess@4 kernel32.dll ExitProcess
extern _GetStdHandle@4
import _GetStdHandle@4 kernel32.dll GetStdHandle
extern _WriteFile@20
import _WriteFile@20 kernel32.dll WriteFile


But i get this errors:

hello.asm:7: error: parser: instruction expected
hello.asm:9: error: symbol `import' redefined
hello.asm:9: error: parser: instruction expected
hello.asm:11: error: symbol `import' redefined
hello.asm:11: error: parser: instruction expected


By seeing this error message, i think, import can be called only once.
After that i commented out the last 2 imports and tried compiling and
got this error here:

hello.asm:7: error: parser: instruction expected


BTW I've compiled with this command:
nasm -f win32 hello.asm -o hello.o


Thanks for any help!
Was This Post Helpful? 0
  • +
  • -

#7 v0rtex  Icon User is offline

  • Caffeine: db "Never Enough!"
  • member icon

Reputation: 223
  • View blog
  • Posts: 773
  • Joined: 02-June 10

Posted 09 September 2010 - 11:48 AM

JITHU: try nasm -f win32 hello.asm
then gcc -o hello hello.o
and you should get an executable

Quote

I think, it would be great if you could also show us how to link this programs, maybe using the gcc linker.

-Assuming you have both Netwide Assembler and gcc installed already.

This post has been edited by v0rtex: 09 September 2010 - 11:51 AM

Was This Post Helpful? 0
  • +
  • -

#8 vividexstance  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 650
  • View blog
  • Posts: 2,223
  • Joined: 31-December 10

Posted 04 February 2011 - 06:27 PM

I converted this example so it would work on my OS, ubuntu linux. Here it is:
; This is a console application that writes the digits 0 .. 9
; followed by a new-line to the console and then exits.

				global 		_start			; main program entry point

				section 	.data			; start of the data segment
message			db			'0', 10			; the asci digit 0 and a newline
bytes_written	dd			0				; return 32-bit word from WriteFile
counter			dd			0				; loop counter initialized to zero

				section 	.code
				
				; We need to get hold of the standard output handle so
				; we can write out text to it.
				
_start:
				; The start of out loop, and the position we return to
				; if we want to print out the next digit
				
start_loop:
				mov			eax, 4			; sys_write call number
				mov			ebx, 1			; output handle
				mov		ecx, message		; address of message to write
				mov			edx, 2			; length of message write
				int			80H				; write the text to std output
				inc		byte [message]		; increment the digit char
				inc		dword [counter]		; increment the counter
				mov		eax, [counter]		; load the counter value into eax
				cmp			eax, 10			; compare the counter to 10
				jb		start_loop			; if count < 10, goto start of loop
				
				; The text has been written to console, so now all we need to
				; do is exit the process and return control back to the console
				
				mov			eax, 1			; sys_exit call number
				xor			ebx, ebx		; exit code
				int			80H				; leave the system


This post has been edited by vividexstance: 04 February 2011 - 06:29 PM

Was This Post Helpful? 1
  • +
  • -

Page 1 of 1