从零手写Boot Loader:未设置ES寄存器为何仍能正常工作?
我正在跟着Nick Blundell的《Writing tiny OS from scratch》学习从零编写操作系统,已经完成了大部分内容和习题。在学习磁盘扇区读取章节时,发现书中示例代码存在疑问:
代码中有一行注释标注:
mov bx, 0x9000 ;load 5 sectors to 0x0000(ES):0x9000(BX) Why?
但完整示例代码中并未对ES寄存器进行任何设置,完整代码如下:
[org 0x7c00] ;;; read secotrs from the boot disk mov [BOOT_DRIVE], dl ;bios stores our drive in dl, remember later mov bp, 0x8000 mov sp, bp mov bx, 0x9000 ;load 5 sectors to 0x0000(ES):0x9000(BX) Why? mov dh, 5 ; this is an error by the book, shall be 2 ; i've added however the missing 3 sectors mov dl, [BOOT_DRIVE] call disk_load mov dx, [0x9000] call print_hex mov dx, [0x9000 + 512] ;first call print_hex mov dx, [0x9000 + 512 + 512] ;second call print_hex mov dx, [0x9000 + 512 + 512 + 512] ;third call print_hex mov dx, [0x9000 + 512 + 512 + 512 + 512] ;fourth call print_hex jmp $ %include "../utils/print_string.asm" %include "../utils/print_hex.asm" %include "../utils/disk_load.asm" ;;; variables for later BOOT_DRIVE: db 0 times 510-($-$$) db 0 dw 0xaa55 ;;; we know bios will load only first 512 bytes from disk so we in purpose add ;;; more sectors to our code by repeating some familiar numbers so we can prove ;;; we actually loaded those 5 sectors from the disk times 256 dw 0xdada times 256 dw 0xface times 256 dw 0xdead times 256 dw 0xbeef times 256 dw 0x1111
我已修正书中的错误(将dh的值改为2并补充了对应扇区数据),且代码能通过以下脚本正常编译运行并输出正确结果:
#!/bin/bash nasm boot_sec.asm -f bin -o boot_sec.bin qemu-system-amd64 boot_sec.bin
但我有两个疑问:
- 未设置ES寄存器却能正常工作的原理是什么?
- 如何在原示例中更明确地体现ES与BX的关联?
未设置ES却正常工作的原因
BIOS在将引导扇区加载到内存0x7c00并跳转执行时,ES寄存器的初始值默认是0x0000。这是x86实模式下BIOS引导的标准行为——绝大多数BIOS会将段寄存器(包括ES、DS等)初始化为0。
实模式下物理地址的计算公式是:段基址 × 16 + 偏移量。当ES=0时,ES:BX对应的物理地址就是0×16 + 0x9000 = 0x9000,和你代码中直接访问[0x9000]的物理地址完全一致。因此磁盘加载的数据会被写入正确位置,后续读取自然能得到预期结果。
需要注意:这个默认行为是BIOS的通用约定,但并非所有老旧BIOS都严格遵守,显式设置ES是更严谨的做法,能避免潜在的兼容性问题。
如何明确体现ES与BX的关联
要在代码中清晰展示ES和BX的配合逻辑,只需在设置BX之前显式将ES设置为0即可,同时可以优化注释让意图更明确:
修改代码中的对应片段:
mov ax, 0x0000 ; 显式初始化段寄存器ES为0 mov es, ax mov bx, 0x9000 ; 磁盘数据将被加载到ES:BX指向的物理地址(0x0000*16 + 0x9000 = 0x9000)
如果想进一步验证ES的作用,还可以尝试调整段寄存器和偏移量的组合:比如将ES设为0x0900,BX设为0x0000,此时ES:BX对应的物理地址仍是0x0900×16 + 0x0000 = 0x9000,运行后结果和之前一致,能直观验证实模式下地址计算的逻辑。
内容的提问来源于stack exchange,提问作者Ilian Zapryanov

