El ASM de RISC-V, o el lenguaje ensamblador para esta arquitectura, es todavía desconocido para muchos programadores, ya que se han centrado en otras ISAs más extendidas como el ensamblador de x86 o el de ARM. Sin embargo, es interesante conocer cómo es este lenguaje ensamblador para estas máquinas y algunos aspectos importantes.
Índice de contenidos
Lenguaje ensamblador y lenguaje máquina
Qué es el lenguaje máquina
El lenguaje máquina o código máquina es un sistema de códigos que puede interpretar directamente un circuito electrónico, como un microprocesador, un microcontrolador, etc. Se compone de unos y ceros, aunque también se puede representar en hexadecimal. Éste código binario a su vez representa una serie de instrucciones de la ISA y de datos que se almacenarán en memoria y serán llevados a la CPU para su procesamiento.
Este lenguaje máquina está por debajo del nivel del ensamblador. A pesar de que el ensamblador es un lenguaje de programación de bajo nivel, el código máquina es lo más «íntimamente» que se puede representar un programa con respeto al hardware.
Qué es el lenguaje ASM
No confundas el lenguaje ensamblador con los binarios ejecutables, son tipos de archivo totalmente diferentes. Un fichero en lenguaje ensamblador no se puede ejecutar por una máquina, mientras el binario o código máquina sí se puede.
Si recuerdas lo que es una ISA, ésta determinaba, entre otras cosas, los tipos de datos que se pueden manejar, y tenía una serie de instrucciones como add, sub, mul, div, etc., que puede procesar la CPU bajo dicha arquitectura. Pues bien, el código ensamblador o ASM (del inglés assembled)es un lenguaje de programación de bajo nivel que emplea precisamente esas instrucciones y tipos de datos, así como los registros y modos de direccionamiento para escribir programas.
Es muy poco intuitivo y no se suele usar ampliamente debido a la dificultad. En su defecto se emplean lenguajes de programación de alto nivel que son más intuitivos para el programador. De hecho, dejo aquí un Hola mundo escrito en lenguaje de programación de alto nivel C para que puedas compararlo con el ASM de RISC-V:
/*Eejemplo de Hola mundo en C*/
//stdio.h es la biblioteca estándar de E/S para C que es necesaria para usar la función printf
#include <stdio.h>
int main() {
//La función printf() imprime en pantalla una cadena de texto
printf("Hola mundo\n");
return 0; //Finaliza el programa
}
Este snippet no solo es más corto, sino que es bastante más intuitivo que el lenguaje ensamblador para RISC-V. Por otro lado, este código fuente de C puede compilarse para múltiples sistemas operativos y arquitecturas (x86, RISC-V, ARM,…), en cambio, el código ensamblador de RISC-V es específico para dicha ISA y no funcionaría en otra.
Ejemplos de ASM de RISC-V
Para las diferentes arquitecturas, o ISAs, existen diferentes lenguajes ASM o ensambladores que emplean los tipos de datos que puede manejar dicha arquitectura, y las palabras reservadas o instrucciones. En el caso de la RISC-V, el lenguaje ensamblador puede tener la siguiente sintaxis de un «Hola mundo» para Linux:
Para comprender cada línea, las he comentado. Ten en cuenta que las líneas que comienzan por # son comentarios, por lo que a partir de ese # se ignorará por parte del compilador y no serán convertidas a código máquina o el binario ejecutable para la plataforma RISC-V.
# #Ejemplo de Hola mundo! # # a0-a2 - Registros usados para los parámetros para las funciones de Linux # a7 - Registro con el número de la función o, mejor dicho, la syscall del sistema # .global _start #Provee una dirección de inicio del programa al linker o enlazador #Configuración de los parámetros para el Hola mundo. #y llamada al sistema o syscall necesaria para imprimir en pantalla el mensaje en Linux. #1 = StdOut, es el número correspondiente a la salida estándar #Es decir, para que se muestre por la pantalla _start: addi a0, x0, 1 la a1, holamundo #Carga la dirección del Hola Mundo addi a2, x0, 11 #Esta instrucción de suma de registros es la necesaria para marcar la longitud de la cadena de texto del mensaje en este caso addi a7, x0, 64 #Carga en el registro el código de la syscall de Linux correspondiente a la escritura ecall #Se llama a la syscall write() para escribir #Configuración de los parámetros para salir del programa #Y la correspondiente syscall de Linux addi a0, x0, 0 #Se usa 0 para el código return addi a7, x0, 93 #Código 93 de terminación ecall #Llamada para terminar el programa .data helloworld: .ascii "Hola mundo\n"
Una llamada al sistema o syscall es un mecanismo de los programas del espacio de usuario para encargar algún tipo de trabajo al espacio kernel, es decir, para realizar tareas que necesitan de un nivel privilegiado sin tener dichos privilegios. Por ejemplo, en este caso que es un programa que imprime un mensaje en pantalla, se necesita acceso a recursos de hardware como es escribir en la salida estándar o pantalla dicho mensaje. De eso se debe encargar el kernel de Linux con la syscall write(). Una subrutina de código que ejecutará un código y luego devolverá el control nuevamente al programa del espacio de usuario.
Lo cierto es que muchas nuevas generaciones no saben qué es el lenguaje ensamblador por la llegada de los lenguajes de alto y medio nivel que lo han sustituido, pero hace décadas se utilizaba bastante más, especialmente para controladores o para el kernel de un sistema operativo.
¿Sabías ya qué era el ASM y cómo funcionaba? ¿O es la primera vez que has leído sobre este tema?