En el mundo actual de la computación, donde la mayoría de los desarrolladores recurren a sistemas operativos complejos para ejecutar sus aplicaciones, la capacidad de crear software que funcione directamente sobre hardware sin intermediarios abre una puerta a la experimentación, optimización y comprensión profunda del funcionamiento de los dispositivos. Esto es especialmente relevante para dispositivos embebidos como Raspberry Pi, que ofrecen una plataforma accesible para realizar proyectos de bajo nivel. Un ejemplo destacado de esta tendencia es la creación de un juego gráfico que se pueda ejecutar en modo bare-metal, es decir, arrancando directamente en la máquina, sin necesidad de un sistema operativo convencional, utilizando C# y el compilador nativo AOT (Ahead-Of-Time) introducido en .NET 7. El avance de .
NET 7 en cuanto a compilación nativa ha supuesto una revolución para los desarrolladores de C#, quienes ahora pueden generar ejecutables que no requieren de una máquina virtual para su ejecución. Tradicionalmente, C# y .NET estaban vinculados a entornos administrados y pesados, dependientes de un runtime que gestionase la ejecución y servicios auxiliares como recolección de basura y manejo de excepciones. Con la llegada del compilador nativo AOT, el código C# puede compilarse hacia código máquina nativo, eliminando la necesidad de ese runtime y permitiendo operaciones de muy bajo nivel similares a las que se consiguen en C o C++. Este avance ha abierto la posibilidad de realizar proyectos experimentales donde se puede vigilar cada aspecto del programa, desde el propio arranque, la utilización de memoria, hasta la interacción directa con el hardware.
Uno de los ejemplos más fascinantes es la creación de un juego sencillo de laberintos inspirado en clásicos como Wolfenstein 3D, que puede arranquear directamente en un dispositivo como Raspberry Pi empleando únicamente C#. El proceso de arranque moderno de computadoras ha evolucionado enormemente desde los días del BIOS, desapareciendo la rigidez y limitaciones para convertirse en el estándar UEFI. UEFI funciona como un pequeño sistema operativo prearranque que proporciona interfaces extensas para acceder a almacenamiento, manejo de dispositivos y carga de ejecutables. Lo que esto significa es que ahora es posible generar aplicaciones compatibles con UEFI que pueden ser arrancadas directamente desde dispositivos de almacenamiento como USB en arquitecturas modernas tanto x64 como ARM64. Para facilitar la creación de este tipo de aplicaciones nativas, el proyecto bflat representa un avance significativo.
Bflat es un compilador avanzado construido sobre el runtime de .NET, permitiendo elegir entre distintas bibliotecas de tiempo de ejecución: la completa de .NET, una implementación mínima llamada zerolib, o incluso sin ninguna biblioteca estándar. Además, bflat está diseñado para soportar varias arquitecturas y sistemas operativos, incluyendo Linux, Windows, Android y crucialmente el entorno UEFI, que es la llave para generar ejecutables arrancables en modo bare-metal. Cuando se crea un programa gráfico que arranque directamente en hardware usando UEFI, es importante comprender que la interacción con el hardware no es la misma que bajo sistemas operativos tradicionales.
En UEFI, el programa recibe un puntero a una tabla del sistema EFI, que contiene direcciones a funciones para la interacción con el entorno. Esto incluye servicios de consola para texto, servicios para manejar memoria e incluso protocolos para gráficos. Para manejar la salida gráfica, es fundamental localizar el protocolo EFI Graphics Output Protocol, el cual permite configurar el modo gráfico, consultar las resoluciones disponibles y realizar operaciones de blit, es decir, copiar datos de un buffer a la pantalla. Gracias al soporte de punteros no gestionados y llamadas unmanaged en C#, es viable realizar p/invoke a esas interfaces EFI y manipular directamente el framebuffer. El reto está en que .
NET y C# no incluyen de forma nativa APIs para manipulación gráfica y entrada de dispositivos en entornos tan limitados como UEFI. Sin embargo, con la facilidad para interoperar con código no gestionado, es posible construir estas capas de abstracción en C#, como hace el proyecto zerolib. Por ejemplo, la clase Console en zerolib está implementada para utilizar las funciones UEFI de consola de manera que los usuarios puedan escribir líneas sin preocuparse por las complejidades internas. Para crear un juego de laberintos con gráficos, se configura un framebuffer de un tamaño suficiente para representar el escenario (por ejemplo, 640 x 480 píxeles) donde cada píxel puede tener componentes RGB y un byte de relleno. El programa entra luego en un bucle principal donde escucha la entrada del usuario mediante teclas de dirección, actualiza la posición y estado del personaje dentro del laberinto y redibuja la pantalla utilizando algoritmos de renderizado inspirados en técnicas clásicas como raycasting.
Una dificultad inherente es que muchas funcionalidades disponibles en el ecosistema .NET no están implementadas en zerolib, por ejemplo funciones matemáticas trigonométricas como sin y cos. Para superarlo se recurre a implementaciones propias, simples pero suficientes, que utilizan series de Taylor para calcular aproximaciones en coma flotante. Este desarrollo da como resultado un ejecutable UEFI que puede ser copiado en una unidad USB o en la memoria de un Raspberry Pi configurado para arrancar desde ella. Al encender el dispositivo o reiniciarlo, el firmware UEFI lo reconocerá como un archivo arrancable y ejecutará el juego directamente.
Esto elimina la necesidad de un sistema operativo como Linux o Windows y permite disfrutar de una experiencia de juego inmediata con un mínimo de recursos y código. Más allá de la fascinación técnica, este tipo de proyecto tiene implicaciones para el aprendizaje de sistemas embebidos, optimización del software y entendimiento profundo de la arquitectura del hardware. También abre el camino para crear aplicaciones extremadamente eficientes y especializadas para dispositivos con recursos limitados. Además, la posibilidad de utilizar C# en este contexto puede atraer a una audiencia más amplia de desarrolladores acostumbrados a este lenguaje, quienes de otro modo podrían verse intimidado por proyectos de bajo nivel en C o ensamblador. Esta democratización convierte a proyectos como bflat en herramientas valiosas tanto académicamente como en el ámbito industrial.
En términos de ejecución en Raspberry Pi, la arquitectura ARM64 es perfectamente compatible con esta metodología. Haciendo uso correcto de UEFI y generando el archivo correspondiente (bootaa64.efi para ARM64), es posible desplegar el juego y disfrutarlo. Esto añade otro nivel de versatilidad ya que permite no solo crear demos en PC sino directamente en hardware embebido real. Finalmente, para quienes deseen iniciar en este mundo, el proyecto está disponible en GitHub y cuenta con ejemplos prácticos que facilitan el aprendizaje.