3. BIOS中断调用与实模式内存布局
任务目标
编写一个引导扇区程序,在实模式下通过BIOS中断 INT 10h 在屏幕上显示字符,并借助BIOS内存检测中断 INT 15h, E820h 读取实模式内存布局,将内存区域信息打印到屏幕。程序将被写入虚拟软盘镜像的首扇区,由QEMU启动执行。
环境与依赖
- 汇编器:NASM(
nasm) - 虚拟机:QEMU(
qemu-system-x86_64) - 操作系统:Linux(本文以Ubuntu/Debian为例)
实现原理
引导扇区代码从 0x7C00 开始执行,此时CPU处于实模式,段寄存器指向BIOS数据区。程序首先通过 INT 10h 的 0x0E 功能(TTY字符输出)显示标题字符串。随后调用 INT 15h 的 E820h 功能遍历系统内存映射,每次调用返回一个内存区域描述符(20字节),包含起始地址、长度和类型。程序将每个区域的起始地址、长度和类型以十六进制格式打印到屏幕,直到返回 0x00 表示遍历结束。
关键完整代码
; boot.asm - BIOS中断调用与实模式内存布局
; 汇编: nasm -f bin boot.asm -o boot.bin
; 运行: qemu-system-x86_64 -fda boot.bin
BITS 16
ORG 0x7C00
start:
; 初始化段寄存器
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7C00
; 显示标题
mov si, msg_title
call print_string
; 使用INT 15h E820h检测内存布局
mov di, 0x0500 ; 描述符缓冲区(避开栈和代码)
xor ebx, ebx ; 首次调用必须为0
mov bp, di ; 保存缓冲区地址
detect_loop:
mov eax, 0xE820
mov ecx, 20 ; 描述符大小
mov edx, 0x534D4150 ; 'SMAP'签名
int 0x15
jc .error ; CF=1表示出错
cmp eax, 0x534D4150 ; 检查签名
jne .error
; 打印当前区域信息
mov si, msg_addr
call print_string
mov eax, [bp] ; 起始地址低32位
call print_hex32
mov si, msg_len
call print_string
mov eax, [bp+8] ; 长度低32位
call print_hex32
mov si, msg_type
call print_string
mov eax, [bp+16] ; 类型
call print_hex32
call newline
; 检查是否还有更多区域
test ebx, ebx
jz .done
add di, 20 ; 指向下一个描述符
mov bp, di
jmp detect_loop
.error:
mov si, msg_error
call print_string
jmp halt
.done:
mov si, msg_done
call print_string
halt:
hlt
jmp halt
; 打印以SI指向的以0结尾的字符串
print_string:
lodsb
or al, al
jz .ret
mov ah, 0x0E
int 0x10
jmp print_string
.ret:
ret
; 打印换行
newline:
mov al, 0x0D
mov ah, 0x0E
int 0x10
mov al, 0x0A
int 0x10
ret
; 以十六进制打印EAX中的32位值
print_hex32:
push eax
shr eax, 16
call print_hex16
pop eax
call print_hex16
ret
print_hex16:
push eax
shr eax, 8
call print_hex8
pop eax
call print_hex8
ret
print_hex8:
push eax
shr eax, 4
call print_hex4
pop eax
call print_hex4
ret
print_hex4:
pusha
and al, 0x0F
add al, '0'
cmp al, '9'
jle .digit
add al, 7 ; 'A'-'9'-1
.digit:
mov ah, 0x0E
int 0x10
popa
ret
msg_title db 'BIOS Memory Map:', 0x0D, 0x0A, 0
msg_addr db ' Addr: 0x', 0
msg_len db ' Len: 0x', 0
msg_type db ' Type: 0x', 0
msg_error db 'E820 error!', 0x0D, 0x0A, 0
msg_done db 'Done.', 0x0D, 0x0A, 0
times 510-($-$$) db 0
dw 0xAA55
编译与运行
nasm -f bin boot.asm -o boot.bin
qemu-system-x86_64 -fda boot.bin
验证方法
QEMU窗口打开后,屏幕应显示以下内容:
- 第一行标题
BIOS Memory Map:,证明INT 10h字符显示功能正常。 - 随后逐行列出内存区域,每行包含
Addr(起始地址)、Len(长度)和Type(类型),均为十六进制格式。 - 最后一行显示
Done.,表示遍历完成。
典型输出中,第一行应为 Addr: 0x00000000 Len: 0x0009FC00 Type: 0x00000001(类型1表示可用内存),最后一行通常为 Addr: 0xFFFC0000 Len: 0x00040000 Type: 0x00000002(类型2表示保留内存)。若程序显示 E820 error!,说明当前BIOS不支持E820功能(QEMU默认支持,此情况极少发生)。
实模式内存布局说明
程序运行时的内存布局如下:
0x00000000 - 0x000003FF:中断向量表(IVT),共1024字节,存放256个中断处理程序入口。0x00000400 - 0x000004FF:BIOS数据区(BDA),存放键盘状态、显示模式等系统信息。0x00000500 - 0x00007BFF:可用常规内存,本程序将E820描述符缓冲区放在0x0500处。0x00007C00 - 0x00007DFF:引导扇区加载地址,即当前代码所在位置。0x000F0000 - 0x000FFFFF:BIOS ROM区域,包含BIOS中断服务例程。
E820返回的内存区域描述符中,类型1为操作系统可用内存,类型2为保留内存,类型3为ACPI可回收内存,类型4为ACPI NVS(非易失性存储)。