Na instrução do microprocessador 8085, há uma operação de controle da máquina "nop" (sem operação). Minha pergunta é: por que precisamos de uma operação sem? Quero dizer, se tivermos que encerrar o programa, usaremos o HLT ou o RST 3. Ou se queremos passar para a próxima instrução, forneceremos as próximas instruções. Mas por que nenhuma operação? Qual é a necessidade?
microprocessor
Demietra95
fonte
fonte
Respostas:
Um uso da instrução NOP (ou NOOP, sem operação) em CPUs e MCUs é inserir um pequeno atraso previsível no seu código. Embora os NOPs não realizem nenhuma operação, leva algum tempo para processá-los (a CPU precisa buscar e decodificar o código de operação, portanto, precisa de algum tempo para fazer isso). Tão pouco quanto 1 ciclo de CPU é "desperdiçado" para executar uma instrução NOP (o número exato pode ser deduzido da folha de dados da CPU / MCU, geralmente), portanto, colocar N NOPs em seqüência é uma maneira fácil de inserir um atraso previsível:
onde K é o número de ciclos (na maioria das vezes 1) necessário para o processamento de uma instrução NOP e é o período do relógio.Tc l o c k
Por que você faria isso? Pode ser útil forçar a CPU a esperar um pouco pelos dispositivos externos (talvez mais lentos) para concluir seu trabalho e relatar dados à CPU, ou seja, o NOP é útil para fins de sincronização.
Veja também a página da Wikipedia relacionada no NOP .
Outro uso é alinhar o código em determinados endereços da memória e outros "truques de montagem", conforme explicado também neste tópico no Programmers.SE e neste outro tópico no StackOverflow .
Outro artigo interessante sobre o assunto .
Este link para uma página de livro do Google refere-se especialmente à CPU 8085. Excerto:
EDIT (para resolver uma preocupação expressa em um comentário)
Se você estiver preocupado com a velocidade, lembre-se de que a eficiência (tempo) é apenas um parâmetro a considerar. Tudo depende da aplicação: se você deseja calcular o décimo bilionésimo número de , talvez sua única preocupação seja a velocidade. Por outro lado, se você deseja registrar dados de sensores de temperatura conectados a um MCU por meio de um ADC, a velocidade geralmente não é tão importante, mas é essencial aguardar a quantidade certa de tempo para permitir que o ADC conclua corretamente cada leitura . Nesse caso, se o MCU não esperar o suficiente, corre o risco de obter dados completamente não confiáveis (eu admito que os dados seriam mais rápidos , porém: o).π
fonte
As outras respostas estão considerando apenas um NOP que realmente é executado em algum momento - que é usado com bastante frequência, mas não é o único uso do NOP.
O NOP não em execução também é bastante útil ao escrever código que pode ser corrigido - basicamente, você preencherá a função com alguns NOPs após a
RET
(ou instrução similar). Quando você precisar corrigir o executável, poderá adicionar facilmente mais código à função, começando pelo originalRET
e usando quantos NOPs forem necessários (por exemplo, para saltos longos ou mesmo código embutido) e terminando com outroRET
.Nesse caso de uso, ninguém espera que a
NOP
execução seja executada. O único ponto é permitir o patch do executável - em um executável teórico não acolchoado, você teria que realmente alterar o código da função (às vezes, ele pode se ajustar aos limites originais, mas muitas vezes você precisará de um salto de qualquer maneira ) - isso é muito mais complicado, especialmente considerando a montagem escrita manualmente ou um compilador de otimização; você deve respeitar saltos e construções semelhantes que possam ter apontado em algum trecho de código importante. Em suma, bastante complicado.Obviamente, isso era muito mais usado nos velhos tempos, quando era útil fazer correções como essas pequenas e online . Hoje, você apenas distribuirá um binário recompilado e pronto. Ainda existem alguns que usam NOPs de patches (executando ou não, e nem sempre literais
NOP
s - por exemplo, o Windows usaMOV EDI, EDI
para patches on-line - é o tipo em que você pode atualizar uma biblioteca do sistema enquanto o sistema está realmente em execução, sem a necessidade de reiniciar).Portanto, a última pergunta é: por que ter uma instrução dedicada para algo que realmente não faz nada?
MOV AX, AX
farão exatamente o mesmo, mas não sinalizam a intenção com tanta clareza.NOP
bastante; o código de montagem compilado real não tem mais isso. Deve-se notar que esses não são x86-NOP
s.NOP
, facilitando muito a leitura da desmontagem) quanto para o hot-patch (embora eu prefira, de longe, Editar e Continuar no Visual Studio: P).Para executar NOPs, é claro que existem mais alguns pontos:
MOV EDI, EDI
, há outros NOPs efetivos que não o literalNOP
.MOV EDI, EDI
tem o melhor desempenho como um NOP de 2 bytes no x86. Se você usasse doisNOP
s, seriam duas instruções para executar.EDITAR:
Na verdade, a discussão com @DmitryGrigoryev me forçou a pensar um pouco mais sobre isso, e acho que é uma adição valiosa a esta pergunta / resposta, então deixe-me adicionar alguns bits extras:
Primeiro, ponto, obviamente - por que haveria uma instrução que faça algo assim
mov ax, ax
? Por exemplo, vejamos o caso do código de máquina 8086 (mais antigo que o código de máquina 386):0x90
. Ainda é o momento em que muitas pessoas escreveram em assembléia, lembre-se. Portanto, mesmo se não houvesse umaNOP
instrução dedicada , aNOP
palavra - chave (alias / mnemonic) ainda seria útil e seria mapeada para isso.MOV
na verdade são mapeadas para muitos opcodes diferentes, porque isso economiza tempo e espaço - por exemplo,mov al, 42
é "mover byte imediato para oal
registrador", que se traduz em0xB02A
(0xB0
sendo o opcode,0x2A
sendo o argumento "imediato"). Então isso leva dois bytes.mov al, al
(já que isso é uma coisa estúpida de se fazer, basicamente), então você terá que usar amov al, rmb
sobrecarga (rmb sendo "registradora ou memória"). Na verdade, isso leva três bytes. (embora provavelmente use o menos específicomov rb, rmb
, que deve levar apenas dois bytes paramov al, al
- o argumento byte é usado para especificar o registro de origem e o destino; agora você sabe por que o 8086 tinha apenas 8 registros: D). Compare comNOP
, que é uma instrução de byte único! Isso economiza memória e tempo, já que a leitura da memória no 8086 ainda era muito cara - sem mencionar o carregamento desse programa a partir de uma fita ou disquete ou algo do tipo, é claro.Então, de onde
xchg ax, ax
vem? Você apenas precisa olhar os códigos de código das outrasxhcg
instruções. Você verá0x86
,0x87
e, finalmente,0x91
-0x97
. Portanto,nop
com isso0x90
parece um bom ajuste paraxchg ax, ax
(o que, novamente, não é umaxchg
"sobrecarga" - você precisaria usarxchg rb, rmb
, em dois bytes). Na verdade, tenho certeza de que esse foi um bom efeito colateral da microarquitetura da época - se bem me lembro, foi fácil mapear todo o intervalo de0x90-0x97
"xchg, agindo sobre registrosax
eax
-di
" ( o operando é simétrico, isso ofereceu a faixa completa, incluindo o nopxchg ax, ax
; observe que a ordem éax, cx, dx, bx, sp, bp, si, di
-bx
é depoisdx
,ax
; lembre-se, os nomes dos registradores são mnemônicos, não os pedidos - acumulador, contador, dados, base, ponteiro de pilha, ponteiro de base, índice de origem, índice de destino). A mesma abordagem também foi usada para outros operandos, por exemplo, omov someRegister, immediate
conjunto. De certa forma, você poderia pensar nisso como se o código de operação não fosse realmente um byte completo - os últimos bits são "um argumento" para o operando "real".Tudo isso dito, no x86,
nop
pode ser considerado uma instrução real, ou não. A microarquitetura original tratou-a como uma variante de,xchg
se bem me lembro, mas na verdade foi nomeadanop
na especificação. E comoxchg ax, ax
realmente não faz sentido como uma instrução, você pode ver como os projetistas do 8086 economizaram em transistores e caminhos na decodificação de instruções, explorando o fato de que0x90
mapeia naturalmente algo que é totalmente "desagradável".Por outro lado, o i8051 possui um código de operação totalmente projetado para
nop
-0x00
. Meio prático. O design das instruções está basicamente usando a mordidela alta para operação e a mordidela baixa para selecionar os operandos - por exemplo,add a
é0x2Y
e0xX8
significa "registre 0 direto", assim0x28
éadd a, r0
. Economiza muito em silício :)Eu ainda poderia continuar, já que o design da CPU (para não mencionar o design do compilador e o design da linguagem) é um tópico bastante amplo, mas acho que mostrei muitos pontos de vista diferentes que entraram no design muito bem.
fonte
NOP
geralmente é um apelido paraMOV ax, ax
,ADD ax, 0
ou instruções semelhantes. Por que você projetaria uma instrução dedicada que não faz nada quando há muitas por aí?MOV ax, ax
distância;NOP
sempre terá uma quantidade fixa de ciclos para execução. Mas não vejo como isso seja relevante para o que escrevi na minha resposta.MOV ax, ax
distância, porque quando sabem que é umaMOV
instrução já está no pipeline.NOP
eMOV ax, ax
). CPUs modernos são muito mais complexas do que os compiladores C oldschool :))LD r, r
instruções onder
existe um único registro, semelhante à suaMOV ax, ax
instrução. O motivo é 7 e não 8 é porque uma das instruções está sobrecarregada para se tornarHALT
. Portanto, o 8080 e o Z80 têm pelo menos 7 outras instruções que fazem o mesmo queNOP
! Curiosamente, mesmo que essas instruções não sejam logicamente relacionadas por padrão de bits, todas elas levam 4 estados T para serem executadas, portanto não há razão para usar asLD
instruções!No final dos anos 70, nós (eu era um jovem estudante de pesquisa na época) tínhamos um pequeno sistema de desenvolvimento (8080 se a memória serve) que rodava em 1024 bytes de código (ou seja, uma única UVEPROM) - ele tinha apenas quatro comandos para carregar (L ), salve (S), imprima (P) e outra coisa que não me lembro. Foi conduzido com um teletipo real e uma fita perfurada. Foi bem codificado!
Um exemplo de uso de NOOP estava em uma rotina de serviço de interrupção (ISR), espaçada em intervalos de 8 bytes. Essa rotina acabou tendo 9 bytes de comprimento, terminando com um salto (longo) para um endereço um pouco mais acima do espaço de endereço. Isso significava, dada a pequena ordem de bytes endian, que o byte de endereço alto era 00h e se encaixava no primeiro byte do próximo ISR, o que significava que ele (o próximo ISR) começava com NOOP, apenas para 'nós' caber o código no espaço limitado!
Portanto, o NOOP é útil. Além disso, suspeito que fosse mais fácil para a Intel codificá-lo dessa maneira - eles provavelmente tinham uma lista de instruções que queriam implementar e ela começou em '1', como todas as listas fazem (eram os dias de FORTRAN), então o zero O código NOOP se tornou um problema. (Eu nunca vi um artigo argumentando que um NOOP é uma parte essencial da teoria da Ciência da Computação (o mesmo Q: os matemáticos têm uma nul op, distinta do zero da teoria dos grupos?)
fonte
0x00
paranop
na minha resposta. Em resumo, economiza na decodificação de instruções -xchg ax, ax
flui naturalmente da maneira como a decodificação de instruções funciona e faz algo "desagradável"; então, por que não usar isso e chamá-lonop
, certo? :) Usado para salvar um pouco sobre o silício para a descodificação de instrução ...Em algumas arquiteturas,
NOP
é usado para ocupar slots de atraso não utilizados . Por exemplo, se a instrução de ramificação não limpar o pipeline, várias instruções após a execução serão executadas de qualquer maneira:Mas e se você não tiver instruções úteis para ajustar após o
JMP
? Nesse caso, você terá que usarNOP
s.Os slots de atraso não se limitam a saltos. Em algumas arquiteturas, os riscos de dados no pipeline da CPU não são resolvidos automaticamente. Isso significa que, após cada instrução que modifica um registro, existe um slot em que o novo valor do registro ainda não está acessível. Se a próxima instrução precisar desse valor, o slot deve ser ocupado por
NOP
:Além disso, algumas instruções de execução condicional ( If-True-False e similares) usam slots para cada condição e, quando uma condição específica não possui ações associadas, seu slot deve ser ocupado por
NOP
:fonte
Outro exemplo de uso de um NOP de dois bytes : http://blogs.msdn.com/b/oldnewthing/archive/2011/09/21/10214405.aspx
fonte