Sunteți pe pagina 1din 137

LENGUAJE MQUINA

Cada tipo de CPU entiende su propio lenguaje de m quina. Las instrucciones en lenguaje de mquina son n meros almacenados como bytes en memoria. Cada instruccin tiene su propio y nico cdigo llamado cdigo de operacin u opcode. Las instrucciones del procesador 80X86 varan de tamao. El opcode est siempre al inicio de la instruccin. Muchas instrucciones incluyen tambin datos (ver constantes o direcciones) usados por las instrucciones. El lenguaje de mquina es muy difcil de programar directamente. Descifrar el significado de las instrucciones codificadas numricamente es te dioso para los humanos. Por ejemplo la instruccin para sumar los registros EAX y EBX y almacenar el resultado en EAX est codificada por los siguientes cdigos hexadecimales: 03 C3 Esto no es obvio. Afortunadamente, un programa llamado ensamblador puede hacer este aburrido trabajo para el programador.

LENGUAJE ENSAMBLADOR
Un programa Escrito en lenguaje ensamblador es almacenado como texto (tal como programas de alto nivel). Cada instruccin representa exactamente una instruccin de la mquina. Por ejemplo, la instruccin de suma descrita arriba podr ser representada en lenguaje ensamblador como: Mnemonico operando(s)

PROGRAMACIN EN ENSAMBLADOR USANDO LA SINTAXIS GAS EN ARQUITECTURAS x86


Las instrucciones que hacen uso de la sintaxis GAS tiene la forma: Mnemonico Por ejemplo: movb $0x05, %al La cual est relacionada con un direccionamiento inmediato del dato 5h al registro al. Cuando se hace referencia a un registro, dicho registro requiere ser prefijado con el signo de %. Los tipos de datos constantes sern antecedidos por el carcter $. Algunas caractersticas de la sintaxis de GAS se relacionan con la sintaxis del operando de direcciones: Se permiten hasta 4 parmetros de un operando de direcciones que se presentan en la siguiente sintaxis de desplazamiento (registro base, registro de offset, multiplicador escalar). Esto es equivalente a la sintaxis de Intel [registro base + desplazamiento + registro de offset * multiplicador escalar ], las diferentes forma de implementacin se ilustran a continuacin:
movl movl movl leal leal -4(%ebp, %edx, 4), %eax -4(%ebp), %eax (%ecx), %edx 8(,%eax,4), %eax (%eax,%eax,2), %eax # ejemplo completo: load *(ebp - 4 + (edx * 4)) into eax # ejemplo tpico: carga una variable del stack en eax # sin offset: copiar la salida de un apuntador a un registro # Aritmtica: multiplicar eax por 4 y sumarle 8 # Aritmtica: multiplicar eax por 2 and sumarle eax (esto es multiplicar por 3)

dato_fuente, dato_destino

GENERANDO CDIGO ENSAMBLADOR A PARTIR DE CDIGO EN C


Debido a que el lenguaje ensamblador se corresponde de forma directa a las operaciones que la unidad de procesamiento realiza, una rutina en ensamblador cuidadosamente escrita puede tener la capacidad de correr mucho ms rpidamente que la misma rutina escrita en un lenguaje de alto nivel, tal como el lenguaje C. En contra parte, las rutinas en lenguaje ensamblador por lo regular requieren de mayor esfuerzo a ser escritas que su equivalente en lenguaje C. Una forma de conocer el funcionamiento de una rutina en ensamblador, haciendo uso del compilador de C, es escribindola en C y generar su similar en ensamblador haciendo uso del compilador mismo. Tal como se muestra a continuacin: #include <stdio.h> int main(void) { printf("Hello, world!\n"); return 0; }

Dicho programa es muy simple pero nos permitir demostrar su traduccin a lenguaje ensamblador. Para ello, la compilacin se realizar de la siguiente forma: $ gcc -S hello.c Lo cual deber de crear un archivo de nombre hello.s (la extensin .s, proviene de la filosofa de los proyectos GNU). Una vez realizado lo anterior se procede a compilar dicho archivo en ensamblador para generar el archivo ejecutable. $ gcc -o hello.exe hello.s

Posterior a ello se tendr que realizar la ejecucin del programa correspondiente. Lo interesante de este programa est en visualizar el contenido del archivo en ensamblador obtenido despus de la compilacin correspondiente.

PROGRAMAS EN ENSAMBLADOR UTILIZANDO DDD COMO DEPURADOR, ESTRUCTURA BSICA DE UN PROGRAMA


1. /* 2. Este es un tipo de comentario 3. */ 4. #Otro tipo de comentario 5. #nombre de archivo (opcional) 6. .file "Ejemplo1.s" 7. #secciones 8. .section .data 9. # <- datos 10. # <- datos 11. #PROGRAMA 12. .section .text 13. .global _start 14. _start: 15. #FINALIZA PROGRAMA 16. xorl %eax, %eax 17. incl %eax 18. xorl %ebx, %ebx 19. int $0x80 20 .end

.text

# section declaration

# we must export the entry point to the ELF linker or .global _start # loader. They conventionally recognize _start as their # entry point. Use ld -e foo to override the default. _start:

.text .global _start _start: movl $len, %edx movl $msg, %ecx movl $1, %ebx movl $4, %eax int $0x80 movl $0, %ebx movl $1, %eax int $0x80 .data msg: .ascii "hola mundo!\n" len = 12

# write our string to stdout movl$len,%edx movl$msg,%ecx movl$1,%ebx movl$4,%eax int $0x80 # and exit movl$0,%ebx movl$1,%eax int $0x80 .data # first argument: exit code # system call number (sys_exit) # call kernel # section declaration # third argument: message length # second argument: pointer to message to write # first argument: file handle (stdout) # system call number (sys_write) # call kernel

msg: .ascii "Hello, world!\n" # our dear string len = . - msg # length of our dear str

ESTRUCTURA EN PSEUDOCDIGO
Por ms trivial que parezca el problema, siempre resulta til hacer el esquema de ejecucin a modo de pseudocdigo. De esta forma garantizamos, en primer lugar, una buena lgica de programa, y en segundo, reducir la labor de programacin a una simple traduccin de un lenguaje al cual ya estamos familiarizados. Como primer intento podramos hablar de un pseudocdigo de alto nivel de este tipo.

Ahora bien, si tenemos en cuenta que Assembler es un lenguaje donde las operaciones son predominantemente binarias, valdr la pena convertir nuestro algoritmo a uno que slo requiera operaciones de ste tipo.

VARIABLES EN LENGUAJE ENSAMBLADOR


Algo importante en la programacin en ensamblador es tener claras las variables que se utilizan. Para esto, se debe tener en cuenta lo siguiente: 1. Nombre de la Variable: Toda variable debe tener nombre. Debe ser fcil de recordar ya que el orden es muy importante en la programacin en ensamblador. 2. Tamao de la variable: Definir de que tamao es la variable (64 bits, 32 bits, 16 bits, 8 bits o un espacio de memoria de N Bytes) Segn nuestro algoritmo necesitaremos 6 espacios de memoria. Cinco para los nmeros que necesitamos sumar y un espacio para el acumulador. El Procesador Intel posee espacios para almacenar variables, estos espacios se denominan Registros. Las ventajas que nos ofrece la utilizacin de registros es la velocidad de acceso, por esto es preferible que los datos que se utilicen con mayor frecuencia se alojen en registros. Los registros de Intel que podemos utilizar se organizan de esta forma: Registros de 32 bits EAX, EBX, ECX, EDX Registros de 16 bits Corresponden a la mitad derecha de los registros de 32 bits, y son: AX, BX, CX, DX Registros de 8 bits Corresponden a cada una de las mitades de los registros de 16 (H para High, L para Low) y son: AL, AH, BL, BH, CL, CH, DL, DH

Conociendo esto, alojaremos nuestras variables de la siguiente forma.


Las Variables en memoria se definen en el rea de datos as: <label>: <tama~o> <valor inicial> Aqui label sirve como el nombre de la variable, es decir, el nombre con el cual haremos referencia al valor guardado. El tamao se define como .long para 32 bits, .word para 16 bits y .byte para 8 bits. Los valores iniciales equivalen a los nmeros que tendr el programa. Por defecto se leen en decimal pero pueden escribirse en binario (inicializando la cadena con 0b), en hexadecimal (inicializando la cadena con 0x) o en octal inicializndola en 0. Ya con esto podemos escribir nuestra seccin .data para alojar las variables a las que hallaremos la sumatoria (alojaremos 100, 100, 100, 100, -100). .section .data Numero1: .long 100 Numero2: .long 0144 Numero3: .long 0x64 Numero4: .long 0b1100100 Numero5: .long -100

PROGRAMA EN ENSAMBLADOR DE EJEMPLO


.file "Sumatoria.s" .section .data Numero1: .long 100 Numero2: .long 0144 Numero3: .long 0x64 Numero4: .long 0b1100100 Numero5: .long -100 .global _start .text _start: clrl %eax addl Numero1,%eax addl Numero2,%eax addl Numero3,%eax addl Numero4,%eax addl Numero5,%eax xorl %eax, %eax incl %eax xorl %ebx, %ebx int $0x80 .end

FORMA DE COMPILACIN DEL PROGRAMA

GAS (Gnu ASsembler) utiliza la sintaxis de AT&T, que tiene pequeas diferencias con respecto a la sintaxis estndar de Intel (usada en NASM, TASM, MASM, etc). Las principales diferencias se detallan a continuacin:

En AT&T, el destino se coloca a la derecha y el fuente a la izquierda (en Intel es al revs). Las siguientes instrucciones cargan en ebx el valor de eax AT&T: movl %eax, %ebx INTEL: mov ebx, eax En AT&T, a los valores inmediatos se les aade el prefijo $ en el siguiente ejemplo, la primera instruccin carga la direccin de la variable en eax; la segunda carga el valor 0F02h en ebx AT&T: movl $var, %eax movl $0xf02, %ebx INTEL: mov eax, offset var mov ebx, 0f02h

En AT&T, el tamao del resultado se especifica con sufijos (b, w o l) en las instrucciones (en Intel cuando hay ambigedad se utiliza byte ptr, word ptr o dword ptr). Si lo omitimos, GAS intentar adivinar el tamao, y es algo que no queremos que haga... AT&T: movb var, %ah movw %bx, %ax movb movw movl movl INTEL: mov ah, byte ptr var mov ax, bx

AT&T: %bl,%al %bx, %ax %ebx,%eax (%ebx),%eax

INTEL: mov al,bl mov ax, bx mov eax,ebx mov eax, dword ptr [ebx]

Direccionamiento a memoria: Es uno de los aspectos que ms cambian. Veamos la sintaxis de Intel para hacer un direccionamiento a base, con ndice y desplazamiento:

[ base + ndice*escala + desplazamiento ]


en la sintaxis AT&T esto queda como sigue: desplazamiento ( base , ndice , escala )

Veamos dos ejemplos: AT&T: movl array (, %eax, 4), %edx INTEL: mov edx, array[eax*4]

AT&T: movl (%ebx) , %eax movl 3(%ebx) , %eax Salto lejano AT&T: lcall $seccin, $offset ljmp $seccin, $offset lret $V

INTEL: mov eax , [ebx] mov eax,[ebx+3]

INTEL: call far seccin:offset jmp far seccin:offset ret far V

Nemotcnico. Varan los nemotcnicos de algunas instrucciones AT&T: AT&T INTEL movswl %ax, %ecx movsx ecx, ax movzbw %ah, %cx movzx cx, ah cbtw cbw cwtl cwde cwtd cwd cltd cdq

Directivas del compilador. Como vimos, los programas ensamblador, adems de las instrucciones que componen el programa, contienen rdenes al compilador que le servirn para definir las secciones del programa, definir los tipos de datos, macros, etc. Como comentamos ms arriba, hay diferencias en cuanto a algunas directivas al programar con el ensamblador GAS o NASM. En ambos ensambladores hay que definir las secciones de datos y cdigo utilizando los mismos nombres (.data .bss .text). Sin embargo, la directiva utilizada para definir las secciones difiere de un ensamblador a otro:

En ambos ensambladores, la etiqueta de entrada al programa ensamblador suele ser _start. Sin embargo, la directiva utilizada difiere de un ensamblador a otro:

La definicin de datos constantes se lleva a cabo utilizando de la misma forma, pero utilizando palabras reservadas diferentes:

USO DE MACROS EN GAS


Podemos hacer uso de macros para facilitar la programacin. Para ello, debemos utilizar la directiva .macro de la siguiente forma: .macro nombreMacro instrucciones .endm

En el ejemplo definiremos una macro para terminar el programa, otra para mostrar una cadena por salida estndar, y otra para leer cadenas de texto desde entrada estndar.
La forma de llamar a una macro es parecida a como se llama a una funcin de C/C++ (por la forma en que le pasaremos los valores): # # # COMPILAR: as -o m.o m.s ls -o m m.o

.macro terminar movl $1,%eax movl $0,%ebx int $0x80 .endm #espera ECX=cadena ; EDX=longitud
.macro escribir_cadena cadena longitud movl $4,%eax movl $1,%ebx #stdout movl \cadena,%ecx movl \longitud,%edx int $0x80 .endm #espera ECX=cadena ; EDX=longitud .macro leer_cadena cadena longitud movl $3,%eax movl $0,%ebx #stdin movl \cadena,%ecx movl \longitud,%edx int $0x80 .data retorno: .byte 0x0A mensaje1: .ascii "\n Introduce una cadena: " longitud1 = . - mensaje1 buffer: .ascii " " .text .globl _start _start: escribir_cadena $mensaje1 $longitud1 leer_cadena $buffer $10 escribir_cadena $retorno $1 escribir_cadena $buffer $10 escribir_cadena $retorno $1 terminar

Podemos hacer uso de funciones para facilitar la programacin. Dentro de la seccin .text (y antes del punto de entrada al programa) podemos definir las diferentes funciones (subrutinas), utilizando una etiqueta que indiquen el inicio de la funcin y cuidando siempre terminar la ejecucin de sta con la instruccin ret. Una funcin tendr el siguiente aspecto: nombreFuncion: instrucciones ret Veamos el ejemplo anterior (el de las macros) utilizando tres subrutinas. Como se ver en el programa principal, el paso de parmetros a la funcin hay que hacerlo a travs de la pila o en los registros o variables globales del programa (segn como se haya programado la subrutina):

.data retorno: .byte 0x0A mensaje1: .ascii "\n Introduce una cadena: " longitud1 = . - mensaje1 buffer: .ascii " " .text .globl _start funcion_terminar: movl $1,%eax movl $0,%ebx int $0x80 ret #parmetros ECX=cadena ; EDX=longitud funcion_escribir_cadena: movl $4,%eax movl $1,%ebx #stdout int $0x80 ret #parmetros ECX=cadena ; EDX=longitud

funcion_leer_cadena: movl $3,%eax movl $0,%ebx #stdin int $0x80 ret _start: #los parmetros se pasan en los registros movl $mensaje1,%ecx movl $longitud1,%edx call funcion_escribir_cadena movl $buffer,%ecx movl $10,%edx call funcion_leer_cadena movl $retorno,%ecx movl $1,%edx call funcion_escribir_cadena movl $buffer,%ecx movl $10,%edx call funcion_escribir_cadena movl $retorno,%ecx movl $1,%edx call funcion_escribir_cadena #esta ltima no necesita ningn parmetro call funcion_terminar

NASM
Una directiva es un artificio del ensamblador no de la CPU. Ellas se usan generalmente para decirle al ensamblador que haga alguna cosa o informarle al ensamblador de algo. Ellas no se traducen en cdigo de mquina. Los usos comunes de las directivas son:

Definir constantes Definir memoria para almacenar datos en ella Definir la memoria para almacenar datos en ella Agrupar la memoria en segmentos Incluir cdigo fuente condicionalmente Incluir otros archivos
El cdigo de NASM pasa a travs de un preprocesador tal como C. Tiene muchas de las rdenes del preprocesador tal como C. Sin embargo las directivas del preprocesador de NASM comienzan con un como en C.

directiva equ
La directiva equ se puede usar para definir un smbolo. Los smbolos son constantes con nombre que se pueden emplear en el programa ensamblador. El formato es: smbolo equ valor Los valores de los smbolos no se pueden redefinir posteriormente.

La directiva %define
Esta directiva es parecida a la #define de C. Se usa normalmente para definir macros tal como en C. %define SIZE 100 mov eax, SIZE El cdigo de arriba define un macro llamado size y muestra su uso en una instruccin MOV. Los macros son ms flexibles que los smbolos de dos maneras. Los macros se pueden redefinir y pueden ser ms que simples constantes numricas.

Las directivas de datos son usadas en segmentos de datos para definir espacios de memoria. Hay dos formas en que la memoria puede ser reservada. La primera es solo definir el espacio para los datos; la segunda manera define el espacio y el valor inicial. El primer mtodo usa una de las directivas RESX. La X se reemplaza con una letra que determina el tamao del objeto (u objetos) que sern almacenados. La tabla 1.3 muestra los valores posibles. El segundo mtodo (que define un valor inicial tambin) usa una de las directivas DX. Las X son las mismas que las de la directiva RESX.

Las comillas dobles o simples se interpretan igual. Las definiciones consecutivas de datos se almacenan secuencialmente en memoria. Esto es, la palabra L2 se almacena inmediatamente despus que la L1. Se pueden definir tambin secuencias de memoria. L9 L10 L11 db db db 0, 1, 2, 3 "w", "o", "r", d, 0 word, 0 ; define 4 bytes ; define una cadena tipo C = "word" ; igual que L10

Veamos un ejemplo de lectura de los argumentos de lnea de comando, programado para el ensamblador GAS. En este ejemplo se hace uso de todo lo descrito en Acceso a la pila en un programa ensamblador en Linux:
.data retorno: .byte 0x0A funcion_pintar_cadena: #definicion de una funcion movl %eax,%ecx #el parametro ha sido pasado en EAX xorl %edx,%edx contar: movb (%ecx, %edx, 1),%al testb %al,%al #comprobar el caracter de la cadena jz fin_contar incl %edx #vamos incrementando el calculo en EDX jmp contar fin_contar: movl $4,%eax #una vez calculada la longitud,se movl $1,%ebx int $0x80 movl $4,%eax #mostramos el RETORNO_CARRO movl $retorno,%ecx movl $1,%edx #es un solo caracter int $0x80 ret

.text .globl _start _start: pop %eax #extraer de la pila el ARGC repetir: #bucle para recorrer todos los argumentos pop %eax #extraer el ARGV[i] testl %eax,%eax #comprobar si es NULL jz terminar call funcion_pintar_cadena #llamada a la funcion jmp repetir terminar: movl $1, %eax movl $0, %ebx int $0x80 #funcion del sistema para terminar

Para cada parmetro llamamos a una funcin que lo muestre por salida estndar. Para ello debe calcular la longitud de la cadena (argumento actual), contando uno por uno cada carcter que la forma. Tras cada argumento impreso, se hace un retorno de carro (es una cadena de caracteres de longitud 1). El programa muestra tambin el nombre del ejecutable como primer argumento (sera casi inmediato hacer que slo muestre los argumentos reales).

El siguiente ejemplo lee los 1024 primeros bytes de un fichero que le pasemos como primer argumento por la lnea de comandos y los muestra por salida estndar. En este ejemplo, la sintaxis utilizada ha sido la de Intel (NASM). section .data mensaje db 0xA,"---vamos a probar esto---",0xA longitud equ $ - mensaje mensaje2 db 0xA,"---hemos terminado---",0xA longitud2 equ $ - mensaje2 tamano equ 1024

section .bss buffer: resb1024 section .text global _start ;definimos el punto de entrada _start: mov edx,longitud ;EDX=long. de la cadena mov ecx,mensaje ;ECX=cadena a imprimir mov ebx,1 ;EBX=manejador de fichero (STDOUT) mov eax,4 ;EAX=funcin sys_write() del kernel int 0x80 ;interrupc. 80 (llamada al kernel) pop ebx pop ebx ;extraer "argc" ;extraer argv[0] (nombre del ejecutable)

pop ebx ;extraer el primer arg real (puntero a cadena) mov eax,5 ;funcin para sys_open() mov ecx,0 ;O_RDONLY (definido en fcntl.h) int 0x80 ;interrupc. 80 (llamada al kernel) test eax,eax ;comprobar si dev. error o el descriptor jns leer_del_fichero hubo_error: mov ebx,eax ;terminar, devolviendo el cdigo de error mov eax,1 int 0x80 leer_del_fichero: mov ebx,eax ;no hay error=>devuelve descriptor push ebx mov eax,3 ;funcin para sys_read() mov ecx,buffer ;variable donde guardamos lo leido mov edx,tamano ;tamao de lectura int 0x80 js hubo_error mostrar_por_pantalla: mov edx,eax ;longitud de la cadena a escribir mov eax,4 ;funcin sys_write() mov ebx,1 ;descriptor de STDOUT int 0x80

cerrar_fichero: pop ebx mov eax,6 ;funcin para cerrar un fichero int 0x80 mov edx,longitud2 ;EDX=long. de la cadena mov ecx,mensaje2 ;ECX=cadena a imprimir mov ebx,1 ;EBX=manejador de fichero (STDOUT) mov eax,4 ;EAX=funcin sys_write() del kernel int 0x80 ;interrupc. 80 (llamada al kernel) mov ebx,0 ;EBX=cdigo de salida al SO mov eax,1 ;EAX=funcin sys_exit() del kernel int 0x80 ;interrupc. 80 (llamada al kernel)

$nasm -f elf acceso_a_fich.asm $ ld -s -o acceso_a_fich acceso_a_fich.o

Llamar a funciones externas definidas en un mdulo de C/C++ desde ensamblador


Vamos a suponer que tenemos un mdulo escrito en C/C++ que define varias funciones muy tiles que luego querremos llamar desde nuestro programa ensamblador. El mdulo en C/C++ de este ejemplo tendr dos funciones, una que recibe dos parmetros, y otra que no recibe ninguno. Ambas, para este ejemplo, slo van a mostrar por salida estndar un mensaje y los parmetros recibidos. El cdigo fuente podra ser el siguiente:
#include <stdio.h> #include <stdlib.h> void func_con_parametros(int x, int y); void func_sin_parametros(); void func_con_parametros(int x, int y) { printf("Llamada con dos parametros x=%d y=%d \n", x , y ); } void func_sin_parametros() { printf("Llamada SIN parametros \n"); }

En este caso, nos vemos obligados a definir como punto de entrada del programa ensamblador, en lugar de _start, la etiqueta main (como en C/C++). Ya dentro del programa, antes de llamar a ninguna funcin, nuestro programa ensamblador (sintaxis GAS) debe inicializar la pila. Luego, para pasar los parmetros (si son necesarios), debe insertarlos en la pila en el orden inverso (primero el ltimo, etc.), hacer la llamada a la funcin, y por ltimo, deshacer las inserciones hechas antes de la llamada (ajustar el valor del registro puntero de pila, SP):

.data mensaje: .ascii " --mensaje desde ASM directamente--\n" mensaje_SIZE = . - mensaje .text .globl main main: # inicializar la pila pushl %ebp movl %esp, %ebp # llamar a la funcin que recibe 2 argumentos de tipo entero pushl $25 #segundo parametro (4 bytes) pushl $76 #primer parametro (4 bytes) call func_con_parametros #func_con_parametros(76,25) addl $8, %esp #quitar de la pila 8 bytes #(dos num. enteros) # mostrar texto directamente con la INT 80h movl $4, %eax movl $1, %ebx movl $mensaje, %ecx movl $mensaje_SIZE, %edx int $0x80

# llamar a la funcin que NO recibe argumentos


call func_sin_parametros # restaurar el valor del EBP # (dejar la pila como esta al principio del programa) popl %ebp # terminar y salir al sistema operativo movl $1, %eax movl $0, %ebx int $0x80

$gcc -Wall -O2 -o progr.exe funciones.c progr.s

Depuracin de cdigo usando gdb


En Linux podemos hacer uso del gdb para depurar el cdigo que hemos escrito (trazar paso a paso, comprobar el valor de ciertos registros en cada momento, etc). Para ello, debemos ensamblar nuestros programas con una opcin especial del as : as a -gstabs o prog.o prog.s la opcin a nos mostrar un listado de memoria durante el proceso de ensamblaje, donde podremos ver la localizacin de las variables y cdigo respecto al principio de los segmentos de cdigo y datos. La opcin gstabs introduce informacin de depuracin en el fichero binario, que luego usar gdb. Primer paso de la depuracin: llamar a gdb indicndole el ejecutable a depurar

La orden l muestra el texto del programa de 10 en 10 lneas:

Antes de ejecutar el programa debemos establecer dos puntos de ruptura (break): uno correspondiente a la etiqueta de comienzo del programa (_start) y otro en la lnea siguiente (en el primero no para, pero es necesario ponerlo...). Vemos que al poner el primer punto, nos indica un nmero de lnea. Nosotros debemos poner otro punto en la lnea cuyo nmero es el siguiente al que nos acaba de indicar. Una vez hecho esto, ya podemos ejecutar el programa (run): Podemos ir viendo los valores de los registros mediante info registers o bien con p/x

La traza paso a paso del programa la haremos con la orden step . A cada paso nos va mostrando la instruccin a ejecutar a continuacin; mediante las rdenes anteriores podremos ir viendo cmo cambia el contenido de los registros:

ALGUNAS SEMEJANZAS ENTRE COMANDOS DE C/C++ Y ENSAMBLADOR

INSTRUCCIONES (move y extend) movz y movzx


.data byteval: .byte 204 .text .global _start _start: movzbw byteval, %ax # %eax is now 204 movzwl %ax, %ebx # %ebx is now 204 movzbl byteval, %esi # %esi is now 204 # Linux sys_exit mov $1, %eax xorl %ebx, %ebx int $0x80

.data byteval: .byte 204 .text .global _start _start: movzbw byteval, %ax # %eax is now 204 movzwl %ax, %ebx # %ebx is now 204 movzbl byteval, %esi # %esi is now 204 # Linux sys_exit mov $1, %eax xorl %ebx, %ebx int $0x80

.file "MayorenArreglo.s" .section .data Tam: .long 5 Arreglo: .int 0,2,4,8,10 prueba: .long 4 .text .global _start _start: movl Tam, %ecx loop1: cmpl %eax, Arreglo(,%ecx,4) JLE else movl Arreglo(,%ecx,4), %eax jmp fin else: #En este caso no hay Else fin: loop loop1 xorl %eax, %eax incl %eax xorl %ebx, %ebx int $0x80 .end

CONTROL DE FLUJO, LOOP


El control de flujo en los programas de alto nivel se realiza con estructuras if, while, for. En ensamblador dichas estructuras no existen, en cambio, existen los saltos condicionales, una especie de goto condicionados, con los cuales se pueden escribir cada una de las rutinas que utilizan los lenguajes de alto nivel. Las instrucciones del ensamblador de Intel que nos permiten hacer esto. La instruccin Loop es muy til para algoritmos de la forma:

El Loop funciona de la siguiente manera: 1. Se hace dec %ECX


2. Si ECX es igual a cero, pasa a la siguiente instruccin. Si no, salta a la etiqueta que se le pasa por parmetro. Siendo as, una secuencia loop ser de la forma: 1. mov n, %ecx 2. bucle: 3. ##SECUENCIA 4. loop bucle

LA SENTENCIA if-then-else
CMP: Sirve para comparar. Internamente es una resta entre los 2 operandos que se comparan JE: Este es un salto que se ejecuta cuando la comparaci n anterior dio como resultado que los 2 operandos son iguales. JNE: Salta si no es igual. JG: Salta si es mayor. JGE: Salta si es mayor o igual. JL: Salta si es menor. JLE: Salta si es menor o igual. JMP: Salta siempre. 1. cmp A,B 2. <Salto> else 3. #SECUENCIA DE PASOS SI SE CUMPLE LA CONDICIN 4. jmp fin 5. else: 6. #SECUENCIA DE PASOS SI NO SE CUMPLE LA CONDICIN 7. fin:

LA SENTENCIA WHILE

1. while: 2. cmp a,b 3. <Salto> salir 4. #SECUENCIA DE PASOS DENTRO DEL WHILE 5. jmp while 6. salir: Algoritmo de un nmero Mayor en Arreglo

FUNCIONES Y MODULARIDAD
A medida que un programa aumenta de complejidad, se detectan ciertas porciones de cdigo que realizan lo mismo basados en 0 o mas parmetros. A estos fragmentos, podemos agruparlos en funciones. Independiente de la arquitectura utilizada, una funcin est constituida por dos componentes: El Llamador y el Llamado El Llamador (Caller ) y el Llamado (Called) El Llamador1 es la parte del cdigo que tiene estos objetivos:

Colocar Los parmetros en el sitio adecuado Guardar la direccin de retorno Invocar el Llamado El Llamado, es la parte que se encarga de:

Garantizar que todo quede como estaba. Localizar (si los hay) los parmetros. Definir (si las hay) las variables locales. Recuperar la direccin de retorno Retornar el control al llamador.

Una Aproximacin al Llamador y el Llamado desde asm

1. .section .data 2. 3. a: .long 4 4. b: .long 5 5. 6. .section .text 7. .global sumar 8. sumar: 9. pushl %ebp 10. movl %esp, %ebp 11. movl 8(%ebp), %ebx 12. movl 12(%ebp), %eax 13. addl %ebx, %eax 14. #leave 15. ret

17. .global _start 18. _start: 19. # "Main" 20. pushl a 21. pushl b 22. call sumar 23. popl %ebx 24. popl %ebx 25. # Finalizo el programa 26. xorl %eax, %eax 27. incl %eax 28. xorl %ebx, %ebx 29. int $0x80
30. end

Programacin en Lenguaje Ensamblador


Este material se refiere al compilador gcc (GNU compiler collection) el cual corre bajo ambiente Linux. El ensamblador de gcc se llama as y por formar parte de gcc comnmente se conoce como gas.

Programacin en Lenguaje Ensamblador


Plataforma: IA-32
IA-32 es la arquitectura de microprocesadores de 32 bits de Intel (Intel Architecture 32). Son los microprocesadores ms usados en los ordenadores personales (PC). Esta gama de microprocesadores comenz con el Intel 80386 en 1985, conocido luego popularmente como 386 o x86 para denominar a toda la gama. Los procesadores de Intel que siguieron y mantuvieron la compatibilidad son el 486, Pentium, Pentium II, Pentium III, Pentium 4, y la lnea Intel Core. La novedad de estos procesadores con respecto a sus predecesores es que incluyen gestin de memoria avanzada (segmentacin, paginacin, soporte de memoria virtual), unidad de punto flotante, y a partir del Pentium MMX, soporte para operaciones matriciales complejas, muy usadas en aplicaciones grficas y multimedia.

Plataforma: IA-32
Tipos de datos
La informacin se puede accesar de diversas maneras. Se puede leer un slo byte (8 bits) o un conjunto de bytes, en esta mquina en particular se denomina palabra a dos bytes y doble palabra a 4 bytes. La notacin puede ser en decimal o en hexadecimal.

Plataforma: IA-32
Tamaos de los datos:

Plataforma: IA-32
Orden de los datos en memoria:
En gas las instrucciones utilizan un sufijo para indicar el tamao de los datos sobre los cuales operan. El sistema guarda los datos en memoria en secuencia inversa de bytes (little endian) lo cual trae como consecuencia que el byte menos significativo se ubica en la posicin de menor orden y el byte ms significativo en la posicin de memoria de mayor orden. Por ejemplo si se transfiere el dato 0x457A a las posiciones consecutivas de memoria 0x100 y 0x101 se ubica el byte 7A en la posicin 0x100 y el byte 45 en la posicin 0x101.

Plataforma: IA-32
Registros de propsito general
Los registros de propsito general se utilizan para almacenar datos temporalmente, debido a que estos registros han evolucionado desde una mquina de 8 bits (el 8080) un grupo de registros an se puede acceder de 8 bits para mantener compatibilidad con toda la lnea de procesadores.

An cuando estos registros pueden mantener cualquier tipo de datos, algunos tienen cierta funcionalidad especfica o son usados de manera especial por algunas instrucciones.

Plataforma: IA-32
Registros de propsito general

Plataforma: IA-32
Registros de propsito general
En gas los registros se denotan usando el smbolo de porcentaje antes del nombre del registro. Los registros %eax, %ebx, %ecx y %edx pueden ser accesados con tamaos de 8, 16 o 32 bits cambiando su nomenclatura de acuerdo al tamao. Ejemplo para %eax:

Plataforma: IA-32
Registros de propsito general
Los registros %edi, %esi, %ebp y %esp se pueden accesar como registros de 16 o 32 bits. Ejemplo para %edi:

Plataforma: IA-32
Registro de instruccin:
El registro de instruccin o

contador de programa contiene la direccin de la prxima instruccin a ejecutarse.

Registros de punto flotante:

Son 8 registros los

cuales son tratados como una pila. Se nombran %st(0), %st(1), %st(2), etc. %st(0) se ubica en el tope de la pila.

Banderas:

Proveen una manera de obtener informacin

acerca del estado actual de la mquina y el resultado de procesamiento de una instruccin. La plataforma IA-32 utiliza un registro de 32 bits llamado EFLAGS que contiene las banderas.

Plataforma: IA-32
Banderas:
Estas son las banderas ms comunes:

Plataforma: IA-32
Banderas:

La bandera de acarreo se activa cuando se produce acarreo en una operacin matemtica entre nmeros sin signo. La bandera de paridad se usa para indicar si el resultado, en un registro, de una operacin matemtica es vlido.

La bandera de ajuste se utiliza en operaciones matemticas con nmeros decimales codificados en binario (BCD). Se activa si hay acarreo presente.
La bandera de cero se activa si el resultado de una operacin es cero. La bandera de signo muestra el bit ms significativo del resultado de una operacin, el cual denota el signo del nmero.

Plataforma: IA-32
Banderas:
La bandera de direccin controla la seleccin de autoincremento o autodecremento de los registros %edi o %esi durante las operaciones con cadenas de caracteres.

La bandera de direccin slo se utiliza con las instrucciones para el manejo de cadenas de caracteres.
La bandera de desbordamiento se utiliza en la aritmtica de enteros con signo cuando un nmero sobrepasa la capacidad de representacin del registro.

Programacin en ensamblador
Un programa en lenguaje ensamblador est orientado a lneas y cada enunciado especifica una operacin sencilla. Conceptos bsicos de los programas en lenguaje ensamblador: Espacio: Un espacio es equivalente a cualquier nmero de espacios o tabuladores. Los espacios no pueden aparecer en medio de un nmero o identificador.

Comentario: Texto que aparece despus de un carcter de inicio de comentario, el ensamblador ignora los comentarios. Los comentarios comienzan con el smbolo #.

Programacin en ensamblador
Conceptos bsicos de los programas en lenguaje ensamblador: Identificador: Es una letra seguida de cualquier cantidad de letras o dgitos (y en algunos casos caracteres especiales).

Etiqueta: identificador seguido de dos puntos.

Instruccin: es una operacin seguida de una lista de operandos.

Debe haber un espacio entre el nombre de la operacin y la lista de operandos. Los operandos se separan por comas, el nmero de operandos en la lista depende de la operacin.

Programacin en ensamblador
Conceptos bsicos de los programas en lenguaje ensamblador: Directriz: consiste en un nombre de directriz seguido de una lista de parmetros. Los nombres de la directrices comienzan con un punto.

Con las directrices se especifica la forma en que el ensamblador traduce las instrucciones, es decir, las directrices dirigen el proceso de traduccin.

Programacin en ensamblador
El programa escrito en lenguaje ensamblador se compone de varias secciones. Las secciones ms comunes son:

seccin de texto
seccin de datos seccin bss. En la seccin de texto se escriben las instrucciones, en la seccin de datos los datos inicializados y en la seccin bss las variables sin inicializar. Cada una de las secciones se declara por medio de una directiva.

Programacin en ensamblador
Para declarar las secciones mencionadas se usan las siguientes directivas:
.section .text para la seccin de texto .section .data para la seccin de datos .section .bss para la seccin bss Comnmente las secciones se colocan en la siguiente secuencia: .section .data .section .bss .section .text bss son las siglas correspondientes a "block storage start", que significa inicio de bloque de almacenaje.

Programacin en ensamblador
Punto de inicio de programa: se define por medio de la declaracin de una etiqueta: _start la cual indica a partir de qu instruccin se comienza a ejecutar el cdigo. Esta etiqueta debe ser declarada como global, es decir, que est disponible para aplicaciones externas; esto se logra utilizando la directiva .globl.

Programacin en ensamblador
Finalizacin del programa: Gas no provee una instruccin de fin de ejecucin, esto se logra mediante una llamada al sistema. Para realizar esta llamada se pasa dos parmetros: El valor 1 en el registro %eax indica el cdigo de llamada a exit (salida). El valor 0 en el registro %ebx indica la salida normal del programa.

Programacin en ensamblador
Estructura general En general la estructura de un programa en lenguaje ensamblador tiene la siguiente forma:

Programacin en ensamblador
Los datos se definen en las secciones .data y .bss. Para definir datos en la seccin .data se pueden utilizar las siguientes directivas:

Programacin en ensamblador
El formato para estas directivas es el siguiente:

etiqueta: directiva valor


Ejemplo: declaracin de variables inicializadas

Se pueden definir mltiples valores en la misma lnea. Cada uno de ellos ser guardado en memoria en el orden en el cual fueron declarados.

Programacin en ensamblador
Declaracin de mltiples valores con una misma etiqueta

En este caso cuando se lee la variable var arroja el valor 10, para poder leer el siguiente valor se debe incrementar la direccin de var en 4 bytes (debido a que la variable est declarada como long, es decir de 32 bits) de esta manera se usa la etiqueta var como la direccin inicial de estos valores y su tratamiento es el de un arreglo donde cada acceso se realiza tomando var como posicin inicial lo cual sera equivalente a decir var[0] y las posiciones siguientes como un desplazamiento de 4 bytes cada uno. Para leer por ejemplo el valor 30 se accesara var+8.

Programacin en ensamblador
Cuando se definen las variables, el sistema las guarda en forma consecutiva en memoria. Por ejemplo si se definen variables de 16 bits y luego se leen usando instrucciones de 32 bits el sistema no produce un mensaje de error y accesa los bytes consecutivos leyendo datos no vlidos.

An cuando la seccin .data se utiliza principalmente para definir variables tambin puede ser usada para definir constantes. Esto se hace usando la directiva .equ, el formato para esta directiva es:
directiva smbolo, valor Ejemplo: definicin de constantes

Programacin en ensamblador
Para definir datos en la seccin bss se usan dos directivas:

La directiva .lcomm se usa para datos locales, que no sern usados fuera del cdigo local. El formato, para ambas directivas es el siguiente: directiva smbolo, tamao en bytes Ejemplo : declaracin de un rea de memoria sin inicializar

Se declara una variable llamada area de 100 bytes de tamao.

Programacin en ensamblador
La ventaja de declarar variables en la seccin .bss es que esos datos no se incluyen en el programa ejecutable y por lo tanto el tamao total del programa es menor al tamao generado por la declaracin equivalente en la seccin .data.

Programacin en ensamblador
Instrucciones Las instrucciones en gas tienen un sufijo que indica el tamao del dato sobre el cual acta la instruccin.

Las instrucciones pueden no tener operandos, tener un slo operando o dos operandos; dependiendo de la instruccin en particular. En general las instrucciones tienen la forma: instruccin operando fuente, operando destino

Programacin en ensamblador
Los operandos se pueden clasificar en tres tipos:

Inmediato: para valores constantes.


Registro: denota el contenido de uno de los registros. Referencia a memoria: denota el contenido de una posicin de memoria.

Programacin en ensamblador
Hay varias maneras de obtener la informacin las cuales se pueden resumir en la siguiente tabla:

inm denota un inmediato reg denota un registro, regb un registro base y regi un registro ndice e es la escala la cual puede ser 1, 2, 4 8 R[reg] significa el contenido del registro reg M[x] significa el contenido de la posicin de memoria con direccin x

Programacin en ensamblador
Ejemplo: valores para cada modo de direccionamiento Asumiendo los contenidos de: %eax= 0x100 y %ecx= 0x10

Programacin en ensamblador
El valor inmediato se puede expresar en decimal o en hexadecimal como se puede observar en el siguiente ejemplo: Asumiendo el contenido de %eax=0x100

Programacin en ensamblador
La instruccin mov
La instruccin mov permite el movimiento de datos, ya que gas utiliza un prefijo para sealar el tamao de los datos podemos tener tres opciones al momento de realizar una transferencia de datos:
movb movw movl mueve un byte mueve una palabra (2 bytes) mueve dos palabras (4bytes)

Usaremos la nomenclatura F para denotar el operando fuente y D para denotar el operando destino.

Programacin en ensamblador
La instruccin mov La instruccin mov permite el movimiento de datos, ya que gas utiliza un prefijo para sealar el tamao de los datos podemos tener tres opciones al momento de realizar una transferencia de datos: movb movw movl mueve un byte mueve una palabra (2 bytes) mueve dos palabras (4bytes)

Usaremos la nomenclatura F para denotar el operando fuente y D para denotar el operando destino.

Programacin en ensamblador
Movimiento de datos inmediatos a registro o a memoria Los datos inmediatos se especifican directamente en la instruccin. Deben estar precedidos por el smbolo dlar para indicar que son datos inmediatos. Pueden estar expresados en decimal o hexadecimal. Ejemplo:

Programacin en ensamblador
Movimiento de datos entre registros Esta es la transferencia de datos que toma menor tiempo dentro del sistema es buena prctica de programacin utilizar este tipo de transferencia en vez de accesos a memoria ya que ello redunda en una mayor eficiencia. Ejemplo:

Programacin en ensamblador
Movimiento de datos entre memoria y registros Las direcciones de memoria usualmente se expresan con etiquetas, cuando por ejemplo se escribe:

a debe haber sido declarado en la seccin de datos. Como se estn transfiriendo 4 bytes stos sern guardados en memoria de manera consecutiva a partir de la posicin a.

Programacin en ensamblador
Movimiento de datos entre memoria y registros Para mover la informacin de memoria a un registro se escribe:

Programacin en ensamblador
Para leer o escribir en arreglos se utiliza direccionamiento indexado. Hay que tomar en cuenta la direccin inicial del arreglo y utilizar un ndice para ir recorriendo cada uno de sus elementos. El tamao de los datos representa el desplazamiento entre dos posiciones del arreglo. La forma general de accesar un arreglo se puede expresar como: direccin inicial (desplazamiento, ndice, escala) La direccin se calcula como: direccin inicial + desplazamiento + ndice *escala Donde escala refleja el tamao del tipo de dato del arreglo.

Programacin en ensamblador
Ejemplo de declaracin y lectura de un arreglo de enteros:

Programacin en ensamblador
Movimiento de datos con extensin Hay dos instrucciones adicionales que permiten mover datos extendindolos.

Programacin en ensamblador
Movimiento de datos con extensin Hay dos instrucciones adicionales que permiten mover datos extendindolos.

La instruccin movsbl toma un operando fuente de 1 byte, ejecuta una extensin de signo a 32 bits y lo copia al destino de 32 bits. La instruccin movzbl hace un procedimiento similar pero extiende con ceros. Ejemplo de movsbl:

Ejemplo de movzbl:

Programacin en ensamblador
Uso de la pila La pila es un rea de memoria que crece desde una direccin inicial hacia direcciones menores. El ltimo elemento colocado en la pila es el que est disponible para ser retirado. Las instrucciones para el manejo de la pila son dos, una para apilar un operando fuente y una para desapilar el valor que est en el tope de la pila y colocarlo en un operando destino.

Programacin en ensamblador
Uso de la pila
Ejemplo de uso de las instrucciones pushl y popl: Dados los valores iniciales: %esp=0x108 %ebx=0xCD %eax=0xAB %ecx=0xFE Al ejecutarse las instrucciones:

Ocurre lo siguiente:

Programacin en ensamblador

Programacin en ensamblador
Uso de la pila

Carga direccin efectiva (instruccin leal) La instruccin leal (load effective address) permite obtener la direccin de un operando en vez de su valor.

Programacin en ensamblador
Carga direccin efectiva (instruccin leal) La instruccin leal (load effective address) permite obtener la direccin de un operando en vez de su valor.

Ejemplo: Dados los siguientes valores en los registros: %eax=0x100 %ebx=0x10

Programacin en ensamblador
Instrucciones aritmticas y lgicas Para estas instrucciones usaremos el sufijo l para los ejemplos aunque tambin se pueden usar con b o w.

Programacin en ensamblador
Instrucciones aritmticas y lgicas

Programacin en ensamblador
Operaciones aritmticas especiales

Programacin en ensamblador
Ejemplo de un programa que realiza algunas operaciones aritmticas:

Programacin en ensamblador
Control de flujo Los cdigos de condicin describen los atributos de las operaciones aritmticas y lgicas ms recientes. Por lo tanto la ejecucin de las instrucciones aritmticas y lgicas afectan las banderas. La instruccin leal no afecta las banderas. Las banderas ms comunes son las siguientes: Bandera de Acarreo (CF), bandera de Cero (ZF), bandera de Signo (SF) y bandera de desbordamiento (OF). Las instrucciones cmpl y testl actualizan las banderas sin afectar los operandos. Tambin existen las versiones con los sufijos b y w.

Programacin en ensamblador
Control de flujo

Cuando se usa cmpl se compara el operando 1 con el operando 2 Ejemplo:

testl generalmente se usa repitiendo el operando para saber si es cero, positivo o negativo. Ejemplo:

Programacin en ensamblador
Actualizacin de un registro con valor de una bandera Es posible transferir el contenido de una bandera o una condicin que dependa de la combinacin de varias banderas a un registro. Esto es til para poder usar la misma condicin en varias partes del programa. Estas instrucciones slo mueven un byte, las ms usadas son:

Programacin en ensamblador
Actualizacin de un registro con valor de una bandera Ejemplo:

Programacin en ensamblador
Las instrucciones de salto Una instruccin de salto produce un cambio en la ejecucin del programa pasando a una nueva posicin, no secuencial. La nueva direccin se representa con una etiqueta.

Programacin en ensamblador
Las instrucciones de salto La instruccin jmp salta de manera incondicional, es decir, no chequea ningn cdigo de condicin. El salto puede ser directo a la direccin representada por la etiqueta o indirecto donde la direccin de destino se lee de un registro o de una posicin de memoria. Ejemplo de salto incondicional directo:

En este caso la tercera instruccin no se ejecuta ya que al ejecutarse el salto el programa pasa a ejecutar la instruccin 4.

Programacin en ensamblador
Las instrucciones de salto Ejemplo de salto incondicional indirecto: Dados los siguientes valores: %eax = 0x120 y el contenido de la posicin de memoria 0x120 = 0x180

Programacin en ensamblador
Las instrucciones de salto Las otras instrucciones de salto son condicionales lo cual significa que la mquina revisa los cdigos de condicin antes de realizar el salto, si la condicin se cumple realiza el salto a la etiqueta especificada, si la condicin no se cumple contina la ejecucin de manera secuencial. Ejemplo de salto condicional:

Programacin en ensamblador
Las instrucciones de salto Podemos observar en el ejemplo anterior que si se cumple la condicin de comparacin valor1<valor2 el programa salta a la instruccin 7 sin pasar por las instrucciones 5 y 6. En caso de no cumplirse la condicin, es decir valor1>=valor2, entonces contina la ejecucin en la instruccin siguiente, en este caso, la instruccin 5. Es importante destacar la necesidad de introducir la instruccin 6, un salto incondicional a etiq2 ya que de no estar presente la mquina seguira el orden secuencial y luego de realizar la resta ejecutara la suma lo cual arrojara un resultado errneo. En este programa slo se ejecuta una de las dos operaciones aritmticas, la resta en caso de cumplirse la condicin o la suma en caso de que no se cumpla.

Programacin en ensamblador
Traduccin de estructuras condicionales Para traducir expresiones de la forma: if (condicin) instrucciones else instrucciones En lenguaje ensamblador se utilizan los saltos condicionales. Por ejemplo para realizar un extracto de programa que haga lo siguiente: if (x>y) result=x-y; else result=y-x;

Programacin en ensamblador

Programacin en ensamblador
Ciclos
En lenguaje ensamblador se implementan los ciclos usando comparaciones de condiciones y saltos.

Do while

While
For

Do while
Consideremos el siguiente programa en lenguaje C para encontrar el valor del elemento n en la serie de Fibonacci. La serie se define como F1=1; F2=1; Fn=Fn-1+Fn-2 para n>=3. Por ejemplo los primeros 7 elementos de la serie son: 1,1,2,3,5,8,13 int fib_dw(int n) { int i=0; int val=0; int nval=1; int t=0; do { t=val+nval; val=nval; nval=t; i++; } while (i<n); return val; }

Do while Asumamos que el parmetro n est en el registro %esi. Usaremos los registros %ecx, %ebx, %edx y %eax para representar las variables i, val, nval y t respectivamente.

El resultado estar en la variable val, en este caso en %ebx

Do while

While
Cuando se utiliza while se comprueba la condicin antes de entrar en el ciclo. Se puede reescribir el ejemplo anterior utilizando un while de la siguiente manera: int fib_w(int n) { int i=1; int val=1; int nval=1;

int t=0;
while (i<n) { t=val+nval; val=nval; nval=t; i++; } return val; }

While De nuevo asumimos que n est en %esi y usamos los mismos registros anteriores:

While

For
En este caso tambin se revisa la condicin antes de entrar al ciclo. El ejemplo anterior escrito con un for:

int fib_f(int n) { int i; int val=1; int nval=1; int t=0; for(i=1; i<n; i++) { t=val+nval; val=nval; nval=t; } return val; }

For
De nuevo usamos las mismas variables:

Esta solucin es casi idntica a la del while.

Arreglos
Cuando se declara un arreglo en C se escribe una expresin con la siguiente estructura: T A[N] Esta declaracin designa el nombre del arreglo como A de

N posiciones del tipo de dato T. Esto produce la asignacin


de un espacio de memoria de N*T, es decir el nmero de elementos del arreglo por el nmero de bytes del tipo de

dato. Los elementos del arreglo se acceden con un ndice


cuyo rango es { 0, N-1}.

Arreglos
Ejemplo de declaracin y parmetros de arreglos en C: Al declarar los arreglos: char A[12]; int B[8]; double C[6]; Sus parmetros sern los siguientes:

Arreglos
En lenguaje ensamblador se utiliza el tipo de direccionamiento escalado para acceder a las posiciones de un arreglo. Por ejemplo si se declara el arreglo a como:

Los elementos del arreglo se guardan en memoria en posiciones consecutivas y el identificador a apunta a la primera posicin del arreglo, es decir la posicin cuyo ndice es cero.

Arreglos
Para leer un elemento de a se puede utilizar la siguiente instruccin:

donde a es la direccin inicial del arreglo y se utiliza un registro, en este caso %ebx como ndice. La escala (4) viene dada por el tamao del tipo de dato del arreglo, en este caso es 4 porque a est declarado como "long".

Arreglos
Ejemplo: dado un arreglo de 10 posiciones, inicializado, sumar los valores del arreglo y guardar el resultado en la variable "result"

Arreglos
Cuando se declara un arreglo de dos dimensiones como por ejemplo el arreglo A declarado en C como: int A[4][3]; Se reserva el espacio de memoria para 12 elementos (4*3) y se ordenan de la siguiente manera:

En general para un arreglo de dos dimensiones A[R][C] el elemento A[i][j] est en la direccin XA + nmero de bytes del tipo de dato por (C * i + j). Para el ejemplo anterior el elemento A[2][2] est en la direccin XA+4(3*2+2)=XA+32.

Arreglos
Ejemplo en ensamblador: Declaracin del arreglo a de dos dimensiones a [3][2] con los siguientes contenidos:

Arreglos

Lectura del elemento a [2][0]: La direccin de a [2][0] es: a +4*(2*2+0)= a+16

Los primeros procesadores Intel no tenan soporte de hardware para las operaciones de punto flotante. Esto no significa que ellos no podan efectuar operaciones de punto flotante. Esto slo significa que ellas se realizaban por procedimientos compuestos de muchas instrucciones que no son de punto flotante. Para estos primeros sistemas, Intel suministraba un circuito integrado adicional llamado coprocesador matemtico. Un coprocesador matemtico tiene instrucciones de mquina que realizan instrucciones de punto flotante mucho ms rpido que usando procedimientos de software (en los primeros procesadores se realizaban al menos 10 veces ms rpido). El coprocesador para el 8086/8088 fue llamado 8087. Para el 80286 era el 80287 y para el 80386 un 80387. El procesador 80486DX integr el coprocesador matemtico en el 80486 en s mismo. Desde el Pentium, todas las generaciones del 80X86 tienen un coprocesador matemtico interno; sin embargo todava se programa como si fuera una unidad separada. An los primeros sistemas sin un coprocesador pueden instalar un software que emula el coprocesador matemtico. Estos emuladores se activan automticamente cuando un programa ejecuta una instruccin del coprocesador y corre un procedimiento que produce los mismos resultados que el coprocesador (mucho ms lento claro est. El coprocesador numrico tiene ocho registros de punto flotante. Cada registro almacena 80 bits. Los nmeros de punto flotante se almacenan en estos registros siempre como nmeros de 80 bits de precisin extendida. Los registros se llaman STO, ST1, . . . ST7. Los registros de punto flotante se usan diferentes que los registros enteros en la CPU. Los registros de punto flotante estn organizados como una pila. Recuerde que una pila es una lista LIFO (Last In Firist Out). STO siempre se refiere al valoren el tope de la pila. Todos los nmeros nuevos se aaden al tope de la pila. Los nmeros existentes se empujan en la pila para dejarle espacio al nuevo nmero. Hay tambin un registro de estado en el coprocesador numrico. Tiene varias banderas. Slo las 4 banderas usadas en comparaciones sern estudiadas. C0 , C1 , C2 and C3 . El uso de ellas

LA UNIDAD DE PUNTO FLOTANTE


En decimal, los dgitos a la derecha del punto decimal tienen asociados potencias de 10 negativas. No es sorprendente que los n meros binarios trabajen parecido.

Conversin a binario del nmero decimal 0.5625 Conversin a binario del nmero decimal 0.85

REPRESENTACIN DE NMEROS CON EXPONENTES


Un nmero de punto flotante a normalizado tiene la forma: 1,sssssssssssx2eeeeeeeeeeeeeeeee Donde 1, sssssssssssss es la mantisa y eeeeeeee es el exponente.

El IEEE (Institute of Electerical and Electronic Engineer) es una organizacin internacional que ha diseado formatos binarios especficos para almacenar nmeros de punto flotante. Este formato se usa en la mayora de (pero no en todas) las computadoras hechas hoy da. A menudo es soportado por el hardware de la computadora en s mismo. Por ejemplo el coprocesador numrico (o matemtico) de Intel (que est insertado en todas las CPU que hacen uso de este procesador desde el Pentium) lo usa. El IEEE define dos formatos con precisin diferentes: precisin simple y doble, la precisin simple es usada por las variables del tipo float en C y la precisin doble es usada por la variable double.

El coprocesador matemtico de Intel utiliza un tercer nivel de mayor precisin llamado precisin extendida. De hecho, todos los datos en el coprocesador en s mismos estn en esta precisin. Cuando se almacenan en la memoria desde el coprocesador se convierte a precisin simple o doble automticamente. La precisin extendida usa un formato general ligeramente

PRECISIN SIMPLE IEEE


El punto flotante de precisin simple usa 32 bits para codificar el nmero. Normalmente son exactos los primeros 7 dgitos decimales. Los nmeros de punto flotante son almacenados en una forma mucho ms complicada que los enteros. La siguiente figura muestra la forma bsica del formato de precisin simple del IEEE. Hay varias peculiaridades del formato. Los nmeros de punto flotante no usan la representacin en complemento a 2 para los nmeros negativos. Ellos usan la representacin de magnitud y signo. El bit 31 determina el signo del nmero como se muestra.

El exponente binario no se almacena directamente. En su lugar, se almacena la suma del exponente y 7F en los bits 23 al 30. Este exponente polarizado siempre es no negativo. La parte fraccionaria se asume que es normalizada (en la forma 1.sssssssss). Ya que el primer bit es siempre uno, ste uno no se almacena. Esto permite el almacenamiento de un bit adicional al final y as se incrementa un poco la precisin. Esta idea se conoce como la representacin oculta del uno.
Cmo se podra almacenar 23.85? Primero es positivo, as el bit de signo es 0, ahora el exponente verdadero es 4, as que el exponente es 7F + 4 = 8316 . Finalmente la fraccin es (recuerde el uno de adelante est oculto). Colocando todo esto unido (para ayudar a aclarar las diferentes secciones del formato del punto flotante, el bit de signo y la fraccin han sido subrayados y los bits se han agrupado en nibles):

La doble precisin IEEE usa 64 bits para representar nmeros y normalmente son exactos los 15 primeros dgitos decimales significativos. Como muestra la Figura 6.4, el formato bsico es muy similar a la precisin simple. Se usan ms bits para el exponente (ll) y la fraccin (52) que para la precisin simple. El gran rango para el exponente tiene dos consecuencias. La primera es que se calcula como la suma del exponente real y 3FF (1023) (no 7F como para la precisin simple). Segundo, se permite un gran rango de exponentes verdaderos (y as usa un gran rango de magnitudes). Las magnitudes de precisin doble van de 10308 hasta 10308 aproximadamente. En el campo de la fraccin el responsable del incremento en el nmero dgitos significativos para los valores dobles. Como un ejemplo, considere nuevamente 23.85 otra vez. El exponente polarizado ser 4 + 3FF = 403 en hexadecimal. As la representacin doble sera: 0 100 0000 0011 0111 1101 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1010 O 40 37 D9 99 99 99 99 9A en hexadecimal. Si uno convierte esto a decimal uno encuentra 23.8500000000000014 (hay 12 ceros!) que es una aproximacin mucho mejor de 23.85. La precisin doble tiene los mismos valores especiales que la precisin simple. 3 Los nmeros no normalizados son muy similares tambin. La principal diferencia es que los nmeros dobles sin normalizados usan 21023 en lugar de 2127 .

ARITMTICA DE PUNTO FLOTANTE


La aritmtica de punto flotante en un computador diferente que en la matemtica continua. En las matemticas, todos los nmeros pueden ser considerados exactos. Como se muestra en la seccin anterior, en un computador muchos nmeros no se pueden representar exactamente con un nmero finito de bits. Todos los clculos se realizan con una precisin limitada. En los ejemplos de esta seccin, se usarn nmeros con una mantisa de 8 bits por simplicidad. Para sumar dos nmeros de punto flotante, los exponentes deben ser iguales. Si ellos, no son iguales, entonces ellos se deben hacer iguales, desplazando la mantisa del nmero con el exponente ms pequeo Por ejemplo, considere 10,375 + 6,34375 = 16,71875 o en binario: 1,0100110 23 1,1001011 22

Estos dos nmeros no tienen el mismo exponente as que se desplaza la mantisa para hacer iguales los exponentes y entonces sumar:

1,0100110 23 + 0,1100110 23 10,0001100 23

Observe que el desplazamiento de 1,1001011 22 pierde el uno delantero y luego de redondear el resultado se convierte en 0,1100110 23 . El resultado de la suma, 10,000110023 (o 1,0000110024 ) es igual a 10000,1102 o 16.75. Esto no es igual a la respuesta exacta (16.71875) Es s lo una aproximacin debido al error del redondeo del proceso de la suma. Es importante tener en cuenta que la aritmtica de punto flotante en un computador (o calculadora) es siempre una aproximacin. Las leyes de las matemticas no siempre funcionan con nmeros de punto flotante en un computador. Las matemticas asumen una precisin infinita que un computador no puede alcanzar. Por ejemplo, las matemticas ensean que (a+b)b = a; sin embargo, esto puede ser exactamente cierto en un computador.
La resta trabaja muy similar y tiene los mismos problemas que la suma. Como un ejemplo considere 16,75 15,9375 = 0,8125: 1,0000110 24 1,1111111 23

Desplazando 1,1111111 23 da (redondeando arriba) 1,0000000 24


1,0000110 24 1,0000000 24 0,0000110 24 0,0000110 24 = 0,112 = 0,75 que no es exacto.

Los primeros procesadores Intel no tenan soporte de hardware para las operaciones de punto flotante. Esto no significa que ellos no podan efectuar operaciones de punto flotante. Esto slo significa que ellas se realizaban por procedimientos compuestos de muchas instrucciones que no son de punto flotante. Para estos primeros sistemas, Intel suministraba un circuito integrado adicional llamado coprocesador matemtico. Un coprocesador matemtico tiene instrucciones de mquina que realizan instrucciones de punto flotante mucho ms rpido que usando procedimientos de software (en los primeros procesadores se realizaban al menos 10 veces ms rpido). El coprocesador para el 8086/8088 fue llamado 8087. Para el 80286 era el 80287 y para el 80386 un 80387. El procesador 80486DX integr el coprocesador matemtico en el 80486 en s mismo 5 . Desde el Pentium, todas las generaciones del 80X86 tienen un coprocesador matemtico empotrado; sin embargo todava se programa como si fuera una unidad separada. An los primeros sistemas sin un coprocesador pueden instalar un software que emula el coprocesador matemtico. Estos emuladores se activan automticamente cuando un programa ejecuta una instruccin del coprocesador y corre un procedimiento que produce los mismos resultados que el coprocesador (mucho ms lento claro est).

section .data valor: dd 0 ;argc suma: dd 0 ; sumatoria de los enteros formato: db '%.3f', 10, 0 ;Formato de impresin de resultado error: db 'No se introducieron valores', 10, 0 ;mal forma de uso global main extern printf extern atoi section .text main: mov ecx, [esp+4] ; dec ecx ; Checar que se hayan introducido enteros. jz nada mov [valor], ecx ; guardar el numero introducido argc o argumentos de enteros. mov edx, [esp+8] ; agregar: push ecx ; insertar dato en la pila push edx ; insertar dato en la pila push dword [edx+ecx*4] ; argv[ecx] call atoi ; convertir el arg de entrada a entero add esp, 4 ; aumentar el apuntador de la pila pop edx ; sacar registros usados de los registros pop ecx add [suma], eax ; guardar dato en sumatoria dec ecx jnz agregar ; si hay mas argumentos continua el ciclo.

average: fild dword [suma] fild dword [valor] fdivp st1, st0 sub esp, 8 promedio (resultado) fstp qword [esp] push formato call printf add esp, 12 ret nada: push error call printf add esp, 4 ret

; cargar en st0 la sumatoria de enteros en la pila ; cargar en st0 numero de enteros (argc) en la pila ; promedio = (suma / valor) st1 / st0 ; mover el apuntador para posicinarlo en el ; almacena el promedio y saca de la pila ; alistar de impresion ; llamar a la libreria de C para imprimir ; avanzar el apuntador para sacar los resultados

; mensaje de error

Para la multiplicacin, las mantisas son multiplicadas y los exponentes son sumados. Considere 10,375 2,5 = 25,9375: 1,0100110 23 1,0100000 21 10100110 + 10100110 1,10011111000000 24 Claro est, el resultado real podra ser redondeado a 8 bits para dar: La divisin es ms complicada, pero tiene problemas similares con errores de redondeo. El principal punto de esta seccin es que los clculos de punto flotante no son exactos. El programador necesita tener cuidado con esto. Un error comn que el programador hace con nmeros de punto flotante es compararlos asumiendo que el clculo es exacto. Por ejemplo considere una funcin llamada f (x) que hace un clculo complejo y un programa est tratando de encontrar las races de la funcin. 4 . Uno podra intentar usar la siguiente instruccin para mirar si x es una raz: if ( f (x) == 0.0 )

S-ar putea să vă placă și