Assembly Language – Arithmetic Instructions

Before we continue with command line parsing, we will have a brief diversion covering how arithmetic instructions work. We have already seen the increment and decrement instructions, incq and decq. These add one and subtract one from the value in a register. Now we will be covering more general arithmetic operations. In a previous post we saw how to use comparison instructions. Arithmetic instructions are really quite similar.

If we wish to add two quad-word values, we use the addq instruction. The syntax is:

addq X, Y

where X is the name of a register or a constant value and Y is the name of a register. So the instruction addq $17, %rax adds 17 to the value in register rax and stores the result in rax. To subtract we use the subq instruction which uses the exact same syntax.

There are two different multiplication instructions. The first, imulq, works just like the addq and subq instructions. This performs signed multiplication. However, as the result is stored in a single 64 bit register this instruction can quite easily lead to an overflow. Indeed, if we try to use constant values that are too large the assembly step will fail. For example the instruction

imulq $0x8000000, %rax 

will cause an error when you try and assemble. This is, roughly, because max positive value you can store in 32 bits is 7FFFFFFF. However, you can still move this value into a register and multiply that way.

There is another multiply syntax that allows us to multiply 64 bit numbers without overflow. This syntax uses two instruction names, mulq and imulq, but it takes a single register value. The instruction imulq performs signed multiplication and the instruction mulq performs unsigned multiplication. These instructions multiply the value in the supplied register by whatever value is in the rax register and stores the result across rdx and rax. The lower 64 bits are stored in rax and the upper 64 bits in rdx.

To perform division we use idivq and divq. As before, idivq is signed division, and divq is unsigned division. The division instructions take a single argument, the name of a register. With these instructions, the CPU takes the values in rax and rdx as a single value, rax is the lower 64 bits and rdx is the upper 64 bits. It divides this value by the value in the register supplied. The result of this division is then stored in rax and the remainder is stored in rdx.

There is also a unary negation operation negq, that negates the value in a register.

Many of these instructions will overflow. And, unlike in some higher level languages, our program will continue to execute happily with whatever values the registers now contain. To avoid this behaviour we use a special instruction: jo. This is the jump on overflow instruction. Whenever an arithmetic operation that causes an overflow occurs the CPU sets the overflow flag. The jo instruction jumps conditioned on this flag. If the flag is set, execution jumps to the address supplied.

There are also versions of the above arithmetic operations for non-quad words. However we aren’t particularly interested in them right now.

Leave a Reply

Your email address will not be published. Required fields are marked *