编译时:
#include <inttypes.h>
void foo(void)
{
*(uint16_t *) (0xb8000) = 0xf61;
}
与
gcc test.c -c -m16 -O1
我收到以下警告:
/tmp/ccyziKm4.s: Assembler messages:
/tmp/ccyziKm4.s:9: Warning: 753664 shortened to 32768
当我删除-O1
开关时,我得到no,gcc使用0x67
前缀按预期切换地址大小(-m16
基本上发出前缀32位代码):< / p>
00000000 <foo>:
0: 66 55 push %ebp
2: 66 89 e5 mov %esp,%ebp
5: 66 b8 00 80 0b 00 mov $0xb8000,%eax
b: 67 c7 00 61 0f movw $0xf61,(%eax)
10: 90 nop
11: 66 5d pop %ebp
13: 66 c3 retl
所以,显然这与优化开关-O1
有关。 gcc手册页描述了它设置的所有选项,我编写了一个脚本来单独列出每个选项并将它们传递给gcc,但它并没有真正起作用。现在,gcc根本没有显示警告,即使是整个警告。
我很感激有关如何解决此问题的任何建议。
答案 0 :(得分:1)
我会说这是gcc中的错误,但我看到了这种行为背后的一些逻辑:
没有优化的GCC使用2条指令生成非常简单的代码(我更喜欢intel语法):
mov eax, 0xb8000 # move value 0xb8000 to eax
movw [eax], 0xf61 # move value 0xf61 to address stored in eax
二进制视图:
66 b8 00 80 0b 00
^ operation: move 16 bit value to 16 register ax
^ size override prefix to indicate that 32 bit data is used instead of 16 bit, so eax should be used instead of ax
66 c7 00 61 0f
^ operation: move 16 bit value to 16 address in ax
^ size override prefix
GCC with optimization尝试优化,因此它生成以下代码:
movw [0xb8000], 0xf61 # mov value 0xf61 directly to 32 bit address 0xb8000 without any intermediate registers
二进制视图:
66 c7 05 00 80 0b 00 61 0f
^ operation: move 16 bit value to 16 bit address
^ size override prefix
因此,32位操作码实际上与16位操作码相同,但使用66/67 prefix。
这是问题所在:
movw [REGISTER], 0xf61
合法且在16/32模式下均得到官方支持movw [0xb8000], 0xf61
是合法的,但值&gt; 16位(0xffff)在16实模式下不受官方支持,32位受保护 - 它们是官方支持的这就是编译器发出警告并将值0xb8000截断为0x8000以生成合法和官方支持的指令的原因。
注意:我相信gcc也应该在第一种情况下发出警告,因为它不能像你预期的那样工作16位:
eax
不能超过0xffff
(实际上它不会使用eax
,而只会使用ax
部分。eax
。我不知道为什么gcc允许你使用m16
标志,而不能正确支持16位代码生成和实模式内存模型。我建议你改用别的东西。 20年前,watcom非常酷。
如果你处于虚幻模式,它会自动意味着你可以而且应该使用m32指令。