W instrukcji mikroprocesora 8085 występuje operacja sterowania maszyną „nop” (brak operacji). Moje pytanie brzmi: dlaczego potrzebujemy operacji bez? Mam na myśli, że jeśli musimy zakończyć program, użyjemy HLT lub RST 3. Lub jeśli chcemy przejść do następnej instrukcji, podamy kolejne instrukcje. Ale dlaczego nie ma operacji? Jaka jest potrzeba?
microprocessor
Demietra95
źródło
źródło
Odpowiedzi:
Jednym z zastosowań instrukcji NOP (lub NOOP, brak operacji) w procesorach i MCU jest wstawienie niewielkiego, przewidywalnego opóźnienia w kodzie. Chociaż NOP nie wykonują żadnej operacji, ich przetworzenie zajmuje trochę czasu (procesor musi pobrać i zdekodować kod operacyjny, więc zajmuje to trochę czasu). Tylko 1 cykl CPU jest „marnowany” na wykonanie instrukcji NOP (zwykle dokładną liczbę można wywnioskować z arkusza danych CPU / MCU), dlatego ustawianie N NOP w sekwencji jest łatwym sposobem na wstawienie przewidywalnego opóźnienia:
gdzie K jest liczbą cykli (najczęściej 1) potrzebnych do przetworzenia instrukcji NOP, a jest okresem zegara.T.c l o c k
Dlaczego chcesz to zrobić? Przydatne może być wymuszenie na procesorze trochę czasu, aż zewnętrzne (być może wolniejsze) urządzenia zakończą pracę i przekażą dane do CPU, tzn. NOP jest przydatny do celów synchronizacji.
Zobacz także powiązaną stronę Wikipedii na NOP .
Innym zastosowaniem jest wyrównanie kodu pod pewnymi adresami w pamięci i innymi „sztuczkami asemblera”, jak wyjaśniono również w tym wątku na Programmers.SE oraz w tym drugim wątku na StackOverflow .
Kolejny interesujący artykuł na ten temat .
Ten link do strony książki Google dotyczy w szczególności procesora 8085. Fragment:
EDYCJA (aby rozwiązać problem wyrażony w komentarzu)
Jeśli martwisz się szybkością, pamiętaj, że wydajność (czasowa) to tylko jeden parametr do rozważenia. Wszystko zależy od zastosowania: jeśli chcesz obliczyć 10-miliardową liczbę , być może Twoim jedynym problemem może być prędkość. Z drugiej strony, jeśli chcesz dane z czujników temperatury podłączonych do MCU przez ADC zalogować, prędkość nie jest zwykle tak ważne, ale czeka odpowiednią ilość czasu, aby umożliwić ADC, aby prawidłowo zakończyć każdy odczyt jest niezbędna . W tym przypadku, jeśli MCU nie zaczeka wystarczająco długo, ryzykuje to uzyskaniem całkowicie niewiarygodnych danych (przyznaję, że szybciej uzyska te dane : o).π
źródło
Inne odpowiedzi dotyczą tylko NOP, który faktycznie wykonuje się w pewnym momencie - jest to dość powszechne, ale nie jest to jedyne użycie NOP.
Niewykonujący się NOP jest również bardzo użyteczny podczas pisania kodu, który można załatać - w zasadzie po funkcji
RET
(lub podobnej instrukcji) wypełnisz funkcję kilkoma NOP . Kiedy musisz załatać plik wykonywalny, możesz łatwo dodać więcej kodu do funkcji, zaczynając od oryginałuRET
i używając tyle NOP, ile potrzebujesz (np. Do skoków w dal lub nawet kodu wbudowanego), a kończąc na innymRET
.W takim przypadku noöne nigdy nie oczekuje
NOP
wykonania. Jedyną rzeczą jest umożliwienie łatania pliku wykonywalnego - w teoretycznym nie wypełnionym pliku wykonywalnym trzeba by było zmienić kod samej funkcji (czasem może pasować do pierwotnych granic, ale dość często i tak potrzebujesz skoku ) - jest to o wiele bardziej skomplikowane, zwłaszcza biorąc pod uwagę ręcznie napisany zestaw lub optymalizujący kompilator; musisz szanować skoki i podobne konstrukcje, które mogły wskazywać na jakiś ważny fragment kodu. W sumie dość trudne.Oczywiście, to było dużo większym stopniu wykorzystywane w dawnych czasach, kiedy to było przydatne do łatki jak te małe i internetowych . Dzisiaj będziesz po prostu dystrybuował skompilowany plik binarny i skończysz z nim. Nadal są tacy, którzy używają łatania NOP (wykonując lub nie, i nie zawsze dosłownie
NOP
s - na przykład Windows używaMOV EDI, EDI
do łatania online - w ten sposób możesz aktualizować bibliotekę systemową, podczas gdy system faktycznie działa, bez potrzeby restartowania).Ostatnie pytanie brzmi: po co mieć dedykowaną instrukcję dla czegoś, co tak naprawdę nic nie robi?
MOV AX, AX
zrobią dokładnie to samo, ale nie sygnalizują tak wyraźnie intencji.NOP
dość dużo; rzeczywisty skompilowany kod zestawu już go nie ma. Należy jednak zauważyć, że nie są to x86NOP
.NOP
, co znacznie ułatwia demontaż), jak i do łatania na gorąco (choć zdecydowanie wolę Edytuj i kontynuuj w Visual Studio: P).Do wykonania NOP jest oczywiście jeszcze kilka punktów:
MOV EDI, EDI
, istnieją inne skuteczne NOP niż dosłowneNOP
.MOV EDI, EDI
ma najlepszą wydajność jako 2-bajtowy NOP na x86. Jeśli użyłeś dwóchNOP
s, byłyby to dwie instrukcje do wykonania.EDYTOWAĆ:
W rzeczywistości dyskusja z @DmitryGrigoryev zmusiła mnie do zastanowienia się nad tym trochę i uważam, że jest to cenny dodatek do tego pytania / odpowiedzi, więc dodaję kilka dodatkowych bitów:
Po pierwsze, rzecz jasna - dlaczego miałaby istnieć instrukcja, która coś takiego robi
mov ax, ax
? Na przykład spójrzmy na przypadek kodu maszynowego 8086 (starszy niż nawet kod maszynowy 386):0x90
. To wciąż czas, kiedy wiele osób pisało na zgromadzeniu, pamiętajcie. Nawet gdyby nie było dedykowanejNOP
instrukcji,NOP
słowo kluczowe (alias / mnemonic) byłoby nadal przydatne i byłoby do niego przypisane.MOV
mapują wiele różnych kodów operacyjnych, ponieważ oszczędza to czas i przestrzeń - na przykładmov al, 42
jest „przenieś natychmiastowy bajt doal
rejestru”, co przekłada się na0xB02A
(0xB0
bycie opcode,0x2A
bycie „bezpośrednim” argumentem). To zajmuje dwa bajty.mov al, al
(ponieważ jest to głupota, w zasadzie), więc będziesz musiał użyćmov al, rmb
przeciążenia (rmb to „rejestr lub pamięć”). To faktycznie zajmuje trzy bajty. (chociaż prawdopodobnie użyłby mniej specyficznegomov rb, rmb
, który powinien zająć tylko dwa bajtymov al, al
- bajt argumentu służy do określenia zarówno rejestru źródłowego, jak i docelowego; teraz wiesz, dlaczego 8086 miał tylko 8 rejestrów: D). Porównaj zNOP
, która jest instrukcją jednobajtową! Oszczędza to pamięć i czas, ponieważ czytanie pamięci w 8086 było wciąż dość drogie - nie wspominając o ładowaniu tego programu z taśmy, dyskietki czy czegoś takiego.Więc skąd
xchg ax, ax
pochodzi? Musisz tylko spojrzeć na kody innychxhcg
instrukcji. Zobaczysz0x86
,0x87
i wreszcie,0x91
-0x97
. Wydaje się więc,nop
że jest0x90
to całkiem dobre dopasowaniexchg ax, ax
(co znowu nie jestxchg
„przeciążeniem” - trzeba by użyćxchg rb, rmb
dwóch bajtów). I faktycznie jestem całkiem pewien, że był to miły efekt uboczny0x90-0x97
mikrotechniki tamtych czasów - jeśli dobrze pamiętam, łatwo było zmapować cały zakres na „xchg, działający na rejestryax
iax
-di
” ( operand jest symetryczny, to dał ci pełny zakres, w tym NOPxchg ax, ax
; Zauważ, że kolejność jestax, cx, dx, bx, sp, bp, si, di
-bx
to podx
,ax
; pamiętaj, że nazwy rejestrów są mnemoniczne, a nie uporządkowane - akumulator, licznik, dane, baza, wskaźnik stosu, wskaźnik bazy, indeks źródłowy, indeks docelowy). To samo podejście zastosowano również w przypadku innych operandów, na przykładmov someRegister, immediate
zestawu. W pewnym sensie można by pomyśleć o tym, jakby kod operacyjny nie był w rzeczywistości pełnym bajtem - kilka ostatnich bitów stanowi „argument” dla „prawdziwego” argumentu.Wszystko to powiedziane na x86
nop
może być uważane za prawdziwą instrukcję, czy nie. Oryginalna mikroarchitektura traktowała to jako wariant,xchg
jeśli dobrze pamiętam, ale tak naprawdę została nazwananop
w specyfikacji. A ponieważxchg ax, ax
tak naprawdę nie ma to sensu jako instrukcja, możesz zobaczyć, jak projektanci 8086 zapisali na tranzystorach i ścieżkach podczas dekodowania instrukcji, wykorzystując fakt, że0x90
mapuje naturalnie na coś, co jest całkowicie „noppy”.Z drugiej strony i8051 ma całkowicie zaprojektowany kod operacji dla
nop
-0x00
. Trochę praktyczne. Projekt instrukcji w zasadzie używa wysokiej wartości dla operacji i niskiej wartości dla wyboru argumentów - na przykładadd a
jest0x2Y
i0xX8
oznacza „rejestr 0 bezpośredni”, tak0x28
jestadd a, r0
. Dużo oszczędza na krzemie :)Nadal mogę kontynuować, ponieważ projektowanie procesorów (nie wspominając o projektowaniu kompilatora i projektowaniu języka) jest dość szerokim tematem, ale myślę, że pokazałem wiele różnych punktów widzenia, które weszły w projekt całkiem niezły.
źródło
NOP
to zwykle aliasemMOV ax, ax
,ADD ax, 0
lub podobne instrukcje. Dlaczego miałbyś zaprojektować dedykowaną instrukcję, która nic nie robi, gdy jest jej dużo.MOV ax, ax
;NOP
zawsze będzie mieć określoną liczbę cykli wykonania. Ale i tak nie rozumiem, jak to ma związek z tym, co napisałem w mojej odpowiedzi.MOV ax, ax
odejścia, ponieważ zanim się dowiedzą, żeMOV
instrukcja jest już w przygotowaniu.NOP
iMOV ax, ax
). Nowoczesne procesory są znacznie bardziej złożone niżLD r, r
instrukcji, gdzier
jest dowolny pojedynczy rejestr, podobny do twojejMOV ax, ax
instrukcji. Powodem jest 7, a nie 8, ponieważ jedna z instrukcji jest przeciążonaHALT
. Więc 8080 i Z80 mają co najmniej 7 innych instrukcji, które robią to samo coNOP
! Co ciekawe, mimo że instrukcje te nie są logicznie powiązane wzorcem bitowym, wszystkie wykonują 4 stany T, więc nie ma powodu, aby korzystać zLD
instrukcji!W późnych latach 70. mieliśmy (wtedy byłem młodym studentem badań) mały system deweloperów (8080, jeśli pamięć służy), który działał w 1024 bajtach kodu (tj. W jednym UVEPROM) - miał tylko cztery polecenia do załadowania (L ), zapisz (S), wydrukuj (P) i coś innego, czego nie pamiętam. Był napędzany prawdziwym teletypem i taśmą dziurkowaną. To było ściśle zakodowane!
Jednym z przykładów użycia NOOP była procedura obsługi przerwań (ISR), która była rozmieszczona w odstępach 8-bajtowych. Ta procedura zakończyła się na 9 bajtach kończących się (długim) skokiem na adres nieco dalej w przestrzeni adresowej. Oznaczało to, biorąc pod uwagę małą kolejność bajtów endian, że wysoki bajt adresu wynosił 00h i był wstawiany do pierwszego bajtu następnego ISR, co oznaczało, że (następny ISR) zaczynał się od NOOP, tak aby „pasowaliśmy” kod w ograniczonej przestrzeni!
Więc NOOP jest przydatny. Poza tym podejrzewam, że najłatwiej było kodować go w ten sposób - prawdopodobnie mieli listę instrukcji, które chcieli zaimplementować i zaczęło się od „1”, jak to robią wszystkie listy (to były dni FORTRAN), więc zero Kod NOOP stał się wypadnięciem. (Nigdy nie widziałem artykułu, w którym argumentowałby, że NOOP jest istotną częścią teorii informatyki (to samo pytanie: czy matematycy mają zerową wartość, w odróżnieniu od zera teorii grup?)
źródło
0x00
nanop
moim odpowiedź. Krótko mówiąc, oszczędza na dekodowaniu instrukcji -xchg ax, ax
wypływa naturalnie ze sposobu, w jaki działa dekodowanie instrukcji, i robi coś „noppy”, więc dlaczego by tego nie użyć i nazwaćnop
, prawda? :) Używane, aby zaoszczędzić sporo na krzemie do dekodowania instrukcji ...W niektórych architekturach
NOP
służy do zajmowania nieużywanych miejsc na opóźnienia . Na przykład, jeśli instrukcja rozgałęzienia nie wyczyści potoku, to mimo to kilka instrukcji po jego wykonaniu:Ale co, jeśli nie masz żadnych przydatnych instrukcji, które pasowałyby po
JMP
? W takim przypadku będziesz musiał użyćNOP
s.Automaty do opóźnień nie są ograniczone do skoków. W niektórych architekturach zagrożenia danych w potoku procesora nie są rozwiązywane automatycznie. Oznacza to, że po każdej instrukcji modyfikującej rejestr jest miejsce, w którym nowa wartość rejestru nie jest jeszcze dostępna. Jeśli następna instrukcja wymaga tej wartości, miejsce powinno być zajęte przez
NOP
:Ponadto niektóre instrukcje wykonania warunkowego ( jeśli-prawda-fałsz i podobne) używają gniazd dla każdego warunku, a gdy dany warunek nie jest powiązany z żadnymi działaniami, jego miejsce powinno być zajęte przez
NOP
:źródło
Kolejny przykład zastosowania dwubajtowego NOP: http://blogs.msdn.com/b/oldnewthing/archive/2011/09/21/10214405.aspx
źródło