lunes, 19 de enero de 2015

Programa en x86 que se ejecute directamente en una máquina que tenga esa arquitectura

Mostrar por pantalla la tabla de multiplicar de un número dado y su longitud.

Start:
Imprimimos por pantalla el enunciado del problema con la instrucción,
Invoke puts, “Enunciado”
Pedir:
Pedimos por pantalla un número con el cual mostraremos su tabla de multiplicar, Leemos el valor introducido y lo almacenamos en la dirección de memoria de N, realizamos la misma operación con el siguiente invoke, pero esta vez pedimos la longitud de la tabla y la almacenamos en LONGI.
Invoke puts, “número”
Invoke scanf, "%d", Addr N
Invoke puts, “Enunciado”
Invoke scanf, "%d", Addr LONGI

Movemos el valor de [LONGI] al registro Eax, para acceder al contenido de una dirección de memoria, tanto para lectura como para escritura, lo indicaremos encerrando la dirección entre corchetes, sumamos 1 al registro Eax para que no haya desplazamiento y así me imprima la longitud de la cadena introducida, movemos el valor del registro Eax a [LONGI] en este caso 1, y movemos el valor 0 al registro Ebx.

Mov Eax, [LONGI]
add Eax, 1
Mov [LONGI], Eax
Mov Ebx, 0

BUCLE:

En la primera línea del subprograma “BUCLE”, utilizaremos la instrucción CMP junto con el registro EFLAGS, para llevar a cabo las comparaciones realiza la operación operando1 - operando2 y coloca las banderas en función del resultado, Jz salta si ZF = 1, Su valor es 1 si el resultado de la comparación es 0 y 0 en caso contrario.

En este caso como el resultado de la comparación es 1 no realiza el salto, y pasamos a la siguiente instrucción, movemos el contenido del registro [N] a Eax, PushAD introduce en la pila los ocho registros de propósito general, con la instrucción Push, decrementamos el valor del puntero de pila (en ESP) e introduce el operando fuente en la cima de la pila, con estos comandos anteriormente usados estamos usando la pila como un almacén de parámetros para los subprogramas, la instrucción Call consiste en meter en la pila la dirección de retorno y saltar a la primera dirección del subprograma.

El programa llamador(“BUCLE”) es el encargado de eliminar los parámetros que ha introducido en la pila puesto que las funciones de C admiten que el número de parámetros sea indefinido (por ejemplo, printf puede tener tantos parámetros como sea necesario). Para esto, en lugar de pop, utilizaremos la instrucción add, sumando a ESP 4 por cada parámetro introducido.

Es recomendable guardar los valores de aquellos registros que pueden ser modificados en la pila antes de introducir los parámetros del subprograma. En este punto, utilizamos la instrucción PopAD, que introduce y extrae, respectivamente, los ocho registros de propósito general.

Una vez hecho esto, procedemos a introducir los valores calculados en el subprograma fTabla, imprimiendo los resultados por pantalla, incrementamos Ebx en 1, de manera que cuando Ebx tenga el mismo valor que la longitud de nuestra tabla, de esta forma en la comparación inicial nos devolverá un ZF=1, así pues nuestra instrucción  Jz realizara un salto hacia el subprograma HECHO.

Cmp Ebx, [LONGI]
Jz > HECHO
Mov Eax, [N]
PushAD
Push Ebx
Call fTabla
Add Esp, 4
PopAD
Invoke printf, "%d x %d = %d", [N], Ebx, [RESULT]
Invoke puts, ""
Inc Ebx
Jmp BUCLE

fTabla:

Como bien he comentado anteriormente la instrucción Call consiste en meter en la pila la dirección de retorno y saltar a la primera dirección del subprograma anterior una vez ejecutado el bucle fTabla, el cual, al principio del subprograma se debe guardar el contenido de EBP en la pila y copiar el contenido de ESP en EBP. Esto se conoce como “prólogo del subprograma”, para acceder a los parámetros que han sido pasados al subprograma no los sacamos de la pila, sino que utilizaremos EBP, que tras el prólogo del subprograma apunta a la posición de la pila donde está guardado el valor anterior de EBP. Dado que en [EBP] se encuentra el contenido anterior del registro EBP y en [EBP+4] la dirección de retorno, tendremos en [EBP+8] el primer parámetro, en [EBP+12] el segundo, en [EBP+16] el tercero, etc, la siguiente instrucción MUL realiza una multiplicación con el primer dato de nuestra pila, a continuación movemos el registro Eax a [RESULT], al final del subprograma se debe sacar de la pila el valor anterior de EBP con la instrucción Pop (dejando en la cima la dirección de retorno) y almacenarlo de nuevo en EBP. A continuación se invocaría a la instrucción  RET. Esto se conoce como “epílogo del subprograma”, mediante la instrucción  Ret volvemos a nuestro programa llamador,”BUCLE”.

Push Ebp
Mov Ebp, Esp
Mov Ebx, [Esp + 8]
Mul Ebx
Mov [RESULT], Eax
Pop Ebp
Ret


HECHO:

En este subprograma creamos como un pequeño menú en el cual mediante una serie de invokes preguntamos al usuario cual es la siguiente opción que desea realizar, mediante un scanf tomamos el valor introducido el cual nos dirá la opción que desea realizar, movemos el dato introducido del registro OP a Eax, hacemos una comparación lo que quiere decir que restamos el dato introducido a 0, si nos devuelve 1 hacemos un jump al subprograma pedir, el cual volveremos a pedir un nuevo valor con el que trabajar, en caso contrario se finalizara el programa.

Invoke puts, "¿Que desea hacer?"
Invoke puts, "1.Volver a empezar"
Invoke puts, "0.Salir"
Invoke scanf, "%d", Addr OP
Mov Eax, [OP]
Cmp Eax, 0
Jz > salir

Jmp pedir


Isaac Acejo Peña.

No hay comentarios:

Publicar un comentario