!boot.s
!首先利用BIOS中断把内核代码(head代码)加载到内存0x1000处,然后移动到内存0处,
!最后进入保护模式,并跳转到内存0(head代码)开始处继续运行
BOOTSEG = 0X07C0 !引导程序(本程序)被BIOS加载到内存0x7c00处
SYSSEG = 0X1000 !内核第一步加载到的地址
SYSLEN = 17 !内核占用的最大磁盘数
entry start
start:
Jmpi go,#BOOTSEG !段间跳转到0x7c0:go处。这指令还会带来加载CS等的段寄存器效果。
go: mov ax,cs !cs现在为0x7c0
mov ds,ax !ds(数据段),ss(堆栈段)段寄存器也都为0x7c0
mov ss,ax !
mov sp,#0x400 !sp栈顶指针为0x400
!加载head代码到 0x1000处,这个代码是利用BIOS中断,从软盘读取数据到内存,
!这里也就说明了,为什么要先移动到0x1000,而不直接移动到0x0,因为现在BIOS中断
!才能从软盘读数据,而先移动到0x1000是为了不覆盖掉BIOS。等移动到0x1000后,就可以
!覆盖BIOS,移动到0x0处了。
load_system:
mov dx,#0x0000 !利用BIOS中断int 0x13功能2从启动盘读取head代码
mov cx,#0x0002 !DH-磁头号;DL驱动器号;CH是10位磁道号的低8位;
mov ax,#SYSSEG !CL的7,6位是磁道号的高2位,其5-0位是起始扇区号
mov es,ax !ES:BX 读入缓冲区位置(0x1000:0x0000).
xor bx,bx !AH-读扇区功能号;AL-需读的扇区数(17)
mov ax,#0x200+SYSLEN !
int 0x13 !
jnc ok_load !
die: jmp die !
!把内核代码移动到内存0开始处,共移动8KB字节(内核长度不超过8KB)
ok_load cli !disable interrupt
mov ax,#SYSSEG !
mov ds,ax !
xor ax,ax !
mov es,ax !
mov cx,#0x1000 !
sub si,si !
sub di,di
rep
movw ! MOVW:将DS:SI的内容送至ES:DI,是复制过去.
!加载IDT和GDT基地址寄存器IDTR和GDTR
mov ax, #BOOTSEG
mov ds, ax !数据段DS指向0x7c0
lidt idt_48 !加载IDTR。idt_48定义在后面,2字节表长度,4字节基地址
lgdt gdt_48 !同上
!设置控制寄存器CR0(即机器状态字),进入保护模式,段选择符值8对应GDT表中第2个段描述符
mov ax, #0x0001 !对应保护模式开启位PE(位0)
lmsw ax !加载状态字指令,即将ax放入CR0
jmpi 0,8 !段间跳转,保护模式jmpi这里的8是是相对于GDT表中的位置。我们知道逻辑地址是段选择符(这里的8就是段选择符,对应GDT表的第1项(第0项为空),加上偏移地址。这个要跳转的地址的为GDT表中第一个段描述的中的基地址加上偏移量0).段选择符的0-1位是RPL(请求特权级),2位是TI(1表示在LDT表中,0表示在GDT表中),3-15位是索引值。所以0x08就是表示GDT表中的第一项,且请求特权级为0。
!GDT表定义
gdt .word 0,0,0,0 !第一个段描述符全为空不用,格式参考4.3.4段描述符。
.word 0x07ff !段限长 2048
.word 0x0000 !段基值 0x0
.word 0x9a00 !标识为代码段 可读/可执行
.word 0x00c0 !颗粒度 4KB,结合段限长2048.则此段实际最大长度为8M
.word 0x07ff !略
.word 0x0000 !
.word 0x9200 !
.word 0x00c0 !
!定义idt(中断描述符表),gdt段寄存器值.用来存放GDT表的32位基地址和16为段限长
idt_48: .word 0 ! IDT表长度
.word 0,0 ! IDT表的线性基地址0
gdt_48: .word 0x7ff ! GDT表长度是2048字节,可容纳256个描述项
.word 0x7c00+gdt,0 ! GDT表的线性基地址在0x7c0段的偏移gdt处
.org 510 ! 对齐到510
.word 0xAA55 ! 第512字节(引导扇区最后),为有效标志字节,应为0xAA55