In this blog I share my observations, thoughts and experience about computers, linguistics, philosophy and many other things that interest me.

Thursday, July 09, 2026

gk208-videobios, part 8/8 — The Datasheet: Handing a Live Controller to an Operating System

Last in a series on building a firmware-free VideoBIOS for RISC-V. In Part 7 the card drew text I controlled — a grid of cells turned into pixels by a compute kernel on the GPU's own engines, with no firmware anywhere. That made it a video controller. This post is about the part that makes a controller worth building: letting something else drive it.


The story so far. A cold GK208, brought up from nothing by RISC-V code, now shows text I choose, generated on the card's own engines. But it was still my program doing the drawing. A controller that only its author can drive is not a controller.

The thing that draws is not the thing that stays

A video facility at power-on cannot be a program that has to keep running. The moment I began this series for — text on the screen before an operating system exists — works because the firmware draws it and then hands the machine on. So my program's job is narrow, and it ends: cold-init the card, draw the first thing on the screen, and then it is done. What it leaves behind has to be enough for the next stage to take over, because my code will not be there to help.

A block of memory that says a controller is up

Nothing in the GK208's silicon records that a text controller exists. The cell array is memory I chose; the repaint is a kernel I wrote; the hardware knows about neither. So the only authoritative sign that a controller is up is one I publish myself: a block of memory, marked with a private GUID and a magic word, holding everything a consumer needs — where the cells live, how a cell is encoded, where the framebuffer is, how to repaint. I called it the Controller Handoff Block. A consumer does not probe the PCI bus or read a single card register to find it, since a cold card would tell it nothing useful anyway. It looks for the block. Presence is the whole signal.

Exit, and persist

Handing off across an exit has one hard part. The block cannot live inside my program, because the program unloads when it exits — the description would leave with the describer. So the .efi copies the block into memory the firmware keeps after an application is gone, and registers it there as a configuration table. Then it exits. The controller stays up on its own — scanout is autonomous, the picture holds — and the instructions for driving it survive in a pool that outlives the program that wrote them.

The doorbell

Scanout holding by itself is not the same as new text appearing. When a consumer writes new cells, something has to turn them into pixels — wake the compute kernel — and that wake is a small fixed sequence: flush the framebuffer, write one entry into the channel's ring, preempt, then bump the put-pointer. You write all the cells first and ring once, never per character. The last step is where the hardware taught me something I had assumed wrong, and I left the lesson in a comment:

/* Writing GP_PUT here pokes the PBDMA's doorbell — a PRAMIN/VRAM write does not. */

The doorbell is a specific address that means "go," and writing the same value anywhere else in memory does nothing. Ring it and the kernel repaints the grid; write the cells without ringing and the screen never changes.

The loop closes

On the 1st of July 2026 the whole thing ran end to end. A cold GK208: my .efi inits it, draws a logo and a cursor, publishes the handoff block, and exits. Then the next boot stage — mr-bml, the loader that will become part of QSOE — finds the block by its GUID, writes its own menu into the cells, rings the doorbell, and its menu appears on the monitor. My program was already gone. The screen was being driven by software that had never touched the card, only the block of memory I left behind. That is the line between a demo and a controller, and it had finally been crossed.

Writing the datasheet

What is left is the work of making it a thing other software wants to use, and lately that has meant small, deliberate additions to the handoff block. A consumer-writable palette, so the operating system sets its own colours instead of mine. And a transparent cell: one bit that tells the generator to leave a cell's pixels untouched, so a BIOS can cut a rectangle out of the text grid and draw a logo straight into the framebuffer beneath it, with the text intact around the hole. The bit had to come from somewhere, and the cell had one to spare — glyph codepoints never reach 0x2600, so the top bit of every cell was free to take:

[31] transparent   [30:16] symbol (UCS-2)   [15] blink   [14:8] bg   [7:0] fg

In the kernel it costs three instructions: mask the bit, test it, and return without writing if it is set. The GPU documents none of this — not the cells, not the doorbell, not the transparent bit. The handoff block is the datasheet for a controller that exists only because I wrote it, and writing that datasheet down plainly, so someone else can build against it, is the actual product.

Saying hello

Now the RISC-V Personal Computer I've built 5 years ago finally has proper means of announcing information on the screen when it's powered on. It's very satisfying, and it paves the way towards the next challenge: real BIOS, with real POST screen and real Set-Up program.

No comments: