There are many instructions we can use to do regular arithmetic, but I'll just teach you the basic ones for now. They are add, sub, mul, div, inc, dec, and neg. Guess what they do.
There's one thing that you need to know about all these instructions, though: the result is stored in the destination operand. So something like: add eax,ecx is the same as eax += ecx. Same with all the others.
MUL - Unsigned Multiplication
This instruction takes only one operand, and assumes AX/AX:DX/EAX:EDX/RAX:RDX as the destination operand. Yes, it clobbers [E/R]DX on 16-, 32-, and 64-bit multiplications. The reason is this: let's say you have 0xFF in AL and you want to multiply that by 0x80. Clearly if we restrict the result to AL it's going to overflow and give an inaccurate result. So 8-bit multiplication gives a 16-bit result. Naturally 16-bit multiplication would yield a 32-bit result, and so on. But why not just use EAX instead of AX for the low word and DX for the high word? Well, back in the old days, they only had 16-bit registers, so the designers were forced to use two registers. When the 80386--the first 32-bit Intel processor--came out, there were still a lot of 16-bit applications out there, so backwards compatibility was a must. Hence 16-bit multiplications on a 32-bit system still use AX:DX. The EAX:EDX and RAX:RDX issue is the same thing.
; al *= cl ;result stored in ax mul cl ;ax *= cx ;dx contains high word of result, ;ax contains low word mul cx ;eax *= ecx ;edx contains high dword, ax low dword mul ecx
DIV - Unsigned Division
div works exactly like mul, except it uses AH/DX/EDX/RDX to store the remainder (i.e. modulus) of the result of the division. So if I were to do this:
mov al,5 mov cl,2 div clthe result is al=2 and ah = 1. If I were to have assigned ax=5 and cx=2, and divided ax, then the result would be ax=2 and dx=1. Get it?
IMUL & IDIV - Signed Multiplication & Division
Exactly the same except the operands are treated as if they were signed integers.
A Note About Multiplication and Division
Multiplication and division are SLOW. Use a combination of shifting and adding if you can. For example, if you want to multiply AX by 256, don't use mul. Use shl ax,8.
ADD / SUB
These are relatively straightforward. add a,b is the same as a += b, and sub a,b is the same as a -= b. You can pretty much use whatever operands you want for these instructions (though remember we can't have two memory operands). If an addition overflows, the overflow flag (OF) is set in the flags register; these instructions don't use additional registers to catch the overflow value.
INC / DEC
These are used to increment and decrement a memory location or a register. If you specify a memory location, you have to specify the size as well, since there's no other way to figure out what size integer you mean:
inc DWORD PTR [eax] dec BYTE PTR [0x000138E0]
Warning: Due to a change in instruction encoding, you cannot use the inc and dec instructions in 64-bit code. You have to use add OPERAND, 1. I'm not going to get into why unless someone asks me, as it's somewhat complicated especially for someone who's at this stage in learning ASM. Just take my word for it for now.
NEG - Negate an Integer
Well...it negates an operand. Same as a = -a. Like the inc and dec instructions, you have to specify the size when you pass memory operands; unlike inc and dec, you can use this instruction in 64-bit mode.
I'm only going to teach you basic jumping for now; conditional jumping will come a bit later. This is the equivalent of the abhorred goto statement in higher-level languages. Unlike higher-level languages, however, it's a necessary component for creating if-elseif-else and switch blocks, and for, while, and do-while loops to a certain degree as well. The syntax is easy:
;jump to a specific label. this is the most common ;as the compiler handles figuring out the actual ;addresses for you. jmp <label> ;jump to a hard-coded address ;segment 0x1941 offset 0x000000F0 jmp 0x1941:0x000000F0 ;assume eax contains the address of a pointer. it can ;either be a short pointer (i.e. you can only jump ;within your current segment), or a long pointer, in ;which case you can jump to any offset in another ;segment. either way you need to specify. jmp near [eax] jmp far [eax]
Putting It All Together
Here's a useless program in C:
a = 5; b = 10; a += b; //a now equals 15 a = -a; //a now equals -15 b += a; //b now equals -5; b *= a; //b now equals 50. --a; //a now equals -16 goto some_place;and here's the equivalent ASM, with AX for a and BX for b.
mov ax, 5 mov bx, 10 add ax, bx neg ax add bx, ax ;uh-oh...we want to multiply bx but we have the values in ;the wrong registers for that...let's swap them. xchg ax,bx ;now a=bx and b=ax. let's multiply... mul bx ;swap them back so a=ax and b=bx xchg ax,bx ;continue on with life. dec ax ;alternatively, if running on a 64-bit processor, we would ;have do do sub ax,1. ;now jump somewhere... jmp ES:[EDX]
Well, that's all for today. Hope you've found this informative!
Next In This Series
Edited by dargueta, 18 November 2010 - 07:18 PM.
Fixed syntax error