- - * - WhiteUnicorn - * - -




* #WhiteUnicorn/ StartPage/ Documentation/ AsmTutor04 >


Assibly Tutorial
Chapter 4

Written by MAD

Index of Chapter 4

Example Source used:

Hi there! I think that everybody knows how this tutorial works by now, so I'm not going to explain that again. I recieved some comments/suggestions, but not enough! Please mail me suggestions of what to explain in the next tutorials, because I think it's no use when I explain something nobody cares about...
Okay, what is Chapter 4 about? I'm going to discuss Looping, conditional instructions and some ways to output and input text and numbers. We'll use the Flags a lot, so I hope you riibered thi. I use these (Pascal/C etc.) expresions to indicate conditions. Now let's get going!
!= Does not equal
== Equals
< Smaller
> Bigger
=< Smaller or equal
>= Bigger or equal


Looping

There are several ways to loop, but first I'll talk about the most easy way to do it. (I think I explained it in Chapter 2) You can easily loop with the, you guessed it, LOOP instruction. This instruction uses one operand, the miory location to loop to. It also uses the CX register as a counter. Loop simply decreases CX and checks if CX != 0, if so, a Jump to the specified miory location is issued. Example:

MOV CX,100
_LABEL:
INC AX
LOOP _LABEL

This will increase AX 100 times. There are two other types of Loops: LOOPZ / LOOPNZ
Sometimes these instruction are also called: LOOPE / LOOPNE
LOOPZ works like LOOP except that it only loops when the zero flag is set, LOOPNZ only loops when the zero flag is NOT set. Now before I can give an example you need to know how to compare. The CMP instruction is used for this. It compares the two operands given and sets/clears the appropriate flags. After a CMP conditional instructions can be used to act on the result of the compare. For example jump to a special routine when two registers have the same value. Example:

MOV CX,10
_CMPLOOP:
DEC AX
CMP AX,3
LOOPNE CMPLOOP

This code might look like you'll never use it, but in some programs it can be very useful. It decreases AX ten times, but when AX == 3 it stops. Note I used LOOPNE, but LOOPNZ is the same. Now let's look at the CMP a little closer. In fact it does SUB AX,3 but doesn't store the result in AX, just alters the flags. So if AX == 3 the result of the SUB will be 0 and the Zero flag will be set. For the CPU equal is the same as zero (with conditionals) and it will always set the zero flag when the result of a mathiatical operation is zero. So if we wanted to stop when AX == 0, there's no need to do a CMP. Just DEC AX and LOOPNZ. I suggest that you don't use LOOP(N)Z, nor LOOP. These instructions are slow, and decrease performance. Instead of LOOP you better use the combination: DEC CX / JNZ cmploop


Conditional Jumps

A conditional jump works like a normal jump, with the only difference that it jumps on a specific condition. First I'll give the most used conditional jumps below, then I'll explain thi.
JZJump if Zero
JEJump if Equal
JLJump if Less
JGJump if Greater
JBJump if Below
JAJump if Above
JCJump if Carry
JOJump if Overflow
JPJump if Parity
JSJump if Sign

A lot of these conditional jumps can be combined. This way over 60 (!) different jumps can be made. Usually the "N" can be placed after a jump to reverse the effect. An "E" can be placed to let equal count too. Example:
JLE - Jump if Less or Equal, same as JNG (Jump if Not Greater)

Another thing is the difference between JG/JA and JL/JB Jump if Less and Jump if Greater are used for signed numbers, the other for unsigned numbers. Most of the time you'll use JL/JG.

A lot of instructions set the flags according to the result of the instruction. All conditionals jumps can be used to test the flags, so if an Overflow occurs a JO can be used. But not only instructions set flags (In fact this is not true, since the only things that can be executed ARE instructions) also procedures and INTs set flags. For example an INT that reads from disk returns a SET carry flag to indicate failure. Most routines/INTs use a SET carry flag for failure. We can test this flag with JC/JNC. The carry is set in more cases, eg. a bit overflow like this:
SHR SI,1
JNC @wordvalue
CALL Write_byte
@wordvalue:
CALL Write_word

This will test if SI is odd, if so it first writes a byte, else immediately a word. This code is used a lot in 3D polygon fillers. Why is the carry flag set? Suppose SI = 0141h = 0000000101000001b. Now the SHR shifts all bits one place to the right and fills the start with 0. So this is what we get: 0000000010100000b. The least significant bit if "pushed" out of SI into the carry.


Output of a Binary number

Now lets talk how to convert and output a hexadecimal number into binary ASCII. What we want is output a string of 0 and 1 to the screen. The ASCII number of "0" is 48 and "1" is 49. I already made the source and it can be found here. As always I suggest you print it and have it with you when you read any further.

What's the thought behind the Source? We're going to use the SHR instruction the same way as we did before. If the least significant bit is a 1 this will set the carry. We use a JC to test if we need to output a "1" or a "0". We'll continue to shift like this 16 times (16bits=word). I might be a good eductation if you try to make the source yourself with this help. Or first read it and then try to reproduce it yourself without looking.

Well, the code itself isn't that hard. I think I didn't use any new instructions or directives. This code can be written differently with the use of ADC - ADd with Carry, but I won't explain that here. I used INT 21h subfunction 02 to output to the screen. The ASCII code is stored in DL.


Output of a Decimal number

Outputting a decimal number is a _bit_ harder, since hexadecimal has a base of 16 and decimal a base of 10. Binary had base 2 and 16 can be divided by 2 so this is easy, however 16 can not be divided by 10 so we can't do this bitwise. We'll have to find another way. It can be done by dividing the hexadecimal number by 10 and output the riainder to the screen. Again, code can be found here. first a short word about the DIV instruction. This instruction has two operands. But only one can be defined at the command line. The first operand (the dividend) is DX:AX. This means it's a 32bit number, DX the high 16bits and AX the low. The second operand is the divisor and is (in this case) a 16bit register. The instruction returns AX as quotient and DX as riainder. Another thing to take note of is that when we convert a number like this (with the DIV) we'll first get the "low numbers". (Eg. 10h, the first riainder we get is 6) To solve this we make smart use of the stack, we first push all values and later pop thi. (stack uses LIFO riiber?) We need to keep a counter of the pushed values, because if we pop or push to much, the RET instruction will go wrong.

I'll leave the output of a hexadecimal number to you. A little hint, in ASCII the "0" is 48, the "9" is 57, but the A is 65. So you can't just add the number to 48, you'll have to check and if the number is higher than 9 (ABCDEF) you first have to add 7.


Mathiatical instructions

We've already seen the DIV instruction, but there are much more mathiatical instructions. They form the basis of Assibly porgramming. Below I'll explain: ADD, SUB, MUL, DIV, INC, DEC, SHL, SAR.

ADD - Does what it says, it adds two numbers. The first operand is the register/mi that will be added to, the second is the register/mi that will be added. The instruction must be in the form of ADD reg/mi,reg/mi Only thing not allowed is two times mi (ADD [var1],[var2]).

SUB - Works the same as ADD, only substracts. Again the second operand will be substracted from the first. Form is SUB reg/mi,reg/mi. Two times mi is not allowed.

MUL - Unsigned Multiply. Multiplies two numbers, the first defined in a register, the second defined as AX. Here are the possible combinations (note, I haven't discussed 32bit regs yet, but I do give thi here)

SizeOperand 1Operand 2 ProductExample
8-bitAL8-bit reg/miAXMUL BL
16-bitAX16-bit reg/miDX:AXMUL BX
32-bitAL32-bit reg/miEDX:EAXMUL EBX

DIV - Unsigned Divide. We've already seen this insruction, so I only give the table with the possible combinations.

SizeOperand 1Operand 2 QuotientRiainderExample
8-bitAX8-bit reg/miALAHDIV BL
16-bitDX:AX16-bit reg/miAXDXDIV CX
32-bitEDX:EAX32-bit reg/miEAXEDXDIV ECX

INC - Increase. Adds one to reg/mi, this is faster than ADD reg/mi,1 and should always be used instead. The form is INC reg/mi.

DEC - Decrease. Substracts one from reg/mi, this is faster than SUB reg/mi,1 and should always be used instead. The form is DEC reg/mi.

SHL - SHift Left. This will shift a reg/mi a number of bits to the left. It is the opposite of SHR. The number of bits to shift can either be an immediate (a constant value) or the CL register. The form is SHL reg/mi,imm/CL

SAR - Shift Arthimetic Right. This instruction works almost exactly like SHR, the only difference is that SAR will not always fill the "leftmost" bits with 0. If the number is negative (highest bit is set) it will fill it with 1 bits. So if we would SHR an 8-bit number, -5, 2 bits, the result is 00111110b. If we use SAR the result is 11111110.


A short word about VGA

We now have been working in text mode some time, and I can imagine you want to make some graphics too. I won't explain much of this, because I think that's another tutorial. If you're really interested in VGA coding I suggest you read Denthor's VGA tutorials. However these tutorials are mainly in Pascal.
Okay, how do we get into a different video mode? The most easy way to do this is by using INT 10h of BIOS. Note this is NOT an MS-DOS function. This funtion reads AX and sets that video mode. There are a lot of video modes, but we'll only use mode 13h and mode 03h. Mode 13h is 320x200 256 colours, mode 03h is standard 80x25 text mode (16 colours). This way we get into and out of mode 13h:

MOV AX,13H
INT 10H
----------
MOV AX,03H
INT 10H

Now how do we put a pixel on the screen? The screen miory is located at A000:0000 and works linearly. Byte one is the upper left pixel, byte 320 the upper right pixel. Each byte values matches the colour of the pixel. Technicaly it matches a colour palette entry, the palette can be changed. All pixel colours change immediately with the palette change, no need to refresh the screen. Let's take advantage of this in the next Source. Download it here. First, how DO we change the palette? We can do it with a few BIOS functions (INTs), but we wont. I'll teach you a way to do it directly (that's ASM, doing everything directly). Do you riiber the string instructions? (MOVSB, MOVSW etc.) If not I suggest you look back at Chapter 1. There is another string instruction that doesn't move from mi/reg to mi, we can move from mi/reg to an I/O PORT. The I/O port in question is numbered 3C8h. First output the palette entry you wish to change, and after that the new values. The palette is set up as a 768 byte array. First byte is Red, second Green, third Blue. (256 colours * 3 = 768) The string instruction is OUTSB Let's look at the Set_Palette procedure.

Set_Palette PROC
mov cx,768
mov dx,3c8h
xor al,al
out dx,al
inc dx
rep outsb
ret
Set_Palette ENDP

First we make CX 768, because we want to set all colours, we load DX with the I/O port number. Then we make AX 0 because we want to start at the first colour in the palette, we output this to the port. DX needs to be increased to write the actuall RGB values. Finally we output all the colours wich DS:SI points to. Well, this should be a short word, and it's getting long already, so just look at the Source. If you don't understand it, please be patient or read Denthor's tutorials. (or you can always drop me an iail)


Closing words

We have come to the end of this tutorial. In the next tutorial I'll explain some file operations and some more instructions, also some different ways of inputing data (mouse, keyboard). With the knowledge of ASM you now have, you should be able to make a program already. Nothing fancy, but just a small working program. And as always (you guessed it) I hope you learned something. My tutorials can always be found at:

http://www.xs4all.nl/~smit/
http://www.geocities.com/SiliconValley/6112/


Copyright 1996 Ferdi Smit. All rights reserved.
Design and text by MAD
Send any comments and suggestions to UZteam



* #WhiteUnicorn/ StartPage/ Documentation/ AsmTutor04 >



- - * - Anastasija aka WhiteUnicorn - * - - LJLiveJournal
PFPhotoFile