aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/arch/x86/mbr/mbr.S
blob: b74bae57e6a5509d0b416125052ff55c1d1c9e57 (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
.code16
.intel_syntax noprefix

.section .boot, "ax", @progbits
    // Disable interrupts.
    cli

    // Clear segment selectors.
    xor ax, ax
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov fs, ax
    mov gs, ax

    // Set cs to 0x0000, as some BIOSes load the MBR to either 07c0:0000 or 0000:7c000.
    jmp 0x0000:entry_rm16

entry_rm16:
    // Set video mode 3, see [1].
    //   * 80x25 text mode
    //   * 640x200 pixel resolution (8x8 pixel per char)
    //   * 16 colors (4bit)
    //   * 4 pages
    //   * 0xB800 screen address
    //
    // [1] http://www.ctyme.com/intr/rb-0069.htm
    mov ax, 0x3
    int 0x10

    // Move cursor to second row.
    // http://www.ctyme.com/intr/rb-0087.htm
    mov ah, 0x02
    mov bh, 0  // page
    mov dh, 1  // row
    mov dl, 0  // col
    int 0x10

    // Clear direction flag for lodsb below.
    cld

    // Load pointer to msg_rm string (null terminated).
    lea si, [msg_rm]

    // Teletype output char at current cursor position.
    // http://www.ctyme.com/intr/rb-0106.htm
    mov ah, 0x0e
1:
    lodsb         // al <- ds:si ; si+=1 ; (al char to write)
    test al,al    // test for null terminator
    jz 2f
    int 0x10
    jmp 1b
2:

    // Enable A20 address line.
    in al, 0x92
    or al, 2
    out 0x92, al

    // Load GDT descriptor.
    lgdt [gdt_desc]

    // Enable protected mode (set CR0.PE bit).
    mov eax, cr0
    or  eax, (1 << 0)
    mov cr0, eax

    // Far jump which loads segment selector (0x0008) into cs.
    // 0x0008 -> RPL=0, TI=0(GDT), I=1
    jmp 0x0008:entry_pm32

.code32
entry_pm32:
    // Select data segment selector (0x0010) for ds.
    mov ax, gdt_data - gdt
    mov ds, ax

    // Write through VGA interface (video memory).
    // Each character is represented by 2 bytes.
    //   4 bit bg | 4 bit fg | 8 bit ascii char
    //
    // Start writing at third line.
    mov edi, 0xb8000 + (80 * 2 * 2) //

    lea esi, [msg_pm]
1:
    lodsb           // al <- ds:esi ; esi+=1
    test al, al     // test for null terminator
    jz 2f
    or eax, 0x1f00  // blue bg, white fg
    stosw           // ds:[edi] <- ax; edi+=2
    jmp 1b
2:
    hlt
    jmp 2b

// For simplicity keep data used by boot sector in the same section.
.balign 8
msg_rm:
    .asciz "Hello from Real Mode!"
msg_pm:
    .asciz "Hello from Protected Mode!"

.balign 8
gdt:
    .8byte 0x0000000000000000 // 0x00 | null descriptor
    .8byte 0x00cf9a000000ffff // 0x08 | 32 bit, code (rx), present, dpl=0, g=4K, base=0, limit=fffff
gdt_data:
    .8byte 0x00cf92000000ffff // 0x10 | 32 bit, data (rw), present, dpl=0, g=4K, base=0, limit=fffff
gdt_desc:
    .2byte .-gdt-1  // size
    .4byte gdt      // address

// Write MBR boot magic value.
.fill 510 - (. - .boot), 1, 0x00
.2byte 0xaa55