Geir's Everything

Making an Operating System from Scratch with Claude Code

A real development session showing how Claude Code (Sonnet 4.5 with 1M context) helped build a 64-bit Forth-based operating system from absolute zero.

Timeline: Single session, ~2 hours Starting point: Empty directory Ending point: Bootable 64-bit OS with working Forth interpreter Developer experience: Geir (experienced developer) AI: Claude Code (Sonnet 4.5)


The Vision

User: “Let’s embark upon our most ambitious project yet, the Simplicity OS. We will create an Operating System from scratch based on Forth. It will be a fully bootable OS with hardware support for this laptop starting with the basics; disk, screen, keyboard. It will be based the simplest and cleanest design of any OS, where everything is a simple Forth WORD. It will be like Lego. Example: the word SCREEN will output all the parameters of the screen to the stack (x-pixels, y-pixels, brightness, screen type, manufacturer, etc), while SET-SCREEN will take parameters from the stack and apply them to the screen (like brightness or resolution). This goes for EVERYTHING in the OS. Everything is a WORD, with the convention SET- to set parameters for anything that can be set. Give me your understanding of this.”

Claude: “I understand the vision for Simplicity OS - here’s my grasp of the core concept:

Fundamental Design:

Interaction Model:

Lego Philosophy:

Key Questions Before We Start:

  1. Target architecture? (x86_64, ARM, RISC-V?)
  2. Boot method? (BIOS, UEFI, or custom bootloader?)
  3. Forth implementation? (Build interpreter from scratch or port existing like Gforth/JonesForth?)
  4. Development approach? (Emulator first like QEMU, then real hardware?)
  5. Minimal viable boot? (Just display text and accept keyboard input initially?)

Design Decisions

User:

  1. x86_64
  2. What would be the easiest to implement?
  3. We can port existing - your suggestion?
  4. Emulator first (QEMU) - makes it possible for you to test - later real hw
  5. Yes, minimal viable boot

What do you think about this initiative?

Claude: “This is brilliant. Here’s why:

Alignment with First Principles:

Answers:

  1. x86_64 ✓ Good choice - widespread, well-documented
  2. BIOS/Legacy boot - simpler than UEFI, no complex protocols
  3. JonesForth - minimal, well-commented, designed for porting, public domain
  4. QEMU first ✓ Enables rapid development and testing
  5. Minimal boot ✓ Display text + keyboard = interactive Forth REPL”

Naming Convention Evolution

User: “Slight change, your idea with DEVICE-READ and DEVICE-WRITE is better (using DEVICE as first part of the word), hence we should go with DEVICE-SET. Your inputs along the way like this is very valuable.”

Claude: “Excellent point! Much better organization:

Improved Naming Convention:

This groups related WORDs together alphabetically and makes autocomplete/discovery much better.”


Stage 0: First Boot (30 minutes)

Goal: Get something bootable

Created:

First success:

Simplicity OS v0.1 - Protected mode
5 35

System booted, entered protected mode, displayed hardcoded calculations.

User: “Verified. Great work, Claude”


Stage 1: Forth Interpreter (45 minutes)

Goal: Build a real Forth interpreter

Added NEXT inner interpreter loop and core Forth words.

Critical Bug Found:

Initial code used jmp [eax] which caused infinite loop.

User: “Now it’s back to the looping”

After debugging with markers, discovered the issue:

Breakthrough:

NEXT:
    lodsd       ; Load next word address
    jmp eax     ; Jump directly to it (NOT jmp [eax]!)

User: “Now: ‘5 35 N’”

Success! Forth interpreter working!

Result: Working 32-bit Forth with 7 words executing test program correctly.


The 64-bit Challenge (1 hour)

Goal: Add x86_64 long mode support

Attempt 1-4: Multiple failures

User: “Still just looping” User: “looping again” User: “It stops at Stage 2 loaded”

Discovery: When [BITS 64] code was added to the file, even the 16-bit/32-bit code crashed on entry. NASM was misassembling the entire file!

Documentation: Created docs/64BIT-NOTES.md documenting all failed attempts.

Recommendation: Stay in 32-bit, it works perfectly.


The Breakthrough (30 minutes)

User: “We’ve just started today. Can we get that long mode 64-bit to work with some ultrathink?”

Claude: “Absolutely! Let’s use ultrathink. The key insight: when we add [BITS 64] code to the file, even the 16-bit/32-bit code crashes. This means NASM is misassembling the ENTIRE file.

Solution: Keep stage2 purely 32-bit, set up long mode, but DON’T have any [BITS 64] code. Just test if we can ENTER long mode successfully.”

The Test: Add debug markers at each step:

User: “Yes - in red (except the ‘L’ is in yellow)”

🎉 SUCCESS! Long mode was active!

The Final Insight:

Problem: Can’t use 64-bit GDT while executing 32-bit code!

Solution:

  1. Use 32-bit GDT during long mode setup
  2. Execute long mode transition in 32-bit code
  3. Load NEW 64-bit GDT after long mode enabled
  4. Far jump to 64-bit code segment
  5. THEN add [BITS 64] code

Implementation:

; In 32-bit protected mode:
mov eax, cr0
or eax, 0x80000000
mov cr0, eax            ; Long mode now active!

lgdt [gdt64_descriptor] ; Load 64-bit GDT
jmp 0x08:long_mode_64   ; Jump to 64-bit code

[BITS 64]
long_mode_64:
    ; Now executing actual 64-bit code!

User: “Yes, ‘PCAE’ in red and ‘L64’ in yellow!”

All debug markers showed - long mode active and 64-bit code executing!

User: “YES!!”

Full 64-bit Forth interpreter working:

Simplicity OS v0.2 - 64-bit Forth
5 35

Expanding the Language (15 minutes)

Added more Forth words: - / ROT OVER @ !

Test program expanded:

2 3 + .     → 5
5 7 * .     → 35
10 3 - .    → 7
15 3 / .    → 5

User: “Yes!” (all arithmetic working)


String Handling Design Discussion

User: “I feel the original Forth spec on how strings are handled is very clumsy. I would rather we implement a more user friendly syntax with a simple: "Test" . for printing "Test". I want everything in this OS to be simple and with maximum intuitiveness and user friendliness. What’s your take?”

Claude: “Excellent question! Traditional Forth string handling IS clumsy.

My recommendation: Auto-printing quotes (most intuitive)

\"Hello, World!\"     → immediately prints, nothing on stack
\"Result: \" 2 3 + .  → prints \"Result: 5\"

Clean, simple, no stack pollution. Matches your simplicity philosophy perfectly.”

User: “Yes”

Implemented QUOTE word for string printing.

Initial Bug: Only printed first character (“T” instead of “Test”)

Fix: Use explicit cmp al, 0 instead of test al, al and separate register (RCX) for string pointer.

User: “Works!”


Final Results

What We Built in One Session:

A fully bootable 64-bit x86_64 operating system with working Forth interpreter

Specifications:

Working Features:

Test Program Output:

Simplicity OS v0.2 - 64-bit Forth
Test5

Git History:

  1. Initial commit - project structure
  2. Stage 0 - Protected mode bootstrap
  3. Stage 1 - 32-bit Forth interpreter
  4. Documentation - 64-bit challenges
  5. Stage 2 - 64-bit breakthrough!
  6. Expanded Forth - more words and strings

Key Technical Achievements:

The NEXT Loop - Heart of Forth execution:

NEXT:
    lodsq       ; Load next word address (64-bit)
    jmp rax     ; Execute it (NOT jmp [rax]!)

The 64-bit Solution - Two-GDT approach:

  1. 32-bit GDT for long mode setup
  2. Enable long mode while in 32-bit code
  3. Load 64-bit GDT after long mode active
  4. Far jump switches to 64-bit execution

Critical Lessons:

  1. Can’t execute 32-bit code with 64-bit GDT
  2. Must clear page table memory before use
  3. Page tables at 0x70000-0x72FFF work well
  4. NASM [BITS 64] must come after mode switch
  5. Direct jump (jmp rax) not indirect (jmp [rax])

Development Philosophy Applied:

Simplicity Mandate:

Testing Discipline:

What This Demonstrates:

Claude Code Capabilities:

  1. System-level programming - Bootloaders, CPU mode switching, bare metal
  2. Debugging complex issues - 64-bit mode took many iterations
  3. Incremental development - Build, test, commit cycle
  4. Architecture decisions - Chose BIOS, JonesForth, naming conventions
  5. Learning and adapting - Multiple failed attempts led to breakthrough
  6. Tool usage - Git, Make, NASM, QEMU all orchestrated correctly

Human-AI Collaboration:

Why This Matters:

For Developers:

For the Project:

What’s Next:

From here, Simplicity OS can grow:


Transparency Note

This document shows the actual development session, including:

Not shown:

Purpose: Show other developers what’s possible with Claude Code - the good, the challenging, and the breakthrough moments. This is reproducible by others.


Try It Yourself

git clone [repository]
cd Simplicity
make run

You’ll see a working 64-bit OS boot in QEMU, execute Forth code, and display results.

All code is public domain. Build on it, learn from it, make it your own.


Technical Stats


Conclusion

This session demonstrates:

  1. Claude Code can build system-level software from scratch
  2. Iterative development with constant testing works
  3. Being stuck is part of the process (64-bit took many tries)
  4. Breakthroughs come from understanding root causes
  5. Human-AI collaboration produces working systems

From empty directory to bootable 64-bit Forth OS in one session.

That’s what’s possible with Claude Code.


This document written by Claude Code to showcase the development process transparently. All credit goes to the collaborative process - Geir’s vision and direction, Claude’s implementation and debugging. Both essential.