Ассемблер - косвенная адресация памяти

Я новичок в Assembly, немного раньше занимался Java/C... так что это все равно что учиться ползать после полета.

В настоящее время я учусь на "Tutorial's Point", так как это кажется действительно дружелюбным для начинающих

Я столкнулся с этим кодом:

MY_TABLE TIMES 10 DW 0  ; Allocates 10 words (2 bytes) each initialized to 0
MOV EBX, [MY_TABLE]     ; Effective Address of MY_TABLE in EBX
MOV [EBX], 110          ; MY_TABLE[0] = 110
ADD EBX, 2              ; EBX = EBX +2
MOV [EBX], 123          ; MY_TABLE[1] = 123
  1. Выделение эффективного адреса MY_TABLE для EBX, вроде как указатель на первое значение таблицы? (я знаю, это не сравнимо с C... но я должен понять это как-то:))

  2. он говорит ADD EBX, 2 (что означает inc в регистре EBX на 2, но затем, когда ссылка на него указана на следующей строке, не должна ли она вернуться к [2] месту? снова... думая об этом, как о массивах Си).

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

1 ответ

MOV EBX, [MY_TABLE] будет читать 4 байта из памяти по адресу MY_TABLE (т.е. первые два элемента "массива").

По комментарию я бы предположил, что автор намеревался использовать LEA EBX, [MY_TABLE],

LEA сокращение от "эффективный адрес загрузки", и это в основном первая половина инструкции MOV с эталонным вариантом памяти. Он рассчитает окончательный адрес, откуда такой MOV будет загружать данные, но вместо контакта с микросхемой памяти и загрузки данных, сам адрес загружается в регистр назначения. Также то же самое может быть достигнуто в NASM путем MOV ebx,MY_TABLE или в MASM/TASM по MOV ebx,OFFSET MY_TABLE,

Так что да, это похоже на указатель на первое значение в таблице, но чтобы полностью оценить необработанность сборки, следует иметь в виду, это как указатель на первый BYTE в таблице, так как указатели в Assembly не имеют какого-либо типа в 32b защищенный режим с плоской моделью памяти (Win32, linux32) адрес uint32_tи память адресуется байтами, поэтому каждое такое число указывает куда-то в память (либо в том месте, где отображается физический чип / память ввода-вывода устройства, либо в пустую недопустимую область). Это ограничивает простой 32-разрядный режим с плоской памятью до 4 ГБ пространства памяти (x86 допускает более сложные схемы отображения памяти в 32-разрядных режимах, что позволяет адресовать более 4 ГБ адресного пространства памяти, но, конечно, не с одним адресом 32-разрядного регистра).

2) так как этот указатель указывает на БАЙТЫ, а ваша таблица из СЛОВ, каждое слово имеет размер 2 байта. Первый элемент таблицы находится в смещении 0, но занимает также следующий байт в смещении 1, Первый байт второго значения (таблица [1] в C) смещен 2, таким образом add ebx,2 используется для получения адреса второго значения. add ebx,1 заставил бы вас указать в середине первого my_table[0] значение.

Постарайтесь не ссылаться на эти вещи, как на их аналоги из C, сами массивы являются своего рода низкоуровневыми, поэтому они часто совпадают, но любые более высокие конструкции C часто просто запутывают это больше. C-массивы уже скрывают от вас арифметику указателей, используя тип элемента для вычисления правильного адреса для вас. В сборке этого не происходит.

Другие варианты, как 32b x86 может обращаться к C-подобному значению my_table[1]:

lea  ebx,[my_table]
mov  esi,1
mov  ax,[ebx + esi*2]  ; by index register scaled by 2

lea  ebx,[my_table]
mov  esi,2
mov  ax,[ebx + esi]    ; by offset

lea  ebx,[my_table]
mov  ax,[ebx + 2]      ; by immediate offset

mov  ax,[my_table + 2] ; by absolute address

Редактировать: теперь я наконец заметил, что вы на самом деле делаете с этим ebx, я полагаю MOV [EBX], 110 "небрежный" стиль программирования, потому что ни от одного аргумента такого mov Понятно, каков размер данных.

Если вы делаете mov [ebx],ax, размер ax определяет ширину данных такой операции равной 16 битам, но 110 может быть 8, 16 или 32-битным (или даже 64-битным в 64-битном режиме). Таким образом, правильный / хороший стиль в такой ситуации (ссылка на память или немедленная) заключается в явном указании размера аргумента, например:

mov [ebx], word 110   ; C-like: ((short *)my_table)[some_index] = (short)110;

А по соображениям производительности на современном x86 лучше избегать использования 16-битных частей регистров, поэтому, когда вы имеете дело с массивом шортов или байтов, может быть лучше загрузить их, расширив их до полного 32-битного значения:

movzx eax,word [ebx] ; to extend word value [ebx] with zero bits
movsx ecx,byte [esi] ; to sign-extend value [ebx]
    ; the top most (sign) bit of original value is copied up to fill upper ecx

Тогда вы можете сделать все свои расчеты с 32B вариант регистра (eax а также ecx в приведенном выше примере), и используйте частичное ax а также cl только в конце вычисления для сохранения правильного результата (конечно, убедитесь, что само вычисление не дает неправильных результатов из-за использования 32-битного регистра / значений).

Другие вопросы по тегам