Skip to content

L6: Practica 2

javierMaciasGMAIL edited this page Oct 22, 2024 · 136 revisions

Sesión Laboratorio 6: Práctica 2-3

  • Tiempo: 2h
  • Objetivos de la sesión:
    • Saber definir cadenas
    • Entender cómo se almacenan las cadenas en memoria
    • Servicios PrintString y ReadString
    • Instrucciones para trabajar con bytes: lb y sb
    • Instrucciones para trabajar con medias palabras: lh y sh
    • Acceso a los 2 displays de 7 segmentos y teclado de RARs
    • Directiva .include

Vídeos

Vídeo 1/5: Cadenas definidas en tiempo de compilación

Haz click en la imagen para ver el vídeo en Youtube

Click to see the youtube video

Vídeo 2/5: Cadenas definidas en tiempo de ejecución

Haz click en la imagen para ver el vídeo en Youtube

Click to see the youtube video

Vídeo 3/5: Manipulando cadenas. Instrucciones lb y sb

Haz click en la imagen para ver el vídeo en Youtube

Click to see the youtube video

Vídeo 4/5: Accediendo a los displays y al teclado hexadecimal

Haz click en la imagen para ver el vídeo en Youtube

Click to see the youtube video

Vídeo 5/5: Medias palabras. Alineamiento. Directiva Include

Haz click en la imagen para ver el vídeo en Youtube

Click to see the youtube video

Contenido

Introducción

Las cadenas de caracteres son importantísimas. Toda nuestra comunicación con los ordenadores se basa en cadenas de texto. Aprenderemos a definirlas y trabajar con ellas a bajo nivel, desde nuestro procesador RISC-V

Definiendo cadenas

Las cadenas son secuencias de caracteres almacenados en posiciones consecutivas de memoria. En las cadenas que utilizaremos cada carácter ocupa un byte, y será almacenado por su código ASCII. Por tanto, no usaremos la letra ñ ni vocales con acentos: á, é, í, ó, ú: sólo los caracteres de la tabla ASCII

Las cadenas las definimos mediante la directiva .string (que es un alias de la directiva .asciiz) y su último elemento debe ser siempre el byte 00. Esto nos permite detectar dónde acaba una cadena. Como el resto de datos, las cadenas se almacenan en el segmento de datos

Este es un ejemplo de la definición de dos cadenas: "En un laboratorio de la URJC" y "de cuyo numero no quiero acordarme"

#-- Ejemplo de definición de cadenas

	#-- Se definen en el segmento de datos
	.data
	
	#-- Usamos la directiva .string
	.string "En un lab de la URJC"
	.string "de cuyo numero no quiero acordarme"

Lo ensamblamos y nos fijamos en el segmento de datos. Vemos que hay información almacenada, pero no la entendemos. Pinchamos en el botón que pone ASCII para interpretarla como caracteres ASCII

La ventana que muestra el segmento de datos organiza la información por palabras (4 bytes), por eso vemos la cadena de esa forma tan extraña

Estas cadenas están definidas en tiempo de compilación. Es decir, que cuando se ejecute el programa, las cadenas ya están en memoria. Su tamaño se conoce a priori

Almacenamiento de las cadenas en memoria

Las cadenas son secuencias de bytes, que se almacenan consecutivamente. Los 8 primeros bytes de la cadena definida en el apartado anterior están almacenados en memoria así:

Cuando lo vemos como palabras es cuando el orden nos parece extraño, ya que se utiliza la organización little-endian. Pero esto no es un problema ya que... ¡las cadenas siempre las trataremos como bytes independientes!

Si volcamos el segmento de dato en formato ASCII TEXT

veremos la memoria volcada como palabras. Nos fijamos en que ambas cadenas acaban en el carácter \0:

Si lo exportamos a Binario y lo vemos con las aplicaciones adecuadas (por ejemplo GHEX o hd en el terminal), como los datos se tratan como bytes, podremos leer los cadenas tal cual están guardadas en memoria

Imprimiendo cadenas: Servicio PrintString

Podemos imprimir cualquier cadena invocando el servicio PrintString, cuyo código es el 4. Hay que colocar en a0 la dirección donde está el primer carácter de la cadena

Ampliamos el ejemplo anterior para imprimir la primera cadena:

#-- Ejemplo impresion de una cadena

	#-- Codigo de los servicios del Sistema Operativo
	.eqv EXIT         10
	.eqv PRINT_STRING 4

	.data
	
	#-- Definimos las cadenas en
	#-- tiempo de compilacion
cad1: 	.string "En un lab de la URJC"
cad2:	.string " de cuyo numero no quiero acordarme"
	
	
	.text
	
	#------ Imprimir la primera cadena
	
	#-- Situar en a0 la direccion de la cadena
	la a0, cad1
	
	#-- Imprimir la cadena
	li a7, PRINT_STRING
	ecall 
	
	#-- Terminar
	li a7, EXIT
	ecall

Lo ensamblamos y lo ejecutamos. En la consola vemos la cadena "En un lab de la URJC"

¡¡Ahora ya entendemos totalmente el ejemplo "Hola Mundo" que simulamos en la sesión 1!!. El círculo se ha cerrado, mi joven Padawan... (recuerda que .string es un alias de .asciiz)

Leyendo cadenas introducidas por el usuario

El sistema operativo nos ofrece un servicio para solicitar al usuario que introduzca una cadena. Este tipo de cadenas se dice que se define en tiempo de ejecución, ya que antes de ejecutar el programa desconocemos cuál es la cadena

Lo que sí que debemos definir es el tamaño máximo que reservamos en memoria para el almacenamiento de la cadena

Reservando bytes: directiva .space

La directiva .space sirva para especificar el número de bytes que queremos reservar. Los usaremos para almacenar la cadena pedida al usuario, pero en general nos sirve para cualquier uso (no sólo cadenas)

En este ejemplo reservamos 18 bytes, y a continuación definimos una cadena en tiempo de compilación

#-- Ejemplo de reserva de espacio para almacenar  
#-- una cadena definida en tiempo de ejecucion

	#--- Tamano maximo cadena
	.eqv MAX 18

	.data
	
	#-- Reserva de espacio para una cadena
cad1:	.space MAX
	
	#-- Cadena definida en tiempo de compilacion
cad2:   .string "Nombre?: "

Vamos a echar un vistazo a cómo queda organizado el segmento de datos. Ensamblamos el programa

En los primeros 18 bytes encontramos que no hay nada: Es el espacio reservado para almacenar la cadena que pediremos al usuario. A continuación comienza la cadena que hemos definido en tiempo de compilación. Las cadenas son bytes, por lo que pueden comenzar en cualquier dirección (no tienen que comenzar en direcciones alineadas)

En esta figura se ha representado la memoria byte a byte, para ver cómo están los datos en el segmento de datos: los primeros 18 bytes están a cero. A partir de la dirección 0x10010012 empieza la cadena "Nombre?: \0"

Servicio ReadString

Para solicitar una cadena al usuario invocamos el servicio ReadString del sistema operativo, cuyo código es el 8. Los parámetros a pasar son los siguientes:

  • a0: Dirección de memoria donde situar el primer carácter de la cadena
  • a1: Tamaño máximo de la cadena que el usuario puede introducir

Al invocarlo, se le solicita la entrada de datos al usuario. Si está activada la opción Settings/Pop up dialog for syscalls aparecerá una ventana emergente pidiendo explícitamente la cadena. Si no, aparecerá un cursos parpadeando en la consola. Al devolvernos el control, podremos ver en la memoria lo que ha introducido el usuario

En este programa de ejemplo se solicita al usuario que introduzca su nombre, almacenándose en la cadena cad1

#-- Ejemplo de reserva de espacio para almacenar  
#-- una cadena definida en tiempo de ejecucion

	#--- Tamano maximo cadena
	.eqv MAX 18
	
	#-- Codigo de los servicios del Sistema Operativo
	.eqv EXIT         10
	.eqv PRINT_STRING 4
	.eqv READ_STRING  8	


	.data
	
	#-- Reserva de espacio para una cadena
cad1:	.space MAX
	
	#-- Cadena definida en tiempo de compilacion
cad2:   .string "Nombre?: "
	
	.text
	
	#------ Imprimir mensaje para pedir el nnombre
	
	#-- Situar en a0 la direccion de la cadena
	la a0, cad2
	
	#-- Imprimir la cadena
	li a7, PRINT_STRING
	ecall 
	
	#--- Llamar al servicio ReadString para pedir el nombre
	#-- Direccion donde almacenar la cadena del usuario
	la a0, cad1
	
	#-- Longitud máxima de la cadena
	li a1, MAX
	
	li a7, READ_STRING
	ecall
	
	#-- Terminar
	li a7, EXIT
	ecall

Lo ensamblamos y lo probamos. Al ejecutarlo nos pide que introduzcamos el nombre. Fíjate que nos dice que la cantidad máxima de caracteres a introducir es 17. Nosotros hemos reservado 18, y se lo hemos pasado como argumento en a1. Hay que tener en cuenta que el carácter de fin de cadena: '\0' FORMA PARTE DE LA CADENA. Y hay que incluirlo. Por tanto, la cadena sólo puede tener como máximo 17 caracteres y el '\0' (Total: 18 bytes)

Introducimos el nombre del usuario y pinchamos en OK (o si lo hemos hecho desde la consola, pulsamos Enter). En el segmento de datos veremos que aparece el nombre introducido. Observamos también que se ha añadido el carácter '\n' (y por supuesto termina con un '\0')

En esta animación se muestra el funcionamiento:

Errores de alineamiento de palabras

Para trabajar con variables que son palabras, usamos las instrucciones lw/sw. Deben estar situadas en direcciones alineadas, o de lo contrario se produce un error de acceso a memoria

En este ejemplo se define primero una cadena, y luego se reservan 4 bytes para almacenar una palabra

#-- Error de alineamiento

	.eqv EXIT 10

	#-- En el segmento de datos se define
	#-- una cadena y una variable (palabra)
	.data
str:	.string "Hola"

	#-- Reservar 4 bytes para usarlos para almcenar una palabra
v1:     .space 4

	.text
	
	#-- t0 es el puntero a v1
	la t0, v1
	
	#-- Leer la variable v1 y meterla en el reg t1
	lw t1, 0(t0)
	
	#-- Terminar
	li a7, EXIT
	ecall

Lo ensamblamos. Al ejecutarlo observamos que se produce un error

El mensaje de error tiene esta pinta. ¿Qué ha pasado?

Error in /home/obijuan/develop/myTeachingURJC/2019-20-LAB-AO/wiki/L06/download/align-error-1.s line 19: 
Runtime exception at 0x00400008: Load address not aligned to word boundary 0x10010005

Si nos fijamos en la tabla de símbolos, vemos que la etiqueta v1 se corresponde con la dirección 0x10010005, que NO es múltiplo de 4, y por tanto NO está alineada

En la línea 19 estamos usando la instrucción lw para leer una palabra de una dirección NO alineada. Esto no es posible, y por eso sale el mensaje de error

lw t1, 0(t0)

Solucionando problemas de alineamiento

Estos problemas de alineamiento se solucionan de manera sencilla. Tenemos varias soluciones:

  • Solución 1: Usar .word para definir las palabras

Siempre que usemos la directiva .word para definir una palabra en memoria, el ensamblador se encargará de que se encuentre en una dirección alineada, y que NO se produzcan estos errores. El ejemplo anterior quedaría así:

#-- Error de alineamiento: Solucionado

	.eqv EXIT 10

	#-- En el segmento de datos se define
	#-- una cadena y una variable (palabra)
	.data
str:	.string "Hola"

	#-- Variable
v1:     .word 4

	.text
	
	#-- t0 es el puntero a v1
	la t0, v1
	
	#-- Leer la variable v1 y meterla en el reg t1
	lw t1, 0(t0)
	
	#-- Terminar
	li a7, EXIT
	ecall
  • Solución 2: Usar .align 2 antes de definir la palabra

La directiva .align le dice al ensamblador que la siguiente dirección esté alineada. Los distintos tipos de alineamiento se especifican mediante el parámetro de .align (consultar el manual). Así, con

   .align 2

se indica que se quiere un alineamiento de palabra. Esto lo colocamos justo antes de definir el espacio para la palabra. El ejemplo modificado quedaría así:

#-- Error de alineamiento: Solucionado

	.eqv EXIT 10

	#-- En el segmento de datos se define
	#-- una cadena y una variable (palabra)
	.data
str:	.string "Hola"

	#-- Variable
	.align 2
v1:     .space 4

	.text
	
	#-- t0 es el puntero a v1
	la t0, v1
	
	#-- Leer la variable v1 y meterla en el reg t1
	lw t1, 0(t0)
	
	#-- Terminar
	li a7, EXIT
	ecall

Trabajando con bytes

Para trabajar con cadenas, tenemos que leer bytes de memoria y escribir bytes en ella. Los acceso a memoria de tipo byte los hacemos con las instrucciones lb y sb (load-byte, store-byte). Son iguales que lw y sw, pero los accesos son sólo a bytes

Cuando trabajamos con bytes, las direcciones puedes ser cualesquiera, no tienen que estar alineadas

Lectura de bytes: Instrucción lb

En este ejemplo se define una cadena, se lee el primer carácter y se imprime usando el servicio PrintChar. Como se trata de un único carácter, lo leemos con lb

#-- Ejemplo de lectura de un byte
#-- Se define una cadena y se imprime en la 
#-- consola su primer carácter

	#-- Código de servicios del S.O
	.eqv EXIT       10
	.eqv PRINT_CHAR 11
	

	.data
	
cad1:	.string "Hola"

	.text
	
	#-- Puntero de acceso a la cadena
	la t0, cad1
	
	#-- Leemos el primer caracter
	lb t1, 0(t0)
	
	#-- Imprimir el caracter
	mv a0, t1
	li a7, PRINT_CHAR
	ecall
	
	#-- Terminar
	li a7, EXIT
	ecall 

Lo ensamblamos y lo ejecutamos. Vemos cómo en la consola se imprime una "H", que es nuestro primer carácter

Escritura de bytes: Instrucción sb

La escritura de un byte en memoria se hace con la instrucción sb (store-byte). En este ejemplo definimos una cadena, la imprimimos, modificamos su primer carácter y la volvemos a imprimir, para comprobar que, efectivamente, hemos escrito un byte

#-- Ejemplo de escritura de un byte
#-- Se modifica el primer caracter de l cadena

	#-- Código de servicios del S.O
	.eqv EXIT         10
	.eqv PRINT_STRING 4
	

	.data
	
cad1:	.string "Hola\n"

	.text
	
	#-- Puntero de acceso a la cadena
	la t0, cad1
	
	#-- Imprimir la cadena original
	mv a0, t0
	li a7, PRINT_STRING
	ecall
	
	#-- Modificar el primer caracter por una 'B'
	li t1, 'B'
	sb t1, 0(t0)
	
	#-- Imprimir la nueva cadena
	mv a0, t0
	li a7, PRINT_STRING
	ecall
	
	#-- Terminar
	li a7, EXIT
	ecall 

Lo ensamblamos y lo probamos. En la consola vemos que efectivamente, la primera "H" se ha cambiado por una "B", por lo que aparecen los mensajes "Hola" y "Bola". También lo podemos ver en el segmento de datos

Directiva .byte

Podemos almacenar bytes aislados en la memoria usando la directiva .byte. En este ejemplo declaramos 3 bytes, inicializados con los valores 01, 02 y 03:

#-- Ejemplo de almacenamiento de 3 bytes
#-- con la directiva .byte

	.data
	
	.byte 01
	.byte 02
	.byte 03

Lo ensamblamos y miramos el segmento de datos. Vemos que los 3 bytes están ahí almacenados, dentro de la primera palabra

Trabajando con medias palabras

En el RISC-V también podemos trabajar con medias palabras (16 bits, 2 bytes): usamos las instrucciones lh y sh (la h significa half-word)

Es necesario que las medias palabras estén almacenadas en direcciones múltiplos de 2: deben acabar siempre en 0,2,4,6,8,A,C,E. Si no es así se producirá un error de acceso al usar lh o sh

La directiva .half

Para definir variables de tipo media-palabra usamos la directiva .half. El ensamblador se encarga de que estén correctamente alineadas. En este ejemplo definimos 3 medias palabras:

#-- Ejemplo de definicion de
#-- medias palabras
	.data
	
	.half 0xCAFE
	.half 0xBACA
	.half 0xCACA

Lo ensamblamos y miramos el segmento de datos. Ahí están nuestras medias palabras

Lectura de medias palabras: Instrucción lh

Para leer una media palabra en un registro usamos la instrucción lh. En este ejemplo se leen 3 medias palabras y se almacenan en los registros t1, t2 y t3

#-- Ejemplo de lectura de medias
#-- palabras


	#-- Código de los servicios del S.O
	.eqv EXIT 10

	.data

datos:		
	.half 0xCAFE
	.half 0xBACA
	.half 0xCACA
	
	.text
	
	
	#-- Usamos le puntero t0 para acceder a las
	#-- medias palabras
	la t0, datos
	
	#-- En t1 se mete la primera media-palabra
	lh t1, 0(t0)
	
	#-- En t2 se mete la segunda media-palabra
	lh t2, 2(t0)
	
	#-- En t3 se mete la tercera media-palabra
	lh t3, 4(t0)
	
	#-- Terminar
	li a7, EXIT
	ecall

Lo ensamblamos y lo ejecutamos. Vemos que efectivamente, los 16-bits de menor peso de los registros t1,t2 y t3 contienen los valores de las medias palabras que se habían almacenando en memoria: 0xCAFE, 0xBACA y 0xCACA

Escritura de medias palabras: Instrucción sh

Para almacenar una media-palabra se usa la instrucción sh. En este ejemplo se carga un valor de 16-bits en el registro t1 y se guarda en la media-palabra definida en la memoria

#-- Ejemplo de escritura de medias
#-- palabras en memoria


	#-- Código de los servicios del S.O
	.eqv EXIT 10

	.data

	#-- Variable v1: es una media palabra
v1:	.half 0
	
	.text
	
	
	#-- t0: Puntero de acceso a la media palabra
	la t0, v1
	
	#-- Inicializamos t1 con el valor a guardar en memoria
	li t1, 0xDEBE
	
	#-- Almacenar la media palabra en memoria
	sh t1, 0(t0)
	
	#-- Terminar
	li a7, EXIT
	ecall

Lo ensamblamos y ejecutamos. Vemos que se ha transferido la media palabra a memoria

Accediendo a los Displays y al teclado

Los periféricos que tenemos disponibles en la herramienta Tools/Digital Labs Sim son dos displays de 7 segmentos y un teclado hexadecimal. Ya sabemos cómo acceder al display derecho. Lo hemos usado con la intrucción sw. En realidad, el acceso a estos periféricos se hace mediante 4 puertos de tipo byte. Cada byte tiene una función diferente

En esta figura se muestra el esquema general. Hay 3 puertos de salida, y uno de entrada. Todos son de tipo byte, accesibles en las direcciones indicadas

Acceso a los dos displays

Los displays de 7 segmentos se encuentran en las direcciones 0xFFFF0010 y 0xFFFF0011. Para escribir en cada uno de ellos hay que usar la instrucción sb

Este es un ejemplo en el que se muestra el número 73 en los displays. El dígito 7 se saca por el display izquierdo (por lo que se escribe en la dirección 0xFFFF0011) y el dígito 3 por el display derecho (Dirección 0xFFFF0010)

#-- Ejemplo de uso de los 2 displays
#-- de 7 segmentos: el izquierdo y el derecho
#-- Se saca el numero 73 por ellos

	#-- Puertos de perifericos
	.eqv BASE 0xFFFF0010  #-- Direccion base
	
	#-- Servicios del S.O
	.eqv EXIT 10
	
	#-- Offset de acceso a los displays
	.eqv DISP_R 0  #-- Display derecho: BASE + 0
	.eqv DISP_L 1  #-- Display izquierdo: BASE + 1
		
	#-- Codigo de los digitos
	.eqv DIG_7 0x07
	.eqv DIG_3 0x4F
	
	.text
	
	#-- Puntero de acceso a los displays
	#-- Se toma la dirección base. A partir de ella se accede 
	#-- a ambos displays
	li t0, BASE
	
	#-- Sacar el numero 3 por el display derecho
	li t1, DIG_3
	sb t1, DISP_R(t0)
	
	#-- Sacar el numero 7 por el display izquierdo
	li t1, DIG_7
	sb t1, DISP_L(t0)
	
	#-- Terminar
	li a7, EXIT
	ecall

Se accede a ambos displays usando la dirección base 0xFFFF0010, y definiendo los desplazamientos DISP_R y DISP_L. Ensamblamos le programa y lo ejecutamos. Este es el resultado:

Lectura del teclado

El teclado dispone de dos puertos de acceso (de 1 byte). Con el puerto de salida (0xFFFF0012) se selecciona la fila del teclado que se quiere comprobar. Leyendo del puerto de entrada (0xFFFF0014) se obtiene el código de la tecla pulsada, ó 0 si no hay ninguna apretada

Para seleccionar la fila a comprobar, se usan los códigos 1,2,4,y 8, correspondientes a las filas de la 1 a la 4. En este ejemplo se lee la primera fila del teclado y se carga en el registro t2 el código de la tecla pulsada

#-- Ejemplo de lectura de las teclas
#-- de la primera fila del teclado

	#-- Direccion BASE
	.eqv BASE 0xFFFF0010
	
	#-- Servicios del S.O
	.eqv EXIT 10
	
	#-- Codigo de los digitos
	.eqv DIG_7 0x07
	.eqv DIG_3 0x4F
	
	#------ Acceso al teclado
	
	#-- Seleccion de la fila
	.eqv KEY_ENA 02  #-- BASE + 2
	
	#-- Lectura del codigo de tecla
	.eqv KEY_RD  04  #-- BASE + 4
	
	.text
	
	#-- Puntero base de acceso a perifericos
	li t0, BASE

	#-- Bucle infinito que está constntemente leyendo la 
	#-- primera fila
bucle:		
	#-- Seleccionar la fila 1
	li t1, 01
	sb t1, KEY_ENA(t0)
	
	#-- Leer la tecla
	lb t2, KEY_RD(t0)
	
	b bucle

Para probarlo lo ensamblamos, y bajamos la velocidad (si está a su máxima velocidad el teclado NO funcionará). Le damos al play y nos fijamos en el registro t2. Cuando no hay ninguna tecla apretada, su valor es 0. Cuando hay una tecla pulsada (la tecla está en verde) veremos su código

Como sólo se está seleccionando la primera fila, si se aprieta cualquier tecla que NO esté en esta fila el código leído será 0

La directiva .include

Para facilitarnos la programación, el ensamblador del RARs incluye una directiva que permite incluir otro fichero en ensamblador dentro de nuestro fichero. Esto es muy útil, por ejemplo, para tener almacenadas las constantes en un fichero, y poder compartirlas con varios programas

Un ejemplo muy útil es el de tener los códigos de los servicios del sistema operativo en el fichero servicios.asm:

#-- Código de los servicios del sistema operativo
#-- Incluir estos archivos en tus programas
#-- para acceder a ellos fácilmente, y hacerlos más
#-- legibles

	.eqv PRINT_INT    1
	.eqv READ_INT     5
	.eqv PRINT_STRING 4
	.eqv READ_STRING  8
	.eqv PRINT_CHAR   11
	.eqv READ_CHAR    12
	.eqv EXIT         10

Ahora desde cualquier otro programa, que esté en el mismo directorio que el fichero servicios.asm, simplemente añadimos esta directiva, y el fichero se incluirá automáticamente:

.include "servicios.asm"

Este es un programa de ejemplo que accede a los servicios de PrintString y Exit, cuyos códigos están en el fichero servicios.asm:

#-- Ejemplo de la directiva include

	#-- Incluir fichero con los códigos
	#-- de los servicios del Sistema operativo
	.include "servicios.asm"
	
	.data
msg1:   .string "HOLA!!"
	
	.text
	
	#-- Imprimir la cadena
	la a0, msg1
	li a7, PRINT_STRING
	ecall
	
	#-- Terminar
	li a7, EXIT
	ecall

Recopilación de instrucciones hasta el momento

  • Instrucciones básicas: Son las que se transforman a código máquina y que ejecuta el procesador

  • Pseudo-instrucciones: No existen realmente como instrucciones. El ensamblador las transforma en instrucciones básicas. Una pseudo-instrucción puede dar lugar a 1 ó varias instrucciones básicas

  • Directivas: Dar información al programa ensamblador. No generan código máquina

Actividades NO guiadas

Presta atención a los detalles. Lee los manuales. Comprende el porqué de las cosas. Modifica aquí y allá para ver qué ocurre. No hay código mágico: debes entender lo que significa cada una de las instrucciones que usas. Sólo así lograrás ser un buen ingeniero. Un ingeniero al que todos llamen para resolver sus problemas

Y sobre todo. Practica. Practica. Practica

Ejercicio 1

  1. Escribe un programa que almacene, en tiempo de compilación, las siguientes tres cadenas en el segmento de datos: "Cadena 1", "Cadena 2" y "Cadena 3"

  2. ¿Cuál es el contenido de la segunda palabra del segmento de datos, en ASCII?

  3. ¿Cuántos bytes del segmento de datos se están usando?

  4. Completa la siguiente tabla, poniendo en la izquierda las direcciones y en la derecha los caracteres almacenados en cada dirección. Debe tener tantas filas como bytes almacenados en el segmento de datos

Direccion Carácter
.... .....
.... .....

Ejercicio 2

Escribe un programa que defina las dos cadenas siguientes: "Hola\n" y "Adios\n" en tiempo de compilación, y que las imprima por la consola, de manera independiente (primero una cadena y luego la otra)

Ejercicio 3

Escribe un programa que realice la suma de dos números enteros que se piden al
usuario. El programa imprimirá el mensaje correspondiente en cada momento, informando al usuario de lo que debe hacer: "Introduce primer numero", "Introduce el segundo numero", y "La suma es: "

En esta animación se muestra un ejemplo de funcionamiento del programa pedido

Ejercicio 4

  1. Escribe un programa que saque un mensaje pidiendo al usuario que entre un texto: "Introduce un texto: ", el usuario lo introduce y se almacena en una cadena en memoria. A continuación se escribe el mensaje "Esto es lo que has escrito: ". En la siguiente línea debe aparecer el texto introducido por el usuario

En esta animación se muestra un ejemplo:

Reserva en memoria 1024 bytes para el almacenamiento de esta cadena, definida en tiempo de ejecución

  1. Una vez que te funcione, pruébalo con la siguiente cadena: "Busca el caracter X" (introdúcelo sin las comillas). ¿En qué dirección está almacenado el carácter X?

Ejercicio 5

Analiza el siguiente programa y responde a las preguntas. El código lo ha hecho una persona que no ha puesto ningún comentario, por lo que no está claro cuál es el propósito del código

	.data
	
	.string "1234"
v1:	.byte 0xAA
	.byte 0xBB
	.byte 0xCC
	.byte 0xDD
	
	.text
	
	la t0, v1
	lw t1, 0(t0)
	
	li a7,10
	ecall
  1. ¿Cuál es el tamaño total en bytes de los datos almacenados en el segmento de datos?
  2. ¿Cuántas palabras del segmento de datos se están usando?
  3. ¿Cuáles son los bytes almacenados?
  4. Ejecuta el programa. ¿Qué ocurre?
  5. Soluciona el error, pero sólo modificando el segmento de datos
  6. A partir del código original, soluciona el error pero sólo modificando el segmento de código

Ejercicio 6

Escribe un programa en el que se defina la cadena "Hola\n" en tiempo de compilación. El programa la imprimirá en la consola. A continuación accederá a memoria y cambiará el carácter 'a' por una 'i', e imprimirá la nueva cadena. Al ejecutar el programa, en la consola deberá aparecer lo siguiente:

Hola
Holi

Ejercicio 7

Escribir un programa para hacer un cifrado simple de la cadena definida en tiempo de compilación "Hola\n". Primero se imprime el mensaje original: "Hola". A continuación se incrementan los 4 caracteres en una unidad 1 (sin hacer bucles, porque todavía no sabemos). Finalmente se imprime la cadena resultante, que ya está cifrada. En la consola deberá aparecer:

Hola
Ipmb

Para incrementar cada carácter hay que cargarlo en un registro, sumarle una unidad, y volverlo a guardar en su posición

Ejercicio 8

Escribir un programa que defina tres variables en la memoria, en este orden. Primero una palabra (v1), después una media palabra (v2) y finalmente un byte (v3). Deben estar inicializados con los valores 0xCADACAFE, 0xBACA y 0xEA respectivamente. El programa deberá incrementar en una unidad cada una de estas variables, y terminar

Ejercicio 9

En este ejemplo vamos a manejar los dos displays de 7 segmentos: el derecho y el izquierdo. Escribir un programa que defina dos contadores, uno inicializado a 0 y otro inicializado a 1. Los valores de estos contadores se escribirán en los puertos de salida del display derecho y el izquierdo, respectivamente (recordar que son bytes). Luego se incrementan los contadores y se repite en un bucle infinito.

Para probarlo, bien bajar la velocidad de ejecución o bien colocar un breakpoint y ejecutar con el play. En los dos displays veremos ciertos segmentos encendidos y ciertos apagados.

Ejercicio 10

Escribe un programa para leer el código de cualquiera de las teclas de la primera fila del teclado hexadecimal. Este código se debe escribir en el puerto de salida del display de 7 segmentos izquierdo. Meterlo en un bucle infinito para que esta operación se realiza todo el tiempo. No olvidar probarlo a baja velocidad, para que se refresque el teclado y el display

Notas para el profesor

  • Título informal de la clase "Comunicación con humanos..."
  • La comunicación básica entre el computador y los humanos es mediante "mensajes de texto"
  • Estos mensajes los codificamos como cadenas de caracteres (strings), que son una serie de bytes consecutivos que terminan por el numero 0
  • En esta sesión veremos todo lo necesario para trabajar con cadenas y comunicar la cpu con el humano (tanto en entrada como en salida)

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