平台
处理器:Intel Celeron(R) Dual-Core CPU
操作系统:Windows7 专业版 x86
阅读《30天自制操作系统》—川合秀实[2015.03.16 –03.17],[03.29整理]笔记。
将《30天自制操作系统》简称为“书”。
工具:../tolset/..
1 准备画面获取键盘LED灯的状态
BIOS程序只能在实模式下工作,需要在进入保护模式之前准备保护模式要用的画面(屏幕)模式。
1. ;用于保存画面模式信息
2. CYLS EQU 0x0ff0
3. LEDS EQU 0x0ff1
4. VMODE EQU 0x0ff2 ;存颜色的位数
5. SCRNX EQU 0x0ff4 ;存屏幕X方向的分辨率
6. SCRNY EQU 0x0ff6 ;存屏幕Y方向的分辨率
7. VRAM EQU 0x0ff8 ;显存的开始地址
8.
9. MOV AH,0x00 ;AH=00/INT10H 是用来设定显示模式的服务程序
10. MOV AL,0x13 ;320*200*8位彩色
11. INT 0x10
12.
13. MOV BYTE[VMODE],8 ;保存画面模式信息 320* 200 * 8
14. MOV WORD[SCRNX],320
15. MOV WORD[SCRNY],200
16. MOV DWORD[VRAM],0x000a0000 ;画面显存起始地址0xa0000
17.
18. MOV AH,0x02 ;读取键盘状态
19. INT 0x16
20. MOV [LEDS],AL ;AL返回为程序返回值2 进入保护模式的准备
2.1 准备GDT
x86 CPU采用段/页来管理内存。实模式下,段基址由段寄存器提供;保护模式下,段基址保存在GDT中。若要进入保护模式就需要在实模式下准备好GDT。根据“[x86实模式] 内存地址空间分布”选择GDT临时的内存空间;查看《Intel3卷本》“Protect-Mode Memory Management”及“Protection”章节,根据得到的“段描述符内容”内容准备的GDT如下:
21. ;设置GDT
22. [INSTRSET "i486p"] ;使用80386以后的指令
23.
24. LGDT [GDTR0] ;将GDT的首地址和界限赋给GDTR寄存器此时还未进入保护模式,这是在实模式下设置GDT,在进入保护模式后可重新设置GDT。
2.2 屏蔽中断
CPU在实模式和保护模式下的工作模式不一样,所以(最好)在由实模式切换到保护模式的过程中要关闭所有的中断。
25.. ;eral_to_protect.nas: 由实模式切换到保护模式
26..
27. CLICLI将CPU标志寄存器的中断标志位清0,屏蔽中断。在成功进入保护模式后,要立马使用STI指令将CPU标志寄存器的中断标志置1来允许中断。
2.3 开启第21根地址线
在x86实模式下,第21根地址线是关闭(始终为0)的,若要进入保护模式就需要将第21根地址线开启。
28. ;80386开启第21根地址线:
29. ;这里,向804x键盘控制器0x64端口号写一个字节,就是发送一个键盘控制器命令,
30. ;命令可带一个参数,参数从0x60端口写入
31. CALL waitkbdout
32. MOV AL,0xd1 ;0xd1的意思是向8042端口的P2写数据
33. OUT 0x64,AL ;P2原IBMPC使用输出端口的位2控制A20门
34. CALL waitkbdout
35. MOV AL,0xdf ;enableA20
36. OUT 0x60,AL
37. CALL waitkbdout ;等待A20使能完成3 实模式到保护模式的切换
3.1 设置运行保护模式位
若将CR0寄存器的PE位设置为1,CPU就会工作在保护模式下。
38. ;进入保护模式
39. MOV EAX,CR0
40. AND EAX,0x7fffffff
41. OR EAX,0x00000001 ;设置PE位为1
42. MOV CR0,EAX ;进入不用颁的保护模式3.2 刷新流水线
43. JMP pipelineflush ;进入保护模式后,需要用jmp指令重新执行进入流水线的语句
44. pipelineflush:
45. MOV AX,1*8 ;初始化段寄存器,各段寄存器的段号为1
46. MOV DS,AX
47. MOV ES,AX
48. MOV FS,AX
49. MOV GS,AX
50. MOV SS,AX
51. HLT ;无中断,CPU进入休眠
52.
53. ;等待键盘控制电路空闲
54. waitkbdout:
55. IN AL,0x64 ;0x64为键盘控制电路设备的端口号
56. AND AL,0x02 ;键盘控制电路第2位为0则表示键盘控制电路空闲
57. JNZ waitkbdout
58. RET
59.
60. ALIGNB 16 ;让GDT0的值为16的倍数
61. GDT0:
62. RESB 8 ;处理器要求将GDT的第一个段描述符设置为空
63. DW 0xffff,0x0000,0x9200,0x00cf ; 0x0000000 ~0xffffffff, 特权为2,页,可以访问的代码段
64. DW 0xffff,0x0000,0x9a28,0x0047 ;0x00002800 ~ 0x00002800 + 0xfffff, 段,可以执行的段
65.
66. DW 0
67. GDTR0:
68. DW 8*3-1 ;段描述符表字节数-1(段描述符界限)
69. DD GDT0 ;GDT地址进入保护模式设置IDT后,应尽快使用STI指令开通所有的中断。
4 将实模式切换到保护模式的代码加入到IPL中
1. ; ipl
2. ; TAB=4
3. CYLS EQU 10 ;读磁盘10个柱面
4.
5. ORG 0x7c00 ;后续程序从0x7c00开始装载
6.
7. ;FAT12格式软盘启动区规定内容(非必须)
8. JMP entry
9. DB 0x90
10. DB "HARIBOTE" ;启动区的名称,可以是任意8字节的字符串
11. DW 512 ;每个扇区(sector)的大小(必须为512字节)
12. DB 1 ;簇(cluster)的大小(必须为1个扇区)
13. DW 1 ;FAT的起始位置(一般从第一个扇区开始)
14. DB 2 ;FAT的个数(必须为2)
15. DW 224 ;根目录的大小(一般设成224)
16. DW 2880 ;读磁盘(软盘内存数据的介质)的大小(必须是2880扇区)
17. DB 0xf0 ;磁盘的种类(必须是0xf0)
18. DW 9 ;FAT的长度(必须是9扇区)
19. DW 18 ;1个磁道(track)有几个扇区(必?是18)
20. DW 2 ;磁头个数(必是2)
21. DD 0 ;不使用分区,必是0
22. DD 2880 ;重写一次磁盘的大小
23. DB 0,0,0x29
24. DD 0xffffffff
25. DB "HARIBOTEOS" ;磁盘的名称(11字节)
26. DB "FAT12 " ;磁盘的格式名称(8字节)
27. RESB 18 ;空出18字节
28.
29.
30. ;程序主体:读磁盘内容到内存
31. entry:
32. MOV AX,0 ;根据程序被加载的地址初始化寄存器
33. MOV SS,AX
34. MOV SP,0x7c00
35. MOV DS,AX
36.
37. ;读磁盘内容到内存的程序
38. MOV AX,0x0820
39. MOV ES,AX
40. MOV CH,0 ;磁盘的柱面,BIOS0x13h中断程序参数
41. MOV DH,0 ;磁头0,BIOS 0x13h中断程序参数
42. MOV CL,2 ;扇区2,BIOS 0x13h中断程序参数
43. readloop:
44. MOV SI,0 ;读扇区失败的次数
45. retry:
46. MOV AH,0x02 ; AH=0x02:读磁盘;BIOS0x13h中断程序参数
47. MOV AL,1 ;一个扇区;BIOS 0x13h中断程序参数
48. MOV BX,0 ;将磁盘内容读到ES:BX 处
49. MOV DL,0x00 ; A驱动器
50. INT 0x13 ;调用BIOS 0x13h中断程序
51. JNC next ;无出错就跳转到next处
52. ADD SI,1 ;记录操作磁盘失败的次数
53. CMP SI,5
54. JAE error ;如果SI >= 5就跳到error处
55. MOV AH,0x00
56. MOV DL,0x00
57. INT 0x13
58. JMP retry
59. next:
60. MOV AX,ES
61. ADD AX,0x0020
62. MOV ES,AX ;把内存地址后移0x200(512)
63. ADD CL,1 ;下一个扇区
64. CMP CL,18
65. JBE readloop ; CL <= 18 就继续读下一个扇区
66. MOV CL,1
67. ADD DH,1
68. CMP DH,2
69. JB readloop ; DH < 2(分别读磁盘两个面的CH柱面的1 ~ 18扇区)
70. MOV DH,0
71. ADD CH,1
72. CMP CH,CYLS
73. JB readloop ; CH < CYLS 磁盘每个面都读CYLS个柱面
74.
75. jmp oth_sec
76. ;加载磁盘内容后、CPU执行完新任务后进入休眠状态
77. fin:
78. HLT
79. JMP fin
80.
81. error:
82. MOV SI,msg ;用si访问字符串
83. putloop:
84. MOV AL,[SI]
85. ADD SI,1
86. CMP AL,0
87. JE fin
88. MOV AH,0x0e ;0x0e表示显示一个字符;BISO 0x10中断程序参数
89. MOV BX,15 ;显示字符的属性;BISO 0x10中断程序参数
90. INT 0x10 ;调用BIOS 0x10号中断程序
91. JMP putloop
92. msg:
93. DB 0x0a, 0x0a ;换行
94. DB "loaderror"
95. DB 0x0a
96. DB 0
97.
98. RESB 0x7dfe-$ ;再填0直到0x7de - 1处
99.
100. DB 0x55, 0xaa ;表明需要执行启动程序IPL
101.
102.
103. org 0x8200 ;让本程序中jmp指令的参数以0x8200位起始偏移计算,
104. ;因为以下这段程序将会被加载到0x8200处
105. ;后续扇区内容,从偏移0x8200开始
106. oth_sec:
107. ;由实模式切换到保护模式
108. ;eral_to_protect.nas
109. ;***************
110. ;设置屏幕的显存
111. ;CYLS EQU 0x0ff0
112. LEDS EQU 0x0ff1
113. VMODE EQU 0x0ff2 ;存颜色的位数
114. SCRNX EQU 0x0ff4 ;存屏幕X方向的分辨率
115. SCRNY EQU 0x0ff6 ;存屏幕Y方向的分辨率
116. VRAM EQU 0x0ff8 ;显存的开始地址
117.
118. MOV AH,0x00 ;AH=00/INT10H 是用来设定显示模式的服务程序
119. MOV AL,0x13 ;320*200*8位彩色
120. INT 0x10
121. MOV BYTE [VMODE],8 ;保存画面模式
122. MOV WORD [SCRNX],320
123. MOV WORD [SCRNY],200
124. MOV DWORD [VRAM],0x000a0000
125.
126. MOV AH,0x02 ;读取键盘状态
127. INT 0x16
128. MOV [LEDS],AL ;AL返回ASCII
129.
130. ;***************
131. ;设置GDT
132. [INSTRSET "i486p"] ;使用80386以后的指令
133. LGDT [GDTR0] ;将GDT的首地址和界限赋给GDTR寄存器
134.
135. CLI ;屏蔽中断
136.
137. ;***************
138. ;开启第21根地址线:
139. ;这里,向0x64端口号写一个字节,就是发送一个键盘控制器命令,
140. ;命令可带一个参数,参数从0x60端口写入
141. CALL waitkbdout
142. MOV AL,0xd1 ;0xd1的意思是向8042端口的P2写数据
143. OUT 0x64,AL ;P2:原IBM PC使用输出端口的位2控制A20门
144. CALL waitkbdout
145. MOV AL,0xdf ;enableA20
146. OUT 0x60,AL
147. CALL waitkbdout ;等待使能完成
148.
149. ;********************
150. ;进入保护模式
151. MOV EAX,CR0
152. AND EAX,0x7fffffff
153. OR EAX,0x00000001 ;设置PE位为1
154. MOV CR0,EAX ;进入不用颁的保护模式
155. JMP pipelineflush ;进入保护模式后,需要用jmp指令重新执行进入流水线的语句
156. pipelineflush:
157. MOV AX,1*8 ;初始化段寄存器,各段寄存器的段号为1
158. MOV DS,AX
159. MOV ES,AX
160. MOV FS,AX
161. MOV GS,AX
162. MOV SS,AX
163.
164. HLT ;中断已经关闭,CPU进入休眠
165.
166. ;等待键盘控制电路空闲
167. waitkbdout:
168. IN AL,0x64 ;0x64为键盘控制电路设备的端口号
169. AND AL,0x02 ;键盘控制电路第2位为0则表示键盘控制电路空闲
170. JNZ waitkbdout
171. RET
172.
173. ALIGNB 16 ;让GDT0的值为16的倍数
174. GDT0:
175. RESB 8 ;处理器要求将GDT的第一个段描述符设置为空
176. DW 0xffff,0x0000,0x9200,0x00cf ;0x092cf ~ 0x192ce, 可以读写的段
177. DW 0xffff,0x0000,0x9a28,0x0047 ;0x09a47 ~ 0x19a46, 可以执行的段
178.
179. DW 0
180. GDTR0:
181. DW 8*3-1 ;段描述符表字节数-1(段描述符界限)
182. DD GDT0 ;GDT地址
183.
184. RESB 1474560 + 0x7c00 - $用nask.exe将上表的程序编译输出helloos.img,打开“!cons_nt.bat”,运行“install.bat”将helloos.img下载到软盘中。将软盘插在一台以软盘启动的电脑上,程序执行后出现以下界面:
Figure1. 由实模式到保护模式代码的执行
CPU执行HLT指令后进入休眠,CPU此时处于保护模式模式。
[X86OS] Note Over.
[2015.04.17]