Reputation: 145
I am a complete newbie to this Language, and i am trying my best to learn it.
This is my first time dealing with low level language.
Here's my incomplete code:
;------------Block 1----------
.386
.model flat,stdcall
option casemap:none
;------------Block 2----------
include windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
;------------block 3----------
.data
first DW 1 ; increment this value
;------------Block 4----------
.data?
retvalue dd ?
;------------Block 5----------
.code
start:
mov ecx,10
mov eax, '1'
l1:
nop ;Add code here
loop l1
xor eax,eax
invoke ExitProcess,eax
end start
Its 64bit Architecture with Intel processor.
I referred to these articles, but none of them seems to fit in my architecture of code.
It's been irritating me for 4 hours.
Any help would be highly appreciated.
Upvotes: 0
Views: 2807
Reputation: 84521
Assembly can be a challenge to make friends with. Primarily because you are given very basic building blocks to work with, memory segments, addresses within the segments to use, processor registers to load values into and get results from, the basic system calls, and a way to invoke those system calls to operate on the values currently in the processor registers. Then it's up to you to do the rest. I'll try to give you a rough overview of the thought process you can use in approaching basic assembly calls below.
There are many web-articles that provides a small piece of the puzzle, but very few that provide a reasonably complete overview of the language. That's why I mentioned The Art of Assembly Language Programming in the comments, and I'll mention it here again. If you take time to go through the sections, you will have a good handle on how to use assembly.
In assembly, there are usually a couple ways to approach any problem. Printing 0-9
is no different. In my comment I explained the difference between the numeric values of 0-9
and the printable ASCII characters '0'-'9'
. In order to output values to the screen, you must write the ASCII values to stdout
(or file-descriptor number 1
, where stdin - 0
, stdout - 1
, stderr - 2
).
To write the values to stdout
you must make a proper sys_write
system call with the proper values in the proper processor registers. (you can find the proper system calls in unistd_32.h
(32-bit) or unistd_64.h
(64-bit), located in your distro dependent include directory, generally /usr/include/asm
or /usr/include/asm-x86
) You only need to worry about 2 for you needs, sys_write
(number 4
) and sys_exit
(number 1
).
What goes in what processor register? You know the syscall number will go in the first register eax
. Thankfully, you can generally figure the rest out from the C man page
for the command in question. For writing the man 2 write page
will help (you can use the man page for all system functions in a similar manner). Look at the write
function declaration. Your register values are generally the parameters required by the function (in order). e.g.
ssize_t write(int fd, const void *buf, size_t count);
| | |
register: ebx ecx edx
Now you know what goes in each register to write a character to stdout
(the file-descriptor number), to execute the write, you generate a kernel interrupt to do it. (for x86, that is int 80h
). Look at how you would write one character to stdout
:
mov eax, 4 ; linux (sys_write) in eax
mov ebx, 1 ; fileno in ebx (stdout)
mov ecx, achar ; move achar address to ecx
mov edx, 1 ; num chars to write in edx
int 0x80 ; kernel interrupt
(where achar
is the memory address for the character to write)
As discussed in the comment, you can either start with the digits 0-9
and add '0'
to the value to get the ASCII character value (or you can simply or
the value with '0'
to accomplish the same thing). You can also just start with the ASCII value '0'
, print it, increment its value by 1
(to get '1'
, etc...) and do it 10 times in total. (so a loop comes to mind)
I'll let you read further about loops in assembly, but the basic scheme is to load your loop count (10
in your case) into ecx
and then jump (jmp
) (or loop
) to the beginning label for your loop (e.g. next:
or looplbl:
) and then decrement the value in ecx
each time through until you hit 0
.
When your looping is done, to finish up, you then load the exit value of your program into ebx
and sys_exit
into eax
and call the kernel interrupt to exit. Now there are a lot of additional sub-issue basics to this process. This is simply an overview of one way to approach a solution. You will simply have to read and investigate the remainder because it is far more than can be crammed into this post.
To help, work through the following example. It simply subtracts the starting ASCII value '0'
from the ending ASCII value '9'
(then adds 1
for a total of 10-characters and uses that value for your loop count). It then loops 10-times starting by printing '0'
and adding 1
to the previous value printed with each pass through the loop until all '0'-'9'
have been printed. It then prints a newline
so the numbers are not crammed on the same line with your prompt and exits:
section .data
achar db '0'
nwln db 0xa
section .text
global _start ; must be declared for using gcc
_start: ; tell linker entry point
xor eax, eax ; zero eax register
mov al, byte '9' ; move byte '9` (57) to al
sub al, byte '0' ; subtract byte '0' (48) from al
inc al ; add 1 to al (for total chars)
xor ecx, ecx ; zero exc
mov cx, ax ; move result to cx (loop count)
next:
push ecx ; save value of ecx on stack
mov eax, 4 ; linux (sys_write) in eax
mov ebx, 1 ; fileno in ebx (stdout)
mov ecx, achar ; move achar address to ecx
mov edx, 1 ; num chars to write in edx
int 0x80 ; kernel interrupt
pop ecx ; restore loop count
mov dx, [achar] ; move value of achar to dx
inc byte [achar] ; increment value of achar (next char)
loop next ; jump to next:
mov eax, 4 ; linux (sys_write) in eax
mov ebx, 1 ; fileno in ebx (stdout)
mov ecx, nwln ; move nwln (newline) to ecx
mov edx, 1 ; num chars to write in edx
int 0x80 ; kernel interrupt
xor ebx, ebx ; zero ebx (for exit code 0)
mov eax, 1 ; system call number (sys_exit)
int 0x80 ; kernel interrupt
(note: you can output all printable characters in the ASCII character set by substituting '~'
for '9'
in the code above)
Compile/Link
This is a compile/link example with the nasm assembler. It will be similar if you are using fasm, or yasm. I write all my object files to an obj
subdir and my binaries to a bin
subdir just to reduce clutter.
nasm -f elf -o obj/prn_digits_32.o prn_digits_32.asm
ld -m elf_i386 -o bin/prn_digits_32 obj/prn_digits_32.o
Output
$ ./bin/prn_digits_32
0123456789
I hope this helps. Let me know if you have questions. There are probably 20 more ways to do this, some I'm sure much better. But it is really just a matter of taking it one-step-at-a-time and paying attention to what each register and byte in memory is doing.
Upvotes: 2