¿Por qué aprender ensamblador?
A nosotros nos importa bien poco en qué lenguaje de programación se ha compilado una aplicación, sólo en alguna ocasión será interesante saber en qué lengua se ha escrito algún programa (y tampoco será imprescindible averiguarlo). Todos sabéis que se programe en el lenguaje que se programe la compilación traduce las líneas de código al único código que entiende la máquina, éste código es el ensamblador y es la única lengua que necesitamos conocer tanto para añadir como para quitar código a una aplicación. En esta página tenéis la posibilidad de aprender un poco cada una de las instrucciones más habituales y cómo hacer uso de ellas. Al final disponéis del juego de instrucciones completo, de momento está en Ingles pero lo iremos traduciendo al español, como siempre, cuando tengamos un rato.
Comenzaremos con los registros.
De los registros podíamos estar hablando días y días enteros, pero lo que nos interesa saber de ellos es que son lugares donde guardar y conocer datos. Con ellos podemos sumar, restar, multiplicar, guardar un dato, ejecutar una función determinada de una interrupción, en fin, son imprescindibles. Aquí tienes la lista de algunos registros.
AX Acumulador, de uso general. Se puede dividir en AH y AL. Obtendremos el mismo resultado si hacemos "MOV AX , FF00" que si hacemos primero "MOV AH , FF" y después "MOV AL , 00".
BX Registro base, de uso general. Se puede dividir en BH y BL.
CX Registro contador, de uso general. Se puede dividir en CH y CL.
DX Registro de datos, de uso general. Se puede dividir en DH y DL.
DS Registro del segmento de datos. Utilizado para apuntar a un segmento.
ES Registro del segmento extra. Lo mismo que DS.
SS Registro del segmento de pila. Nos indica el segmento donde se encuentra la pila (mas abajo hablaremos de ella).
CS Registro del segmento de código. Nos indica el segmento donde se encuentra el código que se está ejecutando.
BP Registro de apuntadores base. Digamos que este registro se utiliza para apuntar donde guardamos datos.
SI Registro índice fuente.
DI Registro índice destino.
SP Registro del apuntador de la pila. Este registro nos indica dónde está la pila en la que se guardan los datos que introducimos en ella.
IP Registro que apunta a la siguiente instrucción que se tiene que ejecutar.
F Registro de flags. Este registro es necesario leerlo en binario. Cada bit representa el resultado de alguna operación, por ejemplo si comparamos dos números este en este registro encontramos información sobre si uno es menor o mayor que el otro o si son iguales, etc. Los flags (o banderas) son los siguientes CF, PF, AF, ZF, SF, TF, IF y OF.
Antes de continuar hablaremos de la instrucción NOP. Esta instrucción es nula, o sea que no hace absolutamente nada. Es utilizada para provocar un retraso en la CPU y nosotros la utilizamos PARA ELIMINAR EL CODIGO QUE NO QUEREMOS QUE SE EJECUTE (una forma de parchear) cambiando el código que existe por tantos nop's como bytes queremos eliminar.
¿Qué es la pila?
Ahora que sabemos como guardar un dato determinado usando los registros, nos damos cuenta de que en ocasiones necesitaremos guardar mas datos que registros tenemos. ¿Como solucionamos esto? pues con el uso de la pila. Supongamos que debemos de guardar 10 números y solo disponemos de 4 registros de uso general, la solución es hacer los siguiente:
MOV AX , PRIMER_NUMERO..... <= cargamos en ax el número
PUSH AX ....................................... <= y lo dejamos caer en la pila
MOV AX , SEGUNDO_NUMERO. <= cargamos en ax el siguiente nº
PUSH AX........................................ <= y también lo descargamos en la pila.
.... así hasta terminar de descargar nuestros datos. ¿Donde se han guardado? pues en SS:SP.
Para recuperar lo introducido en la pila, hay que saber que siempre sale primero el último dato que entra, este es el motivo de que se le llame pila, se apilan los datos. Imagina que amontonas platos uno encima de otro... para sacar el primer plato que has puesto debes de sacarlos TODOS. Este es el concepto de la pila.
POP AX <= Recupera de la pila EL ULTIMO DATO INTRODUCIDO y lo deposita en el registro AX.
Bien... Ya hemos visto que son los registro, ahora vamos a ver como los guardamos en la pila y como los recuperamos de ella...
PUSH REGISTRO..... <= Guarda en la pila un registro.
PUSH NUMERO........ <= Guarda en la pila un número.
PUSHF....................... <= Guarda en la pila los flag.
PUSHA...................... <= Guarda en la pila los registros AX, CX, DX, BX, SP, BP, SI y DI en este mismo orden.
POPA........................ <= Recupera de la pila los registros DI, SI, PB, SP, BX, DX, CX y AX en este mismo orden.
POPF......................... <= Recupera de la pila los flags.
POP REGISTRO....... <= recupera un dato de la pila y lo deposita en "REGISTRO".
Instrucciones de salto
Si has programado alguna vez en cualquier Básic, sabrás que las instrucciones de salto tales como goto y gosub son la madre de cualquier programa. En ensamblador es lo mismo, solo que tenemos muchisimas más posibilidades.
JMP DIRECCION.... <= Hace que el programa salte a dirección. Sería como el goto del Básic.
CALL DIRECCION. <= Hace que el programa salte a dirección para ejecutar un fragmento de código y cuando termina retorna a la siguiente instrucción despues de call con RET. Sería como el gosub del Básic.
Después de una instrucción de comparación de 2 números podemos hacer saltos condicionados:
CMP AX , BX............ <= Compara el registro ax con bx.
JE DIRECCION......... <= Salta a dirección si los registros son iguales.
CMP AX , NUMERO. <= Compara ax con número.
JA DIRECCION......... <= Salta si ax es mayor que número.
JAE DIRECCION....... <= Salta si ax es mayor o igual que número.
JB DIRECCION.......... <= Salta si ax es menor que número.
JBE DIRECCION....... <= Salta si ax es menor o igual que número.
JA DIRECCION.......... <= Salta si ax es mayor o igual que número.
JNE DIRECCION....... <= Salta si no son iguales.
JCXZ DIRECCION..... <= Salta si CX es cero.
Instrucción nula
¿para qué sirve una instrucción que no hace nada? NOP es una instrucción cuya misión es la de provocar un retraso en la CPU, es frecuente verla en fragmentos de código donde se hacen operaciones de lectura/escritura en puertos para darle tiempo a aceptar otra instrucción al periferico al que se le hace la solicitud. Esta instrucción es muy útil a la hora de parchear un programa, cuando hay un fragmento de código que no queremos que se ejecute simplemente NOPEAMOS los bytes que nos interesa y eliminamos lo que no nos interesa.
Instrucciones aritméticas
Hay muchas instrucciones que nos permiten sumar, restar, multiplicar, dividir etc. pero solo mencionaré las de uso mas frecuente.
ADD AX , BX................. <= Esta instrucción la leeremos como "súmale a ax el contenido de bx y depositalo en ax". Sería como a=a+b en Básic.
SUB AX , NUMERO........ <= Es lo mismo que la instrucción anterior pero restando.
INC BX............................ <= Incrementa en uno el contenido de bx.
DEC CX........................... <= Decrementa en uno el contenido de cx.
MUL BX.......................... <= Atención, para multiplicar un número pondremos en el registro ax uno de los números a multiplicar, a continuación hacemos mul destino donde destino ha de ser bx, cx ó dx y además contendrá el otro número por el que hay que multiplicar ax. En Básic A=A*B.
DIV BX........................... <= Funciona igual mul pero para dividir.
Instrucciones de bucle
Si necesitamos repetir un fragmento de código cualquier cantidad de veces usaremos los bucles que son como los for-next del Básic.
MOV CX , REPETICION...<= Carga en cx la cantidad de veces que se repetirá el bucle.
bucle:...........................<= Esto es una etiqueta.
... ................................<= Instrucciones a ejecutar.
... ................................<= Instrucciones a ejecutar.
LOOP bucle.....................<= Reduce el valor de cx en uno y salta a bucle mientras cx sea mayor que cero.
Instrucciones de lectura/escritura de puertos
Cada dispositivo que está conectado a nuestro ordenador se comunica con él a través de un puerto. Habras oido hablar del puerto serie o el paralelo. Piensa en los puertos como en lugares a donde ha de llegar un dato o donde podremos encontrar un dato. Para enviar un dato a un puerto se utilizan las siguientes instrucciones:
MOV DX , NUMERO_DE_PUERTO
MOV AX , NUMERO_A_ENVIAR
OUT DX , AX
Y para leer un dato de un puerto:
MOV DX , NUMERO_DE_PUERTO
IN AX , DX
Juego completo (con tiempo iremos traduciendo y comentando todas las instrucciones)
AAA - Ascii Adjust for Addition
AAD - Ascii Adjust for Division
AAM - Ascii Adjust for Multiplication
AAS - Ascii Adjust for Subtraction
ADC - Add With Carry
ADD - Arithmetic Addition
AND - Logical And
ARPL - Adjusted Requested Privilege Level of Selector (286+ PM)
BOUND - Array Index Bound Check (80188+)
BSF - Bit Scan Forward (386+)
BSR - Bit Scan Reverse (386+)
BSWAP - Byte Swap (486+)
BT - Bit Test (386+)
BTC - Bit Test with Compliment (386+)
BTR - Bit Test with Reset (386+)
BTS - Bit Test and Set (386+)
CALL - Procedure Call
CBW - Convert Byte to Word
CDQ - Convert Double to Quad (386+)
CLC - Clear Carry
CLD - Clear Direction Flag
CLI - Clear Interrupt Flag (disable)
CLTS - Clear Task Switched Flag (286+ privileged)
CMC - Complement Carry Flag
CMP - Compare
CMPS - Compare String (Byte, Word or Doubleword)
CMPXCHG - Compare and Exchange
CWD - Convert Word to Doubleword
CWDE - Convert Word to Extended Doubleword (386+)
DAA - Decimal Adjust for Addition
DAS - Decimal Adjust for Subtraction
DEC - Decrement
DIV - Divide
ENTER - Make Stack Frame (80188+)
ESC - Escape
HLT - Halt CPU
IDIV - Signed Integer Division
IMUL - Signed Multiply
IN - Input Byte or Word From Port
INC - Increment
INS - Input String from Port (80188+)
INT - Interrupt
INTO - Interrupt on Overflow
INVD - Invalidate Cache (486+)
INVLPG - Invalidate Translation Look-Aside Buffer Entry (486+)
IRET/IRETD - Interrupt Return
Jxx - Jump Instructions Table
JCXZ/JECXZ - Jump if Register (E)CX is Zero
JMP - Unconditional Jump
LAHF - Load Register AH From Flags
LAR - Load Access Rights (286+ protected)
LDS - Load Pointer Using DS
LEA - Load Effective Address
LEAVE - Restore Stack for Procedure Exit (80188+)
LES - Load Pointer Using ES
LFS - Load Pointer Using FS (386+)
LGDT - Load Global Descriptor Table (286+ privileged)
LIDT - Load Interrupt Descriptor Table (286+ privileged)
LGS - Load Pointer Using GS (386+)
LLDT - Load Local Descriptor Table (286+ privileged)
LMSW - Load Machine Status Word (286+ privileged)
LOCK - Lock Bus
LODS - Load String (Byte, Word or Double)
LOOP - Decrement CX and Loop if CX Not Zero
LOOPE/LOOPZ - Loop While Equal / Loop While Zero
LOOPNZ/LOOPNE - Loop While Not Zero / Loop While Not Equal
LSL - Load Segment Limit (286+ protected)
LSS - Load Pointer Using SS (386+)
LTR - Load Task Register (286+ privileged)
MOV - Move Byte or Word
MOVS - Move String (Byte or Word)
MOVSX - Move with Sign Extend (386+)
MOVZX - Move with Zero Extend (386+)
MUL - Unsigned Multiply
NEG - Two's Complement Negation
NOP - No Operation (90h)
NOT - One's Compliment Negation (Logical NOT)
OR - Inclusive Logical OR
OUT - Output Data to Port
OUTS - Output String to Port (80188+)
POP - Pop Word off Stack
POPA/POPAD - Pop All Registers onto Stack (80188+)
POPF/POPFD - Pop Flags off Stack
PUSH - Push Word onto Stack
PUSHA/PUSHAD - Push All Registers onto Stack (80188+)
PUSHF/PUSHFD - Push Flags onto Stack
RCL - Rotate Through Carry Left
RCR - Rotate Through Carry Right
REP - Repeat String Operation
REPE/REPZ - Repeat Equal / Repeat Zero
REPNE/REPNZ - Repeat Not Equal / Repeat Not Zero
RET/RETF - Return From Procedure
ROL - Rotate Left
ROR - Rotate Right
SAHF - Store AH Register into FLAGS
SAL/SHL - Shift Arithmetic Left / Shift Logical Left
SAR - Shift Arithmetic Right
SBB - Subtract with Borrow/Carry
SCAS - Scan String (Byte, Word or Doubleword)
SETAE/SETNB - Set if Above or Equal / Set if Not Below (386+)
SETB/SETNAE - Set if Below / Set if Not Above or Equal (386+)
SETBE/SETNA - Set if Below or Equal / Set if Not Above (386+)
SETE/SETZ - Set if Equal / Set if Zero (386+)
SETNE/SETNZ - Set if Not Equal / Set if Not Zero (386+)
SETL/SETNGE - Set if Less / Set if Not Greater or Equal (386+)
SETGE/SETNL - Set if Greater or Equal / Set if Not Less (386+)
SETLE/SETNG - Set if Less or Equal / Set if Not greater or Equal
SETG/SETNLE - Set if Greater / Set if Not Less or Equal (386+)
SETS - Set if Signed (386+)
SETNS - Set if Not Signed (386+)
SETC - Set if Carry (386+)
SETNC - Set if Not Carry (386+)
SETO - Set if Overflow (386+)
SETNO - Set if Not Overflow (386+)
SETP/SETPE - Set if Parity / Set if Parity Even (386+)
SETNP/SETPO - Set if No Parity / Set if Parity Odd (386+)
SGDT - Store Global Descriptor Table (286+ privileged)
SIDT - Store Interrupt Descriptor Table (286+ privileged)
SHL - Shift Logical Left
SHR - Shift Logical Right
SHLD/SHRD - Double Precision Shift (386+)
SLDT - Store Local Descriptor Table (286+ privileged)
SMSW - Store Machine Status Word (286+ privileged)
STC - Set Carry
STD - Set Direction Flag
STI - Set Interrupt Flag (Enable Interrupts)
STOS - Store String (Byte, Word or Doubleword)
STR - Store Task Register (286+ privileged)
SUB - Subtract
TEST - Test For Bit Pattern
VERR - Verify Read (286+ protected)
VERW - Verify Write (286+ protected)
WAIT/FWAIT - Event Wait
WBINVD - Write-Back and Invalidate Cache (486+)
XCHG - Exchange
XLAT/XLATB - Translate
XOR - Exclusive OR
sábado, 12 de marzo de 2011
Suscribirse a:
Enviar comentarios (Atom)
0 comentarios:
Publicar un comentario