Neden “nop” ihtiyacımız var Ie Mikroişlemci 8085'te işlem talimatı yok?


19

Mikroişlemci 8085 talimatında, bir makine kontrol işlemi "nop" (işlem yok) vardır. Benim sorum neden ameliyat olmamıza gerek var? Yani programı sonlandırmak zorunda kalırsak HLT veya RST 3 kullanacağız. Veya bir sonraki talimata geçmek istiyorsak bir sonraki talimatı vereceğiz. Ama neden operasyon yok? İhtiyaç nedir?


5
NOP sık sık hata ayıklama ve program güncelleme için kullanılır. Daha sonraki bir tarihte, programınıza bazı satırlar eklemek isterseniz, yalnızca NOP'un üzerine yazabilirsiniz. Aksi takdirde satır eklemeniz gerekir ve ekleme, tüm programı kaydırmak anlamına gelir. Ayrıca, aynı argümanları izleyerek hatalı talimatlar (yanlış) NOP ile değiştirilebilir (sadece üzerine yazılır).
Plütonyum kaçakçısı

Ohkay. Ama nop kullanmak da alanı arttırıyor. Oysa birincil hedefimiz çok az yer kaplamaktır.
Demietra95

* Yani amacımız bir problemi küçültmek. Peki bu da bir sorun haline gelmiyor mu?
Demietra95

2
Bu yüzden akıllıca kullanılmalıdır. Aksi takdirde, tüm programınız sadece bir grup NOP olacaktır.
Plütonyum kaçakçısı

Yanıtlar:


34

CPU ve MCU'larda NOP (veya NOOP, işlemsiz) komutunun bir kullanımı, kodunuza biraz öngörülebilir bir gecikme eklemektir. NOP'lar herhangi bir işlem yapmasa da, bunları işlemek biraz zaman alır (CPU'nun op kodunu alması ve kodunu çözmesi gerekir, bu yüzden bunu yapmak için biraz zaman gerekir). Bir NOP talimatı yürütmek için 1 CPU çevrimi "israf edildiğinde" (tam sayı genellikle CPU / MCU veri sayfasından çıkarılabilir), bu nedenle N NOP'ları sıraya koymak tahmin edilebilir bir gecikme eklemek için kolay bir yoldur:

tdelbiry=N-TclÖckK

K NOP talimatı işlenmesi için gerekli olan döngü sayısı (en sık 1) olup, burada saat süredir.TclÖck

Neden bunu yapasın? CPU'yu harici (belki daha yavaş) cihazların işlerini tamamlaması ve verileri CPU'ya bildirmesi için biraz beklemeye zorlamak yararlı olabilir, yani NOP senkronizasyon amaçları için yararlıdır.

Ayrıca NOP ile ilgili Wikipedia sayfasına bakın .

Başka bir kullanım olarak da açıklanabilir, hafıza ve diğer "montaj hileler" belirli adreslerde hizalama koduna olduğunu Programmers.SE bu iplik ve StackOverflow'daki bu diğer iplik .

Konuyla ilgili bir başka ilginç makale .

Bu bir Google kitap sayfasına link özellikle 8085 CPU'yu gösterir. Alıntı:

Her NOP komutu, getirme, kod çözme ve yürütme için dört saat kullanır.

EDIT (bir yorumda ifade edilen bir sorunu gidermek için)

Hız konusunda endişeleniyorsanız, (zaman) verimliliğinin dikkate alınması gereken tek parametre olduğunu unutmayın. Herşey uygulamaya bağlıdır: Eğer 10000000000 rakam hesaplamak istiyorsanız , o zaman belki tek endişesi hız olabilir. Öte yandan, bir ADU aracılığıyla bir MCU'ya bağlı sıcaklık sensörlerinden veri kaydetmek istiyorsanız, hız genellikle çok önemli değildir, ancak ADC'nin her okumayı doğru bir şekilde tamamlaması için doğru süreyi beklemek önemlidir . Bu durumda, MCU yeterince beklemezse, tamamen güvenilir olmayan veriler elde etme riski vardır (bu verileri daha hızlı alacağına inanıyorum : o).π


3
Birçok şey (özellikle UC dışında IC'leri çalıştıran çıkışlar) 'D'nin sabit olması ile saat kenarı arasındaki minimum süre 100 us' veya 'IR LED'i 1 MHz'de yanıp sönmelidir' gibi zamanlama kısıtlamalarına tabidir. Bu nedenle (doğru) gecikmeler sıklıkla gereklidir.
Wouter van Ooijen

6
NOP'lar, bir seri protokolü biraz vurarak zamanlamayı doğru yapmak için yararlı olabilir. Ayrıca, Program Sayacı bozulursa (örn. PSU hatası, nadir bir gama ışını olayının etkisi, vb.) Ve bir aksi takdirde kod alanının boş kısmı.
Techydude

6
Atari 2600 Video Bilgisayar Sistemi'nde (kartuşlarda depolanan programları çalıştırmak için ikinci video oyun konsolu), işlemci her tarama satırında tam olarak 76 döngü çalıştıracak ve bir çok işlemin başlamasından sonra belirli sayıda döngü gerçekleştirmesi gerekecektir. tarama çizgisi. Bu işlemcide, belgelenmiş "NOP" komutu iki döngü alır, ancak kodun, belirli sayıda döngüye kadar bir gecikmeyi önlemek için aksi takdirde işe yaramayan üç döngü talimatından faydalanması da yaygındır. Daha hızlı koşmak tamamen bozuk bir görüntü verirdi.
supercat

1
Gecikmeler için NOP'ların kullanılması, bir G / Ç aygıtının ardışık işlemler arasında minimum süre uyguladığı, ancak maksimum olmayan durumlarda gerçek zamanlı olmayan sistemlerde bile anlamlı olabilir. Örneğin, birçok denetleyicide, bir baytın bir SPI bağlantı noktasını kaydırmak sekiz CPU döngüsü gerektirir. Baytları bellekten almaktan ve SPI portuna çıkarmaktan başka bir şey yapmayan kod biraz hızlı çalışabilir, ancak SPI portunun her bayt için hazır olup olmadığını test etmek için mantık eklemek gereksiz yere yavaşlatacaktır. Bir NOP veya iki eklemek, kodun maksimum kullanılabilir hıza ulaşmasına izin verebilir ...
supercat

1
... kesinti olmadığı durumda. Bir kesinti isabet ederse, NOP'lar gereksiz yere zaman kaybeder, ancak bir veya iki NOP tarafından boşa harcanan zaman, bir kesintinin onları gereksiz hale getirip getirmediğini belirlemek için gereken süreden daha az olacaktır.
supercat

8

Diğer cevaplar sadece bir noktada gerçek anlamda çalışan bir NOP düşünüyor - bu oldukça yaygın kullanılıyor, ancak NOP'un tek kullanımı değil.

Yamalı edilebilir kod yazarken olmayan yürütme NOP de oldukça yararlıdır - temelde, sen olacak ped birkaç NOPs ile işlev sonraRET (ya da benzeri talimat). Yürütülebilir dosyayı yamalamak zorunda olduğunuzda, orijinalden başlayarak RETve bu NOP'lardan istediğiniz kadarını (örn. Uzun atlama veya hatta satır içi kod için) kullanarak ve başka biriyle bitirerek işleve kolayca daha fazla kod ekleyebilirsiniz RET.

Bu kullanım durumunda, noöne hiçbir NOPzaman yürütmeyi beklemez . Tek nokta, çalıştırılabilir dosyanın yamalanmasına izin vermektir - teorik, yastıklı olmayan bir yürütülebilir dosyada, işlevin kodunu gerçekten değiştirmeniz gerekir (bazen orijinal sınırlara uyuyor olabilir, ancak çoğu zaman yine de bir sıçrama yapmanız gerekir ) - bu özellikle elle yazılmış montaj veya optimize edici bir derleyici göz önüne alındığında çok daha karmaşıktır; bazı önemli kod parçalarına işaret edebilecek sıçramalara ve benzer yapılara saygı göstermelisiniz. Sonuçta, oldukça zor.

Tabii ki, bu eski günlerde, bu küçük ve çevrimiçi gibi yamalar yapmanın yararlı olduğu çok daha yoğun bir şekilde kullanıldı . Bugün, yeniden derlenmiş bir ikili dosyayı dağıtacak ve onunla işleneceksiniz. Yamalama NOP'larını kullanan bazı kullanıcılar var (her zaman değişmez değil NOP- yürütüyor ya da değil - örneğin, Windows MOV EDI, EDIçevrimiçi yama için kullanır - bu, sistem yeniden başlatıldığında sistem çalışırken gerçekte bir sistem kitaplığını güncelleyebileceğiniz türdür).

Öyleyse son soru, neden gerçekten hiçbir şey yapmayan bir şey için özel bir talimatınız var?

  • Bu gerçek bir talimattır - montajda hata ayıklama veya elle kodlama yaparken önemlidir. Gibi talimatlar MOV AX, AXtam olarak aynı şeyi yapacak, ama niyetini çok açık bir şekilde göstermiyor.
  • Doldurma - "kod", yalnızca hizalamaya bağlı olan kodun genel performansını artırmak için kullanılır. Asla idam etmek istemedi. Bazı hata ayıklayıcılar, doldurma NOP'larını sökme işlemlerinde gizler.
  • Derleyicileri optimize etmek için daha fazla alan sağlar - hala kullanılan desen, birincisi oldukça basit olan ve çok sayıda gereksiz montaj kodu üretirken, iki adım derlemeniz olması, ikincisi ise adres referanslarını temizler, yeniden sarır ve kaldırır yabancı talimatlar. Bu genellikle JIT derlemeli dillerde de görülür - hem .NET'in IL hem de JVM'nin bayt kodu kullanımı NOPoldukça fazladır; derlenmiş asıl montaj kodu artık bunlara sahip değil. Bununla birlikte, bunların x86- NOPs olmadığı belirtilmelidir .
  • Çevrimiçi hata ayıklamayı hem okumak için kolaylaştırır (önceden sıfırlanmış bellek her NOPşeydir, sökülmeyi çok daha kolay okunur hale getirir) hem de sıcak yama için (Visual Studio'da Düzenleme ve Devam Etmeyi tercih ediyorum: P).

NOP'ları yürütmek için elbette birkaç nokta daha var:

  • Performans, elbette - bu yüzden 8085'te değildi, ancak 80486'da bile "hiçbir şey yapmamak" için biraz daha karmaşık bir boru hattı talimatı yürütmesi vardı.
  • Görüldüğü gibi MOV EDI, EDI, kelimenin tam anlamıyla etkili başka NOP'lar var NOP. MOV EDI, EDIx86'da 2 baytlık NOP olarak en iyi performansa sahiptir. İki NOPs kullandıysanız , yürütmek için iki talimat olurdu.

DÜZENLE:

Aslında, @DmitryGrigoryev ile yapılan tartışma beni biraz daha düşünmeye zorladı ve bence bu soru / cevap için değerli bir katkı, bu yüzden biraz fazladan bit eklememe izin verin:

İlk olarak, açıkçası - neden böyle bir şey yapan bir talimat olsun ki mov ax, ax? Örneğin, 8086 makine kodu durumuna (386 makine kodundan daha eski) bakalım:

  • Opcode ile özel bir NOP talimatı vardır 0x90. Bu hala birçok insanın mecliste yazdığı zamandır. Bu nedenle, özel bir NOPtalimat olmasa bile NOP(takma ad / anımsatıcı) anahtar kelimesi yine de yararlı olur ve buna eşlenir.
  • Talimatlar gibi MOVo zaman ve mekan tasarruf çünkü, aslında birçok farklı opcodes eşleme - örneğin, mov al, 42"hemen bayt taşımak olduğunu alçevirir, kayıt" 0xB02A( 0xB0, işlemkodu olma 0x2A"acil" argüman olmak üzere). Bu iki bayt alır.
  • Kısayol op kodu yoktur mov al, al(çünkü bu aptalca bir şeydir, temelde), bu nedenle mov al, rmb(rmb "kayıt veya bellek" olmak üzere) aşırı yüklemeyi kullanmanız gerekir. Bu aslında üç bayt alır . ( mov rb, rmbbunun yerine daha az spesifik kullanılmasına rağmen , yalnızca iki bayt almalıdır mov al, al- argüman baytı hem kaynağı hem de hedef kaydını belirtmek için kullanılır; şimdi 8086'nın neden sadece 8 kaydı olduğunu biliyorsunuz: D). NOPTek baytlık bir talimat olan ile karşılaştırın ! Bu, bellek ve zamandan tasarruf sağlar, çünkü 8086'daki belleği okumak hala oldukça pahalıdır - bu programı bir banttan veya disketten veya elbette bir şeyden yüklemekten bahsetmiyoruz bile.

Peki nereden xchg ax, axgeliyor? Sadece diğer xhcgtalimatların opcodlarına bakmanız gerekir . Göreceksin 0x86, 0x87nihayet ve 0x91- 0x97. Yani nopbunun 0x90için oldukça iyi bir uyum gibi görünüyor xchg ax, ax(ki, yine, bir xchg"aşırı yük" değil - xchg rb, rmbiki baytta kullanmanız gerekir). Ve aslında, bunun zamanın mikro mimarisinin güzel bir yan etkisi olduğuna eminim - doğru hatırlarsam, 0x90-0x97"xchg, registerlar üzerinde hareket etmek axve ax- di" ( işlenen simetriktir, bu size nop dahil olmak üzere tüm aralığı verdi xchg ax, ax; siparişin ax, cx, dx, bx, sp, bp, si, di- bxsonra olduğunu dx,ax; Unutmayın, kayıt adları anımsatıcılar, sıralı adlar değildir - akümülatör, sayaç, veri, taban, yığın işaretçisi, taban işaretçisi, kaynak dizini, hedef dizini). Aynı yaklaşım, örneğin mov someRegister, immediateküme gibi diğer işlenenler için de kullanılmıştır . Bir bakıma, bunu opcode aslında tam bir bayt değilmiş gibi düşünebilirsiniz - son birkaç bit, "gerçek" işlenen için "bir argüman" dır.

Tüm bunlar, x86'da nopgerçek bir talimat olarak kabul edilebilir veya olmayabilir. Orijinal mikro mimari, xchgdoğru hatırlamıyorsam bir varyant olarak davrandı , ancak aslında nopspesifikasyonda adlandırıldı . Ve xchg ax, axgerçekten bir talimat olarak mantıklı olmadığından, 8086 tasarımcılarının, 0x90tamamen "noppy" olan bir şeye doğal olarak eşlediği gerçeğini kullanarak kod çözmede transistörlerde ve yollarda nasıl tasarruf ettiklerini görebilirsiniz.

Öte yandan, i8051 tamamen tasarlanmış-in işlem kodu için vardır nop- 0x00. Biraz pratik. Öğretim tasarımı temelde çalışma için yüksek nibble ve işlenen seçilmesi için düşük nibble kullanıyor - örneğin, add abir 0x2Yve 0xX8araçlar "0 doğrudan kayıt", bu yüzden 0x28olduğunu add a, r0. Silikonda çok tasarruf sağlar :)

CPU tasarımı (derleyici tasarımı ve dil tasarımından bahsetmiyorum) oldukça geniş bir konu olduğu için hala devam edebilirdim, ancak tasarıma oldukça hoş giden birçok farklı bakış açısı gösterdim.


Aslında, NOPgenellikle için diğer ad MOV ax, ax, ADD ax, 0ya da benzer talimatlar. Neden orada bolca olduğunda hiçbir şey yapmayan özel bir talimat tasarlasın?
Dmitry Grigoryev

@DmitryGrigoryev Bu gerçekten CPU dilinin (mikro mimari) tasarımına giriyor. Çoğu CPU (ve derleyici) MOV ax, axuzaklığı optimize etme eğilimindedir ; NOPyürütmek için her zaman sabit bir döngü döngüsüne sahip olacaktır. Ama bunun cevabımda yazdıklarım ile ne kadar alakalı olduğunu anlamıyorum.
Luaan

CPU'lar gerçekten bir MOV ax, axuzaklığı optimize edemez , çünkü bildikleri zaman bir MOVtalimat zaten boru hattındadır.
Dmitry Grigoryev

@DmitryGrigoryev Bu gerçekten bahsettiğiniz CPU türüne bağlıdır. Modern masaüstü işlemciler, sadece komut borularını değil, birçok şeyi yapar. Örneğin, CPU önbellek hatlarını geçersiz kılmak zorunda olmadığını bilir, aslında hiçbir şey yapması gerekmediğini bilir (HyperThreading ve hatta genel olarak dahil olan çoklu borular için çok önemlidir). (Muhtemelen aynı olurdu gerçi o da şube tahminini etkilemiş eğer sürpriz olmaz NOPve MOV ax, ax). Modern CPU'lar eski C derleyicilerinden çok daha karmaşık :))
Luaan

1
Kimseyi noöne'ye optimize etmek için +1 Sanırım hepimiz işbirliği yapmalı ve bu yazım tarzına geri dönmeliyiz! Z80 (ve sırayla 8080), LD r, rtalimatınıza rbenzer şekilde tek bir kayıt olan 7 MOV ax, axtalimat içerir. 8 değil, 7 olmasının nedeni, talimatlardan birinin aşırı yüklenmesi HALT. Bu yüzden 8080 ve Z80, aynı olan en az 7 talimat daha içerir NOP! İlginçtir ki, bu talimatlar bit deseniyle mantıksal olarak ilişkili olmasa da, hepsi 4 T Durumunu alırlar, bu yüzden LDtalimatları kullanmak için bir neden yoktur !
CJ Dennis

5

70'lerin sonunda, biz (o zaman genç bir araştırma öğrencisiydim) 1024 bayt kod (yani tek bir UVEPROM) içinde çalışan küçük bir dev sistemi (bellek hizmet veriyorsa 8080) vardı - yüklemek için sadece dört komut vardı (L ), kaydedin (S), yazdırın (P) ve hatırlayamadığım başka bir şey. Gerçek bir teletip ve yumruk bandı ile sürüldü. Sıkı kodlanmıştı!

NOOP kullanımına bir örnek, 8 bayt aralıklarla aralıklı bir kesme servis rutini (ISR) idi. Bu rutin, 9 bayt uzunluğunda ve adres alanının biraz daha yukarısında bir adrese (uzun) bir sıçrama ile son buldu. Bu, küçük endian bayt sırası göz önüne alındığında, yüksek adres baytının 00 saat olduğu ve bir sonraki ISR'nin ilk baytına yerleştirildiği anlamına geliyordu, bu da (bir sonraki ISR) NOOP ile başladığı anlamına geliyordu. sınırlı alanda kod!

Dolayısıyla NOOP faydalıdır. Ayrıca, intel'in bu şekilde kodlaması en kolay yol olduğundan şüpheliyim - Muhtemelen uygulamak istedikleri talimatların bir listesi vardı ve tüm listelerde olduğu gibi '1'de başladı (bu, FORTRAN'ın günleriydi), yani sıfır NOOP kodu düştü. (Hiç bir NOOP'un Bilgi İşlem Bilimi teorisinin önemli bir parçası olduğunu savunan bir makale görmedim (aynı Q ile aynıdır: matematikçiler grup teorisinin sıfırından farklı olarak nul op'a sahip mi?)


Tüm CPU'larda NOP kodlanmış değildir (8085, 8080 ve en çok tanıdığım CPU Z80 olsa da). Ancak, koyduğum yerdeki bir işlemci tasarlasaydım! Kullanışlı başka bir şey, belleğin genellikle tüm 0x00'e başlatılmasıdır, bu nedenle CPU sıfırlanmamış belleğe ulaşıncaya kadar kodun çalıştırılması hiçbir şey yapmaz.
CJ Dennis

X86 CPU kullanmayın neden açıkladım @CJDennis 0x00için nopcevabım. Kısacası, komut kod çözme tasarruf sağlar - xchg ax, axdoğal olarak komut kod çözme çalışma biçiminden akar ve "noppy" bir şey yapar, bu yüzden neden bunu kullanmıyor ve çağırmıyor nop, değil mi? :) Talimat kod çözme için silikonda biraz tasarruf etmek için kullanılır ...
Luaan

5

Bazı mimarilerde, NOPkullanılmayan gecikme yuvalarını işgal etmek için kullanılır . Örneğin, şube talimatı boru hattını temizlemezse, yine de yürütüldükten sonra birkaç talimat:

 JMP     .label
 MOV     R2, 1    ; these instructions start execution before the jump
 MOV     R2, 2    ; takes place so they still get executed

Peki ya sonradan sığacak kullanışlı talimatlarınız yoksa JMP? Bu durumda NOPs'yi kullanmanız gerekir .

Gecikme yuvaları atlamalarla sınırlı değildir. Bazı mimarilerde, CPU boru hattındaki veri tehlikeleri otomatik olarak çözülmez. Bu, bir kaydı değiştiren her komuttan sonra, kaydın yeni değerinin henüz erişilebilir olmadığı bir yuva olduğu anlamına gelir. Bir sonraki talimatın bu değere ihtiyacı varsa, yuva aşağıdakilerle dolu olmalıdır NOP:

 ADD     R1, R1
 NOP               ; The new value of R1 is not ready yet
 ADD     R1, R3

Ayrıca, bazı koşullu yürütme talimatları ( If-True-False ve benzeri) her koşul için yuvalar kullanır ve belirli bir koşulun kendisiyle ilişkili bir eylemi yoksa, yuvası aşağıdakiler tarafından kullanılmalıdır NOP:

CMP     R0, R1       ; Compare R0 and R1, setting flags
ITF     GT           ; If-True-False on GT flag 
MOV     R3, R2       ; If 'greater than', move R2 to R3
NOP                  ; Else, do nothing

+1. Tabii ki, bunlar sadece geriye dönük uyumluluğa önem vermeyen mimarilerde ortaya çıkma eğilimindedir - x86, talimat boru hattını tanıtırken böyle bir şey denediyse, hemen hemen herkes bunu yanlış olarak adlandırır (sonuçta CPU'larını yükselttiler ve uygulamalar çalışmayı durdurdu!). Bu nedenle x86 , bu tür iyileştirmeler CPU'ya eklendiğinde uygulamanın fark etmediğinden emin olmalıdır - yine de çok çekirdekli CPU'lara ulaşıncaya kadar ...: D
Luaan

2

İki baytlık bir NOP kullanımı için başka bir örnek : http://blogs.msdn.com/b/oldnewthing/archive/2011/09/21/10214405.aspx

MOV EDI, EDI komutu, iki adımlık bir NOP'tur, bu da bir atlama talimatını yamalamak için yeterli alandır, böylece fonksiyon anında güncellenebilir. Amaç, MOV EDI, EDI komutunun, kontrolü fonksiyonun başlamasından hemen önce gelen beş bayt yama alanına yönlendirmek için iki baytlık bir JMP $ -5 komutuyla değiştirilmesidir. Tam atlama komutu için beş bayt yeterlidir; bu, adres alanında başka bir yerde kurulu değiştirme işlevine denetim gönderebilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.