summaryrefslogtreecommitdiffhomepage
path: root/src/entry/bootsector.asm
blob: a98c22b9368f730a351cda73c38ae5758d77e1c5 (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
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
; MIT License, Copyright (c) 2021 Marvin Borner

; This file includes definitions to reduce magic numbers
%include "src/entry/definitions.asm"

bits 16 ; Real mode is 16 Bit
org LOCATION ; Bootsector location

global _start
_start:
	jmp .skip_bpb ; Some BIOSes override the BPB area
	nop
	times 87 db 0 ; Fill BPB area with 0
.skip_bpb:

	; Clear registers (some BIOSes are weird)
	xor bx, bx
	mov ds, bx
	mov es, bx
	mov ss, bx
	; Other registers may contain relevant data

	mov sp, LOCATION ; Set stack (top) pointer - grows downwards

	mov ax, SCREEN_CLEAR ; Clear screen command
	int SCREEN_INT ; CLEAR!

	; Print friendly welcome message
	mov si, hello_msg
	call print

	call disk_support ; Is disk supported? Implicitly verifies that this is a 386+ architecture
	mov esp, LOCATION ; Clear upper 16 Bit of esp, now that 16 Bit support is guaranteed
	cli ; Disable interrupts

	jmp load_stage ; Load and execute main stage

; Print function uses si as string pointer
print:
	; a and b regs get partially destroyed - push for restoration
	push bx
	push ax

	mov ah, SCREEN_OUT ; Set VGA command
	xor bh, bh ; Clear b register (according to BIOS spec)
.putch:
	lodsb ; Load next string byte (using ds:si)
	test al, al ; Test loaded byte
	jz .end ; End if al is zero (using previous test); NULL-termination
	int SCREEN_INT ; WRITE!
	jmp .putch ; Continue
.end:
	; Restore ax/bx
	pop ax
	pop bx
	ret

; Check if disk is supported using ID checks and LBA test
disk_support:
	; BIOS puts the disk id into dl
	cmp dl, 0x80
	jb disk_error ; Error if below 0x80 - probably floppy disk
	cmp dl, 0x8f
	ja disk_error ; Error if above 0x8f - invalid

	; Check if int 0x13 and LBA are supported
	mov ah, DISK_EXT_CHECK ; Set needed interrupt values
	mov bx, DISK_EXT_CHECK_REQ
	int DISK_INT ; CHECK!
	jc disk_error ; Carry means something went wrong
	cmp bx, DISK_EXT_CHECK_RESP
	jne disk_error ; Response is incorrect => error!
	ret

; Read sectors from disk using dap information
disk_read:
	mov si, dap
	mov ah, DISK_READ
	int DISK_INT
	jc error_loop
	ret

; Loads the main stage (main.c)
load_stage:
	mov bx, loader
	mov [dap.dest], bx
	call disk_read

	lgdt [gdt] ; Load GDT

	; Set protected mode (32 Bit mode)
	mov eax, cr0
	or ax, 1 ; Set PE (Protection Enable) Bit
	mov cr0, eax

	jmp 0x08:protected_mode

bits 32
protected_mode:
	; Set segment registers
	mov ax, 0x10
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax
	mov ss, ax

	call loader
	jmp error_loop
bits 16

; Error handling
error_loop:
	cli ; Disable interrupts
	hlt ; Wait for interrupt; should wait forever
	jmp error_loop ; Loop if returns anyway

disk_error:
	mov si, disk_msg
	call print
	jmp error_loop

; Messages (NULL-terminated strings with newline/return)
hello_msg db "Hello world! Booting...", NEWLINE, RETURN, NULL
disk_msg db "Disk is invalid or unsupported", NEWLINE, RETURN, NULL

; Disk configuration data for DISK_READ interrupt (filled according to spec)
dap: ; Disk Address Packet
	db 0x10 ; Disk address packet size
	db 0 ; Always 0
.count:
	dw 64 ; Number of sectors (512B each) to read ; 0x8000
.dest:
	dw 0 ; Destination offset
	dw 0 ; Destination segment
.lba:
	dd 1 ; LBA number ; Inter stage is directly after first LBA (0)
	dd 0 ; More storage bytes

; Global Descriptor Table (GDT)
; There are better explanations for the constants in definitions.asm
gdt:
	dw .size - 1 + 8 ; GDT size
	dd .start - 8 ; GDT start address
.start:
	; Code
	dw 0xffff ; Limit
	dw 0x0000 ; Base (low 16 bits)
	db 0x00 ; Base (mid 8 bits)
	db GDT_PRESENT | GDT_DESCRIPTOR | GDT_EXECUTABLE | GDT_READWRITE ; Access
	db GDT_GRANULARITY | GDT_SIZE | 0xf ; Granularity
	db 0x00 ; Base (high 8 bits)

	; Data
	dw 0xffff ; Limit
	dw 0x0000 ; Base (low 16 bits)
	db 0x00 ; Base (mid 8 bits)
	db GDT_PRESENT | GDT_DESCRIPTOR | GDT_READWRITE ; Access; Data isn't executable
	db GDT_GRANULARITY | GDT_SIZE | 0xf ; Granularity
	db 0x00 ; Base (high 8 bits)
.end:
.size: equ .end - .start

; The partition table gets inserted here (0x1b8 -> @440B)
times 0x1b8 - ($ - $$) db 0

times SECTOR_SIZE - 2 - ($ - $$) db 0 ; Fill until 512 (SECTOR_SIZE) - 2 bytes; -2 because of 2B signature
dw SECTOR_END_SIG ; Bootsector end signature

loader:
incbin "build/loader.bin"

; Limit to 0x8000 due to ext2 superblock start at 1024 on partition 1
times 0x8000 - ($ - $$) db 0
superblock: