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

16位汇编开发中汇编器为何将[bx]操作数替换为ds:[edi]

问题根因

这是汇编器位模式设置错误导致的指令编码异常,和硬件、BIOS中断逻辑无关:

  1. 你看到的机器码前缀67是x86架构的地址尺寸覆盖前缀:16位实模式下CPU默认按16位规则解释寻址编码,一旦遇到67前缀,就会把后续ModR/M字节按32位寻址规则解析。
  2. 你编写的mov dl,[bx]对应ModR/M字节为0x17:
    • 按16位寻址规则解析:rm字段值为111,对应操作数[bx],是你预期的正确行为
    • 按32位寻址规则解析:rm字段值为111,对应操作数[edi],就是Bochs调试器中看到的错误指令
  3. 指令中多出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调试器界面截图如下:
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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 16:39:19