=====================================================================

...  GAVIN'S GUIDE TO 80x86 ASSEMBLY

=====================================================================

.Copyright (c) Gavin Estey, 1995. All rights reserved.


This was originally written for the Game Developer's Magazine and 

after getting lots of positive feedback I added and expanded it. I 

have spent a lot of time working on it and I would appreciate 

hearing from you if you like it.


If you want to contact me then email me at:


    gavin@senator.demon.co.uk or on CompuServe 100767,1325    

  


RESOURCES THAT WOULD BE USEFUL

---------------------------------------------------------------------


There are several resources that you may find useful:


List of Instructions and timings:


If you have TASM then the "Borland Turbo Assembler Quick Reference" 

has a list of instructions and timings up to 486. The "Intel Pentium 

Family User's Manual: Volume 3" is equally useful.


List of Interrupts:


There are several books that have this information but the most up to

date is Ralf Brown's Interrupt list available freely in four parts at

ftp://x2ftp.oulu.fi/pub/msdos/programming/docs/interXX[a-d].zip where

XX is the version.


A book that covers both these topics and is a useful assembly

reference is "The Revolutionary guide to Assembly Language",

ISBN 1-874416-12-5 publised by WROX Press.

  

OVERVIEW OF THE 80x86 FAMILY 

---------------------------------------------------------------------


The 80x86 family was first started in 1981 with the 8086 and the 

newest member is the Pentium which was released thirteen years later 

in 1994. They are all backwards compatible with each other but each 

new generation has added features and more speed than the previous 

chip. Today there are very few computers in use that have the 8088 

and 8086 chips in them as they are very outdated and slow. There are 

a few 286's but their numbers are declining as today's software 

becomes more and more demanding. Even the 386, Intel's first 32-bit 

CPU, is now declining and it seems that the 486 is now the entry 

level system. 


Representation of numbers in binary

---------------------------------------------------------------------


Before we begin to understand how to program in assembly it is best 

to try to understand how numbers are represented in computers. 

Numbers are stored in binary, base two. There are several terms which 

are used to describe different size numbers and I will describe 

what these mean. 


1 BIT:          0


One bit is the simplest piece of data that exists.  Its either a one 

or a zero. 


1 NIBBLE:       0000

4 BITS


The nibble is four bits or half a byte. Note that it has a maximum 

value of 15 (1111 = 15). This is the basis for the hexadecimal (base 

16) number system which is used as it is far easier to understand. 

Hexadecimal numbers go from 1 to F and are followed by a h to 

state that the are in hex. i.e. Fh = 15 decimal. Hexadecimal numbers 

that begin with a letter are prefixed with a 0 (zero).


1 BYTE          00000000

2 NIBBLES

8 BITS


A byte is 8 bits or 2 nibbles. A byte has a maximum value of FFh 

(255 decimal). Because a byte is 2 nibbles the hexadecimal 

representation is two hex digits in a row i.e. 3Dh. The byte is also 

that size of the 8-bit registers which we will be covering later.


1  WORD         0000000000000000

2  BYTES

4  NIBBLES 

16 BITS


A word is two bytes that are stuck together.  A word has a maximum 

value of FFFFh (65,536).  Since a word is four nibbles, it is 

represented by four hex digits.  This is the size of the 16-bit 

registers.


Registers

---------------------------------------------------------------------


Registers are a place in the CPU where a number can be stored and 

manipulated. There are three sizes of registers: 8-bit, 16-bit and 

on 386 and above 32-bit. There are four different types of 

registers; general purpose registers, segment registers, index 

registers and stack registers. Firstly here are descriptions of the 

main registers. Stack registers and segment registers will be 

covered later. 


General Purpose Registers

---------------------------------------------------------------------


These are 16-bit registers. There are four general purpose registers;

AX, BX, CX and DX. They are split up into 8-bit registers. AX is split

up into AH which contains the high byte and AL which contains the low

byte. On 386's and above there are also 32-bit registers, these have

the same names as the 16-bit registers but with an 'E' in front i.e.

EAX. You can use AL, AH, AX and EAX separatly and treat them as

separate registers for some tasks.


If AX contained 24689 decimal:

                        

                   AH        AL   

                01100000  01110001


AH would be 96 and AL would be 113. If you added one to AL it would

be 114 and AH would be unchanged. 


SI, DI, SP and BP can also be used as general purpose registers but

have more specific uses. They are not split into two halves.


Index Registers

---------------------------------------------------------------------


These are sometimes called pointer registers and they are 16-bit 

registers. They are mainly used for string instructions. There are 

three index registers SI (source index), DI (destination index) and 

IP (instruction pointer). On 386's and above there are also 32-bit 

index registers: EDI and ESI. You can also use BX to index strings.

IP is a index register but it can't be manipulated directly as it

stores the address of the next instruction. 


Stack registers

---------------------------------------------------------------------


BP and SP are stack registers and are used when dealing with the 

stack. They will be covered when we talk about the stack later on.


Segments and offsets

---------------------------------------------------------------------


The original designers of the 8088 decided that nobody will ever 

need to use more that one megabyte of memory so they built the chip 

so it couldn't access above that. The problem is to access a whole 

megabyte 20 bits are needed. Registers only have 16 bits and they 

didn't want to use two because that would be 32 bits and they 

thought that this would be too much for anyone. They came up with 

what they thought was a clever way to solve this problem: segments 

and offsets. This is a way to do the addressing with two registers 

but not 32 bits.


OFFSET  = SEGMENT * 16

SEGMENT = OFFSET / 16 (the lower 4 bits are lost)


One register contains the segment and another register contains the 

offset. If you put the two registers together you get a 20-bit 

address.


SEGMENT         0010010000010000----

OFFSET          ----0100100000100010

20-bit Address  00101000100100100010

..====== DS ======                                              

..     ====== SI ======

..

Notice that DS and SI overlap. This is how DS:SI is used to make a 

20 bit address. The segment is in DS and the offset is in SI. The 

standard notation for a Segment/Offset pair is: SEGMENT:OFFSET


Segment registers are: CS, DS, ES, SS. On the 386+ there are also FS 

and GS.


Offset registers  are: BX, DI, SI, BP, SP, IP.  In 386+ protected 

mode1, ANY general register (not a segment register) can be used as 

an  Offset register. (Except IP, which you can't manipulate 

directly).


If you  are now thinking that assembly must be really hard and you 

don't understand segments and offsets at all then don't worry. I 

didn't understand them at first but I struggled on and found out 

that they were not so hard to use in practice.


THE STACK

---------------------------------------------------------------------


As there are only six registers that are used for most operations, 

you're probably wondering how do you get around that. It's easy. 

There is something called a stack which is an area of memory which 

you can save and restore values to.


This is an area of memory that is like a stack of plates. The last 

one you put on is the first one that you take off. This is sometimes 

refered to as Last On First Off (LOFO) or First In First Out (LIFO).

If another piece of data is put on the stack it grows downwards.


As you can see the stack starts at a high address and grows 

downwards. You have to make sure that you don't put too much data in 

the stack or it will overflow.


AN INTRODUCTION TO ASSEMBLY INSTRUCTIONS

---------------------------------------------------------------------


There are a lot of instructions in assembly but there are only about 

twenty that you have to know and will use very often. Most 

instructions are made up of three characters and have an operand then 

a comma then another operand. For example to put a data into a 

register you use the MOV instruction. 


.mov ax,10                       ;put 10 into ax

.mov bx,20                       ;put 20 into bx

.mov cx,30                       ;put 30 into cx

.mov dx,40                       ;put 40 into dx


Notice that in assembler anything after a ; (semicolon) is ignored. 

This is very useful for commenting your code.


PUSH AND POP: TWO INSTRUCTIONS TO USE THE STACK

---------------------------------------------------------------------


You know about the stack but not how to put data in an out of it. 

There are two simple instructions that you need to know: push and 

pop. Here is the syntax for their use:


PUSH    Puts a piece of data onto the top of the stack


Syntax:

.push data


POP     Puts the piece of data from the top of the stack into a

        specified register or variable.


Syntax:

.pop register or variable

.

This example of code demonstrates how to use the push and pop 

instructions


.push cx                 ;put cx on the stack

.push ax                 ;put ax on the stack

.pop cx                  ;put value from stack into cx

.pop ax                  ;put value from stack into ax


Notice that the values of CX and AX will be exchanged. There is an

instruction to exchange two registers: XCHG, which would reduce the

previous fragment to "xchg ax,cx".


TYPES OF OPERAND 

---------------------------------------------------------------------


There are three types of operands in assembler: immediate, register 

and memory. Immediate is a number which will be known at compilation 

and will always be the same for example '20' or 'A'. A register 

operand is any general purpose or index register for example AX or 

SI. A memory operand is a variable which is stored in memory which 

will be covered later.


SOME INSTRUCTIONS THAT YOU WILL NEED TO KNOW

---------------------------------------------------------------------


This is a list of some important instructions that you need to know 

before you can understand or write assembly programs.


MOV     moves a value from one place to another.


Syntax:

.MOV destination, source


for example:

.mov ax,10               ;moves an immediate value into ax 

.mov bx,cx               ;moves value from cx into bx

.mov dx,Number           ;moves the value of Number into dx


INT     calls a DOS or BIOS function which are subroutines to do 

        things that we would rather not write a function for e.g. 

        change video mode, open a file etc.


Syntax:

.INT interrupt number


For example:

.int 21h                 ;Calls DOS service

.int 10h                 ;Calls the Video BIOS interrupt


Most interrupts have more than one function, this means that you 

have to pass a number to the function you want. This is usually put 

in AH. To print a message on the screen all you need to do is this:


.mov ah,9                ;subroutine number 9

.int 21h                 ;call the interrupt


But first you have to specify what to print. This function needs 

DS:DX to be a far pointer to where the string is. The string has to 

be terminated with a dollar sign ($). This would be easy if DS could 

be manipulated directly, to get round this we have to use AX.


This example shows how it works:

.

.mov dx,OFFSET Message   ;DX contains offset of message

.mov ax,SEG Message      ;AX contains segment of message

.mov ds,ax               ;DS:DX points to message

.mov ah,9                ;function 9 - display string

.int 21h                 ;call dos service


The words OFFSET and SEG tell the compiler that you want the 

segment or the offset of the message put in the register not the 

contents of the message. Now we know how to set up the code to 

display the message we need to declare the message. In the data 

segment1 we declare the message like this:


.Message DB "Hello World!$"


Notice that the string is terminated with an dollar sign. What does 

'DB' mean? DB is short for declare byte and the message is an array 

of bytes (an ASCII character takes up one byte). Data can be 

declared in a number of sizes: bytes (DB), words (DW) and double 

words (DD). You don't have to worry about double words at the moment 

as you need a 32-bit register, such as EAX, to fit them in.


Here are some examples of declaring data:

.

.Number1 db ?

.Number2 dw ?


The question mark (?) on the end means that the data isn't 

initialised i.e. it has no value in to start with. That could as 

easily be written as:


.Number1         db 0

.Number2         dw 1


This time Number1 is equal to 0 and Number2 is equal to 1 when you 

program loads. Your program will also be three bytes longer.  If you 

declare a variable as a word you cannot move the value of this 

variable into a 8-bit register and you can't declare a variable 

as a byte and move the value into a 16-bit register. For examples:


.mov al,Number1          ;ok

.mov ax,Number1          ;error


.mov bx,Number2          ;ok

.mov bl,Number2          ;error


All you have to remember is that you can only put bytes into 8-bit 

registers and words into 16-bit registers.


YOUR FIRST ASSEMBLY PROGRAM

---------------------------------------------------------------------


Now that you know some basic instructions and a little about data it 

is time that we looked at a full assembly program which can be 

compiled. 


Listing 1: 1STPROG.ASM


;This is a simple program which displays "Hello World!" on the 

;screen. 


.model small

.stack                  

.data           

     

Message db "Hello World!$"              ;message to be display


.code           

.mov dx,OFFSET Message           ;offset of Message is in DX 

.mov ax,SEG Message              ;segment of Message is in AX

        mov ds,ax                       ;DS:DX points to string 

        mov ah,9                        ;function 9 - display string                 

        int 21h                         ;call dos service                 


        mov ax,4c00h                    ;return to dos DOS 

        int 21h      

.

END start                               ;end here


COMPILATION INSTRUCTIONS

---------------------------------------------------------------------


These are some instructions to compile and link programs. If you 

have a compiler other than TASM or A86 then see your instruction 

manual.


Turbo Assembler:


.tasm file.asm

.tlink file [/t]


The /t switch makes a .COM file. This will only work if the memory 

model is declared as tiny in the source file.


A86:


.a86 file.asm


This will compile your program to a .COM file. It doesn't matter 

what the memory model is.


SOME INSTRUCTIONS THAT YOU NEED TO KNOW 

---------------------------------------------------------------------


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.


MAKING THINGS EASIER

---------------------------------------------------------------------


The way we entered the address of the message we wanted to print was 

a bit cumbersome. It took three lines and it isn't the easiest thing 

to remember


.mov dx,OFFSET MyMessage                                         

.mov ax,SEG MyMessage                                            

.mov ds,ax              


We can replace all this with just one line. This makes the code 

easier to read and it easier to remember.


.mov dx,OFFSET MyMessage


To make this work at the beginning of your code add these lines:

.

.mov ax,@data            

.mov ds,ax


Note: for A86 you need to change the first line to: 


.mov ax,data


This is because all the data in the segment has the same SEG value.

Putting this in DS saves us reloading this every time we want to

use another thing in the same segment.


KEYBOARD INPUT

---------------------------------------------------------------------


We are going to use interrupt 16h, function 00h to read the 

keyboard. This gets a key from the keyboard buffer.  If there isn't 

one, it waits until there is. It returns the SCAN code in AH and the 

ASCII translation in AL.


.xor ah,ah               ;function 00h - get character

.int 16h                 ;interrupt 16h


All we need to worry about for now is the ascii value which is in AL.


Note: XOR performs a Boolean Exclusive OR. It is commonly used to

erase a register or variable.


PRINTING A CHARACTER

---------------------------------------------------------------------


The problem is that we have the key that has been pressed in ah. How 

do we display it? We can't use function 9h because for that we need 

to have already defined the string which has to end with a dollar 

sign. This is what we do instead:


;after calling function 00h of interrupt 16h


.mov dl,al               ;move al (ascii code) into dl

.mov ah,02h              ;function 02h of interrupt 21h

.int 21h                 ;call interrupt 21h


If you want to save the value of AH then push AX before and pop it 

afterwards.


CONTROL FLOW

---------------------------------------------------------------------


In assembly there is a set of commands for control flow like in any 

other language. Firstly the most basic command:

.

.jmp label 


All this does it to move to the label specified and start executing 

the code there. For example:


.jmp ALabel

..

..

..     

.ALabel:



What do we do if we want to compare something? We have just got a 

key from the user but we want to do something with it. Lets print 

something out if it is equal to something else. How do we do that? 

It is easy. We use the jump on condition commands. Here is a list 

of them:


JUMP ON CONDITION INSTRUCTIONS:


JA      jumps if the first number was above the second number

JAE     same as above, but will also jump if they are equal

JB      jumps if the first number was below the second

JBE     same as above, but will also jump if they are equal

JNA     jumps if the first number was NOT above  (JBE)

JNAE    jumps if the first number was NOT above or the same as (JNB)

JNB     jumps if the first number was NOT below (JAE)

JNBE    jumps if the first number was NOT below or the same as (JA)     

JZ      jumps if the two numbers were equal

JE      same as JZ, just a different name

JNZ     jumps if the two numbers are NOT equal

JNE     same as above

JC      jump if carry flag is set

.

Note: the jump can only be a maximum of 127 bytes in either 

direction.


Syntax:

.CMP register or variable, value

.jxx destination      


An example of this is:


      cmp al,'Y'      ;compare the value in al with Y

      je ItsYES       ;if it is equal then jump to ItsYES


Every instruction takes up a certain amount of code space. You will 

get a warning if you try and jump over 127 bytes in either direction 

from the compiler. You can solve this by changing a sequence like this:


.cmp ax,10               ;is AX 10?

.je done                 ;yes, lets finish


to something like this:


.cmp ax,10               ;is AX 10?

.jne notdone             ;no it is not

.jmp done                ;we are now done

notdone:

.

This solves the problem but you may want to think about reordering 

your code or using procedures if this happens often.


Now we are going to look at a program which demonstrates input, 

output and control flow. 


Listing 2: PROGFLOW.ASM


;a program to demonstrate program flow and input/output

.model tiny

.code                        

org 100h

start:


.mov dx,OFFSET Message   ;display a message on the screen

.mov ah,9                ;using function 09h

.int 21h                 ;of interrupt 21h

      

.mov dx,OFFSET Prompt    ;display a message on the screen  

.mov ah,9                ;using function 09h

.int 21h                 ;of interrupt 21h

.jmp First_Time


Prompt_Again:

.mov dx,OFFSET Another   ;display a message on the screen  

.mov ah,9                ;using function 09h

.int 21h                 ;of interrupt 21h

      

First_Time:

    mov dx,OFFSET Again         ;display a message on the screen  

    mov ah,9                    ;using function 09h

    int 21h                     ;of interrupt 21h

      

    xor ah,ah                   ;function 00h of

    int 16h                     ;interrupt 16h gets a character      

    mov bl,al                   ;save to bl 

      

    mov dl,al                   ;move al to dl

    mov ah,02h                  ;function 02h - display character

    int 21h                     ;call DOS service         

      

    cmp bl,'Y'                  ;is al=Y?

    je Prompt_Again             ;if yes then display it again

    cmp bl,'y'                  ;is al=y?

    je Prompt_Again             ;if yes then display it again

      

TheEnd:

    mov dx,OFFSET GoodBye       ;print goodbye message

    mov ah,9                    ;using function 9

    int 21h                     ;of interrupt 21h

    mov ah,4Ch                  ;terminate program DOSusing

    int 21h               


.DATA                            

CR  equ 13      ;enter                                  

LF  equ 10      ;line-feed


Message DB "A Simple Input/Output Program$"     

Prompt  DB CR,LF,"Here is your first prompt.$"     

Again   DB CR,LF,"Do you want to be prompted again? $"   

Another DB CR,LF,"Here is another prompt!$"    

GoodBye DB CR,LF,"Goodbye then.$"

end start


INTRODUCTION TO PROCEDURES

---------------------------------------------------------------------


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:


PROC AProcedure

    .

    .           ;some code to do something       

    .

    RET         ;if this is not here then your computer will crash                    

ENDP AProcedure


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.


Listing 3: SIMPPROC.ASM


;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            ;interrupt 21h function 4Ch


Display_Hi PROC       

.mov dx,OFFSET HI   ;put offset of message into DX

.mov ah,9           ;function 9 - display string

.int 21h            ;call DOS service

.ret

Display_Hi ENDP       


HI      DB "Hello World!$" ;define a message


end Start



PROCEDURES THAT PASS PARAMETERS

---------------------------------------------------------------------


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 registers

---------------------------------------------------------------------


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. 


Listing 4: PROC1.ASM


;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 cx                 ;save registers to be destroyed


.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


PASSING THROUGH MEMORY 

---------------------------------------------------------------------


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.


Listing 5: PROC2.ASM    


;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


Row     db ?    ;variables to store data

Col     db ?

Colour  db ?

Char    db ?


end Start


Passing through Stack

---------------------------------------------------------------------


This is the most powerful and flexible method of passing parameters 

the problem is that it is more complicated.


Listing 6: PROC3.ASM


;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


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. 


WHAT ARE MEMORY MODELS?

---------------------------------------------------------------------


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.


MACROS (in Turbo Assembler)

---------------------------------------------------------------------


(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 use. 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

..pop ax

..pop bx

..pop cx

..pop dx

.endm

.

.RestoreRegs macro

..pop dx

..pop cx

..pop bx

..pop ax

.

.endm


Note 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 ds cs 

       pop ds

       mov dx,OFFSET cs:PrintMe

       mov ah,9

       int 21h

       pop ds dx ax

endm


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.


Macros with parameters

---------------------------------------------------------------------


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



FILES AND HOW TO USE THEM

---------------------------------------------------------------------


Files can be opened, read and written to. DOS has some ways of doing 

this which save us the trouble of writing our own routines. Yes, 

more interrupts. Here is a list of helpful functions of interrupt 

21h that deal with files.


Note: Bits are numbered from right to left. 


Function 3Dh: open file


Opens an existing file for reading, writing or appending on the 

specified drive and filename.


INPUT:

.AH = 3Dh

.AL = bits 0-2   Access mode 

..  000 = read only

..  001 = write only

..  010 = read/write

.     bits 4-6   Sharing mode (DOS 3+)

..  000 = compatibility mode

..  001 = deny all 

..  010 = deny write 

..  011 = deny read 

..  100 = deny none 

.DS:DX = segment:offset of ASCIIZ pathname


OUTPUT:

.CF = 0 function is succesful

.AX = handle

.CF = 1 error has occured

.AX = error code

..01h missing file sharing software

..02h file not found

..03h path not found or file does not exist

..04h no handle available

..05h access denied

..0Ch access mode not permitted


What does ASCIIZ mean? An ASCIIZ string like a ASCII string with a 

zero on the end instead of a dollar sign.


Important: Remember to save the file handle it is needed for later.


How to save the file handle


It is important to save the file handle because this is needed to do 

anything with the file. Well how is this done? There are two methods 

we could use: copy the file handle into another register and don't 

use that register or copy it to a variable in memory.


The disadvantages with the first method is that you will have to 

remember not to use the register you saved it in and it wastes a 

register that can be used for something more useful. We are going to 

use the second. This is how it is done:


.FileHandle DW 0     ;use this for saving the file handle

..

..

..

.mov FileHandle,ax   ;save the file handle


Function 3Eh: close file


Closes a file that has been opened.


INPUT:

.AX = 3Eh

.BX = file handle

 

OUTPUT:

.CF = 0 function is successful

.AX = destroyed

.CF = 1 function not successful

.AX = error code - 06h file not opened or unauthorised handle.


Important: Don't call this function with a zero handle because that 

will close the standard input (the keyboard) and you won't be able 

to enter anything.

       

Function 3Fh: read file/device


Reads bytes from a file or device to a buffer.


INPUT:

.AH = 3Fh

.BX = handle

.CX = number of bytes to be read

.DS:DX = segment:offset of a buffer


OUTPUT:

.CF = 0 function is successful

.AX = number of bytes read

.CF = 1 an error has occurred

..05h access denied

..06h illegal handle or file not opened


If CF = 0 and AX = 0 then the file pointer was already at the end of 

the file and no more can be read. If CF = 0 and AX is smaller than 

CX then only part was read because the end of the file was reached 

or an error occurred. 


This function can also be used to get input from the keyboard. Use a 

handle of 0, and it stops reading after the first carriage return, 

or once a specified number of characters have been read. This is a 

good and easy method to use to only let the user enter a certain 

amount of characters.


Listing 7: READFILE.ASM


;a program to demonstrate creating a file and then writing to 

;it


.model small

.stack

.code                


.mov ax,@data            ;base address of data segment

.mov ds,ax               ;put this in ds

.

.mov dx,OFFSET FileName  ;put address of filename in dx        

        mov al,2                ;access mode - read and write

.mov ah,3Dh              ;function 3Dh -open a file

.int 21h                 ;call DOS service

        mov Handle,ax           ;save file handle for later

.jc ErrorOpening         ;jump if carry flag set - error!

.

.mov dx,offset Buffer    ;address of buffer in dx

.mov bx,Handle           ;handle in bx

.mov cx,100              ;amount of bytes to be read

.mov ah,3Fh              ;function 3Fh - read from file

.int 21h                 ;call dos service

        jc ErrorReading         ;jump if carry flag set - error!  


.mov bx,Handle           ;put file handle in bx 

        mov ah,3Eh              ;function 3Eh - close a file

        int 21h                 ;call DOS service

.   

        mov cx,100              ;length of string

        mov si,OFFSET Buffer    ;DS:SI - address of string

.xor bh,bh               ;video page - 0

        mov ah,0Eh              ;function 0Eh - write character


NextChar:

.lodsb                   ;AL = next character in string

        int 10h                 ;call BIOS service

.loop NextChar


.mov ax,4C00h            ;terminate program                                                                        

        int 21h          


ErrorOpening:

      mov dx,offset OpenError   ;display an error                                                                            

      mov ah,09h                ;using function 09h

      int 21h                   ;call DOS service

      mov ax,4C01h              ;end program with an errorlevel =1   

      int 21h   

..

ErrorReading:

      mov dx,offset ReadError   ;display an error                                                                            

      mov ah,09h                ;using function 09h

      int 21h                   ;call DOS service

..

      mov ax,4C02h              ;end program with an errorlevel =2   

      int 21h

 

.data


Handle        DW ?                      ;to store file handle   

FileName      DB "C:\test.txt",0        ;file to be opened


OpenError     DB "An error has occured(opening)!$"

ReadError     DB "An error has occured(reading)!$"


Buffer        DB 100 dup (?)            ;buffer to store data


END



Function 3Ch: Create file


Creates a new empty file on a specified drive with a specified pathname.


INPUT:

.AH = 3Ch

.CX = file attribute

..bit 0 = 1 read-only file

..bit 1 = 1 hidden file

..bit 2 = 1 system file

..bit 3 = 1 volume (ignored)

..bit 4 = 1 reserved (0) - directory

..bit 5 = 1 archive bit

..bits 6-15 reserved (0)

    DS:DX = segment:offset of ASCIIZ pathname


OUTPUT:

    CF = 0 function is successful

    AX = handle

    CF = 1 an error has occurred

..03h path not found

..04h no available handle

..05h access denied


Important: If a file of the same name exists then it will be lost. 

Make sure that there is no file of the same name. This can be done 

with the function below.


Function 4Eh: find first matching file


Searches for the first file that matches the filename given.


INPUT:

    AH = 4Eh

    CX = file attribute mask (bits can be combined)

..bit 0 = 1 read only

..bit 1 = 1 hidden

..bit 2 = 1 system

..bit 3 = 1 volume label

..bit 4 = 1 directory

..bit 5 = 1 archive

..bit 6-15 reserved

    DS:DX = segment:offset of ASCIIZ pathname


OUTPUT:

    CF = 0 function is successful

    [DTA] Disk Transfer Area = FindFirst data block


The DTA block


Offset      Size in bytes     Meaning


0               21            Reserved

21              1             File attributes

22              2             Time last modified

24              2             Date last modified

26              4             Size of file (in bytes)

30              13            File name (ASCIIZ)


An example of checking if file exists:


File    DB "C:\file.txt",0      ;name of file that we want


.mov dx,OFFSET File      ;address of filename

.mov cx,3Fh              ;file mask 3Fh - any file

.mov ah,4Eh              ;function 4Eh - find first file

.int 21h                 ;call DOS service

.jc NoFile

.

.;print message saying file exists

NoFile:

.;continue with creating file


This is an example of creating a file and then writing to it.


Listing 8: CREATE.ASM


;This example program creates a file and then writes to it.


.model small

.stack

.code               


     mov ax,@data       ;base address of data segment

     mov ds,ax          ;put it in ds


     mov dx,offset StartMessage ;display the starting message 

     mov ah,09h                 ;using function 09h

     int 21h                    ;call dos service

 

     mov dx,offset FileName     ;put offset of filename in dx        

     xor cx,cx                  ;clear cx - make ordinary file

     mov ah,3Ch                 ;function 3Ch - create a file

     int 21h                    ;call DOS service

     jc CreateError             ;jump if there is an error

.

     mov dx,offset FileName     ;put offset of filename in dx

     mov al,2                   ;access mode -read and write

     mov ah,3Dh                 ;function 3Dh - open the file

     int 21h                    ;call dos service

     jc OpenError               ;jump if there is an error

     mov Handle,ax              ;save value of handle        


     mov dx,offset WriteMe      ;address of information to write 

     mov bx,Handle              ;file handle for file

     mov cx,38                  ;38 bytes to be written

     mov ah,40h                 ;function 40h - write to file

     int 21h                    ;call dos service

     jc WriteError              ;jump if there is an error

     cmp ax,cx                  ;was all the data written?

     jne WriteError             ;no it wasn't - error!


     mov bx,Handle              ;put file handle in bx 

     mov ah,3Eh                 ;function 3Eh - close a file

     int 21h                    ;call dos service

.

     mov dx,offset EndMessage   ;display the final message 

     mov ah,09h                 ;using function 09h

     int 21h                    ;call dos service

 

ReturnToDOS:

     mov ax,4C00h               ;terminate program 

     int 21h                    


WriteError:

     mov dx,offset WriteMessage ;display an error message 

     jmp EndError


OpenError:

     mov dx,offset OpenMessage  ;display an error message 

     jmp EndError


CreateError:

     mov dx,offset CreateMessage  ;display an error message 


EndError:

     mov ah,09h                         ;using function 09h

     int 21h                            ;call dos service

     mov ax,4C01h                       ;terminate program 

     int 21h                            


.data                            


CR      equ     13

LF      equ     10


StartMessage    DB "This program creates a file called NEW.TXT"                         DB ,"on the C drive.$"

EndMessage      DB CR,LF,"File create OK, look at file to"                      DB ,"be sure.$"


WriteMessage  DB "An error has occurred (WRITING)$"

OpenMessage   DB "An error has occurred (OPENING)$"

CreateMessage DB "An error has occurred (CREATING)$"


WriteMe         DB "HELLO, THIS IS A TEST, HAS IT WORKED?",0  


FileName        DB "C:\new.txt",0         ;name of file to open                              

Handle          DW ?                      ;to store file handle   


END


This is an example of how to delete a file after checking it exists:


Listing 9: DELFILE.ASM


;a demonstration of how to delete a file. The file new.txt on 

;c: is deleted (this file is created by create.exe). We also 

;check if the file exits before trying to delete it


.model small

.stack

.data


CR      equ 13

LF      equ 10


File    db "C:\new.txt",0 


Deleted db "Deleted file c:\new.txt$"

NoFile  db "c:\new.txt doesn't exits - exiting$"

ErrDel  db "Can't delete file - probably write protected$"


.code

.mov ax,@data            ;set up ds as the segment for data

.mov ds,ax               ;use ax as we can't do it directly


.mov dx,OFFSET File      ;address of filename to look for

        mov cx,3Fh              ;file mask 3Fh - any file

.mov ah,4Eh              ;function 4Eh - find first file

.int 21h                 ;call dos service

.jc FileDontExist


        mov dx,OFFSET File      ;DS:DX points to file to be killed                        

        mov ah,41h              ;function 41h - delete file

        int 21h                 ;call DOS service

        jc ErrorDeleting        ;jump if there was an error


.jmp EndOk


EndOk:

.mov dx,OFFSET Deleted   ;display message 

        jmp Endit


ErrorDeleting:

.mov dx,OFFSET ErrDel    ;display message 

.jmp Endit


FileDontExist:

.mov dx,OFFSET NoFile    ;display message 


EndIt:

.mov ah,9

.int 21h

.

.mov ax,4C00h            ;terminate program and exit to DOS

.int 21h                 ;call DOS service


end


USING THE FINDFIRST AND FINDNEXT FUNCTIONS

---------------------------------------------------------------------


Listing 10: DIRC.ASM


;this program demonstrates how to look for files. It prints

;out the names of all the files in the c:\drive and names of

;the sub-directories


.model small

.stack

.data


FileName db "c:\*.*",0          ;file name

DTA      db 128 dup(?)          ;buffer to store the DTA       

ErrorMsg db "An Error has occurred - exiting.$"


.code

     mov ax,@data               ;set up ds to be equal to the 

     mov ds,a                   ;data segment

     mov es,ax                  ;also es

...

     mov dx,OFFSET DTA          ;DS:DX points to DTA             

     mov ah,1AH                 ;function 1Ah - set DTA

     int 21h                    ;call DOS service


     mov cx,3Fh                 ;attribute mask - all files

     mov dx,OFFSET FileName     ;DS:DX points ASCIZ filename

     mov ah,4Eh                 ;function 4Eh - find first

     int 21h                    ;call DOS service

     jc error                   ;jump if carry flag is set


LoopCycle:

     mov dx,OFFSET FileName     ;DS:DX points to file name

     mov ah,4Fh                 ;function 4fh - find next

     int 21h                    ;call DOS service

     jc exit                    ;exit if carry flag is set

 

     mov cx,13                  ;length of filename

     mov si,OFFSET DTA+30       ;DS:SI points to filename in DTA

     xor bh,bh                  ;video page - 0

     mov ah,0Eh                 ;function 0Eh - write character


NextChar:

     lodsb                      ;AL = next character in string

     int 10h                    ;call BIOS service

     loop NextChar


     mov di,OFFSET DTA+30       ;ES:DI points to DTA

     mov cx,13                  ;length of filename

     xor al,al                  ;fill with zeros

     rep stosb                  ;erase DTA


     jmp LoopCycle              ;continue searching


error:                          

.mov dx,OFFSET ErrorMsg  ;display error message

.mov ah,9

.int 21h

exit:

.mov ax,4C00h            ;exit to DOS

.int 21h


end


STRING INSTRUCTIONS

---------------------------------------------------------------------


In assembly there are some very useful instructions for dealing with 

strings. Here is a list of the instructions and the syntax for using 

them:


MOV*       Move String: moves byte, word or double word at DS:SI 

           to ES:DI


Syntax:


.movsb           ;move byte

.movsw           ;move word

.movsd           ;move double word


CMPS*       Compare string: compares byte, word or double word at 

            DS:SI to ES:DI


Syntax:


.cmpsb           ;compare byte

.cmpsw           ;compare word

.cmpsd           ;compare double word


Note: This instruction is normally used with the REP prefix. 


SCAS* Search string: search for AL, AX, or EAX in string at ES:DI


Syntax:

.

.scasb           ;search for AL

.scasw           ;search for AX

.scasd           ;search for EAX


Note: This instruction is usually used with the REPZ or REPNZ prefix.


REP     Prefix for string instruction repeats instruction CX times


Syntax:


.rep StringInstruction


STOS*  Move byte, word or double word from AL, AX or EAX to ES:DI


Syntax:

.

.stosb           ;move AL into ES:DI

.stosw           ;move AX into ES:DI

.stosd           ;move EAX into ES:DI



LODS* Move byte, word or double word from DS:SI to AL, AX or EAX


Syntax:


.lodsb           ;move ES:DI into AL

.lodsw           ;move ES:DI into AX

.lodsd           ;move ES:DI into EAX

.

Listing 11: STRINGS.ASM


;This program demonstrates string examples


.model small

.stack

.code 


      mov ax,@data              ;ax points to of data segment

      mov ds,ax                 ;put it into ds

      mov es,ax                 ;put it in es too

.

      mov ah,9                  ;function 9 - display string

      mov dx,OFFSET Message1    ;ds:dx points to message

      int 21h                   ;call dos function


      cld                       ;clear direction flag


      mov si,OFFSET String1     ;make ds:si point to String1

      mov di,OFFSET String2     ;make es:di point to String2

      mov cx,18                 ;length of strings

      rep movsb                 ;copy string1 into string2

    

      mov ah,9                  ;function 9 - display string

      mov dx,OFFSET Message2    ;ds:dx points to message

      int 21h                   ;call dos function

.

      mov dx,OFFSET String1     ;display String1

      int 21h                   ;call DOS service

.

      mov dx,OFFSET Message3    ;ds:dx points to message

      int 21h                   ;call dos function

.

      mov dx,OFFSET String2     ;display String2

      int 21h                   ;call DOS service


      mov si,OFFSET Diff1       ;make ds:si point to Diff1  

      mov di,OFFSET Diff2       ;make es:di point to Diff2  

      mov cx,39                 ;length of strings

      repz cmpsb                ;compare strings

      jnz Not_Equal             ;jump if they are not the same

.

      mov ah,9                  ;function 9 - display string

      mov dx,OFFSET Message4    ;ds:dx points to message

      int 21h                   ;call dos function

.

      jmp Next_Operation


Not_Equal:

.mov ah,9                ;function 9 - display string

.mov dx,OFFSET Message5  ;ds:dx points to message

.int 21h                 ;call dos function

.

Next_Operation:

.mov di,OFFSET SearchString ;make es:di point to string

.mov cx,36               ;length of string

.mov al,'H'              ;character to search for

.repne scasb             ;find first match

.jnz Not_Found


.mov ah,9                ;function 9 - display string

.mov dx,OFFSET Message6  ;ds:dx points to message

.int 21h                 ;call dos function


.jmp Lodsb_Example


Not_Found:

.mov ah,9                ;function 9 - display string

.mov dx,OFFSET Message7  ;ds:dx points to message

.int 21h                 ;call dos function


Lodsb_Example:

.mov ah,9                ;function 9 - display string

.mov dx,OFFSET NewLine   ;ds:dx points to message

.int 21h                 ;call dos function


.mov cx,17               ;length of string

        mov si,OFFSET Message   ;DS:SI - address of string

        xor bh,bh               ;video page - 0

.mov ah,0Eh              ;function 0Eh - write character

NextChar:

      lodsb                     ;AL = next character in string

      int 10h                   ;call BIOS service

      loop NextChar


      mov ax,4C00h              ;return to DOS

      int 21h               


.data


CR equ 13

LF equ 10


String1 db "This is a string!$"

String2 db 18 dup(0)


Diff1   db "This string is nearly the same as Diff2$"

Diff2   db "This string is nearly the same as Diff1$"


Equal1  db "The strings are equal$"

Equal2  db "The strings are not equal$"


SearchString db "1293ijdkfjiu938uHello983fjkfjsi98934$"

 

Message db "This is a message"


Message1 db "Using String instructions example program.$"

Message2 db CR,LF,"String1 is now: $"

Message3 db CR,LF,"String2 is now: $"

Message4 db CR,LF,"Strings are equal!$"

Message5 db CR,LF,"Strings are not equal!$"

Message6 db CR,LF,"Character was found.$"

Message7 db CR,LF,"Character was not found.$"


NewLine  db CR,LF,"$"


end 


HOW TO FIND OUT THE DOS VERSION

---------------------------------------------------------------------


In many programs it is necessary to find out what the DOS version 

is. This could be because you are using a DOS function that needs 

the revision to be over a certain level.


Firstly this method simply finds out what the version is.


.mov     ah,30h          ;function 30h - get MS-DOS version

.int     21h             ;call DOS function


This function returns the major version number in AL and the minor 

version number in AH. For example if it was version 4.01, AL would 

be 4 and AH would be 01. The problem is that if on DOS 5 and higher 

SETVER can change the version that is returned. The way to get round 

this is to use this method.


.mov     ah,33h          ;function 33h - actual DOS version

.mov     al,06h          ;subfunction 06h

.int     21h             ;call interrupt 21h     


This will only work on DOS version 5 and above so you need to check 

using the former method. This will return the actual version of DOS 

even if SETVER has changed the version. This returns the major 

version in BL and the minor version in BH.


MULTIPLE PUSHES AND POPS

---------------------------------------------------------------------


You can push and pop more than one register on a line in TASM and 

A86. This makes your code easier to understand.


.push ax bx cx dx                ;save registers

.pop dx cx bx ax                 ;restore registers


When TASM (or A86) compiles these lines it translates it into 

separate pushes an pops. This way just saves you time typing and 

makes it easier to understand.


Note: To make these lines compile in A86 you need to put commas (,) 

in between the registers.


THE PUSHA/PUSHAD AND POPA/POPAD INSTRUCTIONS

---------------------------------------------------------------------


PUSHA is a useful instruction that pushes all general purpose 

registers onto the stack. It accomplishes the same as the following:


.temp = SP

.push ax

.push cx

.push dx

.push bx

.push temp

.push bp

.push si

.push di


The main advantage is that it is less typing, a smaller instruction 

and it is a lot faster. POPA does the reverse and pops these 

registers off the stack. PUSHAD and POPAD do the same but with the 

32-bit registers ESP, EAX, ECX, EDX, EBX, EBP, ESI and EDI.


USING SHIFTS FOR FASTER MULTIPLICATION AND DIVISION

---------------------------------------------------------------------


Using MUL's and DIV's is very slow and should be only used when 

speed is not needed. For faster multiplication and division you can 

shift numbers left or right one or more binary positions. Each shift 

is to a power of 2. This is the same as the << and >> operators in 

C. There are four different ways of shifting numbers either left or 

right one binary position. 


SHL Unsigned multiple by two

SHR Unsigned divide by two

SAR Signed divide by two

SAL same as SHL


The syntax for all four is the same.


Syntax:

SHL operand1,operand2


Note: The 8086 cannot have the value of opperand2 other than 1. 

286/386 cannot have operand2 higher than 31.


LOOPS

---------------------------------------------------------------------


Using Loop is a better way of making a loop then using JMP's. You 

place the amount of times you want it to loop in the CX register and 

every time it reaches the loop statement it decrements CX (CX-1) and 

then does a short jump to the label indicated. A short jump means 

that it can only 128 bytes before or 127 bytes after the LOOP 

instruction. 


Syntax:

.mov cx,100                      ;100 times to loop

Label:

..

..       

..

.Loop Label:             ;decrement CX and loop to Label


This is exactly the same as the following piece of code without 

using loop:


.mov cx,100                      ;100 times to loop

Label:

.dec cx                  ;CX = CX-1

.jnz Label                       ;continue until done


Which do you think is easier to understand? Using DEC/JNZ is faster 

on 486's and above and it is useful as you don't have to use CX.


This works because JNZ will jump if the zero flag has not been set. 

Setting CX to 0 will set this flag.


HOW TO USE A DEBUGGER

---------------------------------------------------------------------


This is a good time to use a debugger to find out what your program 

is actually doing. I am going to demonstrate how to use Turbo 

Debugger to check what the program is actually doing. First we need 

a program which we can look at.


Listing 12: DEBUG.ASM


;example program to demonstrate how to use a debugger


.model tiny        

.code

org 100h

start:


.push ax                 ;save value of ax

.push bx                 ;save value of bx

.push cx                 ;save value of cx

.

.mov ax,10               ;first parameter is 10                  

.mov bx,20               ;second parameter is 20

.mov cx,3                ;third parameter is 3

.

.Call ChangeNumbers      ;call procedure

.

.pop cx                  ;restore cx

.pop bx                  ;restore bx

.pop ax                  ;restore dx


.mov ax,4C00h            ;exit to dos

.int 21h 


ChangeNumbers PROC      

.add ax,bx               ;adds number in bx to ax

.mul cx                  ;multiply ax by cx

.mov dx,ax               ;return answer in dx

.ret

ChangeNumbers ENDP      


end start


Now compile it to a .COM file and then type:


.td name of file


Turbo Debugger then loads. You can see the instructions that make up 

your programs, for example the first few lines of this program is 

shown as:

.

.cs:0000 50              push   ax

.cs:0001 53              push   bx

.cs:0002 51              push   cx


Some useful keys for Turbo Debugger:


F5              Size Window

F7              Next Instruction

F9              Run

ALT F4          Step Backwards


.

The numbers that are moved into the registers are different that the 

ones that in the source code because they are represented in their 

hex form (base 16) as this is the easiest base to convert to and 

from binary and that it is easier to understand than binary also. 


At the left of this display there is a box showing the contents of 

the registers. At this time all the main registers are empty. Now 

press F7 this means that the first line of the program is run. As 

the first line pushed the AX register into the stack, you can see 

that the stack pointer (SP) has changed. Press F7 until the line 

which contains mov ax,000A is highlighted. Now press it again. Now 

if you look at the box which contains the contents of the registers 

you can see that AX contains A. Press it again and BX now contains 

14, press it again and CX contains 3. Now if you press F7 again you 

can see that AX now contains 1E which is A+14. Press it again and 

now AX contains 5A 1E multiplied by 3. Press F7 again and you will 

see that DX now also contains 5A. Press it three more times and you 

can see that CX, BX and AX are all set back to their original values 

of zero.


MORE OUTPUT IN TEXT MODES

---------------------------------------------------------------------


I am going to cover some more ways of outputting text in text modes. 

This first program is an example of how to move the cursor to 

display a string of text where you want it to go.


Listing 12: TEXT1.ASM


.model tiny 

.code

org 100h

start:

.mov dh,12               ;cursor col   

.mov dl,32               ;cursor row

.mov ah,02h              ;move cursor to the right place

.xor bh,bh               ;video page 0

.int 10h                 ;call bios service


.mov dx,OFFSET Text      ;DS:DX points to message

.mov ah,9                ;function 9 - display string

.int 21h                 ;call dos service


.mov ax,4C00h            ;exit to dos

.int 21h


Text    DB "This is some text$" 


This next example demonstrates how to write to the screen using the 

file function 40h of interrupt 21h.


Listing 13: TEXT2.ASM


end start

.model small

.stack

.code

.mov ax,@data            ;set up ds as the segment for data

.mov ds,ax               ;put this in ds

.

.mov ah,40h              ;function 40h - write file

.mov bx,1                ;handle = 1 (screen)

.mov cx,17               ;length of string

.mov dx,OFFSET Text      ;DS:DX points to string

.int 21h                 ;call DOS service

.

.mov ax,4C00h            ;terminate program

.int 21h


.data


Text    DB "This is some text"


end


The next program shows how to set up and call function 13h of 

interrupt 10h - write string. This has the advantages of being able 

to write a string anywhere on the screen in a specified colour but 

it is hard to set up.


Listing 14: TEXT3.ASM


.model small

.stack

.code

.mov ax,@data            ;set up ds as the segment for data

.mov es,ax               ;put this in es

.mov bp,OFFSET Text      ;ES:BP points to message


.mov ah,13h              ;function 13 - write string

.mov al,01h              ;attrib in bl,move cursor

.xor bh,bh               ;video page 0

.mov bl,5                ;attribute - magenta

.mov cx,17               ;length of string

.mov dh,5                ;row to put string

.mov dl,5                ;column to put string

.int 10h                 ;call BIOS service

.

.mov ax,4C00h            ;return to DOS

.int 21h


.data


Text    DB "This is some text"

end


The next program demonstrates how to write to the screen using rep 

stosw to put the writing in video memory. 


Listing 15: TEXT4.ASM


.model small

.stack

.code

.mov ax,0B800h           ;segment of video buffer

.mov es,ax               ;put this into es

.xor di,di               ;clear di, ES:DI points to video memory

.mov ah,4                ;attribute - red

.mov al,"G"              ;character to put there

.mov cx,4000             ;amount of times to put it there 

.cld                     ;direction - forwards

.rep stosw               ;output character at ES:[DI]

.

.mov ax,4C00h            ;return to DOS

.int 21h


end



The next program demonstrates how to write a string to video memory.


Listing 15: DIRECTWR.ASM


;write a string direct to video memory 


.model small

.stack

.code

.mov ax,@data

.mov ds,ax

.

.mov ax,0B800h           ;segment of video buffer

.mov es,ax               ;put this into es

.

.mov ah,3                ;attribute - cyan

.mov cx,17               ;length of string to print

.mov si,OFFSET Text      ;DX:SI points to string

.xor di,di


Wr_Char:

.lodsb                   ;put next character into al        

.mov es:[di],al          ;output character to video memory

.inc di                  ;move along to next column

.mov es:[di],ah          ;output attribute to video memory

.inc di

.loop Wr_Char            ;loop until done

.

.mov ax,4C00h            ;return to DOS

.int 21h

.

.data


Text    DB "This is some text"   


end


It is left as an exercise to the reader to modify it so that only 

one write is made to video memory. 


MODE 13h

---------------------------------------------------------------------


Mode 13h is only available on VGA, MCGA cards and above. The reason 

that I am talking about this card is that it is very easy to use for 

graphics because of how the memory is arranged.


FIRST CHECK THAT MODE 13h IS POSSIBLE

---------------------------------------------------------------------


It would be polite to tell the user if his/her computer cannot 

support mode 13h instead of just crashing his computer without 

warning. This is how it is done.


Listing 16: CHECK13.ASM


.model small

.stack

.data


NoSupport       db "Mode 13h is not supported on this computer."

..db ,"You need either a MCGA or VGA video"                               db ,"card/monitor.$"

Supported       db "Mode 13h is supported on this computer.$"


.code


.mov ax,@data            ;set up DS to point to data segment

.mov ds,ax               ;use ax 


.call Check_Mode_13h     ;check if mode 13h is possible

.jc Error                ;if cf=1 there is an error


.mov ah,9                ;function 9 - display string

.mov dx,OFFSET Supported ;DS:DX points to message

.int 21h                 ;call DOS service


.jmp To_DOS              ;exit to DOS


Error:

.mov ah,9                ;function 9 - display string

.mov dx,OFFSET NoSupport ;DS:DX points to message

.int 21h                 ;call DOS service


To_DOS:

.mov ax,4C00h            ;exit to DOS 

.int 21h


Check_Mode_13h PROC             ;Returns: CF = 1 Mode 13h not possible

.

.mov ax,1A00h            ;Request video info for VGA

.int 10h                 ;Get Display Combination Code

.cmp al,1Ah              ;Is VGA or MCGA present?

.je Mode_13h_OK          ;mode 13h is supported

.stc                     ;mode 13h isn't supported CF=1


Mode_13h_OK:

.ret                     


Check_Mode_13h ENDP


end 


Just use this to check if mode 13h is supported at the beginning of 

your program to make sure that you can go into that mode.


SETTING THE VIDEO MODE

---------------------------------------------------------------------


It is very simple to set the mode. This is how it is done.


.mov  ax,13h                     ;set mode 13h

.int  10h                        ;call BIOS service


Once we are in mode 13h and have finished what we are doing we need 

to we need to set it to the video mode that it was in previously. 

This is done in two stages. Firstly we need to save the video mode 

and then reset it to that mode.


VideoMode db ?

....

.mov ah,0Fh                      ;function 0Fh - get current mode

.int 10h                         ;Bios video service call

.mov VideoMode,al                ;save current mode


.;program code here


.mov al,VideoMode                ;set previous video mode

.xor ah,ah                       ;clear ah - set mode

.int 10h                         ;call bios service

.mov ax,4C00h                    ;exit to dos

.int 21h                         ;call dos function


Now that we can get into mode 13h lets do something. Firstly lets 

put some pixels on the screen. 


Function 0Ch: Write Graphics Pixel


This makes a colour dot on the screen at the specified graphics 

coordinates.


INPUT:

.AH = 0Ch

.AL = Color of the dot

.CX = Screen column (x coordinate)

.DX = Screen row (y coordinate)


OUTPUT:

.Nothing except pixel on screen.


Note: This function performs exclusive OR (XOR) with the new colour 

value and the current context of the pixel of bit 7 of AL is set.


This program demonstrates how to plot pixels. It should plot four 

red pixels into the middle of the screen.


Listing 17: PIXINT.ASM


;example of plotting pixels in mode 13 using bios services - 

;INT 10h


.model tiny

.code

org 100h


start:

.mov ax,13               ;mode = 13h                      

.int 10h                 ;call bios service


.mov ah,0Ch              ;function 0Ch

.mov al,4                ;color 4 - red

.mov cx,160              ;x position = 160

.mov dx,100              ;y position = 100

.int 10h                 ;call BIOS service

.

.inc dx                  ;plot pixel downwards

.int 10h                 ;call BIOS service

.inc cx                  ;plot pixel to right

.int 10h                 ;call BIOS service

.dec dx                  ;plot pixel up

.int 10h                 ;call BIOS service


.xor ax,ax               ;function 00h - get a key

.int 16h                 ;call BIOS service

.mov ax,3                ;mode = 3

.int 10h                 ;call BIOS service


.mov ax,4C00h            ;exit to DOS

.int 21h


end start


SOME OPTIMIZATIONS

---------------------------------------------------------------------


This method isn't too fast and we could make it a lot faster. How? 

By writing direct to video memory. This is done quite easily. 


The VGA segment is 0A000h. To work out where each pixel goes you use 

this simple formula to get the offset.


.Offset = X + ( Y x 320 )


All we do is to put a number at this location and there is now a 

pixel on the screen. The number is what colour it is.


There are two instructions that we can use to put a pixel on the 

screen, firstly we could use stosb to put the value in AL to ES:DI 

or we can use a new form of the MOV instruction like this:


.mov es:[di], color


Which should we use? When we are going to write pixels to the screen 

we need to do so as fast as it is possible. 


Instruction             Pentium     486     386     286     86


STOSB                   3           5       4       3       11

MOV AL to SEG:OFF       1           1       4       3       10


If you use the MOV method you may need to increment DI (which STOSB 

does).


[ put pixel instruction]


If we had a program which used sprites which need to be continuously 

draw, erased and then redraw you will have problems with flicker. To 

avoid this you can use a 'double buffer'. This is another part of 

memory which you write to and then copy all the information onto the 

screen. 

