When writing code that runs without an operating system "bare-metal code", you're not dealing with the luxuries of system calls, drivers, or protected memory. You're dealing directly with the CPU and memory as they are. Understanding how the CPU actually behaves in this state,how it executes, loops, crashes, and responds to interrupts is foundational for developers and systems programmers.
Let’s break this down from the ground up.
Instruction Flow: The CPU Moves Forward By Default
At its simplest, the CPU is a machine that fetches instructions from memory and executes them, one after another. This sequential behavior is driven by a special register: the Program Counter (PC), or in x86, the Instruction Pointer (IP).
The Program Counter holds the address of the next instruction to execute. After executing an instruction, the CPU increases the PC to point to the following one. That’s how programs “move forward” .line by line, byte by byte.
Example in x86 assembly:
mov eax, 5
add eax, 2
After mov
, the PC is incremented, and it proceeds to add
. Unless told otherwise, the CPU just keeps marching forward.
Control Flow Is Yours: Loops, Jumps, Branches
But that "forward march" isn’t locked in. Assembly provides full control of program flow through jump instructions (jmp
), function calls (call
), loops (loop
, jnz
, etc.), and conditional branches.
For example, you can build a loop like this:
start:
dec eax
jnz start
This code counts down from whatever value is in eax
, repeatedly jumping back to start
until eax
hits zero. You’ve created a backward loop, manually. The CPU isn't "thinking" in directions it's just following whatever address it's told to fetch next.
Memory Isn’t Sequential: You Can Reuse and Overwrite Freely
The CPU also doesn't treat memory like a one-way street. It's just a flat space of addresses and you can write, erase, and jump around in it freely. For instance:
mov [0x1000], eax ; write eax to memory
mov eax, [0x1000] ; read it back
That’s direct memory access. No protection, no guards unless you write them.
You could even store instructions themselves in RAM and jump between them as needed. This lets you build dynamic code flows, jump tables, or even self-modifying code (if you're careful and a little daring).
Manual Loops Across Memory Locations
You don’t even have to put your code in one spot. Here’s an example of a manual loop jumping between non-sequential locations:
; Assume:
; 0x0000: mov eax, 5
; 0x0003: jmp 0x0010
; ...
; 0x0010: add eax, 2
; 0x0013: jmp 0x0000
In this flow:
- The CPU starts at
0x0000
, setseax
to 5, - Jumps to
0x0010
, adds 2 toeax
, - Jumps back to
0x0000
— and loops.
This kind of manual control over memory and instruction flow is exactly how low-level systems work .The CPU is simply obeying pointers and doing what it’s told.
Crashes and CPU Chaos
If you mess up and overwrite your instructions, jump to invalid memory, or create an infinite loop there’s no safety net. No OS to save you. The CPU may freeze, reboot, or enter undefined behavior.
Here’s an infinite loop that’ll run forever:
start:
jmp start
And a more useful loop:
mov al, 0
loop_start:
inc al
cmp al, 10
jne loop_start
hlt
This code counts from 0 to 9 and then halts the CPU. That hlt
instruction is essential otherwise the CPU would just keep reading random memory and likely crash.
The CPU Never Stops ,Unless You Tell It To!!
The CPU doesn’t know when to stop. It doesn’t assume your program ends after a few instructions. If you don’t explicitly stop it with hlt
or reset it via hardware, it just keeps going. It’ll execute whatever’s next — even if that means garbage data in RAM.
This is why real-mode programs and BIOS routines are so strict about handling “what’s next.” In the absence of guardrails, everything is your responsibility.
Interrupts Still there Even Without an OS
Let’s take a deeper dive into interrupts. Yes, even with no OS at all, the hardware like a keyboard or timer can still fire interrupts.
For instance:
- The keyboard fires IRQ1
- The system timer fires IRQ0
- These go to the Programmable Interrupt Controller (PIC)
- The PIC forwards them to the CPU
The CPU, on receiving an interrupt, checks its Interrupt Vector Table (IVT) to find the address of the relevant handler.
But here's the catch: if you haven’t set that handler up, the CPU jumps into random memory. That’s an instant crash.
BIOS: The Unsung Hero of Early Execution
During early boot, you're not completely alone.if BIOS is still active, it provides basic interrupt services. You can use them like functions:
mov ah, 0
int 16h ; BIOS keyboard input
These BIOS interrupts work only in real mode. As soon as you leave that mode or overwrite the handlers, you're on your own.
The Interrupt Vector Table (IVT): What It Is and Where It Lives
The IVT is a fixed table in low memory, from 0x0000
to 0x03FF
. It holds 256 entries, each 4 bytes (2 bytes offset + 2 bytes segment), mapping interrupt numbers to handler addresses.
So:
-
INT 0x09
→ CPU looks at0x0024
to0x0027
- Reads the pointer there
- Jumps to that address to run the keyboard handler
The BIOS sets this up on boot, giving you basic services. But if you're writing your own OS or bootloader, you'll need to set your own IVT and do it carefully.
RAM Is Empty at Power-On! So Where Does IVT Come From?
RAM is volatile,and it holds nothing on startup. So how does the IVT get there?
It’s written by the BIOS.
Here’s what happens at power-on:
- Power is turned on
- The CPU begins executing at address
0xFFFF0
— in ROM - BIOS code runs:
- Initializes RAM, screen, keyboard
- Writes the IVT into RAM
- Loads a bootloader (e.g., GRUB or your own code)
This boot process is precise and hardware specific. Without BIOS or UEFI, you’d have to manually write this entire sequence, including the IVT.
Disabling Interrupts With CLI
Until your IVT is ready, interrupts are dangerous. A timer or keyboard interrupt might fire and jump to uninitialized memory.
To prevent this, use:
CLI ; Clear Interrupt Flag
It tells the CPU: “Ignore all hardware interrupts for now.”
Later, once your handlers are in place, re-enable interrupts:
STI ; Set Interrupt Flag
This flow is critical when setting up your own OS kernel or bootloader interrupts must be carefully managed from the start.
You Can’t Use All of RAM Freely in Real Mode
The IVT sits at 0x0000 – 0x03FF
. That memory is off-limits unless you're replacing the table. Similarly, 0x0400 – 0x04FF
is the BIOS Data Area also reserved. And 0xA0000 – 0xBFFFF
is often used for video memory. Writing into these areas without knowing their purpose will break things fast.
Here's a rough map:
-
0x0000 – 0x03FF
: IVT -
0x0400 – 0x04FF
: BIOS Data Area -
0xA0000 – 0xBFFFF
: Video memory -
0xF0000 – 0xFFFFF
: BIOS ROM mapping
Only areas outside these regions are safe, unless of cpurse you know exactly what you’re doing.
Final Thoughts: The CPU Is Obedient, Not Intelligent
The CPU has no opinion. It doesn’t know what a keyboard is, or what a loop is, or that you're trying to build an operating system. It obeys instructions, fetches bytes, and jumps wherever you tell it even into chaos.
This is your playground.
When you write bare-metal code, you’re writing the rules of the world. You’re building not just a program, but a miniature reality that the CPU inhabits it and it goes on until you tell it to stop.
And that, right there, is the power of low-level programming.
Wrapping Up
Diving into bare-metal programming feels like stepping into a new universe one where you control every bit and byte. This direct control is both thrilling and challenging because there’s no OS to hold your hand.
If you're passionate about systems programming or OS development, you're not alone. I’m learning from scattered books, online resources, and AI assists.piecing it all together step by step. If there are passionate experts out there who care to offer guidance, share insight, or steer me in the right direction, feel free to reach out.
And if you spot mistakes, oversights, or have corrections to offer, I genuinely welcome them . drop them in the comments. I’m here to learn, explore, and build this world from the ground up.
References: OSDev.org
: Programming from the Ground Up by Jonathan Bartlett
Top comments (0)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.