Binbows for Developers
Binbows is open source. You can read it, build it, break it, and open a pull request that makes it worse in an interesting way. That last one is the intended workflow.
The whole thing is one main.c of about 1,600 lines, a minimal
uefi.h with only the protocol definitions it actually needs, a generated
font header, and a Makefile. There is no SDK, no GNU-EFI, no EDK2, and no build
system beyond make.
The entry point
A UEFI application starts at efi_main with two arguments: a handle
representing itself, and the system table, which is the door to everything else.
The real thing does more than this, but this is the shape of it.
/* main.c — how Binbows starts */ EFI_STATUS EFIAPI efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) { BS = SystemTable->BootServices; ConIn = SystemTable->ConIn; fs_init(ImageHandle); /* Loaded Image → device → drive C: */ read_cpu_brand(); /* cpuid 0x80000002-4, for FETCH */ /* Locate the framebuffer. No GOP, no picture, nothing to say. */ BS->LocateProtocol(&gopGuid, NULL, (void **)&Gop); if (!Gop) for (;;) __asm__ volatile("hlt"); show_splash(4000); /* C:\LOGO.SYS, four seconds */ draw_bsod(); wait_key(); shell(); /* never returns */ }
Note the hlt loop. There is no operating system to return an error to
and no console to print one on, so a missing GOP is the end of the story.
The protocols it uses
Everything Binbows can do, it does through four UEFI protocols. That is the complete list — there is no fifth one, and there is nothing underneath them.
| Protocol | What it buys |
|---|---|
GRAPHICS_OUTPUT_PROTOCOL | A framebuffer address, a resolution, a scanline pitch and a pixel format. Every pixel on screen is written by hand into that buffer. |
SIMPLE_TEXT_INPUT_PROTOCOL | Keystrokes, polled with Stall() so the cursor can blink in between. This is the entire input system. |
LOADED_IMAGE_PROTOCOL | Which device the application was loaded from — the first half of finding drive C:. |
SIMPLE_FILE_SYSTEM_PROTOCOL | The second half: the volume's root directory, which becomes C:\. Open, read, close, and the file info structs behind DIR and VOL. |
BootServices->Stall() | Not a protocol, but it is the only clock in the building. The FORMAT C: hang at 68 percent is one Stall(900000). |
EFI_BUFFER_TOO_SMALL and expects you
to grow the buffer and ask again. And clang lowers copy loops to
memset/memcpy/memmove even under
-ffreestanding, while -nostdlib supplies none of them — so
the project provides its own.
The shell loop
No message queue, no event dispatch, no window manager. The application reads a line and runs it, forever, and it is the only thing running on the machine.
static void shell(void) { con_init(make_color(192, 192, 192), make_color(0, 0, 0)); con_line("Michaelsoft Binbows 98"); con_line(" (C) Copyright Michaelsoft Corp 1981-1998."); con_line("Recovery console. Type HELP for a list of commands."); char line[LINE_MAX]; for (;;) { con_put_cwd(); con_putc('>'); read_line(line); run_command(line); } } /* If you exit this loop, there is nothing to exit to. Which is why EXIT does not. */
| Piece | What it is |
|---|---|
con_buf[][] | The character grid, mirrored in RAM, so scrolling repaints from memory instead of reading back from the framebuffer — which is very slow. |
con_attr[][] | A parallel colour plane, one palette index per cell. One byte instead of four, and the palette resolves once against the framebuffer's channel order. |
read_line() | Line editing, backspace, history on the arrow keys, ESC to clear, and the blinking cursor, all hand-rolled. |
run_command() | Splits on the first space and walks an if chain of case-insensitive comparisons. No parser, no tokeniser, no shame. |
refusals[] | A table of about forty Linux command names and the line each one gets declined with. Adding to it is the easiest useful pull request in the project. |
bsod_layout[] | The blue screen as data: each line, how it sits, and the vertical advance to the next. That makes the block measurable, which is what lets it be centred rather than started at a fixed offset and hoped to fit. |
Building from source
The repository builds a bootable ISO. On a modern machine this takes about a second, which would have been science fiction to the people this project is imitating.
# dependencies (macOS) brew install llvm lld xorriso mtools # clone and build git clone https://github.com/lucya-astralis/binbows.git cd binbows make # boot it under QEMU with OVMF firmware make run
x86_64-unknown-windows, because a PE/COFF EFI application is, in linker
terms, a Windows binary. Plus xorriso and mtools for the
image, and qemu with an OVMF firmware image if you want
make run. No Python packages, no npm, no container.
Neither mtools nor xorriso has a Windows build, so the
repository carries tools/mkfat.py and tools/gptify.py —
byte-for-byte substitutes for those two steps, both producing images that have been
booted under OVMF.
| File | Description |
|---|---|
main.c | EFI entry point, BSOD renderer, text console, filesystem and shell. All of it. |
uefi.h | Minimal UEFI type definitions — system table, GOP, boot services, text input, simple file system. Only what is used. |
font.h | IBM VGA 8x16 CP437 font data, generated. See the licence note below. |
gen_font.py | Font converter. Reformats the kernel's font_8x16.c; needs no third-party packages, because the source data is already one bit per pixel. |
esp/ | Everything here is copied to the root of the boot volume, which is why DIR has something to show. |
assets/logo.bmp | The boot splash, 8-bit with an animation ramp, installed as C:\LOGO.SYS at build time. |
tools/mklogo.py | Quantiser and ramp builder for the splash. |
tools/mkfat.py | Builds the FAT12 ESP image without mtools. |
tools/gptify.py | Appends the ESP to the ISO as a GPT partition, without xorriso. |
ROADMAP.md | What is done, what is next, and why. Longer and more honest than this page. |
main.c,
uefi.h, gen_font.py, the Makefile — do whatever you want
with it. font.h is the exception: it is a verbatim copy of the IBM VGA
8x16 font from the Linux kernel's lib/fonts/font_8x16.c, which carries
SPDX-License-Identifier: GPL-2.0, and those terms cover it and anything
built from it. If you need the whole thing under looser terms, swap in a font whose
licence suits you and regenerate — gen_font.py is the only thing that
would need changing.
Contributing
Contributions are welcome. The bar is not "is this good," it is "does this make the project more itself."
- Refusals. The lowest-friction contribution in the repository: one line in
refusals[], one joke, done. Keep it dry. - Bugs: open an issue with the machine, the firmware and what you saw. A photo of the screen is a completely valid bug report here, because there is nowhere to copy text to.
- Features: discuss first. Some things are deliberately broken and fixing them is a regression. The roadmap says which.
- Docs: the most valuable and least submitted kind of pull request, as is tradition.
- Tests: there are none. There is a person who boots it under OVMF and looks at it. You can be that person.
API questions go to [email protected], patches to [email protected], and anything involving a lawyer to [email protected]. The first two are the same inbox. So is the third, spiritually.
Coding style
Match what is there. In case that is ambiguous, what is there is:
| Rule | Rationale |
|---|---|
| C17, four spaces | Not a discussion. The discussion happened and this is what came out of it. |
| Braces on the same line | K&R. The file is consistent about it and consistency beats your preference here. |
| Sections marked with rules | Long comment dividers separate the BSOD, the console, the filesystem and the shell. It is one file; it needs the signposts. |
| 80 column limit | Your terminal is 80 columns wide. So is the thing you are writing. It has always been 80 columns wide. |
| Comments explain why | The code already says what. The interesting comments in main.c are all about firmware behaviour you would not guess from the call. |
| No emoji in source | The website is allowed to have fun. The kernel is not. Especially this one, which is not a kernel. |
The protocol notes are incomplete, which puts them in good company.