In the Hello World sample
program we had used the instructions
SCASB to calculate the length of the string being printed at runtime. In this program
we use NASM’s
equ directive to calculate the length during assembly time as
opposed to at runtime. The variable
promptlen gives an example.
In the below program, we print the prompt asking for the user to enter an
integer which is then read into the variable input defined in the
which is the uninitialized memory section, using the function
read_int() defined earlier in the file asm_io.asm.
To multiply two signed integers, we use the
IMUL instruction, which is what we
have used here. To divide two signed integers, we use the
which places the quotient of the division in the
RAX register and the remainder
RDX register. A very common error is to forget to
RDX register, which we do here with instruction
CQO which sign extends
The negation of a number, i.e. multiplication by
-1 is done using
NEG instruction which calculates the two’s complement of the value in the register
operand for the instruction.
%include "asm_io.inc" %macro prologue 0 push rbp mov rbp,rsp push rbx push r12 push r13 push r14 push r15 %endmacro %macro epilogue 0 pop r15 pop r14 pop r13 pop r12 pop rbx leave ret %endmacro section .bss input resd 1 section .rodata prompt db "Enter a number: ",0 promptlen equ $-prompt cube db "The cube of the number is: ",0 square db "The square of the number is: ",0 cube25 db "The value of cube of the number times 25 is: ",0 quotient db "The quotient of cube/100 is: ",0 remainder db "The remainder of cube/100 is: ",0 negation db "The negation of the remainder is: ",0 section .text global main main: prologue ; print the prompt for the user mov rdx, promptlen mov rsi, dword prompt push 0x1 pop rdi mov rax,rdi syscall ; read the integer mov rdi, dword input call read_int ; calculate its square and print the output mov rdi, dword square call print_string mov rdi, [input] imul rdi, rdi mov rbx, rdi call print_int call print_nl ; calculate its cube and print the output mov rdi,dword cube call print_string mov rdi, [input] imul rdi,rdi imul rdi,[input] mov rbx, rdi call print_int call print_nl ; calculate the cube times 25 and print the output mov rdi, dword cube25 call print_string mov rdi, rbx imul rdi,0x19 call print_int call print_nl ; calculate cube/100 and print the output mov rdi, dword quotient call print_string ; initialize rax and sign extend it to rdx mov rax,rbx cqo mov rcx,0x64 ; this is the hex representation of 100 idiv rcx push rdx mov rdi, rax call print_int call print_nl ; calculate the remainder of cube/100 and print the output mov rdi, dword remainder call print_string pop rdx mov rdi,rdx push rdx call print_int call print_nl ; calculate the negation of the remainder and print the output mov rdi, dword negation call print_string pop rdi neg rdi call print_int call print_nl epilogue
To compile the above program which we shall call
math.asm, we execute the following steps:
$ yasm -f elf64 asm_io.asm $ yasm -f elf64 math.asm $ ld -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 \ /usr/lib/x86_64-linux-gnu/crt1.o /usr/lib/x86_64-linux-gnu/crti.o \ math.o asm_io.o /usr/lib/x86_64-linux-gnu/crtn.o -lc -o math.out
Since we are using C library functions like
need to link in the C library during link time, hence the
-lc is used for that.
This however will generate a very large executable because all the C library
functions will become a part of the executable. We do not want this, so we link
in dynamically using the C runtime object files provided by the operating system
/usr/lib64/ (depending on which GNU/Linux you are on) directory as below:
It is advisable that the files should be linked in the correct order as shown above.