[Rx86OS-III] 由实模式切换到保护模式

时间:2022-09-09 09:24:01

平台

处理器: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.        CLI
 

CLI将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下载到软盘中。将软盘插在一台以软盘启动的电脑上,程序执行后出现以下界面:

[Rx86OS-III] 由实模式切换到保护模式

Figure1. 由实模式到保护模式代码的执行

CPU执行HLT指令后进入休眠,CPU此时处于保护模式模式。


[X86OS] Note Over.

[2015.04.17]