Skip to content

Examen: 2021_01_21:GISAM

Juan Gonzalez-Gomez edited this page Jan 23, 2021 · 26 revisions

Examen convocatoria ordinaria: 2021-01-21. GISAM

  • Tiempo: 2h
  • Tipo de examen: Remoto. Entrega realizada a través de Aula Virtual
  • Grados: El examen fue el mismo para los grados de GISAM y TELECO
  • Objetivo:
    • Resolver el problema de examen de la convocatoria ordinadira de GISAM, con fecha 21/Enero/2021

Contenido

Enunciado

Solución comentada

Antes de empezar a implementar las funciones y programas pedidos hay que dar un vistazo general y hacerse un mapa de la situación. Pensar sobre ello. Determinar las relaciones que hay entre las funciones que nos piden. Quién hace qué. Quién llama a quién. Quien imprime en la consola y quién no. Esto nos aclarará muchas dudas. Y nos ayudará a NO cometer errores de violaciones de especificaciones (Si me piden explícitamente que en un programa principal se imprima un mensaje en la consola, y lo imprimimos desde una función, seá un error grave de violación de especificaciones). Recordad que no se puede programar lo que uno quiera, sino lo que le especifiquen. Si algo no está especificado, entonces tendremos que tomar una decisión, usando el sentido común del ingeniero. Nosotros somos los ingenieros, y nosotros tenemos que resolver los problemas

En total hay 6 elementos. Tres de ellos son programas principales, y tres son funciones, de las cuales hay 2 funciones hoja y 1 es una función intermedia

Así por ejemplo, desde el programa principal del ichero test_pesomax.s hay que llamar a la función pesomax() y por la especificación sabemos que esta función debe llamar a su vez a las funciones peso() y max()

Una vez que tenemos esto claro, ya podemos empezar a implementar todo lo que nos piden

Pregunta 1: Función max(a,b)

Hay que implementar una función que nos permita calcular el máximo de dos números. Tiene por tanto 2 argumentos de entrada: a y b, y uno de salida: el valor máximo

Para calcular el valor máximo entre dos números sólo hay que compararlos y devolver el que sea mayor. Por el análisis inicial, sabemos que la función max() es una función HOJA, que llama a ninguna otra función, por lo que no será necesario crear la pila para guardar la dirección de retorno

Como tiene 2 argumentos de entrada, los debemos pasar a través de los registros a0 y a1, OBLIGATORIAMENTE, tal y como establece la ABI del RISC-V (Convenio de uso de registros). El valor máximo hay que devolverlo, tambien OBLIGATORIAMENTE, por el registro a0, para cumplir con el convenio

Esta es una posible implementación:

#------------------------------
#- Funcion max(a,b): Calcular el valor máximo de dos números enteros  
#-
#- Entradas:
#-  a0: Número entero a
#-  a1: Número entero b
#-
#- Salidas:
#-  a0: Valor máximo (a,b)
#-------------------------------------


	.globl max

	.text

	#-- Se trata de una funcion Hoja, por lo que
	#-- no hay que crear pila para guardar lal direccion
	#-- de retorno
max:
	#-- Si a0 > a1, devolver a0
	bgt a0, a1, max_a0
	
	#-- El valor maximo esta en a1
	#-- Lo movemos a a0 para devolverlo
	mv a0, a1
	
max_a0:
	ret

Este programa parece más grande por los comentarios, pero en realidad sólo tiene 3 instrucciones

Pregunta 2: Programa test_max

En esta pregunta nos piden que hagamos un programa de pruebas para comprobar que la función max() está implementada correctamente. Aunque no nos lo pidiesen, TAMBIÉN HARÍAMOS UN PROGRAMA DE PRUEBAS. Cada función que implementemos hay que comprobar que funciona mínimamente. Habrá que llamarla alguna vez. Eso nos da seguridad de que vamos bien. Si NO realizamos ninguna prueba y empezamos a encadenar funciones que se llaman unas a otras y lo probamos todo al final, entonces costará mucho hacerlo funcionar y encontrar los fallos. En el examen el tiempo es muy límitado, y NO daría tiempo

En nuestro caso, el programa de pruebas no lo podemos hacer como queramos, sino que nos dan unas especificaciones que debemos seguir. Debemos llamar a la función max pasándole dos constantes, K1 y K2 (que podrían tener cualquier valor), y nos especifican cómo debe ser la salida por la consola. Esta salida la realiza el programa principal llamando al sistema operativo PERO NO LA FUNCION MAX. Incluir la llamada al sistema desde la función max es un fallo de especificaciones

También, por especificaciones del problema, me indican que los códigos de las llamadas al sistema se deben incluir en el fichero so.s

Esta es la implementación del programa principal:

#-----------------------------------
#-- Programa principal: Test_max
#-----------------------------------

	#-- Especificacion: Me piden que los codigos de los servicios
	#-- del sistema operativo se encuentre en el fichero so.s
	.include "so.s"
	
	#-- Constantes con los numeros a probar
	.eqv K1 20
	.eqv K2 30
	
	.data
msg1:	.string "\nCalcular valor max entre: "
msg2:   .string " y "
msg3:   .string "\nResultado: "
	
	.text
main:

	#--- Imprimir mensaje
	li a7, PRINT_STRING
	la a0, msg1
	ecall
	
	#-- Imprimir constante K1
	li a7, PRINT_INT
	li a0, K1
	ecall
	
	#--- Imprimir mensaje
	la a0, msg2
	li a7, PRINT_STRING
	ecall
	
	#-- Imprimir constante K2
	li a0, K2
	li a7, PRINT_INT
	ecall
	
	#-- Llamar a max
	li a0, K1
	li a1, K2
	jal max
	
	#-- Guardar el resultado en s0
	#-- (se podría usar tambien un registro temporal)
	mv s0, a0
	
	#--- Imprimir mensaje
	li a7, PRINT_STRING
	la a0, msg3
	ecall
	
	#-- Imprimir el resultado
	mv a0, s0
	li a7, PRINT_INT
	ecall

	#-- Terminar
	li a7, EXIT
	ecall

El programa es más largo, debido a que hay que llamar al sistema operativo para realizar la salida por la consola. La parte que llama a la función max, que es la que realmente hace la prueba, es esta:

#-- Llamar a max
  li a0, K1
  li a1, K2
  jal max

Ejecutamos el programa dando diferentes valores a las constantes K1 y K2 para comprobar que efectivamente tanto la función max, como el programa de prueba están funcionando correctamente. ¡Ya tenemos 2 puntos!

Pregunta 3: función peso(pcad)

La función peso() tiene un único parámetro de entrada: un puntero a una cadena, y debe devolver como parámetro de salida la suma de todos los caracteres. Los caracteres de una cadena en realidad son números, que están almacenados en memoria. La función peso() lo único que hace es recorrer la cadena sumando estos números, y devolviendo el resultado

Esta función NO HACE NINGUNA CONVERSIÓN. Lo que se almacena en la memoria son los códigos ASCII de los caracteres correspondientes a la cadena. Esto ya lo ha el ensamblador por nosotros, en el caso de cadenas definidas en tiempo de compilación (usando la directiva .string), o bien el sistema operativo en el caso de cadenas definidas en tiempo de ejecución (pedidas al usuario)

Nos piden explícitamente que usemos un algoritmo iterativo: es decir, NO recursivo. Nos piden la opción fácil. Sólo hay que recorrer la cadena en un bucle, sumando los caracteres. ¿Hasta cuando? Hasta que encontremos bien el caraćter '\n' o bien el 0. En ambos casos habrá que salir del bucle

#-------------------------
#-- Funcion Peso(cad): Calcular la suma de los codigos ascii de los caracteres de la cadena
#--
#-- Entradas:
#--  a0: Puntero a la cadena de texto
#--
#-- Salidas:
#--  a0: Devolver el valor calculado
#----------------------------------------
	
	.globl peso
	
	.text
	
	
	#-- Se trata de una funcion hoja, por lo 
	#-- que no es necesario crear la pila para 
	#-- guardar la direccion de retorno
	
peso:

	#-- Peso inicial: 0
	li t0, 0

bucle:	
	#-- Leer el siguiente caracter de la cadena
	lb t1, 0(a0)
	
	#-- Si es 0, terminar
	beq t1, zero, fin
	
	#-- Si es \n terminar
	li t2, '\n'
	beq t1, t2, fin
	
	#-- Sumar el valor actual del caracter al contador de peso
	add t0, t0, t1
	
	#-- Apuntar al siguiente caracter
	addi a0, a0, 1
	
	b bucle
	
fin:    #-- Devolver el peso
	mv a0, t0

	ret

Para implementar una función como esta, lo mejor es hacerlo directamente en un programa principal. Definimos una cadena constante en el segmento de datos y colocamos su dirección a 0. Ahora implementamos el bucle que calcule la suma de todos los caracteres. Una vez que ya lo tengamos funcionando, lo convertimos a una función, añadiendo la etiqueta del punto de entrada (max:), ret al final y eliminando la cadena definida y el código extra

Casi tendríamos otros 2 puntos, pero hasta que no la probemos como una función no estaremos seguros. Por eso hay que hacer un programa principal en un fichero separado que llame a la función (que es justo la pregunta 4)

Pregunta 4: Programa test_peso

Este programa es sencillo. Sólo hay que definir una cadena en tiempo de compilación, pasársela como parámetro en la llamada a la función peso(), y realizar las llamadas al sistema oportunas para imprimir los mensajes en la consola:

#-----------------------------------
#-- Programa principal: Test_peso
#-----------------------------------

	#-- Especificacion: Me piden que los codigos de los servicios
	#-- del sistema operativo se encuentre en el fichero so.s
	.include "so.s"
	
	.data
cad:	.string "ab"	

msg1:   .string "\nPeso de la cadena "
msg2:   .string ":  "

	.text
main:

	#-- Calcular el peso de cad
	la a0, cad
	jal peso

	#-- Guardar peso en s0
	mv s0, a0


	#--- Imprimir mensaje
	la a0, msg1
	li a7, PRINT_STRING
	ecall
	
	#-- Imprimir cadena original
	la a0, cad
	li a7, PRINT_STRING
	ecall
	
	#--- Imprimir mensaje
	la a0, msg2
	li a7, PRINT_STRING
	ecall
	
	#-- Imprimir peso
	mv a0, s0
	li a7, PRINT_INT
	ecall

	#-- Terminar
	li a7, EXIT
	ecall

Ejecutamos el programa y comprobamos el resultado. Probamos con otros valores de la cadena para asegurarnos que está todo ok. Una prueba que yo también haría sería meter una cadena que terminase en '\n', para asegurarme que en ese caso también funciona. Así, el peso de "ab" y "ab\n" debería salir igual

Con esto ya tenemos 2 puntos adicionales del programa pinciapal + 2 de la función peso. ¡En total ya tenemos un 6!

Pregunta 5: función pesomax(pcad1, pcad2)

Esta función recibe dos punteros a dos cadenas, y debe llamar a la función peso() para calcular el peso de cada una de ellas, llamar a la función max() para calcular el valor máximo y devolverlo

Es una función sencilla. Su única dificultad radica en el hecho de que se trata de una función intermedia, y por tanto tendremos que crear la pila para guardar la dirección de retorno. Además, como llama a otras funciones, deberemos guardar en la pila los resultados intermedios que no queremos perder (si se dejan en registros temporales se pierden al llama a otras funciones, y además el convenio nos lo prohibe)

#--------------------------
#--  pesoMax(cad1, cad2):Funcion para calcular el peso maximo entre dos cadenas
#--- 
#--- Entradas:
#--    a0: Puntero a la primera cadena
#--    a1: Puntero a la segunda cadena
#--
#--  Salidas:
#--    a0: El peso de la cadena de maximo peso
#---------------------------------------------------

	.globl pesomax

	.text
pesomax:	
	
	#-- Es una funcion que llama a otra funcion, por lo que 
	#-- necesitamos crear la pila
	addi sp, sp, -16
	sw ra, 12(sp)
	
	#-- Guardar a1 en la pila (cad2) para no 
	#-- perderlo al llamar a la funcion peso
	sw a1, 8(sp)
	
	#-- Calcular el peso de cad1
	jal peso
	#-- Guardar el peso de cad1 en la pila, para no perderlo
	sw a0, 4(sp)
	
	#-- Calcular el peso de cad2
	lw a0, 8(sp) #-- Recuperar cad2 de la pila
	jal peso
	
	#-- a0: peso de cad2
	#-- Recuperar el peso de cad1 de la pila
	lw a1, 4(sp)
	
	#-- Calcular el maximo entre los dos
	jal max
	
	#-- Recuperar la direccion de retorno y liberar la pila
	lw ra, 12(sp)
	addi sp, sp, 16
	
	ret

Para implementar esta función yo empezaría haciendo un programa principal en el que se definen dos cadenas en tiempo de compilación, se calculan sus pesos llamando a peso() y luego su valor máximo. Una vez que esto funcionase, lo convertiría en una función

Todavía no tenemos garantizados los 2 puntos, porque no lo hemos probado todavía como función. Es el cometido de la última pregunta

Pregunta 6: Programa test_pesomax

Por último, implementamos el programa principal siguiendo las especificaciones. Hay que pedir al usuario dos cadenas e imprimir en la consola el peso máximo. Esto lo repetimos en un bucle hasta que se introduzca la cadena nula al pedir la cadena 1 (La cadena nula es "\n"). En este programa NO haría falta realizar la comprobación de la cadena nula al pedir la cadena 2. Esto no se indica explícitamente y en el pantallazo proporcionado se termina al pedir la caden a1. Sin embargo, si es el caso de implementarlo así, estaría también bien

#-----------------------------
#-- Programa principal de pruebas: test_pesomax
#-----------------------------------------------

	.include "so.s"

	.eqv MAX 100

	.data
	
	#-- Cadenas introducidas por el usuario
cad1:	.space MAX
cad2:   .space MAX

msg1:   .string "\nIntroduzca cadena 1: "
msg2:   .string "Introduzca cadena 2: "
msg3:   .string "Peso maximo: "
	
	.text
	
bucle:
	#-- Pedir cadena 1 al usuario
	
	la a0, msg1
	li a7, PRINT_STRING
	ecall
	
	la a0, cad1
	li a1, MAX
	li a7, READ_STRING
	ecall
	
	#-- Si el primer caracter es '\n' terminar
	lb t0, 0(a0)
	li t1, '\n'
	beq t0, t1, fin
	
	#-- Pedir cadena 2 al usuario
	la a0, msg2
	li a7, PRINT_STRING
	ecall
	
	la a0, cad2
	li a1, MAX
	li a7, READ_STRING
	ecall
	
	#-- Calcular el peso máximo
	la a0, cad1
	la a1, cad2
	jal pesomax
	
	#-- Guardar el peso maximo en s0
	mv s0, a0
	
	#-- Imprimir mensaje de salida
	la a0, msg3
	li a7, PRINT_STRING
	ecall
	
	#-- Imprimir el peso maximo
	mv a0,s0
	li a7, PRINT_INT
	ecall
	
	#-- Repetir
	b bucle
	
fin:
	#-- Terminar
	li a7, EXIT
	ecall
	

Fichero so.s

Como especificaciones del enunciado, se pide explícitamente que las constantes de las llamadas al sistema se encuentre alojadas en el fichero so.o. Los servicios que hemos utilizado son el de Exit, impresion de una cadena, impresión de un número entero, y lectura de una cadena introducida por el usuario

	.eqv EXIT 10
	.eqv PRINT_STRING 4
	.eqv PRINT_INT 1
	.eqv READ_STRING 8

Autores

Licencia

Enlaces

Página principal


Sesiones de Prácticas

P1: Simulador RARs

L1: Práctica 1-1. RARs
L2: Práctica 1-2. Ensamblador
L3: Práctica 1-3. Variables

P2: E/S mapeada. Llamadas al sistema

L4: Pract 2-1. E/S mapeada
L5: Práctica 2-2: Inst. ecall
L6: Prác 2-3: Cadenas

P3: Bucles y Saltos condicionales

L7: Práct 3-1: Bucles y saltos
L8: Práct 3-2: Cadenas II

P4: Subrutinas

L9: Pract 4-1: Subrut. Nivel-1
L10: Pract 4-2: La pila
L11: Pract 4-3: Recursividad

P5: Memoria Dinámica

L12: Pract 5-1. Heap. Listas

VÍDEO DE DESPEDIDA

Ejercicios de examen

Simulacro examen 1
GISAM. Ordinario. 2019-Dic-11
GISAM. Extra. 2020-Jul-03
GISAM. Ordinario. 2021-Ene-21
GISAM. Ordinario. 2022-Ene-10
GISAM. Extra. 2022-Jun-29
GISAM. Parcial 1. 2022-Oct-26
GISAM. Parcial 2. 2022-Nov-30
GISAM. Parcial 3. 2022-Dic-21
GISAM. Parcial 1. 2023-Oct-09
GISAM. Parcial 2. 2023-Nov-11
GISAM. Parcial 3. 2023-Dic-20
GISAM. Extra. 2024-Jun-17
GISAM. Parcial 1. 2024-Oct-14
GISAM. Parcial 2. 2024-Nov-13
GISAM. Parcial 3. 2024-Dic-16
TELECO. Ordinario. 2019-Dic-13
TELECO. Extra. 2020-Jul-07
TELECO. Ordinario. 2021-Ene-21
TELECO. Extra. 2021-Jul-02
TELECO. Ordinario. 2022-Ene-10
TELECO. Extra. 2022-Jun-29
TELECO. Ordinario. 2023-Ene-10
TELECO. Extra. 2023-Jun-29
TELECO. Parcial 1. 2023-Oct-20
TELECO. Parcial 2. 2023-Nov-17
TELECO. Parcial 3. 2023-Dic-22
TELECO. Extra. 2024-Jun-17
TELECO. Parcial 1. 2024-Oct-10
TELECO. Parcial 2. 2024-Nov-21
TELECO. Parcial 3. 2024-Dic-19
Robótica. Ordinario. 2020-Jun-1
Robótica. Extra. 2020-Jul-13
Robótica. Ordinario. 2021-Mayo-20
Robótica. Extra. 2021-Junio-16
Robótica. Parcial 1. 2022-Feb-25
Robótica. Parcial 2. 2022-Abril-1
Robótica. Parcial 3. 2022-Mayo-6
Robótica. Parcial 1. 2023-Feb-27
Robótica. Parcial 2. 2023-Mar-27
Robótica. Parcial 3. 2023-May-08
Robótica. Parcial 1. 2024-Feb-26
Robótica. Parcial 2. 2024-Mar-20
Robótica. Parcial 3. 2024-May-06
Robótica. Extra. 2024-Junio-24
Datos. Parcial 1. 2023-Oct-09
Datos. Parcial 2. 2023-Nov-15
Datos. Parcial 3. 2023-Dic-20
Datos. Parcial 1. 2024-Oct-09
Datos. Parcial 2. 2024-Nov-13

SOLUCIONES

Práctica 1: Sesiones 1,2 y 3
Práctica 2: Sesiones 4, 5 y 6
Práctica 3: Sesiones 7 y 8
Práctica 4: Sesiones 9, 10 y 11
Práctica 5: Sesión 12

Clone this wiki locally