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

#assembly #x86 #syntax-error #masm

# #сборка #x86 #masm

Вопрос:

 .data
    num1    word   0FEEDh

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

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

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

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

Ответ №1:

ecx это 32-разрядный регистр, dword (двойное слово); в терминологии x86 СЛОВО составляет 2 байта.
Когда вы пытались:

 sub     ecx, num1         ;ecx = ecx - num1
 

Ассемблер проверил label 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 на dword, чтобы оно было правильным. (Если только вы не хотели также прочитать все 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
 

Комментарии:

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

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

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

4. @PeterCordes: Да, только что понял, что я все испортил.

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