Skip to content

L4: Práctica 2

Juan Gonzalez-Gomez edited this page Feb 15, 2019 · 67 revisions

Sesión Laboratorio 4: Práctica 2-1

  • Tiempo: 2h
  • Fecha: Viernes, 15-Febrero-2019
  • Objetivos de la sesión:
    • Aprender a usar las pseudoinstrucciones li y mv
    • Entender el concepto de llamada al sistema
    • Aprender a utilizar servicios básicos: impresión en consola, lectura del teclado, etc.

Contenido

Guión para el profesor

  • Psedoinstrucciones li y mv
  • Mapa de memoria. Segmento de código. Sistema operativo
  • Directiva .text. Tabla de símbolos
  • Arranque del programa
  • Llamadas al sistema. Instrucción ecall
  • Manual: llamadas al sistema
  • Primera llamada: Exit
  • Leyendo un número desde el teclado: ReadINT
  • Imprimiendo un número en la consola: PrintINT
  • Ejercicio 1: Programa que pida un número al usuario y lo imprima en la consola
  • Ejercicio 2: programa para pedir dos números por teclado, realizar la suma y mostrar el resultado por la consola

Pseudoinstrucciones

Para facilitar la labor del programador, además de las instruccions básicas, se definen pseudoinstrucciones. Se trata de instrucciones que el programa ensamblador convierte a instrucciones reales del RISC-V. En la Chuleta del RISC-V, en la primera página, justo debajo del formato de las instrucciones, vemos las pseudoinstrucciones que están definidas

Hay dos operaciones con registros que utilizaremos mucho:

  • Carga de un valor inmediato en un registro. Ej. Meter el valor 5 en el registro a7 (a7 = 5)
  • Mover un valor de un registros a otro (a0 = a1)

Carga de un dato inmediato (li)

La operación de carga la realizamos con la instrucción básica addi, haciendo que el registro fuente sea el registro 0. Así, para meter el valor 5 en el registro a7 haríamos lo siguiente

addi a7, x0, 5  # a7 = 5

Esta operación calcula lo siguiente: a7 = x0 + 5. Pero como x0 siempre vale cero, es equivalente a la operación a7 = 5

  • Actividad 1: Comprobarlo en el simulador. Introducir la instrucción anterior y comprobar que el registro a7 toma el valor 5

Esto mismo lo hacemos con la pseudoinstrucción li (load inmediate). Así, el ejemplo anterior quedaría:

li a7,5  # a7 = 5

El programa ensamblador sabe que se trata de una pseudoinstrucción y la traduce a la instrucción real: addi a7, x0, 5

  • Actividad 2: Introducir la instrucción anterior y ensamblar el programa. En la pestaña de ejecución se muestra la pseudoinstrucción en la derecha, y la instrucción básica a la que se traduce

A partir de ahora usaremos siempre li para cargar valores inmediatos en registros

Mover valores entre registros (mv)

La operación de mover de un registro a otro la realizamos con la instrucción básica add, haciendo que uno de los registros fuente sea el x0. Así, para mover el contenido del registro a1 al registro a0 haríamos:

add a0, x0, a1

Esta misma operación la conseguimos con la pseudoinstrucción mv

mv a0, a1
  • Actividad 3: En ensamblar la instrucción anterior y comprobar cómo mv se transforma en add

A partir de ahora, para mover contenido entre los registros, usaremos la pseudoinstrucción mv

Mapa de memoria

Cuando se alimenta el procesador, comienza a ejecutar las instrucciones que están almacenadas a partir de la dirección 0. En esa dirección se encuentra el sistema operativo: un software que gestiona todos los recursos de nuestro computador. Una vez que está todo inicializado, nos pasa el control, y se ejecuta nuestro programa. En el entorno RARS, el segmento de código (.text), donde está nuestro programa, se encuentra en la dirección 0x00400000. El mapa de memoria es el siguiente:

Directiva .text

Para indicar al esamblador que estamos escribiendo código, y que por tanto se debe situar en el segmento de código, utilizamos la directiva .text

    .text
    li a7, 5

Localizando el comienzo de nuestro programa

Para saber la dirección de comienzo de nuestro programa, definimos una etiqueta al comienzo. Las etiquetas son textos que sustituyen a direcciones de memoria. Trabajar directamente con direcciones es tedioso. Por eso, usamos etiquetas, que luego el ensamblador reemplaza por las direcciones reales

	   .text
comienzo:  # Colocamos una etiqueta al comienzo del programa
           # El ensamblador la sustituye por la direccion
           li a7,5

En el RARS, desde el menú Settings/Show Labels Window abrimos una ventana nueva en la que se muestra la equivalencia entre las etiquetas y la dirección real. Así, para el ejemplo anterior tenemos:

Vemos que la dirección de comienzo de nuestro programa es, efectivamente, 0x00400000

Arranque de tu programa

El proceso desde que se enciende el procesador (o se hace un reset) hasta que se ejecuta tu programa se muestra gráficamente en esta imagen

En el punto 3 el sistema operativo pasa el control a tu programa, que se encuentra en el segmento de código a partir de la dirección 0x00400000. Se ejecutan las instrucciones hasta que se llega al final... En ese momento, tu programa debería devolver el control al sistema operativo. Veremos cómo se hace

Llamadas al sistema

La forma que tienen nuestros programas de acceder al sistema operativo es mediante las llamadas al sistema. Invocaremos al sistema operativo cada vez que queramos acceder a alguno de los servicios que nos ofrece: como por ejemplo imprimir mensajes en la consola, leer datos del teclado, realizar una pausa, obtener la hora... o devolverle el control (terminar)

En esta imagen se describe gráficamente el comportamiento típico de los programas. En el arranque el sistema operativo les pasa el control. En ciertos momentos el programa invoca algún servicio del sistema operativo. Este realiza el servidio pedidos y nos devuelve el control. Por último, cuando nuestro program termine debe entregar el control al sistema operativo

Llamando al sistema: ecall

Para realizar una llamada al sistema usamos la instrucción ecall. Al ejecutarla, el procesador salta a una zona del sistema operativo

  • Actividad 4: La instrucción ecall, ¿Es una instrucción o una pseudoinstrucción?. ¿De qué tipo es?

El sistema operativo ofrece servicios como: impresión de cadenas en la consola, lectura del teclado, esperar un tiempo... Cada servicio está identificado mediante un número. Dentro del RARs existe un manual muy completo en el que nos detallan todos estos servicios: cuál es el número de cada servicio y los argumentos de entrada y salida necesarios

Para invocar los servicios colocamos su número en el regsitro a7 y ejecutamos la instrucción ecall:

li a7, CODIGO # <--- Pone aquí el numero de servicio
ecall         # ¡Invocar al sistema operativo!

Invocando el servicio de finalización: EXIT

Empezaremos practicando con el servicio EXIT. Nuestros programas siempre deben devolver el control al sistema operativo, una vez que han terminado de realizar sus operaciones

  • Actividad 5: Buscar cuál es el número del servicio EXIT, en el manual del RARs (¡No hay que saberse nada de memoria! ¡Lo buscamos en el manual! ¡Que para eso está!)

Este es nuestro primer programa que usa una llamada al sistema para terminar. El programa no hace nada. Una vez que el sistema operativo nos pasa el control, se lo devolvemos mediante el servicio EXIT

# Ejemplo de llamada al sistema: EXIT
# Se devuelve el control al sistema operativo, y nuestro
# programa termina de una forma "controlada"

	.text
	
	# Nuestro programa hace sus operaciones
	# (las que sean, no nos importa)
	
	# Hemos terminado. Le devolvemos el control
	# al sistema operativo
	li a7, 10   # Seleccionar el servicio 10: EXIT
	ecall       # Invocar el servicio
	
	# El sistema operativo toma el control
	# Nuestro programa termina
	# El simulador nos informa de que se ha terminado

Leyendo un número desde el teclado: ReadINT

Para pedir un número al usuario, a través del teclado, usamos el servidio ReadInt

  • Actividad 6: Buscar en el manual el número de este servicio, y el registro en el que devuelve el valor leído

Este es un programa de ejemplo: pedimos un valor al usuario, lo almacenamos en el registro t0 y terminamos

# Programa de ejemplo del lectura de un numero
# desde el teclado
# Ejemplo de uso de la llamada al sistema ReadInt

.text
	
# Llamar al servicio ReadInt
li a7, 5
ecall
	
# Esta llamada deveulve el numero en el registro a0
# Lo movemos al registro t0
mv t0, a0
	
#--- Terminar
li a7, 10
ecall

Antes de ejecutarlo, activamos la opción Settings/Popup dialogs for input syscalls. Esto hace que aparezca una nueva nueva ventana donde nos pide que introduzcamos el número, cada vez que se invoca la llamada al sistema ReadInt

Al ejecutar el programa nos aparecerá una ventana pidiéndonos un número

En esta animación se muestra la ejecución completa

Imprimiendo un número en la consola: PrintINT

El servicio PrintINT nos permite imprimir un número en decimal en la consola

  • Actividad 7: Buscar en el manual el número de este servicio, y los parámetros de entrada necesarios

En este programa de ejemplo imprimimos por la consola el número 6, utilizando el servicio PrintINT

# Programa de ejemplo de impresión de un 
# numero en la consola. Se usa el servicio
# printINT

.text
	
# Hay que pasar el numero a imprimir por el registro a0
li a0, 6
	
# Llamar al servicio PrintInt
li a7, 1
ecall

#--- Terminar
li a7, 10
ecall

Al ejecutarlo vemos que efectivamente aparece el número 6 en la consola

Ejercicio 1

Escribe un programa que pida un número al usuario y lo imprima por la consola

Ejercicio 2

Escribe un programa que pida dos números al usuario, los sume y saque la salida por la consola

Actividades NO guiadas

Autores

Licencia

Enlaces

Página principal


Sesiones de Prácticas

Práctica 1: Simulador RARs

L1: Práctica 1-1. Rars
L2: Práctica 1-2. Ensamblador
L3: Práctica 1-3. Formato

Práctica 2: Llamadas al sistema

L4: Práctica 2-1
L5: Práctica 2-2. Datos

Práctica 3: Bucles. Saltos Condicionales

L6: Práctica 3-1
L7: Práctica 3-2

Práctica 4: LLamada a subrutina

L8: Práctica 4-1
L9: Práctica 4-2. Pila
L10: Práctica 4-3. Recursividad

Práctica 5: Memoria dinámica

L11: Práctica 5

Ejercicios

Ejercicios I
Solución examen Abril-2019
Solución examen Junio-2019

Exámenes

  • Ordinario (Lab): 26-Abril-2019. L3.202/3. 11-13h
  • Ordinario (Teoría): 6-Mayo-2019. L3.210. 9h - 12h
  • Final (Teoría y Lab): 18-Junio-2019. L3.210. 9h-11h Teoría. 11-13h Práctica

Material de apoyo

Simulador RARS

Clone this wiki locally