Assembly 如果操作数超过127,CMP指令的行为是否异常?
我编写了这个汇编程序:Assembly 如果操作数超过127,CMP指令的行为是否异常?,assembly,x86-16,bootloader,gnu-assembler,machine-code,Assembly,X86 16,Bootloader,Gnu Assembler,Machine Code,我编写了这个汇编程序: .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 mo
.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
之前运行代码127次。但是,当我增加要比较的值时(例如,增加到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>
/127:
00000037 :
37:83 7d fc 7f cmpl$0x7f,-0x4(%ebp)
3b:7d 09 jge 46
// 128:
00000037 :
37:81 7d fc 80 00 cmpl$0x80,-0x4(%ebp)
3e:7d 09 jge 49
我注意到,
cmpl
指令的操作数在128示例中为4字节长,而在127示例中仅为1字节。我怀疑这是导致此错误的原因。您的问题可能与使用不明确操作数大小的添加$0x1,-4(%ebp)
有关。如果GAS选择字节操作数大小,是否会导致问题?虽然如果上面的字节为零,但扩展到dword中的字节将为零。问题的原因并不明显,但奇怪的是,您将16位和32位地址大小混合在了BP和EBP中
说真的,就像普通人一样,只需在寄存器中输入一个数字,然后使用dec reg
/jnz
循环即可
或者使用调试器查看内存并整理发生的事情。您的cmpl$127,-4(%ebp)
确实指定了一个操作数大小,因此它肯定是在进行dword比较,而不是将128
视为带有8位2的补码的-128
我注意到,在128示例中,cmpl指令的操作数长度为4字节,而在127示例中仅为1字节。我怀疑这就是这个错误的原因
这不是一个错误。大多数基本的x86整数ALU指令都有一个操作码,用于32位立即数的版本,另一个版本具有扩展符号的8位立即数
在原来的8086上,这为指令节省了1字节,如cmpr/m16、imm8
与cmpr/m16、imm16
。在32/64位代码中,这为imm8和imm32节省了3个字节。列出可用的表格
截止点当然是-128+127因为这是一个符号。您的汇编器总是为给定的asm源代码行选择尽可能最小的编码,因此一切都按预期进行
如果您正在为32位模式进行组装,但以16位模式运行,
cmpl$imm32,r/m32
将以不同于其他代码的方式中断
不管模式如何,其他指令都是相同的长度,但以相反的操作数大小(16对32)运行。但是cmpl
和cmpw
的操作码是相同的;差异仅在于操作数大小(模式值a66
前缀切换为非默认值)
因此,当您的cmpl
在16位模式下为32位解码组装时,有2个字节的立即数剩余。这些字节是00 00
,它是一个内存目标add[something],al
(我忘记了00
modrm以16位寻址模式编码的寄存器)。这将从cmp
中删除标志
使用
.code16
或命令行选项生成16位机器代码。因此,如果我理解正确,我将与-128进行比较,而不是与128进行比较,因此它总是大于或等于?如果是这样的话,我需要做什么才能真正与128进行比较呢?@IanRehwinkel:不,cmpl$128,因为您使用了cmpl
,所以有些东西会与128进行比较。如果它将其视为-128
,它可以使用83
操作码对其进行编码。@IanRehwinkel:噢,最后一句话让我意识到你的但可能是什么。您没有展示如何构建代码,但您可能正在以32位的方式进行组装,然后以16位模式运行该机器代码。较长的编码可以解释这里的中断:使用一个正确配置的调试器,以在运行此程序时CPU所处的模式进行解码。如果大于或等于,则jge是有符号跳转,因此需要确保比较的大小正确……如果需要,请使用jae/jnc对无符号大于或等于进行解码。