好的,OK,上次我们说到操作系统模式的转换,有一个gdt全局描述符表,段寄存器里面存储着段选择子,到gdt中找到段基址【全局描述符表通过gdtr找到它自己在内存中的位置】,这只是进入保护模式的一个准备工作,接下来我们看另一个准备工作。
mov al,#0xD1 ; command write
out #0x64,al
mov al,#0xDF ; A20 on
out #0x60,al
在setup.s中有这样一段代码,他的意思是打开A20地址线。
打开A20地址线就是说,突破地址信号线20位的宽度,变成32位可用,8086在CPU只有20位地址线,可是很无奈,我们为了向下兼容还得保持20地址线的模式,只有手动开启32位地址线,才能使用32位地址线。说白了,为了使用能用的几种模式,我们就必须做这些烦人的操作。
之后,经过对可编程中断控制器8259芯片进行编程,我们得到8259芯片的引脚和中断号的对应关系如下。
mov ax,#0x0001 ; protected mode (PE) bit
lmsw ax ; This is it;
jmpi 0,8 ; jmp offset 0 of segment 8 (cs)
上面三行代码实现了模式的切换。
前两行将cr0这个寄存器位0置1,模式就从实模式切换到保护模式了。
第三行有一个段间跳转地址,8表示代码段寄存器cs的值,0表示偏移地址,这里是保护模式下的寻址方式,也就说他是从内存地址为0的地方开始执行了。我们前面已经介绍过,内存地址为0的地方存放着我们的system模块,也就是操作系统最为主要的代码,是由head.s main.c以及其余各模块的操作系统代码合并来的。
boot文件夹下有三个汇编语言写的文件,bootsect.s setup,s以及head.s,我们已经读了前两个,再读完最后一个就看见曙光路。
head.s
好的,我们直接看这个代码。
_pg_dir:
_startup_32:
mov eax,0x10
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
lss esp,_stack_start
_pg_dir:页目录,之后在设置分页机制时,页目录会存放在这里,也会覆盖这里的代码。
下面是连续5个mov操作,分别给ds es fs gs赋值为0x10,根绝段描述符结构解析,这几个段寄存器的值指向全局描述符表中的第二个段描述符,也就是数据段描述符。
lss:让ss:esp这个栈顶指针指向_stack_start这个标号的位置。栈顶指针是0x9ff00
call setup_idt ;设置中断描述符表
call setup_gdt ;设置全局描述符表
mov eax,10h
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
lss esp,_stack_start
这里设置了idt和gdt,也就是中断描述符表和全局描述符表,然后重新设置了寄存器的值,和上面的操作一毛一样。
setup_idt:
lea edx,ignore_int
mov eax,00080000h
mov ax,dx
mov dx,8E00h
lea edi,_idt
mov ecx,256
rp_sidt:
mov [edi],eax
mov [edi+4],edx
add edi,8
dec ecx
jne rp_sidt
lidt fword ptr idt_descr
ret
idt_descr:
dw 256*8-1
dd _idt
_idt:
DQ 256 dup(0)
中断描述符表idt里面存放着中段描述符,每一个中断号,对应一个中段描述符,中段描述符里面存放着中断程序的地址,就像段描述符里面存放着段基址一样。什么作用?也就是说你要中断就拿一个中断号过来,我根据中断号找到你的中段描述符,然后找到中断程序的地址。
这段代码设置了256个中段描述符,每一个中段描述符中的中断程序例程都指向一个ignore_int的函数地址,这个是默认的中断处理程序,之后会被具体的中断程序覆盖。
现在你做的任何中断都会指向ignore_int,也就说你没有写具体的终端任务呢,设置中断也不好使!!!
set_gdt:
DQ 0000000000000000h ;/* NULL descriptor */
DQ 00c09a0000000fffh ;/* 16Mb */
DQ 00c0920000000fffh ;/* 16Mb */
DQ 0000000000000000h ;/* TEMPORARY - don't use */
DQ 252 dup(0)
这里我们还需要设置全局描述符表,大家可能有些疑惑,之前已经在setup.s中设置过了,但是之后setup.s可能会被新的代码覆盖,所以我们head.s还要重新设置一遍。
还是0号描述符为空,1号为代码段描述符,2号位数据段描述符,3号没有设置,最后保留了252项空间放置任务状态段描述符TSS和局部描述符LDT。
jmp after_page_tables
...
after_page_tables:
push 0
push 0
push 0
push L6
push _main
jmp setup_paging
L6:
jmp L6
这里的代码开启了分页机制,并且跳转到main函数,这样我们就可以读C的代码了,真实的是一个不容易了!!!!
好的,OK,今天说到这里,有问题欢迎评论区留言。