summaryrefslogtreecommitdiffstats
path: root/01_exercise/bootloader.c
blob: 0500ce42d678e5e8e7b6cdfc497e8c76e4657773 (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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/* needs to stay the first line */
asm(".code16gcc;"
    "jmp $0, $main");

/*
 * 0 black
 * 1 blue
 * 2 green
 * 3 cyan
 * 4 red
 * 5 magenta
 * 6 brown
 * 7 light gray
 * 8 dark gray
 * 9 light blue
 * A light green
 * B light cyan
 * C light red
 * D light magenta
 * E yellow
 * F white
 */

#define BG_COLOR 0x0
#define FG_COLOR 0x4
#define TEXT_COLOR (BG_COLOR << 4 | FG_COLOR)

void putc(char c) {
    asm volatile("mov $0x0E, %%ah;"
                 "int $0x10;" ::"al"(c));
}

void putc_color(char c, char color) {
    asm volatile("mov $0x09, %%ah;"    // using Int 10h AH=09 for attributes
                 "and $0x00FF, %%bx;"  // erase higher bits of bx
                 "mov $0x0001, %%cx;"  // write the char once
                 "int $0x10;"          // we need to manually move the cursor
                 "mov $0x0300, %%eax;" // get cursor position
                 "int $0x10;"
                 "add $0x1, %%dl;"     // raise column by one
                 "mov $0x0200, %%eax;" // set cursor position
                 "int $0x10;" ::"al"(c),
                 "bl"(color)
                 : "cx", "dx");
}

void print(char const *const str) {
    for (int i = 0; str[i] != '\0'; ++i) {
        putc_color(str[i], TEXT_COLOR);
    }
}

void new_line() {
    putc('\n');
    putc('\r');
}

char getc() {
    char ret;
    asm volatile(
        ".no_key:"
        "xor %%eax, %%eax;"
        "mov $0x0100, %%ax;" // check for keystroke:
        "int $0x16;"         // http://www.ctyme.com/intr/rb-1755.htm
        "jz .no_key;"        // wait for a key
        ".done:"

        "xor %%eax, %%eax;"
        "mov $0x1000, %%ax;" // get keystroke: http://www.ctyme.com/intr/rb-1754.htm
        "int $0x16;"

        "mov %%al, %%bl;"  // temp store char in bl
        "mov $0400, %%ax;" // clear keyboard buffer:
        "int $0x16;"       // http://www.ctyme.com/intr/rb-1759.htm
        : "=b"(ret)
        :
        : "eax");
    return ret;
}

void sleep(long ys) {
    // http://www.ctyme.com/intr/rb-1525.htm : Int 15/AH=86h bios wait
    asm volatile("mov $0x8600, %%ax;"
                 "int $0x15;" ::"cx"((ys >> 16)),
                 "dx"(ys));
}

void main(void) {
    print("Hello!");
    new_line();

    asm volatile("mov $0x00A2, %%ax;" // switch to video mode
                 "int $0x10" ::
                     : "ax");

    while (1) {
        char buf[9];
        for (unsigned char i = 0; i < 8; i++) {
            char c = getc();
            if (c == '\r') {
                if (i == 0) { // empty entry
                    print("Reboot!");
                    sleep(500000); // sleep 500ms, to see the reboot print out, even if
                                   // short
                    asm volatile(
                        "cli;"              // disable interrupts
                        "mov $0x0FE, %%al;" // tell the keyboard controller to...
                        "out %%al, $0x64;"  // ...activate the CPU reset line
                        ".halt:"
                        "hlt;"          // some motherboards require this
                        "jmp .halt;" :: // and again, just to be sure
                        : "al");
                } else {
                    break;
                }
            }
            buf[i] = c;
            buf[i + 1] = 0;
            putc_color('*', 0x02); // hard coded green on black
        }
        while (getc() != '\r')
            ;
        new_line();
        print(buf);
        new_line();
    }
}