Multiplication & Division


In the "Hello World!" sample program we had used the instructions REPNZ and 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 .bss section, 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 IDIV instruction, which places the quotient of the division in the RAX register and the remainder in the RDX register. A very common error is to forget to initialize the RDX register, which we do here with instruction CQO which sign extends RAX to RDX . The negation of a number, i.e. multiplication by -1 , is done using the 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
$ gcc -o math.out math.o asm_io.o -lc
				

Since we are using C library functions like printf() , scanf() and putchar() , we need to link in the C library during link time, hence the -lc is used for that. This is a dynamic link, and we use gcc to do it. If we want to use just the linker ld we can link in the C library statically as given below:

$ ld -o math.out math.o asm_io.o -Bstatic -lc
				

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 in /usr/lib64/ directory as below:

$ ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib64/crt1.o  /usr/lib64/crti.o \
math.o asm_io.o  /usr/lib64/crtn.o -lc -o math.out 
				

It is advisable that the files should be linked in the correct order as shown above.




Tweet


Follow @_vicash_