免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1130 | 回复: 0
打印 上一主题 下一主题

bootsect.S分析 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2006-11-05 19:45 |只看该作者 |倒序浏览

#include      /* for CONFIG_ROOT_RDONLY */
#include
SETUPSECS   = 4           /* default nr of setup-sectors */
BOOTSEG     = 0x07C0      /* original address of boot-sector */
INITSEG     = DEF_INITSEG     /* we move boot here - out of the way */
SETUPSEG    = DEF_SETUPSEG        /* setup starts here */
SYSSEG      = DEF_SYSSEG      /* system loaded at 0x10000 (65536) */
SYSSIZE     = DEF_SYSSIZE     /* system size: of 16-byte clicks */
ROOT_DEV    = 0           /* ROOT_DEV is now written by "build" */
SWAP_DEV    = 0           /* SWAP_DEV is now written by "build" */
#ifndef SVGA_MODE
#define SVGA_MODE ASK_VGA
#endif
#ifndef RAMDISK
#define RAMDISK 0
#endif
#ifndef CONFIG_ROOT_RDONLY
#define CONFIG_ROOT_RDONLY 1
#endif
.code16
.text
.global _start
_start:
/* 1: 把bootsect从0x7c00->0x90000,共512Bytes */
    movw    $BOOTSEG, %ax
    movw    %ax, %ds
    movw    $INITSEG, %ax
    movw    %ax, %es
    movw    $256, %cx
    subw    %si, %si    /* si = 0 */
    subw    %di, %di    /* di = 0 */
    cld /* 清方向标志,把标志(flags)寄存器的DF=0,地址指针si、di增加;如是std则把DF=1,地址指针减小 */
    rep /* if cx != 0, continue */
    movsw    /* ds:si -> es:di; si = si + 2, di = di + 2 */
    ljmp    $INITSEG, $go /* ljmp $SECTION, $OFFSET */
/* 2: 设置堆栈指针 sp = 0x4000 - 12,ss = 0x9000;栈顶:0x94000 - 12 */
go:
    movw    $0x4000-12, %di  /* (bootsect长度+setup长度+堆栈长度)    movw    %ax, %ds    /* ax and es already contain INITSEG */
    movw    %ax, %ss
    movw    %di, %sp    /* put stack at INITSEG:0x4000-12 */
/* 3:磁盘参数表的中断向量(1Eh)由 0x0000:0x0078->0x9000:0x4000-12;1Eh在0x78开始的4Bytes */
    movw    %cx, %fs    /* set fs to 0; cs, ds, es, ss are all 0x9000 */
    movw    $0x78, %bx        /* fs:bx is parameter table address */
    pushw    %ds
    ldsw    %fs:(%bx), %si /* 把地址fs:bx(0x78)的4Bytes内容给ds:si,4Byte内容即为指向磁盘参数表的指针 ?????*/
    movb    $6, %cl    /* copy 12 bytes */
    pushw    %di /* di = 0x4000-12 */
    rep /* don't need cld -> done above */
    movsw
    popw    %di
    popw    %ds
    movb    $36, 0x4(%di)    /* 设置磁盘扇区数为36; 重置磁盘参数表的原因就是bios可能不支持多个扇区读操作 */
    movw    %di, %fs:(%bx)
    movw    %es, %fs:2(%bx) /* 把中断向量地址指向0x9000:0x4000-12 */
/* 4: 重新启动磁盘,使磁盘参数设置生效 */
load_setup:
    xorb    %ah, %ah    /* ah = 0 -> reset disk */
    xorb    %dl, %dl /* dl = 0 -> driver = 0 */
    int     $0x13 /* reset disk */
   
/* 5: 把setup从第2扇区开始的4个扇区copy到0x90200 */  
    xorw    %dx, %dx    /* dl = 驱动器(drive) = 0, dh = 磁头(head) = 0 */
    movb    $0x02, %cl    /* cl = 开始扇区(sector) = 2, 磁道(track) = 0 */
    movw    $0x0200, %bx /* address = 512, es:bx存放读取的数据; 把setup放在bootsect后 */
    movb    $0x02, %ah    /* ah = 2, 在磁盘中断0x13中的作用是读扇区 */
    movb    setup_sects, %al /* al = 4, 所需读取的扇区数 */
    int $0x13 /* read disk */
    jnc    ok_load_setup    /* jnc根据标志(flag)寄存器中的cf来判断,cf=1不跳转,直接执行下面指令,cf=0则跳转;读磁盘出错cf=1,完成cf=0 */
/* 6: 读磁盘出错则执行以下代码 */
    pushw    %ax /* 把ax值压入堆栈; al = 4, ah = 2, 因为print_nl*/
    call    print_nl /* 打印回车换行 */
    movw    %sp, %bp /* 把sp->bp, ss:bp=ax ?????*/
    call    print_hex /* 打印bp所指向的数据, 即为ax寄存器信息 ?????*/
    popw    %ax
    jmp    load_setup
/* 7:获得磁盘扇区数,由36,18,15,9一个个试 */
ok_load_setup:
    movw    $disksizes, %si  /* 注意disksizes前有"$",表示把扇区表首地址放入si中,用来测试四个扇区 */
probe_loop:
    lodsb /* 指令功能:AL(AX)    cbtw    /* 操作数扩展指令,符号扩展:al->ax */
    movw    %ax, sectors /* 把ax中的值赋值给16位的sectors地址单元 */
    cmpw    $disksizes+4, %si /* 对于36,18,15来说, si - $disksizes     jae    got_sectors  /* jae:(si - $disksizes)大于或等于时跳转, 当扇区值为9时就si = $disksizes+4, 发生跳转 */
   
/* 8: 读磁盘操作,如果扇区不对,则会产生错误,重新跳到probe_loop执行 */
    xchgw    %cx, %ax /* 交换寄存器值,把ax中的扇区数给cx;cx:扇区(sector),磁道(track);即读最大的扇区,如果可以读,扇区数就正确,否则就不正确 */
    xorw    %dx, %dx    /* dl = 驱动器(drive) = 0, dh = 磁头(head) = 0 */
    xorb    %bl, %bl    /* bl = 0x00 */
    movb    setup_sects, %bh /* bh = 0x04 */
    incb    %bh /* bh = 0x05 */
    shlb    %bh /* shl:位运算指令,bh左移一位: (0x05    0xa0; bx = 0x0a00, es:bx即为存放数据缓冲区,数据没什么用,只用来测试*/
    movw    $0x0201, %ax /* ah = 2,为读磁盘; al = 1,读一个扇区(sector) */
    int $0x13 /* 磁盘(disk)中断 */
    jc    probe_loop /* jc:有进位时转移;对于磁盘读操作来讲,cf=1代表出错,cf=0读写正确;所以如果出错就继续probe,否则向下继续执行 */
/* 9: 已取得磁盘扇区数,接下来读取光标的位置,并打印"Loading"信息 */
got_sectors:
    movw    $INITSEG, %ax
    movw    %ax, %es
    movb    $0x03, %ah /* 对中断0x10来说,ah=0x03表示,读取光标的位置(cursor pos) */
    xorb    %bh, %bh /* bh = page number; bh = 0, 图形(graphics)模式 */
    int $0x10 /* 显示(video)中断 */
    movw    $9, %cx /* CX = number of characters in string */
    movw    $0x0007, %bx /* bh = page number = 0, bl = attribute 7 (normal) */
    movw    $msg1, %bp /* 把msg1所在地址给bp */
    movw    $0x1301, %ax /* ah=0x13:write string; al=0x01:update cursor after write */
    int $0x10    /* 打印"Loading", 接下来就要加载内核映像 */
   
/* 10: 加载内核映像 */
    movw    $SYSSEG, %ax
    movw    %ax, %es /* we want to load system (at 0x10000) */
    call    read_it /* 把内核映像从磁盘读到内存0x10000开始处 */
    call    kill_motor /* 读完后关闭磁盘 */
    call    print_nl
/* 11: 根据sectors的大小,来判断root设备 */
    movw    root_dev, %ax
    orw %ax, %ax
    jne    root_defined
   
    movw    sectors, %bx
    movw    $0x0208, %ax /* /dev/ps0 - 1.2Mb */
    cmpw    $15, %bx
    je    root_defined
   
    movb    $0x1c, %al    /* /dev/PS0 - 1.44Mb */
    cmpw    $18, %bx
    je    root_defined
   
    movb    $0x20, %al    /* /dev/fd0H2880 - 2.88Mb */
    cmpw    $36, %bx
    je    root_defined
   
    movb    $0, %al    /* /dev/fd0 - autodetect */
root_defined:
    movw    %ax, root_dev
/* 12: 最后,跳到setup处执行 */
    ljmp    $SETUPSEG, $0
/* 读磁盘,把内核映像加载到0x10000处 */
sread:  .word 0   /* sectors read of current track */
head:   .word 0   /* current head */
track:  .word 0   /* current track */
read_it:
    movb    setup_sects, %al /* al = 4 */
    incb    %al /* al = 5, 4个setup扇区 + 1个bootsect扇区 */
    movb    %al, sread /* sread = 5 */
    movw    %es, %ax /* ax = ex = 0x1000 */
    testw    $0x0fff, %ax /* testw类似于and操作,但其结果不保存,只修改标志寄存器zf位;即要按照0x1000段对齐 */
die:    jne    die /* 不等于时转移,即结果不为0(zf=0)时跳转,结果为0(zf=1)时继续执行; es must be at 64kB boundary */
    xorw    %bx, %bx    /* bx is starting address within segment. es:bx is the buffer */
rp_read:
#ifdef __BIG_KERNEL__
    bootsect_kludge = 0x220    /* 0x200 (size of bootsector) + 0x20 (offset of bootsect_kludge in setup.S) */
    lcall    bootsect_kludge
#else
    movw    %es, %ax
    subw    $SYSSEG, %ax /* ax = 0 */
#endif
    cmpw    syssize, %ax /* ax - syssize; 判断是否加载完成 */
    jbe    ok1_read /* jbe:小于或等于时跳转 */
   
    ret
ok1_read:
    movw    sectors, %ax /* sectors为最大扇区数; 36,18,15,9中的一个数 */
    subw    sread, %ax /* sread=5,5为4个setup扇区+1个bootsect扇区;(ax-sread)代表目前未读的扇区数 */
    movw    %ax, %cx /* cx = ax = 未读的扇区数 */
    shlw    $9, %cx /* cx逻辑左移9位,即cx = 512 * cx,为所有未读扇区的总字节数 */
    addw    %bx, %cx /* 加上段内偏移;由于bx = 0, cx = cx + bx = cx */
    jnc    ok2_read /* jnc:无进位(cf=0)时跳转;16位寄存器最大64k Bytes,当(cx=cx+bx)>=64k时cf=1往下执行,否则跳转 */
   
    je    ok2_read /* 等于跳转, = 64k */
    xorw    %ax, %ax
    subw    %bx, %ax
    shrw    $9, %ax
ok2_read:
    call    read_track   /* 读一个磁道(track) */
    movw    %ax, %cx    /* cx = ax = unread sectors */
    addw    sread, %ax /* ax = ax + sectors = sectors nummers of a track */
    cmpw    sectors, %ax
    jne    ok3_read /* 若(ax - sectors) 不为 0 则跳转 */
   
    movw    $1, %ax
    subw    head, %ax
    jne    ok4_read
   
    incw    track
ok4_read:
    movw    %ax, head /* head = ax */
    xorw    %ax, %ax /* ax = 0 */
ok3_read:
    movw    %ax, sread /* sread = ax */
    shlw    $9, %cx
    addw    %cx, %bx
    jnc    rp_read
   
    movw    %es, %ax
    addb    $0x10, %ah
    movw    %ax, %es
    xorw    %bx, %bx
    jmp    rp_read
/* 读一个磁道(track) */
read_track:
    pusha /* 把AX,CX,DX,BX,SP,BP,SI,DI依次压入堆栈 */
    pusha    /* 把AX,CX,DX,BX,SP,BP,SI,DI依次压入堆栈, 第二次压 */
    movw    $0xe2e, %ax /* 调用显示中断ah=0x0e, al=0x2e, 显示ascii码为2e的值,为一个"."字符 */
    movw    $7, %bx /* bh = page number = 0; bl = 前端颜色 = 7 */
    int $0x10
    popa    /* 还原寄存器值 */
    movw    track, %dx /* dx = track; dh = head number, dl = drive number */
    movw    sread, %cx /* cx = sread = 5 */
    incw    %cx /* cl = cx = 6  = 开始的sector */
    movb    %dl, %ch /* ch = dl = track */
    movw    head, %dx /* dx = head = 磁头 */
    movb    %dl, %dh /* dh = dl = head */
    andw    $0x0100, %dx /* dl = drive(驱动器号为0) */
    movb    $2, %ah /* ah = 2 为读磁盘; 注意这里没有设置al的值,即读多少个扇区,应该是把所有扇区都读完 ????? */
    pushw    %dx /* 把现有寄存器压入堆栈,为了进行错误调试 */
    pushw    %cx
    pushw    %bx
    pushw    %ax
    int $0x13
    jc    bad_rt /* 产生进位(cf=1)表示读出错,跳到bad_rt; 没有出错cf=0,则继续执行 */
   
    addw    $8, %sp /* 读磁盘正确,就不需要ax,bx,cx,dx这四个寄存器,sp=sp+8跳过这几个地址 */
    popa /* 把压入堆栈的DI,SI,BP,SP,BX,DX,CX,AX弹出 */
    ret
/* 搬运内核出错后的执行代码 */
bad_rt:
    pushw    /* ax返回读盘出错后的信息:ah = error, al = 读取磁盘扇区数 */
    call    print_all /* 打印出错后ax的值,以及读磁盘前%dx,%cx,%bx,%ax的值 */
    xorb    %ah, %ah /* ah = 0: reset disk */
    xorb    %dl, %dl /* dl = 0: drive 0 */
    int $0x13 /* reset disk */
    addw    $10, %sp /* 把sp的值+10,跳过了error code,%dx,%cx,%bx,%ax这5个地址 */
    popa /* 把压入堆栈的DI,SI,BP,SP,BX,DX,CX,AX弹出 */
    jmp read_track /* 重新读磁道 */
/* 打印出错后ax的值,以及读磁盘前%dx,%cx,%bx,%ax的值,需要按照压入堆栈的先后来打印 */
print_all:
    movw    $5, %cx    /* 打印出错码 + 四个寄存器的值, 出错码为ax寄存器的值 */
    movw    %sp, %bp /* bp = sp */
print_loop:
    pushw    %cx /* 保存cx记数器的值 */
    call    print_nl    /* 打印回车换行 */
    cmpb    $5, %cl /* cl = cl - 5 */
    jae    no_reg    /* 大于或等于时跳转.当cl=5时直接跳到no_reg,打印出错码.其它则不跳转,打印相应寄存器的名称 */
   
    movw    $0xe05 + 'A' - 1, %ax    /* ah = 0x0e, 打印al; al = 'A' + 0x05 - 1 - cl, 刚好为A,B,C,D四个值 */
    subb    %cl, %al
    int $0x10
    movb    $'X', %al
    int $0x10
    movb    $':', %al
    int $0x10
no_reg:
    addw    $2, %bp    /* 定位bp到相应的栈值 */
    call    print_hex    /* 打印bp所指的值 */
    popw    %cx /* cx 出栈 */
    loop    print_loop   /* cx = cx - 1 */  
    ret
/* 打印回车换行 */
print_nl:
    movw    $0xe0d, %ax /* 对于bios中断0x10来说, ah=0x0e表示打印al的值, al=0x0d表示回车(CR), al=0x0a表示换行(LF) */
    int $0x10
    movb    $0xa, %al
    int     $0x10 /* 0x10为调用屏幕显示 */
    ret
/* 打印调试信息,为SS:BP所指内容,并把其按照16进制数打印.比如0x4321就要打印4,3,2,1四个字符,也就是要完成这四个数字的ascii码转换 */
print_hex:
    movw    $4, %cx    /* 打印四个数字 */
    movw    (%bp), %dx    /* 把bp所指的值放入dx中 */
print_digit:
    rolw    $4, %dx    /* 循环左移4bits */
    movw    $0xe0f, %ax /* ah = 0eh, 打印al值; al = 0fh, 作为低四位的掩码 */
    andb    %dl, %al /* al为dl的最低4bits值 */
    addb    $0x90, %al /* 进行ascii码转换 */
    daa /* 十进制调整; 若al低4bits>9或af=1:al=al+06h,af=1;若al>9fh或cf=1:al=al+60h,cf=1;daa在add或adc后执行,不能单独执行 */
    adc $0x40, %al /* 类似于add,不同的是adc带进位加,进位即为标志寄存器的cf位,即为al=0x40+al+c */
    daa
    int $0x10
    loop    print_digit
    ret
/* 关闭软盘 */
kill_motor:
    movw    $0x3f2, %dx
    xorb    %al, %al
    outb    %al, %dx
    ret
sectors:    .word 0
disksizes:    .byte 36, 18, 15, 9
msg1:       .byte 13, 10
        .ascii "Loading"
/* 启动扇区512Bytes结束,最后两个字节为0xaa55,表示扇区结束 */
.org 497
setup_sects:    .byte SETUPSECS
root_flags:    .word CONFIG_ROOT_RDONLY
syssize:    .word SYSSIZE
swap_dev:    .word SWAP_DEV
ram_size:    .word RAMDISK
vid_mode:    .word SVGA_MODE
root_dev:    .word ROOT_DEV
boot_flag:    .word 0xAA55


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/3448/showart_195681.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP