Programming <<

Learning assembler

If you're ready this html document will learn you the coolest programming language assembler!
This course describes real mode assembler and protected mode assembler.

What is assembler?
Learning the MOV operator.
Learn to call interrupts.
Learn to call the I/O ports.
Learn to read/write with memory addresses in 16 bit real mode.
Learn to read/write with memory addresses in 32 bit protected mode.
Linked registers.
Logical operators.


What is assembler?

This is a definition of assembler:
-It's risky
-It's fast
-It's total freedom
-It's hard when you haven't got the right information

Assembler is a low generation programming language. It stands very close to machine code so you're actually telling the processor to do things. This course has been optimized for programming languages that support assembler-code such as: Pascal, C(++), and some BASIC compilers. When you have a standalone assembler you can learn some things from this page but I don't recommend that you use a standalone assembler, at least for now.

Note for C++ users: the character 'h' after a number indicates that it's an hexademical number. To use this in C++ use 0x13 in stead of 13h.

Go to the top of this page


Learning the MOV operator

In assembler each line is called an instruction. In contains an operator and operands if necessary. Now let's learn the MOV operator. It needs two operands. This is a very useful and easy operator, and used very frequently. I recommend that you have a compiler or assembler at hand so you can ALT-TAB to it when necessary.
This is the syntax: MOV dest, src
The yellow word is the operator, the two arguments are the operands. The value from src(source) is placed in dest(destination). An example:
MOV VAR, 10.

What happens here is that the value 10 is placed in VAR. compare it with: VAR = 10 or VAR := 10 Now you can try it yourself. To open up an assembler block in some compilers you can use the ASM statement. You have to declare the variable first! Now that you have learned it let's call the registers in the story. You can compare registers with variables. They are automatically declared variables by the processor and there are many types. Look at the table below.

Register Description
AL, AH, BL, BH, CL, CH, DL, DH 8bit (byte, unsigned char)
AX, BX, CX, DX 16bit (word, unsigned short)
EAX, EBX, ECX, EDX 32bit (longint, unsigned long)
MM0, MM1, MM2, MM3, MM4, MM5, MM6, MM7 64bit (quadword)
DI, SI 16bit memory addressing
EDI, ESI 32bit memory addressing
CS, DS, ES, SS Segment selector
FS, GS 32bit segment selector
SP Stack pointer
ESP 32bit stack pointer
IP Instruction pointer
EIP 32bit instruction pointer
BP Base pointer
EBP 32bit base pointer
AF, CF, OF, PF, SF, ZF Flags (true or false)
There are some registers that will be explained later. The yellow colored ones are only available when in 32 bits protected mode (this includes windows). The cyan colored ones are provided by the MMX extended instruction set (most used in P166 and higher processors).

Now let's do 2 MOV instructions with a register. First declare a variable as word or unsigned short. Now look at this example:

MOV AX, 13
MOV VAR, AX
The value 13 is placed in the register AX. Then it is exported from AX to VAR. Now try it out yourself. Maybe you've got the guts to learn the next lesson.

Go to the top of this page


Learn to call interrupts.

Note for 32 bits protected mode: You can't call interupts as easily as in 16 bit real mode. You'll have to use another way to call these (mostly provided by the compiler).

Now we're actually going to do something! There are 3 ways to call the computer functions: interrupts, I/O ports and I/O Memory addresses. We're going to learn the interrupts. The interrupts are the power of assembler and have functions that your compiler doesn't have. The mouse is a very good example of this. In almost every compiler there seems to be an absence of calling the mouse. Here you're going to need the registers. If you don't know what I'm talking about read the first lesson. The syntax is: INT intnr.
Nr is the number of the interrupt. We're going to study interrupt 10h (Video Services) and interrupt 33h (Mouse routines). Almost each interrupt has sub-functions. Subfunction 0 of interrupt 10h is setting the screenmode. Before calling an interrupt it expect certain registers to be filled with data. Interrupt 10h expects the subfunction in AH. Subfunction 0 expects the screenmode in AX. An example:

MOV AH, 0
MOV AX, 19
INT 10h
Here the subfunction 0 is in AH (setting the screenmode). Then AX is filled with the screenmode 19 (320x200 256 colors). Then INT 10h is called that reads those registers and does the job. You can try out to set different screenmodes as you like (0-19). Just fill AX with different numbers. Now we're going to put a pixel. This is subfunction Ch. It expects the X position in CX and the Y position in DX. Put the color value in AL. Value 15 is white. But you can try other colors as well. An example:
MOV AH, Ch
MOV CX, 100
MOV DX, 100
MOV AL, 15
INT 10h
Here the subfunction Ch is in AH (Write graphics pixel dot). Position [100,100] is filled in CX and DX and color 15 (white) is filled in AL. This function puts a white pixel at [100,100]. Now you can try and put pixels as you like. For setting the screenmode back to text fill AX with 3 (subfunction 0). An example:
MOV AH, 0
MOV AX, 3
INT 10h
I hope you still understand this. Now let's go to interrupt 33h. Because we're using standard screenmodes this interrupt is easy to call. Let's put the mousecursor on. This is subfunction 1, but this interrupt expects the subfunction in AX. Remember that! Further it doesn't expect anything. An example:
MOV AX, 1
INT 33h
That's it! If you're in graphics you'll see the well-known arrow. In textmode you'll see a block. The mousedriver manages all the moving. Now let's turn it off. This is subfunction 2 and doesn't expect anything. An example:
MOV AX, 2
INT 33h
Now it's turned off. Is this easy or what! Now you've learned to work with interrupt that does something. Let's learn to work with interrupts that returns values. In this case you still have to fill certain registers, but other registers are filled after calling the interrupt. A good example is checking if a mousedriver is present. This is subfunction 0. It returns a value in AX. If it is 0 there is no mousedriver. If it is 65535 there is a mousedriver. An example:
MOV AX, 0
INT 33h
MOV VAR, AX
Here AX is filled with subfunction 0. Then the interrupt fills AX with a value. But the value in AX is exported to a variable (declare it as word or unsigned short!). Now you've learned a lot of things and with this small knowledge you can do many things! If you want to learn more go to the next lesson.

Go to the top of this page


Learn to call the I/O ports

Now let's learn the 2nd way to communicate with the computer. These are the I/O ports. The best known port is the soundblaster-port (A220). We're not going to program this port because the soundblaster is very complicated. The operators you're going to use are IN and OUT. Syntax: IN prt, val.
Prt is the port address. val is the value you're going to receive from it. This must be an 8 bit register. The operator OUT has the same syntax as IN operator, but the value is exported to the port. Before we're going to use a port first set a graphics screenmode (320x200 256 colors).
MOV AH, 0
MOV AX, 13h
INT 10h
The number 13h is the same as 19 but the first number is in HEX. At start the whole screen is filled with pixels of value 0 (black). The port we're going to use can give a colornumber a red, green and blue value. Then all pixels of that colornumber becomes that color. This is very fast and are called palette operations. First put the colornumber in port 3C8h. Then write the red, green and blue value in port 3C9h (write 3 times!). Let's make colornumber 0 and the screen red. An example:
MOV DX, 3C8h
MOV AL, 0
OUT DX, AL
MOV DX, 3C9h
MOV AL, 63
OUT DX, AL
MOV AL, 0
OUT DX, AL
OUT DX, AL
First put the port in a 16 bit register. Then we're setting the colornumber to AL (8 bit register) That's 0. Then use the OUT instruction. Then we'll put a 63 value in AL and use the OUT instruction. Then put a 63 value in AL and use the OUT instruction 2 times. Only red must get a high value so we're writing the value 63 first. Then we're writing two ZERO values (green and blue). You can play with the palette as you like. Now let's read the red, green and blue value from a colornumber. Then you must send the colornumber to port 3C7h. Then READ 3 times from port 3C9h. An example:
MOV DX, 3C7h
MOV AL, 0
OUT DX, AL
MOV DX, 3C9h
IN AL, DX
MOV red, AL
IN AL, DX
MOV green, AL
IN AL, DX
MOV blue, AL
As you can see always put the port in a 16 bit register and the value in an 8 bit register. Now you see an IN instruction. Then the values are exported to the declared variables red, green and blue (byte). Now you have mastered the IN's and OUT's and the palette.

Go to the top of this page


Learn to read/write with memory addresses in 16 bit real mode

Now you're going to learn the 3rd way to communicate with the computer. You're not going to learn a new operator. We only need the MOV operator. The best known memory I/O address is the video segment. Which is located at [A000h:0]. And we're going to use that for plotting even faster pixels. Now I'm going to tell you how these addresses are builded. The memory addresses have 2 parts. The first one is called the SEGMENT and the second one the OFFSET. These parts are both word or unsigned short(16 bit). In total there are 1048576 bytes of memory to access this way. To make the 2 parts 1 just multiply the first part with 16 and add the second part, so the real video address is [A0000h]. If that is too confusing don't try to get the real memory address. We're going to use an example to make things more clear. First set the 320x200 256 color screenmode:
MOV ah, 0
MOV ax, 13h
INT 10h
The videocard stores the colornumbers of each pixel in the videosegment. If you change the value of the first address the first pixel will change too. This is a table that show you how the pixels are stored:
0123456789
10111213141516171819
20212223242526272829
30313233343536373839
40414243444546474849
50515253545556575859
60616263646566676869
70717273747576777879
80818283848586878889
90919293949596979899
This is an example of a resolution of 10x10 (not that you're ever going to use that resolution!). Now let's "plot a pixel at memory address A000h". You need the ES register now. This register contains the segment. use any other 16 bit register for the offset. In my examples I will use the DI register because it is specially made for memory addressing. An example:
MOV BX, A000h
MOV ES, BX
MOV DI, 0
MOV ES:[DI], 15
You can't put a value in ES directly so we're using BX first. You can now plot pixels very fast. Don't change ES. Just change DI from 0 to 65535. If DI=16 you can increment the ES register and put 0 in DI. But many programmers don't do that.

Go to the top of this page


Learn to read/write with memory addresses in 32 bit protected mode

If you haven't worked with memory in 16 bit real mode, using assembler I recommend that you try that first before you're going to read this lesson. This lesson is about the 3rd way to communicate with your computer. The MOV operator is only used here, so no new operator to learn. If you've used 16 bit memory addressing before you're in for a big surprise, the memory model here is flat. That means no offset and segment selector. You can address memory physical now. That means that ES loses its function, and only EDI is needed. And don't use DI, but EDI because this is 32 bits. First declare a pointer and allocate some memory, like 256 bytes. An example:
MOV EDI, pointer
MOV AL, 32
MOV [EDI], AL
pointer is a declared pointer in your compiler. The address of the pointer is moved into EDI, and the value 32 is put in the first byte of the allocated memory. Try to write the first byte of your allocated memory to the screen. That's all about 32 bits memory addressing.

Go to the top of this page


Linked registers

If you look at the register table, the first thing you notice that every register that ends with an 'X' is 16 bit. Other's ending with an 'H' or an 'L' are 8 bit registers.
Actually AH and AL together are the same as AX. AL is the low order byte (the L from low) and AH is the high order byte (the H from high) of AX. This means you can calculate AX from AH and AL like this:
AX=AH*256+AL
Try to change AL, and you'll see that AX changes too. Try this:
MOV AX, 0
MOV AL, 12
MOV VAR, AX
VAR is a 16 bit variable. This is an advantage: You don't have to declare a 8 bit variable to get the 8 bit value from AL. This knowledge can be VERY useful.
Let's take a look at the procedure to set the graphics mode:
MOV AH, 0
MOV AX, 13h
INT 10h
You'll see that the first instruction line isn't needed because AH will be 0 when the value 13 is put into AX.

Go to the top of this page


Logical operators.

If you're still reading this it means that you want to use assembler also for optimising. The other lessons where all about calling the computer's hardware. These lessons will be about optimising and you will learn new instructions to optimize your code to it's best. Each instruction needs the processor's clockticks to operate. Some will require more than the other. For example: the instruction XOR (new) is faster than MOV. Now let's take a look at the syntax:
XOR op1, op2.
These instructions (including AND, OR) are logical operators and only needs just one clocktick. You need to have some binary knowledge to use these. This is what XOR really does:
10011101 (1+4+8+16+128=157)
00001111 (1+2+4+8=15)
--------  XOR
10010010 (2+16+128=146)
When the bits are the same, the destination bit will be 0, else it will be 1. The result is stored in op1. All logical operators have the same syntax and store the result in op1. AND works like this:
10011101 (1+4+8+16+128=157)
00001111 (1+2+4+8=15)
--------  AND
00001101 (1+4+8=13)
When the bits are both 1, the result bit is set to 1, else it will be 0. OR works like this:
10011101 (1+4+8+16+128=157)
00001111 (1+2+4+8=15)
--------  OR
10011111 (1+2+4+8+16+128=159)
When the bits are both 0, the result bit is set to 0, else it will be 1. There is another logical operator NOT. This works different than the others.
Syntax: NOT op.
10011010 (before calling not)
01100101 (after calling not)
NOT toggles each bit and stores the result in op. Now that you've learned these let's optimize some code. An example:
MOV AX, 12
XOR AX, AX
MOV VAR, AX
Try filling AX with different numbers before XOR is called. Use VAR to let the compiler write the value in AX to the screen. Whatever you do, AX stays ZERO. Now what really happens:
00000000 00001100 (4+8=12 <-AX)
00000000 00001100 (4+8=12 <-AX)
-----------------  XOR
00000000 00000000 (0 ->AX)
Remember? When the bits are the same the result bit is 0. Now you've learned that when you are passing the same operands to XOR the result will always be ZERO. It's like MOV AX, 0. But why using XOR? If you remember: XOR is faster than MOV. This means that you can set registers faster to ZERO. This technique is used very often. Another technique: when you want to take the modulus of 32. This means that the maximum value will be 31. To do this you can mask out the other bits. An example to make it clear:
10110110 (2+4+16+32+128=182)
00011111 (1+2+4+8+16=31)
--------  AND
00010110 (2+4+16=22)     
And if you take the remainder of 182/32 you'll see that it is 22, just like the answer. You can only do this trick on special binary numbers like 2,4,8,16,32 etc.

If you think something should be added or changed E-mail me at [email protected]
Go to the top of this page