加了一些注释的skelix

  02/bootsect.s
        .text
        .globl    start
        .include "kernel.inc"
include the above file
        .code16
start:
        jmp        code
gdt:  
        .quad    0x0000000000000000 # null descriptor
        .quad    0x00cf9a000000ffff # cs
        .quad    0x00cf92000000ffff # ds
        .quad    0x0000000000000000 # reserved for further use
        .quad    0x0000000000000000 # reserved for further use
gdt_48:
        .word    .-gdt-1           #   当前地址减gdt地址减1得到GDT的长度       # 知道是长度这个属性了,这样子计算的,那个小点,估计代表的是当前地址,看的时候要注意
        .long    GDT_ADDR
code:
        xorw    %ax,    %ax
        movw    %ax,    %ds                               # 数据段 = 0x0000
        movw    %ax,    %ss                               # 堆栈段 = 0x0000
        movw    $0x1000,%sp                           # 保护模式前用的堆栈,不要让他覆盖到7c00处的引导程序即可
        # 我们将加载内核到地址 0x10000
        movw    $0x1000,%ax                           #ax=0x1000
        movw    %ax,    %es                              #es=ax
        xorw    %bx,    %bx                                 # es:bx 加载内核的目标地址
        movw    $KERNEL_SECT,%cx           #cx= KERNEL_SECT, 72           # 内核大小,单位是,36k对于现在来说已经足够了
        movw    $1,     %si    # 0,跳过去,所以是1
rd_kern:
        call    read_sect                                     # 入口参数:si是起始扇区数,es:bx是指定内存地址,从哪儿读到内存es:bx处的呢
        addw    $512,    %bx                             #增量为512,一个扇区的大小
        incw    %si                                              #si扇区的个数
        loop    rd_kern
我们先把内核读到0x10000这个临时地址,然后再把它搬移到0x0(进入保护模式后搬移)。这个函数讲起来有些烦,读者可以自己分析:)

        cli                                                               # 就要进入保护模式了,所以关掉实模式下的中断
        cld                                                              # 将内核的前512字节移到0x0
        movw    $0x1000,%ax                            #ax=0x1000
        movw    %ax,    %ds                               #ds=ax
        movw    $0x0000,%ax                            #ax=0x0000
        movw    %ax,    %es                               #es=ax
        xorw    %si,    %si                                   #si=0
        xorw    %di,    %di                                   #di=0
        movw    $512>>2,%cx                           #cx=128??????,控制传输的次数明白为啥是128了,因为movsl这个指令一次传送的是四个字节(双字)
        rep
        movsl                                                       #将DS:SI这段地址的N个字节复制到ES:DI指向的地址,恩,很有感觉了
 
 
为什么要这样做?因为内核的这个部分是load.s这个文件编译出来的(本课后面会介绍到),load.s会读取“真正的内核”到0x200处,但是在这一课,我们只准备打印"Hello World!",除此之外什么都不做。

        xorw    %ax,    %ax
        movw    %ax,    %ds                                   # 复位 ds 为 0x0000

        movw    $GDT_ADDR>>4,%ax                # (0x80000 + 256 * 8) >> 2    ####IDT_ADDR= 0x80000  
                                                                                                                                  # IDT_SIZE,=(256*8) 
                                                                                                                                  # GDT_ADDR= (IDT_ADDR+IDT_SIZE)        # GDT 在 IDT的后面
        movw    %ax,    %es                                   # gdt所在的数据段
         movw    $gdt,   %si                                     #我感觉gdt标号在编译好的目标中应该相当于一个偏移的感觉
        xorw    %di,    %di                                       # 从ds:si 拷贝到 es:di中
        movw    $GDT_SIZE>>2,%cx                   # 拷贝数据段中的gdt到指定地址
        rep
        movsl
 
#打开A20 Gate Option 详细 为了读取1M以上的地址空间
#    * 读60h端口,读output buffer
#    * 写60h端口,写input buffer
#    * 读64h端口,读Status Register

#seta20.1:    inb    $0x64,%al             # Get status  读状态寄存器  in al,0x64
#        testb    $0x2,%al                       # Busy?  test al,0x02
#       jnz    seta20.1                            # Yes 占用则跳转
#       movb    $0xd1,%al                     # Command: Write
#        outb    %al,$0x64                     #  output port
#seta20.2:    inb    $0x64,%al             # Get status
#       testb    $0x2,%al                        # Busy?
#        jnz    seta20.2                           # Yes
#       movb    $0xdf,%al                      # Enable
#      outb    %al,$0x60                       #  A20

enable_a20:        
        inb    $0x64,   %al                                      #inbyte(猜测的),读64h端口状态
        testb  $0x2,    %al                                      #看看是否繁忙
        jnz    enable_a20

        movb   $0xbf,   %al
        outb   %al,     $0x64                                   #貌似为什么不是60端口呢???,难道状态口和读写口都不分的吗

这种开启a20地址线的方法来自一本书:"The Undocumented PC",中文纸版是《PC技术内幕》,可惜已绝版。a20地址线通过键盘控制器一个端口使能(ibm早期这样设计),当系统启动时,该地址线是关闭的,使能它之后才能访问1MB以外的地址空间。

 
        lgdt    gdt_48                 # 加载gdt地址到寄存器中
        # 进入保护模式
        movl   %cr0,    %eax
        orl    $0x1,    %eax
        movl   %eax,    %cr0           # 使能CR0 控制寄存器中的PE位(即第0位)
现在我们已经进入到保护模式了,是不是简单的另你不敢相信?呵呵

        ljmp   $CODE_SEL, $0x0
我们还需要进行一个绝对地址跳转,因为解码管线中预取了16位指令,需要刷新成后面的32位指令。关于ia32的指令预取和解码管线,网络上有很多相关的文章,建议读者阅读一下相关文章。这个指令跳转到0x08描述符选择子指向的偏移0x的指令处,并开始执行,这个描述符即GDT中的第2项:内核代码段描述符。代码就是load.s的开始处,一会我们开始分析load.s这个程序。
 
 
bootsector.s中的函数:
        # 输入:    si:    LBA 地址,从0开始
        # 输出     es:bx  读取扇区到这个内存地址
read_sect:
        pushw   %ax
        pushw   %cx
        pushw   %dx
        pushw   %bx

        movw    %si,    %ax       
        xorw    %dx,    %dx
        movw    $18,    %bx    # 对于1.44M软盘:每磁道18扇区

        divw    %bx
        incw    %dx
        movb    %dl,    %cl    # cl = 扇区号
        xorw    %dx,    %dx
        movw    $2,     %bx    # 每磁道2磁头
        divw    %bx

        movb    %dl,    %dh    # 磁头
        xorb    %dl,    %dl    # 软驱号
        movb    %al,    %ch    # 柱面
        popw    %bx            # 读取到:es:bx
rp_read:
        movb    $0x1,   %al    # 读1个扇区
        movb    $0x2,   %ah
        int     $0x13
        jc      rp_read
        popw    %dx
        popw    %cx
        popw    %ax
        ret
 
.org    0x1fe,  0x90              # 填充nop指令,机器码是0x90
.word   0xaa55

Hello World Comes Back
当我们进入到保护模式后,所有的通用寄存器和段寄存器保持原来实模式的值,代码段从特权级0开始执行。load.s文件将从地址0处开始执行。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值