Что такое mov в ассемблере
Перейти к содержимому

Что такое mov в ассемблере

  • автор:

Инструкция MOV

В Контакте Ютуб Почта

Пожалуй, инструкция MOV в ассемблере самая простая. Синтаксис этой команды такой:

MOV ПРИЁМНИК, ИСТОЧНИК

С помощью этой команды можно переместить значение из ИСТОЧНИКА в ПРИЁМНИК . То есть по сути команда MOV копирует содержимое ИСТОЧНИКА и помещает это содержимое в ПРИЁМНИК .

Никакие флаги при этом НЕ изменяются.

При использовании этой команды следует учитывать, что имеются некоторые ограничения. А именно, инструкция MOV не может:

  • Записывать данные в регистры CS и IP.
  • Копировать данные из одного сегментного регистра в другой сегментный регистр (сначала нужно скопировать данные в регистр общего назначения).
  • Копировать непосредственное значение в сегментный регистр (сначала нужно скопировать данные в регистр общего назначения).

ИСТОЧНИКОМ может быть один из следующих:

  • Область памяти (MEM)
  • Регистр общего назначения (REG)
  • Непосредственное значение (например, число) (IMM)
  • Сегментный регистр (SREG)

ПРИЁМНИКОМ может быть один из следующих:

  • Область памяти (MEM)
  • Регистр общего назначения (REG)
  • Сегментный регистр (SREG)

С учётом ограничений, которые были описаны выше, комбинации ПРИЁМНИК-ИСТОЧНИК могут быть следующими:

REG, MEM SREG, MEM MEM, REG REG, REG SREG, REG MEM, IMM REG, IMM MEM, SREG REG, SREG

Пример использования инструкции MOV:

MOV AX, 0B800h ; установить AX = B800h (память VGA). MOV DS, AX ; копировать значение из AX в DS. MOV CL, 'A' ; CL = 41h (ASCII-код). MOV CH, 01001110b ; CH = атрибуты цвета (желтый текст на красном фоне). MOV BX, 72eh ; BX = позиция на экране = 2*(x + y*80). MOV [BX], CX ; [0B800h:015Eh] = CX.

ПРИМЕЧАНИЕ
Этот пример не будет работать в Windows 2000 и выше, так как эти операционные системы запрещают программам напрямую обращаться к “железу”, а в этом примере мы пытаемся записать данные непосредственно в видеопамять.

Ну и напоследок скажу, почему эта инструкция называется MOV. Это сокращение от английского слова MOVE, которое можно перевести как “переместить, перенести, передвинуть”. И, как теперь вам уже понятно, эта команда соответствует своему названию — она перемещает значение из одного регистра в другой. Хотя с точки зрения русского языка это будет не совсем правильно, потому что перемещения не происходит — значение из ИСТОЧНИКА никуда не исчезает (не перемещается), по сути оно копируется и вставляется в ПРИЁМНИК.

Что такое mov в ассемблере

Наиболее распространенной инструкцией является инструкция MOV , которая помещает значение в регистр:

MOV Xd, Xs

В своей самой распространенной форме инструкция принимает два операнда. Первый операнд — Xd представляет регистр, в который надо поместить значение (это может быть, например, 64-разрдяный регистр X0-X30 или 32-разрядный регистр W0-W30). Второй операнд — Xs представляет источник, из которого берется значение. Это может быть другой регистр или какое-то конкретное значение (еще назваемое непосредственным операндом)

Например, поместим значение 22 в регистр X0:

mov X0, #22

Здесь число 22 называет непосредственным операндом (immediate operand). Причем перед числом указывается символ решетки (Хотя этот символ можно опускать). Микропрограмма полностью:

.global _start _start: mov X0, #22 mov X8, #93 // номер функции Linux для выхода из программы svc 0 // вызываем системную функцию

По умолчанию все числа рассматриваются как числа в десятичной системе. Если надо передать шестнадцатеричное значение, то перед числом указываются символы 0x , например:

mov X0, #0xff

Здесь в регистр X0 помещается шестнадцатеричное число 0xFF, которое в десятичной равно 255.

Если надо передать число в двоичной системе, то перед числом указывается префикс 0b , например:

mov X0, #0b11

Здесь в регистр X0 помещается двоичное число 0b11, которое в десятичной равно 3.

Стоит учитывать, что непосредственный операнд должен быть размером не более 2 байт.

Также можно загружать значение из одного регистра в другой:

mov X1, #22 mov X0, X1

В данном случае копируем содержимое регистра X1 в регистр X0. То есть в регистре X0 будет число 22

Если данные помещаются в 32-разрядный регистр W0-W30, то старшие 32 бита соответствующего регистра X0-X30 обнуляются:

Команда MOV

Команда MOV — основная команда для работы с данными. Позволяет перемещать данные между регистрами, ячейками памяти, а также использовать для пересылки непосредственные значения, заданные в команде.

Команда MOV
  1. Требует совпадения размеров источника и приемника
  2. При использовании в качестве приемника сегментного регистра, источником должен быть или несегментный регистр или ячейка памяти соответствующего размера
  3. Не допускается использование сегментного регистра CS в качестве приемника
  4. Не допускается пересылка напрямую данных из одной ячейки памяти в другую. Такая пересылка данных должна производиться через какой-нибудь регистр.
mov ax,8 mov [100h],dx mov ds,cx

Что такое mov в ассемблере

Инструкция mov является одной из самых используемых в ассемблере. По разным оценкам, в программе от 25% до 40% всех инструкций составляет инструкция mov . Она копирует данные из одного места в другое и имеет следующий синтаксис:

mov destination, source

Инструкция принимает два операнда. Первый операнд — destination представляет расположение, куда надо поместить данные. В качестве такого места может выступать регистр процессора или адрес в памяти. Второй операнд — source указывает на источник данных, в качестве которого может выступать регистр процессора, адрес в памяти или непосредственный операнд — число. То инструкция mov копирует данные из source в destination. При этом оба операнда не могут быть одновременно адресами в памяти.

Например, определим файл hello.asm со следующей программой на ассемблере:

.code main proc mov rax, 22 ret main endp end

Здесь в регистр rax помещается непосредственный операнд — число 22. Все операнды, которые представляют числовые или символьные литералы (как в данном случае числовой литерал 22), называются непосредственными операндами (immediate operand).

Проверим, что после выполнения этой программы в регистре rax действительно 22. Вывод содержимого регистра на консоль требует ряда других инструкций. Но я не зря использовал именно регистр rax. Дело в том, что при выполнении программы в регистр rax помещается код статуса выполнения программы. Обычно считается, что если этот регистр содержит 0, то программа успешно завершила свое выполнение. Другое число обычно означает код ошибки. Но в нашем случае мы можем использовать этот регистр и число в нем для проверки выполнения инструкций ассемблера. (Также можно помещать код статуса и в младшие части rax — регистры eax и al при условии, что остальная часть регистра rax содержит 0)

Так, вначале скомпилируем файл командой

ml64 hello.asm /link /entry:main

Далее выполним скомпилированное приложение, введя его имя

hello

И для проверки результата в регистре rax после выполнения скомпилированного приложения выполним команду

echo %ERRORLEVEL%

Эта команда отображает код ошибки. Полный вывод:

c:\asm>ml64 hello.asm /link /entry:main Microsoft (R) Macro Assembler (x64) Version 14.35.32217.1 Copyright (C) Microsoft Corporation. All rights reserved. Assembling: hello.asm Microsoft (R) Incremental Linker Version 14.35.32217.1 Copyright (C) Microsoft Corporation. All rights reserved. /OUT:hello.exe hello.obj /entry:main c:\asm>hello c:\asm>echo %ERRORLEVEL% 22 c:\asm>

Оба операнда инструкции mov должны быть одинакового размера. То есть можно поместить значение из 8-битного регистра в другой 8-битный регистр или 8-битную переменную или наоборот. Подобным образом можно копировать данные между 16-битными или между 32-битными или между 64-битными операндами, но использовать в инструкции операнды разных размеров нельзя. Константы и непосредственные операнды могут быть меньшего размера, чем регистр, в который они помещаются. Например:

.code main proc mov rdx, 5 ; помещаем в регистр rdx число 5 mov rax, rdx ; помещаем в регистр rax значение из регистра rdx ret main endp end

Здесь вначале помещаем в регистр rdx число 5. Регистр rdx — 64-разрядный, поэтому в него можно поместить любой целочисленной литерал размером не более 64 бит. В реальности для числа 5 достаточно 8-битного регистра. И в данном случае помещаем число просто будет расширено до 64 бит.

Далее число из регистра rdx (то есть число 5) помещается в регистр rax . Здесь у нас должно быть соответствие по разрядности регистров. Поскольку и rdx, и rax — 64-разрядные регистры, то проблемы не будет.

То же самое касается и переменных. Например:

.data i32 dword 4 .code main proc mov eax, i32 ; помещаем в регистр eax значение переменной i32 ret main endp end

Здесь в секции .data определена переменная i32, которая равна 32 битам. Ее значение помещается в 32-битный регистр eax (младшие 32 бита регистра rax).

.data i8 byte 8 .code main proc mov rax, 0 ; обнуляем регистр rax mov al, i8 ; помещаем в регистр al значение переменной i8 ret main endp end

Здесь значение 8-битной переменной i8 помещается в 8-битный регистр al.

Однако, если мы смешаем операнды с разными размерами, то при компиляции ассемблер укажет нам на ошибку, как в следующем случае:

.data i8 byte 8 .code main proc mov rax, i8 ; помещаем в регистр rax значение переменной i8 ret main endp end

Здесь значение 8-битной переменной i8 помещается в 64-битный регистр rax. И при компиляции ассемблер отобразит нам ошибку:

c:\asm>ml64 hello.asm /link /entry:main Microsoft (R) Macro Assembler (x64) Version 14.35.32217.1 Copyright (C) Microsoft Corporation. All rights reserved. Assembling: hello.asm hello.asm(5) : error A2022:instruction operands must be the same size c:\asm>

Тоже самое касается и констант и числовых литералов, размер которых больше, чем размер регистра:

.code main proc mov al, 512 ; помещаем в регистр al число 512 ret main endp end

Здесь мы пытаемся поместить в 8-разрядный регистр al число 512, которое занимает больше 8 бит. Соответственно при компиляции мы также получим ошибку.

mov и расширение со знаком

Инструкция mov имеет ограничение — операнды должны совпадать по размеру. Но что, если нам надо поместить в 64-разрядный регистр 8-битное значение? Архитектура x86-64 также предоставляет дополнительные расширения инструкции mov — movsx и movzx . movsx выполняет перемещение с расширением знаковым битом, которое копирует данные и расширяет данные по знаку во время их копирования. Синтаксис инструкции movsx аналогичен синтаксису mov:

movsxd dest, source ; если dest - 64-разрядный операнд и source - 32-разрядный movsx dest, source ; для всех остальных комбинаций операндов

При этом первый операнд — dest должен быть по разрядности не меньше второго операнда и обязательно должен предоставлять регистр. Эта инструкция также не допускает константные значения. Например

.code main proc mov dl, -5 movsx eax, dl ; EAX = -5 ret main endp end

Здесь значение из регистра DL помещается в регистр EAX. Поскольку в DL число отрицательное, то используем расширение со знаком. В результате в EAX сохранится число -5.

Для беззнакового расширения нулями есть другая инструкция — movzx :

.code main proc mov dl, 5 movzx eax, dl ; EAX = 5 ret main endp end

Если надо расширить 32-битный регистр до 64-битного, можно просто скопировать (32-битный) регистр в самого себя:

.code main proc mov rax, 0ffffffffffffffffh mov eax, eax ; RAX = 00000000ffffffffh - в RAX остались только младшие 32 бита ret main endp end

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *