alpaca2unix blog






    //    Declare constants for the multiboot header.
    .set ALIGN,         1<<0                    # align loaded modules on page boundaries
    .set MEMINFO,        1<<1                    # provide memory map
    .set FLAGS,            ALIGN | MEMINFO            # this is the multiboot 'flag' field
    .set MAGIC,            0x1BADB002                # 'magic number' lets bootloader find the header
    .set CHECKSUM,        -(MAGIC + FLAGS)        # checksum of above, to prove we are multiboot

    *    Declare a multiboot header that marks the program as a kernel.
    *    These are mafic Values that are documented in the multiboot standerd.
    *    The bootloader will search for this signature in the first 8KiB of the Kernel file, aligned at a 32-bit boundary.
    *    The signature is in its own section so the header can be forced to be within the first 8KiB of the Kernel file.

    .section         .multiboot
    .align            4
    .long            MAGIC
    .long            FLAGS
    .long            CHECKSUM

    *    The multiboot standard does not define the value of the stack pointer register (esp)
    *    and it is up to the kernel to provide a stack.
    *    This allocates room for a small stack by creating a symbol at the bottom of it,
    *    then allocating 16384bytes for it, and finaly creating a symbol at the top.
    *    The stack grows downwards on x86.
    *    The stack is its own section so it can be marked nobits,
    *    which means the kernel file is smaller because it does not contain an uninitialized stack.
    *    The stack on x86 must be 16-byte aligned according to the System V ABI and de-facto extentions.
    *    The compiler will assume the stack is properly aligned and failure to align the stack will result in undefined behavior.

    .section     .bss
    .align        16
    .skip        16384            # 16 KiB

    *    The linker scripts specifies _start as the entry point to the kernel and
    *    the bootloader will jump to this position once the kernel has been loaded.
    *    It desen't make sense to return from this function as the bootloader is gone.

    .section    .text
    .global        _start
    .type        _start, @function

    *    The bootloader has loaded us int 32-bit protected mode on a x86 machine.
    *    Interrupts ar disabled. Paging is disabled/
    *    The processor state is as defined in the multiboot standard.
    *    The kernel has full contorl of the CPU.
    *    The kernel can only make use of hardware features
    *    and any code it provides as part of itself.
    *    There's no printf function,
    *    unless the kernel provides its own <stdio.h> header and a printf implementation.
    *    There are no security restricions, no safeguards, no debugging mechanism,
    *    only what the kernel provides itself.
    *    It has absolute and complete power over the machine.
    *    To set up a stack, we set the esp register to point to the top of our stack
    *    (as it grows downwards on x86 systems).
    *    This is necessarily done in assembly as languages such as C cannot function without a stack.

    mov        $stack_top, %esp

    *    This is a good place to initialize crucial processor state before the high-level kernel is entered.
    *    It's best to minimize the earily environment where crucial features are offline.
    *    Note that the processor is not fully initialized set extensions are not initialized yet:
    *    Features such as floating point instructions and instruction set extensions are not initialized yet.
    *    The GDT should be loaded here.
    *    Paging should be enabled here.
    *    C++ features such as global constructors and exceptions will require runtime support to work as well.
    *    Enter the high-level kernel. The ABI requires the stack is 16-byte
    *    aligned at the time of call instruction (which afterwards pushes the return pointer of size 4 bytes).
    *    The stack was originally 16-byte aligned above and we've since pushed a multiple of 16 bytes to the stack since
    *    (pushed 0 bytes so far) and the alignment is thus preserved and the call is well defined.
    call kernel_main
    *    If the system has nothing more to do,
    *    put the computer into an infinite loop. to that:
    *    1) Disable interrupts with cli (clear interrupt enable in eflags)/
    *       They are already disabled by the bootloader, so this is not needed.    
    *       Mind that you might later enable interrupts and return from kernel_main (which is sort of nonsensical to do).
    *    2) Wait for the next interrupt to arrive with hlt (hlt instruction).
    *       Since they are disabled, this will lock up the computer.
    *    3) Jump to the hlt instruction if it ever wakes up due to a non-maskable interrupt occurring
    *       or due to system management mode.
1:    hlt
    jmp        1b
//    Set the size of the _start symbol to the current location '.' minus its start.
//    This is useful when debugging or when you implement call tracing
.size     _start, . - _start

# as boot.s -o boot.o



/* Surely you will remove the processor conditionals and this comment
   appropriately depending on whethre or not you use C++. */
#if    !defined(__cplusplus)
#include <stdbool.h>    /* C doesm't booleans by default. */
#include <stddef.h>
#include <stdint.h>

/* Check if the compiler thinks we are targeting the wrong operating system    */
#if defined(__linux__)
/* error "You are not using a cross-compiler, you will most certainly run into trouble" */

/* This tutorial will only work for the 32-bit ix86 targets. */
#if !defined(__i386__)
/* error "This tutorial needs to be compiled with a ix86-elf compiler" */

/* Hardware text mode color constants */
enum vga_color {
    VGA_COLOR_RED = 4,

static inline uint8_t vga_entry_color(enum vga_color fg, enum vga_color bg) {
    return fg | bg << 4;

static inline uint16_t vga_entry(unsigned char uc, uint8_t color) {
    return (uint16_t) uc | (uint16_t) color << 8;

size_t strlen(const char* str) {
    size_t len = 0;    
    while (str[len])
    return len;

static const size_t VGA_WIDTH = 80;
static const size_t VGA_HEIGHT = 25;

size_t terminal_row;
size_t terminal_column;
uint8_t terminal_color;
uint16_t* terminal_buffer;

void terminal_initialize(void) {
    terminal_row = 0;
    terminal_column = 0;
    terminal_color = vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK);
    terminal_buffer = (uint16_t*) 0xB8000;
    for (size_t y = 0; y < VGA_HEIGHT; y++) {
        for (size_t x = 0; x < VGA_WIDTH; x++) {
            const size_t index = y * VGA_WIDTH + x;
            terminal_buffer[index] = vga_entry(' ', terminal_color);
void terminal_setcolor(uint8_t color) {
    terminal_color = color;
void terminal_putentryat(char c, uint8_t color, size_t x, size_t y) {
    const size_t index = y * VGA_WIDTH + x;
    terminal_buffer[index] = vga_entry(c, color);
void terminal_putchar(char c) {
    terminal_putentryat(c, terminal_color, terminal_column, terminal_row);
    if (++terminal_column == VGA_WIDTH) {
        terminal_column = 0;
        if (++terminal_row == VGA_HEIGHT)
            terminal_row = 0;
void terminal_write(const char* data, size_t size) {
    for (size_t i = 0; i < size; i++)
void terminal_writestring(const char* data) {
    terminal_write(data, strlen(data));
#if defined(__cplusplus)
extern "C" /* Use C linkage for kernel_main. */
void kernel_main(void) {
    /* Initialize terminal interface */
    /* Newline support is left as an exercise. */
    terminal_writestring("Hello, kernel World!\n");

# gcc -c kernel.c -o kernel.o -std=gnu99 -ffreestanding -O2 -Wall -Wextra


kayo .~*