Assembly Language Step-by-Step

Jeff Duntemann

Mentioned 10

The eagerly anticipated new edition of the bestselling introduction to x86 assembly language The long-awaited third edition of this bestselling introduction to assembly language has been completely rewritten to focus on 32-bit protected-mode Linux and the free NASM assembler. Assembly is the fundamental language bridging human ideas and the pure silicon hearts of computers, and popular author Jeff Dunteman retains his distinctive lighthearted style as he presents a step-by-step approach to this difficult technical discipline. He starts at the very beginning, explaining the basic ideas of programmable computing, the binary and hexadecimal number systems, the Intel x86 computer architecture, and the process of software development under Linux. From that foundation he systematically treats the x86 instruction set, memory addressing, procedures, macros, and interface to the C-language code libraries upon which Linux itself is built. Serves as an ideal introduction to x86 computing concepts, as demonstrated by the only language directly understood by the CPU itselfUses an approachable, conversational style that assumes no prior experience in programming of any kindPresents x86 architecture and assembly concepts through a cumulative tutorial approach that is ideal for self-paced instructionFocuses entirely on free, open-source software, including Ubuntu Linux, the NASM assembler, the Kate editor, and the Gdb/Insight debuggerIncludes an x86 instruction set reference for the most common machine instructions, specifically tailored for use by programming beginnersWoven into the presentation are plenty of assembly code examples, plus practical tips on software design, coding, testing, and debugging, all using free, open-source software that may be downloaded without charge from the Internet.

More on

Mentioned in questions and answers.

What is the actual purpose and use of the EDI & ESI registers in assembler?

I know they are used for string operations for one thing.

Can someone also give an example?

In addition to the registers being used for mass operations, they are useful for their property of being preserved through a function call (call-preserved) in 32-bit calling convention. The ESI, EDI, EBX, EBP, ESP are call-preserved whereas EAX, ECX and EDX are not call-preserved. Call-preserved registers are respected by C library function and their values persist through the C library function calls.

Jeff Duntemann in his assembly language book has an example assembly code for printing the command line arguments. The code uses esi and edi to store counters as they will be unchanged by the C library function printf. For other registers like eax, ecx, edx, there is no guarantee of them not being used by the C library functions.

See section 12.8 How C sees Command-Line Arguments.

Note that 64-bit calling conventions are different from 32-bit calling conventions, and I am not sure if these registers are call-preserved or not.

What I know is that global and static variables are stored in the .data segment, and uninitialized data are in the .bss segment. What I don't understand is why do we have dedicated segment for uninitialized variables? If an uninitialised variable has a value assigned at run time, does the variable exist still in the .bss segment only?

In the following program, a is in the .data segment, and b is in the .bss segment; is that correct? Kindly correct me if my understanding is wrong.

#include <stdio.h>
#include <stdlib.h>

int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9};
int b[20]; /* Uninitialized, so in the .bss and will not occupy space for 20 * sizeof (int) */

int main ()

Also, consider following program,

#include <stdio.h>
#include <stdlib.h>
int var[10];  /* Uninitialized so in .bss */
int main ()
   var[0] = 20  /* **Initialized, where this 'var' will be ?** */

From Assembly Language Step-by-Step: Programming with Linux by Jeff Duntemann, regarding the .data section:

The .data section contains data definitions of initialized data items. Initialized data is data that has a value before the program begins running. These values are part of the executable file. They are loaded into memory when the executable file is loaded into memory for execution.

The important thing to remember about the .data section is that the more initialized data items you define, the larger the executable file will be, and the longer it will take to load it from disk into memory when you run it.

and the .bss section:

Not all data items need to have values before the program begins running. When you’re reading data from a disk file, for example, you need to have a place for the data to go after it comes in from disk. Data buffers like that are defined in the .bss section of your program. You set aside some number of bytes for a buffer and give the buffer a name, but you don’t say what values are to be present in the buffer.

There’s a crucial difference between data items defined in the .data section and data items defined in the .bss section: data items in the .data section add to the size of your executable file. Data items in the .bss section do not. A buffer that takes up 16,000 bytes (or more, sometimes much more) can be defined in .bss and add almost nothing (about 50 bytes for the description) to the executable file size.

I'm considering picking up some very rudimentary understanding of assembly. My current goal is simple: VERY BASIC understanding of GCC assembler output when compiling C/C++ with the -S switch for x86/x86-64.

Just enough to do simple things such as looking at a single function and verifying whether GCC optimizes away things I expect to disappear.

Does anyone have/know of a truly concise introduction to assembly, relevant to GCC and specifically for the purpose of reading, and a list of the most important instructions anyone casually reading assembly should know?

Unlike higher-level languages, there's really not much (if any) difference between being able to read assembly and being able to write it. Instructions have a one-to-one relationship with CPU opcodes -- there's no complexity to skip over while still retaining an understanding of what the line of code does. (It's not like a higher-level language where you can see a line that says "print $var" and not need to know or care about how it goes about outputting it to screen.)

If you still want to learn assembly, try the book Assembly Language Step-by-Step: Programming with Linux, by Jeff Duntemann.

Where could one start learning assembly language from? Could you suggest some place that can get me kick-started with it?

If you're on Windows and have an x86 processor:

  1. Get Assembly Language for Intel-Based Computers, one of the best books on the subject. Alternatively, you may try Randall Hyde's free, online Art of Assembly Language book as well.
  2. Download Masm32 assembler, which you will use to compile your assembly code into executables.
  3. And, if you like IDEs, get Winasm as well. It'll simplify code editing a great deal.

Assembly Language Step by Step by Jeff Duntemann. Good introductory text which will also talk about the basics of setting up a debugger and text editor (kate) to work with assembly.

I decided to learn assembly language because I came to know that learning it has many benefits, we can directly interact with the hardware, we can learn how computers better, and much more. When I started to learn it first, I came to know that it was a little bit weird and not like other programming languages so I thought that maybe I will find it hard to learn. So, I'm just asking what are the basic prerequisites for learning assembly language. For information, I have already learnt programming languages like C, C++, C#, PHP.

You don't really need any prerequisites if you pick right book.

I learned assembly language (basics, just not need more) as my first programming language myself (without any tutor) with Assembly Language Step-by-Step: Programming with Linux 3rd Edition. It teaches basics, but after reading this book you can read without any problem any other advanced assembly books.

You have to tell us what machine's assembly you want to learn. ARM, x86(_64), Sparc, etc are all different ISAs.

If you just want an introduction to the world of assembly programming in general, Randal Hyde's Art of Assembly is a good one (although what you write isn't exactly assembly, but more of a mix between high and low level languages, it will introduce you to the concept nicely).

If you have set your sights on x86, I can recommend this book: Professional Assembly Language. Apart from that book, is a great resource.

For x86, the choice of environment also matters. Here is a great tutorial for windows assembly programming by the University of Illinois Urbana Champaign ACM student chapter - SIGWINDOWS. For Unix, a great tutorial I have met is this one. A great, more general resource is Reverse Engineering for Beginners by Dennis Yurichev. This book, is targeted at both windows and unix environments, and although it concerns reverse engineering, it can help you learn a great deal about the machinations of programs running on your computer.

For ARM, this article serves as a great introduction. This article is also another great introduction to the matter

I'm going through jeff's amazing book assembly step by step, and I am on chapter 8 where he shows an example of an assembly program that takes a file from the user this way:

SECTION .bss            ; Section containing uninitialized data

    BUFFLEN equ 1024    ; Length of buffer
    Buff:   resb BUFFLEN    ; Text buffer itself

it reads the file text into Buff, and outputs a version of that text in ALL CAPS to a different file.

I want to run this program in debug mode to step through it to analyze what is happening with all the registers.

I am running this on ubuntu with INSIGHT.

I am a complete beginner. I know how to use Insight to step through, but the way the user needs to run this program is:

myProgram > outputfile.txt < inputfile.txt

How do I mimic this in a debugger?

here's the full source:

;  Executable name : uppercaser2
;  Version         : 1.0
;  Created date    : 3/25/2009
;  Last update     : 3/25/2009
;  Author          : Jeff Duntemann
;  Description     : A simple program in assembly for Linux, using NASM 2.05,
;    demonstrating simple text file I/O (through redirection) for reading an
;    input file to a buffer in blocks, forcing lowercase characters to 
;    uppercase, and writing the modified buffer to an output file.
;  Run it this way:
;    uppercaser2 > (output file) < (input file)  
;  Build using these commands:
;    nasm -f elf -g -F stabs uppercaser2.asm
;    ld -o uppercaser2 uppercaser2.o
SECTION .bss            ; Section containing uninitialized data

    BUFFLEN equ 1024    ; Length of buffer
    Buff:   resb BUFFLEN    ; Text buffer itself

SECTION .data           ; Section containing initialised data

SECTION .text           ; Section containing code

global  _start          ; Linker needs this to find the entry point!

    nop         ; This no-op keeps gdb happy...

; Read a buffer full of text from stdin:
    mov eax,3       ; Specify sys_read call
    mov ebx,0       ; Specify File Descriptor 0: Standard Input
    mov ecx,Buff        ; Pass offset of the buffer to read to
    mov edx,BUFFLEN     ; Pass number of bytes to read at one pass
    int 80h         ; Call sys_read to fill the buffer
    mov esi,eax     ; Copy sys_read return value for safekeeping
    cmp eax,0       ; If eax=0, sys_read reached EOF on stdin
    je Done         ; Jump If Equal (to 0, from compare)

; Set up the registers for the process buffer step:
    mov ecx,esi     ; Place the number of bytes read into ecx
    mov ebp,Buff        ; Place address of buffer into ebp
    dec ebp         ; Adjust count to offset

; Go through the buffer and convert lowercase to uppercase characters:
    cmp byte [ebp+ecx],61h  ; Test input char against lowercase 'a'
    jb Next         ; If below 'a' in ASCII, not lowercase
    cmp byte [ebp+ecx],7Ah  ; Test input char against lowercase 'z'
    ja Next         ; If above 'z' in ASCII, not lowercase
                ; At this point, we have a lowercase char
    sub byte [ebp+ecx],20h  ; Subtract 20h to give uppercase...
Next:   dec ecx         ; Decrement counter
    jnz Scan        ; If characters remain, loop back

; Write the buffer full of processed text to stdout:
    mov eax,4       ; Specify sys_write call
    mov ebx,1       ; Specify File Descriptor 1: Standard output
    mov ecx,Buff        ; Pass offset of the buffer
    mov edx,esi     ; Pass the # of bytes of data in the buffer
    int 80h         ; Make kernel call
    jmp read        ; Loop back and load another buffer full

; All done! Let's end this party:
    mov eax,1       ; Code for Exit Syscall
    mov ebx,0       ; Return a code of zero 
    int 80H         ; Make kernel call

this is my assignment. I've done my code for this assembly, but is there any way to make the convert speed more fast? thank in advance for any helps ;D

//Convert this nested for loop to assembly instructions
    for (a = 0; a < y; a++)
        for (b = 0; b < y; b++)
            for (c = 0; c < y; c++)
                if ((a + 2 * b - 8 * c) == y)

    _asm {
                mov ecx,0
                mov ax, 0
                mov bx, 0
                mov cx, 0

                push cx
                push bx
                push ax
                add bx, bx
                mov dx, 8
                mul dx
                add cx, bx
                sub cx, ax
                pop ax
                pop bx
                cmp cx, y
                jne increase
                inc count
                increase : pop cx
                          inc ax
                           cmp ax, y
                           jl Back

                           inc bx
                           mov ax, 0
                           cmp bx, y
                           jl Back

                           inc cx
                           mov ax, 0
                           mov bx, 0
                           cmp cx, y
                           jl Back


In Assembly Language Step-by-Step Jeff writes on page 230,

Now, speed optimization is a very slippery business in the x86 world, Having instructions in the CPU cache versus having to pull them from memory is a speed difference that swamps most speed differences among the instructions themselves. Other factors come into play in the most recent Pentium-class CPUs that make generalizations about instruction speed almost impossible, and certainly impossible to state with any precision.

Assuming you're on an x86 machine, my advice would be soak up all that Math in the other answers the best you can for optimizations.

I am learning the basics of assembly programming again. I am slowly working my way through Assembly Language Step-by-Step: Programming with Linux.

I am working on 64-bit Slackware machine (x86-x64), using NASM.

I am a little confused about what it means to increment an address. In my bss section, I have a Buffer resb 4096 buffer. In the text section I assign the address to esi:

mov esi, Buffer

Later on, I inc esi. This appears to advance esi to the next byte offset. I was a little surprised - I was expecting esi to advance 4 bytes because it is a 32-bit register.

Can I assume that incrementing an address will always move one byte at a time? Would I use add esi, 4 to advance 32-bits?

Do I have to explicitly ask for 4 bytes when I want to fill a 32-bit register? Something like mov eax, DWORD [esi]?

%esi is a register. It can contain an address, your bank account balance or anything else. There is no way the cpu knows what kind of value it contains. inc is simply an arithmetic instruction that increments the value stored in a register by 1.

So yeah, if you want to increment it by 32-bit, you have to add 4.

It's different when you do load and stores, the register you'll pick (say either %al or %eax) will indicate the size of load/store. If there is any ambiguity or if you want to change the size of the load/store, you'll need to use the size qualifier (word/dword etc)

Assembly is a family of very low-level programming languages, just above machine code. In assembly, each statement corresponds to a single machine code instruction. These instructions are represented as mnemonics in the given assembly language and are converted into executable machine code by a utility program referred to as an assembler; the conversion process is referred to as assembly, or assembling the code.

Language design

Basic elements

There is a large degree of diversity in the way that assemblers categorize statements and in the nomenclature that they use. In particular, some describe anything other than a machine mnemonic or extended mnemonic as a pseudo-operation (pseudo-op). A typical assembly language consists of three types of instruction statements that are used to define program operations:

  • Opcode mnemonics
  • Data sections
  • Assembly directives

Opcode mnemonics and extended mnemonics

Instructions (statements) in assembly language are generally very simple, unlike those in high-level language. Generally, a mnemonic is a symbolic name for a single executable machine language instruction (an opcode), and there is at least one opcode mnemonic defined for each machine language instruction. Each instruction typically consists of an operation or opcode plus zero or more operands. Most instructions refer to a single value, or a pair of values. Operands can be immediate (value coded in the instruction itself), registers specified in the instruction or implied, or the addresses of data located elsewhere in storage. This is determined by the underlying processor architecture: the assembler merely reflects how this architecture works. Extended mnemonics are often used to specify a combination of an opcode with a specific operand. For example, the System/360 assemblers use B as an extended mnemonic for BC with a mask of 15 and NOP for BC with a mask of 0.

Extended mnemonics are often used to support specialized uses of instructions, often for purposes not obvious from the instruction name. For example, many CPU's do not have an explicit NOP instruction, but do have instructions that can be used for the purpose. In 8086 CPUs the instruction xchg ax,ax is used for nop, with nop being a pseudo-opcode to encode the instruction xchg ax,ax. Some disassemblers recognize this and will decode the xchg ax,ax instruction as nop. Similarly, IBM assemblers for System/360 and System/370 use the extended mnemonics NOP and NOPR for BC and BCR with zero masks. For the SPARC architecture, these are known as synthetic instructions

Some assemblers also support simple built-in macro-instructions that generate two or more machine instructions. For instance, with some Z80 assemblers the instruction ld hl,bc is recognized to generate ld l,c followed by ld h,b. These are sometimes known as pseudo-opcodes.

Tag Use

Use the tag for assembly language programming questions, on any processor. You should also use a tag for your processor or instruction set architecture (, , , , , etc). Consider a tag for your assembler as well (, , , etcetera).

If your question is about inline assembly in C or other programming languages, see . For questions about .NET assemblies, use instead. For Java ASM, use the tag instead.


Beginner's Resources

Assembly Language tutorials, guides, and reference material

I am working my way through

Currently, I'm trying to move some of the code around so that I can compile with GAS, instead of NASM (the book's default compiler), and I'm having trouble understanding what some of it means.

This is my source code of confusion

EatMsg: db *Eat at Joe's!* , 10

EatLen: equ $-EatMst

(it's in .section .data)

how would I rewrite it to work with GAS?