Lenguaje Ensamblador Este tutorial es para principiantes que no saben ensamblador y quieren comenzar a usarlo para programar virus o para optimizar aplicaciones con este verdadero lenguaje de programacion. Porque un programador comun gastaria tiempo en ensamblador?. La razon principal es PODER, tienes el control de lo que hace el hardware y puedes hacer lo que quieras programando en asm. Claro que fue lo unico en tiempos del DOS, y actualmente se usa principalmente por creadores de virus y hackers. Tambien es usado para aumentar velocidad en ciertos procesos de aplicaciones grandes o programar a un nivel que nuestro lenguaje habitual no nos permite llegar. Cuando usamos asm, tenemos 4 registros del procesador para manipular datos: AX, BX, CX y DX. Por supuesto que estos registros tienen nombres y usos especificos. AX = Registro Acumulador BX = Registro Base CX = Registro Contador DX = Registro de Datos Todos estos registros tienen el valor de 16 bits, pero si queremos trabajar en 8 bits, estos registros se dividen en dos, uno de byte alto y el otro de byte bajo, representados con la letra H y L respectivamente: --->AH --->BH --->CH --->DH AX BX CX DX --->AL --->BL --->CL --->DL Cuando usamos ensamblador de 32 bits, los registros son EAX, EBX, ECX y EDX, los cuales no se dividen. Segmentos y Desplazamientos --------------------------- Este tema es el mas complicado (creo) o el mas molesto una vez que lo entiendes en ensamblador. Los dise~adores del 8088 (cuando los dinosaurios dominaban el planeta) decidieron que no era posible necesitar mas de un Mega de memoria, asi que construyeron una maquina que no pudiera acceder a mas de un Mega. Para acceder a un mega completo, se necesitan 20 bits. El problema fue que el registro solo tenia 16 bits, y si se usa otro, serian 32 bits, lo cual era muchisimo (pensaban), asi que se les ocurrio otra brillante forma de direccionamiento. Decidieron no usar los 32 bits, pero los dos registros crearian un direccionamiento de 20 bits y asi fue que los segmentos y desplazamientos nacieron. Offset = Segment * 16 Segment = Offset / 16 Segment * 16 |0010010000010000----| Rango de 0 a 65535 * 16 + Offset |----0100100000100010| Rango de 0 a 65535 = Direccion de 20 bits |00101000100100100010| Rango de 0 a 1048575 ( 1 Mega ) |_____ DS _____| |_____ SI _____| |_Empalme _| Aqui se muestra como DS:SI se usan para construir la direccion de 20 bits. Los Registros de Segmento son: CS, DS, ES, SS y en mayores al 386 tambien existen FS y GS. Los Registros de Desplazamiento son BX, DI, SI, BP, SP, IP. En mayores al 386 a Modo Protegido, cualquier registro general (no registro de segmento) puede usarse como Registro de Desplazamiento, excepto IP, al cual no se puede acceder. La notacion Standar para el par Segmento/Desplazamiento es: Segmento:Desplazamiento o A000:0000 (que esta por supuesto en Hexadecimal) Donde el segmento = 0A000h y el desplazamiento = 00000h (que es la direccion del pixel superior izquierdo en una pantalla a 320x200x256). Tal vez te preguntes si tienes un segmento y un desplazamiento con valores de 0FFFFh cada uno. La suma de ellos seria 0FFF0h + 0FFFFh = 1,114,095, el cual es definitivamente mayor a un Mega (de 1,048,576). Significa que puedes acceder a mas de un mega de memoria !. Bien, para usar actualmente esa memoria debes habilitar algo llamado la linea A20 que habilita el bit 21 para direccionamiento. Este bit de memoria es llamado "Memoria Alta", y se usa cuando cargas algo a la memoria alta o dices: DOS = HIGH en el Autoexec.bat. Hymem.sys actualmente lo pone ahi. No necesitas conocer ese ultimo bit, pero el conocimiento es bueno, no?. Los Registros ------------- Como habia mencionado, los registros tienen usos especiales, es decir que el resultado de alguna operacion cae directamente en ellos. AX Se usa para destino de IN (in port) IN AL, DX IN AX, DX Para fuente de OUT OUT DX, AL out DX, AX Destino para LODS (toma un byte o palabra de [DS:SI] e incremente SI) lodsb (igual que: MOV AL, [ds:si]; inc si) lodsw (igual que: MOV AX, [ds:si]; inc si ; inc si) Fuente de STOS (pone AX/AL hacia [ES:DI] e incrementa DI) stosb (igual que: MOV [es:di], AL ; inc di) stodw (igual que: MOV [es:di], AX ; inc di , inc di) Usado por MUL, IMUL, DIV, IDIV BX Como se menciono antes, se puede usar como Registro de Desplazamiento. MOV ax,[ds:bx] (agarra la palabra en la direccion creada por DS y BX) CX Usado en REP para repetir una instruccion CX numero de veces. MOV CX, 10 MOV AX, 0 REP stosb ; esto deberia escribir 10 ceros en [ES:DI] ; e incrementa DI por 10 Usado en LOOP MOV cx, 100 THELABEL: ; pon algo que imprima "Hola" loop THELABEL ; esto deberia imprimir "Hola" ; 100 veces, el loop es igual a decir ; dec cx; jne THELABEL DX Usado para MUL, DIV, IMUL, IDIV como destino o sobrante. MOV BX, 10 MOV AX, MUL bx ; esto multiplica BX por AX y pone el resultado ; en DX:AX DIV BX ; esto divide DX:AX por BX y pone el resultado en AX ; y el sobrante (en este caso CERO) en DX usado para contener direcciones para IN y OUT (como AX). Registros Indice ---------------- DI Usado como contenedor de direccion destino para stos, movs y puede ser usado tambien como registro de desplazamiento. SI Usado como contenedor de direcciones fuente para lods, movs y tambien puede ser usado como registro de desplazamiento. Ejemplo de MOVS: movsb ; mueve lo que esta en [DS:SI] hacia [ES:DI] e incrementa movsw ; DI y SI a uno para movsb y a dos para movsw. Nota: Hasta aqui hemos asumido que la bandera de direccion esta limpia. Si se instala, DI y SI se decrementan en vez de incrementar. cld ; limpia la bandera de direccion std ; instala la bandera de direccion La Pila Relacionada con Registros Indice ----------------------------------------- BP Puntero Base. Puede usarse para acceder a la pila. El segmento por default es SS. Puede usarse para acceder datos en otros segmentos a traves de un segmento override. mov a, [ES:BP] ; mueve un byte del segmento ES, desplazamiento BP ; el segmento override es usado para especificar cual ; de los 4 registros de segmento usar. SP Puntero de Pila. Simplemente eso. Apunta la posicion de la pila donde se esta trabajando. No la alteres a menos que sepas realmente lo que pretendes hacer. Registros de Segmento --------------------- DS Segmento de Datos. Todos los datos leidos son de este segmento apuntado para ser este segmento hasta que se use un segmento override. Usado como segmento fuente para movs y lods. Este segmento tambien puede ser pasado como segmento default porque si no se presenta un segmento override, se asume que DS sea el segmento de donde quieres tomar datos. ES Segmento Extra. Es usado como segmento destino para movs y stos. Puede ser usado como otros segmento, necesitas especificar [ES:XX] para usar este segmento. Existen en los 386 para arriba 2 segmentos mas: FS No hay ninguna razon particular para su nombre, es decir, tenemos CS, DS y ES porque no tener un FS?. Es solo otro segmento. GS Otro segmento, las mismas razones del anterior. Otros que no deberias o no puedes cambiar: ------------------------------------------- CS: Segmento que apunta a la siguiente instruccion, no se puede cambiar directamente. IP: Apuntador de desplazamiento a la proxima instruccion, no se puede acceder la unica forma de cambiar CS o IP podria ser a traves de JMP, CALL o RET. SS: Segmento de Pila. NO juegues con este a menos que sepas lo que haces. Cambiarlo probablemente provocaria que truene la computadora, este es el segmento donde reside la pila. Ya que lo mencionamos... veamos que es la pila: LA PILA es una area de memoria que tiene las caracteristicas de una pila de platos: el ultimo que pones es el primero que sacas. La unica diferencia es la pila de platos esta en el techo de cabeza (ya se que eso no puede pasar a menos que la gravedad se vuelva loca), asi que va bajando con cada plato que ponemos (PUSH) y sube cuando quitamos uno (POP). ________ Tope de la memoria en el segmento de pila (SS) Ü Ü Ü Ü Ü <<< SP (El puntero de pila se~ala el ultimo byte puesto) Realmente no necesitas saber mas que uan pila tiene la propiedad LIFO (Last In First Off) como los platos. Ejemplo: Equivocado: push cx push ax .... pop cx pop ax Correcto: push cx push ax .... pop ax pop cx Vamos a ver los diferentes modos de direccionamiento: mov ax, 5 ; mueve un valor inmediato a ax (piensa en ax = 5) mov bx, cx ; mueve un registro hacia otro registro mov cx, [SI] ; mueve [DS:SI] hacia cx (el segmento default es usado) mov [DI+5], ax ; mueve ax hacia [DS:DI+5] mov [ES:DI+bx+34], al ; igual que arriba, pero tiene un desplazamiento mas ; complicado y un segmento override. mov ax, [546] ; mueve lo que esta en [DS:546] hacia ax Nota que el ultimo ejemplo seria distinto si se quitaran lo corchetes. Esto significaria un valor inmediato de 546 en el segmento default puesto en AX a la vez que el deplazamiento de 546 en el segmento default. Donde veas corchetes alrededor de algo, significara que se refiere a lo que esta en el deplazamiento, por ejemplo, digamos que tienes la siguiente situacion: MiDato dw 55 ... mov ax, [MiDato] Esto esta claramente moviendo lo que esta en la direccion de MiDato, el cual deberia ser 55, y no moviendo el desplazamiento de MiDato por si mismo. Pero que tal si quieres el desplazamiento?, bueno, debes especificar directamente. MiDato dw 55 ... mov ax, OFFSET MiDato Similarmente si quieres lo que esta en el segmento de MiDato harias: MiDato dw 55 ... mov ax, SEG MiDato INT --- Es usada para llamar subrutinas que ejecutan funciones que preferirias no escribirlas tu mismo. Por ejemplo, usarias la interrupcion de video del BIOS para poner el modo de pantalla, mover el cursor o cualquier otra funcion que seria dificil programar. La subrutina que ejecutara la interrupcion esta especificada por AH. Por ejemplo, si quieres imprimir un mensaje en pantalla, usarias la funcion 9 de la interrupcion 21h. mov ah, 9 int 21h Asi de facil. Por supuesto, para que la funcion haga algo, necesitas especificar lo que tiene que imprimir. Esta funcion requiere que hayas puesto a DS:DX un apuntador FAR que apunte a la cadena para desplegar. Esta cadena debe terminar con signo de dolar, por ejemplo: MiMensaje db "Este es el mensaje!. $" ... mov dx, offset MiMensaje mov ax, SEG MiMensaje mov ds, ax mov ah,9 int 21h ... DB, asi como DW y DD declaran el tipo de pieza de codigo. DB -> Declara Byte (piensa como Data Byte) DW -> Declara Word (piensa como Data Word) DD -> Declara Doble (piensa como Data Doble) Tambien te puedes dar cuenta que primero puse el valor del segmento en AX y despues lo puse en DS. Lo hice porque no se permite poner un valor inmediato en un registro de segmento. Puedes de cualquier forma sacar algo hacia un registro de segmento o mover un valor indexado hacia el registro de segmento, por ejemplo: LEGAL: mov ax, SEG MiMensaje mov ds, ax push SEG MiMensaje pop ds mov ds, [SegOfMiMensaje] ; donde [SegOfMiMensaje] ya ha sido cargado con el segmento ; donde reside MiMensaje ILEGAL: mov ds, 10 mov ds, SEG MiMensaje Entrada del Teclado ------------------- Vamos a manejar la interrupcion 16h, y aqui nombramos algunas funciones: 0h Obtiene una tecla del buffer del teclado. Si no hay una, espra hasta que haya una. Lo regresa en ah y su traduccion al ASCII en al. 1h Checa si una tecla esta lista para agarrarse. Pone la bandera en cero si esta lista, se agarra con la funcion 0h, regresa la misma informacion que la funcion 0h pero no la quita del buffer del teclado. 2h Regresa las banderas de shift en al y son: bit 7 Insert Activo bit 6 Caps lock Activo bit 5 Num Lock Activo bit 4 Scroll Lock Activo bit 3 Alt presionado bit 2 Ctrl presionado bit 1 Shift izquierdo presionado bit 0 Shift derecho presionado 3h Se configura la captura y retardo con esta funcion. Los registros deben estar como sigue: al = 5 bh = valor de retardo (0-3 : 250, 500, 750, 1000 millisec) bl = rango de captura (0-1fh) 1fh = mas lento (2 caracteres por segundo) 0 = mas rapido (30 caracteres por segundo) 4h Control del teclado - No es importante 5h El teclado Entrada: CH = codigo SCAN CL = codigo ASCII Salida: AL = 0 Sin Error AL = 1 Buffer de teclado lleno 10h Igual que la 0 pero para teclado extendido. Checa todas las teclas. 11h Igual que la 1, pero para teclado extendido. 12h Igual que la 2 pero AH contiene banderas de shift adicionales: bit 7 - Sys se requiere presionar bit 6 - Caps Lock activo bit 5 Num Lock activa bit 4 Scroll lock activa bit 3 Alt derecho activo bit 2 Ctrl derecho activo bit 1 Alt izquierdo activo bit 0 Alt izquierdo activo AL es exactamente la misma que la funcion 2 Donde AH es el numero de funcion que quieres llamar de la Interrupcion 1h Ahora veremos el control de flujo por medio de comparaciones y saltos. Comparacion (cmp) Es igual que sub, pero no altera ningun registro, solo las banderas, es usado junto con un salto. Hace una comparacion y cambia la bandera dependiendo si es verdadera o falsa la comparacion (si son iguales). Saltos Vamos a dividir esta seccion en tres partes: comparando numeros con signo, comparando numeros sin signo y ambos. Date cuenta que un numero con signo o sin signo depende solo de como lo trates, es por eso que hay diferentes. Sin signo --------- JA - Salta si el primer numero es mayor al segundo JAE - Igual que el anterior, pero salta tambien si son iguales JB - Salta si el primer numero es menor al segundo JBE - (Solo piensa...) JNA - Salta si el primer numero no es mayor (igual a JBE) JNAE - Salta si el primero no es mayor o igual (igual a JB) JNB - Salta si el primer numero no es menor (igual que JAE) JNBE - Salta si el primer numero no es menor o igual (como JA) JZ - Salta si los dos numero son iguales (Bandera Zero = 1). JE - Igual que JZ, solo con otro nombre (Equal). JNZ - Es muy obvio, espero ... JNE - Igual al anterior Con signo --------- JG - Salta si el primer numero es mayor a el segundo numero JGE - Salta si el primer numero es mayor o igual al segundo JL - Salta si el primer numero es menor al segundo numero JLE - mmm... adivina... JNG - Salta si el 1o no es > al 2o (JLE) JNGE - Salta si el 1o no es >= al 2o (JL) JNL - Salta si el 1o no es < al 2o (JGE) JNLE - Salta si el 1o no es <= al 2o (JG) JZ, JE, JNZ, JNE - Igual que los que no tienen signo Ambos ----- JC - Salta si la bandera es puesta. JNC - Imaginatelo... Aqui van los demas, no se usan mucho, pero... JO - Salta si la bandera Overflow es puesta JNO - ... JP - Salta si la bandera de paridad es puesta JNP - ... JPE - Salta si la paridad esta en par (como JP) JPO - Salta si la paridad es non (cmom JNP) JS - Salta si la bandera de signo es puesta JNS - ... Aqui estan las banderas rapidamente: bit # 8 7 6 5 4 3 2 1 0 ######################### Simbolo: O D I T S Z A P C O = Overflow D = * Direction I = Interrupt T = Trap S = Sign (signo) Z = * Zero (cero) A = Auxiliar C = * Carry (acarreo) * Las direcciones que debes conocer. Vamos a la accion: ; /_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ ; / / ; / El programa mas sencillo para desplegar por / ; / pantalla un texto con el formato basico ASM / ; / / ; /_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ dosseg .model small .stack 200h .data hola db 13,10, "Hola. $" .code start: mov ax, @data mov ds, ax mov ah, 9 mov dx, offset hola int 21h mov ax, 4c00h int 21h end start ; /_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ ; / / ; / Este programa hace lo mismo, pero si eres / ; / observador, notaras una diferencia con esos 13, 10, / ; / / ; /_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ dosseg .model small .stack 200h .data hola db 13,10,13,10,13,10, "Hola. $" .code start: mov ax, @data mov ds, ax mov ah, 9 mov dx, offset hola int 21h mov ax, 4c00h int 21h end start @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@