L1 db “a”, “bc”
L2 dd 0AABBCCAAh
L3 times 2 dw -25
L4 db “d”, 0
mov eax, L2
add eax, 3
mov word [eax], 23
mov ebx, L3
mov ebx, [ebx]
mov eax, 011223344h
mov [L3], eax
NASM Program Structure
; include directives
segment .data
; DX directives
segment .bss
; RESX directives
segment .text
; instructions
What is in the text segment?
Remember the C driver:
int main() // C driver
{
int ret_status;
ret_status = asm_main();
return ret_status;
}
The text segment defines the asm_main symbol:
global _asm_main ; makes the symbol
visible
_asm_main: ; marks the beginning of
the ; routine instructions
On Windows, you need the ‘_’ before asm_main
although in C the call is simply to “asm_main” not
to “_asm_main”
NASM Program Structure
; include directives
segment .data
; DX directives
segment .bss
; RESX directives
segment .text
global _asm_main
_asm_main:
; instructions
More on the text segment
Before and after running the instructions of your program
there is a need for some “setup” and “cleanup”
We’ll understand this later, but for now, let’s just accept
the fact that your text segment will always looks like this:
enter 0,0
pusha
;
; Your program here
;
popa
mov eax, 0
leave
ret
NASM Skeleton File
; include directives
segment .data
; DX directives
segment .bss
; RESX directives
segment .text
global asm_main
asm_main:
enter 0,0
pusha
; Your program here
popa
mov eax, 0
leave
ret
Our First Program
Let’s just write a program that adds two
4-byte integers and writes the result to
memory
Yes, this is boring, but we have to start
somewhere
The two integers are initially in
the .data segment, and the result will
be written in the .bss segment
Our First Program
segment .data
integer1 dd 15 ; first int
integer2 dd 6 ; second int
segment .bss
result resd1 ; result
segment .text
global _asm_main
_asm_main:
enter 0,0
pusha
mov eax, [integer1]
add eax, [integer2]
mov [result], eax
popa
moveax, 0
leave
ret
I/O?
This is all well and good, but it’s not very interesting if
we can’t “see” anything
We would like to:
Be able to provide input to the program
Be able to get output from the program
Also, debugging will be difficult, so it would be nice if
we could tell the program to print out all register
values, or to print out the content of some zones of
memory
Doing all this requires quite a bit of assembly code and
requires techniques that we will not see for a while
The author of our textbook provides a nice I/O package
that we can just use, without understanding how it
works for now
asm_io.asm and asm_io.inc
The “PC Assembly Language” book comes
with many add-ons and examples
Downloadable from the course’s Web site
A very useful one is the I/O package, which
comes as two files:
asm_io.asm (assembly code)
asm_io.inc (macro code)
Simple to use:
Assemble asm_io.asm into asm_io.o
Put “%include asm_io.inc” at the top of your
assembly code
Link everything together into an executable
Simple I/O
Say we want to print the result integer
in addition to having it stored in
memory
We can use the print_int “macro”
provided in asm_io.inc/asm
This macro prints the content of the
eax register, interpreted as an integer
We invoke print_int as:
call print_int
Let’s modify our program
Our First Program
%include “asm_io.inc”
segment .data
integer1 dd 15 ; first int
integer2 dd 6 ; second int
segment .bss
result resd 1 ; result
segment .text
global _asm_main
_asm_main:
enter 0,0
pusha
mov eax, [integer1]
add eax, [integer2]
mov [result], eax
call print_int
popa
mov eax, 0
leave
ret
How do we run the program?
Now that we have written our program using a text editor, we
need to assemble it
When we assemble a program we obtain an object file (a .obj
file)
We use NASM to produce the .obj file:
% nasm -f win32 first.asm
So now we have a .obj file, that is a machine code translation
of our assembly code
We also need a .obj file for the C driver:
% cl -c driver.c
We generate a 32-bit object
We also create asm_io.obj by assembling asm_io.asm
Now we have three .obj files.
We link them together to create an executable:
% cl –Fefirst.exe driver.obj first.obj asm_io.obj
And voila...
Makefile
nasm -f win32 %1.asm
cl -Fe%1.exe %1.obj driver.obj
asm_io.obj
The Big Picture
File2.a
File1.a sm File3.a Driver.
sm sm c
na na na
s s s cl
m m m
File1.o File2.o File3.o Driver.
bj bj bj obj
cl
execut
able
More I/O A
X
=
AH AL
EAX
print_char: prints out the character corresponding to the
ASCII code stored in AL
print_string: prints out the content of the string stored at
the address stored in eax
The string must be null-terminated (last byte = 00)
print_nl: prints a new line
read_int: reads an integer from the keyboard and stores
it into eax
read_char: reads a character from the keyboard and
stores it into AL
Let us modify our code so that the two input integers
are read from the keyboard, so that there are more
convenient messages printed to the screen
Our First Program
%include “asm_io.inc” mov eax, [integer1] ; eax = first
integer
add eax, [integer2] ; eax += second
segment .data integer
msg1 db “Enter a number: ”, 0 mov [result], eax ; store the
msg2 db “The sum of “, 0 result
msg3 db “ and “, 0 mov eax, msg2 ; note that this is a
msg4 db “ is: “, 0 pointer
segment .bss call print_string
integer1 resd 1 ; first integer mov eax, [integer1] ; note that this is a
value
integer2 resd 1 ; second integer
call print_int
result resd 1 ; result
mov eax, msg3 ; note that this is a
segment .text
pointer
global _asm_main
call print_string
_asm_main:
mov eax, [integer2] ; note that this is a
enter 0,0
value
pusha
call print_int
mov eax, msg1 ; note that this is a
pointer! mov eax, msg4 ; note that this is a
call print_string pointer
call read_int ; read the first integer call print_string
mov [integer1], eax ; store it in memory mov eax, [result] ; note that this is a
mov eax, msg1 ; note that this is a value
pointer! call print_int
call print_string call print_nl
call read_int ; read the second integer popa
mov [integer2], eax ; store it in memory mov eax, 0
leave
Our First Program
In the examples accompanying our
textbook there is a very similar example
of a first program (called first.asm)
So, this is great, but what if we had a
bug to track?
We will see that writing assembly code is
very bug-prone
It would be _very_ cumbersome to rely
on print statements to print out all
registers, etc.
So asm_io.inc/asm also provides two
convenient macros for debugging!
dum_regs and dump_mem
The macro dump_regs prints out the bytes stored in all
the registers (in hex), as well as the bits in the FLAGS
register (only if they are set to 1)
dump_regs 13
‘13’ above is an arbitrary integer, that can be used to
distinguish outputs from multiple calls to dump_regs
The macro dump_memory prints out the bytes stored in
memory (in hex). It takes three arguments:
An arbitrary integer for output identification purposes
The address at which memory should be displayed
The number of 16-byte segments that should be displayed
for instance
dump_mem 29, integer1, 3
prints out “29”, and then 3*16 bytes
Using dump_regs and dump_mem
To demonstrate the usage of these two
macros, let’s just write a program that
highlights the fact that the Intel x86
processors use Little Endian encoding
We will do something ugly using 4
bytes
Store a 4-byte hex quantity that
corresponds to the ASCII codes: “live”
“l” = 6Ch
“i” = 69h
“v” = 76h
“e” = 65h
Print that 4-byte quantity as a string
Little-Endian Exposed
%include “asm_io.inc”
segment .data
bytes dd 06C697665h ; “live”
end db 0 ; null
segment .text
global _asm_main
_asm_main:
enter 0,0
pusha
mov eax, bytes ; note that this is an address
call print_string ; print the string at that
address
call print_nl ; print a new line
mov eax, [bytes] ; load the 4-byte value into
eax
dump_mem 0, bytes, 1 ; display the memory
dump_regs 0 ; display the registers
pusha
popa
mov eax, 0
leave
ret
Output of the program
The address of
The program “bytes” is
prints “evil” 080499AC”
and not “live”
evil “bytes” starts here
Memory Dump # 0 Address = 080499AC
080499A0 00 00 00 00 00 00 00 00 A8 98 04 08 65 76 69 6C "????????????evil"
080499B0 00 00 00 00 25 69 00 25 73 00 52 65 67 69 73 74 "????%i?%s?
Regist"
Register Dump # 0
EAX = 6C697665 EBX = 4014EFF4 ECX = BFFFDD60 EDX = 00000001
ESI = 00000000 EDI = 40015CC0 EBP = BFFFDD28 ESP = BFFFDD08
EIP = 0804844D FLAGS = 0286 SF PF
and yes, it’s
“evil”
The “dump” starts
at address bytes in eax
080499A0 (a are in the
multiple of 16) “live” order
Conclusion
It is paramount for the assembly
language programmer to understand
the memory layout precisely
We have seen the basics for creating an
assembly language program,
assembling it with NASM, linking it with
a C driver, and running it
Time for you to start playing around
with the sample programs
We’re now ready to learn how to write
more real programs
Programming Example
Ask user to enter a character and then,
display that character on the screen
with a message ‘You entered this
CHAR’. Example is below: