0% encontró este documento útil (0 votos)
368 vistas78 páginas

Tema 3

Cargado por

J.S. C.V.
Derechos de autor
© Attribution Non-Commercial (BY-NC)
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
368 vistas78 páginas

Tema 3

Cargado por

J.S. C.V.
Derechos de autor
© Attribution Non-Commercial (BY-NC)
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd

Universidad de Murcia

Facultad de Informtica

T TULO DE G RADO EN I NGENIERA I NFORMTICA

Estructura y Tecnologa de Computadores


Tema 3: Lenguaje ensamblador Apuntes
C URSO /

V ERSIN 2.0

Departamento de Ingeniera y Tecnologa de Computadores


rea de Arquitectura y Tecnologa de Computadores

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

ndice general
3.1. Introduccin . . . . . . . . . . . . . . . . . . . . . 3.2. Repertorios de instrucciones . . . . . . . . . . . . 3.3. Operandos del computador . . . . . . . . . . . . . 3.3.1. Los registros en MIPS . . . . . . . . . . . 3.4. El ISA de MIPS . . . . . . . . . . . . . . . . . . . 3.4.1. Instrucciones aritmetico-lgicas . . . . . . 3.4.2. Instrucciones de acceso a memoria . . . . . 3.4.3. Instrucciones de salto . . . . . . . . . . . . 3.4.4. Instrucciones para soportar procedimientos 3.4.5. Instrucciones de coma otante . . . . . . . 3.5. Manejo de interrupciones y excepciones . . . . . . 3.6. Llamadas al sistema operativo . . . . . . . . . . . 3.7. Codicacin de las instrucciones en MIPS . . . . . 3.7.1. Formato de instruccin R . . . . . . . . . . 3.7.2. Formato de instruccin I . . . . . . . . . . 3.7.3. Formato de instruccin J . . . . . . . . . . 3.7.4. Pseudoinstrucciones . . . . . . . . . . . . 3.8. Alternativas al ISA de MIPS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 3 4 5 7 8 12 15 18 23 28 29 30 31 32 34 34 36 39 39 39 40 41 41 41 44 48 53 53 53 53 53 54 56 58 58 58 58 58 58 62 62 64

A3.Apndices A3.1.Manipulacin de datos a nivel de bits . . . . . . . . . . . . . . . . . . . . A3.1.1. Lectura de campos de bits . . . . . . . . . . . . . . . . . . . . . A3.1.2. Escritura de campos de bits . . . . . . . . . . . . . . . . . . . . . A3.2.Uso de la memoria en MIPS . . . . . . . . . . . . . . . . . . . . . . . . A3.2.1. Modos de direccionamiento a memoria del ensamblador de MIPS A3.2.2. Organizacin de la memoria en MIPS . . . . . . . . . . . . . . . A3.2.3. Datos y punteros a datos . . . . . . . . . . . . . . . . . . . . . . A3.2.4. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B3.Boletines de prcticas B3.1. Uso del simulador MARS . . . . . . . . . . . . . . B3.1.1. Objetivos . . . . . . . . . . . . . . . . . . B3.1.2. Prerequisitos . . . . . . . . . . . . . . . . B3.1.3. Plan de trabajo . . . . . . . . . . . . . . . B3.1.4. El simulador MARS . . . . . . . . . . . . B3.1.5. Anatoma de un programa en ensamblador B3.1.6. Ejercicios . . . . . . . . . . . . . . . . . . B3.2. Convenciones de programacin en MIPS . . . . . . B3.2.1. Objetivos . . . . . . . . . . . . . . . . . . B3.2.2. Prerequisitos . . . . . . . . . . . . . . . . B3.2.3. Plan de trabajo . . . . . . . . . . . . . . . B3.2.4. Ejemplos de paso de parmetros . . . . . . B3.2.5. Ejemplo de procedimiento recursivo . . . . B3.2.6. Ejercicios . . . . . . . . . . . . . . . . . . B3.3. Proyecto de programacin en MIPS . . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

B3.3.1. Objetivos . . . . . . . . . . . . . . . . B3.3.2. Pong . . . . . . . . . . . . . . . . . . B3.3.3. Descripcin del programa inicial . . . . B3.3.4. Modicaciones propuestas al programa B3.3.5. Criterios de evaluacin . . . . . . . . . B3.3.6. Requisitos de entrega . . . . . . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

64 65 65 72 77 77

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

3.1.

Introduccin

Una de las propiedades ms interesantes de los computadores es su capacidad para realizar funciones muy variadas con el mismo hardware, gracias a que son programables. Un programa es el conjunto ordenado de instrucciones que dirigen el funcionamiento del computador. Una instruccin es un smbolo o conjunto de smbolos que representa una orden concreta para el computador. Dependiendo del nivel de abstraccin del lenguaje, una instruccin puede representar una operacin simple como sumar dos nmeros o ms compleja como mostrar un nmero por pantalla. Los programas se pueden expresar mediante una gran variedad de lenguajes. En esta asignatura nos ocuparemos de los lenguajes llamados de bajo nivel, en oposicin a los lenguajes de alto nivel. Esta clasicacin se reere al nivel de abstraccin del lenguaje respecto al funcionamiento del computador. Los lenguajes de bajo nivel se abstraen muy poco de los detalles del computador concreto que ejecutar el programa, ofreciendo instrucciones simples de ejecutar, especcas y dependientes de la mquina. Por el contrario, los lenguajes de alto nivel se abstraen ms del funcionamiento de la mquina que ejecutar los programas y ofrecen instrucciones y construcciones ms complicadas de ejecutar, ms genricas y ms independientes de la mquina concreta. Gracias a esta abstraccin, los lenguajes de alto nivel permiten al programador expresar algoritmos de forma ms natural y ms directa, por lo que son los utilizados normalmente. Aunque existen miles de lenguajes de programacin diferentes, la mayora permite expresar cualquier programa1 . En particular, no se debe confundir el que un lenguaje sea de ms alto o ms bajo nivel con la capacidad del lenguaje de expresar un programa concreto. Para que un programa escrito en un lenguaje determinado pueda ser ejecutado, debe primero traducirse a un lenguaje de muy bajo nivel que depende de la mquina que deseamos utilizar para ejecutar el programa. A este lenguaje se le llama normalmente cdigo mquina. Esta traduccin se realiza casi siempre mecnicamente usando programas llamados compiladores y ensambladores, con los que el alumno ya estar familiarizado. Gracias a asignaturas cursadas antes que Estructura y Tecnologa de Computadores, el alumno debe estar familiarizado con, al menos, algn lenguaje de programacin de alto nivel y al menos un lenguaje ensamblador. El objetivo de este tema es estudiar el lenguaje ensamblador del MIPS y su traduccin a cdigo mquina con suciente detalle para permitir la comprensin de cmo se implementa un circuito (procesador) capaz de ejecutar el cdigo mquina resultante. Tambin se pretende que el alumno sea capaz de realizar programas de complejidad moderada directamente en ensamblador y que sepa cmo se traducen a ensamblador algunas de las construcciones ms usuales en lenguajes de alto nivel.

3.2.

Repertorios de instrucciones

El repertorio de instrucciones (en ingls, ISA o Instruction Set Architecture) de un computador es el conjunto de instrucciones ejecutables por dicho computador. El ISA de un computador tambin determina caractersticas de la mquina como el nmero y tipo de registros, modos de direccionamiento, manejo de interrupciones y excepciones, y manejo de la entrada/salida. En general, los repertorios de instrucciones de los distintos modelos de computador son bastante similares. Esto se debe a que su hardware se disea usando unos principios fundamentales similares y, por lo tanto, existe un repertorio bsico de funciones que todos los computadores deben, de una u otra forma, proporcionar. Asimismo, el mismo ISA puede ser utilizado por varios computadores diferentes, incluso computadores totalmente diferentes y diseados por fabricantes diferentes. Por ejemplo, el ISA IA-32 (tambin conocido como x86) es utilizado por procesadores fabricados por Intel y AMD, entre otros. El propio ISA MIPS que estudiaremos en este tema se utiliza en los procesadores de productos tan diferentes entre s como estaciones de trabajo (algunos modelos de Silicon Graphics, p.e.), supercomputadores (NEC, ORIGIN 2000), ordenadores
1

Hay lenguajes de propsito especco que solo permiten expresar algunos programas que cumplen ciertas condiciones.

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

de bolsillo (palmtops), mviles, impresoras (modelos lser de Hewlett-Packard), cmaras digitales, videoconsolas (Nintendo 64 o Playstation 2 de Sony, entre otras), routers o incluso robots (como el AIBO de Sony). En principio, es fcil elegir un repertorio de instrucciones que sea capaz de representar cualquier programa ejecutable por un computador. Sin embargo, la eleccin de un conjunto de instrucciones adecuado es importante para optimizar el rendimiento y el coste del sistema nal. Los objetivos principales a seguir cuando se disea un repertorio de instrucciones (ISA) son los siguientes: 1. Facilitar la construccin del hardware: a no ser que la complejidad est debidamente justicada, debe disearse un repertorio de instrucciones que permita que el diseo del procesador sea lo ms sencillo posible. 2. De igual modo, debe permitir que el diseo del compilador encargado de pasar de lenguaje de alto nivel a lenguaje ensamblador sea sencillo, en la medida de lo posible. 3. Ha de maximizar el rendimiento obtenido, entendido ste, en un sentido amplio, como la cantidad de procesamiento efectivo til por unidad de tiempo. 4. Debe minimizarse el coste de implementacin del procesador capaz de ejecutar dicho repertorio. Para que se pueda programar de forma efectiva un computador, adems de conocer el ISA del mismo es necesario conocer y seguir una serie de convenciones sobre el uso de registros, la llamada a procedimientos o el interfaz con el sistema operativo y con la entrada/salida. Estas convenciones de programacin junto con el ISA del computador determinan el interfaz de programacin binaria (en ingles ABI, o Application Binary Interface) particular de cada plataforma. En el resto de este tema se explicar el repertorio de instrucciones y las convenciones de programacin de un computador MIPS, como ejemplo de un ABI tpico. MIPS es un ISA usado en varios sistemas reales y representativo de las arquitecturas tipo RISC (Reduced Instruction Set Computer). Como tal se caracteriza por ofrecer instrucciones sencillas, formatos de instruccin regulares, muchos registros de propsito general y modos de direccionamiento sencillos.

3.3.

Operandos del computador

Los lenguajes de alto nivel utilizan variables y constantes de distintos tipos (enteros, reales, caracteres, arrays o tablas, etc) como operandos de las instrucciones. Cuanto mayor sea el nivel de abstraccin del lenguaje, menos tendr que preocuparse el programador de detalles tales como dnde se almacenan esos operandos, cunto ocupan o qu pasos en concreto es necesario dar para realizar una operacin concreta con esos operandos. Por ejemplo, en un lenguaje como C++ es posible denir un tipo matriz que represente matrices de m n y a continuacin denir e inicializar dos variables de ese tipo, para luego denir una tercera como la suma de los anteriores. En este ejemplo, el compilador se encargar de decidir cmo almacenar las tres variables y de generar las instrucciones necesarias para realizar la suma de matrices, lo cual involucrar seguramente varios accesos a memoria y varias sumas de los elementos de las matrices. En los computadores, los operandos siguen una losofa similar. Sin embargo, debido al bajsimo nivel de abstraccin del cdigo mquina y del lenguaje ensamblador, slo hay disponible un nmero pequeo y jo de tipos de operandos. A la hora de realizar operaciones sobre ellos, los operandos deben estar almacenados en lugares determinados y limitados, siguiendo restricciones segn el tipo de los operandos, la operacin concreta, o si el operando es un dato de entrada para la operacin o un resultado. El programador (o, ms frecuentemente, el compilador) debe encargarse de gestionar el movimiento de los datos de un lugar a otro para poder realizar las operaciones deseadas. Las instrucciones necesitan referirse a los operandos. Para ello, es necesario codicar de alguna manera dnde se encuentra el operando (su direccin). Los modos de direccionamiento se reeren a la manera en que

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

los operandos estn especicados dentro de la representacin binaria de la instruccin, y por tanto determinan dnde se pueden encontrar esos operandos. Dado que la representacin binaria de una instruccin es de longitud limitada (en MIPS, por ejemplo, todas las instrucciones se codican usando 32 bits), la codicacin elegida impondr unos lmites en el nmero de direcciones de los operandos (por ejemplo, en MIPS se utilizan 5 bits para codicar el nmero de registro, por lo que solo se pueden direccionar 32 registros). En el caso de MIPS, los modos de direccionamiento disponibles son: Direccionamiento inmediato: el operando se encuentra codicado en la propia instruccin. En ensamblador, el operando aparece como un nmero entero (en decimal o hexadecimal). Es el caso de las constantes que se pueden codicar en menos de 16 bits (que es el nmero de bits reservado en el formato de instruccin I, ver seccin 3.7.2). Direccionamiento a registro: el operando se encuentra en un registro. Es el caso ms frecuente, y en ensamblador el registro aparece identicado por su nmero o por un nombre (ambas formas son equivalentes). El nmero de registro se codica en la instruccin en uno de tres posibles campos, de 5 bits cada uno. En algunos casos, el registro concreto donde se pueda colocar el operando estar sujeto a algunas restricciones segn el tipo del operando (p.e.: nmeros enteros o en punto otante) o la operacin (p.e.: el cociente de una divisin siempre se almacenar en el registro especial HI, o los operandos de doble precisin siempre se encontrarn en registros pares). Vase la seccin 3.3.1 para ms detalles. Direccionamiento base ms desplazamiento: el operando se encuentra en memoria, y la direccin de memoria donde se encuentra se calcula sumando el valor almacenado en un registro (base) y una constante (desplazamiento). En ensamblador aparece el valor del desplazamiento seguido del nombre del registro entre parntesis. En la instruccin se codica el nmero del registro en un campo de 5 bits y la constante en complemento a 2 en un campo de 16 bits. Vase la seccin 3.4.2 para ms detalles. Direccionamiento relativo al contador de programa (PC): el operando se encuentra en memoria y es una instruccin destino de un salto. En ensamblador, la instruccin de destino aparecer normalmente identicada por una etiqueta2 , mientras que en la instruccin se codica en complemento a 2 y en un campo de 16 bits el nmero de instrucciones que hay que avanzar (o retroceder, si el nmero es negativo) el contador de programa3 desde la instruccin siguiente a la actual para llegar a la instruccin de destino. Vanse las secciones 3.4.3 y 3.7.2 para ms detalles. Direccionamiento pseudodirecto: de nuevo, el operando se encuentra en memoria y es una instruccin destino de un salto. En ensamblador, la instruccin de destino aparecer normalmente identicada por una etiqueta o por su direccin en memoria. En la instruccin se codican 26 de los 32 bits de la instruccin de destino. Vanse las secciones 3.4.3 y 3.7.3 para ms detalles. No todos los modos de direccionamiento se pueden utilizar en cualquier momento. Al contrario, cada instruccin permitir slo unos pocos modos de direccionamiento jos. En particular, en MIPS y otras arquitecturas RISC casi todas las instrucciones utilizan preferentemente el direccionamiento a registro y slo las instrucciones especcas de acceso a memoria utilizan el direccionamiento base ms desplazamiento.

3.3.1.

Los registros en MIPS

Los operandos sobre los que trabajan las instrucciones de un computador pueden estar almacenados en registros o en memoria:
Tambin puede aparecer el nmero de bytes que es necesario avanzar (o retroceder si es negativo) el PC para apuntar a la instruccin de destino. 3 El contador de programa (llamado tambin PC) es un registro especial que contiene la direccin de la siguiente instruccin a ejecutar.
2

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

Registros: hay pocos disponibles, pero tienen un tiempo de acceso muy rpido. Memoria: es de gran tamao pero con un tiempo de acceso mucho ms lento que los registros. MIPS dispone de 32 registros enteros de propsito general y 32 registros de coma otante de simple precisin (que tambin se pueden utilizar como 16 de doble precisin, ver seccin 3.4.5). Tambin dispone de algunos registros especiales como los registros HI y LO descritos en la seccin 3.4.1, o el contador de programa (PC) a los que slo se puede acceder con determinadas instrucciones. En MIPS, por ser una arquitectura de tipo RISC, la mayora de las operaciones necesitan que sus operandos se encuentran en registros. Debido a que los datos de un programa no caben todos en el nmero limitado de registros disponibles, estos datos se almacenan en general en la memoria y el programador (o el compilador) necesita encargarse de colocar los datos adecuados en los registros cuando necesita operar con ellos y almacenar los resultados en memoria para poder utilizar los registros en otra operacin (ver seccin 3.4.2). Supongamos, como analoga, que tenemos una carpintera, donde fabricamos a mano todo tipo de muebles. En cada momento, en nuestra mesa de trabajo tendremos slo las piezas que necesitamos para construir el mueble actual, mientras que en el almacn (o memoria, en nuestra analoga con el computador) tendremos guardados todos los tornillos, maderas y dems utensilios necesarios para construir muebles, ya que todos no caben a la vez en nuestra mesa de trabajo. Esta ltima hara, por tanto, el papel del archivo o banco de registros. El hecho de disponer slo de 32 registros se debe a que un nmero demasiado alto de registros incrementara el tiempo de acceso al banco de registros y, por tanto, tambin aumentara el tiempo que tardan los programas en ejecutarse. Disponer de pocos registros tambin aumentara el tiempo de ejecucin de los programas porque sera necesario mover ms frecuentemente los operandos de la memoria a los registros para trabajar con ellos, incrementando el nmero de accesos a memoria. El nmero de registros tambin afecta al formato de instruccin, debido a que un mayor nmero de registros implicara instrucciones ms largas para poder codicar el nmero de los registro usados por la instruccin (ver seccin 3.7). Por tanto, ofrecer 32 registros de propsito general es un compromiso para no penalizar demasiado ni el tiempo de acceso a los registros, ni el nmero de accesos de memoria, ni el tamao de las instrucciones. Los 32 registros enteros se identican en ensamblador por su nmero precedido de un signo de dlar ($0, $1, $2, . . . , $31) o, equivalentemente, por su nombre precedido de un signo de dlar. La tabla 1 muestra los nombres de los registros enteros de propsito general de MIPS y algunas caractersticas de cada uno. Para cada registro, en la tabla 1 se muestra cul es su uso habitual y si es un registro cuyo valor debe ser preservado por los procedimientos que lo modiquen o no (ver seccin 3.4.4 para una explicacin de las convenciones para llamada a procedimientos en MIPS). El registro $zero es especial, ya que cualquier escritura en ese registro se ignora y su valor es siempre la constante 04 . El registro $at lo usa el ensamblador para implementar las pseudoinstrucciones que necesitan un registro temporal, como se explica en la seccin 3.7.4. Los registros $v0 y $v1 se utilizan para que los procedimientos comuniquen sus resultados, mientras que los registros $a0, $a1, $a2 y $a3 se utilizan para comunicar a un procedimiento sus argumentos, como se explica en la seccin 3.4.4. Los registros $t0, $t1, $t2, $t3, $t4, $t5, $t6, $t7, $t8 y $t9 son los registros de uso temporal general. Se utilizan para valores intermedios que no es necesario conservar entre llamadas. Por su parte, los registros $s0, $s1, $s2, $s3, $s4, $s5, $s6 y $s7 se utilizan para valores temporales de ms larga duracin que se desea preservar cuando se hace una llamada a otro procedimiento. El valor de estos ltimos se debe almacenar en la pila antes de modicarlo, como se explica en la seccin 3.4.4. Los registros $k0 y $k1 estn reservados para su uso por el sistema operativo (ms concretamente, para su uso por las rutinas de atencin de interrupciones, ver seccin 3.5) y no deben ser usados por el resto de programas, ya que su valor puede cambiar en cualquier momento (desde el punto de vista del programa).
4

Esta propiedad es muy til, entre otras cosas, para implementar pseudoinstrucciones como se muestra en la seccin 3.7.4.

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

Nmeros

Nombres

Uso

Preservados entre llamadas No aplicable No No No5 No S No No S S S S

0 1 2, 3 4, . . . , 7 8, . . . , 15 16, . . . , 23 24, 25 26, 27 28 29 30 31

$zero $at $v0, $v1 $a0, . . . , $a3 $t0, . . . , $t7 $s0, . . . , $s7 $t8, $t9 $k0, $k1 $gp $sp $fp $ra

Valor constante 0 Reservado para uso temporal del ensamblador Resultados de las llamadas a procedimiento y evaluacin de expresiones Argumentos de las llamadas a procedimiento Temporales Temporales guardados Temporales Reservado para su uso por el ncleo del sistema operativo Puntero global Puntero de pila Puntero de marco Direccin de retorno Tabla 1: Registros enteros de MIPS

El registro $gp se utiliza para apuntar al comienzo del rea de memoria reservada para las variables globales del programa o librera. El registro $sp apunta siempre a la cima de la pila, tal y como se explica en la seccin 3.4.4. El registro $fp se usa para apuntar al marco de pila del procedimiento actual, es decir, a la zona de la pila que se utiliza para almacenar variables locales. Su uso no es estrictamente necesario (depende del compilador y de las opciones de compilacin), por lo que se puede utilizar alternativamente como un registro temporal adicional preservado entre llamadas. El registro $ra se utiliza para almacenar la direccin de retorno del procedimiento actual, como se explica en la seccin 3.4.4. Obsrvese que, salvo en el caso del registro $zero, la informacin sobre el uso de los registros y su preservacin entre llamadas es slo una convencin: el hardware no impide que se usen de forma diferente si se quiere. Estas convenciones forman parte del ABI de la plataforma, y deben ser seguidas por programadores (y compiladores) para que sus procedimientos puedan llamar a procedimientos escritos por otros programadores. Adems de los registros enteros mencionados aqu, el ISA de MIPS tambin ofrece un conjunto de registros para trabajar en coma otante, descrito en la seccin 3.4.5.

3.4.

El ISA de MIPS

Clasicaremos las instrucciones de MIPS segn el tipo de accin que realizan y mostraremos ejemplos de algunas instrucciones de cada tipo. Algunas de las instrucciones que se mencionarn en esta seccin son pseudoinstrucciones. Es decir, son instrucciones de ensamblador que no tienen una traduccin directa a cdigo mquina (ver seccin 3.7.4).
Aviso: algunas ediciones de Estructura y Diseo de Computadores de David A. Patterson y John L. Hennessy clasican incorrectamente estos registros como preservados entre llamadas. Este error ha sido subsanado en ediciones modernas de dicho libro, aunque se ha propagado a otros materiales disponibles en Internet. En particular, la gura 3.13 de la pgina 131 de la 1a edicin en espaol del libro citado anteriormente es incorrecta. Hacer estos registros preservados entre llamadas sera una mala decisin en el diseo del ISA porque incrementara el trco entre los registros y la pila.
5

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

3.4.1.

Instrucciones aritmetico-lgicas

El primer tipo de operacin, y quizs el ms natural, puesto que es el que realiza el verdadero procesamiento de los datos, es implementado por las instrucciones aritmticas. Los computadores han de ser capaces de realizar sumas, restas, multiplicaciones y divisiones con los datos. Un ejemplo de ello sera la instruccin de suma de registros en MIPS: add $a,$b,$c # $a=$b+$c En la linea anterior, $a, $b y $c indican cualesquiera de los 32 registros enteros de MIPS. Esta operacin indica al computador que sume (add6 = sumar) el contenido de los registros $b y $c y que guarde el resultado en $a. Se puede observar que el formato es rgido: las operaciones tienen, forzosamente, dos operandos fuentes y un operando destino. Si se quisieran sumar 4 valores, por ejemplo, los almacenados en los registros $b, $c, $d y $e, para colocar el resultado en el registro $a, se podra hacer as: add $a,$b,$c # $a=$b+$c add $a,$a,$d # $a=$a+$d=$b+$c+$d add $a,$a,$e # $a=$a+$e=$b+$c+$d+$e Muy similares a las instrucciones aritmticas (suma, resta, multiplicacin, divisin, etc) son las instrucciones lgicas, que trabajarn siguiendo un esquema idntico (dos operandos fuentes y uno destino), pero realizando operaciones lgicas sobre los bits de dichos operandos, en lugar de aritmticas (por ejemplo, and, or, not, xor). Dentro de este tipo de operaciones tambin encontramos operaciones de manipulacin de bits (desplazamientos de bits a derecha o a izquierda, etc). En este apartado explicaremos algunas de las instrucciones aritmtico-lgicas ms representativas del ensamblador de MIPS, que trabajan con datos de tipo entero (vase la seccin 3.4.5 para las instrucciones que trabajan con nmeros en coma otante). Comenzamos con las instrucciones aritmticas (suma, resta, multiplicacin, etc). Instrucciones aritmticas Las dos instrucciones aritmticas ms comunes son la suma y la resta. Por ejemplo: add rd,rs,rt # rd=rs+rt sub rd,rs,rt # rd=rs-rt Siempre tienen tres operandos. El operando de destino es cualquiera de los 32 registros enteros7 . En el caso de las instrucciones anteriores, los operandos fuente son tambin dos registros enteros cualquiera (que pueden coincidir entre s y con el registro destino). Existe un tipo de instruccin adicional, denominada de tipo inmediato, uno de cuyos operandos no es un registro sino un valor constante (al cual se denomina valor inmediato). Por ejemplo, la siguiente instruccin suma un valor inmediato (una constante entera de cmo mximo 16 bits en complemento a 2) al registro rs para almacenar el resultado en rd: addi rd,rs,inm # rd=rs+inm Por ejemplo:
A la palabra que determina el tipo de operacin en ensamblador se le llama mnemnico de la instruccin; ejemplos de mnemnicos que veremos en este captulo sern add, sub, bne, lw, etc. 7 En realidad, aunque $0 puede ser registro destino, es de slo lectura, y siempre contiene la constante cero. Esto se puede utilizar para incluir en el programa instrucciones que no tienen ningn efecto (nops).
6

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

addi $t0,$s0,100 addi $t0,$s0,-100

# # # #

Suma 100 al registro $s0 y guarda el resultado en $t0. Resta 100 al registro $s0 y guarda el resultado en $t0.

El repertorio de instrucciones de MIPS tambin contiene otras instrucciones aritmticas como la multiplicacin y la divisin. Hay, sin embargo, un par de particularidades que tenemos que tener en cuenta a la hora de trabajar con estas instrucciones. La primera es que el programador debe determinar si la operacin que quiere hacer (multiplicacin o divisin) es con o sin signo. Este problema no apareca al sumar y al restar, puesto que, como sabemos, se suman de igual manera los nmeros sin o con signo en complemento a 2. Sin embargo, al multiplicar, obviamente no es lo mismo, por ejemplo, multiplicar por el valor 0xFFFFFFFF si se considera con signo (que correspondera al valor -1) que si se considera sin signo (que correspondera al valor 232 1). Para diferenciar estos dos tipos de operacin, se emplean mnemnicos diferentes. As, las instrucciones mult y div, respectivamente, multiplicarn o dividirn sus registros fuentes considerndolos con signo, mientras que multu y divu lo harn sin tomar en cuenta ste. La segunda consideracin es que, a diferencia de la suma y la resta, un registro de 32 bits puede no ser sucientemente grande para almacenar el resultado. En efecto, si multiplicamos dos valores de 32 bits, el resultado puede llegar a ocupar hasta 64 bits. Por ello, la instruccin de multiplicacin en MIPS tiene slo dos operandos fuente explcitos y, en lugar de un operando destino indicado directamente en la instruccin, dos registros de 32 bits implcitos llamados LO y HI donde siempre se coloca el resultado de la operacin. As, la instruccin mult $10,$11 # Multiplica $10 por $11, con signo multiplica con signo los valores de los registros $10 y $11 (de 32 bits cada uno) para colocar el resultado en los registros HI y LO. En estos registros se almacenan, respectivamente, las mitades superior (high) e inferior (low) del resultado (que puede tener, por tanto, hasta 64 bits). Por su parte, la instruccin multu $10,$11 # Multiplica $10 por $11, sin signo realiza la misma operacin pero considerando los valores de los registros $10 y $11 como enteros de 32 bits sin signo. El resultado ser un nmero de 64 bits entre 0 y 264 1 almacenado entre los registros HI y LO. Los registros HI y LO no pertenecen al grupo de los 32 registros enteros de uso general, por lo que no pueden utilizarse como operandos de otro tipo de instrucciones. As, si queremos acceder a sus valores, tenemos que pasarlos previamente a alguno de los registros generales del procesador. Para ello, MIPS ofrece las siguientes instrucciones: mflo rd # Mueve el contenido de LO al registro rd mfhi rd # Mueve el contenido de HI al registro rd Por ejemplo para almacenar en el registro $t0 cinco ms el triple del valor de $t1 podramos hacer lo siguiente: addi $t2, $0, 3 # Ponemos la contante 3 en un registro auxiliar mult $t1, $t2 # HI y LO juntos contienen 3 * $t1 mflo $t0 # Copiamos a $t0 los 32 bits menos # significativos del resultado de la # operacin anterior. addi $t0, 5 # Realizamos la suma

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

Obsrvese que en el ejemplo anterior se descartan los 32 bits ms signicativos del resultado de la multi31 31 plicacin, por lo que el resultado solo ser correcto cuando $t1 valga entre 2 y 2 31 . 3 Con las divisiones ocurre un situacin similar: cuando hacemos una divisin entera de dos valores de 32 bits, generalmente nos interesar conocer tanto el cociente como el resto de la operacin y cada uno de ellos puede llegar a ocupar tambin 32 bits. As, la instruccin div $10,$11 # Divide $10 por $11, con signo. divide (con signo) el valor contenido en $10 por el valor contenido en $11, colocando el cociente entero resultante en el registro LO y el resto de la divisin en el registro HI. Y la instruccin divu $10,$11 # Divide $10 por $11, sin signo. opera anlogamente pero sin considerar el signo de los valores de los registros $10 y $11. De nuevo, el programador puede acceder a estos resultados utilizando las instrucciones mfhi y mflo. Desbordamientos Cuando realizamos operaciones aritmticas con operandos de rango limitado puede ocurrir que el resultado de la operacin se encuentre fuera de dicho rango. En el caso de MIPS, el rango de los operandos para la mayora de las operaciones est limitado, en el caso de los enteros con signo, a los valores representables en complemento a 2 con 32 bits (desde 231 hasta 231 1) y, en el caso de los enteros sin signo, a los valores representables en binario natural con 32 bits (desde 0 hasta 232 1). Las situaciones de desbordamiento son fciles de detectar cuando se trabaja con nmeros con signo. En el caso de la suma, por ejemplo, ocurre cuando se suman dos nmeros positivos y el resultado es negativo, o cuando se suman dos nmeros negativos y el resultado es positivo. Por tanto, basta con mirar los bits de signo de los 3 operandos para detectarlo. El ISA de MIPS (y otros ISAs) proporciona mecanismos para detectar estas situaciones cuando se usan operaciones aritmticas sobre enteros con signo (mediante instrucciones como add o addi). La gestin de los desbordamientos, es decir, el procedimiento a seguir cuando se produce un desbordamiento en una operacin aritmtica durante la ejecucin de un programa, depende de la implementacin concreta de cada procesador. En el caso del MIPS, un desbordamiento produce una excepcin (o interrupcin provocada por el software). Esta excepcin produce una llamada no planicada a un procedimiento especial (vase la seccin 3.5 para ms detalles) que se encarga de tratar la situacin (el tratamiento de la excepcin puede consistir en mostrar un mensaje de error, terminar con el programa, o cualquier otra accin adecuada). Como muchas veces el programador quiere hacer operaciones aritmticas sin preocuparse del desbordamiento, MIPS proporciona instrucciones para ignorarlo si ste se produce. Por ejemplo, en el caso de la suma en MIPS, existe la instruccin addu (add unsigned, suma sin considerar el signo), que realiza la suma bit a bit ignorando el posible desbordamiento (es decir, sin provocar la excepcin en caso de que se produzca, y continuando con la ejecucin de forma normal). Instrucciones lgicas y de desplazamiento de bits Las instrucciones lgicas son anlogas a las instrucciones aritmticas, slo que las instrucciones lgicas trabajan sobre los registros fuente bit a bit en lugar de interpretar el contenido de los registros como nmeros (realizando el and o el or lgico de cada bit, por ejemplo). Algunos ejemplos de instrucciones lgicas son: and rd,rs,rt or rd,rs,rt # rd es el AND lgico de los registros rs y rt # rd es el OR lgico de los registros rs y rt

Tambin existen las correspondientes versiones inmediatas: 10

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

andi rd,rs,imm ori rd,rs,imm

# rd es el AND lgico de rs y el valor imm # rd es el OR lgico de rs y el valor imm

Por ejemplo, si el registro $10 contiene el valor 0xf0f0f0f0 y el $9 contiene el valor 0x0ff00000, las siguiente instrucciones: and or andi ori $11,$10,$9 $12,$10,$9 $13,$10,0xff $14,$10,0xff # # # # $11 $11 $11 $11 = = = = $10 $10 $10 $10 & | & & $9 = $9 = 0xff 0xff 0x00f00000 0xfff0f0f0 = 0x000000f0 = 0xf0f0f0ff

almacenarn en los registros $11, $12, $13 y $14 los valores 0x00f00000, 0xfff0f0f0, 0x000000f0 y 0xf0f0f0ff, respectivamente. En el grupo de las instrucciones lgicas suelen tambin aadirse las instrucciones de desplazamiento de bits. Las instrucciones de desplazamiento que veremos son las siguientes: sll rd,rs,n srl rd,rs,n sra rd,rs,n # Desplaza a la izquierda (lgico) # Desplaza a la derecha (lgico) # Desplaza a la derecha (aritmtico)

La instruccin sll desplaza los bits contenidos en el registro rs n posiciones hacia la izquierda, introduciendo n ceros por la derecha y descartando los n bits ms signicativos del valor original. De esta forma, el valor almacenado en rd ser siempre igual al valor en el registro rs multiplicado por 2n . Por ejemplo, si el registro $10 contiene el valor 0xfedcba98, la siguiente instruccin: sll $11,$10,4 # $11 = 0xfedcba98 << 4 = 0xedcba980

almacenar el valor 0xedcba980 en el registro $11. Por su parte, srl funciona de forma similar, pero los bits se desplazan hacia la derecha, introduciendo n ceros por la izquierda y descartando los n bits menos signicativos del valor original. El valor almacenado en el registro rd ser el resultado de dividir el valor en rs por 2n si y slo si el valor de rs no era negativo. El valor resultante siempre ser positivo. Por ejemplo, si volvemos a suponer que el registro $10 contiene el valor 0xfedcba98, la siguiente instruccin: srl $11,$10,4 # $11 = ((unsigned int) 0xfedcba98) >> 4 = 0x0fedcba9

almacenar el valor 0x0fedcba9 en el registro $11. Por ltimo, sra realiza un desplazamiento aritmtico (en lugar de lgico) a la derecha. El funcionamiento es similar a la instruccin anterior, pero en lugar de introducir n ceros por la izquierda, se replica n veces el bit ms signicativo de rs. De esta forma se consigue que el valor almacenado en el registro rd sea siempre el resultado de dividir el valor en rs por 2n , y el valor resultante siempre tendr el mismo signo que tena rs. Por ejemplo, si $10 contiene el valor 0xfedcba98, la siguiente instruccin: sra $11,$10,4 # $11 = ((int) 0xfedcba98) >> 4 = 0xffedcba9

almacenar el valor 0xffedcba9 en el registro $11. Otras instrucciones similares a las de desplazamiento son las de rotacin de bits: rol rd,rs,n ror rd,rs,n # Rota a la izquierda. # Rota a la derecha.

11

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

La instruccin rol desplaza los bits contenidos en el registro rs n posiciones hacia la izquierda, introduciendo n por la derecha y los n bits ms signicativos del valor original. ror funciona anlogamente pero hacia la derecha. As, si el registro $10 contiene el valor 0xfedcba98, las siguientes instrucciones: rol $11,$10,4 ror $12,$10,4 almacenaran en $11 y $12 los valores 0xedcba98f y 0x8fedcba9, respectivamente. Las instrucciones lgicas de desplazamiento y rotacin de bits son muy tiles a la hora de examinar o manipular el valor de los bits individuales almacenados en un registro. Instrucciones de comparacin Finalmente, nos encontramos con las instrucciones de comparacin. En MIPS disponemos de la siguiente instruccin: slt rd,rs,rt # Pone rd a 1 o a 0 si rs es menor o no que rt que compara los valores contenidos en rs y rt (restndolos) y almacena en rd un 1 si el contenido de rs es menor que el de rt y un 0 si es mayor o igual. Las instrucciones de comparacin suelen combinarse con las instrucciones de salto condicional (beq y bne, ver seccin 3.4.3) para realizar saltos condicionales dependiendo de si un valor es mayor, menor, mayor o igual o menor o igual que otro.

3.4.2.

Instrucciones de acceso a memoria

Como se ha visto ya, la mayora de las instrucciones de MIPS operan slo con registros y constantes. Sin embargo, en el banco de registros caben muy pocos datos, mientras que los programas actuales necesitan manejar grandes cantidades de informacin que no queda ms remedio que almacenar en la memoria, de mucha ms capacidad. Por ello, para trabajar con esa informacin es necesario moverla primero de la memoria a los registros y volverla a mover despus de los registros a la memoria. Estas transferencias de informacin entre registros y memoria se realizan mediante las instrucciones de acceso a memoria. Siguiendo con la analoga de la carpintera utilizada anteriormente (ver seccin 3.3.1), estas instrucciones se encargaran de traer el material a la mesa de trabajo del carpintero y de llevar la silla, una vez fabricada, de vuelta al almacn. Hay dos tipos de instrucciones de transferencia de memoria: las de carga, que copian en un registro un dato que se encuentra en una determinada direccin de memoria; y las de almacenamiento, que copian en una determinada direccin de memoria un dato que se encuentra en un registro. Desde el punto de vista del ISA, la memoria est estructurada como una gran tabla unidimensional, y la direccin de memoria acta como ndice de esa tabla. En concreto, en MIPS las direcciones tienen 32 bits, y cada una de las 232 posibles direcciones apunta a una posicin de memoria que contiene un byte. Por tanto, hay espacio para 232 bytes (4 GB) o, alternativamente, 230 palabras de 4 bytes, almacenadas cada 4 posiciones de memoria. Como ya se ha dicho, para leer datos de memoria se utilizan las instrucciones de carga (load). Por ejemplo, en el repertorio de instrucciones de MIPS existe la instruccin lw rt,direccin # Load word (carga palabra)

12

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

que lee los cuatro bytes almacenados en la posicin indicada por direccin y en las tres siguientes y los escribe en el registro rt. La direccin se expresa con el modo de direccionamiento base ms desplazamiento8 , es decir mediante un registro y un desplazamiento constante de 16 bits (positivo o negativo). Por ejemplo, si $t1 contiene el valor 0x1004000, la instruccin: lw $t2,8($t1) leer las posiciones de memoria desde 0x1004008 a 0x100400b inclusive y copiar los datos en el registro $t2. El resultado no ser el mismo si los bits del primer byte ledo de memoria se copian en los 8 bits ms signicativos del registro, los bits del segundo byte de memoria en los 8 bits siguientes y as sucesivamente, que si los bytes de memoria se van colocando en el registro empezando por los bits menos signicativos del registro. Al primer modo de proceder se le denomina big-endian byte order, mientras que al segundo se le denomina little-endian byte order. La mayora de las implementaciones del ISA del MIPS son bigendian, aunque tambin existen implementaciones little-endian e incluso algunas que pueden operar de las dos maneras. Nosotros asumiremos ordenacin little-endian (el byte apuntado por la direccin de memoria corresponde al byte de menos peso de una palabra de 32 bits) siempre que no se indique lo contrario9 . Si en la memoria estuvieran almacenados los bytes 0x12, 0x34, 0x56 y 0x78 en las direcciones 0x1004008, 0x1004009, 0x100400a y 0x100400b respectivamente, la instruccin del ejemplo anterior escribira en $t2 el valor 0x12345678 en una arquitectura big-endian o el valor 0x78563412 en una arquitectura little-endian. lw es una instruccin de carga alineada. Es decir, la direccin de memoria del primer byte a leer debe ser mltiplo del tamao del dato (una palabra de 4 bytes en este caso). Todas las instrucciones de acceso a memoria que veremos son alineadas10 . La mayora de las veces los datos estarn colocados adecuadamente en memoria para acceder directamente a ellos con instrucciones alineadas, pero en ocasiones ser necesario utilizar varias instrucciones alineadas (que accedan a datos ms pequeos) para cargar un dato que se encuentre en memoria en una posicin no alineada con su tamao. Las instrucciones de almacenamiento son anlogas a las de carga. Por ejemplo, tenemos la instruccin sw rt,direccin # Store word (almacena palabra)

que copia el contenido de rt en los cuatro bytes de memoria indicados por direccin. Por ejemplo, si $t1 contiene el valor 0x1004000 y $t2 contiene el valor 0x12345678, la instruccin: sw $t2,8($t1) escribir 0x78, 0x56, 0x34, 0x12 en las posiciones de memoria 0x1004008, 0x1004009, 0x100400a y 0x100400b, respectivamente, cuando se usa una ordenacin de bytes littleendian. Al igual que las instrucciones lw y sw que sirven para cargar y almacenar palabras (4 bytes), existen otras instrucciones para trabajar con bytes individuales, o con valores de 16 bits (llamados medias palabras). As, tambin estn disponibles las siguientes instrucciones: lb rt,direccin lbu rt,despl(rs) sb rt,direccin # Carga un byte con signo # Carga un byte sin signo # Almacena un byte

8 Aunque ste es el nico modo de direccionamiento admitido por las instrucciones de acceso a memoria a nivel de cdigo mquina, el ensamblador ofrece algunos modos de direccionamiento adicionales que se implementan mediante pseudoinstrucciones (vase la seccin A3.2.1). 9 MARS, el simulador de MIPS que usamos en la asignatura, usa ordenamiento little-endian. 10 De hecho, en MIPS todas las instrucciones de acceso a memoria son alineadas aunque existen pseudoinstrucciones para acceder a memoria con direcciones no alineadas (p.e.: ulw). Algunas otras ISAs incluyen instrucciones de acceso a memoria no alineado.

13

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

lh rt,direccin lhu rt,despl(rs) sh rt,direccin

# Carga media palabra (2 bytes) con signo # Carga media palabra (2 bytes) sin signo # Almacena media palabra (2 bytes)

Estas instrucciones obtienen la direccin de memoria exactamente igual que lw y sw, aunque en este caso la direccin no tendr que ser mltiplo de 4, sino de 2 en el caso de lh, lhu y sh, y de 1 en el caso de lb, lbu y sb. Todas estas instrucciones trabajan con los 8 o 16 bits de menos peso del registro rt. En el caso de los almacenamientos, se copiar en el byte/media palabra de memoria direccionado los 8 o 16 bits de menor peso del registro rt. En el caso de las cargas, el valor ledo de memoria se copiar en los 8 o 16 bits de menor peso del registro rt y los 24 o 16 bits superiores se rellenarn replicando el bit de signo del byte/palabra cargado en el caso de lb y lh o con ceros en el caso de lbu y lhu. Por ejemplo, si la direccin de memoria 0x1001000b contiene el valor 0xf1, y $t0 contiene el valor 0x10010000, la instruccin lb $t1,0xb($t0) cargar el valor 0xfffffff1 en el registro $t1. Por el contrario, la instruccin lbu $t1,0xb($t0) cargar el valor 0x000000f1 en $t1. Traduccin de acceso a arrays Los datos usados por un programa normalmente se almacenan en memoria usando estructuras de datos. Unas de las estructuras ms usadas y ms simples son los arrays. Un array almacena consecutivamente en memoria varios elementos del mismo tipo (y tamao), de forma similar a una tabla. Los elementos almacenados se identican mediante nmeros enteros consecutivos llamados ndices. En C y muchos otros lenguajes, el primer elemento tiene el ndice 0 y al elemento i-simo del array a se le denota a[i]. Para utilizar un array en ensamblador es necesario conocer: 1. La direccin de memoria a partir de la que estn almacenados los elementos del array (base). Esta direccin puede ser un argumento de un procedimiento (en cuyo caso estar almacenada en un registro) o una etiqueta del programa (en cuyo caso habr que moverla a un registro con la instruccin la). 2. El tamao de cada elemento del array (t). 3. El ndice del elemento al que queremos acceder, ya sea para leer o escribir (i). Supondremos que el ndice del primer elemento es 0. Para acceder a un elemento de un array, tanto para lectura como para escritura, primero es necesario calcular su direccin, que vendr dada por la expresin base + t i. Para realizar la multiplicacin se deber utilizar la instruccin sll cuando t sea potencia de 2, lo cual es muy frecuente. Por ejemplo, el siguiente fragmento de cdigo: int v[20]; ... v[i] = v[i + 1]; 14

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

se podra traducir de la siguiente forma si suponemos que i es una variable entera y est almacenada en el registro $s0: .data .space 80

v: ...

# 20 elementos de 4 bytes cada uno

.code la $t0,v sll $t1,$s0,2 add $t2,$t0,$t1 lw $t4, 4($t2) sw $t4, 0($t2)

# # # # # # #

Carga la base de v en $t0 Calcula el desplazamiento desde el inicio del array hasta el elemento i-simo: 4 * i Calcula la direccin de v[i] Lee v[i+1], cuya direccin es 4 bytes mayor que la de v[i] Escribe v[i]

3.4.3.

Instrucciones de salto

La ejecucin normal de los programas se realiza en orden secuencial, es decir, las instrucciones se ejecutan una tras otra tal y como estn situadas en memoria, incrementando cada vez en 4 el valor del registro contador de programa (PC), ya que cada instruccin ocupa 4 bytes. Sin embargo se caracteriza por la capacidad de tomar decisiones. Esto se traslada en cambios en el ujo de ejecucin del programa en funcin del valor de algunos operandos (registros en MIPS). Es decir, se ejecutar un cdigo determinado si se cumple una condicin, u otro trozo de cdigo si no se cumple. El repertorio de instrucciones MIPS proporciona varias instrucciones para alterar el ujo del programa en funcin de una condicin. Estas instrucciones se conocen como saltos condicionales debido a que el salto, es decir, la alteracin del orden de ejecucin, se produce en funcin de una condicin: beq rs,rt,etiq # Salta a la direccin del programa situado en # etiq si el contenido de los dos registros # es igual. # Salta a la direccin del programa situada en # etiq si el contenido de los dos registros # es diferente.

bne rs,rt,etiq

Tambin existen instrucciones de salto incondicional, en las que el cambio en el ujo de ejecucin del programa se realiza siempre que se ejecuta la instruccin, independientemente de ninguna otra condicin: j etiqueta # Salta a la instruccin en la # direccin apuntada por etiqueta. # Salta a la instruccin en la # direccin contenida en rd.

jr rd

Traduccin de expresiones condicionales Los programas de alto nivel incluyen instrucciones que, dependiendo de los datos de entrada y los creados durante la computacin, hacen que el computador ejecute un camino del programa u otro. Un ejemplo de instruccin de toma de decisiones es la construccin condicional 15

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

if <condicin> then <cdigo a ejecutar si la condicin es cierta> else <cdigo a ejecutar si la condicin es falsa> que se encuentra, con una u otra sintaxis, en prcticamente todos los lenguajes de alto nivel. Este cdigo analiza la condicin (la cual puede ser, por ejemplo, una comparacin de magnitud entre dos valores), y ejecuta las instrucciones que hay despus del then, si se cumple la condicin, o las que hay despus del else en caso contrario. La manera de traducir estas instrucciones a ensamblador es a travs de combinaciones de las instrucciones de comparacin y las de salto (ver secciones 3.4.1 y 3.4.3). As, por ejemplo, la secuencia slt $10,$11,$12 bne $10,$0,etiqueta servira para saltar a la direccin apuntada por etiqueta si $11 contiene un valor menor que $12. Jugando con el orden de los registros comparados por la primera instruccin (slt) y usando como segunda instruccin bne y beq con el registro $0 como uno de los operandos, pueden realizarse todos los posibles saltos condicionales en funcin de la comparacin de dos enteros. El ensamblador de MIPS proporciona instrucciones y pseudoinstrucciones para realizar saltos condicionales en funcin de cualquier comparacin de valores enteros, como se puede ver en la tabla 2. Tabla 2: Instrucciones y pseudoinstrucciones para realizar saltos condicionales. (Pseudo)instruccin beq rs,rt,etiq bne rs,rt,etiq bgt rs,rt,etiq blt rs,rt,etiq bge rs,rt,etiq ble rs,rt,etiq Comparacin = = > < Expansin en instrucciones No aplicable No aplicable slt $at,rt,rs bne $at,$0,etiq slt $at,rs,rt bne $at,$0,etiq slt $at,rs,rt beq $at,$0,etiq slt $at,rt,rs beq $at,$0,etiq

Utilizando las instrucciones anteriores, el siguiente fragmento de programa en C: if (i < j) k = k + 1; k = 4 * k se podra traducir en ensamblador de la siguiente manera, suponiendo que las variables i, j y k estn almacenadas en los registros $t0, $t1 y $t2, respectivamente: slt $t3,$t0,$t1 beq $t3,$0,l1 addi $t2,$t2,1 l1: sll $t2,$t2,2 # # # # $t3 = 1 si i < j Salta hasta "l1" Se ejecuta si la Se ejecuta si la 16 o 0 si i >= j si la condicin es falsa condicin es cierta condicin es cierta o falsa

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

Obsrvese que las dos primeras instrucciones se podran substituir por bge $t0,$t1,l111 (que salta a l1 cuando es cierta la condicin opuesta a la del fragmento de cdigo en C). Si el fragmento a traducir tuviera una parte a ejecutar si no se cumple la condicin, se procedera de manera similar. Por ejemplo: if (i k else k k = k >= j) = k * i; = 4 * k + j

Se podra traducir (suponiendo que las variables se encuentran en los mismos registros que en el ejemplo anterior): blt $t0,$1,l1 mult $t2,$t0 mflo $t2 j l2 l1: sll $t2,$t2,2 l2: add $t2,$t2,$t1 Traduccin de bucles Los lenguajes de alto nivel tambin ofrecen construcciones para repetir varias veces la ejecucin de algunas instrucciones. Hay diversas de estas construcciones (bucles for, while, repeat. . . ). Cada iteracin se repite el mismo conjunto de instrucciones, aunque el valor de las variables ser (normalmente) distinto. Por ejemplo, el siguiente fragmento de cdigo en C calcula la suma de los elementos de un array de n elementos enteros llamado a y deja el resultado en la variable s: int s = 0; for (int i = 0; i < n; i++) { s = s + a[i] } Los bucles tambin se traducen mediante combinaciones de instrucciones de comparacin y de salto (ver secciones 3.4.1 y 3.4.3). Si suponemos que las variables p, i y n estn almacenadas en los registros $v0, $t0 y $a1 respectivamente, y que la direccin de inicio del array a est almacenada en $a0, el ejemplo anterior se podra traducir as: addi $v0,$0,0 # Inicializacin de p addi $t0,$0,0 # Inicializacin de i l1: bge $t0,$a1,l2 # Comprobacin de la condicin # Comienzo del cuerpo del bucle sll $t1,$t0,2 # Clculo del desplazamiento de a[i] en a add $t2,$a0,$t1 # Clculo de la direccin de a[i] lw $t3,0($t2) # Carga de a[i] add $v0,$v0,$t3 # Suma a p # Fin del cuerpo del bucle addi $t0,$t0,1 # Incrementa i j l1 # Salta a la siguiente iteracin l2:
11

# # # # # #

Salta hasta "l1" si la condicin es falsa Se ejecuta si la condicin es cierta Se ejecuta si la condicin es cierta evita ejecutar la parte siguiente Se ejecuta si la condicin es falsa Se ejecuta si la condicin es cierta o falsa

Al hacer esta substitucin, ya no se usara el registro $t3 para almacenar el resultado de la comparacin, sino el registro $at

17

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

3.4.4.

Instrucciones para soportar procedimientos

Los procedimientos son una herramienta que los programadores usan para estructurar mejor los programas. Permiten que el programa sea ms legible y tambin permiten que el cdigo sea reutilizado (tanto varias veces dentro de un mismo programa, como incluso para otros programas). Los procedimientos podran denirse como secuencias de instrucciones que ejecutan una tarea determinada sobre unos operandos de entrada para producir unos determinados resultados. En la jerga de la programacin en ensamblador, a los procedimientos a menudo se les llama tambin rutinas o subrutinas. Algunos lenguajes, como C, llaman funciones a los procedimientos. Un procedimiento se caracteriza por tener unos parmetros de entrada, que son sobre los que opera, y unas variables de salida, en donde guarda los resultados para su uso posterior en el programa. Los procedimientos pueden usar variables locales, que son privadas a cada invocacin del procedimiento. Los procedimientos tambin pueden leer y modicar variables globales, que son compartidas entre varios procedimientos. En general, un programa se estructura como una serie de procedimientos que se llaman entre s. Cuando un procedimiento quiere llamar a otro, al procedimiento que realiza la llamada se le denomina invocador (caller) y al que es llamado se le denomina invocado (callee). Estos dos procedimientos deben seguir los siguientes pasos: 1. El invocador sita los parmetros en un lugar donde el invocado pueda acceder a ellos. 2. Se transere el control al invocado. 3. El invocado adquiere los recursos de almacenamiento (memoria y registros) necesarios. 4. El invocado realiza la tarea deseada. 5. El invocado sita los resultados en un lugar donde el invocador pueda acceder a ellos. 6. El invocado libera los recursos adquiridos en el paso 3. 7. Se retorna el control al punto de invocacin del procedimiento, que ser distinto para invocaciones distintas del mismo procedimiento. Las distintas ISAs tienen que ofrecer instrucciones que den soporte eciente a los procedimientos. Adems, son necesarias una serie de convenciones a nivel del ABI para organizar el paso de parmetros entre procedimientos y para que los procedimientos puedan compartir los registros y la memoria sin estorbarse entre s. En MIPS, disponemos de la siguiente instruccin para transferir el control a un procedimiento de forma que luego se pueda regresar a la posicin anterior del programa12 : jal etiqueta # # # # Salta a la instruccin en la direccin apuntada por etiqueta, guardando la direccin de la instruccin siguiente en $ra ($31)

que sirve para almacenar en el registro $ra la direccin de la instruccin siguiente y transferir la ejecucin del programa al principio de la subrutina cuyo cdigo comienza en la direccin apuntada por etiqueta. Gracias a que la direccin de la instruccin siguiente se almacena en $ra, la subrutina invocada puede devolver el control de la ejecucin al cdigo invocador mediante la instruccin jr: jr $ra
12

# Salta a la direccin contenida en $ra ($31)

La instruccin jal es anloga a la instruccin j que se vio en la seccin 3.4.3. Tambin se dispone de la instruccin jalr, anloga a la instruccin jr.

18

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

El paso de parmetros se realiza mediante un conjunto de registros reservados para ello. Como se mencion en la seccin 3.3.1, estos registros son $a0, $a1, $a2 y $a3 para los parmetros enteros (y $f12, $f13, $f14, $f15 para valores en coma otante); y $v0 y $v1 para los resultados enteros (y $f0 y $f1 para valores en coma otante). Si el espacio de almacenamiento ofrecido por dichos registros no es suciente para los parmetros o los resultados, se utiliza tambin la pila (como se explicar en la seccin 3.4.4). Con todo lo anterior, veamos cmo se traduce a MIPS la siguiente funcin escrita en C: int ejemplo1(int g, int h, int i, int j) { f = (g + h) - (i + j); return f; } Como g, h, i y j son los parmetros (o argumentos) de la funcin, estarn en los registros $a0, $a1, $a2 y $a3, respectivamente. Por otro lado, f es el valor que se retorna y estar en el registro $v0. De esta forma, se podra traducir as: ejemplo1: add $t0,$a0,$a1 add $t1,$a2,$a3 sub $v0,$t0,$t1 # # # # # # # etiqueta para llamar al procedimiento Suma de g y h Suma de i y j Resta de las dos suma, el resultado se almacena en $v0 por ser el resultado de la funcin Volvemos al programa principal

jr $ra

Obsrvese cmo se utilizan los registros $t0 y $t1 para almacenar los valores intermedios. Como se explicar a continuacin, estos registros son no preservados entre llamadas, por lo que el procedimiento puede usarlos libremente. Normalmente los procedimientos llaman a su vez a otros procedimientos, y esto hace que surjan algunos problemas. Por ejemplo, si un procedimiento es invocado y se le pasa un parmetro por el registro $a0, y ste a su vez tiene que llamar a un segundo procedimiento que tambin espera un parmetro en el registro $a0, se perdera el valor que haba antes de realizar la llamada al segundo procedimiento. Un problema similar surge con la direccin de retorno almacenada en $ra. Imaginemos que el procedimiento A llama a B, guardando en $ra la direccin de retorno de B a A. Si B a su vez llama al procedimiento C, la direccin de retorno de C a B se guardar en $ra, perdiendo el valor anterior, que era el de retorno de B a A. De hecho, el mismo problema surge con cualquier registro. Estos problemas se pueden resolver utilizando la pila. Hay dos opciones sobre cmo proceder: 1. En el procedimiento invocador y antes de llamar al procedimiento invocado, guardar los registros cuyo valor se quiera utilizar despus de que el procedimiento invocado acabe. Despus de realizar la llamada, se restaura el valor de los registros usando los valores guardados. 2. En el procedimiento invocado, guardar el valor de los registros que se vayan a modicar antes de operar sobre ellos. Los valores antiguos se restauran justo antes de devolver el control al procedimiento invocador. En MIPS se utilizan los dos mtodos anteriores, dependiendo del registro involucrado13 :
La razn de MIPS para ofrecer los dos tipos de registros es de eciencia, ya que se ahorran operaciones de apilado y desapilado. As, los registros no preservados entre llamadas pueden utilizarse libremente en un procedimiento sin tener que guardar antes su contenido; y a la vez, los registros preservados entre llamadas no tienen que guardarse cada vez que se llama a otro procedimiento.
13

19

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

Registros no preservados entre llamadas: son registros cuyo valor no se conserva cuando se llama a un procedimiento. Por tanto, es responsabilidad del procedimiento que realiza la llamada guardar su valor usando el mtodo 1 si quiere usarlo despus de la llamada. Los registros de este tipo son $at, $v0, $v1, $v2, $v3, $a0, $a1, $a2, $a3, $t0, $t1, $t2, $t3, $t4, $t5, $t6, $t7, $t8, $t9, $k0 y $k1. Tambin son de este tipo los registros de coma otante $f0 a $f19. Registros preservados entre llamadas: son registros cuyo valor s se conserva cuando se llama a un procedimiento. El procedimiento invocado tiene la responsabilidad de que, cuando el control se devuelva al procedimiento invocador, el contenido de los registros sea el mismo que antes de realizar la llamada. Para ello, el procedimiento invocado debe guardar todos los registros de este tipo antes de modicarlos, usando el mtodo 2. Los registros enteros de este tipo son $s0, $s1, $s3, $s4, $s5, $s6, $s7, $gp, $sp, $fp y $ra. Tambin son de este tipo los registros de coma otante $f20 a $f31. Por ltimo, los procedimientos necesitan espacio de almacenamiento en memoria para realizar su trabajo (para almacenar variables locales). Este espacio tambin se reserva en la pila. La pila en MIPS La pila es una zona de memoria cuyo uso sigue una serie de normas. Siguiendo estas normas, se consigue que varios procedimientos puedan usarla para: 1. Almacenar valores de registros para conservar su valor cuando se realizan llamadas a procedimientos. 2. Comunicar parmetros y resultados. 3. Conseguir memoria para variables temporales. El acceso a la pila se hace de tal forma que el ltimo elemento que entra es el primero que sale (LIFO, Last In First Out). Este modo de funcionar es adecuado para que los procedimientos que se llamen entre s (e incluso para que un procedimiento que se llame a s mismo) puedan compartir la pila, siempre y cuando desapilen todo lo que hayan apilado antes de devolver el control al procedimiento invocador. Las dos posibles operaciones que se pueden realizar sobre una pila son: Apilar (push): aade un valor la pila. Desapilar (pop): recupera el ltimo elemento apilado, eliminndolo de la pila. Llamaremos cima de la pila a la posicin de memoria de la ltima palabra (4 bytes) apilada. En MIPS, se utiliza el registro $sp para apuntar a la cima actual de la pila (es decir $sp siempre apunta al ltimo elemento apilado). Al apilar y desapilar elementos, la cima de la pila cambia. En MIPS, cuando se apilan elementos la cima se mueve a direcciones ms bajas ($sp se decrementa), mientras que cuando se desapilan la cima sube ($sp se incrementa). Por ejemplo, en la gura 1 se muestra el estado de una pila tras el apilamiento de tres valores A, B y C, en ese orden. Para apilar un valor, se ajusta $sp decrementndolo (normalmente 4 unidades, para hacer sitio para una palabra de 4 bytes) y se escribe el valor en la nueva cima. Por ejemplo, la gura 2a muestra el estado de la pila anterior despus de apilar un nuevo valor D. Para ello, si el valor D estuviera almacenado en el registro $s1, se podra haber utilizado el siguiente cdigo: addi $sp,$sp,-4 sw $s1, 0($sp) # Hace sitio en la pila # Copia el valor a almacenar en la cima

20

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

Direcciones ms altas de memoria

A B C

$sp

Direcciones ms bajas de memoria

...

Figura 1: Pila con tres valores apilados (A, B y C).

A B C D

$sp

A B C D

$sp

A B C E

$sp

...

...

...

(a) Pila con cuatro valores apilados (A, B, C y D).

(b) Pila con tres valores apilados (A, B y C). El valor D ser sobreescrito cuando se apile E.

(c) Pila con cuatro valores apilados (A, B, C y E).

Figura 2: Evolucin de la pila al apilar y desapilar.

21

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

Para desapilar un valor, basta con leerlo y ajustar $sp, incrementndolo para que apunte a la nueva cima. En la gura 2b se puede ver el estado de la pila despus de desapilar D. Para ello, se habra utilizado el siguiente cdigo: lw $s1, 0($sp) addi $sp,$sp,4 # Lee el valor de la cima y lo escribe en $s1 # Devuelve el espacio a la pila

Obsrvese que el valor de D sigue almacenado en memoria despus de desapilarlo, pero ser sobreescrito cuando se apile un nuevo valor, como se puede ver en la gura 2c. Adems de almacenar el valor de registros, la pila tambin se puede usar para conseguir memoria para las variables locales de un procedimiento. Para ello, basta con, cuando se entra en el procedimiento, decrementar $sp tantos bytes como sea necesario para almacenar las variables, incrementndolo en la misma cantidad antes de abandonar el procedimiento. Teniendo en cuenta todo lo visto, el siguiente procedimiento: int ejemplo2(int a) { int x = a + 7; int y = a; int p = ejemplo1(x, y, 2, 3); return p + y; } se podra traducir as: ejemplo2: addi $sp,$sp,-8 sw $s0,4($sp) sw $ra,0($sp) addi $t0,$a0,7 add $s0,$0,$a0 add $a0,$0,$t0 add $a1,$0,$s0 addi $a2,$0,2 addi $a3,$0,3 jal ejemplo1 add $v0,$v0,$s0 lw $ra,0($sp) lw $s0,4($sp) addi $sp,$sp,8 jr $ra # # # # # # # # # # # # # # # # etiqueta para llamar al procedimiento hace sitio en la pila para guardar 2 registros apila $s0 apila $ra calcula x en $t0 calcula y en $s0 primer argumento para llamar a ejemplo1 segundo argumento para llamar a ejemplo1 tercer argumento para llamar a ejemplo1 cuarto argumento para llamar a ejemplo1 llamada a ejemplo1, p queda en $v0 suma p e y, calculando el resultado final en $v0 recupera el valor de $ra recupera el valor de $s0 devuelve el espacio usado a la pila vuelve al invocador

Obsrvese cmo se uitiliza la pila para guardar los registros preservados entre llamadas que son modicados por ejemplo2 y cmo se recupera su valor anterior antes de la instruccin jr. Se guarda el registro $ra, ya que se modica en la lnea 11 (instruccin jal), y el registro $s0 que se modica en la lnea 4. Se ha asignado la variable x al registro $t0 y la variable y al $s0. En general, lo ms comn es asignar las variables a los registros temporales no preservados entre llamadas para evitar tener que almacenar su valor en la pila, tal como se ha hecho con la variable x. Sin embargo, la variable y se ha asignado a un registro preservado entre llamadas ($s0) para poder utilizar su valor despus de la llamada a ejemplo1. Si se hubiera asignado la variable y a un registro no preservado entre llamadas como $t1, sera necesario almacenarlo en la pila antes de llamar a ejemplo1, as:

22

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

ejemplo2: addi $sp,$sp,-8 sw $ra,4($sp) addi $t0,$a0,7 add $t1,$0,$a0 add $a0,$0,$t0 add $a1,$0,$s0 addi $a2,$0,2 addi $a3,$0,3 sw $t1,0($sp) jal ejemplo1 lw $t1,0($sp) add $v0,$v0,$t1 lw $ra,4($sp) addi $sp,$sp,8 jr $ra

# # # # # # # # # # # # # # # #

etiqueta para llamar al procedimiento hace sitio en la pila para guardar 2 registros apila $ra calcula x en $t0 calcula y en $t1 primer argumento para llamar a ejemplo1 segundo argumento para llamar a ejemplo1 tercer argumento para llamar a ejemplo1 cuarto argumento para llamar a ejemplo1 apila $t1 llamada a ejemplo1, p queda en $v0 desapila $t1 suma p e y, calculando el resultado final en $v0 recupera el valor de $ra devuelve el espacio usado a la pila vuelve al invocador

De esta forma no es necesario apilar $s0 (porque no se usa) pero es necesario apilar $t1 para conservar su valor despus del jal, con lo que el nmero de instrucciones y de accesos a memoria permanece igual. Una tercera opcin sera asignar las dos variables a dos registros preservados entre llamadas (x a $s0 e y a $s1, por ejemplo), pero en ese caso habra que realizar ms accesos a memoria para apilar ms registros: ejemplo2: addi $sp,$sp,-12 sw $ra,8($sp) sw $s0,4($sp) sw $s1,0($sp) addi $s0,$a0,7 add $s1,$0,$a0 add $a0,$0,$s0 add $a1,$0,$s1 addi $a2,$0,2 addi $a3,$0,3 jal ejemplo1 add $v0,$v0,$s1 lw $s1,0($sp) lw $s0,4($sp) lw $ra,8($sp) addi $sp,$sp,12 jr $ra # # # # # # # # # # # # # # # # # # etiqueta para llamar al procedimiento hace sitio en la pila para guardar 3 registros apila $ra apila $s0 apila $s1 calcula x en $s0 calcula y en $s1 primer argumento para llamar a ejemplo1 segundo argumento para llamar a ejemplo1 tercer argumento para llamar a ejemplo1 cuarto argumento para llamar a ejemplo1 llamada a ejemplo1, p queda en $v0 suma p e y, calculando el resultado final en $v0 recupera el valor de $s1 recupera el valor de $s0 recupera el valor de $ra devuelve el espacio usado a la pila vuelve al invocador

3.4.5.

Instrucciones de coma otante

Hasta ahora todas las instrucciones que hemos visto trabajan con valores enteros, y por ello utilizan los 32 registros de uso general, $0 a $31. Pero MIPS tambin permite trabajar con datos en punto otante. Para ello, ofrece un conjunto adicional de instrucciones que trabajan con 32 registros adicionales que se llaman $f0 a $f31, y se encuentran en una zona del procesador llamada coprocesador 1. Cada uno de esos registros permite almacenar 32 bits. MIPS trabaja con el estndar IEEE-754 para representacin de nmeros en coma otante, por lo que cada uno de estos registros puede contener un dato en simple precisin. Alternativamente, estos registros tambin 23

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

se utilizan por pares ($f0 y $f1, $f2 y $f3, $f4 y $f5. . . ) en algunas instrucciones para contener valores en doble precisin (64 bits). En este ltimo caso, el nombre del registro es el correspondiente al nmero par. Es decir, se puede disponer de 16 registros de 64 bits llamados $f0, $f2, $f4. . . . A la hora de pasar valores en coma otante entre procedimientos, se utilizan los registros $f12, $f13, $f14 y $f15 para pasar argumentos a un procedimiento y los registros $f0 y $f1 para devolver los resultados. Los registros $f20 al $f31 se consideran preservados entre llamadas (ver seccin 3.4.4). Al igual que ocurra con las instrucciones enteras, existen instrucciones para cargar y almacenar datos en punto otante, para operar aritmticamente y para realizar comparaciones. A continuacin se muestran algunos ejemplos de cada tipo. Conversiones de tipo de datos El banco de registros del coprocesador 1, tambin llamado banco de registros en coma otante (o punto otante), contiene 32 registros de 32 bits. A pesar de su nombre, estos registros pueden contener un dato de cualquier tipo, al igual que los 32 registros de propsito general del banco principal de registros. En ensamblador, cada instruccin puede interpretar la misma secuencia de bits de diferente forma. En realidad, la diferencia principal14 entre una instruccin de suma de enteros y una instruccin de suma en punto otante de simple precisin es la forma en la que interpretan las dos secuencias de 32 bits con las que operan. En el primer caso, cada secuencia se interpreta como un nmero entero codicado en complemento a 2. En el segundo caso, cada secuencia se interpreta como un nmero en coma otante representado mediante el estndar IEEE-754. Muy frecuentemente es necesario convertir un dato representado en complemento a 2 a su equivalente representado en coma otante mediante el estndar IEEE-754. Para ello, MIPS proporciona las instrucciones cvt.s.w y cvt.d.w: cvt.s.w $f0, $f1 # Pone en $f0 la representacin en IEEE-754 de # simple precisin del entero en complemento # a 2 almacenado en $f1. # Pone en $f0 y $f1 la representacin en # IEEE-754 de doble precisin del entero en # complemento a 2 almacenado en $f1.

cvt.d.w $f0, $f2

Para realizar la conversin en sentido contrario se proporciona ms variedad de instrucciones que se diferencian entre s en la forma en la que realizan el redondeo. Estas instrucciones son: cvt.w.s $f0, $f1 # # # # # # # # # # # # # Pone en $f0 la representacin en complemento a 2 del entero resultado de truncar el valor en coma flotante de simple precisin IEEE-754 almacenado en $f1 Pone en $f0 la representacin en complemento a 2 del entero resultado de truncar el valor en coma flotante de doble precisin IEEE-754 almacenado en $f2 y $f3 Pone en $f0 la representacin en complemento a 2 del entero resultado de truncar el valor en coma flotante de simple precisin IEEE-754 almacenado en $f1 Pone en $f0 la representacin en complemento

cvt.w.d $f0, $f2

trunc.w.s $f0, $f1

trunc.w.d $f0, $f2


14

Por supuesto, hay ms diferencias. Por ejemplo, cada una de las instrucciones mencionadas obtiene el par de secuencias de 32 bits con el que opera de bancos de registros diferentes.

24

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

round.w.s $f0, $f1

round.w.d $f0, $f2

ceil.w.s $f0, $f1

ceil.w.d $f0, $f2

floor.w.s $f0, $f1

floor.w.d $f0, $f2

# # # # # # # # # # # # # # # # # # # # # # # # # # #

a 2 del entero resultado de truncar el valor en coma flotante de doble precisin IEEE-754 almacenado en $f2 y $f3 Pone en $f0 la representacin en complemento a 2 del entero ms cercano al valor en coma flotante de simple precisin IEEE-754 almacenado en $f1 Pone en $f0 la representacin en complemento a 2 del entero ms cercano al valor en coma flotante de doble precisin IEEE-754 almacenado en $f2 y $f3 Pone en $f0 la representacin en complemento a 2 del menor entero mayor o igual al valor en coma flotante de simple precisin IEEE-754 almacenado en $f1 Pone en $f0 la representacin en complemento a 2 del menor entero mayor o igual al valor en coma flotante de doble precisin IEEE-754 almacenado en $f2 y $f3 Pone en $f0 la representacin en complemento a 2 del mayor entero menor o igual al valor en coma flotante de simple precisin IEEE-754 almacenado en $f1 Pone en $f0 la representacin en complemento a 2 del mayor entero menor o igual al valor en coma flotante de doble precisin IEEE-754 almacenado en $f2 y $f3

La diferencia entre el comportamiento de las instrucciones anteriores se muestra en la tabla 3 (cvt.w.s y cvt.w.d se comportan exactamente igual que trunc.w.s y trunc.w.d, respectivamente). Tabla 3: Comparacin de las instrucciones de conversin de coma otante a entero ofrecidas por MIPS. Coma otante -3.7 -3.5 -3.3 -3.0 3.0 3.3 3.5 3.7 cvt.w.s -3 -3 -3 -3 3 3 3 3 trunc.w.s -3 -3 -3 -3 3 3 3 3 round.w.s -4 -4 -3 -3 3 3 4 4 ceil.w.s -3 -3 -3 -3 3 4 4 4 floor.w.s -4 -4 -4 -3 3 3 3 3

Movimiento de datos entre bancos de registros Frecuentemente es necesario llevar un dato del banco de registros de enteros al banco de registros de coma otante para operar con l, despus de realizar las conversiones de tipo correspondientes. Para ello, se dispone de las instrucciones mfc1 y mtc1:

25

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

mfc1 $t1, $f1 mtc1 $t1, $f1 mfc1.d $t1, $f1 mtc1.d $t1, $f1

# # # # # #

Copia el contenido del registro Copia el contenido del registro Copia el contenido del registro y el contenido de $f2 a $t2 Copia el contenido del registro y el contenido de $t2 a $f2

$f1 a $t1 $t1 a $f1 $f1 a $t1 $t1 a $f1

Obsrvese el orden inusual de los operandos de las instrucciones mfc1 y mfc1.d. Las instrucciones mfc1.d y mtc1.d son pseudoinstrucciones que se traducen en pares de las otras dos instrucciones. Estas instrucciones copian los 32 (o 64) bits de un registro a otro sin realizar ninguna transformacin, por lo que habitualmente irn acompaadas de instrucciones de conversin como las descritas en la seccin 3.4.5. Por ejemplo, el siguiente fragmento de cdigo almacena en $a0 el valor entero ms aproximado del rea de un crculo cuyo radio est almacenado como entero en la direccin de memoria apuntada por $t0, suponiendo que la constante est almacenada como valor en coma otante de simple precisin en la direccin de memoria apuntada por la etiqueta pi. l.s cvt.s.w l.s mul.s mul.s round.w.s mfc1 $f12, ($t0) # Carga en $f12 el valor entero del radio $f12, $f12 # Lo convierte a coma flotante $f13, pi # Carga en $f13 el valor de $f12, $f12, $f12 # Calcula el cuadrado del radio $f12, $f13, $f12 # Lo multiplica por $f12, $f12 # Lo redondea al entero ms cercano $a0, $f12 # Lo mueve al banco de registros de enteros

Carga y almacenamiento Las instrucciones de carga y almacenamiento de valores en coma otante son similares a las instrucciones descritas en la seccin 3.4.2. La principal diferencia es que el registro que se utiliza para el valor cargado o almacenado ser un registro de coma otante. Un ejemplo de instruccin de carga de un valor en simple precisin sera el siguiente: l.s $f4,0x100c($29) # Carga $f4 con la palabra de la # direccin $29+0x100c.

Como vemos, la instruccin utiliza el mismo modo de direccionamiento que lw, una base ms un desplazamiento inmediato de 16 bits para obtener la direccin destino. El valor cargado de memoria se copia al registro sin ningn tipo de modicacin. Esto quiere decir que si el valor almacenado en memoria no estaba ya en formato IEEE-754 deber ser convertido antes de operar con l (ver seccin 3.4.5). De modo anlogo funciona la instruccin de almacenamiento s.s: s.s $f4,0x100c($29) # Almacena $f4 en la direccin # $29+0x100c.

Para valores en doble precisin, los mnemnicos correspondientes son l.d y s.d. Estas instrucciones acceden a 8 bytes (64 bits) de memoria y a dos registros de coma otante (el indicado directamente en la instruccin y el siguiente). Por ejemplo: l.d $f4,0x100c($29) s.d $f4,0x100c($29) # # # # Carga ($f4,$f5) con los 8 bytes a partir de la direccin $29+0x100c. Almacena ($f4,$f5) en 8 bytes a partir de la direccin $29+0x100c. 26

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

En realidad, l.s, l.d, s.s y s.d son pseudoinstrucciones (ver seccin 3.7.4), que se traducen a secuencias de las instrucciones reales lwc1 y swc1 (carga y almacenamiento del coprocesador 1). As, por ejemplo, una instruccin l.s $f4,0x100c($29) se traducir como: lwc1 $f4,0x100c($29) Y una instruccin l.d $f4,0x100c($29) como el par de instrucciones: lwc1 $f4,0x100c($29) lwc1 $f5,0x1010($29) Aritmticas Las instrucciones bsicas (suma, resta, multiplicacin y divisin) se encuentran tambin tanto para simple como para doble precisin. Por ejemplo, para la suma: add.s $f4,$f5,$f6 add.d $f4,$f6,$f8 # $f4 = $f5 + $f6 (simple precisin) # ($f4,$f5) = ($f6,$f7) + ($f8,$f9) # (doble precisin)

De modo anlogo a las instrucciones de suma funcionan las instrucciones de resta (sub.s y sub.d), multiplicacin (mul.s y mul.d), y divisin (div.s y div.d). Comparacin y salto Las instrucciones para comparar datos en punto otante en MIPS tienen un funcionamiento ligeramente distinto a las comparaciones con enteros. En aquellas, las instrucciones como slt utilizaban un registro general explcito de la arquitectura para colocar un 0 o un 1 con el resultado de la comparacin. Sin embargo, en las instrucciones de punto otante, se utiliza un registro especial de 1 bit en el coprocesador 1. Este registro (ag o bit de bandera) almacena el resultado de la ltima comparacin realizada sobre valores en coma otante. Todas las instrucciones de comparacin disponibles son del tipo c.X.s y c.X.d (donde X puede ser eq, neq, lt, le, gt o ge para comparaciones del tipo =, =, <, , >, , respectivamente). De esta manera tenemos: c.X.s $fi,$fj # # # # # Compara los datos en simple precisin $fi y $fj, activando o desactivando el flag del coprocesador 1 dependiendo de si se cumple o no la condicin X. Idem, para datos en doble precisin.

c.X.d $fi,$fj

As, despus de realizarse una comparacin, se utilizar una instruccin de salto condicional que comprobar si dicho ag fue o desactivado por dicha comparacin. Las correspondientes instrucciones de salto condicional, que comprueban el valor del ag para realizar el salto, son: bc1t bc1f # Salta si la ltima comparacin fue cierta # Salta si la ltima comparacin fue falsa

27

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

3.5.

Manejo de interrupciones y excepciones

Durante la mayora del tiempo, un computador ejecuta instrucciones en el mismo orden en el que estn almacenadas en memoria, o siguiendo el orden indicado por el programador mediante instrucciones de salto (ver seccin 3.4.3). Esta forma de funcionar es sencilla de comprender y es suciente para la mayora de las tareas, pero no permite que el computador pueda reaccionar a eventos externos (como la pulsacin de una tecla) o situaciones inesperadas (como la aparicin de una instruccin incorrecta). Las excepciones e interrupciones son eventos que modican el ujo de ejecucin de instrucciones de forma asncrona. Ambos conceptos son muy similares15 , pero haremos la siguiente distincin: Interrupcin: suceso cuyo origen es externo al procesador que requiere la atencin de ste. Por ejemplo: el movimiento del ratn podra activar una seal que indicara al procesador que necesita interrumpir momentneamente la ejecucin del programa actual para leer la nueva posicin del ratn y actualizar el cursor en la pantalla. Excepcin: suceso cuyo origen es interno al procesador y que requiere la modicacin del ujo de ejecucin del programa. Adems de las excepciones por desbordamiento que se mencionaron en la seccin 3.4.1, existen otras causas que tambin pueden requerir que el procesador detenga momentneamente su funcionamiento para atender una situacin excepcional, producida durante la ejecucin del programa. Algunos ejemplos seran el intento de acceso a una direccin de memoria no vlida (no perteneciente al espacio de direcciones del programa), el intento de ejecucin de una instruccin inexistente (con un cdigo invlido, por ejemplo), etc. Cuando se produce una interrupcin o una excepcin, se transere el control a un procedimiento especial denominado manejador de interrupcin (o manejador de excepcin en su caso). En MIPS, el proceso que se sigue cuando se produce una interrupcin o una excepcin es el siguiente: 1. Se almacena en el registro especial EPC (Exception Program Counter, contador de programa en la excepcin) la direccin de la instruccin que ha producido la excepcin (en el caso de las excepciones) o la de la siguiente instruccin a ejecutar (en el caso de las interrupciones). Este registro podr ser utilizado por el manejador correspondiente para analizar la causa de la excepcin y para devolver el control al programa una vez tratada la excepcin o interrupcin. 2. El programa salta a una subrutina almacenada en una direccin predenida donde se tratar la interrupcin o excepcin. Como se ha mencionado, esta subrutina se denomina manejador de interrupcin o manejador de excepcin y es parte del sistema operativo. Puede haber un manejador disponible para cada tipo de interrupcin o excepcin, o un manejador comn a todos los tipos. En este ltimo caso, se almacena la causa de la excepcin o interrupcin en el registro especial CAUSE. En el caso de que la excepcin sea debida a un acceso a memoria, se almacena la direccin implicada en el registro especial VADDR (por ejemplo, para un acceso a memoria no alineado o un fallo de pgina). 3. El manejador realiza las acciones apropiadas segn la excepcin o interrupcin particular. 4. En los casos que sea posible, una vez tratada la excepcin se contina con la ejecucin normal del programa, posiblemente noticndole de la excepcin o interrupcin mediante un mecanismo que depende del sistema operativo. En otras ocasiones, el sistema operativo aborta la ejecucin del programa. Un manejador de interrupciones o excepciones es, habitualmente, parte del sistema operativo. Estos procedimientos son especiales porque pueden ser llamados desde cualquier punto del programa (sin que el programador o compilador pueda preverlo). Por tanto, entre otras cosas, usarn un convenio de uso de registros
De hecho, en la documentacin de muchas arquitecturas se utiliza diferente terminologa a la que aqu se presenta, o incluso no hace distincin entre ambos conceptos.
15

28

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

diferente. Para que sea posible devolverle el control al programa en el paso 4, es necesario que durante el paso 3 no se modique el contenido de los registros que usa el programa16 . Para realizar su trabajo, los manejadores de interrupcin en MIPS tienen reservados los registros $k0 y $k1. En el paso 4, el manejador puede decidir terminar el proceso causante de la excepcin o informarle del problema (por ejemplo, en POSIX, mediante una seal) para que el programador pueda decidir cmo tratar la excepcin (por ejemplo, mostrando un mensaje de error). Para hacer esto posible, MIPS ofrece la instruccin mfc0 (move from coprocessor 017 ) que permite copiar el registro EPC en un registro normal, para poder despus continuar con la ejecucin del programa usando la instruccin jr. Tambin se dispone de la instruccin eret, que copia el contenido del registro EPC en el registro PC y reactiva el procesamiento de posteriores interrupciones. Las interrupciones juegan un papel fundamental en el manejo de la entrada y salida, como se explicar en temas posteriores de esta asignatura.

3.6.

Llamadas al sistema operativo

La mayora de los computadores utilizan un sistema operativo para, entre otras cosas, ofrecer a los programas que se ejecutan en la mquina determinados servicios que requieren el acceso al hardware a bajo nivel. Por ejemplo, como se ver en posteriores temas de esta asignatura, la implementacin de las operaciones de entrada y salida requiere cdigo que manipula direcciones concretas de memoria o que se encarga de atender determinadas interrupciones. Ese cdigo es parte del sistema operativo y ste pone esta funcionalidad a disposicin del resto de programas mediante determinadas llamadas al sistema. De esta forma, el programa de usuario no tiene que conocer los detalles concretos del hardware18 . En general, el sistema operativo y el resto de programas que se ejecutan en la mquina se mantienen estrictamente separados entre s. Esta separacin hace posible que varios programas se puedan ejecutar en el mismo computador y se consigue, en la mayora de los casos, mediante modos diferentes de funcionamiento del procesador19 y el uso espacios de direcciones virtuales diferentes como se ver en temas posteriores. Debido a esta separacin entre el sistema operativo y los programas, un programa no puede llamar a un procedimiento del sistema operativo usando los mecanismos habituales que se han descrito en la seccin 3.4.4. Por tanto, es necesario un mecanismo diferente. MIPS ofrece para este n la instruccin syscall. Cuando se ejecuta, esta instruccin genera una excepcin (ver seccin 3.5) que se utiliza para transferir el control al sistema operativo. El sistema operativo realiza las acciones asociadas a la llamada al sistema solicitada y devuelve el control al sistema operativo. Una llamada al sistema se identica mediante un nmero que se escribe en el registro $v0 y se invoca mediante la instruccin syscall. La tabla 4 muestra una seleccin de algunas de las llamadas al sistema ofrecidas en el entorno del simulador MARS. De igual forma que los procedimientos normales, las llamadas al sistema reciben sus argumentos en los registros $a0, $a1, $a2 y $a3 (excepto las que trabajan con nmeros en coma otante) y devuelven sus resultados en $v0 y $v1. Sin embargo, a diferencia del caso de los procedimientos normales, no es necesario guardar los registros temporales en la pila antes de realizar una llamada al sistema. Por ejemplo, el siguiente fragmento de cdigo lee un entero, le suma 1 y a continuacin lo escribe: li $v0,5
16 17

# Cdigo de la llamada al sistema read int

Por supuesto, el manejador puede usar cualquier registro si almacena primero su valor y lo restaura antes del paso 4 Se llama as porque el registro EPC se encuentra en una parte del procesador a la que se le llama coprocesador 0 18 De hecho, la mayora de los sistemas operativos no permiten que un programa realice estas acciones directamente. 19 Normalmente, existe un modo de funcionamiento de usuario, usado por la mayora de los programas, y un modo de funcionamiento privilegiado, usado por el sistema operativo. El modo de usuario no tiene acceso a algunas instrucciones (o direcciones de memoria) que s estn disponibles en el modo privilegiado y que son necesarias slo para implementar la funcionalidad del sistema operativo.

29

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

Tabla 4: Ejemplos de llamadas al sistema ofrecidas por el simulador MARS. Cdigo 1 2 3 4 Nombre print integer print oat print double print string Argumentos Nmero a imprimir en $a0. Nmero a imprimir en $f12. Nmero a imprimir en $f12. Direccin de la cadena a imprimir en $a0. La cadena debe acabar en 0. Resultado Imprime el entero. Imprime el nmero en coma otante. Imprime el nmero en coma otante. Imprime la cadena.

read integer

read oat

read double

Espera a que el usuario introduzca un entero mediante un cuadro de dilogo. Lo almacena en $v0. Espera a que el usuario introduzca un nmero en coma otante mediante un cuadro de dilogo. Lo almacena con precisin simple en $f0. Espera a que el usuario introduzca un nmero en coma otante mediante un cuadro de dilogo. Lo almacena con precisin doble en $f0 y $f1. Direccin del buffer en el que almacenar la cadena leda en $a0 y tamao mximo de la cadena a leer en $a1. Nmero de bytes de memoria a reservar en $a0

read string

sbrk

10

exit

Reserva memoria en el heap (montculo) y devuelve la direccin en $v0. Termina la ejecucin del programa.

syscall addi $a0,$v0,1 li $v0,1 syscall

# # # #

Lee el entero y coloca el resultado en $v0 Suma 1 al valor ledo y pone el resultado en $a0 Cdigo de la llamada al sistema print int Imprime el entero almacenado en $a0

3.7.

Codicacin de las instrucciones en MIPS

En esta seccin se ver cmo se representan las instrucciones del ensamblador dentro del computador, para que ste las pueda leer, decodicar y ejecutar. En realidad, al igual que los datos, las instrucciones se representarn en el computador como simples series de ceros y unos. Para interpretarlas, estas series se dividirn en campos, cada uno de los cuales servir para almacenar una informacin necesaria para la ejecucin de la instruccin. Por ejemplo, una instruccin slt $t2,$t0,$t1, una vez codicada, tendr que tener un campo con 30

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

un valor determinado para indicar que se trata de un slt (y no un add, o un lw, etc). Asimismo, tendr que tener dos campos para indicar que los registros a comparar son $t0 y $t1, y otro campo para indicar que el registro destino (donde escribir la constante 1 si se cumple la condicin, o 0 en caso contrario) es $t2. A la distribucin concreta de estos campos en la secuencia de bits completa para codicar la instruccin se le denominar formato de la instruccin. Siguiendo la losofa RISC, lo ideal a la hora de decidir cmo codicar las instrucciones sera elegir un formato nico para todas las instrucciones, ya que de esta forma se simplica el hardware necesario para realizar la decodicacin. Sin embargo, esta estrategia podra acarrear problemas, tales como obtener instrucciones demasiado largas, con muchos campos sin utilizar (por ejemplo, en MIPS algunas instrucciones necesitan codicar una constante de hasta 16 bits, mientras que otras no lo necesitan y tendran el correspondiente campo vaco). En MIPS, como solucin de compromiso, existen unos pocos formatos de instrucciones20 de 32 bits todos ellos. A la hora de decidir qu formato utilizar para cada instruccin, el factor determinante es qu modos de direccionamiento (ver seccin 3.3) usa dicha instruccin. A continuacin se pasan a describir los tres formatos de instrucciones (formatos R, I y J) disponibles en la arquitectura MIPS, con ejemplos de cmo son utilizados por los diferentes tipos de instrucciones, acomodndose en cada caso al modo de direccionamiento empleado.

3.7.1.

Formato de instruccin R

En este formato de instruccin, los operandos fuente y destino se especican usando el modo de direccionamiento registro. El modo concreto de distribuir los 32 bits de la instruccin es el siguiente (el bit 31, de ms peso, se encuentra en el extremo izquierdo):
31 0

op
6 bits

rs
5 bits

rt
5 bits

rd
5 bits

shamt
5 bits

func
6 bits

El signicado de los distintos campos es el siguiente: op: indica el cdigo de operacin. Se utiliza para diferenciar los distintos tipos de instrucciones. Este campo es el nico que aparece en todos los formatos de instruccin (no slo en las de tipo R, sino tambin las de tipo I y J), puesto que slo una vez que se lee, y se sabe el tipo de instruccin de la que se trata, la unidad de decodicacin de la instruccin podr conocer el formato real que tiene la instruccin. rs: primer registro fuente de la operacin. rt: segundo registro fuente de la operacin. rd: registro del operando destino, donde se guardar el resultado de la instruccin. shamt: tamao del desplazamiento. Slo se utiliza para almacenar la cantidad de desplazamiento en las instrucciones de desplazamiento o rotacin (sll, srl. . . ), y vale 0 en el resto. func: sirve para distinguir instrucciones que, por ser muy similares, tienen el mismo cdigo de operacin (por ejemplo, todas las aritmtico-lgicas que trabajan con tres registros). Le indica a la ALU qu funcin de las posibles (suma, resta, AND lgico. . . ) debe realizar. El formato R se utiliza en las instrucciones aritmtico-lgicas que operan sobre dos registros fuente para colocar el resultado en un determinado operando destino. Por ejemplo, la instruccin
20 Otra solucin posible es la adoptada por la arquitectura x86 (IA-32) o la amd64 (IA-64). En estas arquitecturas, las instrucciones tienen longitud variable y la decodicacin es un proceso ms complejo. De hecho, implementaciones modernas de estas arquitecturas funcionan traduciendo las instrucciones x86 a un formato interno ms regular antes de ejecutarlas. Esta opcin, sin embargo, es contraria a la losofa RISC seguida por MIPS, una de cuyas caractersticas principales es la uniformidad en el tamao de sus instrucciones.

31

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

add $8,$18,$9 se representara de la siguiente manera:


31 0

0
op (6)

18
rs (5)

9
rt (5)

8
rd (5)

0
shamt (5)

32
func (6)

El cdigo de operacin op indica que se trata de una instruccin aritmtico-lgica (op=0) con dos registros fuente (rs=18, rt=9) y un operando destino (rd=8). El campo shamt no se utiliza, puesto que no es una instruccin de desplazamiento, y se deja a 0. Finalmente, el campo func determina el tipo concreto de operacin, en este caso una suma (func=32). Por tanto, esta instruccin estar almacenada en memoria como la ristra de bits 0000 0010 0100 1001 0100 0000 0010 0000 (0x02494020, en hexadecimal). De forma similar, la instruccin sra $8,$18,7 se representara de la siguiente manera:
31 0

0
op (6)

0
rs (5)

18
rt (5)

8
rd (5)

7
shamt (5)

3
func (6)

En este caso, el campo func indica que se trata de un desplazamiento hacia la derecha, y el campo shamt contiene el nmero de bits a desplazar. El registro a desplazar se encuentra codicado en el campo rt, mientras que el campo rs no se utiliza y se deja a cero.

3.7.2.

Formato de instruccin I

Este formato se caracteriza por tener un campo dedicado a almacenar un valor inmediato de 16 bits. Se utiliza para codicar las instrucciones en las que uno de los operandos utiliza el direccionamiento inmediato, el direccionamiento base ms desplazamiento o el direccionamiento relativo al contador del programa. Es decir, ser utilizado por instrucciones aritmticas cuando uno de los operandos es un valor constante, instrucciones de transferencia de datos y saltos condicionales. La distribucin de campos es la siguiente:
31 0

op
6 bits

rs
5 bits

rt
5 bits

imm
16 bits

op: cdigo de operacin. rs: registro fuente. rt: registro fuente/destino dependiendo de la operacin. imm: valor inmediato. Su interpretacin depende de la operacin. A continuacin mostramos como codicar los diferentes tipos de instruccin que utilizan este formato: Instrucciones aritmticas con operandos constantes Estas instrucciones utilizan el campo imm para codicar en complemento a dos el valor del operando. El campo rs codica el otro operando fuente y el campo rt codica el registro de destino Por ejemplo, la instruccin: addi $17,$18,-45

32

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

se codica:
31 0

8
op (6)

18
rs (5)

17
rt (5)

-45
imm (16)

Instrucciones de acceso a memoria Las instrucciones de carga y almacenamiento utilizan el campo imm para codicar en complemento a dos el desplazamiento que hay que sumarle al contenido del registro base, codicado en rs. El campo rt codica el registro fuente o destino, segn sea una instruccin de carga o almacenamiento, respectivamente. Por ejemplo, la instruccin: lw $17,100($18) se codica:
31 0

35
op (6)

18
rs (5)

17
rt (5)

100
imm (16)

Las instrucciones de almacenamiento se codicar de modo anlogo, por ejemplo: sb $17,-50($18) se codica:
31 0

40
op (6)

18
rs (5)

17
rt (5)

-50
imm (16)

Instrucciones de salto condicional En este caso, el campo imm se utiliza para codicar en complemento a dos el nmero de instrucciones21 a avanzar, empezando en la instruccin siguiente al salto para llegar a la instruccin de destino (si hubiera que retroceder, se codicara un nmero negativo). Cuando se ejecuta la instruccin, se utiliza el contenido del campo imm y el valor del contador de programa (PC) para calcular la direccin de memoria de la instruccin de destino. En MIPS la memoria se direcciona por bytes, pero, como sabemos, cada instruccin ocupa 4 de ellos. Por tanto, una direccin de una instruccin MIPS es siempre mltiplo de 4 (los dos ltimos bits siempre valdrn 00). Por esta razn no es necesario codicar en imm el nmero de bytes que hay que sumarle al PC, y se consigue abarcar ms instrucciones de destino con el campo inmediato de slo 16 bits Por ejemplo, la instruccin bne en la siguiente secuencia: l1: sub $t0,$t2,$t3 addi $s5,$s5,1 bne $t0,$s5,l1 ... se codicara:
31 0

5
op (6) 21

8
rs (5)

21
rt (5)

-3
imm (16)

Por supuesto, es el nmero de instrucciones reales de MIPS, no pseudoinstrucciones.

33

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

Como se puede observar, la direccin apuntada por la etiqueta se encuentra dos instrucciones antes de bne. Adems, hay que tener en cuenta que el desplazamiento es relativo al valor del contador de programa en el momento de ejecutar la instruccin bne (que es PC + 4, es decir, la direccin de memoria de la instruccin siguiente a bne). Por tanto, el valor almacenado en el campo imm de la instruccin es 3 (como siempre, en complemento a dos).

3.7.3.

Formato de instruccin J

Este ltimo tipo de formato se usa principalmente para las instrucciones de salto incondicional, que usan el direccionamiento pseudodirecto. Estas instrucciones especican casi directamente la direccin destino del salto, al contrario que los saltos condicionales, donde la direccin destino se codica con respecto a la direccin de la instruccin actual. Al tener que especicarse directamente la direccin destino, los 16 bits del campo inmediato en el formato I son insucientes, por lo que se crea un nuevo formato. Es imposible codicar la direccin completa de la instruccin (que tiene 32 bits) de destino junto con el cdigo de operacin porque slo hay 32 bits en cada instruccin. Por tanto, en este formato se reservan todos los bits que no son del cdigo de instruccin (26) para la direccin, y se aprovecha de nuevo el que todas las instrucciones estn almacenadas en direcciones mltiplo de 4. Los campos son los siguientes:
31 0

op
6 bits

j
26 bits

op: especica el cdigo de operacin. j: almacena los bits del 27 al 2 (inclusive) de la direccin destino. Adems de los 26 bits almacenados en el campo j, son necesarios 6 bits ms para conocer la direccin de la instruccin de destino. Los dos bits menos signicativos no es necesario codicarlos, porque sern siempre 0 al ser la direccin mltiplo de 4. Los cuatro bits restantes para formar una direccin de 32 bits se toman de los 4 bits ms signicativos del PC de la instruccin en curso (la de salto). En resumen, la direccin de memoria de la instruccin destino se formar (de mayor a menor peso) cogiendo los 4 bits ms signicativos del PC de la instruccin de salto, ms los 26 indicados en el campo j, ms dos ceros. A continuacin se muestra un ejemplo para ilustrar esto. Supongamos que la etiqueta bucle se corresponde con la direccin 0x0040000c. En ese caso,suponiendo que la propia instruccin j se encontrase en una direccin que tuviese sus cuatro bits de ms peso como ceros (es decir, fuese de la forma 0x0NNNNNNN), la codicacin de la siguiente instruccin: j bucle sera (en binario):
31 0

000010
op (6)

00000100000000000000000011
j (26)

3.7.4.

Pseudoinstrucciones

El lenguaje ensamblador contiene instrucciones que no se implementan directamente sobre el hardware del procesador, sino que tienen que ser traducidas por el ensamblador (el programa que traduce de lenguaje ensamblador a lenguaje mquina).

34

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

Este tipo de instrucciones (que no tienen correspondencia directa con una instruccin mquina, pero que son traducidas a una secuencia de instrucciones mquina equivalente) se conocen como pseudoinstrucciones. Las pseudoinstrucciones simplican la programacin en lenguaje ensamblador, puesto que aumentan la potencia expresiva del mismo sin suponer ningn incremento de complejidad en la construccin del hardware. En otras palabras, las pseudoinstrucciones dan al MIPS un repertorio de instrucciones ms rico que el que implementa la circuitera. Veamos un ejemplo. Como sabemos, la arquitectura MIPS asegura que el registro $zero siempre tenga el valor cero. Este registro se utiliza para traducir la instruccin de lenguaje ensamblador move, una pseudoinstruccin que copia el contenido de un registro en otro. El ensamblador de MIPS acepta esta instruccin, aunque no se encuentra en la arquitectura MIPS: move $t0,$t1 # Copia $t1 en $t0.

Lo que realmente hace el ensamblador es convertir esta instruccin en una equivalente que s tiene correspondencia directa en lenguaje mquina, valindose del registro $zero: add $t0,$t1,$zero # Copia $t1 en $t0

Algunas pseudoinstrucciones se traducen en ms de una instruccin mquina. Y, en algunos casos, estas instrucciones necesitan comunicarse algn valor temporal entre s. Para ello, MIPS tiene reservado el registro $at ($1). Un ejemplo sera la pseudoinstruccin blt $s0,$s1,destino # Salta a destino si $s0 < $s1

El ensamblador traduce esta pseudoinstruccin en la siguiente secuencia de instrucciones mquina reales, que implementan el comportamiento deseado: slt $at,$s0,$s1 bne $at,$zero,destino # Activa $at si $s0 < $s1 # Realiza el salto condicional

Hay todo un repertorio de pseudoinstrucciones para los distintos tipos de saltos, como se mencion en la seccin 3.4.3. Todas ellas se traducirn a una secuencia de como mucho dos instrucciones, una slt y otra beq o bne. Otro ejemplo tpico de pseudoinstruccin es la relacionada con la carga de constantes grandes en un registro. Por ejemplo, supongamos que queremos cargar la constante de 32 bits 0x1234abcd en el registro $10. Para ello, se dispone de la pseudoinstruccin li $10,0x1234abcd # Carga 0x1234abcd en $10.

El problema de esta operacin es el tamao de la constante manejada. Como se ha visto en el apartado 3.7.2, dicha constante no puede ser codicada en una sola instruccin, puesto que ocupa 32 bits y las instrucciones MIPS poseen esa misma longitud. Puesto que, adems de la constante, ser obligatorio codicar tambin en otros campos otra informacin de inters (como el cdigo de operacin de la instruccin, el propio registro sobre el que se quiere realizar la carga, etc), es obvio que el trabajo requerido tiene que realizarse con ms de una instruccin. La solucin, pues, pasa por partir la constante en dos mitades de 16 bits, las cuales se cargan en dos pasos en el registro destino, primero la mitad superior y luego la inferior. As, la pseudoinstruccin anterior se traducira a las dos instrucciones mquina reales: lui $at,0x1234 ori $10,$at,0xabcd # Carga la mitad superior (y pone los 16 bits # inferiores a 0) ... # ... y carga la mitad inferior.

La instruccin lui carga la constante en la mitad superior de el registro auxiliar ($at), dejando la mitad inferior con ceros, y posteriormente el ori completa el valor a colocar en $10 con los 16 bits inferiores. 35

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

3.8.

Alternativas al ISA de MIPS

Hemos presentado hasta ahora el ISA del MIPS como ejemplo de un ISA moderno tpico. Muchos ISAs son similares entre s, pero existen algunas alternativas al diseo de MIPS que conviene conocer. El diseo del ISA de un computador tiene implicaciones en la complejidad, el coste y el rendimiento nal del mismo. Por ejemplo, un modelo sencillo del tiempo de ejecucin de un programa es el siguiente: ciclos de reloj segundos instruccion ciclos de reloj Como se ve, el tiempo de ejecucin de un programa es proporcional al nmero de instrucciones ejecuta22 del mismo. Por tanto, una forma de reducir el tiempo de ejecucin sera disear el ISA de tal manera das que hicieran falta menos instrucciones para realizar algunas operaciones. Es decir, incluir instrucciones ms complejas. El problema de complicar el repertorio de instrucciones es que, normalmente, conlleva una complicacin del hardware del procesador. Este aumento de complejidad no slo incrementa el coste (ahora hay que invertir ms tiempo en disear y probar el procesador), sino que tambin puede hacer aumentar el segundo o el tercer factor de la ecuacin (ciclos por instruccin o tiempo de ciclo), ya que las instrucciones ms complejas necesitarn un mayor nmero de ciclos o ciclos ms largos para realizar su tarea. Por tanto, cualquier mejora que se proponga para un ISA debe ser evaluada cuidadosamente para evitar que empeore el rendimiento en lugar de mejorarlo. Por ejemplo, el PowerPC es otro ISA muy extendido que se puede encontrar en consolas y gran variedad de dispositivos multimedia y de telecomunicaciones. Es una arquitectura muy similar a la de MIPS en cuanto a nmero de registros y formato de las instrucciones. Sin embargo, incorpora dos modos nuevos de direccionamiento que aaden ms funcionalidad que la proporcionada por MIPS: T iempo = N umero de instrucciones Modo de direccionamiento indizado: en este modo, se pueden usar dos registros para especicar una direccin de memoria. Para obtener la direccin, se ha de sumar el contenido de ambos registros. Por ejemplo el siguiente cdigo MIPS add $t0,$a0,$s3 lw $t1,0($t0) # $a0 es la base de la tabla, $s3 el ndice

se podra expresar de la siguiente manera en PowerPC: lw $t1,$a0+$s3 # $a0 es la base de la tabla, $s3 el ndice

Modo de direccionamiento actualizado o autoincremento: es utilizado en recorridos de tablas, donde se van leyendo todas las posiciones consecutivamente. La siguiente secuencia de instrucciones MIPS: lw $t0,8($s3) add $s3,$s3,4 # $s3 contiene la direccin de la siguiente # posicin a leer

en PowerPC se hara con una sola instruccin, que, aparte de cargar en $t0 el contenido de la memoria cuya direccin es $s3+8, tambin incrementa $s3 en 4: lwu $t0,8($s3) # Carga $t0 de Mem[$s3+8] e incrementa $s3 en # 4 para apuntar a la siguiente posicin

22 Esto se reere al nmero de veces que se ha ejecutado alguna instruccin, no al nmero de instrucciones que tiene el cdigo del programa. Es decir, habr instrucciones que se ejecuten varias veces (por ejemplo, en los bucles) e instrucciones que no se ejecuten ninguna.

36

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

Adems, el PowerPC dispone de operaciones ms potentes relacionadas con la transferencia de datos y el salto condicional al nal de los bucles. En concreto, dispone de una instruccin que permite transferir hasta 32 palabras de datos a la vez, y se usa para hacer copias rpidas de posiciones de memoria y para salvar y restaurar registros. Tambin dispone de una instruccin especial, usada al nal de bucles, que decrementa el ndice del bucle, lo compara con cero y salta, todo ello en una sola instruccin. Por otro lado, un ISA radicalmente diferente a la de MIPS es la de la familia de procesadores x86 (usada por el Intel 386, el Pentium IV y los procesadores ms modernos de 32 bits de Intel y AMD). Las diferencias principales entre MIPS y x86 son las siguientes: 1. Los registros de x86 tienen usos especcos, al contrario que en MIPS, donde son de propsito general. Aunque es cierto que en MIPS los registros se usan segn un convenio acordado por los programadores (es decir, hay algunos destinados al paso de parmetros, otros para variables temporales, otros para la devolucin de resultados, etc), esto es slo una convencin, no una limitacin real de la arquitectura. Es decir, en realidad, la arquitectura MIPS permite que casi todos los registros puedan ser usados para todas las funciones. En cambio, x86 limita el uso de los registros, y unos son usados para ndices de tablas, otros para clculos aritmticos, etc. De hecho, x86 slo dispone de 8 registros de uso general, por 31 MIPS (no contamos ya el registro 0, que sabemos que est cableado al valor cero). 2. Las instrucciones aritmtico-lgicas y las de transferencia de datos son de dos operandos. Uno de ellos es a la vez fuente y destino de la operacin. 3. Existen modos de direccionamiento adicionales. Por ejemplo: Registro indirecto: la direccin del operando est en un registro. Modo base con desplazamiento de 8 32 bits: la direccin es un registro base ms un desplazamiento de 8 32 bits. Base ms ndice escalado: la direccin del operando es base + 2escala ndice, donde escala puede ser 0, 1, 2 3. Base ms ndice escalado con desplazamiento de 8 32 bits: la direccin del operando es base + (2escala ndice) + desplazamiento. Existen restricciones sobre qu registros pueden ser usados en cada modo de direccionamiento (esto entra dentro de la limitacin expuesta en el punto 1). 4. Casi todas las instrucciones ofrecen muchas combinaciones de modos de direccionamiento: Registro y registro. Registro e inmediato. Registro y memoria. Memoria y registro. Memoria e inmediato. Por tanto, en x86 alguno de los operandos de una operacin aritmtico lgica puede estar en memoria (pero no los dos) 5. El tamao de las instrucciones es variable (al contrario de MIPS, que siempre usa 32 bits para codicarlas).

37

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

Las arquitecturas que, como MIPS, requieren que los datos estn casi siempre en registros a la hora de operar con ellos se las conoce como de registro-registro, y es una caracterstica tpica de las mquinas modernas tipo RISC. Por el contrario, las arquitecturas que, como x86, permiten que alguno de los operandos de las instrucciones de procesamiento estn directamente en la memoria se las conoce como de registromemoria, y es una caracterstica tpica de repertorios de instrucciones CISC. La realidad es que, hoy en da, los repertorios de instrucciones CISC como x86 son traducidos a la hora de ejecutarlos por el propio hardware a secuencias de instrucciones internas ms simples (similares a las RISC), en las cuales, entre otras cosas, se aaden instrucciones de carga y almacenamiento explcitas para realizar las operaciones aritmticas que incluyen operandos en memoria. La razn es que es mucho ms fcil disear hardware eciente para repertorios de instrucciones RISC que para repertorios CISC, dada la mayor simplicidad y uniformidad de los RISC.

38

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

Apndices A3.1. Manipulacin de datos a nivel de bits

MIPS, al igual que la mayora de las ISAs, permite direccionar los datos en memoria con una granularidad mnima de un byte. Es decir: es posible referirse de forma independiente a cualquier byte de memoria mediante las instrucciones lb, lbu y sb, las cuales nos permiten leer o escribir a la vez los 8 bits correspondientes al byte indicado por la direccin efectiva que se desee, pero no es posible acceder de forma directa e independiente a un subconjunto de los bits de un byte. En ocasiones resulta til utilizar menos de un byte para representar un dato. Por ejemplo, supongamos que queremos almacenar una lista de valores numricos entre 1 y 3. Si utilizamos 2 bits para almacenar cada elemento, podremos almacenar en la misma cantidad de memoria una lista 4 veces ms larga que si usramos 1 byte. El caso ms extremo (pero relativamente comn) es el de un array de valores booleanos, en el que cada elemento se puede almacenar en un solo bit. Los datos representados mediante un nmero de bits menor que un byte (o que una palabra) se suelen llamar campos de bits (bitelds). El caso extremo de un dato que se almacena usando un solo bit (y por tanto slo puede tomar dos valores) se le suele llamar bandera (ag). El acceso a campos de bits almacenados en memoria se realiza mediante combinaciones de las instrucciones de acceso a memoria habituales (lw, sw, lhu, lh, sh, lbu, lb y sb), operaciones de desplazamiento lgico (sll, srl, sllv y srlv) y operaciones lgicas (and, andi, or y ori).

A3.1.1.

Lectura de campos de bits

Para leer el contenido de un campo de bits de la memoria se pueden realizar las siguientes acciones: 1. Leer el byte, media palabra o palabra que contiene los bits de inters (adems de otros bits adicionales, por supuesto). Para esta tarea se utilizarn las instrucciones habituales de lectura de memoria. 2. Alinear los bits ledos en el registro destino, de forma que el bit de inters de menor peso est colocado en el bit 0 del registro. Para esto se utilizar la instruccin srl o la slrv. 3. Descartar (poner a cero) los bits sobrantes del registro, utilizando las instrucciones and o andi con una mscara de bits adecuada. Por ejemplo, si suponemos que un array de elementos enteros en el que cada elemento ocupa 3 bits comienza en la direccin denotada por la etiqueta l1, el acceso al elemento cuyo ndice se encuentra en el registro $t0 se podra realizar as: # Clculo de la direccin del byte que contiene al elemento: # - Cada byte contiene 8 / 3 elementos, por lo que el elemento # i-simo se encontrar en el byte cuya direccin es # "l1 + (i * 3 / 8)". # - El resto de la divisin anterior nos dar el desplazamiento # en bits en el que comienza el campo de bits dentro del byte. li $t1, 3 mul $t1, $t0, $t1 sra $t2, $t1, 3 # divisin entre 8, cociente andi $t1, $t1, 0x7 # divisin entre 8, resto lbu $t2, l1($t2) # carga el byte 39

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

srlv andi

$t2, $t2, $t1 $t2, $t2, 0x7

# alinea el campo de bits en el registro # pone a 0 todos los bits salvo los 3 primeros

Muy frecuentemente el tamao del campo de bits ser potencia de 2, por lo que se podr utilizar una instruccin de desplazamiento aritmtico en lugar de la instruccin de multiplicacin utilizada en el fragmento de cdigo anterior.

A3.1.2.

Escritura de campos de bits

La escritura en un campo de bits se realiza de forma similar a la lectura, pero teniendo en cuenta que antes de escribir en la posicin de memoria destino es necesario leerla, ya que su contenido slo se modicar parcialmente (los bits de esa posicin de memoria que no pertenezcan al campo de bits actual deben quedar igual al nal del proceso). Los pasos a dar para escribir un campo de bits son los siguientes: 1. Leer el byte, media palabra o palabra que contiene los bits de inters (adems de otros bits adicionales que se debern mantener intactos). Para esta tarea se utilizarn las instrucciones habituales de lectura de memoria. 2. Poner a cero los bits a modicar en el byte (o palabra) ledo utilizando una instruccin and o andi con una mscara de bits adecuada. 3. Alinear los bits a escribir en el registro origen, de forma que los bits a escribir queden en la posicin correspondiente del byte ledo de memoria. Para esto se utilizar la instruccin sll o la sllv. 4. Combinar los bits a escribir con los bits del byte (o palabra) ledo de memoria utilizando la instruccin or. Tambin se puede utilizar la instruccin ori si los bits a escribir se dan como valor inmediato. 5. Escribir el resultado en la misma direccin de memoria de la que se ley inicialmente. Por ejemplo, si suponemos que un array de elementos enteros en el que cada elemento ocupa 3 bits comienza en la direccin denotada por la etiqueta l1, la escritura de los tres bits contenidos en el registro $t1 en la posicin cuyo ndice se encuentra en el registro $t0 se podra realizar as: # Clculo de la direccin del byte que contiene al elemento: # - Cada byte contiene 8 / 3 elementos, por lo que el elemento # i-simo se encontrar en el byte cuya direccin es # "l1 + (i * 3 / 8)". # - El resto de la divisin anterior nos dar el desplazamiento # en bits en el que comienza el campo de bits dentro del byte. li $t2, 3 mul $t2, $t0, $t2 sra $t3, $t2, 3 # divisin entre 8, cociente andi $t2, $t2, 0x7 # divisin entre 8, resto lbu $t4, l1($t3) # carga el byte # Clculo de la mscara para poner a cero los bits a sobreescribir li $t5, 0x7 sllv $t5, $t5, $t2 not $t5, $t5 # $t5 contendr ceros solo en los 3 bits a sobreescribir # Modificacin del byte ledo de memoria and $t4, $t4, $t5 # pone a cero los bits sllv $t1, $t1, $t2 # alinea los bits a escribir or $t4, $t4, $t1 # combina los bits con el byte ledo anteriormente sb $t4, l1($t3) # escribe el byte

40

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

A3.2.

Uso de la memoria en MIPS

El objetivo de este apndice es ofrecer informacin al alumno para que comprenda la organizacin de la memoria en MIPS, conozca el uso habitual que los lenguajes de programacin hacen de cada zona, sepa cmo almacenar y acceder a datos almacenados en memoria utilizando las estructuras de datos ms simples y comunes (arrays y registros) y entienda el concepto de puntero utilizado en muchos lenguajes de programacin (como C).

A3.2.1.

Modos de direccionamiento a memoria del ensamblador de MIPS

MIPS es una arquitectura de carga/almacenamiento, lo que signica que slo las instrucciones de carga y almacenamiento (ver seccin 3.4.2) acceden a la memoria. El resto de instrucciones operan slo con valores almacenados en los registros. La mquina real proporciona un solo modo de direccionamiento para acceder a memoria llamado base ms desplazamiento, que se ha explicado en la seccin 3.4.2. Sin embargo, el ensamblador proporciona modos de direccionamiento adicionales para todas las instrucciones de carga y almacenamiento que implementa mediante pseudoinstrucciones. Las maneras de acceder a una posicin desde una instruccin en nuestro cdigo fuente se resumen en la tabla A3.1. Tabla A3.1: Modos de direccionamiento a memoria del MIPS Clculo de direccin Contenido de registros Inmediato Inmediato + contenido de registro Direccin de etiqueta Direccin de la etiqueta + inmediato Direccin de la etiqueta + inmediato + contenido de registro Ejemplo lw lw lw lw lw lw $t0, $t0, $t0, $t0, $t0, $t0, ($a0) 0x1001001c 0x1000($a0) variable variable+0x1000 variable+0x1000($a0)

El ensamblador traducir las instrucciones que usen estos modos adicionales a las secuencias adecuadas de instrucciones reales. Por ejemplo, la instruccin lw $t0,variable+0x1000($a0), que utiliza el ltimo de los modos de direccionamiento adicionales que aparecen en la tabla A3.1, el ensamblador la traduce internamente por la siguiente secuencia de instrucciones reales si suponemos que variable est en la direccin 0x10012340 del segmento de datos: lui $at, 0x1001 # Carga 0x1001 en la mitad superior de $at addu $at, $at, $a0 # Le suma el registro $a0 lw $t0, 0x3340($1) # Aade la constante 0x1000 # y la parte baja del 0x10012340 correspondiente a # la direccin de la variable (0x2340 + 0x1000).

A3.2.2.

Organizacin de la memoria en MIPS

Como sabemos, la memoria del computador es una tabla indexada por direcciones. Al conjunto de direcciones disponibles se le llama normalmente espacio de direcciones. Como se ver en el tema 6 de la asignatura, los programas de usuario no acceden directamente al espacio de direcciones fsico, sino que cada programa de usuario tiene disponible un espacio de direcciones virtual privado. En el caso de MARS, dado que slo se ejecuta una aplicacin cada vez, se simula un espacio de direcciones virtual nico compuesto de todas las direcciones representables con 32 bits. 41

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

El espacio virtual de direcciones se divide en varias zonas destinadas a distintos usos (almacenamiento del cdigo del programa, almacenamiento de datos, pila. . . ). Cada plataforma (combinacin de procesador, sistema operativo y libreras del sistema) impone unas reglas sobre cmo se debe organizar el espacio de direcciones privado. La organizacin bsica de la memoria en MIPS (o, ms correctamente, de la plataforma virtual ofrecida por MARS) es similar a la de casi todos los computadores. De hecho, podemos ver y cambiar dicha organizacin mediante la entrada de men Settings / Memory Configuration.... En la gura A3.1 podemos ver la conguracin por defecto, que ser la que usaremos.

Figura A3.1: Conguracin por defecto de la memoria en MARS En primer lugar, el espacio de direcciones est dividido en dos mitades: Direcciones entre 0x00000000 a 0x7fffffff, reservadas para el programa de usuario. Direcciones entre 0x80000000 a 0xffffffff, reservadas para el sistema operativo (kernel). En un sistema real, un programa de usuario no podr acceder a las direcciones reservadas para el sistema operativo (saltara una excepcin si lo intentase, ver seccin 3.5). Como se ve en la gura A3.1, el espacio reservado para el sistema operativo se divide a su vez en varias reas ms pequeas. Veremos la funcin de algunas de ellas en temas posteriores de la asignatura. Por su parte, la zona de memoria reservada para el usuario se divide tambin en varias zonas. De ellas, destacamos las siguientes: Las direcciones entre 0x00400000 y 0x0fffffff son el segmento de texto, que contiene las instrucciones del programa. El segundo bloque, comprendido entre la direccin 0x10000000 y la 0x7fffffff es el segmento de datos. Este bloque se divide a su vez en: 42

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

El rea de datos estticos, que incluye las direcciones entre 0x10010000 y 0x1003ffff. En este rea se colocan los datos declarados con la directiva .data. El tamao de estos datos se conoce en el momento de realizar el ensamblado y su tiempo de vida es toda la ejecucin del programa. El rea de datos dinmicos, que comienza en la direccin 0x10040000 y crece hacia el lmite superior del segmento de datos. En este rea se colocan los datos cuyo tamao no se conoce hasta el momento de la ejecucin del programa o cuyo tiempo de vida no es toda la ejecucin del programa. Para colocar datos en esta zona, se utiliza la llamada al sistema sbrk (ver seccin A3.2.2). A esta zona tambin se la conoce como montculo, montn o heap. El tercer bloque es el espacio de memoria reservado para la pila (ver seccin 3.4.4). Comienza en la direccin 0x7fffffff y crece hacia direcciones inferiores. MARS inicializa el puntero de pila a 0x7fffeffc, por lo que los 4 KB superiores de este rea no se usan. Obsrvese que la pila crece hacia el rea de datos dinmicos, y el rea de datos dinmicos crece hacia la pila. Por tanto, el tamao mximo de cada rea depende del espacio ocupada en la otra. En la mayora de los casos (cuando programemos directamente en ensamblador), nuestros datos se colocarn en la zona de datos estticos usando la directiva .data (como se ha hecho hasta ahora en todos los ejemplos). Es donde se suelen almacenar las variables globales de los lenguajes de alto nivel (en C: aquellas variables que se declaran fuera del cuerpo de cualquier funcin). Sin embargo, los datos tambin se suelen almacenar en la pila y en el rea de datos dinmicos. En la pila se almacenan las variables locales de los lenguajes de alto nivel (en C: aquellas que se declaran dentro del cuerpo de alguna funcin). Las variables locales tambin se pueden almacenar exclusivamente en registros cuando haya registros libres sucientes1 , como hemos hecho hasta ahora en todos los ejemplos. Para almacenar una variable en la pila usando ensamblador, basta con decrementar el puntero de pila tantos bytes como sea el tamao del dato a almacenar. Es importante recordar que cuando el procedimiento termina, tiene que dejar el puntero de pila en la misma posicin que lo encontr. Por tanto, una variable almacenada en la pila ser local y solo estar disponible hasta que el procedimiento acabe2 . Hasta ahora hemos usado la pila principalmente para almacenar el valor de los registros preservados entre llamadas. Obsrvese que la forma en la que la hemos usado es equivalente a crear variables locales para almacenar dichos valores. Por ltimo, en el rea de datos dinmicos se almacenan (sorprendentemente) la variables dinmicas, las cuales se denominan as porque son creadas y destruidas de forma dinmica durante la ejecucin del programa (en C: son los datos cuya memoria se reserva con malloc y se libera con free). La llamada al sistema sbrk Para poder almacenar datos en el rea de memoria dinmica, es necesario primero reservar el espacio que ocuparn. Para ello, se utiliza la llamada al sistema sbrk. Dicha llamada, recibe el nmero de bytes que queremos reservar en el registro $a0 y nos devuelve en $v0 la direccin de memoria donde comienza un bloque del tamao deseado y disponible para su uso. Esta llamada al sistema se puede utilizar para implementar las funciones malloc y free de la librera de C3 . En este caso, sbrk se utilizara para pedirle memoria al sistema operativo, y la librera que implementara malloc y free debera mantener informacin sobre el uso de esa memoria, para saber qu partes estn
Hay ms condiciones (que dependen del lenguaje de alto nivel) para poder evitar reservar espacio en la pila para una variable local. Por ejemplo, en C ser necesario almacenar una variable local en la pila si se le aplica en algn momento el operador & para obtener su direccin de memoria, porque sera imposible obtener la direccin de memoria de una variable que se almacena exclusivamente en un registro. 2 Un error comn de programacin consiste en olvidar esto y almacenar la direccin de una variable local en una variable global (de tipo puntero) y utilizar dicha variable posteriormente a que el procedimiento que reserv espacio para la variable haya acabado. El resultado de esto es que cuando se accede a dicha variable, la memoria que usaba puede haber sido sobreescrita por cualquier otro procedimiento que haya usado la pila. 3 Una implementacin moderna de la librera de C podra utilizar otras tcnicas adems de, o en lugar de, sbrk.
1

43

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

reservadas y cuales han sido ya liberadas con free (y por tanto, pueden volver a reservarse a consecuencia de otra llamada a malloc).

A3.2.3.

Datos y punteros a datos

La mayora de los datos usados por un programa se almacenan en la memoria, excepto en el momento justo en el que necesitan ser manipulados directamente (momento en el que se mueven a los registros del procesador, como hemos visto). Cuando se declara una variable en un lenguaje de alto nivel, el compilador le asigna una porcin de la memoria adecuada para el tamao del dato a almacenar4 . Dependiendo de varios factores (algunos de los cuales ya hemos mencionado), la variable se almacenar en la pila, el rea de datos estticos o el rea de datos dinmicos. La posicin de la variable en memoria puede estar sujeta a restricciones segn el tipo del dato. Por ejemplo: en MIPS es conveniente almacenar los enteros en posiciones de memoria cuya direccin sea mltiplo de 4 para poder acceder a ellos de forma rpida. En un lenguaje de alto nivel, el compilador se encargar de acceder a las variables de la forma adecuada cuando se necesite, moviendo los datos entre la memoria y los registros de forma transparente al programador. Sin embargo, en ensamblador es el programador quien debe realizar este trabajo. La forma de referirse en ensamblador a una variable almacenada en memoria es mediante su direccin, la cual normalmente podremos nombrar mediante una etiqueta. Esta direccin se utilizar para cargar el dato mediante instrucciones de carga y almacenamiento (seccin 3.4.2). Algunos lenguajes de programacin ofrecen la posibilidad de trabajar directamente con las direcciones de memoria de las variables. Por ejemplo, si en C tenemos las siguientes variables: char c = x; int i = 5; double d = 7.0; podremos referirnos a sus direcciones mediante las expresiones &c, &i y &d. A su vez, la direccin de una variable se puede almacenar en otra variable. Las variables que almacenan direcciones se llaman punteros. En C, el tipo de un puntero a una variable de tipo X se denota con X *5 . Por ejemplo, a las declaraciones anteriores podemos aadir las siguientes: char *pc = &c; int *pi = &i; double *pd = &d; En C, el valor (o la memoria) al que apunta un puntero x en cada momento se denota por *x. As, despus de las declaraciones anteriores, las siguientes expresiones son ciertas: i == *pi &i == pi Suponiendo que las variables anteriores sean globales, un compilador de C podra emitir el siguiente cdigo MIPS para traducir las declaraciones anteriores:
En ocasiones, no es necesario asignarle memoria a algunas variables locales si su tiempo de vida es muy corto, ya que es suciente con asignarles uno o varios registros. Esto se puede considerar una optimizacin, aunque ha sido el caso comn hasta ahora en los ejemplos que hemos visto. 5 Dado que los punteros son variables, tambin se almacenan en memoria y es posible tener punteros a punteros, que se denotaran con un asterisco adicional.
4

44

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

.data c: .byte c i: .word 5 d: .double 7.0 pc: .word c pi: .word i pd: .word d Obsrvese que todos los punteros ocupan el mismo espacio en memoria (una palabra de 4 bytes) independientemente del espacio ocupado por el dato al que apuntan. En un lenguaje de alto nivel, el uso de punteros es til slo en contadas ocasiones. Sin embargo, en algunos lenguajes de no tan alto nivel, usar punteros es la nica manera de realizar algunas tareas importantes. Por ejemplo, en C (y en ensamblador) es necesario usar punteros para: Pasar variables entre funciones por referencia en lugar de por valor. Esto permite que una funcin modique el valor almacenado en una variable que se le pasa. Utilizar memoria reservada dinmicamente. En C podemos escribir: int *e = malloc(sizeof(int)); para reservar 4 bytes (el tamao de un entero en MIPS) en el rea de memoria dinmica y guardar la direccin de comienzo de esos 4 bytes en la variable e. Esto sera aproximadamente equivalente a usar la llamada al sistema sbrk en ensamblador. Representar ecientemente algunas estructuras de datos, tales como listas enlazadas o rboles. Arrays En la seccin 3.4.2 vimos cmo traducir el acceso a elementos de un array. Si utilizamos el esquema descrito en dicha seccin para traducir el siguiente cdigo en C: int v[20]; ... for (int i = 0; i < 20; ++i) { v[i] = v[i] + 7; } obtenemos el siguiente cdigo en ensamblador: .data .space 80 # 20 elementos de 4 bytes

v: ...

.text li bge $t0, 0 $t0, 20, l2 # Inicializacin de i # Comprobacin de la condicin de salida 45

l1:

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

# Clculo de la $t1, sll $t2, add $t3, lw $t4, addi $t4, sw $t4, addi $t0, j l1 l2:

la direccin de v[i] v # Carga la direccin de comienzo de v $t0, 2 # carga el desplazamiento del elemento i-simo $t1, $t2 # Calcula &v[i] (direccin de v[i]) 0($t3) # Carga v[i] $t4, 7 # Calcula v[i] + 7 0($t3) # Almacena v[i] $t0, 1 # ++i

Podramos haber utilizado alguno de los modos de direccionamiento que acabamos de ver en la seccin A3.2.1 para hacer que el cuerpo del bucle fuera ms corto, pero el resultado a la hora de la ejecucin en la mquina hubiera sido el mismo debido a la traduccin que realizara el ensamblador. Si nos jamos en lo que hace el cdigo que acabamos de mostrar, nos damos cuenta de que en cada iteracin se calcula la direccin de v[i]. Para ello, se carga la direccin de inicio de v, se multiplica el ndice al que queremos acceder por el tamao del elemento (4 en este caso, por lo que podemos utilizar la instruccin sll) y se suman ambos valores. En total, tres instrucciones por iteracin para calcular la direccin de memoria a la que tenemos que acceder. Sin embargo, si analizamos a qu direcciones se accede en cada iteracin nos daremos cuenta de que se accede a posiciones consecutivas del array, cada una en una direccin que es 4 bytes mayor que la anterior. Una forma ms inteligente de proceder sera: calcular la direccin del primer elemento al principio y, en cada iteracin, sumarle 4 a la direccin accedida para obtener la direccin a la que se acceder en la siguiente iteracin, hasta que lleguemos a la direccin nal del array. Para expresar en C la forma de recorrer el array descrita en el prrafo anterior, podramos usar punteros de la siguiente forma6 : int v[20]; ... for (int *p = &v[0]; p < &v[20]; ++p) { *p = *p + 7; } que se podra traducir con el siguiente cdigo ensamblador: .data .space 80 # 20 elementos de 4 bytes

v: ...

.text # Clculo de la direccin de v[0] la $t0, v # Carga la direccin de comienzo de v == &v[0] # Clculo de la direccin de v[20]
En C, cuando se incrementa un puntero en n unidades el valor almacenado en la variable se incrementa en n s, donde s es el tamao del tipo al que apunta el puntero. Es decir, al sumarle 1 a un puntero a entero, el puntero pasa a apuntar al siguiente entero almacenado en memoria, no al siguiente byte.
6

46

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

l1:

# (v[20] no es realmente parte de v, cuyos elementos van de v[0] a v[19]) add $t1, $t0, 80 bge $t0, $t1, l2 # Comprobacin de la condicin de salida lw $t2, 0($t0) # Carga *p (== v[i]) addi $t2, $t2, 7 # Calcula v[i] + 7 sw $t2, 0($t0) # Almacena *p (== v[i]) addi $t0, $t0, 4 # ++p j l1

l2: Como vemos, el cuerpo del bucle es ahora ms corto, por lo que el nmero total de instrucciones ejecutadas es menor (y, por tanto, el programa tarda menos en ejecutarse). La transformacin anterior la realizan automaticamente todos los compiladores modernos. Estructuras Muchos lenguajes de programacin permiten la declaracin de estructuras (tambin llamadas registros). Cuando se declara una estructura en C, se crea un nuevo tipo que representa el producto cartesiano de sus componentes. La forma de almacenar una estructura en memoria es almacenando consecutivamente sus componentes, por lo que el tamao de una variable de un tipo estructura ser, al menos, la suma de los tamaos de sus componentes. Por ejemplo: struct Punto { int x; int y; }; struct Punto p; declara una variable p que tiene dos campos enteros (x e y). El tamao de p ser de 8 bytes, de los que los primeros 4 bytes se utilizarn para almacenar x y los siguientes para y. Para acceder en ensamblador a los campos de p necesitaremos conocer la direccin de p y tendremos que tener en cuenta el desplazamiento de cada campo. Por ejemplo: p: ... .text la li li sw sw .data .space 8

$t0, $t1, $t2, $t1, $t2,

p # Carga la direccin de p 3 4 0($t0) # p.x = 3 4($t0) # p.y = 4

El tamao de una estructura puede ser mayor que el de sus componentes debido a restricciones de alineamiento. Por ejemplo, las variables del siguiente tipo: struct A { char c; int i; }; 47

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

en MIPS ocuparan 8 bytes y no 5. Aunque el campo c slo ocupa un 1 byte, se dejan 3 bytes de espacio entre c e i, porque los enteros deben almacenarse en direcciones mltiplos de 4 para facilitar el acceso.

A3.2.4.

Ejercicios

1. Traduzca el cdigo del programa en C de la gura A3.2, tratando de ser lo ms literal posible y teniendo cuidado de colocar cada variable en la zona de memoria adecuada segn su declaracin. 2. Modique la traduccin anterior para que los recorridos de los arrays se hagan mediante punteros (o mediante subndices si el programa escrito ya usa punteros). Compare el nmero de instrucciones ejecutadas en cada caso (para ello, se puede utilizar la herramienta Instruction Counter de MARS). La solucin del ejercicio 1 se puede ver en las guras A3.3 y A3.4. La solucin del ejercicio 2 sera igual excepto para el procedimiento main que se puede ver en la gura A3.5.

48

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56

int i1; char a1[200]; struct S { int e1; char e2; }; struct S a2[94]; // Este procedimiento recibe un puntero a una variable de tipo // "struct S" y modifica la variable apuntada void proc2(struct S* p) { p->e1 = 1 - p->e1; print_int(p->e1); print_char(p->e2); print_char(\n); } void proc1(int *i, int j, int k) { struct S s; s.e1 = *i; s.e2 = a1[j]; proc2(&s); a2[k] = s; *i = *i + j; // modifica la variable entera apuntada por i } int main(int argc, char *argv[]) { i1 = 8; for (int i = 0; i < 200; ++i) { // almacena los 8 bits menos significativos de i en a1[i] a1[i] = (char) i; } for (int j = 32; j < 126; ++j) { proc1(&i1, j, j - 32); } for (int k = 0; k < 94; ++k) { proc2(&a2[k]); } print_int(i1); return 0; }

Figura A3.2: ej1.c

49

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44

.data i1: a1: .space 4 .space 200 # espacio para un entero # espacio para 200 chars

# struct S { int e1; char e2; }; # El tamao de S ser de 5 bytes, pero el tamao en un array sera de # 8 para preservar el alineamiento de e1 a2: .space 752 # 94 * 8, espacio para 94 S .text # recibe int* i en $a0, int j en $a1 e ink k en $a2 proc1: addi $sp, $sp, 24 # reserva espacio en la pila para $ra, $s0, $s1, $s2 y s (8 bytes) sw $ra, 20($sp) sw $s0, 16($sp) sw $s1, 12($sp) sw $s2, 8($sp) # s.e1 est en 0($sp) y s.e2 en 4($sp) move $s0, $a0 move $s1, $a1 move $s2, $a2 lw $t0, 0($s0) # carga el valor apuntado por i sw $t0, 0($sp) # s.e1 = *i; lb $t1, a1($s1) # carga a1[j]; sb $t1, 4($sp) # s.e2 = a1[j]; la $a0, 0($sp) # pone la direccin de s en $a0 jal proc2 # proc2(&s); la $t0, a2 sll $t1, $s2, 3 # k * 8 add $t0, $t0, $t1 # &a2[k] lw $t1, 0($sp) # s.e1 sw $t1, 0($t0) # a2[k].e1 = s.e1; lb $t1, 4($sp) # s.e2 sb $t1, 4($t0) # a2[k].e2 = s.e2; lw $t1, 0($s0) # *i add $t1, $t1, $s1 # *i + j sw $t1, 0($s0) # *i = *i + j; lw $s2, 8($sp) lw $s1, 12($sp) lw $s0, 16($sp) lw $ra, 20($sp) addi $sp, $sp, -24 jr $ra

Figura A3.3: Procedimiento proc1 y seccin .data de ej1.s

50

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104

# recibe p, un puntero a un S, en $a0 proc2: move $t0, $a0 # guarda el valor de p (para poder usar $a0 libremente) lw $t1, 0($t0) # carga p->e1 li $t2, 1 sub $t2, $t2, $t1 # calcula 1 - p->e1 sw $t2, 0($t0) # almacena p->e1 move $a0, $t2 li $v0, 1 syscall # print_int(p->e1); lb $a0, 4($t0) # carga p->e2 li $v0, 11 syscall # print_char(p->e2); li $a0, \n li $v0, 11 syscall # print_char(\n); jr $ra .globl main: addi sw sw sw li sw li bge la add sb addi j li bge la move subi jal addi j li bge la sll add jal addi j lw li syscall li syscall lw lw lw addi jr $sp, $sp, 12 $ra, 8($sp) $s0, 4($sp) $s1, 0($sp) $t0, 8 $t0, i1 $t1, 0 $t1, 200, l2 $t2, a1 $t2, $t2, $t1 $t1, 0($t2) $t1, $t1, 1 l1 $s0, 32 $s0, 126, l4 $a0, i1 $a1, $s0 $a2, $s0, 32 proc1 $s0, $s0, 1 l3 $s1, 0 $s1, 94, l6 $t3, a2 $a0, $s1, 3 $a0, $t3, $a0 proc2 $s1, $s1, 1 l5 $a0, i1 $v0, 1 $v0, 10 $s1, $s0, $ra, $sp, $ra 0($sp) 4($sp) 8($sp) $sp, -12 # reserva espacio en la pila para $ra, $s0, $s1 main

l1:

# i1 = 8; # i = 0 # i < 200

# a1[i] = (char) i # ++i # j = 32 # j < 126 # &i1

l2: l3:

# proc1(&i1, j, j - 32); # ++j # k = 0 # k < 94 # # # # k * 8 &a2[k] proc2(&a2[k]); ++k

l4: l5:

l6:

Figura A3.4: Procedimientos proc2 y main de ej1.s 51

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103

.globl main: addi sw sw sw li sw li la bge sb addi addi j li bge la move subi jal addi j la add bge move jal addi j lw li syscall li syscall lw lw lw addi jr

main $sp, $sp, 12 $ra, 8($sp) $s0, 4($sp) $s1, 0($sp) $t0, 8 $t0, i1 $t1, 0 $t2, a1 $t1, 200, l2 $t1, 0($t2) $t2, $t2, 1 $t1, $t1, 1 l1 $s0, 32 $s0, 126, l4 $a0, i1 $a1, $s0 $a2, $s0, 32 proc1 $s0, $s0, 1 l3 $s1, a2 $s2, $s1, 752 $s1, $s2, l6 $a0, $s1 proc2 $s1, $s1, 8 l5 $a0, i1 $v0, 1 $v0, 10 $s1, $s0, $ra, $sp, $ra 0($sp) 4($sp) 8($sp) $sp, -12 # reserva espacio en la pila para $ra, $s0, $s1

l1:

# # # # # # #

i1 = 8; i = 0 $t2 apunta al primer elemento de a1 i < 200 a1[i] = (char) i $t2 apunta al siguiente elemento ++i

l2: l3:

# j = 32 # j < 126 # &i1

# proc1(&i1, j, j - 32); # ++j # # # # # # $s1 = &a2[0] $s2 = &a2[94] (94 * 8 = 752) k < 94 ==> $s1 < $s2 &a2[k] proc2(&a2[k]); ++k ==> $s1 + 8

l4: l5:

l6:

Figura A3.5: Procedimiento main de ej2.s

52

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

Boletines de prcticas Normas sobre la entrega de prcticas


Ser necesario seguir las siguientes reglas para entregar las prcticas de todos los boletines, adems de cualquier otra regla especca que se indique en un boletn particular: Las prcticas se entregarn nicamente mediante la opcin de contenidos del alumno en SUMA. Se entregar un nico archivo comprimido en formato .tar.gz o .zip que contendr la memoria en formato PDF, los circuitos y programas que se hayan generado (cdigo fuente) y cualquier otro chero que se considere oportuno. El nombre del archivo ser prcticas-DNI-BOLETIN.FORMATO (por ejemplo: prcticas-12345678-B2.3.tar.gz). La memoria incluir, al menos, la siguiente informacin en un solo documento PDF: Nombre y DNI del autor o autores de la prctica. Descripcin de los cheros y directorios contenidos en el archivo entregado. Contestacin a las preguntas planteadas en los boletines. La respuesta a cada pregunta debe ser independiente, y debe estar clramente identicada. Explicacin de las pruebas realizadas para comprobar la correccin de la prctica entregada e instrucciones para su reproduccin. Cuando sea posible, se incluirn los cheros utilizados en dichas pruebas. Explicacin del trabajo realizado y cualquier aclaracin que el alumno considere pertinente. Lista de bibliografa y otras fuentes de informacin consultadas. No se corregir ninguna prctica que no se cia estrictamente a los formatos especicados anteriormente.

B3.1.
B3.1.1.

Uso del simulador MARS


Objetivos

Los objetivos de esta sesin son que el alumno se familiarice con el simulador MARS y que sea capaz de seguir la ejecucin de programas sencillos en ensamblador de MIPS.

B3.1.2.

Prerequisitos

Lectura de los apuntes de teora, en especial las secciones 3.1, 3.2, 3.3, 3.4 y 3.6.

B3.1.3.

Plan de trabajo

El plan de trabajo de esta sesin ser el siguiente: 1. Lectura por parte del alumno de las secciones B3.1.4 y B3.1.5. 2. Realizacin, en grupos de dos personas, de los ejercicios propuestos en el boletn (con supervisin del profesor).

53

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

B3.1.4.

El simulador MARS

MARS es un programa para simular el ISA de MIPS diseado para uso educacional. Puede ser descargado libremente de la direccin http://cs.missouristate.edu/MARS/, aunque para la realizacin de las prcticas de la asignatura se deber utilizar siempre la versin disponible en la pgina web de la asignatura (http://ditec.um.es/etc/). Esta ltima versin incluye algunas modicaciones necesarias para los ejercicios y el proyecto que se propondrn a lo largo del curso. Cuando se arranca el programa y se abre el chero ejemplo1.s (cuyo contenido se muestra en la gura B3.3), la ventana de MARS tiene el aspecto que se puede ver en la gura B3.1. Podemos editar directamente el programa usando MARS, o usar cualquier otro editor de texto. Tambin se puede ensamblar el programa usando la tecla F3 o el botn de la barra de herramientas, tras lo que el programa tendr el aspecto de la gura B3.2.

Figura B3.1: Aspecto de MARS despus de cargar un programa MARS incluye en su distribucin una documentacin escueta pero til. Esta documentacin est accesible desde el men Help o pulsando la tecla F1 . Se recomienda al alumno que se familiarice con la documentacin disponible. sta incluye un listado de las instrucciones, pseudoinstrucciones, directivas y llamadas al sistema disponibles en MARS. En la gura B3.2 podemos ver que MARS nos muestra varias ventanas y paneles de informacin: La ventana titulada Text Segment nos permite ver el contenido del segmento de texto, es decir, de la zona de memoria que contiene el programa recin ensamblado. Cada lnea de la tabla se corresponde con una palabra de 4 bytes, es decir: una instruccin de cdigo mquina. De cada palabra, podemos ver su direccin, su contenido en hexadecimal, la instruccin correspondiente tal y como la vera el procesador, y la lnea de ensamblador que gener la instruccin. En el caso de pseudoinstrucciones que generan varias instrucciones en cdigo mquina, la lnea de ensamblador aparece asociada solo 54

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

Figura B3.2: Aspecto de MARS despus de ensamblar un programa a la primera instruccin real (como es el caso de las instrucciones almacenadas en las direcciones 0x00400008 y 0x0040000c, que se generan a partir de la lnea 11 de ejemplo1.s). En la ventana Data Segment podemos ver el contenido de la zona de memoria que almacena los datos. De nuevo, la informacin aparece separada en palabras de 4 bytes. Esta ventana tambin nos permite modicar el contenido de la memoria escribiendo directamente en la celda correspondiente. La ventana Labels nos muestra la direccin de memoria que el ensamblador ha asociado con cada una de las etiquetas declaradas en el programa. A la derecha de la ventana principal disponemos de un panel que muestra el contenido de los registros del procesador y permite modicarlo. La informacin est dividida en 3 pestaas: Registers: muestra el contenido de los 32 registros de propsito general y de tres registros especiales: el contador de programa (pc) y los registros hi y lo usados por las instrucciones de multiplicacin y divisin. Coproc0: muestra algunos de los registros del coprocesador 0 relacionados con el manejo de excepciones (vase la seccin 3.5 para ms informacin). Coproc1: muestra el banco de registros en coma otante (seccin 3.4.5). Los registros aparecen tanto individualmente (para los valores de simple precisin) como en parejas (para los valores de doble precisin). En la parte inferior de la ventana disponemos de un panel con 2 pestaas. La primera de ellas muestra los mensajes de MARS, incluyendo los errores encontrados al ensamblar un programa. La segunda muestra la salida del programa simulado. 55

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

Si observamos la informacin mostrada por MARS justo despus de ensamblar el programa, podemos ver, por ejemplo, que la etiqueta var_b est asociada a la direccin 0x1001018. Si miramos en la ventana Data Segment el valor almacenado en dicha direccin, vemos que es 0x17, tal y como se especica en la lnea 4 de ejemplo1.s. Anlogamente, la etiqueta print_resultado est asociada con la direccin 0x00400034, la cual podemos comprobar en la ventana Text Segment que contiene la primera instruccin del procedimiento, denida en la lnea 24 de ejemplo1.s. Tambin podemos ver que el registro pc contiene el valor 0x0040000, que se corresponde con el comienzo del segmento de texto. Cuando se ensambla un programa, MARS asigna a dicho registro la direccin del procedimiento principal1 o la direccin de inicio del segmento de texto si no se encuentra un procedimiento principal. Por su parte, el registro sp se inicializa al valor 0x7fffeffc donde se encuentra la cima de la pila vaca. Podemos iniciar la ejecucin del programa actual con la tecla F5 o el botn . MARS permite ralentizar la ejecucin del programa para facilitar su seguimiento paso a paso usando el control etiquetado Run speed situado a la derecha de la barra de herramientas. La ejecucin tambin se puede realizar instruccin a instruccin usando la tecla F7 o el botn , e incluso hacia atrs con la tecla F8 o el botn . Si ejecutamos el programa despus de ensamblarlo, aparecer en la pestaa Run I/O el mensaje El resultado es 40 seguido de un mensaje indicando que el programa ha acabado correctamente. Para volver a ejecutar el programa, podemos volver a ensamblar el programa o devolver el simulador a su estado original con la tecla F12 o el botn . Tambin podemos usar la casilla Bkpt que se encuentra a la izquierda de cada instruccin en la ventana Text segment para conseguir que la ejecucin se pare cada vez que se llegue a esa instruccin. Adems de simular el ISA de MIPS de forma sucientemente completa para ejecutar programas simples, MARS tambin pone a disposicin del programador un conjunto de llamadas al sistema. En una mquina real, estas llamadas al sistemas seran provistas por el sistema operativo. Las llamadas al sistema disponibles incluyen las necesarias para realizar la entrada y salida. El listado completo de llamadas disponibles puede consultarse en la documentacin de MARS (tecla F1 ). En la seccin B3.1.5 se comentar el uso de las llamadas al sistema usadas por ejemplo1.s (por ejemplo, para mostrar el mensaje con el resultado).

B3.1.5.

Anatoma de un programa en ensamblador

En la gura B3.3 se muestra el programa ejemplo1.s. Este programa es muy simple: realiza la suma de dos valores almacenados en sendas variables e imprime un mensaje con el resultado. El programa est dividido en dos secciones: una de datos y otra de cdigo. Las directivas .data y .text marcan el inicio de cada una de ellas, respectivamente. En la seccin de datos se denen tres etiquetas. La primera de ellas (msg) apuntar a una cadena que se usar posteriormente para mostrar un mensaje. El mensaje se codica con la directiva .asciiz, que dene una cadena codicada en ASCII y terminada en 0. Las dos restantes (var_a y var_b) apuntan al espacio reservado para las dos variables. Este espacio se reserva y se inicializa mediante las directivas .word. En el segmento de cdigo se denen dos procedimientos: el procedimiento principal main y el procedimiento print_resultado, que es llamado desde main. El procedimiento main es el que se empieza a ejecutar al principio del programa. Este procedimiento, al igual que cualquier otro, podemos dividirlo en tres partes: Prlogo: de la lnea 9 a la 10. En l, se guarda en la pila el valor de los registros de tipo preservados entre llamadas (ver seccin 3.4.4) que se van a modicar en el cuerpo del procedimiento.
Para que el procedimiento principal sea reconocido como tal debe tener una etiqueta llamada main que haya sido declarada como global (con la directiva .globl). En particular, el procedimiento main de ejemplo1.s no sera reconocido como procedimiento principal porque no se ha incluido la directiva .globl correspondiente.
1

56

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

msg: var_a: var_b:

.data .asciiz "El resultado es " .word 17 .word 0x17

# Cadena para imprimir # Variable inicializada a 17 # Variable inicializada a 23

.text # Procedimiento main: addi sw lw lw add jal li syscall lw addi jr

principal $sp, $sp, -4 $ra, 0($sp) $t0, var_a $t1, var_b $a0, $t0, $t1 print_resultado $v0,10 $ra, 0($sp) $sp, $sp, 4 $ra # # # # # # # # # # Hace espacio en la pila Almacena $ra en la pila Carga var_a en $t0 Carga var_b en $t1 Suma, resultado en $a0 para print_resultado Llama a print_resultado Cdigo de la llamada al sistema "exit" en $v0 Termina el programa Recupera el valor de $ra de la pila Devuelve el espacio de pila usado

# Procedimiento para # cadena msg seguida print_resultado: move $t0, la $a0, li $v0, syscall move $a0, li $v0, syscall jr $ra

imprimir un mensaje con el resultado. Imprime la del valor que se le pasa como primer argumento (en $a0) $a0 msg 4 $t0 1 # # # # # # # # Guarda en $t0 el valor a imprimir Pone en $a0 la direccin de la cadena Cdigo syscall para imprimir una cadena Imprime la cadena Pone en $a0 el entero a imprimir Cdigo syscall para imprimir un entero Imprime el entero Vuelve al procedimiento invocador

Figura B3.3: ejemplo1.s Cuerpo: de la lnea 11 a la 16. Se realiza el trabajo del procedimiento. Eplogo: de la lnea 17 a la 19. Se recuperan los valores guardados anteriormente por el prlogo y se retorna el control al procedimiento invocador. En el prlogo de este procedimiento se guarda en la pila2 slo el registro $ra, ya que es el nico del conjunto de registros de tipo preservados entre llamadas que se modica en el cuerpo del procedimiento (en concreto, se modica en la lnea 14). El cuerpo del procedimiento carga el valor almacenado en las variables var_a y var_b, lo suma y llama al procedimiento print_resultado. Obsrvese que el resultado de la suma (lnea 13) se almacena en el registro $a0 para que sea el primer argumento de print_resultado. Por ltimo, el cuerpo del procedimiento invoca la llamada al sistema nmero 10, la cual naliza la ejecucin del programa. Por otro lado, el procedimiento print_resultado se limita a recibir un argumento, imprimir un mensaje e imprimir el valor del argumento recibido. Obsrvese que el prlogo de este procedimiento est vaco dado que no modica el valor de ningn registro de tipo preservado entre llamadas y el eplogo se limita a devolver el control al procedimiento invocador mediante la instruccin jr $ra.
2 En el caso concreto de este procedimiento, no sera estrictamente necesario guardar nada, ya que el programa acaba siempre en la lnea 16, antes de que se llegue a ejecutar el eplogo.

57

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

B3.1.6.

Ejercicios

Para cada ejercicio, el portafolios deber incluir una explicacin de cmo se ha resuelto y, en caso de que se realice o modique algn programa, el cdigo realizado y al menos un ejemplo de su ejecucin. 1. Cargue, ensamble y ejecute el programa ejemplo1.s. Compruebe que el mensaje mostrado en la pestaa Run I/O es El resultado es 40. Utilice MARS para, sin modicar el cdigo en ensamblador, conseguir que la salida del programa en la siguiente ejecucin sea El resultado es 5. 2. Modique el programa ejemplo1.s para que, en vez de sumar el contenido de las variables var_a y var_b, le pregunte cada vez al usuario qu nmeros sumar. 3. Escriba un programa para sumar fracciones. El programa le debe preguntar al usuario el valor de cuatro c variables (a, b, c y d) y debe calcular y mostrar el resultado de a + d . No es necesario que la fraccin b resultante aparezca simplicada y se puede asumir que todos los resultados y valores intermedios caben en 32 bits.

B3.2.
B3.2.1.

Convenciones de programacin en MIPS


Objetivos

El objetivo de la sesin es que el alumno sea capaz de escribir procedimientos que realicen tareas sencillas utilizando correctamente las convenciones de usos de registros y de llamadas a procedimientos de MIPS.

B3.2.2.

Prerequisitos

Lectura de los apuntes de teora, en especial las secciones 3.3.1 y 3.4.4.

B3.2.3.

Plan de trabajo

El plan de trabajo de esta sesin ser el siguiente: 1. Lectura por parte del alumno de las secciones B3.2.4 y B3.2.5. 2. Realizacin, en grupos de dos personas, de los ejercicios propuestos en el boletn (con supervisin del profesor).

B3.2.4.

Ejemplos de paso de parmetros

Vamos a comenzar analizando un pequeo programa de ejemplo, cuyo cdigo fuente se encuentra disponible en el chero procedimientos.s. En este programa se incluyen, entre otras cosas, los siguientes procedimientos: main: el procedimiento principal que, en este caso, se limita a llamar a proc1 y a nalizar el programa despus. proc1: es un procedimiento que no recibe ningn argumento ni devuelve ningn resultado. Sin embargo, realiza llamadas a otros procedimientos (gura B3.4). proc2: un procedimiento que recibe tres parmetros y devuelve dos. Siguiendo el convenio de paso de parmetros del ABI de MIPS, los parmetros se comunicarn a proc2 usando los registros $a0, $a1 y $a2. Por su parte, los resultados se recibirn en los registros $v0 y $v1 (gura B3.5). 58

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

proc3: un procedimiento que recibe seis parmetros y devuelve uno. Siguiendo los mismos convenios, los cuatro primeros parmetros se comunicarn usando los registros $a0, $a1, $a2 y $a3, mientras que el quinto y el sexto parmetro se pasarn usando la pila (como se describe ms adelante). Adems, se utilizar el registro $v0 para recibir el resultado del procedimiento (gura B3.6). printlnInt: procedimiento que imprime un entero (que recibe en $a0) y salta a la lnea siguiente de la consola (gura B3.7).
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65

# proc1: no recibe ni devuelve nada proc1: addi $sp, $sp, -12 sw $s1, 8($sp) sw $s0, 4($sp) sw $ra, 0($sp) # llama li li li jal move move a proc2(3, 4, 5) $a0, 3 $a1, 4 $a2, 5 proc2 $s0, $v0 # Primer valor devuelto por proc2 $s1, $v1 # Segundo valor devuelto por proc2

# imprime los resultados en orden inverso move $a0, $s1 jal printlnInt move $a0, $s0 jal printlnInt # llama a proc3(8, 7, 6, 5, 4, 3) li $a0, 8 li $a1, 7 li $a2, 6 li $a3, 5 # Los dos argumentos restantes se pasan usando la pila addi $sp, $sp, -8 # Hace sitio en la pila li $t0, 4 sw $t0, 0($sp) # Quinto argumento: 4 li $t0, 3 sw $t0, 4($sp) # Sexto argumento: 3 jal proc3 addi $sp, $sp, 8 # Devuelve el espacio a la pila # imprime el resultado move $a0, $v0 jal printlnInt lw lw lw addi jr $ra, $s0, $s1, $sp, $ra 0($sp) 4($sp) 8($sp) $sp, 12

Figura B3.4: procedimientos.s proc1 El procedimiento proc1 comienza guardando en la pila los registros preservados entre llamadas que modica ($ra, $s0 y $s1), los cuales se desapilan al nal del procedimiento. A continuacin, este procedi59

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

68 69 70 71 72 73 74

# proc2: recibe proc2: add add mul mul jr

tres enteros (x, y, z) y devuelve dos (x + y + z, x * y * z) $v0, $v0, $v1, $v1, $ra $a0, $v0, $a0, $v1, $a1 $a2 $a1 $a2

Figura B3.5: procedimientos.s proc2


77 78 79 80 81 82 83 84 85 86

# proc3: recibe proc3: add add add lw add lw add jr

seis enteros y devuelve uno (la suma de todos) $v0, $v0, $v0, $t0, $v0, $t1, $v0, $ra $a0, $a1 $v0, $a2 $v0, $a3 0($sp) # El quinto parmetro est en la cima de la pila $v0, $t0 4($sp) # Y el sexto parmetro en la siguiente posicin $v0, $t1

Figura B3.6: procedimientos.s proc3


89 90 91 92 93 94 95 96

# imprime un entero (recibido en $a0) y un retorno de carro printlnInt: li $v0, 1 # el entero a imprimir ya se encuentra en $a0 syscall la $a0, msg_cr # direccin de la cadena "\n" li $v0, 4 syscall jr $ra

Figura B3.7: procedimientos.s printlnInt miento realiza varias llamadas consecutivas a procedimientos: 1. A proc2 pasndole los parmetros 3, 4 y 5. Para ello, simplemente se colocan dichos valores en los registros $a0, $a1 y $a2, y se utiliza la instruccin jal para guardar el contador de programa en $ra y saltar a la primera instruccin de proc2. Obsrvese que los valores devueltos por proc2 mediante los registros $v0 y $v1 se copian a los registros $s0 y $s1. Esto se hace as para evitar que las llamadas posteriores a otros procedimientos sobreescriban esos valores. 2. A printlnInt, pasndole en el registro $a0 el valor almacenado en $s1, es decir, el segundo valor que haba sido devuelto anteriormente por proc2. 3. A printlnInt de nuevo, pasndole ahora el valor almacenado en $s0, es decir, el primer valor que haba sido devuelto anteriormente por proc2. Obsrvese que, si no se hubiera guardado anteriormente en $s0 el valor que se quiere imprimir ahora, ste hubiera sido sobreescrito por la ejecucin anterior de printlnInt. 4. A proc3 pasndole los parmetros 8, 7, 6, 5, 4 y 3. Debido a que el nmero de parmetros que recibe proc3 es mayor que el nmero de registros dedicados para el paso de parmetros (4), es necesario 60

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

utilizar la pila para comunicar algunos de ellos. Los cuatro primeros parmetros se comunican usando los registros $a0, $a1, $a2 y $a3, mientras que el quinto y el sexto parmetros se apilan. Para ello, se decrementa $sp en 8 unidades (porque cada parmetro va a ocupar 4 bytes de la pila) y se mueve el quinto parmetro a la posicin de memoria 0($sp) y el sexto a 4($sp). Es decir, la pila quedar como si se hubiera apilado primero el sexto parmetro y despus el quinto3 . El procedimiento que realiza la llamada (proc3 en este caso) es el responsable de devolver el espacio a la pila una vez que recupera el control (lnea 55). El procedimiento proc2 se limita a realizar las operaciones aritmticas requeridas para calcular los resultados que devuelve en $v0 y $v1. No necesita manipular la pila, ya que no escribe en ningn registro preservado entre llamadas. De hecho, slo escribe en $v0, $v1 y los registros HI y LO 4 . El procedimiento proc3 tiene la peculiaridad de que recibe ms de 4 argumentos. El quinto y el sexto argumentos los recibe mediante la pila, cuyo estado al comienzo del procedimiento ser el siguiente: 6o arg.: 3 5o arg.: 4 $sp ...

Es decir, proc3 debe acceder a 0($sp) para leer su quinto argumento y a 4($sp) para el sexto. Los cuatro primeros argumentos estarn, como es habitual, en los registros $a0, $a1, $a2 y $a3. Sin embargo, en estos casos se ha de tener en cuenta que si proc3 necesita apilar algo, la posicin de los argumentos respecto de $sp cambiar. Por ejemplo, el estado de la pila si apilamos un entero i ser: 6o arg.: 3 5o arg.: 4 i $sp

...

y a partir de ese momento proc3 deber acceder a 4($sp) para leer su quinto argumento y a 8($sp) para el sexto. Alternativamente, se puede utilizar el registro $fp para almacenar el valor de $sp a la entrada del procedimiento y usar dicho registro como base para acceder a los parmetros extra (o variables locales) con desplazamientos estables independientemente de lo que se apile en el cuerpo del procedimiento. En este ltimo caso, habr que guardar previamente el valor de $fp en la pila porque es un registro preservado entre llamadas. En algunos casos, podramos pensar que no es necesario seguir estrictamente los convenios de llamada a procedimiento y las reglas de uso de la pila. Por ejemplo, proc1 guarda en la pila el valor de los registros s0 y s1 antes de modicarlos pero el procedimiento main que llama a proc1 no usa dichos registros, por lo que no se vera afectado si proc1 no los guardara. Sin embargo, si hiciramos eso, proc1 no podra ser llamado desde otro procedimiento que s usara esos registros (reduciendo la reusabilidad) y, si en algn momento
Aunque apilar los parmetros en orden inverso puede parecer extrao, es el habitual en la mayora de ABIs porque facilita la implementacin de funciones que reciben un nmero variable de argumentos (como por ejemplo, la funcin printf de C). 4 La instruccin mul no es una pseudoinstruccin, pero usa el mismo hardware que la instruccin mult descrita en la seccin 3.4.1.
3

61

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

modicramos el procedimiento main, tendramos que recordar no usar esos registros (arriesgndonos a introducir un error en caso contrario). Es importante recordar que es necesario cumplir las convenciones de llamadas siempre, incluso aunque en algunos casos sepamos que el programa funcionara correctamente sin ellas. Estas convenciones aseguran que procedimientos escritos por distintos programadores (o compilados por distintos compiladores) pueden llamarse unos a otros correctamente.

B3.2.5.

Ejemplo de procedimiento recursivo

Frecuentemente es til que un procedimiento se llame a s mismo. Por ejemplo, el elemento isimo de la conocida sucesin de Fibonacci se dene de la siguiente manera: 0 si i = 0 fi = 1 si i = 1 fi2 + fi1 en otro caso Una traduccin directa (aunque muy ineciente) de la denicin anterior sera un procedimiento que recibiera un argumento entero (i ) y siguiera los siguientes pasos: 1. Comprobar si el valor de i es 0, acabando y devolviendo 0 en ese caso. 2. Comprobar si el valor de i es 1, acabando y devolviendo 1 en ese caso. 3. Calcular fi2 , llamndose a s mismo. 4. Calcular fi1 , llamndose a s mismo. 5. Sumar los valores calculados en los 2 pasos anteriores, acabando y devolviendo el resultado de la suma. En el ensamblador de MIPS, un procedimiento puede llamarse a s mismo sin ningn problema, siempre y cuando se sigan correctamente las convenciones de programacin vistas hasta ahora. Por tanto, una implementacin del algoritmo anterior sera el procedimiento de la gura B3.8.

B3.2.6.

Ejercicios

Para cada ejercicio, el portafolios deber incluir una explicacin de cmo se ha resuelto y, en caso de que se realice o modique algn programa, el cdigo realizado y al menos un ejemplo de su ejecucin. 1. El tringulo de Pascal (tambin conocido como tringulo de Tartaglia) es un conjunto de nmeros dispuestos en forma de tringulo que expresan coecientes binomiales. Dicho tringulo permite calcular de forma sencilla nmeros combinatorios. El tringulo se construye siguiendo las siguientes reglas: La primera la tiene un elemento (el 1) y cada la tiene un elemento ms que la anterior. El primer y ltimo elemento de todas las las es el 1. El valor de todos los elementos (excepto el primero y ltimo de cada la) es la suma del elemento que se encuentra en la misma posicin en la la inmediatamente superior y del elemento que se encuentra en la posicin previa de la la inmediatamente superior.

62

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58

fibo:

addi sw sw sw beq beq

$sp, $s1, $s0, $ra,

$sp, -12 8($sp) 4($sp) 0($sp)

$a0, 0, es0 $a0, 1, es1

# guarda $a0 en $s0 move $s0, $a0 # calcula fibo(i - 1) subi $a0, $s0, 1 jal fibo move $s1, $v0 # calcula fibo(i - 2) subi $a0, $s0, 2 jal fibo # suma fibo(i - 2) y fibo(i - 1) add $v0, $v0, $s1 j fin es1: li j li lw lw lw addi jr $v0, 1 fin $v0, 0 $ra, $s0, $s1, $sp, $ra 0($sp) 4($sp) 8($sp) $sp, 12

# guarda el resultado en $s1

es0: fin:

Figura B3.8: fibo.s En la gura B3.9 se pueden ver las 6 primeras lneas del tringulo ya construido. Si numeramos los elementos del tringulo como se muestra en la parte izquierda de la gura B3.9, cada elemento se corresponde con el coeciente binomial n , que a su vez se corresponde con el nmero de k combinaciones de n objetos tomados en grupos de k elementos sin considerar el orden (C(n, k)). El valor de cada elemento del tringulo viene dado por la siguiente frmula: 1 si n = k = 1 si k = 0 C(n 1, k 1) + C(n 1, k) en otro caso

C(n, k) =

n k

El objetivo del ejercicio es hacer un programa que permita calcular C(n, k) y permita tambin imprimir algunas las del tringulo de Pascal. El programa deber tener, como mnimo, los siguientes procedimientos: a) Un procedimiento principal main que le muestre al usuario un men con tres opciones: 1) Calcular un coeciente binomial o nmero combinatorio. 63

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

1 n=0 n=1 n=2 n=3 n=4 n=5 ... k=0 1 1 1 1 1 1 k=1 1 2 3 4 5 k=2 k=3 k=4 k=5 ... 1 1 3 6 10 1 4 10 1 1 5 1 1 1 5 4 3 6 10 10 ... Figura B3.9: Tringulo de Pascal. A la izquierda se muestra en forma de tabla, mientras que a la derecha se muestra de forma que se aprecia mejor la forma triangular y la simetra. En la segunda representacin, cada elemento es la suma de los dos elementos situados sobre l (el que est arriba a la izquierda y el que est arriba a la derecha). 2) Mostrar el tringulo de Pascal. 3) Salir del programa. Este procedimiento deber llamar a cmd_combinaciones o a cmd_triangulo segn la opcin elegida por el usuario. Despud de cada operacin, se volver a presentar el men. b) Un procedimiento pregunta_int que recibe la direccin de una cadena, la cual muestra por pantalla y espera a que el usuario introduzca un nmero entero, el cual devuelve. c) Un procedimiento cmd_combinaciones que pregunte al usuario el valor de n y de k, llame al procedimiento coeficiente para calcular n , e imprima el resultado. k d) Un procedimiento cmd_triangulo que pregunte al usuario un nmero entero N y llame al procedimiento triangulo para que muestre las primeras N las del tringulo de Pascal. e) Un procedimiento recursivo coeficiente para calcular el coeciente binomial propiamente dicho usando la frmula explicada arriba. Deber recibir dos enteros y devolver otro. f ) Un procedimiento triangulo que reciba un nmero entero N e imprima las primeras N las del tringulo de Pascal. Este procedimiento debe llamar al anterior para calcular cada uno de los elementos a mostrar. Es suciente con mostrar los elementos de cada la separados por un espacio (sin que queden necesariamente alineados). El chero pascal.s contiene la implementacin de algunos de los procedimientos mencionados arriba. Complete el programa prestando especial atencin a respetar los convenios de programacin exlicados en las secciones 3.3.1 y 3.4.4. Tenga en cuenta que esta forma de generar el tringulo de Pascal es muy ineciente porque se calcula varias veces el mismo valor. Existen algoritmos mucho ms ecientes pero no son el objetivo de esta prctica. 1 2 3 4 5 1 1 1 1 1

B3.3.
B3.3.1.

Proyecto de programacin en MIPS


Objetivos

El objetivo del proyecto de programacin es que el alumno demuestre que es capaz de entender y modicar correctamente un pequeo programa en ensamblador MIPS. 64

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

B3.3.2.

Pong

El programa que se estudiar y mejorar en esta prctica es una versin de PONG, uno de los primeros videojuegos disponibles comercialmente. PONG fue publicado por primera vez en 1972 por Atari y es posible encontrar gran cantidad de versiones de este juego hoy en da. El juego est inspirado en el tenis de mesa. Participan dos jugadores, cada uno de los cuales maneja una raqueta que puede moverse hacia arriba o hacia abajo. El campo de juego es un rectngulo en el que se mueve una pelota. La pelota se mueve en lnea recta hasta que rebota con la pared superior, con la pared inferior o con alguna de las raquetas, que los jugadores debern de utilizar para evitar que la pelota llegue a la banda que deenden. Inicialmente, cada jugador est situado en el centro de cada una de las bandas izquierda y derecha del campo, y la pelota comienza a moverse lentamente en horizontal. La pelota incrementa su velocidad y cambia su direccin cada vez que rebota. Cuando la pelota llega a la banda derecha o a la izquierda, el jugador del lado contrario gana un punto y la pelota se vuelve a situar en el centro movindose lentamente. Para la realizacin de la prctica se partir de una versin del programa simplicada pero funcional. El alumno deber mejorar el programa realizando una serie de modicaciones que se detallan en la seccin B3.3.4. Para ello, deber primero invertir suciente tiempo en estudiar el cdigo para entender cmo funciona. Es muy aconsejable leer detenidamente el cdigo del programa pong.s a la vez que se leen las explicaciones presentes en este documento.

B3.3.3.

Descripcin del programa inicial

El programa se puede encontrar en el chero pong.s. Para su ejecucin, deberemos utilizar la versin de MARS disponible en la pgina web de la asignatura (http://ditec.um.es/etc/, ver seccin B3.1.4). Si se carga el programa en MARS, se ensambla y se ejecuta, comenzar una partida de PONG en el panel de entrada y salida de ejecucin5 . El jugador (o jugadores) interacta con el programa a travs del teclado, para lo cual hay que tener en cuenta que el panel de entrada y salida debe tener el foco para recibir las pulsaciones de teclas (normalmente es suciente con hacer click con el ratn en el cuadro de texto). Solo unas pocas teclas tienen alguna funcin asociada, como se puede ver en la tabla B3.1. Tecla a z k m x Funcin Mover la pala del jugador izquierdo hacia arriba. Mover la pala del jugador izquierdo hacia abajo. Mover la pala del jugador derecho hacia arriba. Mover la pala del jugador derecho hacia abajo. Terminar la ejecucin del programa.

Tabla B3.1: Pulsaciones de teclas reconocidas por el programa pong.s. El estado actual del juego se representa mediante caracteres en el panel de entrada y salida de MARS. La gura B3.10 muestra ejemplos de la ejecucin del programa inicial. La salida se limpia y se vuelve a generar completamente varias veces por segundo. La primera lnea de la salida muestra la puntuacin de los jugadores. Los lmites del campo se representan con los caracteres # y +, la pelota con el carcter O y las palas de los jugadores derecho e izquierdo con los caracteres [ y ], respectivamente. La ltima lnea muestra varios nmeros que proporcionan informacin sobre el funcionamiento del programa. El signicado de estos valores, en el orden que aparecen, es: 1. Milisegundos transcurridos entre el inicio de las dos ltimas actualizaciones de pantalla.
5 Conviene ajustar el tamao del panel de forma que quepa entero el campo de juego. Para ello, puede ser necesario utilizar las pequeas echas situadas en la esquina superior izquierda del panel.

65

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

0 0 +########################################+ # # # # # # # # # # # # #] [# #] O [# #] [# # # # # # # # # # # # # # # +########################################+ 201 375000 375000 1276500 468750 150 0

6 3 +########################################+ # # # # # # # [# # [# # [# #] # #] # #] # # O # # # # # # # # # # # # # +########################################+ 208 375000 187500 1545950 599096 200 143

Figura B3.10: Dos ejemplos del aspecto de la salida del programa en diferentes momentos del juego. 2. Coordenada vertical del extremo superior de la pala del jugador izquierdo (posicion_jugador_izquierdo_y). 3. Coordenada vertical del extremo superior de la pala del jugador derecho (posicion_jugador_derecho_y). 4. Coordenada horizontal del centro de la pelota (posicion_pelota_x). 5. Coordenada vertical del centro de la pelota (posicion_pelota_y). 6. Componente horizontal de la velocidad de la pelota (velocidad_pelota_x). 7. Componente vertical de la velocidad de la pelota (velocidad_pelota_y). El espacio de coordenadas tiene el origen en la esquina superior izquierda del campo, y las coordenadas crecen hacia la derecha y hacia abajo. El campo mide 2000000 unidades de ancho y 1000000 unidades de alto. Funcionamiento general El programa mantiene en todo momento el estado actual del juego en unas pocas variables globales que se explican en una seccin posterior de este documento. El funcionamiento habitual del programa consiste en repetir un bucle que realiza las siguientes acciones: 1. Comprueba si se ha pulsado alguna tecla, y acta en consecuencia si es as. 2. Obtiene la hora actual del sistema (en milisegundos) y calcula el tiempo transcurrido desde la ltima vez que se actualiz la pantalla. 3. Si el tiempo transcurrido es mayor que el tiempo entre fotogramas: Se actualizan las variables de estado del juego segn el tiempo que haya transcurrido desde la ltima vez. Se limpia la pantalla y se imprime el nuevo estado del programa.

66

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

El tiempo entre fotogramas se calcula al comienzo del programa y depende del nmero de fotogramas mximos por segundo deseados (fps_max), que inicialmente vale 5. El programa utiliza un espacio de coordenadas para codicar el estado del juego que es distinto del espacio de coordenadas utilizado para la representacin mediante caracteres. Esto es as porque el espacio de coordenadas de la representacin viene impuesto por las limitaciones del dispositivo de salida (es decir, el panel de salida de MARS que simula la pantalla). La limitacin principal es que las coordenadas tienen que ser enteros en rangos muy limitados (de 0 a 39 en horizontal y de 0 a 15 en vertical). Esto implica, por ejemplo, que no podemos utilizar este sistema de coordenadas para codicar la posicin de la pelota con suciente precisin para conseguir un movimiento suave (dentro de las limitaciones de la representacin mediante caracteres y del simulador) y una aceleracin gradual. Habra dos formas sencillas de solucionar el problema: Utilizar nmeros en coma otante para representar la posicin de la pelota y otros objetos, utilizando un espacio de coordenadas con el mismo rango que el de la pantalla. Utilizar nmeros enteros para representar todas las posiciones, pero aumentar el rango del espacio de coordenadas para permitir mayor precisin efectiva. Es decir, tendremos un espacio de coordenadas para el modelo (con un tamao del campo de 2000000 1000000) y otro para la vista (con un tamao del campo de 40 16). En este caso habr que hacer una traduccin de las coordenadas del modelo a las de la vista cada vez que se necesite dibujar la pantalla. Ambas soluciones son igual de buenas. En el programa de la prctica se ha elegido la segunda arbitrariamente. Datos del programa En la seccin de datos del programa (.data) se declaran las siguientes variables globales: Variables relativas a la conguracin del juego, las cuales se inicializan al principio del programa y no se modican durante el estado de la partida (se utilizan como constantes). Se muestra el valor por defecto entre parntesis: alto_campo: Altura del campo de juego (1000000). ancho_campo: Anchura del campo de juego (2000000). aceleracion_rebote_x: Incremento del valor absoluto de la componente horizontal de la velocidad cada vez que la pelota rebota contra una pala (25). aceleracion_rebote_y: Incremento del valor absoluto de la componente vertical de la velocidad cada vez que la pelota rebota contra el extremo superior o inferior de una pala. El incremento efectivo en cada rebote es proporcional a la distancia del punto del rebote hasta el centro de la pala (150). alto_jugador: Tamao de las palas de los jugadores ((alto_campo/alto_dibujo) 3). velocidad_jugador: Incremento (o decremento) de la posicin vertical de los jugadores cada vez que se pulsa la tecla correspondiente (alto_campo/alto_dibujo). ancho_dibujo: Ancho del campo en la pantalla, en caracteres (40). alto_dibujo: Alto del campo en la pantalla, en caracteres (16). fps_max: Nmero mximo de fotogramas por segundo a dibujar (5). Variables relativas al estado de la partida actual: posicion_pelota_x: Coordenada horizontal del centro de la pelota. 67

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

posicion_pelota_y: Coordenada vertical del centro de la pelota velocidad_pelota_x: Componente horizontal de la velocidad de la pelota, en unidades por milisegundo. velocidad_pelota_y: Componente vertical de la velocidad de la pelota, en unidades por milisegundo. posicion_jugador_izquierdo_x: Coordenada horizontal de la pala izquierda (se inicializa al valor (ancho_campo/ancho_dibujo)/2). posicion_jugador_izquierdo_y: Coordenada vertical del extremo superior de la pala izquierda. posicion_jugador_derecho_x: Coordenada horizontal de la pala derecha (se inicializa al valor (ancho_campo ancho_campo/ancho_dibujo)/2). posicion_jugador_derecho_y: Coordenada vertical del extremo superior de la pala derecha. puntuacion_jugador_izquierdo: Puntos conseguidos por el jugador izquierdo. puntuacion_jugador_derecho: Puntos conseguidos por el jugador derecho. Procedimientos del programa prod_vector_int: Calcula el producto de un vector de dos enteros por un escalar entero. Argumentos: v.x: (Entero) Primera componente del vector. v.y: (Entero) Segundo componente del vector. c: (Entero) Escalar. Resultados: ret.x: (Entero) v.x c. ret.y: (Entero) v.y c. suma_vector: Suma dos vectores de enteros. Argumentos: a.x: (Entero) Primera componente del primer vector. a.y: (Entero) Segundo componente del primer vector. b.x: (Entero) Primera componente del segundo vector. b.y: (Entero) Segundo componente del segundo vector. Resultados: ret.x: (Entero) a.x b.x. ret.y: (Entero) a.y b.y. dibuja_campo: Dibuja el campo de juego, la pelota y las palas segn el estado actual de las variables globales. Este procedimiento no recibe ningn argumento ni devuelve ningn resultado, solo produce salida por pantalla. En primer lugar, el procedimiento realiza la transformacin del espacio de coordenadas del modelo al espacio de coordenadas de la pantalla. Para ello, se utilizan operaciones aritmticas en coma otante y se calculan las siguientes variables locales a partir de las variables globales: 68

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

escala_x: Factor de escala horizontal. Asignada al registro $f0. Valor: ancho_dibujo/ancho_campo escala_y: Factor de escala vertical. Asignada al registro $f1. Valor: alto_dibujo/alto_campo e_pelota_x: Posicin horizontal escalada del centro de la pelota. Asignada al registro $s0. Valor: posicion_pelota_x escala_x e_pelota_y: Posicin vertical escalada del centro de la pelota. Asignada al registro $s1. Valor: posicion_pelota_y escala_y e_jug_izq_y_sup: Posicin vertical escalada del extremo superior de la pala del jugador izquierdo. Asignada al registro $s2. Valor: posicion_jugador_izquierdo_y escala_y e_jug_izq_y_inf: Posicin vertical escalada del extremo inferior de la pala izquierda. Asignada al registro $s3. Valor: (posicion_jugador_izquierdo_y + alto_jugador) escala_y e_jug_izq_x: Posicin horizontal escalada de la pala izquierda. Asignada al registro $s4. Valor: posicion_jugador_izquierdo_x escala_x e_jug_der_y_sup: Posicin vertical escalada del extremo superior de la pala del jugador derecho. Asignada al registro $s5. Valor: posicion_jugador_derecho_y escala_y e_jug_der_y_inf: Posicin vertical escalada del extremo inferior de la pala del jugador derecho. Asignada al registro $s6. Valor: (posicion_jugador_derecho_y + alto_jugador) escala_y e_jug_der_x: Posicin horizontal escalada de la pala del jugador derecho. Asignada al registro $s7. Valor: posicion_jugador_derecho_x escala_x Para el clculo del valor de estas variables, se utiliza el registro $f2 de forma temporal. Aunque las operaciones que necesitamos realizar requieren nmeros en coma otante (escala_x y escala_y son valores entre 0 y 1), tanto las variables globales de las que partimos como los resultados que necesitamos son valores enteros. Por tanto, es necesario realizar tranferencias entre los bancos de registros de enteros y de coma otante (utilizando las instrucciones mtc1 y mfc1), transferencias de memoria a los registros de coma otante (utilizando la instruccin lwc1) y conversiones entre ambos tipos de datos (utilizando las instrucciones cvt.s.w y cvt.w.s). Por ejemplo, para calcular e_jug_izq_y_inf se siguen los siguientes pasos: 1. Se carga en $t0 el contenido de la variable posicion_jugador_izquierdo_y. 2. Se carga en $t1 el contenido de la variable alto_jugador. 3. Se suman los valores cargados anteriormente, dejando el resultado en $t0. 4. Se copia el contenido de $t0 a $f2 utilizando la instruccin mtc1 $t0, $f2. Esta instruccin copia el contenido del registro literalmente, por lo que el resultado es que tendremos en $f2 el resultado de la suma anterior codicado como entero en complemento a dos. 5. Se utiliza la instruccin cvt.s.w $f2, $f2 para convertir el valor almacenado en $f2 en un nmero en coma otante de simple precisin. 6. Se multiplica el valor contenido en $f2 por escala_y, que est almacenado en $f1. Se utiliza la instruccin mul.s y el resultado se deja en el registro $f2. 69

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

7. Se utiliza la instruccin cvt.w.s $f2, $f2 para convertir el resultado en un entero representado en complemento a 2. Esta conversin realiza redondeo por truncamiento. 8. Se copia el contenido de $f2 a $s3 utilizando la instruccin mfc1 $s3, $f2. Esta copia es literal, por lo que en $s3 queda almacenado el resultado nal codicado en complemento a 2. El resto de variables se calculan de forma anloga. Obsrvese que se utilizan los registros $f0, $f1 y $f2 para almacenar valores en coma otante y realizar clculos con ellos. Estos registros no son preservados entre llamadas segn el ABI de MIPS que utilizamos en las prcticas, por lo que no hay que almacenarlos en la pila. Una vez calculadas estas variables, se procede a mostrar el tablero por pantalla: 1. Se imprime la pared superior, para lo que se imprime un carcter +, se utiliza un bucle que imprime un carcter # en cada iteracin, y nalmente se imprime un carcter + seguido de un salto de lnea. 2. Se utilizan dos bucles anidados para imprimir las lneas intermedias del campo. En cada iteracin se imprime una la: a) Se imprime el carcter #. b) Se utiliza el bucle interior para imprimir un carcter para cada columna de esta la. En el cuerpo de este bucle, el registro $t2 contiene la la actual y el $t3 contiene la columna actual. Para cada posicin, se calcula qu hay que imprimir: 1) Si es la posicin de la pelota, se imprime el carcter O. 2) Si la columna actual coincide con la posicin horizontal de la pala izquierda (e_jug_izq_x) y la la actual es mayor o igual que e_jug_izq_y_sup pero menor que e_jug_izq_y_inf, se imprime un carcter ]. 3) Si la columna actual coincide con la posicin horizontal de la pala derecha (e_jug_der_x) y la la actual es mayor o igual que e_jug_der_y_sup pero menor que e_jug_der_y_inf, se imprime un carcter [. 4) En otro caso, se imprime un espacio. c) Se imprime el carcter # seguido de un salto de lnea. 3. Se imprime la pared inferior de la misma forma que la superior. dibuja_marcadores: Dibuja los marcadores, alineando uno a la izquierda y otro a la derecha. Este procedimiento no recibe ningn argumento ni devuelve ningn resultado, solo produce salida por pantalla. El procedimiento imprime el marcador del jugador izquierdo, imprime cierto nmero de espacios (nespacios), imprime el marcador del jugador derecho, e imprime un salto de lnea. El nmero de espacios a imprimir debe ser el adecuado para que el marcador del jugador derecho quede correctamente alineado. Para calcularlo, se le resta a ancho_dibujo el nmero de cifras necesario para imprimir ambos marcadores. Para calcular el nmero de cifras, se utilizan dos bucles que dividen repetidamente entre 10 los marcadores. inicia_bola: Prepara el estado del juego para comenzar a disputar una bola. Es decir, coloca la bola en el centro del campo movindose lentamente y los jugadores en sus posiciones iniciales. Argumentos: dir_bola: (Entero) Direccin inicial de la bola. Se utiliza el 0 para indicar que la bola se mueva hacia la izquierda y el 1 para que se mueva hacia la derecha. 70

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

El procedimiento se limita a calcular los valores iniciales necesarios para las variables globales relacionadas con la posicin de la pelota, su velocidad y la posicin de los jugadores. La posicin inicial de la pelota se ajusta de forma que el centro de la misma est alineado con el centro de las palas, de forma que los rebotes iniciales sean horizontales (para facilitar el juego). La componente horizontal de la velocidad de la pelota se establece a 150 o -150 unidades por milisegundo segn la direccin inicial sea hacia la derecha o hacia la izquierda, respectivamente. inicia_partida: Pone a cero los marcadores y empieza una partida nueva. Este procedimiento no recibe ningn argumento. El procedimiento inicializa las variables correspondientes, para lo cual delega gran parte del trabajo en el procedimiento inicia_bola. avanza_pong: Actualiza el estado del juego. Argumentos: transcurrido: (Entero) Indica cuntos milisegundos han pasado desde la ltima actualizacin. Este procedimiento es el que simula el juego propiamente dicho. Se encarga de mover la pelota teniendo en cuenta su velocidad actual y el tiempo que ha pasado desde la ltima actualizacin, detectar las colisiones de la pelota contra las paredes y las palas, y actualizar la velocidad y direccin de la pelota cuando rebota. La posicin de la pelota se actualiza mediante sencillas operaciones vectoriales para las cuales se utilizan los procedimientos prod_vector_int y suma_vector. Una vez calculada la nueva posicin de la pelota, se comprueba si se ha producido algn rebote. Para ello, se compara la posicin de la pelota con las coordenadas de las dos palas y las cuatro paredes. En caso de detectarse una colisin con una de las palas, la direccin y velocidad de la pelota cambian. La componente horizontal de la velocidad cambia de signo y su valor absoluto se ve incrementado en aceleracion_rebote_x unidades. Por su parte, la componente vertical cambiar de forma diferente dependiendo del punto de contacto de la pelota y la pala siguiendo la siguiente expresin:
alto_jugador ) 2

velocidad_pelota_y = aceleracion_rebote_y

posicion_pelota_y (p +
alto_jugador 2

En la expresin anterior, p = posicion_jugador_derecho_y en el caso de la pala derecha y p = posicion_jugador_izquierdo_y en el caso de la pala izquierda. Obsrvese adems que la velocidad de la pelota puede aumentar o disminuir dependiendo de si el punto de contacto con la pala se encuentra por encima o por debajo del centro de la pala y de la direccin de la pelota en el momento del contacto. Para calcular este resultado se utiliza aritmtica en coma otante y se realizan las conversiones de datos necesarias mediante las instrucciones mfc1, mtc1, cvt.w.s y cvt.s.w. En caso de colisin con una de las paredes izquierda o derecha, se incrementa la puntuacin del jugador correspondiente y se llama al procedimiento inicia_bola. Finalmente, en caso de colisin con las paredes superior o inferior, se invierte el signo de la componente vertical de la velocidad de la pelota. keyio_poll_key: Realiza sondeo (polling) del teclado. Este procedimiento no recibe argumentos. Resultado: 71

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

tecla: (Entero) Cdigo del carcter correspondiente a la ltima tecla pulsada, o 0 si no se ha pulsado ninguna tecla desde la ltima vez que se comprob. El sondeo se realiza como se explica en el tema 7 de la asignatura. En concreto, se puede consultar el boletn de prcticas B7.1 para ms detalles. poll_entrada: Comprueba si se ha pulsado alguna tecla y la procesa si es as. Este procedimiento no recibe argumentos. Resultado: seguir: (Entero) Vale 0 si se ha pulsado una tecla que indique que la partida debe nalizar, o 1 en cualquier otro caso. El procedimiento llama a keyio_poll_key para ver si se ha pulsado alguna tecla. Si se ha pulsado alguna, comprueba si es una de las teclas mostradas en la tabla B3.1 y en ese caso actualiza las variables globales de forma adecuada. main: Esta es la funcin principal del programa que ejecuta el bucle que se explica en la seccin B3.3.3.

B3.3.4.

Modicaciones propuestas al programa

Para completar la prctica, cada grupo deber elegir varias de las modicaciones propuestas en esta seccin e implementarlas en su programa. Cada modicacin o mejora tiene asociada una puntuacin mxima acorde con su dicultad, de forma que cada grupo conseguir una puntuacin diferente segn qu mejoras haya elegido realizar. Por supuesto, es posible obtener una puntuacin parcial si alguna mejora no se implementa correctamente o no se explica sucientemente bien (tanto en la memoria como a la hora de la entrevista de prcticas). Las mejoras realizadas deben explicarse claramente en la memoria de la prctica. La explicacin de cada mejora no ha de ser demasiado extensa, pero debe incluir, al menos: Explicacin general de la implementacin de la mejora. Se valorar la discusin de otras alternativas consideradas por el grupo para resolver el problema y la justicacin de la estrategia de implementacin elegida. Lista de cambios realizados al cdigo. Lista de pruebas realizadas para comprobar el funcionamiento de la mejora, con instrucciones para reproducirlas y capturas de la salida del programa cuando proceda. Cualquier aclaracin que se considere relevante para la evaluacin del trabajo realizado. Cualquier alumno puede proponer mejoras alternativas a las propuestas en este enunciado. Para ello, deben consultar con el profesor de la asignatura, quien decidir si la mejora se puede tener en cuenta y qu puntuacin mxima tendr asociada. Las mejoras propuestas por un grupo sern pblicas de forma que otros grupos podrn realizar la misma mejora, pero el grupo que proponga la mejora originalmente dispondr de un 20 % extra para la puntuacin mxima asociada a la mejora. A continuacin, se muestra la lista de mejoras propuestas: 1. Hacer que la pelota tenga una aceleracin uniforme. En el programa original, la pelota sigue un movimiento uniforme (con aceleracin 0) excepto cuando rebota contra las palas o las paredes. Para esta mejora, se debe modicar el programa de forma que la pelota siga un movimiento uniformemente acelerado. La aceleracin deber estar especicada mediante una variable global. Puntuacin mxima: 8 puntos. 72

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

2. Hacer que se puedan controlar las palas con teclas maysculas. En programa base solo responde a pulsaciones de teclas minsculas (por ejemplo: no funciona si el bloqueo de maysculas est activado). Para esta mejora se debe solucionar este problema. En caso de que se implemente la mejora 13 y sta a la vez, la puntuacin de esta mejora ser mayor siempre y cuando el usuario pueda congurar sus teclas usando letras maysculas o minsculas y el programa las reconozca siempre en ambos casos. Puntuacin mxima: 10 puntos (15 puntos si se implementa tambin la mejora 13). 3. Hacer que se acabe la partida al alcanzar una puntuacin determinada. Se deber decidir la puntuacin mxima que se debe alcanzar para dar una partida por terminada. El alumno puede decidir si se exige que haya diferencia de ms de un punto entre ambos jugadores. Una vez alcanzada la puntuacin objetivo, el programa debe mostrar un mensaje indicando qu jugador ha ganado la partida y ofrecer la posibilidad de empezar una nueva partida o salir del programa. Puntuacin mxima: 10 puntos. 4. Hacer que los marcadores sigan las reglas del tenis. El programa deber mantener un conteo de puntos, juegos y sets siguiendo las reglas habituales del tenis (que se pueden consultar, por ejemplo, en http://es.wikipedia.org/wiki/Tenis# Puntuaci.C3.B3n). Esta mejora requiere que se implemente tambin la mejora 3. Puntuacin mxima: 15 puntos. 5. Hacer que la pelota se acelere tambin al rebotar contra las paredes superior e inferior (no solo contra las palas como en el programa original). La velocidad slo debe cambiar en valor absoluto, la direccin del vector debe permanecer inalterada respecto a la que resulta actualmente cuando se produce un rebote. El incremento de velocidad de la pelota en cada rebote deber ser aleatorio y comprendido entre 1 y 100 unidades. Para conseguir esto, se utilizar la llamada al sistema de MARS que permite generar nmeros aleatorios. Puntuacin mxima: 15 puntos. 6. Aadir un men inicial. Modicar el programa para que muestre un men con varias opciones cuando empieza, en lugar de comenzar una partida directamente. El men deber incluir al menos las siguientes opciones: Comenzar partida. Mostrar instrucciones del juego. Salir del programa. A este men se le debern aadir ms opciones si se implementan otras mejoras. Puntuacin mxima: 10 puntos. 7. Aadir sonido. Utilizar las llamadas al sistema que permiten generar sonidos ofrecidas por MARS para aadir efectos sonoros al programa. Puntuacin mxima: 30 puntos.

73

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

8. Hacer que los jugadores se puedan mover horizontalmente. En el programa base, los jugadores se encuentran siempre a en la misma posicin horizontal (concretamente: a 25000 unidades de distancia de la banda que deenden). Esta mejora consiste en permitir que se puedan mover horizontalmente mediante pulsaciones de teclas. Puntuacin mxima: 15 puntos. 9. Permitir al usuario cambiar diversas opciones del programa. Esta mejora consiste en aadir opciones para cambiar el tamao del tablero, la frecuencia de actualizacin de la pantalla (FPS), la velocidad de la pelota, aceleracin, tamao de las palas, etc. Esta mejora requiere que se implemente primero la mejora 6. Puntuacin mxima: 15 puntos. 10. Hacer que uno de los jugadores (o los dos) se mueva solo. El programa base solo permite que dos jugadores humanos se enfrenten en un partida (o un solo jugador manejando una pala con cada mano). Esta mejora consiste en hacer que una de las palas pueda moverse automticamente. Para ello, es suciente con usar un algoritmo muy sencillo de forma que el programa decida cada cierto tiempo si mover su pala hacia arriba o abajo en funcin de la posicin relativa de la pala y la pelota. Habr que tener cuidado de evitar que la pala de la mquina se pueda mover mucho ms rpido que la pala del jugador humano. Tambin se valorar si se aade un cierto no determinismo para permitir que el jugador humano gane alguna partida. Esta mejora requiere que se implemente primero la mejora 6 para que se pueda elegir si la partida es entre dos jugadores humanos o entre un humano y la mquina. Puntuacin mxima: 60 puntos. 11. Permitir al usuario elegir la direccin del juego. En el programa base, as como el PONG original, los jugadores estn situados a la derecha y a la izquierda de la pantalla y el movimiento de la pelota es principalmente horizontal. Esta mejora consiste en permitir al usuario jugar colocando las palas en las bandas superior e inferior y que la pelota se mueva principalmente de arriba a abajo. El programa resultante deber funcionar correctamente tanto en modo horizontal como vertical. Esta mejora requiere que se implemente primero la mejora 6. Puntuacin mxima: 40 puntos. 12. Permitir al usuario elegir la direccin del juego especicando cualquier ngulo. Esta mejora propuesta es similar a la 11, pero el usuario podra especicar cualquier ngulo para orientar el campo. Puntuacin mxima: 60 puntos. 13. Permitir al usuario cambiar las teclas que se usan para controlar el juego. El usuario podr elegir las teclas para controlar el movimiento de las palas y cualquier otra funcin del juego. Esta mejora requiere que se implemente primero la mejora 6. Tambin afecta a la mejora 2. Puntuacin mxima: 15 puntos.

74

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

14. Aadir un histrico de mejores puntuaciones. Consiste en aadir un ranking de puntuaciones obtenidas por los jugadores, ordenndolas por la diferencia entre la puntuacin de los dos jugadores. Para ello, el programa le preguntar el nombre a los jugadores cuando acabe cada partida. Esta mejora requiere que se implementen primero la mejoras 6 y la mejora 3. Habr que aadir una opcin al men para permitir consultar el ranking. Puntuacin mxima: 15 puntos si los marcadores se almacenan solo en memoria, 30 puntos si se almacenan en disco. 15. Hacer que aparezcan varias pelotas. Esta mejora consiste en que se pueda jugar con varias pelotas a la vez. El alumno debe decidir cmo aparece ms de una pelota. Por ejemplo: podran ir apareciendo al alcanzar determinada puntuacin, o cuando algn jugador pulse determinada tecla. Puntuacin mxima: 30 puntos. 16. PONG a 4 palas. Permitir que participen 4 jugadores en una partida, uno en cada una de las bandas del campo. Las palas adicionales se manejaran con otras teclas distintas a las de la tabla B3.1. En esta modalidad de juego, la pelota no rebotara al alcanzar las paredes superior o inferior, sino que se incrementara la puntuacin de todos los jugadores menos el que defenda la banda correspondiente. Puntuacin mxima: 30 puntos. 17. Aadir efectos especiales. Esta mejora consiste en aadir animaciones (mediante caracteres) para conseguir efectos visuales. Por ejemplo: la pelota podra explotar cuando alcance una de las bandas. Puntuacin mxima: 30 puntos. 18. Mostrar la puntuacin con nmeros grandes. La puntuacin de los jugadores se mostrara mediante caracteres de mayor tamao dibujados mediante varios caracteres normales, como se muestra en la gura B3.11. Puntuacin mxima: 30 puntos. 19. Permitir controlar el saque a los jugadores Esta mejora consiste en hacer que sean los jugadores los que saquen pulsando el espacio. En lugar de aparecer la pelota en el centro movindose, sta aparecera en la pala de uno de los jugadores que podra moverse con ella hasta para decidir desde dnde sacar. Cuando el jugador pulsara el espacio, la pelota se pondra en movimiento. Cada vez sacara un jugador (o bien sacara el jugador que anot el ltimo punto). Puntuacin mxima: 20 puntos. 20. Poner obstculos en medio del campo. Para esta mejora, se debe aadir diversos obstculos en el campo contra los que la pelota debe rebotar. Los obstculos pueden ser jos o diferentes para cada punto. Puntuacin mxima: 15 puntos si los obstculos son jos, 25 si cambian para cada punto.

75

Tema 3: Lenguaje ensamblador


**** **** * * *** **** * * **** **** +########################################+ # # # # # # # # # # # # #] [# #] O [# #] [# # # # # # # # # # # # # # # +########################################+ 201 375000 375000 1276500 468750 150 0

Estructura y Tecnologa de Computadores

Figura B3.11: Dos ejemplos del aspecto de la salida del programa en diferentes momentos del juego. 21. Reducir el nmero de instrucciones necesarios para dibujar la pantalla y actualizar el estado del juego El programa base no ha sido optimizado y utiliza ms instrucciones de las estrictamente necesarias para actualizar el estado de la partida y dibujar la pantalla. Esta mejora consiste en optimizar el programa, respetando siempre todas las convenciones de programacin vistas en clase. El nmero de instrucciones antes y despus de realizar las optimizaciones se debe medir mediante la herramienta Instruction counter de MARS. Puntuacin mxima: 35 puntos (para una reduccin de al menos un 30 % de instrucciones ejecutadas por iteracin). 22. Eliminar (o reducir) el parpadeo usando un buffer. Para mostrar el estado del juego, el procedimiento dibuja_campo realiza muchas llamadas al sistema (una por carcter), de forma que se puede apreciar a simple vista cmo se dibuja el campo incluso si el programa se ejecuta en una mquina muy rpida. El parpadeo que se produce es molesto a la hora de jugar. Es posible eliminar el parpadeo casi completamente si se utiliza un buffer para generar la imagen completa del campo de juego, en lugar de imprimir carcter a carcter. De esta forma, una vez que se ha generado una imagen actualizada en memoria sin utilizar ninguna llamada al sistema, se limpia la pantalla y se imprime el contenido del buffer inmediatamente. Puntuacin mxima: 45 puntos. 23. Utilizar variables en coma otante para representar el estado del juego. Como se explica en la seccin B3.3.3, el programa base utiliza variables enteras para representar todo el estado del juego, por lo que es necesario realizar una transformacin de un espacio de coordenadas interno al espacio de coordenadas utilizado para dibujar la pantalla. Esta mejora consistira en cambiar la forma de trabajar del programa de forma que se utilicen variables en coma otante para representar el estado del juego. Puntuacin mxima: 25 puntos.

76

Tema 3: Lenguaje ensamblador

Estructura y Tecnologa de Computadores

B3.3.5.

Criterios de evaluacin

El 80 % de la nota nal de la prctica depende de la puntuacin total de todas las mejoras realizadas. Se alcanzara la puntuacin mxima con 100 puntos, aunque la puntuacin mxima total de las mejoras intentadas por el alumno puede ser mayor (de esta forma se pueden incrementar las posibilidades de obtener buena nota si no todas las mejoras intentadas funcionan perfectamente). El 20 % restante de la nota depender de la calidad general del programa, la documentacin entregada y el resultado de la prueba de evaluacin individual que se menciona a continuacin. Una vez evaluado el trabajo, se convocar a los alumnos a una prueba de evaluacin individual en la que tendrn que demostrar su conocimiento del programa entregado mediante la realizacin de pequeos ejercicios que implicarn modicar dicho programa. El resultado de esta prueba determinar si la prctica est aprobada. A la hora de evaluar el trabajo, se consideraran (entre otros) los siguientes aspectos: Funcionamiento correcto del programa con las mejoras realizadas. Claridad del cdigo entregado. Uso correcto de todos los convenios de programacin vistos en la asignatura. No ser posible aprobar la prctica si no se siguen correctamente los convenios de programacin explicados en clase. Completitud, claridad y concisin de la memoria entregada. Para obtener una nota de sobresaliente ser necesario realizar satisfactoriamente al menos una mejora valorada en 30 puntos o ms.

B3.3.6.

Requisitos de entrega

La prctica se entregar por medio de SUMA empaquetada en un archivo .tar.gz o .zip. El archivo debe contener al menos: Informacin suciente para identicar de forma sencilla e inequvoca a los miembros del grupo. Memoria explicativa de la prctica en formato PDF. Cdigo fuente de la prctica. Casos de prueba o ejemplos de ejecucin mostrando todas las caractersticas implementadas. Se debe entregar un solo archivo por cada grupo de prcticas. El nombre del archivo debe seguir el siguiente formato: pong-dniA-dniB.extensin Donde dniA y dniB son los DNI de los integrantes del grupo ordenados ascendentemente y extensin es tar.gz o zip.

77

También podría gustarte