参考:
https://blog.csdn.net/longintchar/article/details/50602851
https://forum.nasm.us/index.php?topic=991.0
https://www.linuxidc.com/Linux/2015-03/114346p4.htm
1、在16位实模式下,默认访问数据的大小是8位或者16位的;控制转移和内存访问时,偏移量也是16位的;
2、处理器在16位实模式下运行时,可以使用32位的寄存器,执行32位运算;
当处理器运行16位实模式下,既然把所有指令都看成16位的,那么怎么使用32位的寄存器,执行32位的运算呢?
答案是利用指令前缀0x66和0x67。指令前缀0x66用来选择非默认值的操作数大小,0x67用来选择非默认值的地址大小。
比如说:
1)指令码0x40在16位模式下对应的指令是 inc ax;
2)如果加上前缀0x66,也就是指令码66 40,当处理器在16位模式下运行,66 40对应的指令是 inc eax;
同理,如果处理器运行在32位模式下,处理器认为指令是32位的,如果加了0x66,那么就表示指令的操作数是16位的。
[bits 16]
mov cx,dx ;89 D1
mov eax,ebx ;66 89 D8
[bits 32]
mov cx,dx ;66 89 D1
mov eax,ebx ;89 D8
#实际测试结果如下:
[BITS 16]
test1:
mov bx, ax
mov ebx, eax
[BITS 32]
test2:
mov bx, ax
mov ebx, eax
=================================================
Disassembly of section .text:
00000000 <test1>:
0: 89 c3 mov %eax,%ebx
2: 66 89 c3 mov %ax,%bx
00000005 <test2>:
5: 66 89 c3 mov %ax,%bx
8: 89 c3 mov %eax,%ebx
上述结果可见:
1)[BITS 32]场景下和预期一致,通过指令前缀0x66,在32位模式下访问16位寄存器;
2)[BITS 16]场景和预期不一致,第一句访问16位寄存器,反汇编结果却是访问32位寄存器;第二句访问32位寄存器,却插入指令前缀0x66,访问了16位寄存器。还没想明白为什么?
补充:上述[BITS 16]场景之所以和预期不一致,是因为objdump反汇编的时候参数不对。反汇编16位代码需要添加参数 -m i8086. 重新反汇编后结果如下,和预期一致。
00000000 <test1>:
0: 89 c3 mov %ax,%bx
2: 66 89 c3 mov %eax,%ebx
下述引用于 https://forum.nasm.us/index.php?topic=991.0