В инструкции микропроцессора 8085 есть операция управления машиной "nop" (без операции). Мой вопрос: зачем нам операция? Я имею в виду, что если нам нужно завершить программу, мы будем использовать HLT или RST 3. Или, если мы хотим перейти к следующей инструкции, мы дадим следующие инструкции. Но почему нет операции? Какая в этом необходимость?
microprocessor
Demietra95
источник
источник
Ответы:
Одно из применений инструкций NOP (или NOOP без операций) в процессорах и микроконтроллерах заключается в вставке небольшой, предсказуемой задержки в ваш код. Несмотря на то, что NOP не выполняют никаких операций, их обработка занимает некоторое время (ЦП должен извлечь и декодировать код операции, поэтому для этого требуется немного времени). Всего 1 цикл ЦП «теряется» для выполнения инструкции NOP (точное число может быть выведено из таблицы данных CPU / MCU, как правило), поэтому размещение N NOP в последовательности - это простой способ вставить предсказуемую задержку:
где K - количество циклов (чаще всего 1), необходимое для обработки инструкции NOP, а - тактовый период.Tс л о с к
Почему ты бы так поступил? Может быть полезно заставить ЦП немного подождать, пока внешние (возможно, более медленные) устройства завершат свою работу и сообщат данные ЦП, т.е. NOP полезен для целей синхронизации.
Смотрите также соответствующую страницу Википедии на NOP .
Другое использование - выравнивание кода по определенным адресам в памяти и другим «приемам сборки», как объяснено также в этом потоке на Programmers.SE и в этом другом потоке на StackOverflow .
Еще одна интересная статья на эту тему .
Эта ссылка на страницу книги Google особенно относится к 8085 CPU. Выдержка:
РЕДАКТИРОВАТЬ (для решения проблемы, выраженной в комментарии)
Если вы беспокоитесь о скорости, помните, что (время) эффективность - это только один параметр, который следует учитывать. Все зависит от приложения: если вы хотите вычислить 10-миллиардную цифру , возможно, вашей единственной заботой может быть скорость. С другой стороны, если вы хотите регистрировать данные от датчиков температуры , подключенных к MCU через АЦП, скорость обычно не столь важна, но в ожидании нужного количества времени , чтобы позволить АЦП правильно выполнить каждое чтение имеет важное значение . В этом случае, если MCU не ждет достаточно, он рискует получить совершенно ненадежные данные (я допускаю, что он получит эти данные быстрее , однако: o).π
источник
Другие ответы касаются только NOP, который на самом деле выполняется в какой-то момент - он используется довольно часто, но это не единственное использование NOP.
Неисполнение NOP также очень полезно при написании кода , который может быть исправлен - в основном, вы будете подушечка функция с несколько НОПОМ после на
RET
(или аналогичной инструкции). Когда вам нужно пропатчить исполняемый файл, вы можете легко добавить больше кода в функцию, начиная с оригиналаRET
и используя столько NOP, сколько вам нужно (например, для длинных переходов или даже встроенного кода) и заканчивая другимRET
.В этом случае использования noöne никогда не ожидает
NOP
выполнения. Единственное, что нужно, это разрешить исправление исполняемого файла - в теоретическом незаполненном исполняемом файле вам фактически нужно изменить код самой функции (иногда она может соответствовать исходным границам, но довольно часто вам все равно потребуется переход ) - это намного сложнее, особенно если учесть написанную вручную сборку или оптимизирующий компилятор; Вы должны уважать переходы и подобные конструкции, которые могли указывать на какой-то важный фрагмент кода. В общем, довольно сложно.Конечно, это было гораздо интенсивнее использовать в старые времена, когда было полезно делать такие патчи, как эти маленькие и онлайн . Сегодня вы просто раздадите перекомпилированный бинарный файл и покончите с этим. Есть еще некоторые, кто использует исправления NOP (выполняются или нет, и не всегда буквальные
NOP
s - например, Windows используетMOV EDI, EDI
для оперативного исправления - это тот тип, где вы можете обновлять системную библиотеку, пока система фактически работает, без необходимости перезапусков).Итак, последний вопрос: почему есть специальная инструкция для чего-то, что на самом деле ничего не делает?
MOV AX, AX
будут делать то же самое, но не так ясно обозначают намерение.NOP
достаточно много; у фактического скомпилированного ассемблерного кода таких больше нет. Следует отметить, что это не x86-NOP
е.NOP
, что облегчает чтение разборки), так и для оперативного исправления (хотя я на самом деле предпочитаю Edit и Continue в Visual Studio: P).Для выполнения NOP, конечно, есть еще несколько моментов:
MOV EDI, EDI
, есть и другие эффективные NOP, чем буквальныеNOP
.MOV EDI, EDI
имеет лучшую производительность как 2-байтовый NOP на x86. Если вы использовали дваNOP
s, это будет две инструкции для выполнения.РЕДАКТИРОВАТЬ:
На самом деле, обсуждение с @DmitryGrigoryev заставило меня задуматься об этом немного больше, и я думаю, что это ценное дополнение к этому вопросу / ответу, поэтому позвольте мне добавить несколько дополнительных моментов:
Во-первых, очевидно, зачем нужна инструкция, которая делает что-то подобное
mov ax, ax
? Например, давайте рассмотрим случай машинного кода 8086 (старше, чем даже машинный код 386):0x90
. Это все еще время, когда многие люди писали на ассамблее. Таким образом, даже если бы не было специальнойNOP
инструкции,NOP
ключевое слово (псевдоним / мнемоника) все равно было бы полезно и соответствовало бы этому.MOV
самом деле, отображаются на множество различных кодов операций, потому что это экономит время и пространство - например,mov al, 42
«переместить непосредственный байт вal
регистр», что означает0xB02A
(0xB0
код операции,0x2A
являющийся «немедленным» аргументом). Так что это занимает два байта.mov al, al
(поскольку это глупо, в принципе), поэтому вам придется использоватьmov al, rmb
перегрузку (rmb - «регистр или память»). Это на самом деле занимает три байта. (хотя, вероятно,mov rb, rmb
вместо этого он использовал бы менее конкретный , который должен занимать только два байтаmov al, al
- байт аргумента используется для указания как исходного, так и целевого регистра; теперь вы знаете, почему у 8086 было только 8 регистров: D). Сравните сNOP
, который является однобайтовой инструкцией! Это экономит память и время, поскольку чтение памяти в 8086 все еще было довольно дорогим - не говоря уже о загрузке этой программы с ленты, с дискеты или чего-то другого, конечно.Так откуда же
xchg ax, ax
взяться? Вам просто нужно посмотреть коды операций другихxhcg
инструкций. Вы увидите0x86
,0x87
и , наконец,0x91
-0x97
. Таким образом,nop
это0x90
выглядит довольно неплохоxchg ax, ax
(что, опять же, не являетсяxchg
«перегрузкой» - вам придется использоватьxchg rb, rmb
два байта). И на самом деле, я почти уверен, что это был хороший побочный эффект0x90-0x97
микроархитектуры того времени - если я правильно помню, было легко сопоставить весь диапазон с «xchg, действующим над регистрамиax
иax
-di
» ( операнд был симметричным, это дало вам полный диапазон, включая nopxchg ax, ax
; обратите внимание, что порядокax, cx, dx, bx, sp, bp, si, di
-bx
послеdx
,ax
; помните, что имена регистров являются мнемониками, а не упорядоченными именами - аккумулятор, счетчик, данные, база, указатель стека, указатель базы, указатель источника, указатель назначения). Тот же подход использовался и для других операндов, например, дляmov someRegister, immediate
множества. В некотором смысле, вы можете думать об этом, как будто код операции на самом деле не был полным байтом - последние несколько битов являются «аргументом» для «настоящего» операнда.Все это говорит о том, что на x86
nop
можно считать настоящей инструкцией или нет. Оригинальная микроархитектура действительно рассматривала его как вариант,xchg
если я правильно помню, но на самом деле это было названоnop
в спецификации. И поскольку вxchg ax, ax
действительности это не имеет смысла в качестве инструкции, вы можете увидеть, как разработчики 8086 сэкономили на транзисторах и путях при декодировании команд, используя тот факт, что он0x90
естественным образом отображается на что-то, что является полностью «неопрятным».С другой стороны, i8051 имеет полностью встроенный код операции для
nop
-0x00
. Вроде практично. Конструкция команды в основном использует высокий полубайт для операции и низкий полубайт для выбора операндов - например,add a
есть0x2Y
, и0xX8
означает «прямой регистр 0», то0x28
естьadd a, r0
. Экономит много на силиконе :)Я все еще мог бы продолжить, так как дизайн процессора (не говоря уже о дизайне компилятора и дизайне языка) является довольно широкой темой, но я думаю, что я показал много разных точек зрения, которые вошли в дизайн довольно хорошо, как есть.
источник
NOP
как правило , это псевдонимMOV ax, ax
,ADD ax, 0
или аналогичные инструкции. Зачем вам разрабатывать специальную инструкцию, которая ничего не делает, когда их много?MOV ax, ax
выезд;NOP
всегда будет иметь фиксированное количество циклов для выполнения. Но я все равно не понимаю, как это относится к тому, что я написал в своем ответе.MOV ax, ax
выезд, потому что к тому времени, когда они узнают, что этоMOV
инструкция уже в процессе разработки.NOP
иMOV ax, ax
). Современные процессоры намного сложнее, чем компиляторыLD r, r
инструкций, гдеr
есть любой отдельный регистр, аналогичный вашейMOV ax, ax
инструкции. Причина 7, а не 8 в том, что одна из инструкций перегружена, чтобы статьHALT
. Таким образом, 8080 и Z80 имеют как минимум 7 других инструкций, которые делают то же самое, что иNOP
! Интересно, что, хотя эти инструкции не связаны логически по битовой структуре, все они выполняют 4 состояния T, поэтому нет причин использоватьLD
инструкции!Еще в конце 70-х у нас (тогда я был молодым студентом-исследователем) была небольшая система разработки (8080, если память служит), которая выполнялась в 1024 байтах кода (т. Е. В одном UVEPROM) - она имела только четыре команды для загрузки (L ), сохранить (S), распечатать (P) и что-то еще, что я не могу вспомнить. Это было приведено в действие с настоящим телетайпом и лентой удара. Это было жестко закодировано!
Одним из примеров использования NOOP была процедура обработки прерываний (ISR), которые были разделены интервалами в 8 байт. Эта процедура оказалась длиной 9 байтов и закончилась (длинным) переходом к адресу чуть дальше по адресному пространству. Это означало, что с учетом порядка байтов с прямым порядком байтов старший байт адреса был 00h и вставлен в первый байт следующего ISR, что означало, что он (следующий ISR) начинался с NOOP, так что «мы» могли соответствовать код в ограниченном пространстве!
Так что NOOP полезен. Кроме того, я подозреваю, что для Intel было проще всего так его кодировать - у них, вероятно, был список инструкций, которые они хотели реализовать, и он начинался с «1», как и все списки (это были дни FORTRAN), поэтому ноль Код NOOP стал выпадать. (Я никогда не видел статью, в которой утверждается, что NOOP является неотъемлемой частью теории вычислительной науки (тот же вопрос, что и: есть ли у математиков ноль операций, в отличие от нуля теории групп?)
источник
0x00
дляnop
в моем ответе. Короче говоря, это экономит на декодировании команд -xchg ax, ax
естественным образом вытекает из того, как работает декодирование команд, и делает что-то «неопрятное», так почему бы не использовать это и не вызывать егоnop
, верно? :) Используется, чтобы сэкономить немного на кремнии для декодирования инструкций ...На некоторых архитектурах
NOP
используется, чтобы занять неиспользуемые интервалы задержки . Например, если инструкция перехода не очищает конвейер, в любом случае выполняется несколько инструкций после него:Но что, если у вас нет никаких полезных инструкций, чтобы соответствовать после
JMP
? В этом случае вам придется использоватьNOP
s.Слоты задержки не ограничиваются прыжками. На некоторых архитектурах опасности данных в конвейере ЦП не разрешаются автоматически. Это означает, что после каждой инструкции, которая изменяет регистр, есть слот, в котором новое значение регистра еще не доступно. Если следующей инструкции нужно это значение, слот должен быть занят
NOP
:Кроме того, некоторые инструкции условного выполнения ( If-True-False и аналогичные) используют слоты для каждого условия, и когда конкретное условие не имеет действий, связанных с ним, его слот должен быть занят
NOP
:источник
Еще один пример использования двухбайтовой NOP: http://blogs.msdn.com/b/oldnewthing/archive/2011/09/21/10214405.aspx
источник