Инструкция CMP ведет себя странно, если операнд больше 127? - PullRequest
1 голос
/ 23 апреля 2020

Я написал эту программу сборки:

.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 (действует как a для l oop, который повторяется 127 раз). Это прекрасно работает и выполняет код 127 раз, прежде чем перейти к .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 имеет длину 4 байта в примере 128, хотя он только 1 байт в примере 127. Я подозреваю, что что-то в этом является причиной этой ошибки.

1 Ответ

2 голосов
/ 23 апреля 2020

Ваша проблема может быть связана с add $0x1, -4(%ebp), который использует неоднозначный размер операнда. Если GAS выбирает размер байтового операнда, это может вызвать проблемы? Хотя, если старшие байты равны нулю, он просто будет простираться в ноль. Причина вашей проблемы не очевидна, но странно, что вы смешиваете 16- и 32-битный размер адреса для BP и EBP.

Серьезно, просто поместите число в регистр и l oop с dec reg / jnz как нормальный человек.

Или используйте отладчик, чтобы посмотреть на память и разобраться, что происходит. Ваш cmpl $127, -4(%ebp) указывает размер операнда, поэтому он определенно сравнивает слова, а не обрабатывает 128 как -128 с дополнением к 8-битному 2.

Я заметил, что операнд Команда cmpl имеет длину 4 байта в примере 128, тогда как в примере 127 она составляет всего 1 байт. Я подозреваю, что что-то об этом является причиной этой ошибки.

Это не ошибка. Большинство основных ALU-инструкций basi c x86 содержат код операции для версии с 32-разрядной немедленной версией, а другой - с расширенным знаком 8-разрядной немедленной .

На оригинальном 8086 это сохранил 1 байт для инструкций, таких как cmp r/m16, imm8 против cmp r/m16, imm16. В 32/64-битном коде это экономит 3 байта для imm8 против imm32. https://www.felixcloutier.com/x86/cmp перечисляет доступные формы.

Точка отсечения, конечно, -128 .. +127, потому что это знак - расширенный немедленный. Ваш ассемблер всегда выбирает наименьшую возможную кодировку для данной исходной строки asm, поэтому все работает так, как задумано.


Если вы собираете 32-битный режим, но работаете как 16-битный В режиме cmpl $imm32, r/m32 будет работать не так, как остальной код .

Все остальные инструкции имеют одинаковую длину независимо от режима, но выполняются с противоположным размером операнда (16 против 32). Но код операции для cmpl и cmpw одинаков; разница только в размере операнда (переключается на префикс 66 значения, отличного от значения по умолчанию для режима).

Поэтому, когда ваш cmpl собран для 32-битных декодеров в 16- битовый режим, осталось 2 байта немедленного. Эти байты равны 00 00, что является местом назначения памяти add [something], al (я забыл, который регистрирует, что 00 modrm кодирует в 16-битном режиме адресации.) Это приведет к сбою флагов из cmp.

Используйте .code16 или параметр командной строки для создания 16-разрядного машинного кода.

...