<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Raymond Mwaura</title>
    <description>The latest articles on Forem by Raymond Mwaura (@raymondmwaura-dev).</description>
    <link>https://forem.com/raymondmwaura-dev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3568332%2Fa06d5217-181c-440b-b660-8558401ee10c.png</url>
      <title>Forem: Raymond Mwaura</title>
      <link>https://forem.com/raymondmwaura-dev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/raymondmwaura-dev"/>
    <language>en</language>
    <item>
      <title>Transitioning to Protected Mode in BeaconOS</title>
      <dc:creator>Raymond Mwaura</dc:creator>
      <pubDate>Thu, 06 Nov 2025 19:50:52 +0000</pubDate>
      <link>https://forem.com/raymondmwaura-dev/transitioning-to-protected-mode-in-beaconos-3pma</link>
      <guid>https://forem.com/raymondmwaura-dev/transitioning-to-protected-mode-in-beaconos-3pma</guid>
      <description>&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Date&lt;/strong&gt;: 2025-11-06&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Author&lt;/strong&gt;: &lt;a href="https://github.com/raymondmwaura-osdev" rel="noopener noreferrer"&gt;Raymond Mwaura&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the previous post, I explored enabling the A20 line to prepare the system for accessing memory beyond the 1 MB boundary. With that foundation in place, I moved on to something I’ve been eagerly anticipating: switching the CPU from &lt;strong&gt;real mode&lt;/strong&gt; to &lt;strong&gt;protected mode&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This post documents that process, explains what protected mode is, and walks through the implementation details and challenges I encountered while bringing BeaconOS into a 32-bit environment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding Protected Mode
&lt;/h2&gt;

&lt;p&gt;Protected mode was introduced with the Intel 80286 processor. It comes with several features missing in real mode:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;4 GB of addressable memory.&lt;/li&gt;
&lt;li&gt;Memory segmentation using the Global Descriptor Table (GDT).&lt;/li&gt;
&lt;li&gt;Virtual memory support through paging.&lt;/li&gt;
&lt;li&gt;Four privilege levels (0-3).&lt;/li&gt;
&lt;li&gt;Hardware-enforced isolation between user and kernel space.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Protected mode allows software to take full advantage of the CPU’s capabilities.&lt;/p&gt;

&lt;p&gt;It’s also worth noting that BIOS interrupts are not available in protected mode. I actually &lt;em&gt;like&lt;/em&gt; this for the exact reason many people don’t: it forces me to handle everything manually. That makes protected mode feel much more “low-level”; which is exactly what drew me to operating system development in the first place. I have a feeling this will be both fun and painful.&lt;/p&gt;

&lt;p&gt;Before switching modes, two conditions must be met:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;strong&gt;A20 line&lt;/strong&gt; must be enabled (covered in the previous post).
&lt;/li&gt;
&lt;li&gt;A proper &lt;strong&gt;Global Descriptor Table (GDT)&lt;/strong&gt; must be defined and loaded.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;Transitioning to protected mode involves several carefully ordered steps. Each must be executed correctly; a single mistake can trigger a triple fault and reset the system. Below is the process I used in BeaconOS, along with explanations for each stage.&lt;/p&gt;

&lt;p&gt;Throughout this process, interrupts were disabled (&lt;code&gt;cli&lt;/code&gt;) to prevent any control transfer during the transition.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Loading the Global Descriptor Table (GDT)
&lt;/h3&gt;

&lt;p&gt;Protected mode uses &lt;strong&gt;descriptors&lt;/strong&gt; to define memory segments, rather than fixed segment values. These descriptors are stored in the GDT.&lt;/p&gt;

&lt;p&gt;For my setup, I created a simple GDT with three entries:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Null Descriptor&lt;/strong&gt;; required placeholder.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kernel Code Segment Descriptor&lt;/strong&gt;; defines executable memory space.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kernel Data Segment Descriptor&lt;/strong&gt;; defines readable/writable memory space.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;; GDT
gdt_start:
    ; Null descriptor.
    dd 0
    dd 0

; Kernel code segment descriptor.
; Limit=0xFFFFF, Base=0.
; Access byte: present bit set, ring 0, code segment, read allowed.
; Flags: granularity is set, 32-bit segment.
kernel_code:
    dw 0xFFFF       ; Lower limit.
    dw 0            ; Base (0-15).
    db 0            ; Base (16-23).
    db 0b10011010   ; Access byte.
    db 0b11001111   ; Flag + higher limit.
    db 0            ; Base (24-31).

; Kernel data segment descriptor.
; Limit=0xFFFFF, Base=0.
; Access byte: present bit set, ring 0, data segment, write allowed.
; Flags: granularity is set, 32-bit segment.
kernel_data:
    dw 0xFFFF       ; Lower limit.
    dw 0            ; Base (0-15).
    db 0            ; Base (16-23).
    db 0b10010010   ; Access byte.
    db 0b11001111   ; Flag + higher limit.
    db 0            ; Base (24-31).

gdt_end:

gdt_descriptor:
    dw (gdt_end - gdt_start - 1)
    dd gdt_start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once defined, I loaded the GDT using the &lt;code&gt;lgdt&lt;/code&gt; instruction. This instruction loads the base address and limit of the GDT (Global Descriptor Table) from memory into the CPU's GDTR (Global Descriptor Table Register). This tells the CPU where the GDT is and how large it is.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lgdt [gdt_descriptor]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  2. Enabling Protected Mode
&lt;/h3&gt;

&lt;p&gt;With the GDT loaded, I set the &lt;strong&gt;Protection Enable (PE)&lt;/strong&gt; bit in the control register &lt;code&gt;CR0&lt;/code&gt;. This single bit flips the CPU from real mode to protected mode.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mov eax, cr0
or eax, 1
mov cr0, eax
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, simply setting the bit isn’t enough; the instruction prefetch queue must be flushed. To do that, I performed a &lt;strong&gt;far jump&lt;/strong&gt; to a 32-bit code segment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jmp 0x08:protected_mode_entry
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This jump clears the queue and loads the new code segment selector (&lt;code&gt;0x08&lt;/code&gt;, the kernel code segment) from the GDT into the &lt;code&gt;cs&lt;/code&gt; register. At this point, the CPU is officially running in protected mode.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Initializing and Testing Protected Mode
&lt;/h3&gt;

&lt;p&gt;At the &lt;code&gt;protected_mode_entry&lt;/code&gt; label, the CPU executes in 32-bit protected mode. The next step is to reload the segment registers (&lt;code&gt;ds&lt;/code&gt;, &lt;code&gt;es&lt;/code&gt;, &lt;code&gt;ss&lt;/code&gt;, etc.) to point to the data segment defined in the GDT.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bits 32
protected_mode_entry:
    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov gs, ax
    mov ss, ax
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I then tested the mode switch by printing a string. Since BIOS interrupts no longer work, I wrote directly to the VGA text buffer (&lt;code&gt;0xB8000&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;protected_mode_entry:
    ; After reloading the segment registers.

    ; Clear the screen.
    mov edi, 0xB8000    ; VGA text buffer.
    mov ecx, (80 * 25)  ; Counter.
    mov eax, 0x0720     ; 0x20 = ' ', 0x07 = light gray on black.
    rep stosw

    ; Print a message.
    lea esi, [success_msg]
    mov edi, 0xB8000
    mov ah, 0x07        ; Attribute: light gray on black.

    .print_loop:
        lodsb
        cmp al, 0
        jz done

        mov [edi], al       ; Write character.
        mov [edi + 1], ah   ; Write attribute.
        add edi, 2          ; Advance pointer.

        jmp .print_loop

done:
    hlt
    jmp done

;; VARIABLES.
success_msg db "Switched to protected mode successfully.", 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That message appearing on-screen confirmed that BeaconOS had successfully entered protected mode; easily one of the most satisfying milestones so far.&lt;/p&gt;




&lt;h3&gt;
  
  
  Full Code
&lt;/h3&gt;

&lt;p&gt;Here’s the full second-stage bootloader responsible for switching to protected mode.&lt;br&gt;
(Note: this assumes the A20 line is already enabled; QEMU does this by default.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;org 0x7E00
bits 16

cli

; Load the GDT.
lgdt [gdt_descriptor]

; Set PE bit.
mov eax, cr0
or eax, 1
mov cr0, eax

jmp 0x08:protected_mode_entry

; GDT
gdt_start:
    ; Null descriptor.
    dd 0
    dd 0

; Kernel code segment descriptor.
; Limit=0xFFFFF, Base=0.
; Access byte: present bit set, ring 0, code segment, read allowed.
; Flags: granularity is set, 32-bit segment.
kernel_code:
    dw 0xFFFF       ; Lower limit.
    dw 0            ; Base (0-15).
    db 0            ; Base (16-23).
    db 0b10011010   ; Access byte.
    db 0b11001111   ; Flag + higher limit.
    db 0            ; Base (24-31).

; Kernel data segment descriptor.
; Limit=0xFFFFF, Base=0.
; Access byte: present bit set, ring 0, data segment, write allowed.
; Flags: granularity is set, 32-bit segment.
kernel_data:
    dw 0xFFFF       ; Lower limit.
    dw 0            ; Base (0-15).
    db 0            ; Base (16-23).
    db 0b10010010   ; Access byte.
    db 0b11001111   ; Flag + higher limit.
    db 0            ; Base (24-31).

gdt_end:

gdt_descriptor:
    dw (gdt_end - gdt_start - 1)
    dd gdt_start

; ========== Protected Mode Code ==========
bits 32
protected_mode_entry:
    ; Setup segment registers.
    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov gs, ax
    mov ss, ax

    ; Clear the screen.
    mov edi, 0xB8000    ; VGA text buffer.
    mov ecx, (80 * 25)  ; Counter.
    mov eax, 0x0720     ; 0x20 = ' ', 0x07 = light gray on black.
    rep stosw

    ; Print a message.
    lea esi, [success_msg]
    mov edi, 0xB8000
    mov ah, 0x07        ; Attribute: light gray on black.

    .print_loop:
        lodsb
        cmp al, 0
        jz done

        mov [edi], al       ; Write character.
        mov [edi + 1], ah   ; Write attribute.
        add edi, 2          ; Advance pointer.

        jmp .print_loop

done:
    hlt
    jmp done

;; VARIABLES.
success_msg db "Switched to protected mode successfully.", 0

times 512 - ($ - $$) db 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Reflections
&lt;/h2&gt;

&lt;p&gt;Switching to protected mode felt like putting on the Infinity Gauntlet; moving from the cramped limitations of real mode into a true 32-bit environment with far more power and control. This step deepened my understanding of how an operating system seizes control of the hardware at its most fundamental level.&lt;/p&gt;

&lt;p&gt;It also reinforced one key lesson: &lt;strong&gt;precision is everything&lt;/strong&gt; in low-level programming. Every byte in the GDT, every instruction in the bootloader, and every bit in a control register matters. One wrong value can mean hours of painful debugging and questioning of life choices.&lt;/p&gt;




&lt;h2&gt;
  
  
  What’s Next
&lt;/h2&gt;

&lt;p&gt;Now that BeaconOS runs in protected mode, the next step is learning &lt;strong&gt;C&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I’ve decided to pause kernel development until I’ve strengthened my C programming skills. Since the BeaconOS kernel will be written primarily in C (with some assembly), mastering the language will make development cleaner, faster, and less error-prone. It’ll also prevent the kind of subtle bugs that come from half-understanding the fundamentals.&lt;/p&gt;

&lt;p&gt;I’m genuinely excited about this next phase. I can't wait to learn C and meet the infamous pointers everyone keeps warning me about.&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;The complete source code, along with development notes and comments, is available in the &lt;a href="https://github.com/TheOSDevLab/Crafting-an-OS-Notes-and-Insights/" rel="noopener noreferrer"&gt;this repository&lt;/a&gt;.&lt;/p&gt;




</description>
      <category>beaconos</category>
      <category>osdev</category>
      <category>protectedmode</category>
      <category>asm</category>
    </item>
    <item>
      <title>Enabling the A20 Line in BeaconOS</title>
      <dc:creator>Raymond Mwaura</dc:creator>
      <pubDate>Wed, 29 Oct 2025 11:39:18 +0000</pubDate>
      <link>https://forem.com/raymondmwaura-dev/enabling-the-a20-line-in-beaconos-27ei</link>
      <guid>https://forem.com/raymondmwaura-dev/enabling-the-a20-line-in-beaconos-27ei</guid>
      <description>&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Date&lt;/strong&gt;: 2025-10-29
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Author&lt;/strong&gt;: &lt;a href="https://github.com/raymondmwaura-osdev" rel="noopener noreferrer"&gt;Raymond Mwaura&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This past week, I have been working on &lt;strong&gt;enabling the A20 line&lt;/strong&gt;, as part of the early boot sequence for &lt;strong&gt;BeaconOS&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;A20 line&lt;/strong&gt;, or the &lt;strong&gt;21st address line&lt;/strong&gt; refers to a specific signal on the system bus that controls the CPU's ability to access memory beyond the first megabyte. Enabling the A20 line is essential because it allows the CPU to access memory beyond the 1MB limit.&lt;/p&gt;




&lt;h2&gt;
  
  
  Historical Context
&lt;/h2&gt;

&lt;p&gt;The Intel 8088 microprocessor had &lt;strong&gt;20 address lines&lt;/strong&gt; (A0-A19). This allowed it to access a maximum of 2²⁰ bytes, or &lt;strong&gt;1 MB of RAM&lt;/strong&gt;. The processor used a segmented memory model to form a 20-bit physical address from two 16-bit values (a segment and an offset), calculated as &lt;code&gt;(segment * 16) + offset&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Due to the 20-bit address bus, any attempt to access an address beyond 1 MB resulted in the 21st bit being silently truncated. For example, the address &lt;code&gt;FFFF:0010&lt;/code&gt; would be calculated as &lt;code&gt;0xFFFF0 + 0x0010 = 0x100000&lt;/code&gt;. However, since the 8088 only had 20 address lines, the &lt;code&gt;1&lt;/code&gt; at bit 20 was lost, causing the address to &lt;strong&gt;wrap around&lt;/strong&gt; to &lt;code&gt;0x00000&lt;/code&gt;. Some programmers, seeking performance optimizations, began to &lt;strong&gt;rely on this wrap-around behavior&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When IBM introduced the PC AT (1984) based on the Intel 80286, a significant problem emerged. The 286 had &lt;strong&gt;24 address lines&lt;/strong&gt;, enabling it to access up to &lt;strong&gt;16 MB of memory&lt;/strong&gt;. In its real mode (intended for 8086 compatibility), the 286 did not force the A20 line to zero. Consequently, addresses above 1 MB no longer wrapped around to zero. Programs that depended on the old wrap-around behavior would now access unexpected memory regions above 1 MB, causing them to &lt;strong&gt;malfunction or crash&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To maintain compatibility with 8086 software, IBM introduced a mechanism to forcibly control the A20 address line. This was the &lt;strong&gt;"A20 gate"&lt;/strong&gt; or &lt;strong&gt;"Gate A20"&lt;/strong&gt;. When this gate was &lt;strong&gt;disabled&lt;/strong&gt; (the default state at boot), the A20 line was forced to zero, mimicking the 8088's wrap-around behavior. When &lt;strong&gt;enabled&lt;/strong&gt;, the A20 line could carry its true signal, allowing the CPU to access the full range of physical addresses. The gate was originally implemented by routing the A20 line through an &lt;strong&gt;AND gate&lt;/strong&gt; controlled by a spare pin on the &lt;strong&gt;Intel 8042 keyboard controller&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Methods for Controlling the A20 Gate
&lt;/h2&gt;

&lt;p&gt;The primary method involved the &lt;strong&gt;Keyboard Controller&lt;/strong&gt;. In this, commands were sent to I/O ports &lt;code&gt;0x64&lt;/code&gt; and &lt;code&gt;0x60&lt;/code&gt; to set the A20 bit. This method was notoriously slow and required careful status polling.&lt;/p&gt;

&lt;p&gt;A faster alternative, the &lt;strong&gt;System Control Port A (or Fast A20 Gate)&lt;/strong&gt;, utilized I/O port &lt;code&gt;0x92&lt;/code&gt;. Setting bit 1 of this port would enable the A20 line. This method was significantly faster and simpler than the keyboard controller approach.&lt;/p&gt;

&lt;p&gt;A third approach was through a &lt;strong&gt;BIOS Interrupt&lt;/strong&gt;. By invoking the BIOS interrupt &lt;code&gt;INT 0x15&lt;/code&gt;, with &lt;code&gt;AX=0x2401&lt;/code&gt;, software could request the BIOS to enable the A20 line. This method was simple and abstracted the hardware details, but its major drawback was inconsistent support across different BIOS versions.&lt;/p&gt;

&lt;p&gt;Operating systems would attempt several methods to ensure successful activation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;To enable the A20 line, I implemented two common methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;BIOS Interrupt (INT 15h, Function 24h)&lt;/li&gt;
&lt;li&gt;Fast A20 Gate (port &lt;code&gt;0x92&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The program begins by checking whether the A20 line is already active. If it is, execution ends immediately with a success message. If not, the system first attempts to enable it using the BIOS interrupt. Should that fail (likely due to BIOS incompatibility), the program falls back to the fast A20 gate method.&lt;/p&gt;

&lt;p&gt;If all attempts fail, the program halts and reports the failure. The logic ensures graceful fallback and clear reporting at each stage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;; Test A20.
call test_a20
cmp al, 1
je  a20_enabled ; If enabled.
jmp bios_enable ; If disabled.

bios_enable:
    lea si, [bios_attempt_msg]
    call print_string

    mov ah, 0x24
    mov al, 0x01
    int 0x15
    jc fast_a20_enable

    call test_a20
    cmp al, 1
    je a20_enabled
    jmp fast_a20_enable

fast_a20_enable:
    lea si, [fast_a20_attempt_msg]
    call print_string

    in al, 0x92
    or al, 0x02
    out 0x92, al

    call test_a20
    cmp al, 1
    je a20_enabled
    jmp a20_disabled

a20_enabled:
    lea si, [a20_enabled_msg]
    call print_string
    jmp done

a20_disabled:
    lea si, [a20_disabled_msg]
    call print_string
    jmp done

done:
    cli
    hlt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Initial check&lt;/strong&gt;:&lt;br&gt;
The program begins by calling &lt;code&gt;test_a20&lt;/code&gt; to verify whether the A20 line is already enabled. If it is, execution jumps directly to &lt;code&gt;a20_enabled&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;BIOS Method&lt;/strong&gt;:&lt;br&gt;
If the A20 line is disabled, the BIOS interrupt &lt;code&gt;INT 15h&lt;/code&gt; (Function 24h, Subfunction 01h) is invoked to enable it. Some BIOS implementations provide this service, but many modern systems do not.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fallback to Fast A20 Gate&lt;/strong&gt;:&lt;br&gt;
If the BIOS call fails (as indicated by the Carry Flag) or if the test still shows A20 is disabled, the program attempts the fast A20 gate method. This method writes to I/O port &lt;code&gt;0x92&lt;/code&gt;, setting bit 1 to enable the A20 line directly via the system control port.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Final verification&lt;/strong&gt;:&lt;br&gt;
After each enabling attempt, &lt;code&gt;test_a20&lt;/code&gt; is called again to confirm success. The program prints a message indicating the result and halts cleanly.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This dual-method approach increases compatibility across different hardware and firmware environments, ensuring the A20 line is enabled regardless of BIOS support.&lt;/p&gt;




&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;To verify whether the A20 line is enabled, I used a helper function, &lt;code&gt;test_a20&lt;/code&gt;. It performs the check using two methods: a &lt;strong&gt;BIOS test&lt;/strong&gt; and a &lt;strong&gt;memory wraparound test&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;; ---------------------------------------------------------------
; Test if A20 is enabled or disabled using either
; BIOS subfunction 02h, or
; memory wraparound method.
; Return value: Places the current A20 state in the AL register.
;   1: Enabled
;   2: Disabled
; ---------------------------------------------------------------
test_a20:
    call bios_test_a20
    cmp al, 2
    jb .test_return

    call memory_test_a20
    jmp .test_return

    .test_return:
        ret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;BIOS test&lt;/strong&gt; (INT 15h, Function 24h, Subfunction 02h) checks the A20 state directly if the BIOS supports it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;; ---------------------------------------------------------------
; Test if A20 is enabled using BIOS subfunction 02h.
; Return value: Places the current A20 state in the AL register.
;   0: Disabled
;   1: Enabled
;   2: Error
; ---------------------------------------------------------------
bios_test_a20:
    mov ah, 0x24
    mov al, 0x02
    int 0x15
    jc .bios_test_fail

    cmp al, 1
    je .bios_a20_enabled
    jmp .bios_a20_disabled

    .bios_a20_enabled:
        mov al, 1
        jmp .bios_test_return

    .bios_a20_disabled:
        mov al, 0
        jmp .bios_test_return

    .bios_test_fail:
        mov al, 2
        jmp .bios_test_return

    .bios_test_return:
        ret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the BIOS does not support this interrupt, the function falls back to the &lt;strong&gt;memory wraparound test&lt;/strong&gt;, which is more hardware-oriented and universally reliable.&lt;/p&gt;

&lt;p&gt;This method compares memory contents at two addresses: &lt;code&gt;0x000000&lt;/code&gt; and &lt;code&gt;0x100000&lt;/code&gt;.&lt;br&gt;
If the A20 line is &lt;strong&gt;disabled&lt;/strong&gt;, both addresses point to the same physical location (due to 20-bit address wraparound), and the values read back are identical.&lt;br&gt;
If the A20 line is &lt;strong&gt;enabled&lt;/strong&gt;, they reference different memory cells, producing distinct values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;; ------------------------------------------------------------------------
; Test whether A20 is enabled or disabled using memory wraparound method.
; Return value: Places the current A20 state in the AL register.
;   1: Enabled
;   2: Disabled
; ------------------------------------------------------------------------
memory_test_a20:
    push ds
    push es
    push si
    push di

    mov ax, 0x0000
    mov ds, ax
    mov si, ax

    mov ax, 0xFFFF
    mov es, ax
    mov di, 0x0010

    ; Preserve original values.
    mov al, [ds:si]
    push ax
    mov al, [es:di]
    push ax

    ; Write.
    mov byte [es:di], 0x00
    mov byte [ds:si], 0xFF

    ; Read.
    mov al, [es:di]
    cmp al, 0xFF
    je .memory_a20_disabled
    jmp .memory_a20_enabled

    .memory_a20_enabled:
        mov al, 1
        jmp .memory_test_ret

    .memory_a20_disabled:
        mov al, 0
        jmp .memory_test_ret

    .memory_test_ret:
        ; Restore original values.
        pop bx
        mov [es:di], bl
        pop bx
        mov [ds:si], bl

        pop di
        pop si
        pop es
        pop ds

        ret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;BIOS Test:&lt;/strong&gt;&lt;br&gt;
Uses &lt;code&gt;INT 15h, AX=2402h&lt;/code&gt; to query the BIOS for the A20 state. If supported, this method is simple and direct. If unsupported, the Carry Flag is set, and the test returns an error code (&lt;code&gt;AL=2&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Memory Wraparound Test:&lt;/strong&gt;&lt;br&gt;
Performs a direct hardware-level test by writing and comparing memory values.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;If values at &lt;code&gt;0x000000&lt;/code&gt; and &lt;code&gt;0x100000&lt;/code&gt; are identical, the A20 line is disabled (address wraparound occurred).&lt;/li&gt;
&lt;li&gt;If they differ, the A20 line is enabled.
 After testing, the function restores the original memory contents to prevent corruption.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Return Values:&lt;/strong&gt;
The final A20 state is stored in the &lt;code&gt;AL&lt;/code&gt; register:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;1&lt;/code&gt;; Enabled&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;0&lt;/code&gt;; Disabled&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;2&lt;/code&gt;; Error or unsupported BIOS call&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This layered verification ensures reliability across systems and helps confirm that the A20 line is properly active.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion and Next Step
&lt;/h2&gt;

&lt;p&gt;Enabling the A20 line marks a significant milestone in the early development of &lt;strong&gt;BeaconOS&lt;/strong&gt;. It sets the stage for the &lt;strong&gt;transition to protected mode&lt;/strong&gt;; a part of the journey I've been anticipating since the start. I'm excited to finally take that step and see BeaconOS come to life in a more capable environment.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The complete source code and accompanying notes are available in &lt;a href="https://github.com/TheOSDevLab/Crafting-an-OS-Notes-and-Insights" rel="noopener noreferrer"&gt;this repository&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




</description>
      <category>osdev</category>
      <category>beaconos</category>
      <category>a20line</category>
      <category>asm</category>
    </item>
    <item>
      <title>Building BeaconOS From Scratch</title>
      <dc:creator>Raymond Mwaura</dc:creator>
      <pubDate>Wed, 22 Oct 2025 15:48:13 +0000</pubDate>
      <link>https://forem.com/raymondmwaura-dev/building-beaconos-from-scratch-3hg7</link>
      <guid>https://forem.com/raymondmwaura-dev/building-beaconos-from-scratch-3hg7</guid>
      <description>&lt;p&gt;Hi there! My name is &lt;strong&gt;Raymond Mwaura&lt;/strong&gt;, and I’m deeply passionate about &lt;strong&gt;low-level programming&lt;/strong&gt;. My main focus is &lt;strong&gt;Operating System development&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For the past &lt;strong&gt;five months&lt;/strong&gt;, I’ve been steadily learning the fundamentals of how computers really work beneath the surface. During this time, I’ve explored topics such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Computer architecture&lt;/strong&gt; and how hardware components interact.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Number systems&lt;/strong&gt; (decimal, hexadecimal, and binary) and how they relate to data representation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assembly language&lt;/strong&gt;, learning syntax, instructions, and how to write efficient low-level routines.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BIOS interrupts&lt;/strong&gt;, understanding how early system software communicates with hardware.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;real mode memory model&lt;/strong&gt;, and how it shapes the earliest stages of system execution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bootloader development&lt;/strong&gt;, including how to load a second-stage bootloader into memory.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Right now, I’m working on &lt;strong&gt;enabling the A20 line&lt;/strong&gt;; a crucial step that allows access to memory beyond the 1MB boundary on x86 systems. Once that’s working, my next milestone is &lt;strong&gt;switching to protected mode&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I’m Building an Operating System
&lt;/h2&gt;

&lt;p&gt;I’m developing an educational OS called &lt;strong&gt;BeaconOS&lt;/strong&gt;. The name &lt;em&gt;Beacon&lt;/em&gt; represents guidance; a light for learners navigating the often complex and confusing world of operating system development. My goal is for BeaconOS to become a practical learning tool that helps others understand how every part of an operating system works.&lt;/p&gt;




&lt;h2&gt;
  
  
  Looking Ahead
&lt;/h2&gt;

&lt;p&gt;This blog will document my journey; the progress, the mistakes, and the lessons learned along the way. I’ll be sharing insights, technical deep dives, and reflections as BeaconOS evolves.&lt;/p&gt;

&lt;p&gt;If you’re interested in &lt;strong&gt;systems programming&lt;/strong&gt;, &lt;strong&gt;low-level computing&lt;/strong&gt;, or just curious about how operating systems are built, I’d love to have you along for the ride.&lt;/p&gt;

&lt;p&gt;Welcome to my journey; and to the story of &lt;strong&gt;BeaconOS&lt;/strong&gt;.&lt;/p&gt;




</description>
      <category>sideprojects</category>
      <category>showdev</category>
      <category>computerscience</category>
      <category>devjournal</category>
    </item>
  </channel>
</rss>
