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

The next example demonstrates how to use these instructions.
Listing 11: STRINGS.ASM



.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

NewLine db CR,LF,"$"



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$"

Message  db "This is a message"

SearchString db "1293ijdkfjiu938uHello983fjkfjsi98934$"



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.$"



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:

SHL operand1,operand2

Note: The 8086 cannot have the value of opperand2 other than 1. The 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 	



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

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.