作者:朱克锋
转载请注明出处:http://blog.csdn.net/linux_zkf
在上篇文章中bootsec已经把所有程序都加载到计算机的内存中的一定位置,下面接着上篇文章的结尾
jmpi 0,SETUPSEG开始要进入setup程序开始执行。
看一下这行代码:
jmpi 0,SETUPSEG
这行代码的作用就是跳转到SETUPSEG出开始执行,就是setup程序加载的位置,OK,此时setup接着bootsec开始继续执行.
在setup开始执行做的第一件事就是读取计算机系统数据,具体代码如下:
entry start
start:
! ok, the read went well so we get current cursor position and save it for
! posterity.
mov ax,#INITSEG ! this is done in bootsect already, but...
mov ds,ax
mov ah,#0x03 ! read cursor pos
xor bh,bh
int 0x10 ! save it in known place, con_init fetches
mov [0],dx ! it from 0x90000.
! Get memory size (extended mem, kB)
mov ah,#0x88
int 0x15
mov [2],ax
! Get video-card data:
mov ah,#0x0f
int 0x10
mov [4],bx ! bh = display page
mov [6],ax ! al = video mode, ah = window width
! check for EGA/VGA and some config parameters
mov ah,#0x12
mov bl,#0x10
int 0x10
mov [8],ax
mov [10],bx
mov [12],cx
! 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
! Get hd1 data
mov ax,#0x0000
mov ds,ax
lds si,[4*0x46]
mov ax,#INITSEG
mov es,ax
mov di,#0x0090
mov cx,#0x10
rep
movsb
! Check that there IS a hd1 :-)
mov ax,#0x01500
mov dl,#0x81
int 0x13
jc no_disk1
cmp ah,#3
je is_disk1
no_disk1:
mov ax,#INITSEG
mov es,ax
mov di,#0x0090
mov cx,#0x10
mov ax,#0x00
rep
stosb
is_disk1:
从代码中可以看到读取的数据覆盖了bootsec的程序!!!!对头,bootsec已经使用完了,使命已经完成,留着无用,干掉吧,呵呵呵,过河拆桥啊,不过正是这种过河拆桥的做法合理的利用了计算机的内存,bootsec所占的内存位512字节,读取的机器系统数据占510字节,仅仅只有2个字节没有被覆盖!!!利用率非常之高。
接下来就是见证历史的时刻了:系统将由实模式转换到保护模式!现代操作系统由此产生!
读取机器系统数据之后紧接着就是:
cli
可不要小看了这个mini代码,他的作用就是关闭中断,从此时开始无论系统是否产生中断,系统都不会响应此中断,直到后来的main函数打开中断,不过那时系统响应的不再是BIOS的中断了,而是由系统自己提供的了。
在setup中,关闭中断之后就是把system从开始的SYSSEG移到0x00000处,代码如下:
do_move:
mov es,ax ! destination segment
add ax,#0x1000
cmp ax,#0x9000
jz end_move
mov ds,ax ! source segment
sub di,di
sub si,si
mov cx,#0x8000
rep
movsw
jmp do_move
在0x00000处原来存放的是BIOS的一些东西,中断向量表,数据等等,这样的移动和前面一样,原来的数据全被新数据覆盖了。
紧接着setup执行如下代码:
end_move:
mov ax,#SETUPSEG ! right, forgot this at first. didn't work :-)
mov ds,ax
lidt idt_48 ! load idt with 0,0
lgdt gdt_48 ! load gdt with whatever appropriate
。。。。。
gdt:
.word 0,0,0,0 ! dummy
.word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb)
.word 0x0000 ! base address=0
.word 0x9A00 ! code read/exec
.word 0x00C0 ! granularity=4096, 386
.word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb)
.word 0x0000 ! base address=0
.word 0x9200 ! data read/write
.word 0x00C0 ! granularity=4096, 386
idt_48:
.word 0 ! idt limit=0
.word 0,0 ! idt base=0L
gdt_48:
.word 0x800 ! gdt limit=2048, 256 GDT entries
.word 512+gdt,0x9 ! gdt base = 0X9xxxx
这段代码的作用就是setup利用自己的数据信息对中断描述符表寄存器IDTR和全局描述符表寄存器GDTR进行初始化。
初始化中断描述符表寄存器IDTR和全局描述符表寄存器GDTR之后执行到这里:
! that was painless, now we enable A20
call empty_8042
mov al,#0xD1 ! command write
out #0x64,al
call empty_8042
mov al,#0xDF ! A20 on
out #0x60,al
call empty_8042
打开A20!!!!
前面说过实模式的中断机制已经被关闭和破坏,但是又不能没有中断机制,所以要为保护模式下建立中断机制,在setup中将对可编程的终端控制器进行重新编程,代码如下:
mov al,#0x11 ! initialization sequence
out #0x20,al ! send it to 8259A-1
.word 0x00eb,0x00eb ! jmp $+2, jmp $+2
out #0xA0,al ! and to 8259A-2
.word 0x00eb,0x00eb
mov al,#0x20 ! start of hardware int's (0x20)
out #0x21,al
.word 0x00eb,0x00eb
mov al,#0x28 ! start of hardware int's 2 (0x28)
out #0xA1,al
.word 0x00eb,0x00eb
mov al,#0x04 ! 8259-1 is master
out #0x21,al
.word 0x00eb,0x00eb
mov al,#0x02 ! 8259-2 is slave
out #0xA1,al
.word 0x00eb,0x00eb
mov al,#0x01 ! 8086 mode for both
out #0x21,al
.word 0x00eb,0x00eb
out #0xA1,al
.word 0x00eb,0x00eb
mov al,#0xFF ! mask off all interrupts for now
out #0x21,al
.word 0x00eb,0x00eb
out #0xA1,al
搞定这些之后,即进入以下代码:
mov ax,#0x0001 ! protected mode (PE) bit
lmsw ax ! This is it!
jmpi 0,8 ! jmp offset 0 of segment 8 (cs)
注意这行代码:
jmpi 0,8
这里就跳到head程序了。
到此呢,setup程序执行完毕,后面就由head继续来完成。
在下一篇文章中我将继续head程序分析、学习linux。加油