=====================================================================
			  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.