Hello Fractalforums!
So far, our only way to communicate with the world is the exit code, which is annoying because the exit code is just an int.
We'll print a friendly "Hello Fractalforums!".
There is many way to write something on the standard output, we'll see that later, the main problem right now is to find a way to store the string.
There is, also, different way to store data. The goal for now is to use the "data" segment.
The data segment is where we put our global static data initialized by the programmer inside the exectuable itself.
Let's take another a new look at wikipedia
http://en.wikipedia.org/wiki/Data_segmentA data segment is a portion of virtual address space of a program, which contains the global variables and static variables that are initialized by the programmer. This size of this segment is determined by the values placed there by the programmer before the program was compiled or assembled, and does not change at run-time.
Please note the existence of :
- the BSS segment that can store uninitialized data
- the HEAP area, managed by malloc
- the stack, used by the asm insutrction PUSH, POP, CALL and RET.
Well... back to our data.
According to the YASM documentation :
DB, DW, DD, DQ, DT, DDQ, and DO are used to declare initialized data in the output file.
DT does not accept numeric constants as operands, and DDQ does not accept float constants as operands. Any size larger than DD does not accept strings as operands.
RESB, RESW, RESD, RESQ, REST, RESDQ, and RESO are designed to be used in the BSS section of a module: they declare uninitialised storage space.
to use in data segment :
db 0x55 ; just the byte 0x55
db 0x55,0x56,0x57 ; three bytes in succession
db 'a',0x55 ; character constants are OK
db 'hello',13,10,'$' ; so are string constants
dw 0x1234 ; 0x34 0x12
dw 'a' ; 0x41 0x00 (it's just a number)
dw 'ab' ; 0x41 0x42 (character constant)
dw 'abc' ; 0x41 0x42 0x43 0x00 (string)
dd 0x12345678 ; 0x78 0x56 0x34 0x12
dq 0x1122334455667788 ; 0x88 0x77 0x66 0x55 0x44 0x33 0x22 0x11
ddq 0x112233445566778899aabbccddeeff00
; 0x00 0xff 0xee 0xdd 0xcc 0xbb 0xaa 0x99
; 0x88 0x77 0x66 0x55 0x44 0x33 0x22 0x11
do 0x112233445566778899aabbccddeeff00 ; same as previous
dd 1.234567e20 ; floating-point constant
dq 1.234567e20 ; double-precision float
dt 1.234567e20 ; extended-precision float
to use in bss segment :
buffer: resb 64 ; reserve 64 bytes
wordvar: resw 1 ; reserve a word
realarray resq 10 ; array of ten reals
...
db = 8 bit = 1 byte (mnemonic : Data Byte)
dw = 16 bits = 2 bytes (Data Word)
dd = 32 bits = 4 bytes (Data Double-word)
dq = 64 bits = 8 bytes (Data Quad-word)
not sure about do and dt, never seen them before.
Please note that YASM recognize a 'string' and also add the null terminator at the end of the string. So we don't need to do something like
We also want to add a cariage return (\n), the ASCII hexadecimal code for \n is 0x0a, and we need a label to refered to this string (we'll call it "hello)
Let's store our string in the data segment.
section .data
hello: db 'Hello Fractalforums!',0x0a
Yes... very easy
We're not printing anything yet. we just stored the string we want to print.
As said earlier, there is many way to do that.
The obvious (if you coded a hello world in C) is to use printf, which is too overkill for us. We don't need any formating or fancy stuff.
We could use fputs which is simplier.
the problem with this solution is that they are not ASM instruction.
We
can call an external function from asm code and we'll learn to do that later.
We know how to do a ssytem call, and there is a system call to write stuff \o/
Back to
http://syscalls.kernelgrok.com/ row #4 :
sys_write
eax : 0x04
ebx : unsigned int fd
ecx : const char __user *buf
edx : size_t count
fd is the file descriptor number. the standard output is "1"
*buf is our string
count is the string size
hello.asm :
; Hello fractalforums !
section .data
hello: db 'Hello Fractalforums!',0x0a
section .text
global main
main:
; Call sys_write(1, "hello world\n", 21)
mov eax, 4
mov ebx, 1
mov ecx, hello
mov edx, 21
int 0x80
; Call sys_exit(0)
mov eax, 1
xor ebx, ebx ; same as mov ebx, 0 (but more efficient)
int 0x80
note that i exit with code 0, which is the usual code for a normal program termination without error.
ker2x@arthurus:~/asm/hello$ yasm -f elf64 hello.asm
ker2x@arthurus:~/asm/hello$ gcc -o hello hello.o
ker2x@arthurus:~/asm/hello$ ./hello
Hello Fractalforums!
ker2x@arthurus:~/asm/hello$
There is a way to let YASM count the size of the hello string.
Please take a look at the YASM documentation about "EQU" (a pseudo-instruction) and "$-label" (an expression)
hello.asm:
; Hello fractalforums !
section .data
hello: db 'Hello Fractalforums!',0x0a
msglen: equ $-hello
section .text
global main
main:
; Call sys_write(1, "hello world\n", 21)
mov eax, 4
mov ebx, 1
mov ecx, hello
mov edx, msglen
int 0x80
; Call sys_exit(0)
mov eax, 1
xor ebx, ebx ; same as mov ebx, 0 (but more efficient)
int 0x80
Pfew... question ?
PS @cKleinhuis : no, i didn't wrote anything on my blog. the main goal was to write an asm tutorial on fractalforums to create fractals using ASM.
It's not as big as you might think. Just 1 or 2 more post to learn to call external function, pass/retrieve parameters and the x64 ABI.
Then everything else will be about fractal code, optimisation and SSE* instruction.