-
Notifications
You must be signed in to change notification settings - Fork 0
/
boot.asm
180 lines (148 loc) · 3.56 KB
/
boot.asm
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
[bits 16]
[org 0x7c00]
boot:
mov [boot_drive], dl
mov si, beep_msg
call print
call load_sector_two
call is_a20_disabled
jne .a20_enabled
mov si, a20_disabled_msg
call print
call enable_a20
call is_a20_disabled
jne .a20_enabled
mov si, unable_to_enable_a20_msg
call print
jmp $
.a20_enabled:
mov si, a20_enabled_msg
call print
; A20 enabled, proceed towards protected mode.
cli
xor ax, ax
mov ds, ax ; Clear ds for lgdt.
lgdt [gdt_descriptor]
; Set protected mode enabled control register bit.
mov eax, cr0
or eax, 1
mov cr0, eax
jmp 0x08:protected_start
; output string pointed to by si
print:
mov ah, 0x0e ; teletype output
xor bh, bh ; page 0
.print_next:
lodsb ; next character
cmp al, 0
je .print_done
int 10h
jmp .print_next
.print_done:
ret
; The check is done by storing a value in an address and
; checking if the address 1MiB higher was written to.
is_a20_disabled:
xor ax, ax
mov fs, ax
not ax
mov gs, ax
mov di, 0x0600
mov si, 0x0610
mov byte [fs:di], 0x00
mov byte [gs:si], 0xFF
cmp byte [fs:di], 0xFF
ret
; Attempts to enable the A20 line using the fast A20 gate method.
; This isn't the most portable method, but it is the most modern
; and convenient one, and it works on VirtualBox where I tested this.
enable_a20:
in al, 0x92
or al, 2
out 0x92, al
ret
load_sector_two:
mov ah, 2 ; read mode
mov bx, 0x7e00 ; destination
mov al, 15 ; sectors
mov cl, 2 ; sector
mov dl, [boot_drive] ; disk
mov ch, 0 ; cylinder
mov dh, 0 ; head
int 0x13
ret
; Set up the global descriptor table, describing the memory segments.
; See https://wiki.osdev.org/GDT for info about the structure of a
; single descriptor.
gdt:
gdt_null:
dd 0
dd 0
gdt_code_segment:
dw 0xFFFF ; limit (0:15)
dw 0 ; base (0:15)
db 0 ; base (16:23)
db 10011010b ; access byte
db 11001100b ; limit (16:19), flags
db 0 ; base (part 4)
gdt_data_segment:
dw 0xFFFF
dw 0
db 0
db 10010010b
db 11001100b
db 0
gdt_end:
gdt_descriptor:
dw gdt_end - gdt - 1
dd gdt
[bits 32]
protected_start:
; Set up segment registers.
mov ax, 10h ; Data segment offset in the GDT.
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov esp, 0x80000
call vga_clear
mov si, protected_mode_msg
mov ah, 0x1f
mov edi, 0
call vga_print
mov si, protected_mode_msg
mov ah, 0x2a
mov edi, 2
call vga_print
call 0x7e00
mov si, protected_mode_msg
mov ah, 0xdb
mov edi, 4
call vga_print
vga_clear:
mov edi, 0xb8000
mov ecx, 80*25
rep stosw
ret
; Output string pointed to by SI with the color AH on the row EDI.
; Only prints the string at col 0 right now, and doesn't handle newlines.
vga_print:
imul edi, 80*2 ; 80 col rows, 2 bytes per character.
add edi, 0xb8000
.vga_print_next:
lodsb
cmp al, 0
je .vga_print_done
stosw
jmp .vga_print_next
.vga_print_done:
ret
boot_drive db 0
beep_msg db 'beep beep boop!', 0x0a, 0x0d, 0
a20_enabled_msg db 'a20 enabled!', 0x0a, 0x0d, 0
a20_disabled_msg db 'a20 disabled!', 0x0a, 0x0d, 0
unable_to_enable_a20_msg db 'unable to enable a20!', 0x0a, 0x0d, 0
protected_mode_msg db 'beep beep boop from protected mode!', 0
times 510-($-$$) db 0
dw 0xaa55