Metadata-Version: 2.4
Name: aetherix
Version: 0.1.0
Summary: A Python-native operating system development framework for building bootable x86 and x86_64 systems. Aetherix supports both legacy BIOS and modern UEFI firmware, generates machine code directly through its own native encoder, and provides high-level APIs alongside low-level instruction control.
Author-email: Divyanshu Sinha <divyanshu.sinha631@gmail.com>
License: AGPL-3.0-or-later
Project-URL: Homepage, https://github.com/DivyanshuSinha136/Aetherix/
Project-URL: Documentation, https://github.com/DivyanshuSinha136/Aetherix/blob/main/API-Reference.md
Project-URL: Repository, https://github.com/DivyanshuSinha136/Aetherix/
Project-URL: Issues, https://github.com/DivyanshuSinha136/Aetherix/issues
Keywords: osdev,operating-system,bootloader,kernel,bios,uefi,x86,x86-64,machine-code,bare-metal,firmware,systems-programming
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Topic :: System :: Boot
Classifier: Topic :: System :: Operating System Kernels
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: C
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE.txt
Provides-Extra: imaging
Requires-Dist: Pillow>=9.0; extra == "imaging"
Dynamic: license-file

![Aetherix](https://raw.githubusercontent.com/DivyanshuSinha136/Aetherix/main/logo.png)

# Aetherix

**A Python-native operating system development framework for building real, bootable x86 and x86_64 systems.**

Aetherix generates machine code directly through its own native x86 encoder — no `nasm`, no `ld`, no external assembler toolchain. It supports two genuinely separate boot paths, legacy **BIOS** (16‑bit real mode → 32‑bit protected mode, MBR) and modern **UEFI** (PE32+, 64‑bit long mode, GPT/FAT32) — and gives you both a batteries-included high-level API and raw, byte-level instruction control when you need it.

[![License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](#license)
[![Python 3](https://img.shields.io/badge/python-3-blue.svg)](#install)
[![Platform: x86 / x86_64](https://img.shields.io/badge/platform-x86%20%2F%20x86__64-lightgrey.svg)](#architecture)

```python
from aetherix import Project

with Project("MyOS") as os:

    @os.kernel_entry
    def main(prog, drivers):
        drivers.vga.clear(prog)
        drivers.vga.print_string(prog, "Hello, world!")
        prog.hlt()

    os.build("myos.img")
```

```bash
qemu-system-i386 -drive file=myos.img,format=raw
```

Or write it to a USB stick: `sudo dd if=myos.img of=/dev/sdX bs=4M status=progress conv=fsync`
(VirtualBox and VMware also accept the raw `.img`/`.bin` directly as a virtual disk.)

---

## Table of contents

- [Why Aetherix](#why-aetherix)
- [Install](#install)
- [Quick start](#quick-start)
- [Examples](#examples)
  - [Full example source](#full-example-source)
- [What you actually get](#what-you-actually-get-read-before-you-assume-too-much)
- [What's not implemented (and why)](#whats-not-implemented-and-why)
- [UEFI support](#uefi-support)
- [Architecture](#architecture)
- [CLI](#cli)
- [Verifying the output yourself](#verifying-the-output-yourself)
- [Documentation](#documentation)
- [Contributing](#contributing)
- [License](#license)

---

## Why Aetherix

Most "write your own OS" tutorials hand you fragile shell scripts wired around `nasm` and a linker. Aetherix takes a different approach:

- **No external assembler.** Machine code is emitted directly by a small C encoder core (`native/encoder.c`), driven from Python. Building an image needs nothing beyond a C compiler at install time.
- **Two real, independently-verified boot paths.** A classic BIOS/MBR path and a genuinely separate UEFI/GPT/FAT32 path — not one bolted onto the other.
- **Verification over vibes.** Every primitive this project ships with has been checked byte-for-byte against a reference (Intel SDM / `objdump`), and the higher-level behavior (disk loading, keyboard input, graphics compositing, UEFI protocol calls) is confirmed by *actually executing* built images under CPU emulation (Unicorn, QEMU) — not just by disassembling them. See [Verifying the output yourself](#verifying-the-output-yourself).
- **Honest about scope.** Aetherix tells you what's implemented and what's a documented extension point (`HAL().scaffolded()`) — it doesn't fake hardware support it doesn't have.
- **Two altitudes of API.** High-level (`drivers.vga.print_string(...)`) for getting an OS running fast, and low-level (`prog.mov16(AX, 0x1234)`) for full instruction-level control when you need it.

---

## Install

```bash
pip install aetherix
```

Requires a C compiler (gcc/clang/MSVC) on the machine building the package — the native encoder core is compiled once, either at install time or on first import for dev checkouts. **Nothing beyond that is required to *build* an image** — Aetherix emits its own machine code. You do need an emulator (QEMU / VirtualBox / VMware) or real hardware to *run* what you build.

```bash
python -c "import aetherix; print(aetherix.__version__)"
```

For image/graphics support (`aetherix.imaging`, used to prepare loading screens, backgrounds, and logos for `aetherix.drivers.graphics`), also install Pillow — a build-time-only dependency, not needed by the OS you build:

```bash
pip install "aetherix[imaging]"
```

## Quick start

```python
from aetherix import Project

with Project("MyOS") as os:

    @os.kernel_entry
    def main(prog, drivers):
        drivers.vga.clear(prog)
        drivers.vga.print_string(prog, "Hello, world!")
        prog.hlt()

    os.build("myos.img")
```

```bash
qemu-system-i386 -drive file=myos.img,format=raw
```

That's the entire pipeline — bootloader generation, kernel assembly, protected-mode switch, and disk image writing all happen inside `os.build(...)`. For the complete API (drivers, the `HAL`, embedding files, disk images, raw `Program` control, and UEFI), see **[API.md](API.md)**.

## Examples

| Example | What it demonstrates |
|---|---|
| `hello_os.py` | Minimal: prints fixed messages, waits for one keypress, halts. |
| `aether_shell.py` | A genuinely interactive OS: a live keyboard-echoing shell with a runtime VGA cursor (moves, wraps at end-of-row, handles Enter/Backspace/Escape/Shift), a speaker beep on F1, and real power control (F2 reboot, F3 QEMU/Bochs shutdown). Verified end-to-end by scripting keystrokes through CPU emulation (`tests/test_emulation.py`), not just by disassembling the output. |
| `graphics_os.py` | VGA graphics (Mode 13h, 320×200, 256-color): a boot loading animation, a background image, and a composited logo. Uses `aetherix.imaging` to prepare ordinary PNGs at build time; ships with self-contained placeholder art so it runs with no external files. Verified pixel-for-pixel and palette-for-palette correct under CPU emulation. |
| `uefi_hello.py` | A **completely separate boot path**: a genuine UEFI application (PE32+, 64-bit long mode) on a GPT+FAT32 disk image, booted by real, unmodified OVMF firmware in real QEMU. See [UEFI support](#uefi-support). |

```bash
python aether_shell.py
qemu-system-i386 -drive file=build/aether_shell.img,format=raw
```

```bash
python graphics_os.py
qemu-system-i386 -drive file=build/graphics_os.img,format=raw
```

```bash
python uefi_hello.py
qemu-system-x86_64 -bios /path/to/OVMF.fd -drive file=build/uefi_hello.img,format=raw
```

### `graphics_os.py` in action

Self-contained placeholder art (`assets/generate_assets.py`), so the example runs with no external files — swap in your own PNGs any time, `aetherix.imaging` handles resizing and color quantization for whatever you give it.

Boot loading animation (a real "video" — a sequence of frames shown in order with a delay between them):

<p>
  <img src="examples/assets/loading_0.png" width="120" alt="Loading animation frame 1, 16%">
  <img src="examples/assets/loading_2.png" width="120" alt="Loading animation frame 3, 33%">
  <img src="examples/assets/loading_4.png" width="120" alt="Loading animation frame 5, 66%">
  <img src="examples/assets/loading_5.png" width="120" alt="Loading animation frame 6, 100%">
</p>

Home screen — background and logo, prepared together with `imaging.load_images_shared_palette` so both render correctly against VGA Mode 13h's single, screen-wide 256-color palette:

<p>
  <img src="examples/assets/background.png" width="240" alt="Gradient background image">
  <img src="examples/assets/logo.png" width="80" alt="Composited home-screen logo">
</p>

### Full example source

<details>
<summary><code>hello_os.py</code> — minimal Aetherix example</summary>

```python
"""
hello_os -- minimal Aetherix example.

Boots to real mode, sets up segments, prints a message via BIOS, loads a
32-bit kernel that switches to protected mode, clears the VGA text screen,
prints a message directly to video memory, waits for a keypress via the
PS/2 controller, then halts.

Run:
    python examples/hello_os.py

Then boot the produced image in an emulator, e.g.:
    qemu-system-i386 -drive file=hello_os.img,format=raw
"""
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent.parent))

from aetherix import Project, regs

with Project("HelloOS", boot_message="HelloOS bootloader starting...") as os_:

    @os_.kernel_entry
    def main(prog, drivers):
        drivers.vga.clear(prog, attr=drivers.vga.WHITE_ON_BLACK)
        drivers.vga.print_string(prog, "Hello from Aetherix!", row=0, col=0,
                                  attr=drivers.vga.GREEN_ON_BLACK)
        drivers.vga.print_string(prog, "Press any key to continue...", row=2, col=0)
        drivers.keyboard.wait_key_scancode(prog)
        drivers.vga.print_string(prog, "Key received. System halted.", row=4, col=0,
                                  attr=drivers.vga.YELLOW_ON_BLACK)
        prog.hlt()

    os_.add_file("readme.txt", "This file is embedded in the disk image via AVFS.\n")

    out_dir = Path(__file__).resolve().parent.parent / "build"
    out_dir.mkdir(parents=True, exist_ok=True)
    out = os_.build(str(out_dir / "hello_os.img"))
    print(f"Built: {out} ({out.stat().st_size} bytes)")
    print(f"Kernel sectors: {(len(os_.kernel.assemble()) + 511) // 512}")
    print("Boot it with: qemu-system-i386 -drive file=hello_os.img,format=raw")
```

</details>

<details>
<summary><code>aether_shell.py</code> — a genuinely interactive example OS</summary>

```python
"""
aether_shell -- a genuinely interactive example OS.

Unlike hello_os.py (which prints a few fixed, compile-time-known messages
and waits for one keypress), this boots into a live loop: it reads your
keyboard input in real time -- Shift and all -- and echoes it to the
screen using a runtime VGA cursor (aetherix.drivers.terminal) that
actually moves, wraps at the end of a row, and responds to
Enter/Backspace. There is no fixed script of what appears where; what
shows up on screen depends on what you type.

Controls:
    (letters, digits, punctuation, space)   echoed as typed
    Shift                                    uppercase letters, shifted symbols
    Enter                                    move to the next line
    Backspace                                erase the previous character
    F1                                        beep the PC speaker
    F2                                        reboot (hardware reset)
    F3                                        shutdown (QEMU/Bochs only --
                                               see aetherix.drivers.power)
    Esc                                       goodbye message, then halt

Run:
    python examples/aether_shell.py

Then boot the produced image in an emulator, e.g.:
    qemu-system-i386 -drive file=aether_shell.img,format=raw

Or verify it under CPU emulation without needing QEMU installed:
    pip install unicorn
    python tests/emulate.py build/aether_shell.img
"""
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent.parent))

from aetherix import Project, regs

with Project("AetherShell", boot_message="AetherShell starting...") as os_:

    @os_.kernel_entry
    def main(prog, drivers):
        vga = drivers.vga
        kb = drivers.keyboard
        term = drivers.terminal
        power = drivers.power

        kb.init(prog)  # zero the Shift-state flag
        vga.clear(prog)
        vga.print_string(prog, "AetherShell v0.2 -- a live Aetherix demo OS",
                          row=0, col=0, attr=vga.GREEN_ON_BLACK)
        vga.print_string(prog, "Type (Shift works!). Enter=newline  Backspace=erase",
                          row=1, col=0)
        vga.print_string(prog, "F1=beep  F2=reboot  F3=shutdown  Esc=halt",
                          row=2, col=0)
        term.init(prog, start_row=4)

        prog.label("shell_loop")
        kb.wait_key_scancode(prog)  # blocks; raw scancode (make OR break) ends up in AL

        prog.cmp8(regs.AL, kb.SCANCODE_ESCAPE)
        prog.jcc(regs.JZ, "shell_halt")
        prog.cmp8(regs.AL, kb.SCANCODE_ENTER)
        prog.jcc(regs.JZ, "shell_enter")
        prog.cmp8(regs.AL, kb.SCANCODE_BACKSPACE)
        prog.jcc(regs.JZ, "shell_backspace")
        prog.cmp8(regs.AL, kb.SCANCODE_F1)
        prog.jcc(regs.JZ, "shell_beep")
        prog.cmp8(regs.AL, kb.SCANCODE_F2)
        prog.jcc(regs.JZ, "shell_reboot")
        prog.cmp8(regs.AL, kb.SCANCODE_F3)
        prog.jcc(regs.JZ, "shell_shutdown")

        # Shift press/release: update the flag keyboard.read_char also
        # uses, then go straight back to waiting -- Shift never prints.
        prog.cmp8(regs.AL, kb.SCANCODE_LSHIFT)
        prog.jcc(regs.JZ, "shell_shift_on")
        prog.cmp8(regs.AL, kb.SCANCODE_RSHIFT)
        prog.jcc(regs.JZ, "shell_shift_on")
        prog.cmp8(regs.AL, kb.SCANCODE_LSHIFT | kb.BREAK_BIT)
        prog.jcc(regs.JZ, "shell_shift_off")
        prog.cmp8(regs.AL, kb.SCANCODE_RSHIFT | kb.BREAK_BIT)
        prog.jcc(regs.JZ, "shell_shift_off")

        # Any other break code (bit 7 set) -- ignore, don't echo on release.
        prog.test_al(kb.BREAK_BIT)
        prog.jcc(regs.JNZ, "shell_loop")

        # Dispatch any other recognized key, Shift-aware: compare the
        # scancode against each known entry and, on a match, print
        # whichever of its two (compile-time-known) characters matches
        # the current runtime Shift state. Same technique
        # keyboard.read_char uses internally, inlined here because this
        # loop also needs to catch Enter/Backspace/F-keys in the same
        # pass, which read_char deliberately doesn't return for.
        all_codes = sorted(set(kb.SCANCODE_SET1_ASCII) | set(kb.SCANCODE_SET1_SHIFTED))
        for code in all_codes:
            skip_label = prog.unique_label("key_skip")
            use_shifted_label = prog.unique_label("key_use_shifted")
            unshifted_ch = kb.SCANCODE_SET1_ASCII.get(code, kb.SCANCODE_SET1_SHIFTED.get(code))
            shifted_ch = kb.SCANCODE_SET1_SHIFTED.get(code, unshifted_ch)

            prog.cmp8(regs.AL, code)
            prog.jcc(regs.JNZ, skip_label)
            prog.load8(regs.BL, kb.SHIFT_STATE_VAR)
            prog.cmp8(regs.BL, 0)
            prog.jcc(regs.JNZ, use_shifted_label)
            term.putchar_imm(prog, unshifted_ch)
            prog.jmp("shell_loop")
            prog.label(use_shifted_label)
            term.putchar_imm(prog, shifted_ch)
            prog.jmp("shell_loop")
            prog.label(skip_label)

        prog.jmp("shell_loop")  # unrecognized key -- ignore and keep waiting

        prog.label("shell_shift_on")
        prog.store8(kb.SHIFT_STATE_VAR, 1)
        prog.jmp("shell_loop")

        prog.label("shell_shift_off")
        prog.store8(kb.SHIFT_STATE_VAR, 0)
        prog.jmp("shell_loop")

        prog.label("shell_enter")
        term.newline(prog)
        prog.jmp("shell_loop")

        prog.label("shell_backspace")
        term.backspace(prog)
        prog.jmp("shell_loop")

        prog.label("shell_beep")
        drivers.speaker.beep(prog, frequency_hz=880, duration_iterations=1_500_000)
        prog.jmp("shell_loop")

        prog.label("shell_reboot")
        vga.print_string(prog, "Rebooting...", row=24, col=0, attr=vga.YELLOW_ON_BLACK)
        power.reboot(prog)

        prog.label("shell_shutdown")
        vga.print_string(prog, "Shutting down...", row=24, col=0, attr=vga.YELLOW_ON_BLACK)
        power.shutdown(prog)

        prog.label("shell_halt")
        vga.print_string(prog, "Goodbye! System halted.", row=24, col=0, attr=vga.YELLOW_ON_BLACK)
        prog.hlt()

    out_dir = Path(__file__).resolve().parent.parent / "build"
    out_dir.mkdir(parents=True, exist_ok=True)
    out = os_.build(str(out_dir / "aether_shell.img"))
    print(f"Built: {out} ({out.stat().st_size} bytes)")
    print(f"Kernel sectors: {(len(os_.kernel.assemble()) + 511) // 512}")
    print("Boot it with: qemu-system-i386 -drive file=aether_shell.img,format=raw")
```

</details>

<details>
<summary><code>graphics_os.py</code> — boot animation, background, and composited logo</summary>

```python
"""
graphics_os -- an example OS with a real boot animation, a background
image, and a home-screen logo, using VGA Mode 13h (320x200, 256-color).

Boot flow:
  1. Loading screen: plays a short progress-bar animation (a real "video"
     -- a sequence of frames shown in order with a delay between them).
  2. Home screen: a full-screen background image with a logo composited
     on top (two separate images, blitted to different screen regions).
  3. Halts, leaving the home screen displayed.

Uses placeholder art generated by examples/assets/generate_assets.py --
run that first (or just run this script, which does it for you if the
assets are missing). Swap in your own PNGs any time; aetherix.imaging
handles resizing and color quantization for whatever you give it.

Run:
    python examples/graphics_os.py

Then boot the produced image in an emulator, e.g.:
    qemu-system-i386 -drive file=graphics_os.img,format=raw

Or verify it under CPU emulation without needing QEMU installed:
    pip install unicorn
    python tests/emulate.py build/graphics_os.img
(the emulator prints the VGA text buffer, which will be blank here since
this example only uses graphics mode -- see tests/test_emulation.py for
how the framebuffer/palette are checked instead.)
"""
import sys
import subprocess
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent.parent))

from aetherix import Project, imaging
from aetherix.drivers import graphics

ASSETS_DIR = Path(__file__).resolve().parent / "assets"

if not (ASSETS_DIR / "background.png").exists():
    print("Generating placeholder assets...")
    subprocess.run([sys.executable, str(ASSETS_DIR / "generate_assets.py")], check=True)

# -- Prepare assets at build time (resizing/quantizing happens here, not
#    at boot -- the kernel only ever handles already-prepared bytes) --
loading_frames = [
    imaging.load_image(ASSETS_DIR / f"loading_{i}.png") for i in range(6)
]

# background and logo will be on screen AT THE SAME TIME, so they must
# share one 256-color palette -- VGA Mode 13h has a single palette for the
# whole screen, not one per image (see imaging.load_images_shared_palette).
background, logo = imaging.load_images_shared_palette([
    (ASSETS_DIR / "background.png", graphics.GFX_WIDTH, graphics.GFX_HEIGHT, True),
    (ASSETS_DIR / "logo.png", 64, 64, False),
])

LOGO_X = (graphics.GFX_WIDTH - logo.width) // 2
LOGO_Y = (graphics.GFX_HEIGHT - logo.height) // 2

with Project("GraphicsOS", graphics_mode=graphics.MODE_13H) as os_:

    @os_.kernel_entry
    def main(prog, drivers):
        # 1. Loading animation -- plays once, then falls through.
        drivers.graphics.play_frames(prog, loading_frames, delay_iterations=3_000_000, loop=False)

        # 2. Home screen -- background, then a logo composited on top.
        drivers.graphics.show_image(prog, background, x=0, y=0)
        drivers.graphics.show_image(prog, logo, x=LOGO_X, y=LOGO_Y)

        prog.hlt()

    out_dir = Path(__file__).resolve().parent.parent / "build"
    out_dir.mkdir(parents=True, exist_ok=True)
    out = os_.build(str(out_dir / "graphics_os.img"))
    print(f"Built: {out} ({out.stat().st_size} bytes)")
    print(f"Kernel sectors: {(len(os_.kernel.assemble()) + 511) // 512}")
    print("Boot it with: qemu-system-i386 -drive file=graphics_os.img,format=raw")
```

</details>

<details>
<summary><code>assets/generate_assets.py</code> — placeholder art used by <code>graphics_os.py</code></summary>

```python
"""
Generates placeholder art for examples/graphics_os.py using Pillow, so the
example is runnable with no external image files. Swap these PNGs for your
own artwork any time -- graphics_os.py just calls aetherix.imaging.load_image
on whatever's in this directory.

Run: python examples/assets/generate_assets.py
"""
from pathlib import Path

from PIL import Image, ImageDraw

OUT_DIR = Path(__file__).resolve().parent


def make_loading_frames(n=6, size=(320, 200)):
    """A simple progress-bar boot animation, frame by frame."""
    frames = []
    for i in range(n):
        img = Image.new("RGB", size, (10, 12, 30))
        draw = ImageDraw.Draw(img)
        draw.text((110, 70), "Aetherix", fill=(120, 200, 255))
        bar_w, bar_h = 200, 14
        bx, by = (size[0] - bar_w) // 2, 110
        draw.rectangle([bx, by, bx + bar_w, by + bar_h], outline=(80, 90, 120))
        fill_w = int(bar_w * (i + 1) / n)
        draw.rectangle([bx, by, bx + fill_w, by + bar_h], fill=(90, 200, 140))
        draw.text((bx, by + 20), f"Loading... {int(100 * (i + 1) / n)}%", fill=(180, 190, 210))
        frames.append(img)
        img.save(OUT_DIR / f"loading_{i}.png")
    return frames


def make_background(size=(320, 200)):
    """A simple vertical gradient desktop background."""
    img = Image.new("RGB", size)
    px = img.load()
    top = (20, 30, 60)
    bottom = (70, 40, 90)
    for y in range(size[1]):
        t = y / (size[1] - 1)
        r = int(top[0] + (bottom[0] - top[0]) * t)
        g = int(top[1] + (bottom[1] - top[1]) * t)
        b = int(top[2] + (bottom[2] - top[2]) * t)
        for x in range(size[0]):
            px[x, y] = (r, g, b)
    img.save(OUT_DIR / "background.png")
    return img


def make_logo(size=(64, 64)):
    """A simple circular logo icon to overlay on the home screen."""
    img = Image.new("RGB", size, (0, 0, 0))
    draw = ImageDraw.Draw(img)
    draw.ellipse([2, 2, size[0] - 2, size[1] - 2], fill=(90, 170, 255), outline=(230, 240, 255))
    draw.text((size[0] // 2 - 4, size[1] // 2 - 6), "A", fill=(15, 20, 40))
    img.save(OUT_DIR / "logo.png")
    return img


if __name__ == "__main__":
    OUT_DIR.mkdir(parents=True, exist_ok=True)
    make_loading_frames()
    make_background()
    make_logo()
    print(f"Generated placeholder assets in {OUT_DIR}")
```

</details>

<details>
<summary><code>uefi_hello.py</code> — a minimal, genuinely working UEFI application</summary>

```python
"""
uefi_hello -- a minimal, genuinely working UEFI application.

This is a completely different boot path from the other examples in this
directory (hello_os.py, aether_shell.py, graphics_os.py), which all boot
via BIOS/MBR. UEFI firmware boots a PE32+ executable directly in 64-bit
long mode -- no 16/32-bit stages, no BIOS interrupts, no boot sector.

Demonstrates: console output, cursor control, and keyboard input (all via
real UEFI protocol calls -- ConOut/ConIn), plus locating an arbitrary
protocol by GUID and calling a method on it that this package doesn't
wrap with a dedicated helper (aetherix.uefi.boot_services/protocol --
the general extension mechanism for any UEFI protocol you need that
isn't built in yet).

This builds:
  - uefi_hello.efi   -- the PE32+ application itself
  - uefi_hello.img   -- a complete GPT+FAT32 disk image containing it at
                         \\EFI\\BOOT\\BOOTX64.EFI, ready to boot in QEMU+OVMF
                         or write to a real USB stick

Run:
    python examples/uefi_hello.py

Then boot it with QEMU + OVMF (get OVMF.fd from your Linux distro's
`ovmf`/`edk2-ovmf` package, or https://github.com/tianocore/edk2):
    qemu-system-x86_64 -bios /path/to/OVMF.fd -drive file=build/uefi_hello.img,format=raw

Or verify it without QEMU/OVMF installed, via real x86-64 CPU emulation:
    pip install unicorn
    python tests/emulate_uefi.py build/uefi_hello.efi

Or write it to a real USB stick (BACK UP THE STICK FIRST -- this
overwrites the entire device):
    sudo dd if=build/uefi_hello.img of=/dev/sdX bs=4M status=progress conv=fsync
Then boot from it on any UEFI PC with Secure Boot disabled (this app
isn't signed).
"""
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent.parent))

from aetherix.uefi.app import UefiApp
from aetherix.uefi import console, keyboard, diskimage
from aetherix import regs

app = UefiApp()


@app.entry
def main(prog, sys_table):
    console.clear_screen(prog, sys_table)
    console.set_cursor_position(prog, sys_table, 0, 0)
    console.println(prog, "Hello from Aetherix UEFI!", sys_table)
    console.println(prog, "", sys_table)
    console.println(prog, "This machine code was generated entirely by aetherix.uefi --", sys_table)
    console.println(prog, "no nasm, no ld, no C compiler toolchain for the .efi itself.", sys_table)
    console.println(prog, "", sys_table)
    console.println(prog, "Press any key to continue...", sys_table)
    keyboard.read_key(prog, sys_table, regs.R12, regs.R13)

    console.set_cursor_position(prog, sys_table, 0, 8)
    console.println(prog, "Key received. Returning control to firmware.", sys_table)


out_dir = Path(__file__).resolve().parent.parent / "build"
out_dir.mkdir(parents=True, exist_ok=True)

efi_path = app.build(str(out_dir / "uefi_hello.efi"))
print(f"Built: {efi_path} ({efi_path.stat().st_size} bytes)")

img_path = diskimage.write_disk_image(efi_path.read_bytes(), str(out_dir / "uefi_hello.img"))
print(f"Built: {img_path} ({img_path.stat().st_size} bytes)")
print("Boot it with: qemu-system-x86_64 -bios /path/to/OVMF.fd -drive file=uefi_hello.img,format=raw")
```

</details>

## What you actually get (read before you assume too much)

Building a full production OS with GPU, USB, camera, printer, and network driver stacks is a multi-year effort even for large teams. Aetherix does **not** claim to hand you that. What it genuinely gives you:

- A verified-correct real-mode bootloader: segment setup, BIOS teletype output, A20 line enable, BIOS LBA disk loading with retry, and a far jump into your kernel.
- A verified-correct 16→32-bit transition: flat GDT construction, `lgdt`, `CR0.PE` set, far jump into a 32-bit code segment — computed with exact, non-guessed offsets.
- Working drivers for hardware that's actually reachable without an existing OS or bus/protocol stack underneath you:
  - **VGA** text-mode framebuffer (clear screen, print strings).
  - **PS/2 keyboard** — full Scancode Set 1 (letters, digits, punctuation, Tab), Shift-aware (uppercase, shifted symbols), plus a high-level `read_char` helper. Extended keys (arrows, Insert/Delete/Home/End/Page Up/Down) aren't covered yet.
  - **PC speaker** — PIT-driven beep with an actual audible duration.
  - **Power control** — `restart` (soft, jump back to kernel start), `reboot` (hard, keyboard-controller reset pulse — works on real hardware and every mainstream emulator), and an honestly-scoped `shutdown`/`sleep_until_keypress` (QEMU/Bochs magic port, not real ACPI).
  - **Terminal** — a *runtime* VGA cursor (not just compile-time-fixed text): moves, wraps at end-of-row, supports newline/backspace.
  - **Graphics** — VGA Mode 13h (320×200, 256-color): palette upload and image blitting, so a kernel can show a loading screen, a background, and composited images, not just text.
- A minimal embedded read-only file archive format (**AVFS**) for bundling files into the disk image at build time.
- A Python API designed so both styles work: high-level (`drivers.vga.print_string(...)`) and low-level (`prog.mov16(AX, 0x1234)`, straight machine-code control).

### What's not implemented (and why)

GPU (beyond VGA Mode 13h — no modern framebuffer/GOP/VBE/hardware acceleration/3D), USB, Ethernet/networking, printer, fan control, battery telemetry, and camera all need real bus enumeration or protocol stacks (PCI, a USB host controller driver, ACPI, etc.) that don't exist yet in this codebase. Rather than fake them, `aetherix.drivers.hal.HAL` lists them as named, documented extension points:

```python
from aetherix import HAL

hal = HAL()
print(hal.implemented())   # ['vga', 'keyboard', 'speaker']
print(hal.scaffolded())    # ['gpu', 'usb', 'ethernet', 'printer', 'fan', 'battery', 'camera']
```

Accessing a scaffolded driver raises `NotImplementedError` with an explanation of what it needs, rather than silently doing nothing. There is also no file manager, task manager, calculator, or custom in-OS programming language yet, no Caps Lock, and no real ACPI shutdown/sleep. Building any of these is exactly the kind of contribution this project is scoped for — see **[CONTRIBUTING.md](CONTRIBUTING.md)** for a concrete, tractability-ordered list of realistic next drivers and opcodes.

## UEFI support

Everything above (`Project`, `BootSector`, `Kernel`, `aetherix.drivers`) is the **BIOS boot path**. UEFI is a genuinely different architecture, not an extension of it: firmware loads a **PE32+ executable directly into 64-bit long mode** — no 16/32-bit stages, no BIOS interrupts, no MBR boot sector. It boots from a **GPT-partitioned disk with a FAT32 EFI System Partition**, not a flat binary. I/O goes through UEFI protocol function-pointer calls (Microsoft x64 calling convention), not `int` instructions. **The two APIs cannot be mixed** — pick one per OS you're building.

`aetherix.uefi` is a separate subsystem built for this from scratch:

- A minimal **64-bit long-mode instruction set** (`Program(bits=64)`) — REX-prefixed mov/add/sub/cmp, RIP-relative `lea`, indirect calls through a register, full RAX–R15 register range.
- A **PE32+ writer** (`aetherix.uefi.pe`) producing a genuinely valid `EFI_APPLICATION` image (position-independent by construction, so no `.reloc` section is needed).
- **`UefiApp`** (`aetherix.uefi.app`) — generates the Microsoft x64 ABI prologue/epilogue around your entry function.
- **Console I/O** (`aetherix.uefi.console`, `aetherix.uefi.keyboard`) — `ConOut`/`ConIn` calls, UCS-2 encoded as the spec requires.
- A **generic protocol-call extension mechanism** (`aetherix.uefi.protocol`, `aetherix.uefi.boot_services`) — `locate_protocol` + `protocol.call` let you drive *any* UEFI protocol (Graphics Output, Block I/O, File System, or anything else) by GUID and byte offset, with no changes needed to this package.
- **GPT + FAT32** (`aetherix.uefi.gpt`, `aetherix.uefi.fat32`, `aetherix.uefi.diskimage`) — a real, from-scratch disk image builder: protective MBR, primary + backup GPT with correct CRC32 checksums, and a FAT32 ESP containing `\EFI\BOOT\BOOTX64.EFI`.

**Scope**: this targets UEFI *applications* — code that uses Boot Services and returns control to firmware, like a shell command. It is not a full custom OS loader (that means calling `ExitBootServices`, taking over the memory map, and setting up your own GDT/IDT/paging — see `CONTRIBUTING.md` if you want to build that on top of this).

```python
from aetherix.uefi.app import UefiApp
from aetherix.uefi import console, diskimage

app = UefiApp()

@app.entry
def main(prog, sys_table):
    console.clear_screen(prog, sys_table)
    console.println(prog, "Hello from Aetherix UEFI!", sys_table)

efi_path = app.build("build/uefi_hello.efi")
diskimage.write_disk_image(efi_path.read_bytes(), "build/uefi_hello.img")
```

```bash
qemu-system-x86_64 -bios /path/to/OVMF.fd -drive file=build/uefi_hello.img,format=raw
```

**Verified at every layer, independently, not just by this codebase's own logic checking itself:**

| Layer | How it's verified |
|---|---|
| PE file | `file` recognizes it as `PE32+ executable (EFI application) x86-64` |
| Machine code | `objdump -M intel -m i386:x86-64` |
| Real execution | A Unicorn (`UC_MODE_64`) harness simulating `EFI_SYSTEM_TABLE`/`ConOut`/`ConIn`/`BootServices` (`tests/emulate_uefi.py`, `tests/test_uefi.py`) |
| GPT | `gdisk -l disk.img` — "No problems found" |
| FAT32 | `fsck.vfat` and `mtools` (`mdir`/`mcopy`), including byte-for-byte file extraction |
| End-to-end | **Real, unmodified OVMF firmware in real QEMU** boots the image and prints the exact expected text |

Full API details (console output, keyboard input, the protocol extension mechanism, disk image building) are in **[API.md, section 18](API.md#18-uefi-support-a-separate-boot-path)**.

## Architecture

```
aetherix/
  asm.py            # two-pass label/jump resolver over the native encoder (16/32/64-bit)
  regs.py           # register / condition-code constants
  context.py        # Project: the batteries-included entry point (BIOS path)
  imaging.py        # build-time image loading/quantization (needs Pillow)
  boot/realmode.py  # BootSector builder (512-byte MBR-style boot sector)
  kernel/
    pmode.py        # flat GDT construction
    builder.py      # Kernel: 16-bit trampoline + GDT + 32-bit body
  drivers/
    vga.py, keyboard.py, speaker.py, terminal.py, graphics.py, power.py   # implemented
    hal.py                                                                 # driver registry + extension points
  fs/simplefs.py    # AVFS embedded file archive
  image/diskimage.py # boot sector + kernel + AVFS -> flat .img/.bin
  uefi/              # separate boot path -- see "UEFI support" above
    app.py           # UefiApp: MS x64 ABI prologue/epilogue + PE assembly
    console.py       # ConOut protocol calls (output, cursor control)
    keyboard.py      # ConIn protocol calls (key input)
    protocol.py      # generic protocol-method-call extension mechanism
    boot_services.py # LocateProtocol (find a protocol interface by GUID)
    guid.py          # EFI_GUID mixed-endian encoding (shared by gpt.py too)
    pe.py            # PE32+ writer
    gpt.py, fat32.py, diskimage.py  # GPT + FAT32 ESP disk image builder
native/
  encoder.c         # the actual x86 instruction encoder (C, no deps)
```

Every layer is independently usable — build a raw `Program` and hand-assemble arbitrary real/protected-mode code without ever touching `Project`, or add a new driver module and register it with `HAL`.

## CLI

```bash
aetherix new MyOS          # scaffold a starter project script
aetherix info               # show implemented vs. scaffolded hardware support
```

## Verifying the output yourself

Don't take byte correctness on faith — disassemble it:

```bash
head -c 512 myos.img > boot.bin
objdump -D -b binary -m i8086 boot.bin      # boot sector, 16-bit
dd if=myos.img of=kernel.bin bs=512 skip=1
objdump -D -b binary -m i386 kernel.bin      # (after the trampoline offset)
```

Or go further and actually *execute* it under CPU emulation — this is how real bugs (like the multi-chunk LBA disk load issue, see [CHANGELOG.md](CHANGELOG.md)) were caught, since disassembly alone proves the bytes are plausible x86, not that the boot sequence completes:

```bash
pip install unicorn
python tests/emulate.py myos.img
```

This prints the real-mode boot message, the disk reads that occurred (LBA, sector count, destination), and the final VGA text buffer contents — i.e. what a real screen would show, without needing QEMU/VirtualBox. `tests/test_emulation.py` runs this automatically as a regression test whenever `unicorn` is installed. The UEFI path has its own equivalent (`tests/emulate_uefi.py`) — see [UEFI support](#uefi-support).

## Documentation

- **[API.md](API.md)** — the complete usage guide: every layer from `Project` down to raw `Program` instructions, the `HAL`, embedding files (AVFS), disk images, writing a custom bootloader/kernel, the full UEFI subsystem, a common-errors table, and a full worked example.
- **[CONTRIBUTING.md](CONTRIBUTING.md)** — how to add a native encoder opcode or a new driver, code style (including the RSP/R12 and RBP/R13 ModRM gotchas), and a concrete, tractability-ordered roadmap of realistic next contributions.
- **[CHANGELOG.md](CHANGELOG.md)** — project history, including real bugs found and fixed during development and how each was verified.

## Contributing

The most valuable contributions right now are new drivers and new instruction-encoder opcodes, following the existing pattern in `aetherix/drivers/vga.py`/`keyboard.py`/`speaker.py` and `native/encoder.c`. See **[CONTRIBUTING.md](CONTRIBUTING.md)** for the full guide, code style, and a roadmap ordered roughly by tractability — from small (serial port, extended keyboard scancodes) to substantial (a real writable filesystem, ACPI parsing, a full custom UEFI OS loader).

## License

AGPL-3.0-or-later.
