Professional Documents
Culture Documents
Clase Práctica
Programación en Assembler “Orga 1”
ASM es un 2GL (por las iniciales de Second Generation Language, o Lenguaje de Segunda
Generación). Los lenguajes de programación se clasifican de forma no muy estricta en 5
“generaciones”, donde la primera corresponde al lenguaje de máquina, la tercera a la
mayoría de los lenguajes convencionales y la quinta a lenguajes donde programar se parece
más a especificar la solución del problema que a dar un método para resolverlo. En este
sentido, en la medida que se pasa de la primera generación a la quinta, el cambio principal
es el nivel de abstracción – los lenguajes buscaron progresivamente facilitar la tarea del
programador.
La máquina “Orga 1”
Modo Ejemplo
Inmediato 0xEF06
Directo [0x28]
Indirecto [[0x67E]]
Registro R3
En todos los casos las direcciones de memoria se refieren a palabras. Para mayor
información, se puede consultar el apunte de la máquina que está al final de la práctica 3.
Este conjunto de instrucciones hace que la máquina “Orga 1” sea lo que se considera una
máquina RISC (Reduced Instruction Set Computer, Computadora de Conjunto de
Instrucciones Reducido), a diferencia de las computadoras de escritorio convencionales con
arquitectura Intel que son CISC (Complex Instruction Set Computer).
Ejercicio 1
Escribir un programa que calcule la división entera entre dos enteros sin signo de 16 bits.
En caso de que el divisor sea 0, devolverá 0.
• R1 contiene la dirección de memoria donde se aloja el par <dividendo, divisor>.
• El resultado debe colocarse en R3.
¡Seudocódigo!
Respecto del enunciado, la única particularidad que presenta está en la representación del
par <dividendo, divisor>. La estructura en memoria de un par es idéntica a la de un arreglo
de 2 posiciones. En este caso, en la dirección de memoria indicada en R1 estará el
dividendo, y en la posición siguiente, el divisor.
El código no tiene mayores complicaciones. Recordemos que para saltar fuera del ciclo
utilizamos el salto correspondiente a la negación de la guarda del while. En este caso,
saltamos a fin cuando el divisor (R4) es estrictamente mayor que el dividendo (R2)
haciendo, en este caso, una comparación de enteros sin signo.
Ejercicio 2
Estoy cansado de que me salgan las fotos oscuras con mi cámara digital. Hagan un
programa que duplique el brillo de mis fotos para remediar mi angustia.
• R1 contiene la dirección donde está la imagen como una matriz de enteros sin signo de 16 bits
• La foto tiene 200x200 pixels y la imagen debe ser modificada en el lugar.
¡Seudocódigo!
Cada punto que constituye una imagen se denomina píxel y utiliza un entero sin signo de
16 bits cuyo valor 0x0000 representa al color negro, su valor máximo 0xFFFF representa
al valor blanco, y los valores intermedios corresponden a los distintos tonos de gris. Este
valor (que en una imagen en tonos de gris se denomina luminancia) es una representación
del brillo de cada punto de la imagen. Así, si duplicamos este valor en todos los píxeles,
obtendremos una imagen más clara.
Una matriz se representa en memoria como un vector de vectores. Así, el orden que tienen
los píxeles en memoria es el mismo que si recorremos una matriz en de arriba hacia abajo
y de izquierda a derecha. Si olvidamos el hecho de que está constituida por filas y
columnas, una matriz de N*M es idéntica en memoria a un vector de N*M posiciones.
Por último, es de destacarse que si lo que queremos es aumentar el brillo de la imagen,
tenemos que tener cuidado con el resultado de nuestras operaciones. Si al duplicar el valor
de un píxel, el resultado no entra en 16 bits, debemos saturarlo, esto es, reemplazar el
resultado de la operación por el valor correspondiente al color blanco, ya que de lo
contrario obtendríamos un tono más oscuro del deseado.
i = 0
while i < 200*200:
tmp = 2 * img[i]
if tmp > 0xFFFF:
tmp = 0xFFFF
img[i] = tmp
i = i + 1
listo
Este código ensamblador tiene algunas particularidades. En primer lugar, para realizar la
multiplicación por dos de un valor utilizamos la operación de suma. Esto a su vez nos
permite utilizar el bit de carry para determinar si el resultado de la duplicación entra o no
en un entero de 16 bits. Si hubo acarreo sabemos que el resultado necesita de un bit
adicional para su representación, y corresponde saturarlo.
Por último puede observarse que el fragmento de código etiquetado como “saturar” obliga
a definir una etiqueta “seguir” para poder volver a la ejecución del ciclo cuando se termina
de saturar una posición. La operación CALL tiene la ventaja de eliminar esta etiqueta extra,
ya que utiliza el stack pointer de la máquina para recordar la dirección donde debe seguir
ejecutando después del procedimiento de saturación (esto es lo que hace la instrucción
RET).