Esto lo escribí en 1996, con 15 o 16 años, para la peña de una BBS llamada Public Enemy. Era whisky-ware: si te servía, me invitabas a un güisqui. El estilo es el que es — cero correcciones, cero censura. Lo que veis es exactamente lo que se distribuyó en un .DOC de Word dentro de un ZIP con su FILE_ID.DIZ y todo. Si os parece cutre, es que no vivisteis los 90.
░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒░ ░▒▓ ▓▒░ ░▒▓ Tutorial de ASM ▓▒░ ░▒▓ ▓▒░ ░▒▓ Sami 1.996 (Manolo Buitre por aquello de mantener anonimato paterno) ▓▒░ ░▒▓ ▓▒░ ░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒░
Hola a todos aquellos que se hayan bajao el ficherete este... ante todo decir que esto no va a ser nada dificil, pero que hay que cogerlo todo a la vez... si ves que te atascas en algun sitio... sigue leyendo... ya que lo mas seguro es que un capitulo tenga que ver con otro y en el siguiente se explique mejor lo que hubiera en el anterior y tal y cual 3:)...
Este primer capitulo va a tratar de ser una iniciacion hacia el "mundo interior" del PC por lo que no pienses que es tan sumamente facil (no quiero engañarte) pero es preciso que lo poco que hay escrito en este capitulo lo tengas entendido para poder seguir. Este es el capitulo => mas importante <= sin duda por lo que tienes que dominarlo 3;DDDDDDDD... yo lo siento, pero es asi... pero te doy animos para que sigas ya que a partir de leer esto todo lo demas es como los juegos... vas perdido al principio pero luego tu nivel va subiendo raaaaapidamente 8*)))))))
01 — Introduccion
Como paso de explicar el por que de usar ASM... si acaso te resumo si no tienes mucha idea de lo que va esto del ASM (ensamblador, que es lo mismo). ... Resulta que tu tienes un micro (un microprocesador, un 8086, un 286, un 386, un 486 y si te pones hasta un 586) de la "saga" esta de los PC. Todos estos se programan por unas instrucciones muy sencillas (como las letras del abecedario)... pero con las que puedes crear funciones complejas (exactamente igual que con el vocabulario humano). Para que te hagas una idea... puedes multiplicar por ejemplo un 5 por un 5 facilmente con una simple instruccion del procesador:
mul 5,5
... pero te preguntaras... ¿y donde mete esto el resultado?... pues digamos que esta orden que acabamos de poner (mul 5,5) no existe realmente. En el micro (sea cual sea, que si un 386 o un 486 o el que tu quieras) tienes unos cuantos "registros"... estos son... pues como las teclas de memoria de las calculadoras (para que me entiendas), pero que son rapidiiiiisimos.
Antes de ponernos a programar programillas sencillos debaras saber los tipos de datos que existen... tendras que saber como pasar un numero de hexadecimal a binario y viceversa (pero no te preocupes... es mas sencillo de lo que "suena" ;)
02 — Tipos de datos
Esto no se por donde cogerlo para explicarlo si no tienes ni idea... pero vamos a intentarlo... Vamos a empezar con como codificarle un numero a tu microprocesador, para que lo entienda. Esto se codifica en bits...
7 6 5 4 3 2 1 0
* * * * * * * *
Cada asterisco de estos es un bit... un bit es como bien decia un amigo... un pedacito de informacion... "algo" que puede estar encendido o apagado (a 1 o 0 como se suele decir... en ingles "set" por si lees algun otro texto) y el numero que le pongo encima al asterisco, es el numero de bit... (que por convenio se pone de izquierda a derecha comenzando en el 0)
Que ocurre pues... imaginate que tenemos un bit. Resulta que este bit lo podemos tener o encendido... o apagado... dos combinaciones, ¿no?...
Que ocurre si le añadimos otro bit al lado:
1 0 <== Este es el numero de bit (de izquierda a dcha)
* *
0 0 <== esta es la primera combinacion
0 1 <== la segunda
1 0 <== y asi todo el rato, claro
1 1 ==> Hasta que vemos que con dos bits podemos
tener hasta 4 combinaciones diferentes.
Si a estos dos bits le añadimos otro... tendremos ocho combinaciones diferentes (haz la prueba si no te lo crees ;) ... y para finalizar... decirte que un byte tiene 8 bits... y un word son dos bytes... usease... 16 bits.
Para ahorrate tiempo pensando sobre esto... mira lo siguiente:
7 6 5 4 3 2 1 0
* * * * * * * *
128 64 32 16 8 4 2 1
¿ Te haces una idea ?... ¿¿¿ aun no ???... venga pues... seguiiiimos....
Suma los valores que tenemos debajo del bit (*)... ¿cuanto da?... ¿ 255?... muy bien... si contamos el 0... son 256 combinaciones :)
Y ahora te preguntaras... pues que bien, no... cuantas combinaciones... pero para que coño quiero yo tanta combinacion y tanta chorrada.......... ¡¡ pues ahi vamos !! (tranquilo, es sencillo... y si no ya sabes quien es Manolo Buitre 3;)
Vamos a guardar en binario el numero... por ejemplo... el 1:
7 6 5 4 3 2 1 0 <= Nº bit
* * * * * * * * <= nada
128 64 32 16 8 4 2 1 <= Valor
-------------------------------------------------------
0 0 0 0 0 0 0 1 <= codific.
No se si pillas lo que te digo... otro ejemplo pues..... el numero 4:
0 0 0 0 0 1 0 0 <= codific.
... el numero 128:
1 0 0 0 0 0 0 0 <= codific.
... el numero 129:
1 0 0 0 0 0 0 1 <= codific.
... el numero 96:
0 1 1 0 0 0 0 0 <= codific.
(cuanto copiar y pegar 3;)
Suficientes ejemplos, ¿no?... ¿sencillo?... y ahora diras... "no me jodas que voy a tener que trabajar poniendo los unos y los ceros esos!???? :-((("... claro que no... se suele usar el decimal en muchos casos... el binario por ciertas caracteristicas que las veremos mas adelantes y sobre todo el hexadecimal por lo bien, bien, bien que se lleva con el binario... sabes que el hexadecimal (hexa para los amigos) va del 1 al 16, pero con estos numeros:
0 1 2 3 4 5 6 7 8 9 A B C D E F <= hexadecimal
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <= valor decimal
Como ves... cuando te pasas del 9 pasas a la A, luego a la B para poner un 11 y total que con un solo digito podemos poner un valor del 0 al 15 (16 combinaciones) en vez de del 0 al 9 del decimal (10 combinaciones)... Pero esto es lo de menos... imaginate un numero binario... el 24 por ejemplo:
7 6 5 4 3 2 1 0 <= Nº bit
* * * * * * * *
128 64 32 16 8 4 2 1 <= Valor
-------------------------------------------------------
0 0 0 1 1 0 0 0 <= codific.
... y en binario 24 es "00011000" ... y para pasarlo a hexadecimal... cogemos el binario "00011000" y lo partimos de 4 en 4 bits (recuerda que con 4 bits podemos tener de 0 a 15 combinaciones... ¿ves a lo que voy? ;-).....
8 4 2 1 | 8 4 2 1 <= VALOR
0 0 0 1 | 1 0 0 0 <= binario
----------------------------------------------------------
1 | 8
Total que 00011000 en binario (que se pone 00011000b) en hexadecimal se escribe 18h (que no es ni mucho menos 18 en decimal)...
Te voy a explicar un poco el porque de usar mas el hexadecimal y el binario en vez del decimal...
a) toda la documentacion de las tarjetas del PC (soundblaster y todo eso) te viene con numeros en hexadecimal... b) si aprendes a codificar en binario te podras dar cuenta de como funciona TODO en el ordenador y no es nada inutil ya que podras "pensar" como el cacharro este que tienes en tus narices y por lo tanto ahorrar mucha memoria, comprender perfectamente el ASCII, las comunicaciones, los bytes, words (enteros en castellano) y en fin... c) te lo mando yo, "jodao"... y como a mi toda la teoria que no vale para nada me da asco... pues te podras imaginar que esto que parece una tonteria... luego se usa (y si no estate por seguro de que pasaria del tema 3;)
Como resumen de este primer apartado... como datos utiles...
Un byte son 8 bits... y por lo tanto 256 combinaciones posibles Un word son 16 bits... y por lo tanto 65536 combinaciones posibles Una letra (si, un ASCII) ocupa un byte... y para hacerte una idea... el ordenador almacena una "A" con el codigo 41h, una "a" con el codigo 61h, y asi sucesivamente. Un byte en hexadecimal no tiene mas de dos letras... 256 es 0FFh Si pones un valor en hexadecimal, asegurate SIEMPRE de que pones la letra "h" (a ser posible minuscula por aquello de que es mas legible) justo pegada detras al valor, y si el numero en hexadecimal comienza por una letra ponle ==> SIEMPRE <== un 0 delante... mas adelante comprenderas el por que... El signo de un numero es el bit nº7... si esta a 1, o el numero es negativo o vale 128 o mas... haz pruebas y veras por que };)
Bueno... son las 12 (creo, vamos)... es Sabado 16 o 17 de Agosto... estoy tratando de acertar con lo que digo puesto que vengo de un parque y ya se sabe lo animados que son los parques los sabados por la noche 38*)... y aunque me gustaria seguir en el parquecito bebiendo !Gaseosa mama, gaseosa! 3;)... pues a la gentuza esta que hay en mi casa les da porque venga al menos un poco recto... ¡ hasta otro capitulo !...
03 — Ensamblando y linkando
No... si comprendo que te cagues en mis muertos al oir estas palabras, pero que se le va a hacer... bueno, al grano... Resulta que para hacer un programa en ASM necesitas un editor de textos que te grabe en ASCII (¿ hay alguno que no lo haga ?)... por ejemplo el edit del DOS es que suelo usar yo. Luego necesitas lo que es el compilador (TASM, MASM, A86... cualquiera que te permita usar un procesador 80XXX es valido, aunque a mi me gusta el TASM o Turbo Assembler mas que los demas) y por ultimo un linkador Turbo Link, TLink, Link o el que mas te guste (para esto da igual cual uses porque son todos muy parecidos).
Y ahora las dudas:
A) ¿ Que es un compilador ?
Tu haces un programa con un simple editor de texto, ¿no?:
.MODEL Tiny
.CODE
mov ax,4Ch
int 21h
.STACK
END
Esto es un programa que lo unico que hace es salirse al dOS, usease, acabar... (no te preocupes si no entiendes el programa porque en estos momentos es lo de menos).
Esto => NO <= es codigo maquina, es decir, codigo que la maquina (tu procesador) pueda entender directamente. El compilador lo que hace es pillar esto que has escrito tu y pasarlo a codigo maquina. El codigo maquina, para que te hagas una idea es exactamente igual que lo que tu estas escribiendo, pero de este modo:
mov ax , 05h <== mete en ax un 5 (esto lo pones tu)
(ocupa 12 bytes)
15h 01h 05h <== esto ocupa solo 3 bytes
Si no has pillao todavia lo que te estoy diciendo... se podria decir (perfectamente) que cada instruccion en ASM (por ejemplo MOV) corresponde con un codigo de instruccion ("opcode" en ingles), por ejemplo el 15h. Como te podras imaginar esto luego es mas complejo, pero pasamos de entrar en el tema porque para hacer programas normales e incluso complicados no se suele usar para nada... era simplemente una explicacion de lo que hace el compilador.
B) ¿ Y el linkador que hace ?
Cuando haces un programa en ASM le sueles poner la extension .ASM (mi_prog.asm por ejemplo) y al compilarlo se creo un fichero objeto (mi_prog.obj). Te recomiendo que empieces ya a hacer pruebas compilando ejemplos de otras personas o copiando codigo ajeno para que te vayan saliendo fallos y dudas. Pero te das cuenta de que el .obj no es ejecutable... pues hay que linkarlo para hacer un EXE o un COM.
Resumiendo los pasos a seguir para compilar y linkar tus primeros programas:
a) Crear el programa con el edit
edit mi_progr.asm
b) Compilarlo
tasm mi_progr.asm
c) Linkarlo a un .EXE del DOS
tlink mi_progr.obj
d) Ejecutar si quieres el programa
mi_prog.exe
Y añado posdata al capitulillo... no te preocupes si todavia no sabes porque se hace esto asi... de momento lo haces y cuando hayamos hecho unos programas nos metemos un poco mas con el linkao, los obj y demas (que ya veras como es sencillo, tranquilo/a ;-)
04 — Los registros del procesador
Los registros del procesador son unos "sitios" donde puedes poner valores para operar con ellos: sumar, restar, multiplicar y otras cosas que las veremos dentro de un rato :) Digamos que los registros son variables... pero con un nombre fijo, y cada uno de ellos se usa para una funcion en concreto. Comencemos pues...
Tenemos los que se llaman registros de proposito general:
AX, BX, CX y DX... todos ellos de 16 bits, con lo que podemos
meter numeros del 0 al 65535 (o negativos)
Una caracteristica general a estos registros es que se pueden manejar tambien de 8 en 8 bits:
AX => AH y AL BX => BH y BL CX => CH y CL DX => DH y DL
15 0 <= Numero de bit
AX => ****|****|****|****
| | | |
--------- ---------
AH AL
Si no has pillao el dibujo (aparte de que es pa matarte) pues AL y AH son de 8 bits cada uno (BX, CX y DX son en todo igual a AX)... asi que vamos con mas ejemplos que son los que de verdan muestran la utilidad de estas cosas:
mov ax,5 ; Esto mete en AX un 5
^^^
* Nota: El ";" significa comentario. Si lo pones en tu programa, todo lo que
haya en esa linea a partir del ";" no se compila.
AX quedaria de esta manera:
AX=0000|0000|0000|0101b <= en binario queda esto
AH quedaria 00000000b
AL quedaria 00000101b
Podemos poner tambien:
mov ah,1
mov al,2
AL => ****|****
0000|0010b
AH => ****|****
0000|0001b
Por lo que AX queda: 0000|0001|0000|0010b
Creo que todo queda muy claro, ¿no?... si acaso ya sabes... ¡a preguntar! Entonces... aclarado esto vayamos a por las caracteristicas especiales de los registros "de proposito general"...
AX: para multiplicar y dividir
BX: direccionamiento de memoria (apuntar a un byte en memoria...
te remito al proximo punto... LA RAM (LA MEMORIA)) y
para multiplicar AX por BX.
CX: contador (bucles... si no sabes lo que es... tranquilo, en
el proximo capitulo lo veras)
DX: se suele usar para guardar el offset de una direccion
(salta a lo de la RAM si no sabes lo que es)
SI: 16 bits... Source Index (en ingles)... se suele usar para
apuntar a una cadena (una cadena es un conjunto de bytes,
por ejemplo... "Manolo quiere ser carpintero")
DI: 16 bits... Destination Index... lo mismo que SI, pero para
apuntar al lugar donde se quiere copiar una cadena.
Y estos son los 4 registros de proposito general. Si trabajas con un 386 o superior... la cosa varia (si tu quieres, claro)... los registros son de 32 bits con lo que el numero de combinaciones es muy grande y las transferencias de datos muy rapidas. A estos registros de 32 bits se les llama EAX, EBX, ECX y asi con todos los registros. AX en este caso serian los 16 bits mas bajos (con menos valor, del nº 0 al nº 15)
05 — La RAM (la memoria)
RAM es la abreviatura de Random Access Memory... (Memoria de Acceso Aleatorio)... esto significa que para acceder a un byte determinado de esta, necesitas la direccion de este byte. Y ahora es cuando viene la parte "dura" del ASM (o mejor dicho del propio esquema del PC)...
Resulta que tenemos pues... 8 megas de RAM, ¿vale?... Asumimos tambien que cada byte va a tener una direccion (que es numerica)... Si al primer byte le pusieramos la direccion 0, al segundo la 1, al tercero la 2... con nuestros registros de 16 bits no podriamos direccionar mas que 65536 bytes, ¿no?... que pasaria pues con el resto de la memoria... ¿la perdemos?... no, claro que no... tenemos ¡ mas registros ! (no llores todavia 3;)... A estos nuevos registros se les llama de segmento.
Como se soluciona el problema de los 64Kb:... por segmentacion... (eh! mis muertos no te han hecho nada, ¿vale? 3;)... Es sencillo... se hace con 2 registros de 16 bits.
Ejemplos de sintaxis:
mov bx,5 ; bx=5
add bx,2 ; suma a bx un 2
; bx ahora vale 7
mov bx,1 ; metemos en bx un 1
mov ax,bx ; ax vale 1
Imaginate que quieres leer el byte numero 64 de la memoria:
mov bx,64
mov al,[bx] ; al coge el valor de la posicion de memoria 64
; ¡NO EL VALOR QUE HAYA EN BX!
Recalcar que esto SOLO se puede hacer con BX, con SI y con DI... con lo que poner:
mov al,[cx]
... no existe y no funcionara. El ordenador parte la memoria en bloques de 64Kb llamados segmentos:
|---------|
| 64Kb |
|---------|--> Segmento 3
| 64Kb |
|---------|--> Segmento 2
| 64Kb |
|---------|--> Segmento 1
| 64Kb |
|---------|--> Segmento 0
El valor del segmento (0,1,2,3 etc) se mete los registros de segmento: DS, CS, SS y ES. Por lo que la direccion queda creada con un par de registros Segmento:offset (desplazamiento):
DS:BX para los datos
CS:IP es la direccion en la que esta tu programa actualmente
por lo que no es muy conveniente cambiarla a mano (ya que
te aseguro que practicamente seguro que la cagas)
ES:DI, ES:SI, DS:SI, DS:DI para datos tambien
¿ Y SS:BP ?... para otro capitulo... despreocupate de ellos. Decir tambien que los registros de segmento no son accesibles directamente por lo que para meter el valor 1 en DS tendras que poner:
mov ax,1
mov ds,ax
06 — La pila (stack)
Es un segmento de memoria del tamaño que tu asignes (en nuestro primer programa ya veras la manera de crearlo). Aqui puedes guardar enteros de 16 bits poniendo tan solo:
push ax ; o push lo que quieras de 16 bits
Y para recuperarlos:
pop ax ; Coge el ultimo elemento que hayas metido
; en la pila y lo mete en AX
Que para que vale esto... pues tiene muchas utilidades... desde la rapidez del push y el pop a pasar parametros entre funciones distintas pasando por un:
push ds
push cs
pop ds
; AQUI VA TU PROGRAMA cuando necesitas que el segmento de
; codigo y el de datos sean el mismo, y luego restaurar
; el segmento de datos donde estaba.
pop ds
07 — Las interrupciones
¡Venga tio! es la ultima parte de teoria y a partir de aqui todo es sencillo. Si pillas las interrupciones que son muyyyy sencillas... luego solo te faltara saber como funciona aquello que quieras programas: SB, VGA etc. etc. pero no te preocupes... eso son todo cuestion de "tablas" y de informacion correcta al respecto. Cuando terminemos con las interrupciones crearemos nuestro primer programa, te empezaras a meter ya por fin en lo que es programacion ASM, pero como comprenderas, estas 620 lineas anteriores eran necesarias para que comprendieras el por que de las instrucciones de la maquina.
Bueno... a lo que vamos... las interrupciones... estas pijorras son unos programillas que estan en memoria (de momento)... y que te ahorran trabajo a la hora de sacar un texto a pantalla o las funciones mas sencillas del PC... estamos hablando principalmente de las funciones del DOS y de la BIOS.
La familia IBM-PC se controla a traves de interrupciones, que pueden generarse por hardware o por software. Cuando ocurre una interrupcion, el programa guarda el CS:IP en la pila (ves, otra aplicacion de la pila) y sigue ejecutandose la interrupcion hasta que encuentra un IRET.
Para llamar a una interrupcion por software tan solo hay que poner:
int 10h ; 10h porque es un servicio de la BIOS
Y luego si por ejemplo queremos poner el modo MCGA (320x200x256 colores) tenemos que meter en AX un 13h... ves... ya tienes tu primer programa:
.Model tiny <== Esto es una *directiva*... tiny quiere decir
que vamos a usar solo un segmento de codigo
y DS va a ser igual a CS.
.code Principal <== Otra directiva: indica que empieza aqui
el segmento de codigo llamado "principal"
mov ax,0013h <== El numero de servicio es el 13h
int 10h <== y es de las rutinas de video de la BIOS
mov ah,4Ch <== el servicio 4Ch del DOS acaba el programa
int 21h y lo que haya en AL lo mete en la variable
esa que habras visto en los bats: errorlevel
end <== Indica al compilador que deje de compilar
a partir de aqui. (es obligatorio ponerlo)
Nota: las interrupciones del DOS y las de la BIOS son muuuuchas por lo que te recomiendo alguna lista que tenga todas estas... porque aunque casi todas las interrupciones son prescindibles... aumentan muchiiisimo la compatibilidad futura del programa ademas de que empequeñecen el programa... aunque tambien lo enlentecen.
Es recomendable que te pilles algun otro tutorial, aunque todos los que he visto eran en ingles. Uno muy bueno y orientado a Basic estaba en un fichero llamado BTU y yo aprendi con ese y con paciencia de leerme teoria por un tubo que luego la verdad es que no vale para mucho 8(
... hasta la proxima (si no muero batido a cabezazos en un concierto del miercoles o Domingo 3;)... Por cierto... este viernes kedada, ¿no?
Ah!... y como profesor soy peor que mi perro de cajero en un sabeco, pues todas las dudas que tengais (que imagino que no seran pocas) pues que vayan a "Manolo Buitre" que no tiene ni email ni hostias, asi que... :-)
- FIN DEL PRIMER CAPITULO -
08 — Extras y plantillas
Un COM se compila igual que cualquier .ASM pero a la hora de linkar le tienes que añadir la opcion /t. Las diferencias con el .EXE son que el COM no es reubicable, es decir, que el DOS no lo puede poner en el offset de CS que le pase por las narices, sino en el que tu le pongas con la orden "org 100h".
; Pausa en un COM. Para su uso en Bat's
Codigo Segment
ASSUME CS:codigo, DS:codigo ; aqui le decimos que el segmento
; llamado codigo va a ser de datos
; y de codigo (piensa por que)
org 100h ; Origin en el offset 100h
Comienzo: ; Una simple etiqueta
MOV AH,7 ; Como ejercicio busca el que
INT 21h ; hacen estas dos lineas
MOV AH,4Ch ; Estas ya sabes lo que hacen
INT 21h ; ¿no?
Codigo EndS
END Comienzo
Te recomiendo que como "equipo" para programar tengas:
- un ensamblador y un linkador (sin esto la has cagao, claro)
- un procesador 80XXX (si no no se como lees esto, 3;)
- un desensamblador (para ir jugando con programas ajenos)
- un depurador (debugger) (para ver lo que hace el procesador con
tus programas o los de otros y eliminar
los fallos que tengas)
- la smartdrv cargada
- el mismo edit.com del DOS
- un fichero bat a este estilo:
asm.bat => edit %1.asm
tasm %1.asm
tlink %1.obj
- luego si te tienes la lista de interrupciones a mano, y algunas
chuletillas de lo que veas que no te vas a acordar, pues OK. :)
Documento güski-whare so... aplikate el kuento 3;) (es coña, pero si se agradecen aportaciones del susodicho tipo 3*)