In assembler, more so than in high level programming languages, mathematical operations are essential. Even to perform the simplest things, like reading in or printing out a decimal number requires a surprisingly large number of mathematical operators.
This is not a long or difficult tutorial because each of the mathematical operators is contained within one instruction and all one has to do is learn the appropriate instruction mnemonics. The instructions I have divided into four categories, and I include a nice sample program at the end.
These are two of the most basic and useful instructions in the instruction set. The instruction "inc" adds one to the parameter, while the instruction "dec" subtracts one from the parameter. These operations are generally faster than using an add instruction to add one to the value.
inc ax ;add one to contents of ax register dec b[bx] ;subtract one from byte pointed to by bx register inc var_name ;increment the variable var_name
There are assembler instructions for all of four basic arithmetic operators: addition, subtraction, multiplication, and division. The important thing about these instructions is that the latter two, multiplication and division are slow to carry out in comparison to other operations, particularly compared to bit operations such as the left and right shift given below. For this reason, when multiplying by a constant value, it can be quicker to perform the operation using a sequence of shifts and adds rather than a multiplication.
Below are some example operations using adds and subtracts. Note, however, that a register or numeric literal must be one argument of the instruction - memory to memory adds are not allowed in one instruction.
add ax,bx ;add value in bx to value in ax (result in ax) sub bx,1 ;subtract 1 from the bx value add [bx],ax ;add value in ax to memory _word_ pointed to by bx add [bx],al ;add value in al to memory _byte_ pointed to by bx sub num,cx ;subtract cx value from variable "num" add cx,num ;add num value to cx value sub num,5 ;subtract 5 from variable num
The multiplication and division operators are much more limited in their parameters. Each instruction takes only one parameter and the other is always the AX (and/or AL and AH) register. Here are some very simple example instructions (Only covering 8 bit multiply and divides to avoid using multiple registers):
mul bl ;multiply bl * al giving result in ax. mul ch ;multiply ch * al giving result in ax. mul num ;multiply variable "num" by al giving ax (num is byte) mul 7 ;ax = 7 * al mul b[bx] ;ax = value-pointed-to-by-bx * al. ("b" specifies 8 bit (byte value) mul) div bl ;divide ax by value in bl. result in al, remainder in ah div ch ;al = ax / ch, ah = ax % ch (% = modulus operator in C = "mod" in Pascal) div num ;al = ax / num, ah = ax % num div 7 ;al = ax / 7, ah = ax % 7 div b[bx] ;al = ax / [bx], ah = ax % [bx]
The bit shifting operators are operators which take the binary representation of a value and move the bits either left or right. With a left shift, a zero is added onto the right of the number and the leftmost bit is removed. This effectively multiplies the number by two, and it is very fast. The right shift is performed in the oposite way, and divides the number by two. The shift operators take two parameters, the data to be shifted and the amount it is to be shifted by. The second parameter is either a literal number, or the cl register.
shl ax,2 ;multiply ax by 4 (2^2) shr bl,1 ;divide bl by 2 (2^1) shl ch,3 ;multiply ch by 8 (2^3) shr dx,cl ;divide dx by 2^value-in-cl shr dl,4 ;clear the lower 4 bits... shl dl,4 ;...of the dl register
As well as shifting bits left and right, the x86 instruction set also contains instructions for performing logical operations on the bits in numbers: and, or, not, and xor. Each of these except the not operator take two parameters (not takes one). The two parameter operators are used in the same way and accept the same parameter types as add and subtract (as in, one must have a literal or register as at least one parameter - no memory memory operations allowed). The not operator, takes one parameter of any non-literal type, memory or register.
This is a simple sample program which reads in two numbers and outputs their sum. Simple, one would think, but not in assembler, as the inputs and outputs have to be converted to and from character values into their numeric equivalents, i.e. we read in the characters '1' and '2' but we have to convert this to the number 12. This makes the program longer.
jmp start ;**************************** ;* Program to read in two * ;* numbers and add them * ;* and print out the result * ;**************************** number db 7 dup 0 ; string which will store input and output n1 dw 0 ; two input variables n2 dw 0 res dw 0 ; one output variable cr dw 13,10,"$" ; carriage return, line feed start: mov dx,offset number mov bx,dx mov b[bx],5 ; maximum 5 characters to read mov ah,0ah int 21h ; read in a string from keyboard mov bx,offset number +1 mov cx,00 mov cl,[bx] ; cl now contains number of digits mov ax,00 ; ax will contain the number input usedigit: inc bx ; get next digit shl ax,1 ; multiply by 10 using 2 shift ops and an add... mov dx,ax ; ... x*8 + x*2 = x*10 is the principle. shl ax,2 add ax,dx ; ax is now multiplied by 10 mov dx,00 mov dl,[bx] ; dl has new character sub dx,48 ; subtract 48 = ascii('0') to get number value add ax,dx ; add to ax loop usedigit ; loop statement= jmp if cx > 0 cmp n1,00 ; see if this is first or second number read jnz second mov n1,ax ; assign it to the first variable jmp start ; read in another number second: mov n2,ax ; or assign to second variable and continue print_cr: mov ah,09 mov dx,offset cr ; print out a carriage return character int 21h addnos: mov ax,n1 ; move numbers to registers ... mov bx,n2 add ax,bx ; ...and add mov res,ax ; store the result mov cx,00 setup_string: mov bx,offset number+7 ; put a $ at end of buffer. mov b[bx],'$' ; we will fill buffer from back forwards dec bx mov ax,res convert_decimal: mov dx,10 div dl ; divide by 10 add ah,48 ; convert remainder to character mov [bx],ah ; and move to buffer for output dec bx mov ah,00 ; quotient becomes new value cmp ax,00 ; if we haven't got all digits divide again jnz convert_decimal printout: mov dx,bx inc dx ; we decremented once too many, go forward one. mov ah,09 int 21h ; output the string close: mov ah,4ch mov al,00 int 21h ; end program
To continue click HERE