基于linux0.11的从零开始学操作系统-bootsect

首先,BIOS有一段自检程序,其作用还不明确。总之,与我们有关的地方是内存中0x07c00
CPU到了0x07c00从这里开始进入bootsect.s

_start:
        mov     ax,#BOOTSEG
        mov     ds,ax
        mov     ax,#INITSEG
        mov     es,ax
        mov     cx,#256
        sub     si,si
        sub     di,di
        rep
        movw
        jmpi    go,INITSEG

而bootsect.s 一开始先是将自己拷贝到0x90000处
bootsect 本身只有512字节,因此才有了上述程序rep那里的内容.
256word= 512 byte
也就是从内存中0x07c00拷贝512字节到0x90000.
0x90000-0x9001ff 这里便是bootsect的新的位置.

这时候,如果你反汇编一下0x07c00 与 0x90000。你会发现二者相同,并且就是bootsect.S中的内容,因此验证猜想正确。

go:     mov     ax,cs          ! 此时cs=9000,IP = 0018
        mov     ds,ax
        mov     es,ax
! put stack at 0x9ff00.
        mov     ss,ax
        mov     sp,#0xFF00

执行完jmpi go,INITSEG后,CPU 便跳到了0x90018处,设置堆栈大小.并将ds,es设置为9000H.此时,cs=ds=es=ss=9000H.
接下来到了ok_load_setup

load_setup:
        mov     dx,#0x0000              ! drive 0, head 0
        mov     cx,#0x0002              ! sector 2, track 0
        mov     bx,#0x0200              ! address = 512, in INITSEG
        mov     ax,#0x0200+SETUPLEN     ! service 2, nr of sectors
        int     0x13                    ! read it
         jnc     ok_load_setup           ! ok - continue

这里的重点就是bios 13号中断.

功能描述:读扇区 
入口参数:AH=02H 
AL=扇区数 
CH=柱面 
CL=扇区 
DH=磁头 
DL=驱动器,00H~7FH:软盘;80H~0FFH:硬盘 
ES:BX=缓冲区的地址 
出口参数:CF=0——操作成功,AH=00H,AL=传输的扇区数,否则,AH=状态代码

bios13号中断为基本的磁盘操作,根据ah不同选择不同功能。
上述ah = 02,al=SETUPLEN=2.
因此,其含义为
“从0柱面第二个扇区第0个磁头读4个扇区(2048B)到内存的0x90200处”
执行完第一个int 13后,我们查看0x90200内容。
会发现0x90200-0x90337共312字节

<bochs:29> u 0x90200 0x902ff
00090200: (                    ): mov ah, 0x03              ; b403
00090202: (                    ): xor bh, bh                ; 30ff
00090204: (                    ): int 0x10                  ; cd10
00090206: (                    ): mov cx, 0x0017            ; b91700
00090209: (                    ): mov bx, 0x0007            ; bb0700
0009020c: (                    ): mov bp, 0x014c            ; bd4c01
0009020f: (                    ): mov ax, 0x1301            ; b80113
00090212: (                    ): int 0x10                  ; cd10
00090214: (                    ): mov ax, 0x9000            ; b80090
00090217: (                    ): mov ds, ax                ; 8ed8
00090219: (                    ): mov ah, 0x03              ; b403
0009021b: (                    ): xor bh, bh                ; 30ff
0009021d: (                    ): int 0x10                  ; cd10
0009021f: (                    ): mov word ptr ds:0x0, dx   ; 89160000

正是后面setup.S中的内容。
如果成功,CF=0,跳转至ok_load_setup
若不成功得想办法补救,即重新读:

        mov     dx,#0x0000
        mov     ax,#0x0000              ! reset the diskette
        int     0x13
        j       load_setup

成功后,继续

ok_load_setup:

! Get disk drive parameters, specifically nr of sectors/track

        mov     dl,#0x00
        mov     ax,#0x0800              ! AH=8 is get drive parameters
        int     0x13

又是int 13.这次AH=08:

入口参数:AH=08H 
DL=驱动器,00H~7FH:软盘;80H~0FFH:硬盘 
出口参数:CF=1——操作失败,AH=状态代码,参见功能号01H中的说明,否则, BL=01H — 360K 
=02H — 1.2M 
=03H — 720K 
=04H — 1.44M 
CH=柱面数的低8位 
CL的位7-6=柱面数的该2位 
CL的位5-0=扇区数 
DH=磁头数 
DL=驱动器数 
ES:DI=磁盘驱动器参数表地址 

执行完int 13后,发现BL= 4代表读取软盘 大小为1.44M,CL =00010010B代表18个扇区 ,将CH置零后,cx = 0012H

      mov     ch,#0x00
        seg cs
        mov     sectors,cx
        mov     ax,#INITSEG
        mov     es,ax

接下来第2,3句为段超越,意思把cx内容移动到cs:[sectors],此时sectors相当于一个变量,用于保存一个值,测试时发现sectors=0x13d。意味着将读取到的数据保存在内存cs:sectors处。
同时,int 13 改变了ES,将es再设回9000。然后,准备在屏幕上显示一些文字。

! Print some inane message

        mov     ah,#0x03                ! read cursor pos
        xor     bh,bh
        int     0x10
AH = 3
读光标位置
输入参数:BH = 页号
输出参数:
CH = 光标开始行
CL = 光标结束行
DH = 行
DL = 列

先读0页光标位置,ch=6,cl=7,dh=10,dl=0

    mov     cx,#24                     !  cs:ip=0x00090056 
       mov     bx,#0x0007              ! page 0, attribute 7 (normal)
        mov     bp,#msg1
        mov     ax,#0x1301              ! write string, move cursor
        int     0x10

显示24个字符串。
接着将系统加载到内存起始0x10000处,代码如下

! ok, we've written the message, now
! we want to load the system (at 0x10000)

        mov     ax,#SYSSEG
        mov     es,ax           ! segment of 0x010000
        call    read_it

将段设置好后调用了读取函数:

read_it:
        mov ax,es
        test ax,#0x0fff
die:    jne die                 ! es must be at 64kB boundary
        xor bx,bx               ! bx is starting address within segment
rp_read:
        mov ax,es
        cmp ax,#ENDSEG          ! have we loaded all yet?
        jb ok1_read
        ret
ok1_read:...
ok2_read:...
.....

将一些东西准备就绪后,就要进入setup

    jmpi 0,SETUPSEG

跳转至0x90200。

感谢稀稀拉拉的赞赏