-
Notifications
You must be signed in to change notification settings - Fork 1
/
ntmio.sys.nas
393 lines (345 loc) · 9.71 KB
/
ntmio.sys.nas
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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
; Author: Bo Zhou (zb.public@outlook.com)
; Under GPLv2.0 lincense
[map symbols ntmio.sys.map]
%include "macro16.nas"
SP_TOP EQU 0x7c00 - 4 ; To define the address of top of the stack
START_MEM_ADDR EQU 0x07E00 ; The start address of the code in memory
NUM_OF_HEAD EQU 2
SECTORS_PER_TRACK EQU 18
SECTORS_PER_FAT EQU 9
SECTORS_FOR_ROOT_DIR EQU (224 * 32) / 512
BOOT_DRIVE EQU 0x00
KERNEL_FILE_BASE_MEM_ADDR EQU 0xa000
MBR_BASE_MEM_ADDR EQU 0x07C00
FAT_BASE_MEM_ADDR EQU 0x500
ROOT_DIR_BASE_MEM_ADDR EQU FAT_BASE_MEM_ADDR + (SECTORS_PER_FAT * 0x200)
ORG START_MEM_ADDR
XOR ax, ax
MOV ss, ax
MOV ds, ax
MOV es, ax
MOV sp, SP_TOP
MOV bx, os_hello_message
CALL display_message
reset_drive 0x00
; Load FAT section
load_fat:
MOV bx, FAT_BASE_MEM_ADDR
MOV cx, SECTORS_PER_FAT
MOV ax, 1
.load_fat_loop:
CMP cx, 0
JE load_root_dir
CALL read_sector
DEC cx
INC ax
ADD bx, 0x200 ; Advanced the address in BX by 512 bytes
jmp .load_fat_loop
load_root_dir:
; Load Root section
MOV bx, ROOT_DIR_BASE_MEM_ADDR
MOV cx, SECTORS_FOR_ROOT_DIR
MOV ax, 1 + (2 * SECTORS_PER_FAT)
.load_root_dir_loop:
cmp cx, 0
JE load_kernel_file
CALL read_sector
DEC cx
INC ax
ADD bx, 0x200
jmp .load_root_dir_loop
load_kernel_file:
XOR ax, ax
MOV dx, 224
MOV ax, ROOT_DIR_BASE_MEM_ADDR
.read_one_entry:
MOV si, ax
MOV di, kernel_filename
MOV cx, 11
REPE CMPSB
CMP cx, 0
JE get_kernel_start_cluster_id
ADD ax, 32
DEC dx
CMP dx, 0
JA .read_one_entry
MOV BX, error_not_kernel_message
CALL display_message
jmp final
get_kernel_start_cluster_id:
; Don't display, bug in display_message to overwrite the register
; MOV bx, kernel_file_found_message
; CALL display_message
MOV bp, ax
MOV bx, KERNEL_FILE_BASE_MEM_ADDR
MOV dx, WORD[bp+26] ; read start cluster id
MOV bp, FAT_BASE_MEM_ADDR
.load_kernel_to_mem_loop:
MOV ax, dx ; Copy start cluster id from DX to AX
ADD ax, 1 + 9 * 2 + (224 * 32) / 512 - 2 ; Calculate the real sector
CALL read_sector ; Read sector
MOV ax, dx ; Copy start cluster id from DX to AX
MOV cx, 12 ; 12bits per FAT entry
MUL cx ; 12bits per FAT entry
XOR dx, dx ; Clear DX for division
MOV cx, 8 ; 8 bit per byte
DIV cx ; AX: start bytes offset in FAT, DX: if not aligned, then the lower 4 bits
mov si, ax
MOV ax, word [bp + si]
MOV cl, dl
SHR ax, cl
AND ax, 0x0fff
CMP ax, 0x0fef
JA init_gdt
MOV dx, ax
ADD bx, 0x200
CMP bx, 0x0
JNE .load_kernel_to_mem_loop
; If BX = 0x0, then it means offset overflow, so need to advance the ES
PUSH ax
MOV ax, es
ADD ax, 0x1000
MOV es, ax
POP ax
JMP .load_kernel_to_mem_loop
init_gdt:
XOR ax, ax
MOV es, ax
MOV bx, kernel_file_load_complete
CALL display_message
; switch_vga_mode:
; Switch to 320x200x8 mode
; MOV al, 0x13
; MOV ah, 0x00
; INT 0x10
; MOV bx, switch_vga_mode_message
; CALL display_message
; Now the FAT table is not needed anymore
; Initialize the GDT
MOV ax, 0
MOV es, ax
MOV di, 0x800
; GDT #0, NULL descriptor
MOV cx, 4
REP STOSW
; GDT #1, code segment descriptor
MOV es:[di], WORD 0xffff
MOV es:[di+2], WORD 0x0000
MOV es:[di+4], BYTE 0x00
MOV es:[di+5], BYTE 0x9a
MOV es:[di+6], BYTE 0xcf
MOV es:[di+7], BYTE 0x00
add di, 8
; GDT #2, data segment descriptor
MOV es:[di], WORD 0xffff
MOV es:[di+2], WORD 0x0000
MOV es:[di+4], WORD 0x00
MOV es:[di+5], WORD 0x92
MOV es:[di+6], WORD 0xcf
MOV es:[di+7], WORD 0x00
LGDT [gdt]
init_idt:
CLI ; Disable interrupt as we will set an empty IDT so no code to handle the interrupt
LIDT [idt]
enable_a20:
; Fast A20 Method first
in al, 0x92
or al, 2
out 0x92, al
init_protect_mode:
; Enable the 32bit protection mode
MOV eax, cr0
or eax, 1
MOV cr0, eax
clean_prefetch_queue:
; First need to clean up the prefetch queue to remove any queued 16bit commands
jmp prepare_32bit_registers
nop
nop
nop
nop
prepare_32bit_registers:
mov ax, 0x10
mov ds, ax
MOV es, ax
MOV fs, ax
MOV gs, ax
MOV ss, ax
MOV esp, 0x2ffff
boot_to_kernel:
; 32bit long jump in 16bit mode
DB 0x66
DB 0xEA
DD KERNEL_FILE_BASE_MEM_ADDR
DW 0x0008
; Goodbyte BL2, now the kernel journey start!
final:
hlt
jmp final
; Display message function
; Parameters:
; - BX: The start index address of the message, the message shall be end with '\0'
; Return:
; - No return value
display_message:
pusha
MOV si, bx
.display:
MOV al, [si]
ADD si, 1
CMP al, 0
JE .return
MOV ah, 0x0e
MOV bx, 15
INT 0x10
JMP .display
.return:
popa
RET
display_char:
push bp
mov bp, sp
push ax
push bx
mov ax, word [bp + 4]
mov ah, 0x0e
mov bx, 15
int 10h
pop bx
pop ax
pop bp
ret
display_word:
push bp
mov bp, sp
push ax
push bx
push si
mov bx, word [bp + 4]
mov ax, bx
shr ax, 12
and ax, 0xF
MOV si, ax
MOV al, BYTE HEX_CHAR_SET[si]
XOR ah, ah
push ax
call display_char
pop ax
mov ax, bx
shr ax, 8
and ax, 0xF
mov si, ax
MOV al, BYTE HEX_CHAR_SET[si]
xor ah, ah
push ax
call display_char
pop ax
mov ax, bx,
shr ax, 4
and ax, 0xF
mov si, ax
MOV al, BYTE HEX_CHAR_SET[si]
xor ah, ah
push ax
call display_char
pop ax
mov ax, bx,
shr ax, 0
and ax, 0xF
mov si, ax
MOV al, BYTE HEX_CHAR_SET[si]
xor ah, ah
push ax
call display_char
pop ax
pop si
pop bx
pop ax
pop bp
ret
; Read a sector to target memory address
; Parameters:
; - AX: Sector LBA
; - ES:BX The target address
; Return:
; - No return value
read_sector:
push cx
xor cx, cx ; Set try count = 0
.read_a_sec:
push dx
push ax ; Store logical block
push cx ; Store try number
push bx ; Store data buffer offset
; Calculate cylinder, head and sector:
; Cylinder = (LBA / SectorsPerTrack) / NumHeads
; Sector = (LBA mod SectorsPerTrack) + 1
; Head = (LBA / SectorsPerTrack) mod NumHeads
mov bx, SECTORS_PER_TRACK ; Get sectors per track
xor dx, dx
div bx ; Divide (dx:ax/bx to ax,dx)
; Quotient (ax) = LBA / SectorsPerTrack
; Remainder (dx) = LBA mod SectorsPerTrack
inc dx ; Add 1 to remainder, since sector
mov cl, dl ; Store result in cl for int 13h call.
mov bx, NUM_OF_HEAD ; Get number of heads
xor dx, dx
div bx ; Divide (dx:ax/bx to ax,dx)
; Quotient (ax) = Cylinder
; Remainder (dx) = head
mov ch, al ; ch = cylinder
xchg dl, dh ; dh = head number
; Call interrupt 0x13, subfunction 2 to actually
; read the sector.
; al = number of sectors
; ah = subfunction 2
; cx = sector number
; dh = head number
; dl = drive number
; es:bx = data buffer
; If it fails, the carry flag will be set.
mov ax, 0x0201 ; Subfunction 2, read 1 sector
mov dl, BOOT_DRIVE ; from this drive
pop bx ; Restore data buffer offset.
int 0x13
jc .read_fail
; On success, return to caller.
pop cx ; Discard try number
pop ax ; Get logical block from stack
pop dx
pop cx
ret
; The read has failed.
; We will retry four times total, then jump to boot failure.
.read_fail:
pop cx ; Get try number
inc cx ; Next try
cmp cx, 3 ; Stop at 3 tries
je .boot_failure
; Reset the disk system:
xor ax, ax
int 0x13
; Get logical block from stack and retry.
pop ax
jmp .read_a_sec
.boot_failure:
MOV bx, error_message
CALL display_message
jmp final
BITS_PER_BYTE DB 8
BITS_PER_FAT_ENTRY DB 12
HEX_CHAR_SET DB '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
gdt DW 24
DD 0x800
idt DW 2048
DD 0x00
error_message DB "Failed to read disk", 0x0d, 0x0a, 0x00
error_not_kernel_message DB "Cannot find kernel file", 0x0d, 0x0a, 0x00
os_hello_message DB "Booting Netium OS...", 0x0d, 0x0a, 0x00
kernel_file_found_message DB "Find kernel file, start to load it...", 0x0d, 0x0a, 0x00
kernel_file_load_complete DB "Complete to load the kernel...", 0x0d, 0x0a, 0x00
inited_gdt_message DB "GDT initialized!", 0x0d, 0x0a, 0x00
inited_idt_message DB "IDT initialized!", 0x0d, 0x0a, 0x00
enabled_a20_message DB "A20 enabled!", 0x0d, 0x0a, 0x00
switch_vga_mode_message DB "Switch VGA mode!", 0x0d, 0x0a, 0x00
kernel_filename DB "KERNEL SYS"