Wednesday, October 19, 2005

Programming in Assembly Language

After a long break I have started programming in pure assembly language again. My recent assembly experiences are to use short inline assembly in C/C++ code or looking at disassembly code during debugging. I have developed a little program to find out if an integer is a prime number or not.

Platforms and necessary tools

The source code is written with AT&T syntax which is most common in *nix world and uses gas (GNU assembler) on Linux. Intel syntax is used for MASM, TASM and NASM and most common in DOS and Windows. Intel syntax can also be compiled with gas-2.9.1. Have a look at gas manual.

You can have look at differences and similarities in AT&T and Intel syntax.

Following is the complete source code of my tiny program in prime.s file:

# Assembly source code to find out whether a number is prime or not

# hash sign (#) is used for comments in the source code

#next section is for data-variables,consts etc.

.section .data

msg_not_prime:

.ascii "Not a prime number\n"

len =. - msg_not_prime

msg_prime:

.ascii "A prime number\n"

len_prime = . - msg_prime

.section .text

.equ ST_ARGC_IN_FUNC, 8

.equ ST_NUMBER_IN_FUNC, 16

.equ ST_NUMBER, 8

.globl _start #entry point - define something like C main()

_start:

#Load the number from command line args

call args_to_int

addl $4, %esp # Free the stack allocated for the adress of

# instruction after return

#push the value in the stack, call prime function

#and it's the parameter

pushl %eax

call prime # Call a function

addl $4, %esp

cmpl $1, %ebx # Check the value in ebx; 1 for prime

je .is_prime

movl $len, %edx

movl $msg_not_prime, %ecx

movl $1, %ebx

movl $4, %eax

int $0x80

jmp .exit

.is_prime:

movl $len_prime, %edx

movl $msg_prime, %ecx

movl $1, %ebx

movl $4, %eax

int $0x80

.exit:

movl $0, %ebx

movl $1, %eax

int $0x80

#function to copy integer arg to eax

.type args_to_int, @function

args_to_int:

pushl %ebp

movl %esp, %ebp

xorl %eax, %eax

xorl %ebx, %ebx

xorl %ecx, %ecx

# return address + saved ebp ; number of args

movl ST_ARGC_IN_FUNC(%ebp), %ebx

cmpl $1, %ebx #if number of args is 1

jle .exit_args

movl ST_NUMBER_IN_FUNC(%ebp), %ebx # first arg is prog name

movl $10, %edi

movb (%ebx), %cl

cmpb $'-', %cl

jne .digit

incb %dh

incl %ebx

.next_digit:

mov (%ebx), %cl

.digit:

subb $'0', %cl

jb .done

cmpb $9, %cl

ja .done

mull %edi

addl %ecx, %eax

incb %dl

incl %ebx

jmp .next_digit

.done:

orb %dh, %dh

je .exit_args

negl %eax

.exit_args:

movl %ebp, %esp

popl %ebp

ret

# This function will store 1 in ebx if the number is prime

# otherwise, will store 0 (zero)

.type prime, @function

prime:

pushl %ebp

movl %esp, %ebp

movl $1, %ebx

movl 8(%ebp), %eax

cmpl $3, %eax

jle .end_loop

movl $2, %ecx

.loop_start:

movl $1, %ebx

cmpl 8(%ebp), %ecx

je .end_loop

movl $0, %ebx

movl 8(%ebp), %eax

movl $0, %edx

divl %ecx

cmpl $0, %edx

je .end_loop

incl %ecx

jmp .loop_start

.end_loop:

movl %ebp, %esp

popl %ebp

ret

How to generate program file:

gas (GNU assembler) is used to compile the souce and ld is used to link the compiled object code to generated executable. Both of them reside in GNU Binutils.

  1. Copy the source to a file named prime.s
  2. Compile the source

as -o prime.o prime.s

  1. Link the object file

ld -o prime prime.o

  1. Run the executable

./prime 7

It should give the following output:

A Prime Number

References:

  1. For everything to Program in assembly under Linux and Unix: http://www.linuxassembly.org/
  2. Programming from the Ground Up: http://savannah.nongnu.org/projects/pgubook/
  3. Art of Assembly.