Dans l'instruction du microprocesseur 8085, il y a une opération de commande de machine "nop" (aucune opération). Ma question est pourquoi avons-nous besoin d'une opération sans? Je veux dire que si nous devons mettre fin au programme, nous utiliserons HLT ou RST 3. Ou si nous voulons passer à l'instruction suivante, nous donnerons les instructions suivantes. Mais pourquoi pas d'opération? Quel est le besoin?
microprocessor
Demietra95
la source
la source
Réponses:
Une utilisation de l'instruction NOP (ou NOOP, sans opération) dans les CPU et les MCU est d'insérer un petit retard prévisible dans votre code. Bien que les NOP n'effectuent aucune opération, cela prend un certain temps pour les traiter (le CPU doit récupérer et décoder l'opcode, il a donc besoin de peu de temps pour le faire). Aussi peu qu'un cycle de CPU est "gaspillé" pour exécuter une instruction NOP (le nombre exact peut être déduit de la fiche technique CPU / MCU, généralement), donc mettre N NOP en séquence est un moyen facile d'insérer un retard prévisible:
où K est le nombre de cycles (le plus souvent 1) nécessaires au traitement d'une instruction NOP, et est la période d'horloge.Tclock
Pourquoi ferais-tu ça? Il peut être utile de forcer le CPU à attendre un peu que les périphériques externes (peut-être plus lents) terminent leur travail et communiquent les données au CPU, c'est-à-dire que NOP est utile à des fins de synchronisation.
Voir également la page Wikipédia correspondante sur NOP .
Une autre utilisation est d'aligner le code à certaines adresses en mémoire et d'autres "astuces d'assemblage", comme expliqué également dans ce fil sur Programmers.SE et dans cet autre fil sur StackOverflow .
Un autre article intéressant sur le sujet .
Ce lien vers une page de livre Google se réfère en particulier au processeur 8085. Extrait:
EDIT (pour répondre à une préoccupation exprimée dans un commentaire)
Si vous vous inquiétez de la vitesse, gardez à l'esprit que l'efficacité (temporelle) n'est qu'un paramètre à considérer. Tout dépend de l'application: si vous voulez calculer le 10 milliardième chiffre de , alors votre seule préoccupation pourrait être la vitesse. D'un autre côté, si vous souhaitez enregistrer les données des capteurs de température connectés à un MCU via un ADC, la vitesse n'est généralement pas si importante, mais il est essentiel d' attendre le bon laps de temps pour permettre à l'ADC de terminer correctement chaque lecture . Dans ce cas, si le MCU n'attend pas suffisamment, il risque d'obtenir des données complètement non fiables (je concède qu'il obtiendrait ces données plus rapidement , cependant: o).π
la source
Les autres réponses ne considèrent qu'un NOP qui s'exécute à un moment donné - qui est utilisé assez couramment, mais ce n'est pas la seule utilisation de NOP.
La non-exécution NOP est également très utile pour écrire du code qui peut être patché - en gros, vous aurez pad la fonction avec quelques NOP après la
RET
(ou une instruction similaire). Lorsque vous devez patcher l'exécutable, vous pouvez facilement ajouter plus de code à la fonction en commençant par l'originalRET
et en utilisant autant de ces NOP que vous en avez besoin (par exemple pour les sauts longs ou même le code en ligne) et en terminant par un autreRET
.Dans ce cas d'utilisation, noöne s'attend à ce que le soit
NOP
exécuté. Le seul point est de permettre de patcher l'exécutable - dans un exécutable théorique non rembourré, vous devrez en fait changer le code de la fonction elle-même (parfois cela pourrait correspondre aux limites d'origine, mais assez souvent vous aurez quand même besoin d'un saut ) - c'est beaucoup plus compliqué, surtout si l'on considère un assemblage écrit manuellement ou un compilateur d'optimisation; vous devez respecter les sauts et les constructions similaires qui auraient pu pointer sur un morceau de code important. Dans l'ensemble, assez délicat.Bien sûr, c'était beaucoup plus utilisé dans les temps anciens, quand il était utile de faire des patchs comme ces petits et ligne . Aujourd'hui, vous allez simplement distribuer un binaire recompilé et en finir avec lui. Il y en a encore qui utilisent les correctifs NOP (exécutés ou non, et pas toujours littéraux
NOP
- par exemple, Windows utiliseMOV EDI, EDI
pour les correctifs en ligne - c'est le genre où vous pouvez mettre à jour une bibliothèque système pendant que le système est en cours d'exécution, sans avoir besoin de redémarrages).Alors la dernière question est, pourquoi avoir une instruction dédiée pour quelque chose qui ne fait vraiment rien?
MOV AX, AX
feront exactement la même chose, mais ne signalent pas l'intention aussi clairement.NOP
beaucoup; le code d'assemblage compilé réel n'en a plus. Il convient de noter que ce ne sont pas des x86NOP
, cependant.NOP
, ce qui rend le démontage beaucoup plus facile à lire) et pour le patch à chaud (bien que je préfère de loin Modifier et Continuer dans Visual Studio: P).Pour exécuter les NOP, il y a bien sûr quelques points supplémentaires:
MOV EDI, EDI
, il y a d'autres NOP efficaces que le littéralNOP
.MOV EDI, EDI
a les meilleures performances en tant que NOP à 2 octets sur x86. Si vous avez utilisé deuxNOP
s, ce serait deux instructions à exécuter.ÉDITER:
En fait, la discussion avec @DmitryGrigoryev m'a obligé à y réfléchir un peu plus, et je pense que c'est un ajout précieux à cette question / réponse, alors laissez-moi ajouter quelques bits supplémentaires:
Premièrement, je veux dire, évidemment - pourquoi y aurait-il une instruction qui ferait quelque chose comme ça
mov ax, ax
? Par exemple, regardons le cas du code machine 8086 (plus ancien que même le code machine 386):0x90
. C'est encore le moment où beaucoup de gens ont écrit en assemblée, faites attention. Donc, même s'il n'y avait pas d'NOP
instruction dédiée , leNOP
mot - clé (alias / mnémonique) serait toujours utile et correspondrait à cela.MOV
que mapper réellement vers de nombreux opcodes différents, car cela économise du temps et de l'espace - par exemple,mov al, 42
"déplacer l'octet immédiat dans leal
registre", ce qui se traduit par0xB02A
(0xB0
étant l'opcode,0x2A
étant l'argument "immédiat"). Cela prend donc deux octets.mov al, al
(puisque c'est une chose stupide à faire, fondamentalement), vous devrez donc utiliser lamov al, rmb
surcharge (rmb étant "registre ou mémoire"). Cela prend en fait trois octets. (bien qu'il utiliserait probablement le moins spécifique à lamov rb, rmb
place, qui ne devrait prendre que deux octetsmov al, al
- l'argument d'argument est utilisé pour spécifier à la fois le registre source et le registre cible; maintenant vous savez pourquoi 8086 n'avait que 8 registres: D). Comparez àNOP
, qui est une instruction à un octet! Cela économise de la mémoire et du temps, car la lecture de la mémoire dans le 8086 était encore assez coûteuse - sans parler du chargement de ce programme à partir d'une bande ou d'une disquette ou quelque chose, bien sûr.Alors d'où
xchg ax, ax
vient-il? Il suffit de regarder les opcodes des autresxhcg
instructions. Vous verrez0x86
,0x87
et enfin,0x91
-0x97
. Donc,nop
avec, cela0x90
semble être un assez bon ajustementxchg ax, ax
(qui, encore une fois, n'est pas unexchg
"surcharge" - vous devrez utiliserxchg rb, rmb
, à deux octets). Et en fait, je suis presque sûr que c'était un bel effet secondaire de la micro-architecture de l'époque - si je me souviens bien, il était facile de mapper toute la gamme de0x90-0x97
"xchg, agissant sur des registresax
etax
-di
" ( l'opérande étant symétrique, cela vous a donné la gamme complète, y compris le nopxchg ax, ax
; notez que l'ordre estax, cx, dx, bx, sp, bp, si, di
-bx
est aprèsdx
,ax
; rappelez-vous, les noms de registre sont des mnémoniques, pas des noms ordonnés - accumulateur, compteur, données, base, pointeur de pile, pointeur de base, index source, index destination). La même approche a également été utilisée pour d'autres opérandes, par exemple l'mov someRegister, immediate
ensemble. D'une certaine manière, vous pourriez penser à cela comme si l'opcode n'était pas un octet entier - les derniers bits sont "un argument" pour l'opérande "réel".Tout cela dit, sur x86,
nop
peut être considéré comme une vraie instruction ou non. La micro-architecture originale le traitait comme une variante dexchg
si je me souviens bien, mais elle était en fait nomméenop
dans la spécification. Et comme celaxchg ax, ax
n'a pas vraiment de sens en tant qu'instruction, vous pouvez voir comment les concepteurs du 8086 ont économisé sur les transistors et les voies dans le décodage des instructions en exploitant le fait que cela0x90
correspond naturellement à quelque chose qui est entièrement "nul".D'un autre côté, le i8051 possède un opcode entièrement conçu pour
nop
-0x00
. Un peu pratique. La conception d'instruction utilise essentiellement le demi - octet pour le fonctionnement et le bit de poids faible pour la sélection des opérandes - par exemple,add a
est0x2Y
, et0xX8
signifie « registre 0 direct »,0x28
c'estadd a, r0
. Économise beaucoup sur le silicium :)Je pourrais continuer, car la conception du processeur (sans parler de la conception du compilateur et de la conception du langage) est un sujet assez large, mais je pense que j'ai montré de nombreux points de vue différents qui sont entrés assez bien dans la conception.
la source
NOP
est généralement un aliasMOV ax, ax
,ADD ax, 0
ou des instructions similaires. Pourquoi voudriez-vous concevoir une instruction dédiée qui ne fait rien alors qu'il y en a beaucoup là-bas.MOV ax, ax
déplacement;NOP
aura toujours un nombre fixe de cycles pour l'exécution. Mais je ne vois pas comment cela est pertinent à ce que j'ai écrit dans ma réponse de toute façon.MOV ax, ax
distance, car au moment où ils savent que c'est uneMOV
instruction qui est déjà dans le pipeline.NOP
etMOV ax, ax
). Les processeurs modernes sont bien plus complexes que les compilateurs C oldschool :))LD r, r
instructions où ser
trouve un seul registre, similaire à votreMOV ax, ax
instruction. La raison pour laquelle c'est 7 et non 8 est parce qu'une des instructions est surchargée pour devenirHALT
. Les 8080 et Z80 ont donc au moins 7 autres instructions qui font la même chose queNOP
! Chose intéressante, même si ces instructions ne sont pas logiquement liées par motif binaire, elles prennent toutes 4 états T à exécuter, il n'y a donc aucune raison d'utiliser lesLD
instructions!À la fin des années 70, nous (j'étais alors un jeune étudiant en recherche) avions un petit système de développement (8080 si la mémoire est utilisée) qui fonctionnait en 1024 octets de code (c'est-à-dire une seule UVEPROM) - il n'avait que quatre commandes à charger (L ), enregistrer (S), imprimer (P) et autre chose dont je ne me souviens pas. Il était piloté avec un vrai téléscripteur et une bande perforée. C'était bien codé!
Un exemple d'utilisation de NOOP était dans une routine de service d'interruption (ISR), qui étaient espacées à des intervalles de 8 octets. Cette routine a fini par être longue de 9 octets, se terminant par un (long) saut vers une adresse légèrement plus haut dans l'espace adresse. Cela signifiait, étant donné le petit ordre d'octets endian, que l'octet d'adresse haute était 00h, et inséré dans le premier octet du prochain ISR, ce qui signifiait qu'il (le prochain ISR) commençait par NOOP, juste pour que `` nous '' puisse tenir le code dans l'espace limité!
Le NOOP est donc utile. De plus, je soupçonne qu'il était plus facile pour Intel de le coder de cette façon - Ils avaient probablement une liste d'instructions qu'ils voulaient implémenter et cela a commencé à '1', comme toutes les listes (c'était l'époque de FORTRAN), donc le zéro Le code NOOP est devenu une retombée. (Je n'ai jamais vu un article affirmant qu'un NOOP est un élément essentiel de la théorie des sciences informatiques (le même Q que: les mathématiciens ont-ils un nul op, différent du zéro de la théorie des groupes?)
la source
0x00
pournop
dans ma réponse. En bref, il économise sur le décodage des instructions -xchg ax, ax
découle naturellement de la façon dont le décodage des instructions fonctionne, et il fait quelque chose de "nul", alors pourquoi ne pas utiliser cela et l'appelernop
, non? :) Utilisé pour économiser un peu sur le silicium pour le décodage des instructions ...Sur certaines architectures,
NOP
est utilisé pour occuper les créneaux de retard inutilisés . Par exemple, si l'instruction de branchement n'efface pas le pipeline, plusieurs instructions après son exécution sont quand même exécutées:Mais que se passe-t-il si vous ne disposez pas d'instructions utiles à adapter après
JMP
? Dans ce cas, vous devrez utiliser l'NOP
art.Les créneaux de retard ne sont pas limités aux sauts. Sur certaines architectures, les risques de données dans le pipeline CPU ne sont pas résolus automatiquement. Cela signifie qu'après chaque instruction qui modifie un registre, il y a un slot où la nouvelle valeur du registre n'est pas encore accessible. Si l'instruction suivante a besoin de cette valeur, l'emplacement doit être occupé par
NOP
:De plus, certaines instructions d'exécution conditionnelle ( If-True-False et similaires) utilisent des emplacements pour chaque condition, et lorsqu'une condition particulière n'a aucune action associée, son emplacement doit être occupé par
NOP
:la source
Un autre exemple d'utilisation d'un NOP à deux octets : http://blogs.msdn.com/b/oldnewthing/archive/2011/09/21/10214405.aspx
la source