This is just a list of some basic assembly instructions that are very important and are used often.
ADD
: Add the contents of one number to another
Syntax: ADD operand1,operand2
This adds operand2
to operand1
. The answer is stored in operand1
. Immediate data cannot be used as operand1
but can be used as operand2
.
SUB
: Subtract one number from another
Syntax: SUB operand1,operand2
This subtracts operand2
from operand1
. Immediate data cannot be used as operand1
but can be used as operand2
.
MUL
: Multiplies two unsigned integers (always positive)
IMUL
: Multiplies two signed integers (either positive or negitive)
Syntax: MUL register or variable
IMUL register or variable
This multiples AL or AX by the register or variable given. AL is multiplied if a byte sized operand is given and the result is stored in AX. If the operand is word sized AX is multiplied and the result is placed in DX:AX
.
On a 386, 486 or Pentium the EAX register can be used and the answer is stored in EDX:EAX
.
DIV
: Divides two unsigned integers (always positive)
IDIV
: Divides two signed integers (either positive or negitive)
Syntax: DIV register or variable
IDIV register or variable
This works in the same way as MUL and IMUL by dividing the number in AX by the register or variable given. The answer is stored in two places. AL stores the answer and the remainder is in AH. If the operand is a 16 bit register than the number in DX:AX is divided by the operand and the answer is stored in AX and remainder in DX.
In assembly a procedure is the equivalent to a function in C or Pascal. A procedure provides a easy way to encapsulate some calculation which can then be used without worrying how it works. With procedures that are properly designed you can ignore how a job is done.
This is how a procedure is defined:
AProcedure PROC . . ; some code to do something . ret ; if this is not here then your computer will crash AProcedure ENDP
It is equally easy to run a procedure all you need to do is this:
call AProcedure
This next program is an example of how to use a procedure. It is like the first example we looked at, all it does is print „Hello World!“ on the screen.
; This is a simple program to demonstrate procedures. It should ; print Hello World! on the screen when ran. .model tiny .code org 100h Start: call Display_Hi ; Call the procedure mov ax,4C00h ; return to DOS int 21h Display_Hi PROC mov dx,OFFSET HI mov ah,9 int 21h ret Display_Hi ENDP HI DB "Hello World!" ; define a message end Start
Procedures wouldn't be so useful unless you could pass parameters to modify or use inside the procedure. There are three ways of doing this and I will cover all three methods: in registers, in memory and in the stack.
There are three example programs which all accomplish the same task. They print a square block (ASCII value 254) in a specified place.
The sizes of the files when compiled are: 38 for register, 69 for memory and 52 for stack (in bytes).
The advantages of this is that it is easy to do and is fast. All you have to do is to is move the parameters into registers before calling the procedure.
; this a procedure to print a block on the screen using ; registers to pass parameters (cursor position of where to ; print it and colour). .model tiny .code org 100h Start: mov dh,4 ; row to print character on mov dl,5 ; column to print character on mov al,254 ; ascii value of block to display mov bl,4 ; colour to display character call PrintChar ; print our character mov ax,4C00h ; terminate program int 21h PrintChar PROC NEAR push bx ; save registers to be destroyed push cx xor bh,bh ; clear bh - video page 0 mov ah,2 ; function 2 - move cursor int 10h ; row and col are already in dx pop bx ; restore bx xor bh,bh ; display page - 0 mov ah,9 ; function 09h write char & attrib mov cx,1 ; display it once int 10h ; call bios service pop cx ; restore registers ret ; return to where it was called PrintChar ENDP end Start
The advantages of this method is that it is easy to do but it makes your program larger and can be slower.
To pass parameters through memory all you need to do is copy them to a variable which is stored in memory. You can use a variable in the same way that you can use a register but commands with registers are a lot faster.
; this a procedure to print a block on the screen using memory ; to pass parameters (cursor position of where to print it and ; colour). .model tiny .code org 100h Start: mov Row,4 ; row to print character mov Col,5 ; column to print character on mov Char,254 ; ascii value of block to display mov Colour,4 ; colour to display character call PrintChar ; print our character mov ax,4C00h ; terminate program int 21h PrintChar PROC NEAR push ax cx bx ; save registers to be destroyed xor bh,bh ; clear bh - video page 0 mov ah,2 ; function 2 - move cursor mov dh,Row mov dl,Col int 10h ; call Bios service mov al,Char mov bl,Colour xor bh,bh ; display page - 0 mov ah,9 ; function 09h write char & attrib mov cx,1 ; display it once int 10h ; call bios service pop bx cx ax ; restore registers ret ; return to where it was called PrintChar ENDP ; variables to store data Row db ? Col db ? Colour db ? Char db ? end Start
This is the most powerful and flexible method of passing parameters the problem is that it is more complicated.
; this a procedure to print a block on the screen using the ; stack to pass parameters (cursor position of where to print it ; and colour). .model tiny .code org 100h Start: mov dh,4 ; row to print string on mov dl,5 ; column to print string on mov al,254 ; ascii value of block to display mov bl,4 ; colour to display character push dx ax bx ; put parameters onto the stack call PrintString ; print our string pop bx ax dx ;restore registers mov ax,4C00h ;terminate program int 21h PrintString PROC NEAR push bp ; save bp mov bp,sp ; put sp into bp push cx ; save registers to be destroyed xor bh,bh ; clear bh - video page 0 mov ah,2 ; function 2 - move cursor mov dx,[bp+8] ; restore dx int 10h ; call bios service mov ax,[bp+6] ; character mov bx,[bp+4] ; attribute xor bh,bh ; display page - 0 mov ah,9 ; function 09h write char & attrib mov cx,1 ; display it once int 10h ; call bios service pop cx ; restore registers pop bp ret ; return to where it was called PrintString ENDP end Start
This shows the stack for a procedure with two parameters:
To get a parameter from the stack all you need to do is work out where it is. The last parameter is at BP+2 and then the next and BP+4.
We have been using the .MODEL
directive to specify what type of memory model we use, but what does this mean?
Syntax: .MODEL MemoryModel
Where MemoryModel can be SMALL, COMPACT, MEDIUM, LARGE, HUGE, TINY
or FLAT
.
Tiny
- this means that there is only one segment for both code and data. This type of program can be a .COM file.Small
- this means that by default all code is place in one segment and all data declared in the data segment is also placed in one segment. This means that all procedures and variables are addressed as NEAR by pointing at offsets only. Compact
- this means that by default all elements of code are placed in one segment but each element of data can be placed in its own physical segment. This means that data elements are addressed by pointing at both at the segment and offset addresses. Code elements (procedures) are NEAR and variables are FAR. Medium
- this is the opposite to compact. Data elements are NEAR and procedures are FAR. Large
- this means that both procedures and variables are FAR. You have to point at both the segment and offset addresses.Flat
- this isn't used much as it is for 32 bit unsegmented memory space. For this you need a DOS extender. This is what you would have to use if you were writing a program to interface with a C/C++ program that used a DOS extender such as DOS4GW or PharLap.Note: All code examples given are for macros in Turbo Assembler.
Macros are very useful for doing something that is done often but for which a procedure can't be used. Macros are substituted when the program is compiled to the code which they contain.
This is the syntax for defining a macro:
Name_of_macro macro ; ; a sequence of instructions ; endm
These two examples are for macros that take away the boring job of pushing and popping certain registers:
SaveRegs macro push ax push bx push cx push dx endm
RestoreRegs macro pop dx pop cx pop bx pop ax endm
Notice that the registers are popped in the reverse order to they were pushed. To use a macro in you program you just use the name of the macro as an ordinary instruction:
SaveRegs
; some other instructions
RestoreRegs
This example shows how you can use a macro to save typing in. This macro simply prints out a message to the screen.
OutMsg macro SomeText local PrintMe,SkipData jmp SkipData PrintMe db SomeText,'$' SkipData: push ax dx cs mov dx,OFFSET cs:PrintMe mov ah,9 int 21h pop cs dx ax endm
The only problems with macros is that if you overuse them it leads to you program getting bigger and bigger and that you have problems with multiple definition of labels and variables. The correct way to solve this problem is to use the LOCAL
directive for declaring names inside macros.
Syntax: LOCAL name
Where name
is the name of a local variable or label.
Another useful property of macros is that they can have parameters. The number of parameters is only restricted by the length of the line.
Syntax:
Name_of_Macro macro par1,par2,par3 ; ; commands go here ; endm
This is an example that adds the first and second parameters and puts the result in the third:
AddMacro macro num1,num2,result push ax ; save ax from being destroyed mov ax,num1 ; put num1 into ax add ax,num2 ; add num2 to it mov result,ax ; move answer into result pop ax ; restore ax endm