本篇主要將boot程序所做的工作做一筆記。
先看代碼吧:
1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2 ; 該文件主要用于加載loader和kernel入內存,其中默認loader和kernel是被壓縮成System.Image
3 ; 文件存放到軟盤的第二個扇區起始的地方,loader最大大小不應超過128KB,loader+kernel不應
4 ; 超過512KB - 32KB = 480KB大小,否則系統將崩潰。
5 ;
6 ; Author :
7 ; v 0.01 2011/11/22
8 ;
9 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
10 org 0x7c00
11 jmp start
12
13 ; loader和kernel映像一起被加載到
14 ; 內存段基址:0x8000 (* 0x10)
15 ; 即在1MB尋址范圍的正中間
16 ; 將來還會從0xf0000地址起始處放置一些系統參數
17 ; 因此loader+kernel大小不應超過512KB - 32KB = 480KB,如果
18 ; 超過這個數字那么地址將回滾,系統崩潰
19 LOADER_ADDR equ 0x8000
20
21 start:
22 mov ax, cs
23 mov ds, ax
24 mov es, ax
25 mov ss, ax
26 mov sp, 0x7b00 ; 棧頂為0x7b00,向低地址延伸;其實boot中我們沒有用到棧,因此不需設置堆棧
27
28 ; 清屏
29 mov ax, 0x0600
30 mov bx, 0x0700
31 mov cx, 0
32 mov dx, 0x184f
33 int 0x10
34
35 ; 設置光標位置
36 mov ah, 0x02
37 mov bh, 0x00
38 mov dx, 0x0
39 int 0x10
40
41 get_disk_param:
42 mov dl, 0x00 ; 讀軟盤
43 mov ax, 0x0800 ; 獲取磁盤參數
44 int 0x13
45 jnc goon_get_disk_param ; 如果讀磁盤驅動器參數成功則保存相關參數
46 mov dx, 0x0000 ; 出錯則將磁盤復位然后無限循環讀
47 mov ax, 0x0000
48 int 0x13
49 jmp get_disk_param ; 出錯則將磁盤復位然后無限循環讀
50
51 goon_get_disk_param:
52 mov bl, cl
53 and bl, 0x3f
54 mov byte [SECTOR_PER_TRACK], bl ; SECTOR_PER_TRACK保存每磁道最大扇區數
55 shr cx, 6
56 mov word [TRACK_NUM], cx ; TRACK_NUM保存最大磁道號
57
58 ; 獲取當前光標位置
59 mov ah, 0x03
60 xor bh, bh
61 int 0x10
62
63 ; 打印一些信息
64 mov ax, cs
65 mov es, ax
66 mov cx, MSG1_LEN
67 mov bx, 0x0007
68 mov bp, MSG1 ; 顯示字符串"Loading System
"
69 mov ax, 0x1301
70 int 0x10
71
72 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
73 ; 加載loader和kernel文件,存放位置在0x80000起始的地方
74 ; loader+kernel大小不應超過480KB,否則由于在實模式下僅有
75 ; 1MB的尋址能力,loader+kernel的存放空間將超過0x80000~0xf0000的大小
76 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
77
78 load_loader_and_kernel:
79 mov ax, 0x00
80 mov byte al, [LOADER_LEN]
81 add word ax, [KERNEL_LEN]
82 mov word [CUR_UNREAD_SECTOR], ax ; 初始化當前未加載的扇區數
83 mov ax, LOADER_ADDR
84 mov es, ax
85 mov bx, 0x00 ; es:bx指向loader+kernel映像加載的位置:0x8000 * 0x10 + 0x0
86
87 read_one_track:
88 mov word ax, [CUR_TRACK]
89 mov ch, al ; 當前讀到的磁道號的低8位
90 shl ah, 0x06
91 mov cl, ah
92 mov byte al, [CUR_READ]
93 inc al
94 or cl, al ; cl寄存器高兩位為當前讀到的磁道號的高兩位,低六位為開始扇區號
95 mov byte dh, [CUR_HEAD] ; 當前的磁頭號
96 mov dl, 0x00 ; 驅動器號為0,代表是軟盤
97 mov ah, 0x02 ; 讀扇區
98 mov byte al, [SECTOR_PER_TRACK]
99 sub byte al, [CUR_READ] ; 要讀取的扇區數量
100 int 0x13
101 mov byte cl, [SECTOR_PER_TRACK]
102 sub byte cl, [CUR_READ]
103 cmp al, cl
104 jne die ; 如果實際讀出的扇區數比所請求的扇區數少,則報錯
105 xor ah, ah
106 sub word [CUR_UNREAD_SECTOR], ax ; 修改當前剩余的未讀扇區數
107 jc finish_loading
108 cmp word [CUR_UNREAD_SECTOR], 0x00
109 je finish_loading ; 如果已經讀完loader和kernel,則跳出
110 shl ax, 0x09 ; al * 512代表本次讀出的字節數
111 add bx, ax
112 jno no_overflow
113 mov cx, es
114 add cx, 0x1000
115 cmp cx, 0xf000
116 ja die ; es已經超過了1MB的界限,說明loader+kernel過大
117 mov es, cx ; 修改es:bx緩沖區地址
118 no_overflow:
119 mov byte [CUR_READ], 0x0
120 cmp byte [CUR_HEAD], 0x0
121 je read_head_1
122 add byte [CUR_TRACK], 0x01
123 mov word ax, [TRACK_NUM]
124 cmp word [CUR_TRACK], ax
125 ja die
126 read_head_1:
127 add byte [CUR_HEAD], 0x01
128 and byte [CUR_HEAD], 0x01 ; 磁頭號由0轉為1,或者由1轉為0
129 jmp read_one_track
130
131 die:
132 ; 獲取當前光標位置
133 mov ah, 0x03
134 xor bh, bh
135 int 0x10
136
137 ; 打印一些信息
138 mov ax, cs
139 mov es, ax
140 mov cx, MSG2_LEN
141 mov bx, 0x0007
142 mov bp, MSG2 ; 顯示字符串"The loader or kernel is too long
"
143 mov ax, 0x1301
144 int 0x10
145
146 xor ax, ax
147 int 0x16
148 int 0x19
149 jmp $
150
151 finish_loading:
152 ; 關閉軟驅馬達
153 mov dx, 0x03f2
154 mov al, 0
155 out dx, al
156 nop
157
158 ; 獲取當前光標位置
159 mov ah, 0x03
160 xor bh, bh
161 int 0x10
162
163 ; 打印一些信息
164 mov ax, cs
165 mov es, ax
166 mov cx, MSG3_LEN
167 mov bx, 0x0007
168 mov bp, MSG3 ; 顯示字符串"Success to load the loader and kernel\n\nEntering loader
"
169 mov ax, 0x1301
170 int 0x10
171
172 jmp LOADER_ADDR:0 ; 跳轉到loader去執行
173
174 ; 要顯示的字符串
175 MSG1: db 13, 10, "Loading loader and kernel
", 13, 10
176 MSG1_LEN equ $ - MSG1
177 MSG2: db 13, 10, "The loader or kernel is too long
", 13, 10
178 MSG2_LEN equ $ - MSG2
179 MSG3: db 13, 10, "Entering loader
", 13, 10
180 MSG3_LEN equ $ - MSG3
181
182 CUR_READ: db 1 ; 當前磁道已經讀出的扇區數
183 CUR_HEAD: db 0 ; 當前的磁頭號
184 CUR_TRACK: dw 0 ; 當前讀到的磁道號
185 CUR_UNREAD_SECTOR dw 0 ; 當前剩余的未讀扇區數
186
187 TRACK_NUM: dw 0 ; 最大磁道號
188 SECTOR_PER_TRACK: db 0 ; 每磁道扇區數
189
190 times 507 - ($ - $$) db 0 ; 填充剩余容量為0
191
192 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
193 ; 將loader文件的大小和kernel文件的大小都
194 ; 放到boot文件oxaa55前的末尾
195 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
196
197 ; loader文件所占用的扇區數,loader不會大過
198 ; 2^8 * 512 = 128KB大小,這在短期內應該足夠了
199 LOADER_LEN db 0
200 ; kernel文件所占用的扇區數,kernel不會大過
201 ; 2^16 * 512 = 32MB大小,這在用1.44MB大小的軟盤里足夠了
202 KERNEL_LEN dw 0
203
204 dw 0xaa55 該文件是由BIOS自動從軟盤的第一個扇區加載進內存的0x0:0x7c00處的。因此必須將boot放到軟盤的第一個扇區,且要求該文件編譯出來必須是512字節,正好占用一個扇區大小。
boot中指定了loader+kernel被加載的位置:0x8000:0x0,也就是物理地址為0x80000的位置。另外將來loader還要在地址0xf000:0x0處放置一些系統參數,因此loader+kernel文件應該位于0x8000:0x0~0xf000:0x0之間,也就是不大于480KB的大小,超過這個大小系統崩潰,無法啟動。
該文件的核心操作就是從軟盤的第二個扇區開始將loader+kernel加載進內存,不過前提是應該已經使用proc_kernel和buildImage程序將boot+loader和kernel壓縮成一個System.Image文件,也就是說loader和kernel被一起放到軟盤的第二個扇區開始的位置,boot將連續讀磁道將loader+kernel加載到從0x8000:0x0起始的內存位置。
還有一個地方需要注意,boot文件可啟動的標志除了大小需要是512B外,最后兩個字節也必須是0xaa55。而且我們在緊挨著0xaa55的地方開辟了3個字節的空間用來記錄loader的大小和kernel的大小,都是以扇區數計量。其中loader大小占1個字節,因此說loader不得大于2^8 * 512B = 128KB,緊挨著是kernel大小,占用2個字節。這兩個值是為了加載loader+kernel做準備的,而且這兩個值不是boot或者loader寫入的,而是buildImage這個工具直接在獲取了loader文件和kernel文件的大小后直接寫入的。
核心的部分是將loader+kernel加載進內存,這個工作具體思想如下:boot先從自己的最后幾個字節位置獲取loader和kernel占用的扇區數量,然后再利用BIOS中斷獲取軟盤參數,最后加載對應數量的扇區,不過為了加快加載速度,這里學習了linux的做法,每次只要可以,就加載整個磁道。
posted on 2011-11-22 19:31
myjfm 閱讀(736)
評論(0) 編輯 收藏 引用 所屬分類:
操作系統