ReactOS操作系统启动代码分析(I386架构)(2)

前端之家收集整理的这篇文章主要介绍了ReactOS操作系统启动代码分析(I386架构)(2)前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

这里紧跟上一篇谈到的0x7C00开始执行,当然此时是在实模式下面,执行最初先禁止中断,因为现在堆栈都没有就算有中断也不能执行,所以第一步禁止中断,一直到设置好堆栈才打开中断。这一部分代码很简单,利用ax将段寄存器全部清零,然后调用切换到保护模式下面去,但是由于CS一直都在使用当中,所以就不需要初始化CS。不过要注意的是实际上进入switch_to_prot的时候,并没有进入保护模式,而是在 返回的时候才是执行保护模式下面的32位代码

1.	    .code16  
2.	EXTERN(_RealEntryPoint)  
3.	    cli  
4.	    xor ax,ax  
5.	    mov ds,ax  
6.	    mov es,ax  
7.	    mov fs,ax  
8.	    mov gs,ax  
9.	    mov ss,ax  
10.	    mov sp,word ptr ds:stack16  
11.	    sti  
12.	    call    switch_to_prot  
13.	.code32  
14.	        xor eax,eax  
15.	        mov dword ptr [_FrldrBootPartition],eax  
16.	        mov byte ptr [_FrldrBootDrive],dl  
17.	        mov byte ptr [_FrldrBootPartition],dh  
18.	        call _EnableA20  
19.	        xor eax,eax  
20.	        mov edi,offset __bss_start__  
21.	        mov ecx,offset __bss_end__ + 3  
22.	        sub ecx,edi  
23.	        shr ecx,2  
24.	        rep stosd  
25.	        push eax  
26.	        call    _BootMain  
27.	        call    switch_to_real  
28.	.code16  
29.	        int HEX(19)  
30.	stop:  
31.	        jmp stop  
32.	        nop  
33.	            nop  

这一段重新初始化段寄存器,同样的落下CS不管。之所以要有这一段,是因为可能中间发生中断从而导致改变了段寄存器的值。从最后的pop指令可以看 出,实际上是为了和调用switch_to_prot函数之前的段寄存器保持一致,记住ds:[code32ret]这个地址,这里适用于返回到 _RealEntryPoint后面的32位代码部分。转向保护模式的过程只是转向实模式过程的逆过程。

1.	EXTERN(switch_to_prot)  
2.	.code16  
3.	    cli   
4.	    xor ax,ax  
10.	    pop word ptr ds:[code32ret]  
11.	    mov word ptr ds:[stack16],sp  
12.	    lgdt    gdtptr  
13.	    lidt    i386idtptr  
14.	    mov eax,cr0  
15.	    or eax,CR0_PE_SET  
16.	    mov cr0,eax  
17.	    jmp far ptr PMODE_CS:inpmode  
18.	.code32  
19.	inpmode:  
20.	    mov ax,PMODE_DS  
21.	    mov ds,ax  
22.	    mov es,ax  
23.	    mov fs,ax  
24.	    mov gs,ax  
25.	    mov ss,ax  
26.	    mov esp,dword ptr [stack32]  
27.	    push dword ptr [code32ret]  
28.	    ret  
首先介绍下什么叫做A20门,因为在最初的8086系统上面可以访问的数据是1M,通过数据段左移四位,加上便宜的得到 最终的数据,所以可以访问的数据是1M+64K-16B,但是由于整个系统只有20位地址线,所以超过1M的部分不能访问,系统会自动在超过1M的时候进 行回卷,也就是说访问1M地址的时候,实际上是访问0号地址,1M+1B的时候访问的是1号地址,依次类推。然而到了80286的时候可以访问的地址是 16M,所以这时就不能回卷了。然而需要兼容的话,这种回卷又必须存在——不然怎么仅仅只访问1M地址。所以,就提出在键盘控制器上面用一个寄存器位表明 是否需要回卷,当这个位被打开的时候,表明可以访问超过1M的地址。当这一位禁止的时候,表明只能访问1M内存。
1.	.code16  
2.	empty_8042:  
3.	    .word   0x00eb,0x00eb            // 这里是jmp $+2的指令码,因为指令码长度刚好是2,所以这里是对时间的一种消耗——也就是常说的等待操作  
4.	    in al,HEX(64)                   //从地址64里面读取数据,这里的64位地址和内存地址不在同一个空间内,这里是8042的命令寄存器  
5.	    cmp al,HEX(ff)               // 判断8042是否空闲  
6.	    jz empty_8042_ret           // 如果控制器空闲,就跳出循环,否则继续等待  
7.	    test al,2  
8.	    jnz empty_8042  
9.	empty_8042_ret:  
10.	    ret  
11.	EXTERN(_EnableA20)  
12.	.code32  
13.	    pusha  
14.	    call switch_to_real  
15.	.code16  
16.	    call empty_8042  
17.	    mov al,HEX(D1)                // 发送消息,表明希望写输出缓冲寄存器  
18.	    out HEX(64),al  
19.	    call empty_8042               //等待设备就绪  
20.	    mov al,HEX(DF)                // 往寄存器当中写数据  
21.	    out HEX(60),al  
22.	    call empty_8042  
23.	    call switch_to_prot              
24.	    .code32  
25.	    popa  
26.	    ret  
相关内容可以查询8042用户手册。在是能8042之后,就转向将BSS部分清空。
1.	xor eax,eax                       //首先将eax清空  
2.	mov edi,offset __bss_start__      //首地址放到edi当中  
3.	mov ecx,offset __bss_end__ + 3    //末尾地址+3进行对齐操作  
4.	sub ecx,edi                       //得到整个BSS段的大小  
5.	shr ecx,2                         //一次处理四位,所以这里右移四位除以四  
rep stosd                          //循环直到ECX等于0 

最后函数的控制转入到bootmain,这是一个实实在在的C函数函数原型为VOID BootMain(LPSTR CmdLine),这里传递进去的参数为0。并且这个函数不会有返回值(假设有返回值的话,整个系统该重启或者陷入死循环)。

猜你在找的React相关文章