Prev Intro Next   Home Email me

Tutorial 5 - Mathematical Operators.

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.

Increment and Decrement

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

Basic Arithmetic Operators

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]

Bit Shifting Operators

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

Logical Operators

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.

For a complete list of the operators and all possible legal parameters to them, consult the A86 Manual Chapter 6.

Sample Program

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


Prev Intro Next   Home Email me