x86二级Bootloader无错误提示且冻结问题求助
问题修复:二级引导加载程序错误显示与冻结问题
核心问题分析
你的二级引导加载程序出现错误无法显示、程序冻结,主要源于以下几个问题:
- 段寄存器未正确初始化:错误分支中调用
print前未设置DS指向自身数据段,导致无法访问错误提示字符串 - 磁盘读取未做错误检查:读取分区引导扇区时未检查
int 0x13的进位标志,读取失败后仍继续执行非法逻辑 - 文件查找的地址逻辑错误:搜索
kernel.bin时未正确指向分区引导扇区的根目录位置 - 文件读取函数参数错误:
read_file中磁盘读取的磁道/磁头参数未正确设置,且地址更新逻辑混乱
修复后的二级引导加载程序代码
ORG 0x8000 BITS 16 section .data buffer db 16 dup(0) pit_info db 16 dup(0) active_partition_info dd 0 ; 改为双字存储LBA起始扇区 boot_sector_buffer times 512 db 0 starting_message db "Starting the operating system...", 0x0A, 0 missing_pit db "Partition Table error!", 0x0A, 0 no_partition db "Kernel partition cannot be found - does it have the active flag?", 0x0A, 0 file_system_error_message db "Unknown boot partition file system!", 0x0A, 0 kernel_not_found_message db "Kernel file (kernel.bin) not found!", 0x0A, 0 partition_found_message db "Kernel partition found!", 0x0A, 0 disk_read_error db "Disk read error!", 0x0A, 0 file_system_signature db 0x55, 0xAA kernel_filename db "KERNEL BIN" ; FAT12文件名格式:8+3,空格补齐 section .text global start start: ; 初始化段寄存器:二级加载程序在0x8000,段基址为0x0000(因为ORG 0x8000) xor ax, ax mov ds, ax mov es, ax mov ss, ax mov sp, 0x7C00 ; 设置栈在主引导加载程序上方 ; 清屏 mov ah, 0x00 mov al, 0x03 int 0x10 mov si, starting_message call print ; 读取主引导扇区的分区表 mov ah, 0x02 mov al, 0x01 mov ch, 0x00 mov dh, 0x00 mov dl, 0x80 mov bx, buffer int 0x13 jc error_missing_pit ; 查找活动分区 xor cx, cx mov si, buffer find_partition_loop: mov ax, [si + 0x00] ; 检查分区活动标志(第1字节) test al, 0x80 jnz found_bootable add si, 16 inc cx cmp cx, 4 jb find_partition_loop no_bootable: mov si, no_partition call print cli hlt found_bootable: mov di, pit_info mov cx, 16 rep movsb mov eax, [si + 0x08] ; 读取分区起始LBA扇区(4字节) mov [active_partition_info], eax mov si, partition_found_message call print jmp kernel_load error_missing_pit: mov si, missing_pit call print cli hlt ; 字符串打印函数 print: mov bx, 0x0007 ; BH=0(页号),BL=7(白字黑底) print_loop: lodsb cmp al, 0 je print_done mov ah, 0x0E int 0x10 jmp print_loop print_done: ret kernel_load: ; 读取分区引导扇区(LBA地址 = active_partition_info) mov eax, [active_partition_info] call lba_to_chs mov ah, 0x02 mov al, 1 mov bx, boot_sector_buffer int 0x13 jc disk_read_fail ; 检查FAT12签名(引导扇区最后2字节) mov si, boot_sector_buffer + 510 mov di, file_system_signature mov cx, 2 repe cmpsb jnz not_fat12 ; 读取FAT12根目录:根目录起始扇区 = 保留扇区数 + FAT表数 * FAT扇区数 mov ax, [boot_sector_buffer + 0x0E] ; 保留扇区数 mov bx, [boot_sector_buffer + 0x16] ; FAT扇区数 mov cx, [boot_sector_buffer + 0x10] ; FAT表数 mul cx add ax, [active_partition_info] mov [active_partition_info], eax ; 更新为根目录起始LBA mov ah, 0x02 mov al, [boot_sector_buffer + 0x11] ; 根目录项数 / 每扇区目录项数(16) = 根目录扇区数 xor ah, ah mov cl, 4 shr ax, cl call lba_to_chs mov bx, 0x0200 ; 根目录读取到0x0200位置 int 0x13 jc disk_read_fail jmp search_file not_fat12: mov si, file_system_error_message call print cli hlt disk_read_fail: mov si, disk_read_error call print cli hlt search_file: mov si, 0x0200 ; 根目录起始地址 mov cx, [boot_sector_buffer + 0x11] ; 根目录项数 search_loop: mov di, kernel_filename mov cx, 11 repe cmpsb je file_found add si, 32 dec cx jnz search_loop file_not_found: mov si, kernel_not_found_message call print cli hlt file_found: ; 获取第一个簇号 mov ax, [si + 0x1A] mov cx, ax ; 起始簇号 mov ax, 0x1000 mov es, ax mov bx, 0 read_file: ; 簇号转LBA:簇号 - 2 + FAT起始扇区 + 保留扇区数 + 分区起始LBA mov ax, cx sub ax, 2 mov bx, [boot_sector_buffer + 0x16] ; FAT扇区数 mul bx add ax, [boot_sector_buffer + 0x0E] ; 保留扇区数 add eax, [active_partition_info] ; 分区起始LBA call lba_to_chs mov ah, 0x02 mov al, [boot_sector_buffer + 0x0D] ; 每簇扇区数 int 0x13 jc disk_read_fail add bx, ax ; 更新ES:BX指针(ax是读取的扇区数*512) mov ax, cx mov bx, 3 mul bx shr ax, 1 ; FAT12项地址 = 簇号 * 3 / 2 mov si, ax add si, boot_sector_buffer + [boot_sector_buffer + 0x0E] * 512 ; FAT表起始地址 mov ax, [si] test cx, 1 jz even_cluster shr ax, 4 even_cluster: and ax, 0x0FFF mov cx, ax cmp cx, 0x0FF8 ; 检查是否为最后一簇 jb read_file jmp 0x1000:0000 ; LBA转CHS函数:输入EAX=LBA地址,输出CH=柱面,DH=磁头,CL=扇区 lba_to_chs: push bx mov bx, [boot_sector_buffer + 0x18] ; 每磁道扇区数 div bx inc dl mov cl, dl xor dx, dx mov bx, [boot_sector_buffer + 0x1A] ; 磁头数 div bx mov dh, dl mov ch, al pop bx ret
关键修复点说明
- 段寄存器初始化:在
start开头就设置DS、ES、SS为0x0000,确保所有数据访问都指向正确的内存地址 - 磁盘读取错误检查:所有
int 0x13调用后都添加jc跳转处理磁盘读取失败 - FAT12文件系统逻辑修正:
- 正确计算根目录和文件簇的LBA地址
- 使用标准的LBA转CHS转换(兼容BIOS中断要求)
- 修正文件名格式为FAT12的8+3空格补齐格式
- 错误分支的字符串访问:所有错误分支调用
print前确保DS已正确设置,能正常读取提示字符串
内容的提问来源于stack exchange,提问作者Florin Voicu
相关产品推荐
相关产品推荐

