我写了这个汇编程序。
.section .data
1: .asciz "Hello"
.section .text
entry:
mov $0x07C0, %ax
add $0x120, %ax
mov %ax, %ss
mov 0x100, %sp
mov $0x7C0, %ax
mov %ax, %ds
# mov $1b, %si
mov $0xE, %ah
mov $0x0, %si
mov $0x0, %bx
push %bp
mov %sp, %bp
mov %di, -20(%bp)
mov %si, -32(%bp)
movl $0x0, -4(%ebp)
.loopcond:
cmpl $127, -4(%ebp)
jge .halt
.print:
lodsb
int $0x10
add $0x1, -4(%ebp)
jmp .loopcond
.halt:
jmp .halt
第一条指令 .loopcond
部分将变量比较为127(就像一个迭代127次的for循环)。这样做很好,在跳转到 .halt
. 然而,当我增加要比较的值时(如128),代码似乎跳到了 .halt
立即。我不明白为什么会出现这种情况。是关于有符号整数比较的事情吗?
我看了objdump,有一次是127和128。
// 127:
00000037 <.loopcond>:
37: 83 7d fc 7f cmpl $0x7f,-0x4(%ebp)
3b: 7d 09 jge 46 <.halt>
// 128:
00000037 <.loopcond>:
37: 81 7d fc 80 00 00 00 cmpl $0x80,-0x4(%ebp)
3e: 7d 09 jge 49 <.halt>
我注意到操作数 cmpl
指令在128的例子中是4个字节长,而在127的例子中只有1个字节。我怀疑是这方面的原因导致了这个错误。
解决方案:
您的问题可能与以下方面有关 add $0x1, -4(%ebp)
其中使用了一个模糊的操作数大小。 如果GAS选取字节操作数大小,可能会引起问题? 虽然如果上面的字节是零,那就只是零扩展到dword中。 你的问题原因并不明显,但你把BP和EBP的16位和32位地址大小混在一起就很奇怪了。
说真的,只要在寄存器里放一个数字,然后用循环 dec reg
jnz
像个正常人一样。
或者使用调试器来查看内存,并整理出发生了什么。 你的 cmpl $127, -4(%ebp)
确实指定了操作数大小,所以它肯定是在做一个字比较,而不是处理为 128
作为 -128
与8位2的补码。
我注意到,在128的例子中,cmpl指令的操作数有4个字节长,而在127的例子中只有1个字节。我怀疑这一点是造成这个错误的原因。
这不是一个错误。 大多数基本的x86整数ALU指令都有一个操作码,一个版本有32位的立即,另一个版本有一个 符号扩展的8位立即.
在原来的8086上,这节省了1个字节的指令,如 cmp r/m16, imm8
与。cmp r/m16, imm16
. 在3264位代码中,这为imm8与imm32节省了3个字节。 https:/www.felixcloutier.comx86cmp 列出了可用的表格。
截止点当然是-128 … +127,因为它是一个 标志-扩展的立即。 你的汇编器总是为给定的asm源码行选择尽可能小的编码,所以一切都按预期工作。
如果你在32位模式下组装,但以16位模式运行。cmpl $imm32, r/m32
将以不同于其他代码的方式中断.
其他指令无论模式如何都是相同的长度,但运行时操作数大小相反(16与32)。 但是操作码为 cmpl
和 cmpw
是一样的;不同的只是操作数大小(切换到非默认的模式值a)。66
前缀)。)
所以当你的 cmpl
在16位模式下组装成32位解码,有2个字节的即时剩余。 这些字节是 00 00
,这是一个存储器-目的地 add [something], al
(我忘了是哪一个登记处了。00
modrm在16位寻址模式下编码)。) 这将从 cmp
.
使用 .code16
或命令行选项来制作16位机器代码。