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

Система команд x86

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

Если операнд-назначение — сегментный регистр (DS, ES, FS, GS или SS), тогда в скрытую часть этого регистра также загружаются данные из соответствующего дескриптора. Эти данные извлекаются из элемента таблицы дескрипторов для данного селектора. Сегментный регистр CS не может быть загружен командой MOV. Попытка использования соответствующего кода приводит к генерации особой ситуации #UD. Для загрузки регистра CS должны использоваться команды JMP, CALL или RET.

Пустой селектор (значения 0000, … 0003) может быть загружен в регистры DS, ES, FS и GS не вызывая особой ситуации. Однако попытка использования нулевых селекторов при обращении к памяти вызывает особую ситуацию #GP(0), и никакого обращения к памяти не происходит.

Ниже представлен листинг всех проверок и действий, предпринимаемых процессором при загрузке сегментного регистра в защищенном режиме:

IF (Загружается SS)

IF (Селектор не нулевой) THEN #GP(0); FI;

Индекс селектора должен попадать в пределы таблицы дескрипторов, иначе #GP(Селектор);

Поле RPL селектора должно быть равно CPL, иначе #GP(Селектор);

AR байт должен задавать сегмент данных, доступный для записи, иначе #GP(Селектор);

Поле DPL AR байта должно быть равно CPL, иначе #GP(Селектор);

Сегмент должен быть помечен, как присутствующий, иначе #SS(Селектор);

Загрузить SS селектором;

Загрузить SS дескриптором;

IF (загружается DS, ES, FS или GS не нулевым селектором)

Индекс селектора должен попадать в пределы таблицы дескрипторов, иначе #GP(Селектор);

AR байт должен задавать сегмент данных, или кодовый сегмент доступный для чтения, иначе #GP(Селектор);

IF (Данные или несогласованный код)

THEN RPL и CPL должны быть меньше или равны DPL в AR байте, иначе #GP(Селектор);

Сегмент должен быть помечен, как присутствующий, иначе #NP(Селектор);

Загрузить селектор в сегментный регистр;

Загрузить дескриптор в сегментный регистр;

IF (Загружается DS, ES, FS или GS нулевым селектором)

Загрузить селектор в сегментный регистр;

Очистить внутренний флаг корректности дескриптора (descriptor valid bit);

Команда загрузки сегмента стека SS запрещает все прерывания до завершения выполнения следующей команды (которая может быть командой MOV в ESP). Более эффективным методом загрузки нового указателя стека является команда LSS. Существуют и другие команды, которые задерживают прием прерываний при выполнении следующей за ними команды, но комбинация таких команд (например, STI и MOV SS,EAX) не означает, что прерывания не будут восприниматься на протяжении двух команд. Такие последовательности могут вызвать серьезные сбои, т.к. станет возможным поступление прерывания сразу после команды загрузки стекового сегмента SS.

В процессорах 32-разрядной архитектуры (Intel386, …) команды пересылки между сегментными регистрами и регистрами общего назначения не требуют наличия префикса 16-битного размера операнда (66h) не зависимо от текущего режима работы. Т.е. в этом случае всегда пересылаются 16-битные данные. Многие ассемблеры при встрече подобных мнемоник (например, MOV DS,AX) автоматически вставляют префикс 16-битного размера операнда перед командой пересылки. Это не влияет на результат исполнения команды, а замедляет только время исполнения. Простейшим выходом из данной ситуации является программирование 32-битной мнемоники (MOV DS,EAX), она преобразуется ассемблером в тот же самый код операции, но без префикса размера операнда. Поскольку пересылка данных в этом случае происходит межу 16-битным и 32-битным регистрами, то в 32-битном регистре в операции учавствуют только его младшие 16 бит. Если 32-битный регистр является операндом-назначением такой операции, то значения в его старших 16 битах после окончания пересылки могут быть различны в различных моделях процессоров. В Pentium Pro, … они заполняются нулями, а для более ранних процессоров Intel их значения не определены.

Операция:

Особые ситуации защищенного режима:

#GP(0), если операнд-назначение находится в сегменте, запрещенном для записи, если используется некорректный эффективный адрес операнда в памяти в сегментах CS, DS, ES, FS, GS или нулевой селектор, а также при попытке загрузки регистра SS нулевым селектором.
#GP(Селектор), если индекс загруженного селектора не попадает в пределы таблицы дескрипторов, если при загрузке регистра SS запрашиваемый уровень привилегий селектора RPL или уровень привилегий соответствующего дескриптора сегмента DPL не равны текущему уровню привилегий CPL, если регистр SS загружается селектором, который указывает на сегмент запрещенный для записи, если регистр DS, ES, FS или GS загружается селектором сегмента, который не является доступным для чтения кодовым сегментом или сегментом данных, если регистр DS, ES, FS или GS загружается селектором сегмента, который является сегментом данных или несогласованным кодовым сегментом, и уровень привилегий дескриптора этого сегмента DPL меньше по значению запрашиваемого уровня привилегий селектора RPL и текущего уровня привилегий CPL.
#SS(0) при использовании некорректного эффективного адреса в сегменте SS.
#SS(Селектор), если регистр SS загружается селектором сегмента, который помечен как неприсутствующий.
#NP(Селектор), если регистр DS, ES, FS или GS загружается селектором сегмента, который помечен как неприсутствующий.
#PF(Код ошибки), страничная ошибка.
#AC(0) при невыровненной ссылке в память при текущем уровне привилегий равном 3.
#UD при попытке загрузки регистра CS.

Особые ситуации режима реальной адресации:

#GP, если любая часть операнда находится вне пространства эффективных адресов в сегментах CS, DS, ES, FS или GS.
#SS, если любая часть операнда находится вне пространства эффективных адресов в сегменте SS.
#UD при попытке загрузки регистра CS.

Особые ситуации режима V86:

Такие же, как и в режиме реальной адресации.
#PF(Код ошибки), страничная ошибка.
#AC(0) при невыровненной ссылке в память.

Замечание:

В процессоре Pentium III существуют некоторые ситуации, когда отладчик будет получать неверную информацию в регистрах отладки от команд MOV SS и POP SS. Обратитесь к технической документации Intel за описанием всех возможных случаев проявления ошибки.

Что такое 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

Первый вариант копирует содержимое одного регистра в другой регистр, тогда как второй вариант заносит в регистр некое число, заданное непосредственно в самой команде (в данно случае число 5). На этом примере наглядно видно, что операнды бывают разных видов. Если в роли операнда выступает название регистра, то говорят о регистровом операнде; если же значение указано прямо в самой команде, такой операнд называется непосредственным операндом.

На самом деле, в рассматриваемом случае следует говорить даже не о различных типах операндов, а о двух разных командах, которые просто обозначаются одинаковой мнемоникой. Две команды mov из нашего примера переводятся в совершенно разные машинные коды, причём первая из них занимает в памяти два байта, а вторая — пять, четыре из которых тратятся на расмещение непосредственного операнда.

Кроме непосредственных и регистровых операндов, существует ещё и третий вид операнда — адресный операнд, называемый также операндом типа память. В этом случае оператор задаёт (тем или иным способом) адрес ячейки или область памяти, с которой надлежит произвести заданное командой действие. Необходимо помнить, что в языке ассемблера NASM операнд типа «память» абсолютно всегда обозначается квадратными скобками, в которых пишется собственно адрес. В простейшем случае адрес задаётся в явном виде, то есть в форме числа; обычнопри программировании на языке ассемблера вместо чисел мы, как уже говорилось, используем метки. Например мы можем написать:

 section .data ; . count dd 0 

(символ «;» задаёт в языке ассемблера комментарий), описав область памяти размером в 4 байт, с адресом которой связана метка count, и в которой исходно хранится число 0. Если теперь написать:

 section .text move [count], eax 

эта команда mov будет обозначать копирование данных из регистра EAX в область памяти, помеченную меткой count, а, напрмер, команда:

 mov edx, [count] 

будет, наоборот, обозначать копирование из памяти по адресу count в регистр EDX.

Чтобы понять, зачем нужны квадратные скобки, рассмотрим команду:

 mov edx, count 

Вспомним, что метку (в данном случае count), как мы уже говорили, ассемблер просто заменяет на некоторое число, в данном случае — адрес области памяти. Например, если область памяти count расположена в ячейках, адреса которых начинаются с 40f2a008, то вышеприведённая команда — это абсолютно то же самое, как если бы мы написали:

 mov edx, 40f2a008h 

Теперь очевидно, что это просто уже знакомая нам форма команды mov с непосредственным операндом, т.е. эта команда заносит в регистр EDX число 40f2a008, не вникая в то, является ли это число адресом какой-либо ячейки памяти или нет. Если же мы добавим квадратные скобки, речь пойдёт уже оо обращении к памяти по заданному адресу, т.е. число будет использовано как адрес области памяти, где размещено значение, с которым надо работать (в данном случае поместить в регистр EDX).

Синтаксис At&T

direct addressing mode

movl ADDRESS, %eax

indexed addressing mode

movl string_start(,%ecx,1), %eax

register addressing mode (режим адресации регистра)

Копировать содержимое регистра EAX в регистр EBX

movl %eax %ebx

indirect addressing mode (режим косвенной адресации)

Если регистр содержит не значение, а адрес в памяти, по котоому находится значение, то копировать содержимое ячейки памяти, ссылка на которую находится в EAX в регистр EBX нужно так:

movl (%eax) %ebx 

base pointer addressing mode (режим адресации базового указателя)

movl 4(%eax) %ebx

immediate mode (немедленный режим)

Поместить десятичное число 7 в регистр EBX

movl $7 %ebx 

Формат ссылки на адрес в памяти

ADDRESS_OR_OFFSET(%BASE_OR_OFFSET,%INDEX,MULTIPLIER) 

Конечный адрес рассчитывается по формуле
FINAL_ADDRESS = ADDRESS_OR_OFFSET + %BASE_OR_OFFSET + %INDEX * MULTIPLIER

of your page —>

Поддержите проект, если он помог вам

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

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

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