Ассемблер - косвенная адресация памяти
Я новичок в 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
Выделение эффективного адреса MY_TABLE для EBX, вроде как указатель на первое значение таблицы? (я знаю, это не сравнимо с C... но я должен понять это как-то:))
он говорит 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-битного регистра / значений).