-
Notifications
You must be signed in to change notification settings - Fork 22
L6: Practica 2
- 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
Haz click en la imagen para ver el vídeo en Youtube
Haz click en la imagen para ver el vídeo en Youtube
Haz click en la imagen para ver el vídeo en Youtube
Haz click en la imagen para ver el vídeo en Youtube
Haz click en la imagen para ver el vídeo en Youtube
- Introducción
- Definiendo cadenas
- Almacenamiento de las cadenas en memoria
- Imprimiendo cadenas: Servicio PrintString
- Leyendo cadenas introducidas por el usuario
- Errores de alineamiento de palabras
- Trabajando con bytes
- Trabajando con medias palabras
- Accediendo a los displays y al teclado
- Recopilación de instrucciones hasta el momento
- Actividades NO guiadas
- Autores
- Licencia
- Enlaces
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
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
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
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)
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
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"
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:
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)
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
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
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
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
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
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
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
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
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
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
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:
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
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
- 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
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
-
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"
-
¿Cuál es el contenido de la segunda palabra del segmento de datos, en ASCII?
-
¿Cuántos bytes del segmento de datos se están usando?
-
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 |
---|---|
.... | ..... |
.... | ..... |
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)
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
- 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
- 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?
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
- ¿Cuál es el tamaño total en bytes de los datos almacenados en el segmento de datos?
- ¿Cuántas palabras del segmento de datos se están usando?
- ¿Cuáles son los bytes almacenados?
- Ejecuta el programa. ¿Qué ocurre?
- Soluciona el error, pero sólo modificando el segmento de datos
- A partir del código original, soluciona el error pero sólo modificando el segmento de código
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
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
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
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.
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
- 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)
- Katia Leal Algara
- Juan González-Gómez (Obijuan)
L1: Práctica 1-1. RARs
L2: Práctica 1-2. Ensamblador
L3: Práctica 1-3. Variables
L4: Pract 2-1. E/S mapeada
L5: Práctica 2-2: Inst. ecall
L6: Prác 2-3: Cadenas
L7: Práct 3-1: Bucles y saltos
L8: Práct 3-2: Cadenas II
L9: Pract 4-1: Subrut. Nivel-1
L10: Pract 4-2: La pila
L11: Pract 4-3: Recursividad
L12: Pract 5-1. Heap. Listas
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
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