summaryrefslogblamecommitdiffstats
path: root/01_exercise/bootloader.c
blob: c11f37e361cc4fe220e7d77a008f0e74871a396b (plain) (tree)
1
2
3
4
                                  

                     
 















                     
 



                                            

                                     








                                                                                       
                                                                     



                                                   
 
 

                                          
                                       

     
 
                 






                                                                                        

 

             

                  
                         


                                                                     
 
                         
                                                                                    
                    
 


                                                                     

                   
                


               
                     
                                                                      


                                                 

 
                 





                                                             
 
               


                                                    


                                            


                                                                                       







                                                                                 



                          







                                                                              
         
 
                   
                   
                   
     
 
/* 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 - could 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();
    }
}