Rainbow-OS has spent its whole life talking to the screen through the PC’s
oldest trick: VGA text mode, an 80×25 grid of character cells at physical
address 0xB8000. It is simple and reliable, but it is also a hard
ceiling — 80 columns, 25 rows, 16 colors, and a fixed bitmap font baked
into the hardware. This update replaces that text console with a real
graphical framebuffer console running at 800×600 in 256 colors,
while keeping every existing program (the shell, the vim-like editor, the BASIC
interpreter, the C compiler) working without a single change to their code.

Why it was possible without rewriting everything
Almost all kernel output already went through one small API —
vga_write, vga_putchar, vga_set_color,
vga_putchar_at, and the dynamic size queries
vga_get_rows() / vga_get_cols(). The editor was already
written to ask the driver for its dimensions rather than assuming 80×25.
That meant the entire change could happen behind the API: re-implement
the vga_* functions to draw glyphs onto a graphics framebuffer, and
every caller transparently inherits the higher resolution. A grep confirmed that
nothing outside the driver itself hard-codes the old dimensions.
Setting up the mode on a Cirrus GD5446
The target is QEMU’s emulated Cirrus Logic GD5446. After boot the CPU is in
protected mode, so the BIOS is gone and the only way to set a video mode is to
program the VGA/Cirrus registers directly. The existing 640×480 demo mode
was extended to 800×600×8bpp with a new CRTC timing table, the Cirrus
8bpp extension (SR7 = 0x01), and the correct scanline pitch:
/* CR01 = horizontal display end = 800/8 - 1 = 0x63
CR13 = offset (pitch) = 800/8 = 0x64 in dword mode
Vertical 600 split across CR12 + overflow bits in CR07 */
At 800×600 with the 8×16 ROM font, that gives a comfortable
100×37 character grid — far more room than the old
80×25.
The font bug nobody had hit yet
The console renders text by blitting glyphs from the VGA ROM font, which the
driver copies out of plane 2 before switching modes. The first run produced
garbled output: digits looked fine, but letters came out as random symbols. The
cause is a classic VGA subtlety — the character generator stores each glyph
on a 32-byte stride (only the first 16 bytes are used for an
8×16 font), not packed at 16 bytes each. The fix is to compact on copy:
for (int ch = 0; ch < 256; ch++)
memcpy(saved_font + ch * 16, font_mem + ch * 32, 16);
Making it fast and flicker-free
Drawing a character now means writing 8×16 pixels instead of one
16-bit cell, and the framebuffer is banked in 64 KB windows, so naive
per-pixel access would be slow. Two tricks keep it responsive:
- A RAM shadow grid mirrors the screen as
char | (attr<<8)cells. Each blit is dirty-checked — if a
cell already holds that character and color, it is skipped. This makes the
editor’s full-screen redraw cheap, because only the handful of cells that
actually changed get repainted. - Hardware-style scrolling shifts the whole framebuffer up by
one text row with a single banked copy, then clears the last row — instead
of re-blitting all 3,700 glyphs.
The old hardware text cursor doesn’t exist in graphics mode, so the console
draws a software underline cursor and erases it by re-rendering
the cell underneath. Colors are handled by programming palette indices 0–15
with the standard VGA colors, so vga_set_color keeps working exactly
as before — only now there are 240 more palette slots available for graphics.
The result
Boot messages, the shell, and the editor all render crisply at 800×600.
The editor in particular now has a 100×36 text area with a full-width status
bar. The gfx demo was updated to fill the larger screen with a
256-color rainbow, and returns cleanly to the console afterwards. The whole
switch touched only two files plus the demo — the payoff of having kept a
clean driver API from the start.
