关于setup.s,我认为是引导程序中3个程序中最简单的一个,在setup.s中我认为最终要的是搞清楚保护模式和实模式的区别
setup.s主要做了如下的事情
- 利用BIOS读取机器系统的参数,然后把这些参数存放到ox9000:0000的位置(覆盖了原来的bootsect.s的程序);
- system模块整体下移了到ox0000处;
- 加载中断描述符寄存器和全局描述符寄存器;
- 开启A20地址线;
- 重新设置8259A,硬件中断号为0x20~0x2f;
- 设置CR0,从而以32位以保护模式运行,跳转到head.s运行;
setup.s部分代码分析
-
小端法
最主要的是关于ax存储方式,通过查表我们知道ox90006的位置是存放显示模式一个字节,ox90007存放字符列数一个字节。在这段代码最后一行mov [6],ax。在它后面注释中,我们知道了al在ox90006,ah在ox90007的位置,这就是采用了小端法。这里主要是一个小的细节。!Get video-card data: int 0x10 mov [4],bx !bh=display page mov [6],ax !al=video mode,ah=widow width
-
lds指令
这里最重要的代码是 lds si,[4*0x41],其主要实现的是把ds:[4*0x41]开始的4个字节存放到si,ds中。[4*0x41]开始的4个字节是41号中断的中断向量,存放的是中断处理函数的地址。在这里指的是hd0参数表的地址。还有一点值得提醒大家,为什么是4*0x41,因为中断向量中的大小是4个字节,分别是段基址和段内偏移这两个16位的值。!Get hd0 data mov ax,#0x0000 mov ds,ax lds si,[4*0x41] mov ax,#INITSEG mov es,ax mov di,#0x0080 mov cx,#0x10 rep movsb
-
stosb指令
这段的主要是判断没有第2个硬盘,则将第二个硬盘参数清零。这里的stosb/sw 是将al/ax的数据装入es:di制定的存储单元,然后更具DF标志增减di。no_disk1: mov ax,#INITSEG mov es,ax mov di,#0x9000 mov cx,#0x10 mov ax,#0x00 rep stosb
-
do_move的分析
这段代码其实设计的还是很漂亮的。刚开始我觉得这段代码设计的太麻烦了,直接设置号ds,si和es,di,然后设置cx不就行了,后来我发现我错了。因为如果按照我的想法,那么cx=0x8000ffff,很明显这已经超过了cx的16位上限了。由于system模块范围是0x1000:0~0x8fff:ffff总共 8*16^4=512kb。这段代码总共移动了0x8000×2*8=512kb所以正如linus所说“we move the system to it's rightful place”。do_move: mov es,ax add ax,#0x1000 cmp ax,#0x9000 jz end_move mov ds,ax sub di,di sub si,si mov cx,#0x8000 rep movsw jmp do_move
-
加载IDTR和GDTR
在lidt idt_48中,通过idt_48来设置IDTR的值即IDT的入口地址,同理lgdt gdt_48设置了GDTR的值即GDT的入口地址。中断向量表的地址从0x0000开始的这在head.s中有详细的解释。全局描述符表可以在任何位置,只要在GDTR中设置它的地址即可。lidt idt_48 lgdt gdt_48
-
进入保护模式
mov ax,#ox0001 lmsw ax jmpi 0,8
进入保护模式首先关闭所有中断,然后设置CR0来使CPU处于保护模式运行。