Trasteando con ensamblador x86 - Parte 1 -
Algunas preguntas antes de empezar:
1 - Que es x86?¿?
x86 es el nombre con el que se conoce la familia de processadores utilizada para el entorno PC y desarollada por Intel a finales de los 70 con el procesador 8086.
2 - Que es un ensamblador?¿?
El ensamblador es un "lenguaje de programación" de bajo nivel especifico para cada arquitectura que permite programar aprovechando al 100% todas las posibilidades del procesador. Actualmente se usa para casos especificos como apoyo en la programación de alto nivel y es una de las formas de aumentar la eficiencia de los programas. (Lo se me dejado muchas cosas xD)
3 - Algo de historia.
En los principios y dejando de lado años, Intel empezo esa arquitectura con unos chips que operaban a 8 bits luego vinieron los 16 bits y aqui viene lo importante a partir del 80386 empezaron con la actual arquitectura x86 mas concretamente con lo que se denomina IA32 (Inter Arquitecture 32 bits). Esta es en la que nos vamos a centrar.
4 - Empecemos con el ensamblador
Disponemos de 8 registros visibles de 32 bits ( %eax - %ebx - %ecx - %edx - %esi - %edi - %ebp - %esp), 8 registros (son los mismos que antes pero partios por el word bajo) de 16 bits (%ax, %bx, %cx ....) que se pueden separar en dos registros de 8 bits (%ah "para la parte alta" %al "para la parte baja")
NOTA: tanto %ebp como %esp son para uso especifico y mas adelante os dire para que sirven sino lo sabeis ya ;)
Recordad que IA32 usa el formato Little Endian es decir la parte baja de la palabra va en la @ más pequeña
Ejemplo
[Si tenemos un dato de 32 bits 0x12345678 la memoria quedara asi
MEM[0x100] --> 78 MEM[0x101] --> 56 MEM[0x102] --> 34 MEM[0x103] --> 12]
. Una vez aclarado esto decir que IA32 es tipo CISC (Complex Instruction Set Computer) es decir podemos tener accesos a memoria en instrucciones (no hace falta cargar ni restaurar valores antes de operar) " en realidad lo que hace el processador es traducir a un lenguaje pseudoRISC para permitir la segmentacion de las instrucciones pero eso es un tema del que algun dia hablaré.Vamos a empezar con las instrucciones.
add[lwb] donde los parametros entre [] son el tipo de acceso "word (16bits) long (32bits), byte (8bits)"
La instruccion addl %eax, %ebx hara esto ---> ebx = eax + ebx
Pero eso no es todo, si recordais, antes he dicho que se puede acceder a memoria en la misma instruccion (no necesitas cargar ni restaurar valores). Un ejemplo seria addl x(%edx,%esi,y),%ebx , esto sumaria el valor de la direccion calculada por x(edx,esi,y) con el de ebx y lo restauraria en %ebx. Donde el parametro "x" es el desplazamiento por base "y" es los bytes a acceder, edx es el registro base y esi es el registro indice. (Esto lo aclaramos con un
Ejemplo:
[ Si hicieramos movl 0(%eax,%esi,4),%ebx con los siguientes valores en los registros %eax --> 0x00004216 %esi --> 2 %ebx, 0x00001234
tendriamos al ejecutar la instruccion que el valor de l'@ base (%eax + %esi*4) + 0 = 0x00004216 + 8 = MEM[0x0000421E ] --> ebx ] este ejemplo guardadlo para cuando toque recorrer vectores y estructuras lineales.
Ahora estareis pensando (este chaval es tonto o algo nos pone cosas que no explica que co... es movl?¿?) pues muy facil movl (y mas concretamente mov) es una instruccion para mover datos (WEEE bienn!!! que listo es el tio. xD)
Pues eso mov[wlb] %eax,%ebx hace algo como poner el valor de %eax a %ebx (mas adelante usare esta nomenclatura %ebx <--- %eax) y ahora fijo fijo que pensais se puede mezclar asignacion a memoria en esa instruccion?¿? pues la respuesta es SI como en todas las instrucciones. Es decir algo como el ejemplo anterior seria valido xD.
Y si solo quiero saber la dirección de una variable que hago?¿? Pues muy facil hay una instruccion especifica para hacer esto 'leal' vamos a ver-la con un sencillo ejemplo:
leal op1,op2 haria op2 <-- &op1 (en op2 hay la direccion "&" de op1)
Vale muy bien y ahora como multiplico y divido?¿?
Pues muy facil, tambien hay instrucciones para eso
imu[wlb] op1, op2 donde op2 <-- op2 * op1
idiv[wlb] op1,op2 donde op2 <-- op2 / op1
Aqui os dejo un resumen con las instrucciones aritmeticas mas comunes y que funcionan de forma similar.
add[wlb] op1, op2 donde op2 <-- op2 + op1
sub[wlb] op1,op2 donde op2 <-- op2 - op1
imul[wlb] op1,op2 donde op2 <-- op2 * op1
idiv[wlb] op1,op2 donde op2 <-- op2 /op1
inc[wlb] op1 donde op1 <-- op1 + 1
dec[wlb] op1 donde op1 <-- op1 - 1
FInal:
Pues de momento eso es todo en otras Partes os ensenyare a tratar los bits (operaciones logicas), control de flujo en ensamblador (codificacion de sentencias if while etc...) y alguna cosa mas complicada.
Por cierto os dejo algunos ejercicios para que pensais y haber si abeis cojido algo de lo que os he explicado ;)
1 - Si tenemos una instruccion como:
addb 0(%eax, %esi,1) , %ebx
donde %eax vale 0x00002341, %esi vale 4 y %ebx no importa decidme:
a) Que valor habra en %ebx?¿?
b) Que tipo de recorrido os recuerda?¿? (Imaginacion es la palabra) xD
2 - Si tenemos este codigo:
leal (%eax,%esi, 4),%ebx
movl (%ebx, , ), %ecx
subl %ecx,%edx
NOTA: Este codigo se puede hacer mas corto es solo un ejemplo para entrenar-os. Comentar que si no se pone nada en (regbase, ,) no importa ya que es como sino estubiera.
donde %eax <-- 0x00001000 %esi <-- 5 %ebx & %ecx & %edx<-- indefinido
a) Que hace la primera instruccion?¿? y la segunda?¿? y la tercera?¿?
b) Quanto valdra %edx al final?¿?
c) Como hariais mas pequeño el codigo?¿ (tiene que hacer lo mismo )
Para posibles soluciones o correcciones postead un comentario con vuestra direccion de e-mail con la pregunta/s que no entendais y os explicare la solucion. Saludos y hasta la proxima.
