Second chapter of the little tutorial. Distributed as whisky-ware through the Public Enemy BBS, circa 1996. The text is the original, untouched.
░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
░▒▓ Sami's dinky ASM howto ▓▒░
░▒▓ Chapter Two ▓▒░
░▒▓ Whisky-ware, by Sami 3;*)▓▒░
░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
-*-
In this chapter you'll already
be programming and all that. I guess
one more little chapter will be
needed (or maybe even two)... but
right now I'm a bit busy, so make
do "training" with this one and
the previous one... If you have
doubts... you know, I'm on Public Enemy.
-*-
( still don't know what I've got the
warlock for) };)
░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒░ ░▒▓ ▓▒░ ░▒▓ ASM Tutorial ▓▒░ ░▒▓ ▓▒░ ░▒▓ (Part II, for now) ▓▒░ ░▒▓ ▓▒░ ░▒▓ Sami, 1.996 (Manolo Buitre, to keep up some paternal anonymity) ▓▒░ ░▒▓ ▓▒░ ░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒░
This is the second chapter (and if it goes like the last one, in 3 chapters we'll have plowed through the whole course ;-)... I hope the last one treated you as well as summer treated me. Let's see what we left pending from last time........
01 — Getting started with hands-on stuff, skipping the theory... -;)
Alright... let's get on with programming... what would you like to do?... I can hear it already! A 3D engine?... sure... I believe that's what's called PRINT in Basic -3;)... but first a tiny bit more theory (which is actually practice, really). Before we go on, do you remember opcodes?... if you don't, go back and review them, because here I'm going a bit further:
; Compila a partir de aqui
; Segundo programa de XXXXXX
.MODEL MediuM ; En este caso mejor un Tiny
.DATA ; Sobra (pero sigue leyendo!)
.CODE
mov ah,4Ch ; Salida al dOS con "errorlevel"
int 21h ; (es un servicio de interrupcion)
END
Come on, bet you know what that program up there does?... now let's move on to a *compiler* directive (not a processor instruction)... actually there are several but they all do very similar things:
Let me introduce you to DB, DW, DD, DQ and DT (although there are a few more, more or less)
Before you go cursing at the saints, I'll have you know I already did, so hush... What does DB do, you ask?... Look:
.MODEL MediuM ; Como tengo datos y codigo mejor el medium
.DATA ; para este ejemplo no sobra, eh! 3;)
Mi_letra DB 'A' ; Mete el codigo ASCII de A
Mi_letra2 DB 65 ; (que es este)
Entero DW 1234h ; definimos 16bits
Mi_letra3 DB 41h
.CODE
mov al,byte ptr Mi_letra ; Mete en al 41h
mov bl,byte ptr Mi_letra2 ; Mete en bl 41h
mov cl,byte ptr Mi_letra3 ; Mete en cl 41h
mov ah,4Ch ; Salida al dOS con "errorlevel"
int 21h ; (es un servicio de interrupcion)
END
Okay dude! Don't get like that... bad example, I know... we'll play around with it a bit so you get the hang of it. First keep your little ASCII table handy, got it?... let's go on...
Let's start by analyzing the program:
- ".MODEL MediuM"
I want a little segment for data and another for code.
- ".DATA"
Whatever comes before ".CODE" will be my data segment,
where I'll stash whatever data I want (up to 64 Kb)
- "Mi_letra DB 'A'"
Assigns a 1-byte variable holding the ASCII code
of the letter "A" (uppercase), which is 65 in decimal
or 41h (same thing, of course). The following assignments
with DB work the same way. If we used DW, "we'd be defining a Word",
that is, a 16-bit integer, with DD a double word, etc.
Let's dig into this a bit more since it's veeeeery important:
The first DB goes at the start of the data segment,
the second DB in the second byte of that segment, and putting
the DW in the third and fourth byte gives us this:
3 4 <== offset within the data segment
-------
34h 12h
I haven't mixed anything up... it's just that words, double
words and so on get stored the other way round, meaning the
"dumbest" part goes first, and the high part of the value
(I believe it's called MSB in English, for
Most Significant Byte) comes after. This is something
very much worth keeping in mind if, say, you've defined
something with a DW and later want to read just one byte
from that memory location.
- "mov al,byte ptr Mi_letra ; Mete en al 41h"
Well, in case I haven't hammered it home enough, that ";" is a
comment. And then there's that "byte ptr" bit... what it does
is say to load one byte from the memory location "Mi_letra".
If we'd written "mov ax,word ptr Mi_letra" instead, ax would
hold this byte plus the next one. (Simple enough, right?)...
Well, maybe this is a silly thing to point out since if you write
"mov al,XXX" it's already implied (because AL is 8-bit) that you
only want to grab one byte. In fact I THINK modern compilers
don't even need this spelled out (but since I'm running my
ancient TASM I prefer to add it for clarity).
Where were we?... ah... right... the program's already done. As an exercise for this mini-topic... grab the Turbo Debugger and load this program (.EXE). To do that you'll need to write the .ASM with a plain text editor, then run it through TASM ("TASM mi_prog.asm") and finally link it ("TLINK mi_prog.obj"). Breakpoints in TD are set with F2, F9 runs the program to completion, and F7 lets you "trace" step by step what the little beast is doing. As tips... play around looking at memory, or your program loaded into memory (as opcodes), etc. TD (Turbo Debugger) will be one of the most useful tools you'll ever have (well, the best one), so at first you won't have a clue what it is, but once you play with it a bit you'll see how simple it is. :)
Another exercise (a bit dumb, maybe) is to grab a disassembler and produce another ASM (but starting from the executable)... this is good for finding out what those .MODEL directives and the rest actually do, plus you'll realize just how many ways there are to write the same thing in ASM.
Even though in the last chapter I lied to you saying there'd be no more theory (I think I've been lying to you the whole "way" ;)... in this one it won't "come at you" as much anymore (which doesn't mean there won't be any more, just that it'll be more "discreet")... in this chapter I've got planned for you to play a bit with the 8086, using it for simple calculations and so on. You'll use some of the more useful interrupts and the rest... so, onward :)
Let's start by printing some text to the screen:
.MODEL MediuM ; 64Kb de datos y 64Kb de codigo
.DATA ; Segmento de datos
Cadena DB "Esto esta en pantalla$"
; La cadena va entre comillas, y si usas
; el DOS para imprimir una cadena (cadena
; es una tira de bytes), el fin de cadena
; se señala con un "$".
.CODE ; Segmento de codigo
mov ax,@DATA ; Configura el DS => @DATA es el segmento
mov ds,ax ; de datos que pones con la directiva ".DATA"
; pero DS no es accesible directamante (como
; los demas registros de segmento) y por eso
; metemos el segmento en AX.
mov dx,offset Cadena ; El servicio 9 de los servicios del DOS
mov ah,9 ; (int 21h) imprime una cadena hasta que
int 21h ; encuentra un "$".. es decir... un 36.
; Para indicarle que cadena quieres sacar
; por la pantalla, el segmento de donde
; esta la cadena se mete en DS, y el offset
; en DX. Luego se llama a la interrupcion 21h.
mov ah,4Ch ; Acaba el programa
int 21h
END ; Fin del programa
This is the "listing" of the program above (it's generated with: TASM uu.asm /la /zi ... and it produces uu.lst):
1 0000 .MODEL MediuM
2 0000 .DATA
4 0000 45 73 74 6F 20 65 73+ Cadena DB "Esto esta en pantalla$"
7 24
13 0016 .CODE
15 0000 B8 0000s mov ax,@DATA
16 0003 8E D8 mov ds,ax
21 0005 BA 0000r mov dx,offset Cadena
22 0008 B4 09 mov ah,9
23 000A CD 21 int 21h
29 000C B4 4C mov ah,4Ch
30 000E CD 21 int 21h
32 END
02 — Templates for your simplest programs
TO MAKE A .COM
Codigo Segment
assume CS:Codigo, DS:Codigo
org 100h
; AQUI VA TU CODIGO Y TUS DATOS.
Comienzo:
mov ax,4C00h ; Salida al dos con errorlevel
int 21h
Codigo EndS
End Comienzo
Compile: Tasm mifich.asm
Link: Tlink/t mifich.obj
TO MAKE AN .EXE
.MODEL medium
.386
.DATA
ejemplo DB "Hola$"
.CODE
mov ax,4C00h ; Salida al dos con errorlevel
int 21h
.END
Compile: Tasm mifich.asm
Link: Tlink/3 mifich.obj
FOR .QLB FILES (for Quick Basic 4.x)
.MODEL medium,Basic
.386
.DATA
ejemplo DB "Hola$"
.CODE
Public MiSubrutina
MiSubrutina Proc Far, parametro:word
MOV BX,parametro
MOV AX,[BX]
RET
MiSubrutina EndP
.END
Compile: Tasm mifich.asm
Link into LIB: Lib milib.lib +mifich.obj
Create QLB: Link/QU milib.lib, milib.qlb,nul,bqlb45.lib
03 — Logic gates
They exist both physically and logically... they form the most basic building block of the computer, from memory chips right up to the processor itself, so you can imagine how important they are.
The processor instructions are: AND, OR, XOR and NOT
At first they'll seem completely useless, you won't remember them, and even if you do you won't know where to use them, but that just comes with practice 3;D
NOT ==> 0 - 1 / 1 - 0 — Negates: if you have a 1 it gives you a 0, and vice versa:
.MODEL tiny
.CODE
mov ax,11111111b ; o lo que es lo mismo 00FFh
not ax ; En AL tendremos todo a 0 y en AH todo a 1
mov ah,4Ch ; Salida al DOS
int 21h
END
The best thing with logic gates is that you reach your own conclusions, since the first time I just memorized them, the second time I forgot them, the third time I drew my own conclusions, and finally I actually used them ;DDDDD. Try them out with the Turbo Debugger, and if you don't have it, well, you know... };)
OR ==> 0-0=0 / 0-1=1 / 1-0=1 / 1-1=1:
mov al,00010001b
or al,00000010b
Bit number 1 gets set to 1 without touching the other
bits. The result, as with every ASM instruction, gets
stored in the first operand, meaning, in this case, AL.
XOR ==> if they're equal it gives 0, if different it gives 1. That's why it's used: "xor ax,ax" to clear AX... since AX and AX are equal (well, duh...)...
AND ==> Both need to be 1 if you want a 1. That's why it's used to make bit-by-bit comparisons:
mov al,01001001b
and al,00000001b ; Esto dara 1... :)
mov al,00000000b
and al,00000001b ; Y esto dara 0... :)
In assembly there's also another kind of AND. What sets it apart is that TEST (that's what this new instruction is called) doesn't store the result anywhere... and since you might be wondering what the point of that is (yeah, riiiight... ..... ;DDD)... let's move on to MORE PRACTICE!!!
04 — Jumps and all that (what's called branching)
So far the handful of little programs we've made have been linear. They started at a beginning. And ended at an end. But of course... few programs can stay linear, and they need to do one thing or another (depending on certain conditions, of course).
Enough rambling, I need my afternoon snack and it's already 7:
.MODEL medium
.DATA
h1 DB "Pues el bit 0 estaba puesto a 1$"
h2 DB "Pues el bit 0 no estaba puesto a 1$"
variable DB 00h
.CODE
mov ax,@data ; Prepara DS con @DATA
mov ds,ax
mov byte ptr variable,00110101b ; mete esto en "variable"
mov al,byte ptr variable ; en al esta 00110101b
test al,00000001b ; una AND con un 1
jz no_uno ; si el resultado da 0...
; ...salta a la etiqueta
mov ah,9 ; no_uno (Jump if Zero)
mov dx,offset h1 ; ^ ^ ==> JZ
int 21h ; ... y si no es 0, seguir
jmp final ; ... y saltar a final
no_uno:
mov ah,9
mov dx,offset h2
int 21h
final:
mov ah,4ch ; Sale al DOS
int 21h
END
Let's look at the explanation for this "phenomenon"... it turns out that where it says "jmp final", while that instruction is executing, the CS:IP (you'll finally know what that is by now)... well, let's say it's 6000:0020 (made up, of course). "Final" is a symbol, a label..."something" that marks a memory location (which is the one for "mov ah,4ch")... so if we tell it to jump to Final (jmp final)... it'll jump to the memory location where "mov ah,4ch" sits... there are three kinds of jumps...
short: allows a *** RELATIVE *** jump (from wherever
you happen to be at that moment) of 8 bits... meaning,
between -128 and +127. Normally you don't have to worry
about the jump type, since the compiler usually sets it
through the directives. It takes up the JMP opcode + 1 byte.
near : an intra-segment jump, within the same code
segment. So it only modifies IP. It therefore only
takes up one more byte than the previous one.
far : an inter-segment jump... between segments.. it modifies
CS:IP, so it takes up 2 more bytes than the previous one.
It's important to know which jump type to use, since the "farther" it is, the slower the jump, and the more memory it takes up (the instruction, of course).
05 — Special aside: the flags ;)
The flags are yet another 16-bit register...... yeeees.... anoootheeer one 3:). ... but this one is special... you can't write "mov ax,flags" or anything like that. Here are the flags (let's hope I don't mess this up too much ;):
15 0 ==> bit number
* * * * | * * * * | * * * * | * * * *
OF DF IF TF SF ZF AF PF CF
Reeeeelax.... it's nothing!!!
If you're reading this for the first time, don't worry if you don't get any of it, this aside is here for reference, not for explanation ;)... the explanation works muuuuch better in practice than in theory. Each of these bits is there to signal one thing or another (that's why they're bits). Here's the list... if a bit is set to 1:
CF: "carry flag" ==> the carry when adding
OF: "OverFlow" ==> when a value overflows and doesn't fit in 8 or 16 bits
ZF: "Zero Flag" ==> (this is the one JZ uses)... 1 if a comparison
is true.
SF: "Sign Flag?" ==> blah, blah, blah
AF: "?" ==> the same as carry but when you're working in BCD
(you'll see what that is later, for now just keep
taking small hops ;)
DF: "Direction flag" ==> tells you whether strings are stored going
up or down. (think about it)
IF: whether interrupts are enabled (or not, of course)
TF: the "trapping" flag... for running programs "step by step"... the
Turbo Debugger uses this little devil, for example, when you're
hitting F7 and F8.
There's more in processors above the 8086, buuuut.... Samy has just as much info on PCs beyond the 8086 as you do... so once we move past the 8086 you'll see how my "knowledge" ("although my mommy says I don't have any") starts degrading 3;(
06 — Jumps and all that (what's called branching) ][
And since there's no two without a one (and I'll bet you anything I'll have to do a third one too... and I'm sure I'll win, obviously X-D)... here come MORE JUMPS!!! So, since earlier I only showed you the unconditional jump, the one that jumps no matter what (jmp), and JZ (if the result is 0)... you must've been getting suspicious, but here come some more:
JA : jump if above => jumps if bigger JAE : jump if above or equal => if bigger or equal JB : (ah, gooootcha) jump if below => if smaller JBE : jump if below or equal => or smaller or equal JE : jump if equal => jumps if equal And now the opposites of these: JNA : if NOT bigger JNB : if not smaller ... well, all negated, just with an N right after the J ;)
We'll keeeeep going... there are looots more CX-DDDDDDDDDD, but first let's train a bit with these since they're the simplest and most used:
.MODEL medium
.DATA
h1 DB "es igual a 1$"
h2 DB "No es igual a 1$"
.CODE
mov ax,@data ; Prepara DS con @DATA
mov ds,ax
mov al,1
cmp al,1 ; compara al con 1
jne no_uno ; y SEGUIDAMENTE... si no es igual
; salta a no_uno
; INCISO: "CMP" es una instruccion
; del procesador, no es el test
; de antes (que tambien es del proce-
; sador). Y casi siempre lleva el
; Jxxx detras.
mov ah,9
mov dx,offset h1
int 21h
jmp final
no_uno:
mov ah,9
mov dx,offset h2
int 21h
final:
mov ah,4ch ; Sale al DOS
int 21h
END
Sorry for not adding comments but it's the same as the previous one :) The rest of these jump types, you know the drill, go practice them 3:D
Next week we'll carry on with other kinds of jumps, subroutines and the rest. We'll create our first interrupt, etc. But I'm leaving you homework (X-DDDDDDDDDD).... try out everything we've covered so far, no fear at all because you can't break anything (as long as you don't touch INT 13h, the disk one)... second: finally go get the interrupt list, it's on Public Enemy (53-18-07)... the actual Ralf Brown one. Try a few things out with it, you learn a lot that way, and then grab yourselves the processor's instruction list (and if you can't find it, just ask me for it). And I don't think I'm forgetting anything else... take care and happy (with a wink, of course) weekend 3:*DDDD...... FAFAN, TREAT YOURSELF TO SOMETHING!!!
PS: Whisky-ware document, share-whisky, or whatever you want to call it }:*D~~