Вычесть переменную из регистра? ошибка A2022: операнды инструкции должны быть одного размера

avatar
MohAliBou
30 января 2022 в 01:30
398
1
2
.data
    num1    word   0FEEDh

.code
main PROC
    mov     ecx, 0E4C7FFFDh   ;ecx = 0E4C7FFFD hex
    sub     ecx, num1         ;;;;; error on this line

Я хочу иметь возможность вычитать переменную num1 из регистра ecx, но эта попытка дает:

ошибка A2022: операнды инструкции должны быть одного размера

Я новичок в сборке и пытаюсь понять основы. Как мне это сделать?

Источник

Ответы (1)

avatar
Joshua
30 января 2022 в 02:29
3

ecx — 32-битный регистр, двойное слово (двойное слово); в терминологии x86 слово WORD имеет размер 2 байта.
Когда вы пытались:

sub     ecx, num1         ;ecx = ecx - num1

Ассемблер проверил метку num1 и обнаружил, что она имеет тип word, поэтому попытался сгенерировать

sub     ecx, word ptr [num1]    ;ecx = ecx - num1

но такого кода операции не существует.

Форма sub r32, r/m32 sub существует, но загрузка 4 байтов из 2-байтовой переменной вызовет 2 байта мусора в старшей половине.

sub     ecx, dword ptr [num1]   ; don't *just* change to this

Это будет собрано, но вам нужно заменить num1 на двойное слово, чтобы оно было правильным. (Если вы не хотите также читать любые 2 байта, которые вы собрали после num1, в свой раздел .data). И если вы используете num1 dword 0FEEDh, MASM sub ecx, num1 будет работать, потому что он определит правильный размер операнда памяти. Если вы хотите, чтобы MASM проверял размеры ваших переменных по тому, как вы их используете, не используйте переопределения размера операнда, если они вам не нужны (например, чтобы скопировать 4 байта сразу из строки).


Ассемблер рекомендует решить проблему, сделав num1 переменной типа dword или заменив ecx на cx. Это не так (cx не может сохранить вашу константу, но masm этого не знает); однако есть еще одна возможность. Вы можете выполнить нулевое расширение до рабочего регистра и вычесть это:

movzx   edx, num1    ; zero-extending load from a byte or word var
sub     ecx, edx

Или, если num1 является подписанным (masm имеет SWORD, но я обычно вижу, что WORD используется как для подписанного, так и для беззнакового):

movsx   edx, num1    ; sign-extending load
sub     ecx, edx
Peter Cordes
30 января 2022 в 02:33
0

sub ecx, byte num1 не существует! Кодировки sub r32, m8 нет. Может быть, вы перепутали sub r/m32, imm8 с расширенным знаком immediate? Единственными скалярными целочисленными инструкциями x86, которые выполняют загрузку со знаком или нулем, являются movsx и movzx, другим инструкциям требуется, чтобы их операнд в памяти был той же ширины, что и размер операнда.

Joshua
30 января 2022 в 02:34
0

@PeterCordes: Подождите, я проверю, что сгенерировал nasm, когда он успешно собрал это.

Peter Cordes
30 января 2022 в 02:36
0

Обратите внимание, что это вопрос MASM, где sub ecx, word ptr num1 — операнд источника памяти. В NASM sub ecx, byte num1 просто предлагает ассемблеру использовать imm8 для кодирования адреса. Фактическая эквивалентная инструкция NASM — sub ecx, byte [num1], которая не ассемблируется. (Помните, что имена простых символов являются операндами памяти в MASM и адресами в NASM).

Joshua
30 января 2022 в 02:36
0

@PeterCordes: Да, я только что понял, что облажался.

Peter Cordes
30 января 2022 в 02:39
0

Когда вы исправите это, я думаю, также важно использовать синтаксис MASM повсюду; тем более, что mov eax, word [foo] действительно собирается в MASM, а word расширяется до числа 2, поэтому становится mov eax, [foo+2], что безумно непрозрачно и сбивает с толку новичков, которые не до конца понимают, что делает word ptr, не говоря уже о MASM против NASM word ptr против word.

Joshua
30 января 2022 в 02:44
0

@PeterCordes: я думаю, что везде использовал синтаксис masm и случайно передал синтаксис masm в nasm.

Peter Cordes
30 января 2022 в 02:48
0

Нет, как я уже сказал, MASM требует word ptr в качестве спецификатора размера операнда для операнда памяти. Я отредактировал, чтобы исправить это для вас.