El informe con una sola línea
El terminal de cobro de una cadena de tiendas se bloquea en mitad de una venta. El informe de fallo solo trae la última instrucción que ejecutó: sw a1, 0(a0).
En el terminal no hay código fuente, solo instrucciones. Quien sabe leerlas puede decir qué hacía el programa cuando se paró.
Al final de esta lección
Nota
Puedo leer un programa corto en RISC-V.
Palabras clave
| Palabra | Qué significa | Ejemplo |
|---|---|---|
| Registro | un almacén pequeño y rápido dentro del procesador | a0 |
| Inmediato | un número escrito dentro de la propia instrucción | 93 |
| Carga | una instrucción que copia un valor de la memoria a un registro | lw |
| Almacenamiento | una instrucción que copia un registro en la memoria | sw |
| Salto condicional | una instrucción que puede saltar a otro punto del programa | bne |
| Etiqueta | un nombre para un punto del programa | loop: |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Treinta y dos registros con su tarea
Un procesador RISC-V de 64 bits tiene 32 registros de 64 bits, llamados de x0 a x31. En ensamblador, casi todos tienen además un nombre según su tarea habitual.
x0 se llama zero: siempre vale 0, y lo que se escribe en él se pierde. Los registros a llevan las entradas de una función y su resultado. Los registros t guardan valores de trabajo que duran poco.
Ejemplo resuelto: los registros que verás
| Número | Nombre | Tarea habitual |
|---|---|---|
| x0 | zero | Siempre vale 0 |
| x2 | sp | El puntero de pila: dónde está la pila en memoria |
| x5 a x7 | t0 a t2 | Valores de trabajo |
| x10, x11 | a0, a1 | Entradas de una función, y su resultado en a0 |
| x17 | a7 | Qué llamada al sistema pide ecall a Linux |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Entra en la carpeta de arquitectura
cd ~/archSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Tres formas de instrucción
Casi todas las instrucciones nombran primero el registro de destino y después de dónde salen los datos.
add a0, a0, t0 suma dos registros. addi t0, t0, 2 suma un registro y un inmediato, un número escrito dentro de la instrucción. li a0, 5 parece de otro tipo, pero es una abreviatura. Para un número pequeño, el ensamblador escribe addi a0, zero, 5.
Cargas y almacenamientos tocan la memoria
Hay pocos registros, así que los datos viven en la memoria. RISC-V solo calcula con registros. Una carga copia de la memoria a un registro, y un almacenamiento copia en sentido contrario.
lw a0, 8(a1) carga una palabra de 4 bytes desde la dirección de a1 más 8. Un int de C ocupa 4 bytes. El loop.c del laboratorio rellena una tabla y la suma, así que gcc tiene que cargar y almacenar.
Compila loop.c a ensamblador RISC-V
riscv64-linux-gnu-gcc -O2 -march=rv64gc -S loop.cSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Busca las cargas y el almacenamiento
grep -E 'lw|sw' loop.sSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Enseña el bucle por filas
grep -A 4 'L6:' loop.sSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Ejemplo resuelto: C junto a RISC-V
| C en loop.c | RISC-V | Qué hace |
|---|---|---|
| grid[r][c] = 1; | sw a3,0(a5) | Escribe a3, que vale 1, en la dirección de a5 |
| sum = sum + grid[r][c]; | lw a4,0(a5) | Lee 4 bytes de la dirección de a5 y los deja en a4 |
| sum = sum + grid[r][c]; | add a3,a3,a4 | Suma el número al total, en registros |
| la siguiente c | addi a5,a5,4 | Avanza la dirección un int, 4 bytes |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Un error frecuente
Advertencia
Mucha gente cree que add puede sumar un número que está en la memoria. En RISC-V no puede. Solo las cargas y los almacenamientos tocan la memoria. Un valor se carga, se trabaja en registros y se vuelve a guardar. Por eso la suma necesita primero lw y después add.
Un salto condicional crea un bucle
Un salto condicional compara dos registros y salta si se cumple la prueba. beq salta cuando son iguales y bne cuando son distintos.
Una etiqueta como loop: nombra el punto al que se salta. El programa siguiente suma los pares de 2 a 12. t0 empieza en 2 y sube de dos en dos, a0 guarda el total, y bne t0, t1, loop vuelve atrás hasta que t0 llega a 14.
Escribe un bucle en RISC-V
printf '.globl _start\n_start:\n li a0, 0\n li t0, 2\n li t1, 14\nloop:\n add a0, a0, t0\n addi t0, t0, 2\n bne t0, t1, loop\n li a7, 93\n ecall\n' > pares.sSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Ensámblalo
riscv64-linux-gnu-as pares.s -o pares.oSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Enlázalo
riscv64-linux-gnu-ld pares.o -o paresSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Ejecútalo en el emulador
qemu-riscv64 ./paresSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Lee el total
echo $?Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Léelo de nuevo sin abreviaturas
riscv64-linux-gnu-objdump -d -M no-aliases paresSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Para ir más lejos: multiplicar sin mul
Escribe un programa RISC-V que calcule 8 por 5 usando solo li, add, addi y un salto condicional: suma 8 al total cinco veces.
Pruébalo primero en tu intérprete. Después escríbelo en un archivo con printf, como hiciste con pares.s, constrúyelo y ejecútalo en el laboratorio. El estado de salida debe ser 40.