16位汇编开发中汇编器为何将[bx]操作数替换为ds:[edi]
问题根因
这是汇编器位模式设置错误导致的指令编码异常,和硬件、BIOS中断逻辑无关:
- 你看到的机器码前缀
67是x86架构的地址尺寸覆盖前缀:16位实模式下CPU默认按16位规则解释寻址编码,一旦遇到67前缀,就会把后续ModR/M字节按32位寻址规则解析。 - 你编写的
mov dl,[bx]对应ModR/M字节为0x17:- 按16位寻址规则解析:rm字段值为111,对应操作数
[bx],是你预期的正确行为 - 按32位寻址规则解析:rm字段值为111,对应操作数
[edi],就是Bochs调试器中看到的错误指令
- 按16位寻址规则解析:rm字段值为111,对应操作数
- 指令中多出
67前缀的核心原因:你通过%include "printer.asm"加载打印函数的位置,汇编器已经被切换到了[bits 32]模式。32位模式下汇编[bx]这类16位寄存器寻址时,会自动添加67前缀提示CPU"此处按16位规则解析寻址",但你调用print_str时CPU还运行在16位实模式下,看到67前缀反而会切换到32位寻址规则解析,直接把源操作数识别成了[edi]。
这个逻辑完全匹配你观察到的所有现象:
- 函数体内联到
call位置时运行正常:call指令所在位置还处于[bits 16]作用域,汇编出的指令没有多余的67前缀 - 用
call调用函数时触发异常:函数定义被插入到了bits 32的作用域中,汇编出的指令带了错误的地址前缀 - 无限打印乱码:edi寄存器中是随机垃圾值,CPU读不到字符串结尾的
0,就会一直循环读取非法地址打印内容
验证方法
对比两个位置生成的机器码即可确认:
- 内联版本的
mov dl,[bx]机器码应为8A 17,无额外前缀 - 被
call调用的函数版本中,同一条指令的机器码为67 8A 17,多了67前缀,和你在Bochs中看到的结果完全一致
对应Bochs调试器界面截图如下:
解决方法
两种方案二选一即可:
- 方案1:把
%include "printer.asm"移动到所有切换[bits 32]的代码之前,保证汇编器处理print_str函数时处于16位模式 - 方案2:在
printer.asm文件的最开头,强制添加[bits 16]声明,让该文件内的代码不受外部位模式设置的影响
附问题涉及的原始代码片段:print_str函数实现:
;BX = ptr to null terminaetd string print_str: pusha next_char: mov dl,[bx] test dl,dl jz print_str_return mov ah,0x0E mov al,dl int 0x10 inc bx jmp next_char print_str_return: popa ret
boot.asm核心逻辑:
[extern kernel_entry] [bits 16] mov [BOOT_DRIVE], dl mov ax,0 mov ds,ax mov es,ax mov ss,ax ;Set up a temporary stack (~65kB) mov ax,0xFFFF mov sp,ax mov bp,sp ... mov bx,msg call print_str ... %include "printer.asm" msg: db "test string",0 ...
内容的提问来源于stack exchange,提问作者MottLx
相关产品推荐
相关产品推荐

