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
的操作码是相同的;差异仅在于操作数大小(模式值a
66
前缀切换为非默认值)

因此,当您的
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对无符号大于或等于进行解码。