.code16 real_start: cli cld xor ax,ax mov ss,ax mov ds,ax mov bp,HEX(7c00) lea sp,[bp-32] sti mov ax,HEX(1FE0) mov es,ax mov si,bp mov di,bp mov cx,256 rep movsw首先将整个数据从1FE0:7C00处给复制到0:7C00处;然后对复制过去的数据进行验证。
ljmp16 HEX(1FE0),cont cont: mov ds,ax xor ax,ax mov es,ax lea di,[bp + HEX(1be)] // start of partition table test_next_for_active: test byte ptr ds:[di],HEX(80) jne active_partition_found add di,16 // next table cmp di,HEX(07c00) + HEX(1fe) // scanned beyond end of table ?? jb test_next_for_active首先,跳转的时候会将CS给设置为1FE0。然后设置其他的段寄存器,注意上面的DS没有设置成1FE0,所以是复制到0:7C00,然后找到活动的主目录。
call print .asciz "no active partition found" WAIT_FOR_REBOOT: jmp WAIT_FOR_REBOOT trouble_reading_drive: call print .asciz "read error while reading drive" jmp WAIT_FOR_REBOOT invalid_partition_code: call print .asciz "partition signature != 55AA" jmp WAIT_FOR_REBOOT active_partition_found: call read_boot_sectorjc trouble_reading_drive如果没有找到却推出循环,那么就要打印出“no activate partition found"字符串,下面先看看这个打印函数,这个函数的实现很有趣,很类似于C语言的函数调用。
print_1char: xor bx,bx // video page 0 mov ah,HEX(0E) // else print it int HEX(10) // via TTY mode print: pop si // this is the first character print1: lodsb // get token push si // stack up potential return address cmp al,0 // end of string? jne print_1char // until done ret // and jump to it首先,执行的第一条语句是pop si,然后由将si当中的额字符经过lodsb指令传递给al,这里由于si指向整个字符串,这些字符串由伪指令.asciz放入到堆栈当中,同时这里也是需要执行的下一条指令,所以需要压栈,以便于后面的ret执行。如果需要打印消息了,就死循环等待用户按下开机重启键。而如果找到相应的活动分区,就执行读MBR操作。read_boot_sector: mov bx,HEX(55aa) mov ah,HEX(41) int HEX(13)//利用系统中断对参数进行再一次验证,如果置进位并且BX不等于AA55,则表明返回错误 jc StandardBios // cmp bx,HEX(0aa55) // jne StandardBios test cl,1 //如果CX的最低位为1,表明使用包结构访问数据,否则返回错误 jz StandardBios jmp short LBABios _bios_LBA_address_packet: .byte 16 .byte 0 .byte 4 // read four sectors - why not .byte 0 .word HEX(7c00) // fixed boot address for DOS sector .word HEX(0000) _bios_LBA_low: .word 0 _bios_LBA_high: .word 0 .word 0,0 LBABios: mov ax,[di + 8] mov word ptr ds:[_bios_LBA_low],ax mov ax,[di + 8 + 2] mov word ptr ds:[_bios_LBA_high],ax mov ax,HEX(4200) // regs.a.x = LBA_READ; mov si,offset _bios_LBA_address_packet // regs.si = FP_OFF(&dap); int HEX(13) ret
INT13 AH=42用于读取磁盘的数据,调用的时候需要传入三个参数,AH=42表明调用函数的序号,DL表示磁盘驱动的序号,在这里整个DL一直没有改变,所以是在读取硬盘分区表的时候就存在的,DS:SI在上面的函数当中进行设置.首先通过两条mov指令设置_bios_LBA_high和_bios_LBA_low,然后在后面将_bios_LBA_address_packet 的偏移放到SI当中。其中整个SI所指向的地址的数据要求如下:
00h |
1 byte |
整个数据区域的大小,为16 |
01h |
1 byte |
未用,必须为0 |
02h..03h |
2 bytes |
需要读取多少个扇区,这里是4 |
04h..07h |
4 bytes |
整个扇区将被读入内存的偏移地址 |
08h..0Fh |
8 bytes |
从哪里开始读,这八个字节由上面的mov指令实现 |
硬盘分区表的结构
字节偏移 |
字段名及说明 |
00 |
活动分区标志,只能是00H和80H。80H为活动,00H为非活动。 |
01 - 03 |
开始柱面(Starting Cylinder)、开始磁头(Starting Head)、开始扇区(Starting Sector) |
04 |
分区的类型 |
05 - 07 |
结束柱面(Ending Cylinder)、结束磁头(Ending Head)、结束扇区(Ending Sector |
08 – 0B |
相对扇区数(Relative Sectors)从磁盘的开始到该分区开始的偏移量,可以看成是分区在硬盘中的起始地址。 |
0C – 0F |
总扇区数(Total Sectors)为该分区的扇区总数。通过4个字节可以表示2^32个扇区。 |
cmp word ptr es:[HEX(7c00)+HEX(1fe)],HEX(0aa55) jne invalid_partition_code ljmp16 0,HEX(7c00)
在进行简单地比较之后,就直接跳转到7C00位置开始执行。