- 论坛徽章:
- 0
|
#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 |
|