m³OS docs GitHub

Phase 47

Doom

m3OS can run DOOM as a real full-screen graphical application. The game loads its WAD data from disk, renders through the framebuffer path, accepts keyboard input for gameplay, and returns the system to a usable administration path when it exits.

Complete

Learning goal

Understand what a real graphical program needs from the OS beyond text output.

Category

showcase

Why this phase matters

m3OS can run DOOM as a real full-screen graphical application. The game loads its WAD data from disk, renders through the framebuffer path, accepts keyboard input for gameplay, and returns the system to a usable administration path when it exits.

How it works in m3OS

Phase 47 is where m3OS stops proving isolated subsystems and starts carrying a real graphical application end to end. It adds framebuffer mapping, raw scancode input, doomgeneric build integration, shareware WAD delivery on the ext2 image, and the debugging work needed to keep a real game responsive under QEMU. The end result is a working DOOM port with known remaining performance headroom.

How it builds on earlier phases

This phase builds on System Services and turns that foundation into Doom.

Implementation outline

  • Define the documented userspace framebuffer and keyboard path used by the port.
  • Implement or finish the platform layer for DOOM.
  • Integrate the WAD and binary into the supported image/build flow.
  • Validate launch, rendering, gameplay input, and exit/recovery behavior.
  • Document what this phase proves and what it does not prove about the later GUI stack.

Task progress snapshot

0 of 81 tracked tasks are complete.

Documentation targets

  • Documentation deliverables have not been listed yet.

Deferred follow-on work

  • Multi-application composition and windowing
  • Pointer-driven GUI policy
  • Audio output for the graphical session
  • Hardware-accelerated rendering

Source materials

Important code

Code spotlight cards

Code spotlight

`sys_framebuffer_info` syscall

kernel/src/arch/x86_64/syscall.rs L7161-L7184

and pixel format before it can render anything — this is the discovery mechanism. Why it matters: Userspace needs the framebuffer dimensions, pitch, bytes-per-pixel,. Focus on `sys_framebuffer_info` and `0x1002` in `kernel/src/arch/x86_64/syscall.rs` while you trace the snippet.

fn sys_framebuffer_info(buf_addr: u64, buf_len: u64) -> u64 {
    const USER_LIMIT: u64 = 0x0000_8000_0000_0000;
    const FB_INFO_SIZE: u64 = core::mem::size_of::<FbInfo>() as u64;

    if buf_addr == 0 || buf_addr >= USER_LIMIT || buf_len < FB_INFO_SIZE {
        return NEG_EINVAL;
    }

    let (width, height, stride, bpp, pixel_format) = match crate::fb::framebuffer_raw_info() {
        Some(info) => info,
        None => return NEG_EINVAL,
    };

    let pixel_format_val: u32 = match pixel_format {
        bootloader_api::info::PixelFormat::Rgb => 0,
        bootloader_api::info::PixelFormat::Bgr => 1,
        _ => return NEG_EINVAL,
    };

    let info = FbInfo {
        width: width as u32,
        height: height as u32,
        stride: stride as u32,
        bpp: bpp as u32,

Code spotlight

Syscall constants in `syscall-lib`

userspace/syscall-lib/src/lib.rs L253-L257

assembly or direct syscall invocation) need named constants for the new syscall. Why it matters: Rust userspace programs (and the C platform layer via inline. Focus on `syscall-lib` and `SYS_FRAMEBUFFER_INFO` in `userspace/syscall-lib/src/lib.rs` while you trace the snippet.

/// Phase 47: framebuffer info syscall.
pub const SYS_FRAMEBUFFER_INFO: u64 = 0x1005;

/// Phase 47: framebuffer mmap syscall.
pub const SYS_FRAMEBUFFER_MMAP: u64 = 0x1006;

Code spotlight

Console yield function

kernel/src/fb/mod.rs L954-L964

same framebuffer memory, causing garbled output. Why it matters: Without this, the kernel text console and DOOM would race on the. Focus on `yield_console` and `write_str` in `kernel/src/fb/mod.rs` while you trace the snippet.

/// Suppresses all framebuffer console output.
///
/// For internal use only.  External callers that need to claim the
/// framebuffer should use [`try_yield_console`] which performs an
/// atomic compare-and-swap ownership check.
fn yield_console(owner_pid: u32) {
    FB_OWNER_PID.store(owner_pid, Ordering::Release);
    crate::arch::x86_64::interrupts::reset_raw_input_state();
    let _guard = CONSOLE.lock();
    CONSOLE_YIELDED.store(true, Ordering::Release);
}

Key files

  • kernel/src/arch/x86_64/syscall.rs — Framebuffer syscalls, raw scancode syscall, TSC-backed gettimeofday() and short nanosleep() behavior
  • kernel/src/arch/x86_64/interrupts.rs — Keyboard IRQ handling, raw scancode buffers, bounded i8042 drain loop
  • kernel/src/fs/ext2.rs — WAD read hot path, enlarged block cache, zero-copy read_block_into_slice() helper
  • kernel/src/fb/mod.rs — Framebuffer ownership, console yield/restore path
  • userspace/doom/dg_m3os.c — m3OS doomgeneric platform layer: framebuffer init, blit path, key translation, timing wrappers
  • userspace/doom/patches/i_input.c — Persistent local patch removing the upstream key-up drain asymmetry
  • xtask/src/main.rs — doomgeneric clone/build flow, patch overlay, WAD population on the ext2 image

How real OSes differ

  • Mature systems usually expose standardized graphics and input interfaces rather than one milestone app driving the first visible proof.
  • Real desktops rely on compositors, richer input stacks, audio, toolkit layers, and often hardware acceleration.
  • m3OS should use this phase to validate the substrate with a real workload, not to freeze the wrong long-term abstraction.

Success criteria

  • DOOM or the chosen equivalent graphical workload launches from m3OS and renders through the documented framebuffer path.
  • The application loads its assets from the supported storage path without undocumented host-side tricks.
  • Interactive keyboard input works well enough for real use of the application.
  • Exiting or crashing the graphical program returns the system to a usable administration path.
  • The docs explicitly describe this milestone as a graphics proof point, not a complete GUI stack.

Related phases

Keep exploring the roadmap