Как изолировать элементы массива байтов и слов в 64-битном регистре - PullRequest
0 голосов
/ 30 октября 2019

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

segment .data
  a      dw  4, 234, -212
  b      db  112, -78, 50
  result dq  0
segment .text       
  global main
main:
  mov   rax, [a]        

Я знаю, что в решении есть что-тоделать со смещениями и индексированием, но я не понимаю, как я могу получить только один элемент массива в регистр.

Что я могу сделать?

1 Ответ

0 голосов
/ 30 октября 2019

Если вы хотите рассматривать ваши значения как подписанные, вам нужно movsx. Предполагая синтаксис NASM:

default rel
; ... declarations and whatever    

    movsx   rax, word [a + 1*2]    ; a is an array of dw = words
    movsx   rcx, byte [b + 1*1]    ; b is an array of db = bytes

    add     rax, rcx
    mov     [result], rax         ; result is a qword

(MASM или GNU .intel_syntax будет использовать word ptr вместо word, просто добавьте ptr в спецификатор размера для операнда памяти.)

1 может быть регистром типа [a + rsi*2] или [b + rsi], так что вы можете легко перебирать массивы. Ссылка на содержимое ячейки памяти. (режимы адресации x86)

Я написал 1*2 вместо 2, чтобы указать, что это индекс 1 (2-й элемент массива), масштабированный по размеру элемента. Ассемблер оценит константное выражение и просто использует тот же (относительный к RIP) режим адресации, что и для [a], но с другим смещением.

Если вам нужно, чтобы он работал в позиционно-независимом коде (гдеВы не можете использовать режим адресации [disp32 + register] с 32-битным абсолютным адресом для символа), сначала lea rdi, [a] (RIP-относительный LEA) и выполнить [rsi + rsi*2].


Если выЕсли вам нужно нулевое расширение, вы бы использовали movzx

    movzx   eax, word [a + 1*2]    ; a is an array of dw = words
    movzx   ecx, byte [b + 1*1]    ; b is an array of db = bytes
    ; word and byte zero-extended into 64-bit registers:
    ; explicitly to 32-bit by MOVZX, and implicitly to 64-bit by writing a 32-bit reg

    ; add     eax, ecx              ; can't overflow 32 bits, still zero-extended to 64
    sub     rax, rcx              ; want the full width 64-bit signed result 
    mov     [result], rax         ; result is a qword

Если бы вы знали, что старшие биты вашего полного результата всегда будут равны нулю, просто используйте EAX (размер 32-битного операнда), за исключениемконец. Преимущества использования 32-битных регистров / инструкций в x86-64

Этот код соответствует C как

static  uint16_t a[] = {...};
static  uint8_t b[] = {...};
static  int64_t result;

void foo(){
    int64_t rax = a[1] - (int64_t)b[1];
    result = rax;    // why not just return this like a normal person instead of storing?
}

Говоря о том, что вы можете посмотреть навывод компилятора в проводнике компилятора Godbolt и ознакомьтесь с этими инструкциями и режимами адресации.


Обратите внимание, что mov al, [b + 1] загрузит байт и объединит это в младший байт RAX.

Обычно вы этого не хотите;movzx - это обычный способ загрузки байта в современном x86. Современные процессоры x86 декодируют x86 в RISC-подобные внутренние мопы для переименования регистров + выполнения вне порядка. movzx позволяет избежать ложной зависимости от старого значения полного регистра. Это аналог ARM ldrb, MIPS lbu и т. Д.

Слияние с младшим байтом или словом RAX - странная вещь CISC, которую может делать x86, но RISC не может.

Вы можете безопасно читать 8-битные и 16-битные регистры (и вам нужно это для хранения слов), но обычно избегайте записи частичных регистров, если у вас нет веских причин и вы понимаете возможную производительностьпоследствия ( Почему GCC не использует частичные регистры? ). например, вы обнулили полный пункт назначения перед cmp + setcc al.

...