© Jo Van Hoey 2019
J. Van HoeyBeginning x64 Assembly Programminghttps://doi.org/10.1007/978-1-4842-5076-1_9

9. Integer Arithmetic

Jo Van Hoey1 
(1)
Hamme, Belgium
 

In this chapter, you’ll see a number of arithmetic instructions for integers. Floating-point arithmetic will be covered in a later chapter. Now is a good time to quickly review Chapter 2 on binary numbers.

Starting with Integer Arithmetic

Listing 9-1 shows the example code we will analyze.
; icalc.asm
extern printf
section .data
      number1    dq    128   ; the numbers to be used to
      number2    dq    19    ; show the arithmetic
      neg_num    dq    -12   ; to show sign extension
      fmt        db    "The numbers are %ld and %ld",10,0
      fmtint     db    "%s %ld",10,0
      sumi  db   "The sum is",0
      difi  db   "The difference is",0
      inci  db   "Number 1 Incremented:",0
      deci  db   "Number 1 Decremented:",0
      sali  db   "Number 1 Shift left 2 (x4):",0
      sari  db   "Number 1 Shift right 2 (/4):",0
      sariex db  "Number 1 Shift right 2 (/4) with "
            db   "sign extension:",0
      multi db   "The product is",0
      divi  db   "The integer quotient is",0
      remi  db   "The modulo is",0
section .bss
        resulti  resq  1
        modulo   resq  1
section .text
      global main
main:
      push  rbp
      mov   rbp,rsp
; displaying the numbers
            mov   rdi, fmt
            mov   rsi, [number1]
            mov   rdx, [number2]
            mov   rax, 0
            call printf
; adding------------------------------------------------------------
      mov   rax, [number1]
      add   rax, [number2]            ; add number2 to rax
      mov   [resulti], rax            ; move sum to result
      ; displaying the result
            mov   rdi, fmtint
            mov   rsi, sumi
            mov   rdx, [resulti]
            mov   rax, 0
            call printf
; substracting------------------------------------------------------
      mov   rax, [number1]
      sub   rax, [number2]            ; subtract number2 from rax
      mov   [resulti], rax
      ; displaying the result
            mov   rdi, fmtint
            mov   rsi, difi
            mov   rdx, [resulti]
            mov   rax, 0
            call printf
; incrementing------------------------------------------------------
      mov   rax, [number1]
      inc   rax               ; increment rax with 1
      mov   [resulti], rax
      ; displaying the result
            mov   rdi, fmtint
            mov   rsi, inci
            mov   rdx, [resulti]
            mov   rax, 0
            call printf
; decrementing------------------------------------------------------
      mov   rax, [number1]
      dec   rax               ; decrement rax with 1
        mov [resulti], rax
      ; displaying the result
            mov   rdi, fmtint
            mov   rsi, deci
            mov   rdx, [resulti]
            mov   rax, 0
            call printf
; shift arithmetic left---------------------------------------------
      mov   rax, [number1]
      sal   rax, 2                  ; multiply rax by 4
      mov   [resulti], rax
      ; displaying the result
            mov   rdi, fmtint
            mov   rsi, sali
            mov   rdx, [resulti]
            mov   rax, 0
            call printf
; shift arithmetic right--------------------------------------------
      mov   rax, [number1]
      sar   rax, 2                  ; divide rax by 4
      mov   [resulti], rax
      ; displaying the result
            mov   rdi, fmtint
            mov   rsi, sari
            mov   rdx, [resulti]
            mov   rax, 0
            call  printf
; shift arithmetic right with sign extension -----------------------
      mov   rax, [neg_num]
      sar   rax, 2                  ; divide rax by 4
      mov   [resulti], rax
      ; displaying the result
            mov   rdi, fmtint
            mov   rsi, sariex
            mov   rdx, [resulti]
            mov   rax, 0
            call  printf
; multiply----------------------------------------------------------
      mov         rax, [number1]
      imul  qword [number2]         ; multiply rax with number2
      mov         [resulti], rax
      ; displaying the result
            mov   rdi, fmtint
            mov   rsi, multi
            mov   rdx, [resulti]
            mov   rax, 0
            call  printf
; divide------------------------------------------------------------
      mov         rax, [number1]
      mov     rdx, 0                ; rdx needs to be 0 before idiv
      idiv  qword [number2]         ; divide rax by number2, modulo in rdx
      mov         [resulti], rax
      mov     [modulo], rdx   ; rdx to modulo
      ; displaying the result
        mov rdi, fmtint
            mov   rsi, divi
            mov   rdx, [resulti]
            mov   rax, 0
            call  printf
            mov   rdi, fmtint
            mov   rsi, remi
            mov   rdx, [modulo]
            mov   rax, 0
            call  printf
mov rsp,rbp
pop rbp
ret
Listing 9-1

icalc.asm

Figure 9-1 shows the output.
../images/483996_1_En_9_Chapter/483996_1_En_9_Fig1_HTML.jpg
Figure 9-1

Integer arithmetic

Examining Arithmetic Instructions

Many arithmetic instructions are available; we are going to show a selection of them, and the others are similar to what you’ll learn here. Before we investigate the arithmetic instructions, note that we use printf with more than two arguments, so we need an additional register: the first argument goes into rdi, the second into rsi, and the third into rdx. That is how printf expects us to provide the arguments in Linux. You’ll learn more about that later, when we talk about calling conventions.

Here are some arithmetic instructions:
  • The first instruction is add , which can be used to add signed or unsigned integers. The second operand (source) is added to the first operand (destination), and the result is placed in the first operand (destination). The destination operand can be a register or a memory location. The source can be an immediate value, a register, or a memory location. The source and destination cannot be a memory location in the same instruction. When the resulting sum is too large to fit in the destination, the CF flag is set for signed integers. For unsigned integers, the OF flag is then set. When the result is 0, the ZF flag is set to 1, and when the result is negative, the SF flag is set.

  • The subtraction with sub is similar to the add instruction.

  • To increment a register or value in a memory location with 1, use the inc instruction . Similarly, dec can be used to decrement a register or value in a memory location with 1.

  • The arithmetic shift instructions are a special breed. The shift left, sal , is in fact multiplying; if you shift left one position, you are multiplying by 2. Every bit is shifted one place to the left, and a 0 is added to the right. Take the binary number 1. Shift left one place, and you obtain binary 10 or 2 in decimal representation. Shift left one place again, and you have binary 100 or 4 in decimal representation. If you shift left two positions, you multiply by 4. What if you want to multiply by 6? You shift left two times and then the add two times the original source, in that order.

  • Shift right, sar , is similar to shift left, but it means dividing by 2. Every bit is shifted one place to the right, and an additional bit is added to the left. Here there is a complication, however: if the original value was negative, the leftmost bit would be 1; if the shift instruction added a 0 bit at the left, the value would become positive, and the result would be wrong. So, in the case of a negative value, a sar will add a 1 bit to the left, and in the case of a positive value, 0 bits will be added to the left. This is called sign extension. By the way, a quick way to see if a hexadecimal number is negative is to look at byte 7 (the leftmost byte, counting from byte 0, which is the rightmost byte). The number is negative if byte 7 starts with an 8, 9, A, B, C, D, E, or F. But you need to take into account all 8 bytes. For example, 0xd12 is still a positive number because the leftmost byte, which is not shown, is a 0.

  • There are also nonarithmetic shift instructions; they will be discussed in Chapter 16.

  • Next, we multiply integers. For multiplying unsigned integers, you can use mul for unsigned multiplication and imul for signed multiplication. We will use imul, signed multiplication, which offers more flexibility: imul can take one, two, or three operands. In our example, we use one operand; the operand following the imul instruction is multiplied with the value in rax. You may expect that the resulting product is stored in rax, but that is not entirely correct. Let’s illustrate with an example: you can verify that when you multiply, for example, a two-digit number with a three-digit number, the product has four or five digits. When you multiply a 48-bit digit with a 30-bit digit, you will obtain a 77-bit digit or a 78-bit digit, and that value does not fit in a 64-bit register. To cope with this, the instruction imul will store the lower 64 bits of the resulting product in rax and the upper 64 bits in rdx. And this can be very deceptive!

    Let’s experiment a little bit: go back to the source code in SASM. Modify number1 so that it contains 12345678901234567 and modify number2 so that it contains 100. The product will just fit in rax; you can check that in SASM debug mode. Put a break before the imul instruction. Restart debugging mode and step through the program. The result of the multiplication will be 1234567890123456700, as you can see in rax after the imul instruction is executed. Now modify number2 into 10000. Restart debugging. Look at rax after executing imul. You see that the product is a large negative number! That is because the most significant bit in rax is a 1 and SASM concludes that this must be a negative number. Also, printf thinks that rax contains a negative number because rax contains a 1 bit in the leftmost position, so it is assumed to be negative. So, be careful with printf!

    We will dig somewhat deeper: as soon as the imul instruction is executed, rax contains 0xb14e9f812f364970. In binary, this is 101100010100111010011111100000010010111100110110010010010 with a 1 in the most significant position and hence is negative.

    And rdx contains 0x6. That is 000000000000000000000000000000000000000000000000000000110 with a 0 in the most significant position and hence is positive.

    The actual product is 0x6b14e9f812f364970 and can be found by combining rdx and rax, in this order: rdx:rax. If you convert this hexadecimal number to decimal, you will find the product you expect: 123456789012345670000. See Figure 9-2.

    On the Internet you can find hexadecimal to decimal conversion apps; see https://www.rapidtables.com/convert/number/hex-to-decimal.html
    ../images/483996_1_En_9_Chapter/483996_1_En_9_Fig2_HTML.jpg
    Figure 9-2

    Content of rax and rdx

  • Let’s continue with integer division, idiv . This is in fact the reverse of multiplication (well, what did you expect?). Divide the dividend in rdx:rax by the divisor in the source operand and store the integer result in rax. The modulo can be found in rdx. It’s important and easy to forget: make sure to set rdx to zero every time before you use idiv or the resulting quotient may be wrong.

64-bit integer multiplication and division have some subtleties for which you can find more details in the Intel manuals. Here we just gave an overview that serves as a general introduction to integer arithmetic. In the Intel manuals, not only will you find more details about the instructions, but you will find a large number of other arithmetic instructions that can be used in specific situations.

Summary

In this chapter, you learned the following:
  • How to do integer arithmetic.

  • How to do arithmetic shift left and shift right.

  • Multiplication uses rax and rdx for storing the product.

  • Division uses rax and rdx for the dividend.

  • Be careful when using printf when printing values.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.40.189