summaryrefslogtreecommitdiff
path: root/x86-bare-metal/mbr-pure-zig/mbr.zig
blob: 666aa6bd7fed79b82c34ae0e4b7b06e5e451a02f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
const bios = @import("bios.zig");

// -- ENTRY POINT ---------------------------------------------------------------

export fn _entry() linksection(".boot") callconv(.naked) noreturn {
    asm volatile (
    // Disable interrupts.
        \\cli
        // Clear segment selectors.
        \\xor %%ax, %%ax
        \\mov %%ax, %%ds
        \\mov %%ax, %%es
        \\mov %%ax, %%ss
        \\mov %%ax, %%fs
        \\mov %%ax, %%gs
        \\mov $0x7c00, %sp
        // Long jump to set cs to 0x0000, as some BIOSes load the MBR
        // to either 07c0:0000 or 0000:7c000.
        \\ljmp $0x0, $main
    );
}

// -- MAIN ----------------------------------------------------------------------

// main should be "callconv(.naked)", once issue is fixed.
// https://github.com/ziglang/zig/issues/18183
export fn main() noreturn {
    for (0..255) |i| {
        if (i < bios.ROWS) {
            bios.set_pos(@truncate(i));
        } else {
            // Once we reach the bottom of the screen start scrolling.
            bios.scroll_up();
            bios.set_pos(bios.ROWS - 1);
        }
        puts_dec(i + 1);
        bios.putc(' ');
        puts_hex(i + 1);
        bios.putc(' ');
        bios.putc(@truncate('a' + (i % 26)));
        bios.putc(@truncate('a' + (i % 26)));
        waste_time();
    }
    puts("\ndone");

    while (true) {
        asm volatile ("hlt");
    }
}

// -- UTILS ---------------------------------------------------------------------

fn puts(str: []const u8) void {
    for (str) |c| {
        bios.putc(c);
    }
}

fn puts_dec(init_val: u32) void {
    var val = init_val;
    var buf: [32]u8 = undefined;
    var idx = buf.len;

    while (val > 0 and idx != 0) : (val /= 10) {
        idx -= 1;
        buf[idx] = @as(u8, @truncate(val % 10)) + '0';
    }
    puts(buf[idx..]);
}

fn puts_hex(init_val: u32) void {
    var val = init_val;
    var buf: [32]u8 = undefined;
    var idx = buf.len;

    while (val > 0 and idx != 0) : (val /= 16) {
        idx -= 1;
        const digit = switch (@as(u8, @truncate(val % 16))) {
            0...9 => |d| '0' + d,
            10...15 => |d| 'a' + d - 10,
            else => '?',
        };
        buf[idx] = digit;
    }
    puts(buf[idx..]);
}

fn rdtsc() u32 {
    return asm volatile ("rdtsc"
        : [eax] "={eax}" (-> u32),
        :
        : "edx"
    );
}

fn waste_time() void {
    const start = rdtsc();
    var now = start;
    var idx: usize = 0;
    while (now - start < (1 << 28)) : (idx += 1) {
        if (idx == 10_000) {
            now = rdtsc();
            idx = 0;
        }
    }
}