操作系统开发之——Loader程序编写(2)


操作系统开发之——Loader程序编写(2)


操作系统开发之——Loader程序编写(2)

上一节我们已经掌握了进入保护模式的关键,这一节我们来具体实现他。


我们之前向屏幕输出都是通过设置中断实现,比较麻烦,这次我们换个方法,向显存输出信息,显存地址(CGA)是0xB8000~0xBFFFF,我们只需要用其中一段就行,显示功能不会用太多。使用方法很简单,两个显存地址分别存储字符和字符的前景,写入后就可以看到效果了:

<code> 1[bits 16] 2;后面保护模式的代码就是32位了,先写着 3org 0x70000 4 5START: 6    ;这次一定要初始化,设置堆栈段和栈指针 7    mov ax,cs 8    mov ds,ax 9    mov es,ax10    mov ss,ax11    mov sp,01213point:14    ;清屏是个好习惯15    mov ax, 0x216    int 0x101718    mov ax, 0xb80019    mov es, ax20    mov di, 021    mov byte [es: di + 0], 'L'22    mov byte [es: di + 1], 0xcf23    mov byte [es: di + 2], 'o'24    mov byte [es: di + 3], 0xcf25    mov byte [es: di + 4], 'a'26    mov byte [es: di + 5], 0xcf27    mov byte [es: di + 6], 'd'28    mov byte [es: di + 7], 0xcf29    mov byte [es: di + 8], 'i'30    mov byte [es: di + 9], 0xcf31    mov byte [es: di + 10], 'n'32    mov byte [es: di + 11], 0xcf33    mov byte [es: di + 12], 'g'34    mov byte [es: di + 13], 0xcf/<code>

截图太麻烦了,我拿我的人格担保,一定可以显示出Loading,效果和之前是一样的。只是有人会发现,这样打印字符串太不舒服了。是的,有更好的办法,但是现在要先解决保护模式,保护模式进入也需要通过字符输出来确认是否成功,但是进入保护模式后,我们就不能使用BIOS中断来输出字符了,所以这里先简单介绍操作显存。

我们现在来初始化GDT并加载他,根据上一节初探保护模式,我们得知,我们需要填写表相应位的值来初始化,填写完后还要用GDTR加载:

<code> 1load_GDTR: 2    ;保存GDT地址到GDTR 3    lgdt[gdt]  4 5gdt_head: 6    dd 0x0000000 7    dd 0x0000000 8 9    dw 0x000ffff    ;段限制:0-15位10    dw 0x0000000    ;段基地址:0-15位11    db 0x0000000    ;段基地址:16-23位12    db 10011010b    ;段描述符的第6字节属性(代码段可读写)13    db 11001111b    ;段描述符的第7字节属性:16-19位14    db 0x0000000    ;段描述符的最后一个字节是段基地址的第二部分:24-31位1516    dw 0x000ffff    ;段限制:0-15位17    dw 0x0000000    ;段基地址:0-15位18    db 0x0000000    ;段基地址:16-23位19    db 10010010b    ;段描述符的第6字节属性(数据段可读写)20    db 11001111b    ;段描述符的第7字节属性:limit(位16-19)21    db 0x0000000    ;段描述符的最后一个字节是段基地址的第二部分:24-31位2223gdt:24    dw gdt - gdt_head - 125    dd gdt_head/<code>

数字后缀b表示二进制。关于GDT的填写,注释已经写得很详细了,可以参考上一篇文章Intel的全局描述符示意图来理解。此时已经完成了GDT的初始化,接下来打开A20地址线。

读端口用in指令,写端口用out指令。有个规定就是在92h端口,如果al的二进制第二位是1则打开,反之关闭:

<code> 1... 2    lgdt[gdt]  3 4enable_A20: 5    in al,0x92 6    or al,10b         ;一般直接写2 7    out 0x92,al 8 9    cli1011gdt_head:12.../<code>

然后就是设置PE位,跳转到保护模式:

<code> 1... 2    out 0x92,al 3 4    cli 5 6set_PE: 7    mov eax,cr0 8    or eax,1 9    mov cr0,eax1011entry_to_protection_mode:12    jmp dword 0x8:flush13...14    dd gdt_head1516[bits 32]17;这里已经是32位模式了,nasm就会编译成32位的代码18flush:/<code>

此时我们要验证保护模式是否真正开启,需要向显存写入信息,但是这次我们不需要再偏移地址了,直接0xb8000就行:

<code>1flush:2    mov dword [0xb8000+160+0],'P'3    mov dword [0xb8000+160+1],0x2f45    jmp $    ;在这停止,cpu进入睡眠模式6    hlt78times 4096-($-$$) db 0    ;对齐内存/<code>

这回截个图:

操作系统开发之——Loader程序编写(2)

进入保护模式之后,很多事情就方便做了。今天先这样。


分享到:


相關文章: