4. 汇编语言基础(x86):常用指令集、寄存器结构、堆栈操作与中断调用
汇编语言是BIOS/UEFI固件开发的核心工具。x86汇编直接操作CPU寄存器、内存和I/O端口,是理解固件启动流程、硬件抽象层(HAL)和实模式/保护模式切换的基石。本章聚焦于最常用的指令集、寄存器架构、堆栈操作以及BIOS中断调用机制。
4.1 x86寄存器结构
x86架构的寄存器分为通用寄存器、段寄存器、指令指针寄存器和标志寄存器。在BIOS开发的实模式(16位)下,寄存器宽度为16位;进入保护模式后,扩展为32位(EAX、EBX等)。
| 类别 | 寄存器 | 16位名称 | 32位名称 | 主要用途 |
|---|---|---|---|---|
| 通用寄存器 | 累加器 | AX | EAX | 算术运算、I/O操作、中断服务 |
| 基址寄存器 | BX | EBX | 内存基址、查表 | |
| 计数寄存器 | CX | ECX | 循环计数、移位次数 | |
| 数据寄存器 | DX | EDX | I/O端口地址、乘除扩展 | |
| 索引/指针 | 源变址 | SI | ESI | 字符串操作源地址 |
| 目的变址 | DI | EDI | 字符串操作目的地址 | |
| 堆栈指针 | 堆栈指针 | SP | ESP | 栈顶偏移地址 |
| 基址指针 | BP | EBP | 栈帧基址 | |
| 指令指针 | 程序计数器 | IP | EIP | 下一条指令偏移地址 |
| 标志寄存器 | 状态标志 | FLAGS | EFLAGS | 运算结果状态(CF、ZF、SF等) |
BIOS开发提示: 在实模式下,段寄存器(CS、DS、ES、SS)与通用寄存器组合形成20位物理地址(段基址×16 + 偏移)。这是理解BIOS中断调用参数传递的关键。
4.2 常用指令集
4.2.1 数据传送指令:MOV
MOV 是最基础的指令,用于在寄存器、内存和立即数之间复制数据。注意:源操作数和目的操作数不能同时为内存地址。
; 立即数送寄存器
MOV AX, 0x1234 ; AX = 0x1234
; 寄存器间传送
MOV BX, AX ; BX = AX
; 内存到寄存器
MOV CX, [0x7C00] ; 将地址0x7C00处的字数据送入CX
; 寄存器到内存
MOV [0x6000], DX ; 将DX内容存入地址0x6000
4.2.2 算术运算指令:ADD / SUB
ADD 和 SUB 影响标志寄存器中的 CF(进位)、ZF(零)、SF(符号)和 OF(溢出)位。
MOV AX, 0x1000
ADD AX, 0x2000 ; AX = 0x3000, ZF=0, CF=0
MOV BX, 0x0005
SUB BX, 0x0008 ; BX = 0xFFFD (补码), CF=1 (借位)
4.2.3 控制转移指令:JMP / CALL / RET
- JMP:无条件跳转,改变IP(或EIP)的值。
- CALL:先将返回地址(下一条指令的地址)压入堆栈,然后跳转到目标地址。
- RET:从堆栈弹出返回地址,恢复执行流。
; 无条件跳转
JMP 0x7C00 ; 跳转到地址0x7C00
; 调用子程序
CALL delay_loop ; 调用子程序,返回地址自动入栈
; 子程序返回
RET ; 从栈顶弹出地址并跳转
4.2.4 中断调用指令:INT
INT n 触发编号为 n 的软件中断。BIOS在实模式下通过中断向量表(IVT)提供服务。最常用的是 INT 0x10(视频服务)和 INT 0x13(磁盘服务)。
; 使用INT 0x10显示一个字符
MOV AH, 0x0E ; 功能号:TTY输出
MOV AL, 'A' ; 要显示的字符
MOV BH, 0x00 ; 页码
INT 0x10 ; 调用BIOS视频中断
关键点: 调用BIOS中断前,必须将功能号放入AH寄存器,参数放入其他指定寄存器。中断返回后,结果通常存放在AX或标志寄存器中。
4.3 堆栈操作
堆栈是一种后进先出(LIFO)的数据结构,由SS(堆栈段)和SP(堆栈指针)共同管理。堆栈在子程序调用、中断处理和临时数据保存中至关重要。
4.3.1 压栈与出栈
- PUSH:先将SP减2(16位)或4(32位),然后将数据存入SP指向的地址。
- POP:从SP指向的地址读取数据,然后将SP加2或4。
PUSH AX ; 将AX压入堆栈
PUSH BX ; 将BX压入堆栈
POP BX ; 弹出到BX(后进先出,BX恢复原值)
POP AX ; 弹出到AX
4.3.2 堆栈在子程序调用中的作用
CALL指令自动将返回地址压栈,RET指令自动弹出。嵌套调用时,堆栈保存了完整的调用链。
4.4 BIOS中断调用详解
BIOS中断是固件提供给实模式程序的标准化服务接口。每个中断对应一个功能类别,通过AH寄存器选择具体功能。
| 中断号 | 服务类别 | 常用功能(AH) | 输入参数 | 输出结果 |
|---|---|---|---|---|
| INT 0x10 | 视频服务 | 0x0E:TTY输出 0x00:设置显示模式 | AL=字符 AL=模式号 | 无 无 |
| INT 0x13 | 磁盘服务 | 0x02:读扇区 0x03:写扇区 | CH=柱面, CL=扇区, DH=磁头, DL=驱动器, ES:BX=缓冲区 | AH=状态码, CF=0成功/1失败 |
| INT 0x16 | 键盘服务 | 0x00:读按键 0x01:检测按键 | 无 | AH=扫描码, AL=ASCII码 |
| INT 0x1A | 时间服务 | 0x00:读系统时间 | 无 | CX:DX=时钟滴答数 |
实模式限制: BIOS中断只能在实模式下使用。进入保护模式或长模式后,必须通过UEFI Runtime Services或直接硬件访问来替代。在UEFI BIOS开发中,传统INT调用被EFI Boot Services和Protocol接口取代。
4.5 综合示例:使用BIOS中断读取磁盘引导扇区
以下代码展示了如何在实模式下使用 INT 0x13 读取硬盘的主引导记录(MBR)到内存地址 0x7C00:
; 读取磁盘第一个扇区(MBR)到 0x7C00
mov ax, 0x07C0
mov es, ax ; ES = 0x07C0
xor bx, bx ; BX = 0, 缓冲区 = ES:BX = 0x7C00
mov ah, 0x02 ; 功能号:读扇区
mov al, 0x01 ; 读取1个扇区
mov ch, 0x00 ; 柱面0
mov cl, 0x01 ; 扇区1
mov dh, 0x00 ; 磁头0
mov dl, 0x80 ; 驱动器:第一个硬盘
int 0x13 ; 调用BIOS磁盘中断
jc read_error ; 如果CF=1,跳转到错误处理
; 读取成功,跳转到0x7C00执行MBR
jmp 0x07C0:0x0000
read_error:
; 错误处理代码(略)
hlt
调试建议: 在QEMU或Bochs中单步执行上述代码,观察寄存器变化和内存内容。使用
info registers 和 xp /16xw 0x7C00 验证读取结果。
4.6 本章小结
- 寄存器:通用寄存器(AX/BX/CX/DX)、指针寄存器(SP/BP)、段寄存器(CS/DS/ES/SS)和标志寄存器(FLAGS)构成了x86编程的基础。
- 指令集:MOV负责数据搬运,ADD/SUB执行算术运算,JMP/CALL/RET控制程序流程,INT触发系统服务。
- 堆栈:通过PUSH/POP实现LIFO管理,是子程序嵌套和中断处理的核心机制。
- 中断调用:BIOS通过INT指令提供标准硬件抽象层,实模式下是访问磁盘、键盘、显示器的唯一标准途径。