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.