You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

关键修复点说明

  1. 段寄存器初始化:在start开头就设置DS、ES、SS为0x0000,确保所有数据访问都指向正确的内存地址
  2. 磁盘读取错误检查:所有int 0x13调用后都添加jc跳转处理磁盘读取失败
  3. FAT12文件系统逻辑修正:
    • 正确计算根目录和文件簇的LBA地址
    • 使用标准的LBA转CHS转换(兼容BIOS中断要求)
    • 修正文件名格式为FAT12的8+3空格补齐格式
  4. 错误分支的字符串访问:所有错误分支调用print前确保DS已正确设置,能正常读取提示字符串

内容的提问来源于stack exchange,提问作者Florin Voicu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 09:08:10