summaryrefslogtreecommitdiffstats
path: root/01_exercise/bootloader.c
blob: fcdc0f40b43b21f87af38851dfc1079f89795e14 (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
128
129
130
131
132
133
/* needs to stay the first line */
asm(".code16gcc;"
    "jmp $0, $main");

#define BLACK 0x0
#define BLUE 0x1
#define GREEN 0x2
#define CYAN 0x3
#define RED 0x4
#define MAGENTA 0x5
#define BROWN 0x6
#define L_GRAY 0x7
#define GRAY 0x8
#define L_BLUE 0x9
#define L_GREEN 0xA
#define L_CYAN 0xB
#define L_RED 0xC
#define L_MAGENTA 0xD
#define YELLOW 0xE
#define WHITE 0xF

#define BG_COLOR BLACK
#define FG_COLOR MAGENTA
#define COLOR(FG, BG) (BG << 4 | FG)
#define TEXT_COLOR COLOR(FG_COLOR, BG_COLOR)

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, %%ax;" // get cursor position
        "int $0x10;"
        "inc %%dl;" // raise column by one (normally we would need to check for end of
                    // line; respective end of screen here - for simplicity (and binary
                    // size) sake, leave that out - will cause bugs)
        "mov $0x0200, %%ax;" // 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() {
    // using Int10h AH=0E (teletype print char) here, because it does evaluate \n and \r
    // (and others)
    asm volatile("mov $0x0E0a, %%ax;" // \n
                 "int $0x10;"
                 "mov $0x0E0d, %%ax;" // \r
                 "int $0x10;" ::
                     : "ax");
}

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

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

        "mov %%al, %%bl;"    // temp store char in bl
        "mov $0x0400, %%ax;" // clear keyboard buffer:
        "int $0x16;"         // http://www.ctyme.com/intr/rb-1759.htm
        : "=b"(ret)
        :
        : "ax");
    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]; // 8 chars + \0
        unsigned char i = 0;
        for (; i < 8; i++) { // read at most 8 chars
            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;                           // buffer the char
            buf[i + 1] = 0;                       // set the following to \0
            putc_color('*', COLOR(GREEN, BLACK)); // hard coded green on black
        }

        if (i == 8) {
            while (getc() != '\r')
                ; // wait for enter press, ignoring other keys
        }

        new_line();
        print(buf);
        new_line();
    }
}