마이크로 프로세서 8085 명령에는 기계 제어 작동 "nop"(작동 없음)이 있습니다. 내 질문은 왜 우리에게 수술이 필요하지 않습니까? 프로그램을 끝내야하는 경우 HLT 또는 RST 3을 사용합니다. 또는 다음 명령으로 이동하려면 다음 명령을 제공합니다. 그러나 왜 조작이 없습니까? 무엇이 필요합니까?
마이크로 프로세서 8085 명령에는 기계 제어 작동 "nop"(작동 없음)이 있습니다. 내 질문은 왜 우리에게 수술이 필요하지 않습니까? 프로그램을 끝내야하는 경우 HLT 또는 RST 3을 사용합니다. 또는 다음 명령으로 이동하려면 다음 명령을 제공합니다. 그러나 왜 조작이 없습니까? 무엇이 필요합니까?
답변:
CPU 및 MCU에서 NOP (또는 NOOP, 비 작동) 명령어를 사용하는 것은 코드에 약간 예측 가능한 지연을 삽입하는 것입니다. NOP는 어떤 작업도 수행하지 않지만 처리하는 데 약간의 시간이 걸립니다 (CPU는 opcode를 가져 와서 디코딩해야하므로 약간의 시간이 걸립니다). NOP 명령을 실행하는 데 1 CPU 사이클 만 낭비되는 경우 (일반적으로 CPU / MCU 데이터 시트에서 정확한 수를 추론 할 수 있음) 따라서 N NOP를 순서대로 배치하면 예측 가능한 지연을 쉽게 삽입 할 수 있습니다.
여기서 K는 NOP 명령어 처리에 필요한주기 수 (대부분 1 개)이고 은 클럭주기입니다.
왜 그렇게 하시겠습니까? 외부 (더 느린) 장치가 작업을 완료하고 CPU에 데이터를보고 할 때까지 CPU가 약간 대기하도록하는 것이 유용 할 수 있습니다. 즉, NOP는 동기화 목적에 유용합니다.
NOP 관련 Wikipedia 페이지 도 참조하십시오 .
다른 용도로도 설명, 메모리 및 기타 "조립 트릭"특정 주소에서 정렬 코드입니다 Programmers.SE에이 스레드 와에 대한 stackoverflow이 다른 스레드 .
Google 도서 페이지에 대한 이 링크 는 특히 8085 CPU를 나타냅니다. 발췌 :
각 NOP 명령어는 페치, 디코딩 및 실행에 4 개의 클럭을 사용합니다.
편집 (의견으로 표현 된 우려를 해결하기 위해)
속도가 걱정된다면 (시간) 효율은 고려해야 할 한 가지 매개 변수 일뿐입니다. 그것은 모두 응용 프로그램에 달려 있습니다 : 의 10 억 번째 그림을 계산하려면 속도가 유일한 문제 일 수 있습니다. 당신이 ADC를 통해 MCU에 연결된 온도 센서에서 데이터를 기록 할 경우 반면에, 속도는 일반적으로 그렇게 중요하지 않지만 시간의 적당한 양의 대기 올바르게에 ADC는 각 읽기가 완료 될 수 있도록 필수적이다 . 이 경우 MCU는 (내가 그 데이터를 얻을 것이라고 인정 완전히 신뢰할 수없는 데이터를 얻기 위해 위험을 충분히 기다리지 않을 경우 빠른 : 오 불구하고).
다른 답변은 실제로 어느 시점에서 실행되는 NOP 만 고려하고 있습니다. 이는 일반적으로 사용되지만 NOP의 유일한 사용은 아닙니다.
패치 할 수있는 코드를 작성할 때 비 실행 NOP는 매우 유용합니다 - 기본적으로, 당신거야 패드 몇 NOP를 가진 기능 후RET
(또는 유사한 명령). 실행 파일을 패치해야 할 때, 원래부터 시작 RET
하여 필요한만큼 많은 NOP를 사용하고 (예 : 긴 점프 또는 인라인 코드와 같은) 다른 코드로 마무리 하는 기능에 코드를 쉽게 추가 할 수 있습니다 RET
.
이 사용 사례에서 noöne NOP
은 실행을 기대합니다 . 유일한 요점은 실행 파일 패치를 허용하는 것입니다. 이론적 인 패딩되지 않은 실행 파일에서는 실제로 함수 코드를 변경해야합니다 (때로는 원래 경계에 맞을 수도 있지만 어쨌든 점프가 필요합니다) )-특히 수동으로 작성된 어셈블리 또는 최적화 컴파일러를 고려하면 훨씬 더 복잡합니다. 중요한 코드에서 지적했을 수있는 점프와 유사한 구성을 존중해야합니다. 대체로 까다 롭습니다.
물론 이것은 작고 온라인 과 같은 패치를 만드는 것이 유용했던 옛날에 훨씬 더 많이 사용되었습니다 . 오늘은 재 컴파일 된 바이너리를 배포하고 완료합니다. 패치 NOP를 사용하는 사람도 있습니다 (실행 여부에 관계없이 항상 리터럴은 아님) NOP
– 예를 들어 Windows가 MOV EDI, EDI
온라인 패치에 사용함 -시스템을 실제로 다시 시작하지 않고도 시스템 라이브러리를 업데이트 할 수있는 종류입니다).
마지막 질문은 왜 실제로 아무것도하지 않는 것에 대한 헌신적 인 지시를 받는가입니다.
MOV AX, AX
은 정확히 동일하지만 의도를 명확하게 나타내는 것은 아닙니다.NOP
상당히 많이 사용됩니다. 실제 컴파일 된 어셈블리 코드에는 더 이상 해당 코드가 없습니다. 그러나 x86은 아닙니다 NOP
.NOP
아닌 메모리가 모두 사용 되어서 읽기가 훨씬 쉬워 짐) 및 핫 패치 (Visual Studio : P에서 편집 및 계속을 선호하지만)를 모두 쉽게 수행 할 수 있습니다.NOP를 실행하기 위해서는 물론 몇 가지 사항이 더 있습니다.
MOV EDI, EDI
리터럴 이외의 다른 효과적인 NOP가 NOP
있습니다. MOV EDI, EDI
x86에서 2 바이트 NOP로 최고의 성능을 발휘합니다. 두 NOP
개의을 사용한 경우 두 개의 명령이 실행됩니다.편집하다:
실제로 @DmitryGrigoryev와의 토론을 통해 이것에 대해 조금 더 생각하게되었고, 이것이이 질문 / 답변에 귀중한 추가라고 생각합니다.
먼저, 분명히 지적하십시오. 왜 같은 명령을 내릴 수 mov ax, ax
있습니까? 예를 들어 8086 머신 코드 (386 머신 코드보다 오래된)의 경우를 살펴 보겠습니다.
0x90
있습니다. 지금도 많은 사람들이 집회에서 글을 썼을 때입니다. 따라서 전용 NOP
명령 이 없더라도 NOP
키워드 (별칭 / 니모닉)는 여전히 유용하며 그에 매핑됩니다.MOV
그 시간과 공간에 저장하기 때문에, 실제로 많은 다른 옵 코드에 매핑 - 예를 들어, mov al, 42
"받는 즉시 바이트를 이동하다 al
로 변환, 레지스터" 0xB02A
( 0xB0
, 옵 코드 인 0x2A
은 "즉시"인수 인). 따라서 2 바이트가 걸립니다.mov al, al
(기본적으로 멍청한 일이므로) 바로 가기 opcode가 없으므로 mov al, rmb
(rmb는 "register or memory") 오버로드 를 사용해야합니다 . 실제로 3 바이트 가 걸립니다 . (아마도 덜 구체적인 mov rb, rmb
것을 사용하지만 두 바이트 만 가져야합니다 mov al, al
-인수 바이트는 소스와 대상 레지스터를 모두 지정하는 데 사용됩니다. 이제 8086에 8 개의 레지스터 만있는 이유는 무엇입니까? : D). NOP
단일 바이트 명령어 인와 비교하십시오 ! 8086에서 메모리를 읽는 것이 여전히 비용이 많이 들기 때문에 메모리와 시간을 절약 할 수 있습니다. 물론 테이프 나 플로피 등에서 프로그램을로드하는 것은 말할 것도 없습니다.그럼 어디에서 xchg ax, ax
왔을 까? 다른 xhcg
명령어 의 opcode 만 살펴보십시오 . 당신은 볼 0x86
, 0x87
마지막으로하고 0x91
- 0x97
. 그래서 nop
그것은 0x90
꽤 잘 맞는 것 같습니다 xchg ax, ax
(다시, xchg
"오버로드" 가 아닙니다 -2 xchg rb, rmb
바이트에서을 사용해야 합니다). 사실, 이것은 이것이 시간의 마이크로 아키텍처의 좋은 부작용이라고 확신합니다. 올바르게 기억한다면, 전체 범위 0x90-0x97
를 "xchg, 레지스터에 작용하는 - ax
및 "( 피연산자이 당신에게 전체 범위, 준, 대칭 인 을 포함하여 NOP를을 , 메모 순서입니다 - 후입니다 ,ax
di
xchg ax, ax
ax, cx, dx, bx, sp, bp, si, di
bx
dx
ax
; 레지스터 이름은 순 서식 이름이 아닌 니모닉입니다 (누산기, 카운터, 데이터, 기본, 스택 포인터, 기본 포인터, 소스 인덱스, 대상 인덱스). 다른 피연산자 (예 : mov someRegister, immediate
세트) 에도 동일한 접근 방식이 사용되었습니다 . 어떤 방식으로, opcode가 실제로 전체 바이트가 아닌 것처럼 생각할 수 있습니다. 마지막 몇 비트는 "실제"피연산자의 "인수"입니다.
이 모든 것은 x86에서 nop
실제 명령으로 간주되거나 그렇지 않을 수 있습니다. 원래의 마이크로 아키텍처는 그것을 xchg
올바르게 기억한다면 변형으로 취급 했지만 실제로 nop
는 사양에서 명명 되었습니다. 그리고 xchg ax, ax
명령어로서 실제로 의미가 없기 때문에 , 8086의 설계자들이 0x90
자연스럽게 "아주"무언가에 자연스럽게 매핑 한다는 사실을 이용하여 명령어 디코딩에서 트랜지스터와 경로를 어떻게 절약했는지 알 수 있습니다 .
한편, i8051은 완전히 디자인 된 연산 코드에 대한이 nop
- 0x00
. 실용적입니다. 명령 디자인은 기본적 작동 높은 니블 및 피연산자를 선택하기위한 로우 니블 사용 - 예를 들어, add a
인 0x2Y
, 및 0xX8
수단은 "0 직접 등록"이므로 0x28
이다 add a, r0
. 실리콘을 많이 절약 :)
CPU 디자인 (컴파일러 디자인 및 언어 디자인은 말할 것도없고)은 매우 광범위한 주제이므로 여전히 계속 진행할 수 있지만, 디자인에 대한 다양한 관점을 그대로 보여준 것 같습니다.
NOP
일반적으로 별칭 MOV ax, ax
, ADD ax, 0
또는 이와 유사한 지침을 제공합니다. 왜 충분한 것이 없을 때 아무것도하지 않는 전용 명령을 설계하겠습니까?
MOV ax, ax
거리 를 최적화하는 경향이 있습니다. NOP
항상 고정주기의 실행주기를 갖습니다. 그러나 나는 그것이 어쨌든 내 대답에 쓴 것과 어떻게 관련이 있는지 알지 못합니다.
MOV ax, ax
을 알기 때문에 CPU를 실제로 최적화 할 수 없습니다 MOV
.
NOP
과 MOV ax, ax
). 최신 CPU는 oldschool C 컴파일러보다 훨씬 복잡합니다. :))
LD r, r
명령어 r
와 유사한 단일 레지스터가있는 7 개의 명령어 가 있습니다 MOV ax, ax
. 그것이 8이 아닌 7 인 이유는 명령 중 하나가 과부하되어 있기 때문 HALT
입니다. 따라서 8080 및 Z80에는 적어도 7 개의 다른 명령이 있습니다 NOP
. 흥미롭게도, 이러한 명령어는 비트 패턴과 논리적으로 관련이 없지만 실행하는 데 4 개의 T 상태가 필요하므로 LD
명령어 를 사용할 이유가 없습니다 !
70 년대 후반에, 우리 (저는 어린 연구생이었습니다)는 1024 바이트의 코드 (즉, 단일 UVEPROM)에서 실행되는 작은 dev 시스템 (메모리가 제공되는 경우 8080)을 가졌습니다.로드 할 명령은 4 개뿐이었습니다 (L ), 저장 (S), 인쇄 (P) 및 기타 기억할 수없는 사항이 있습니다. 실제 텔레타이프 및 펀치 테이프로 구동되었습니다. 그것은 단단히 코딩되었습니다!
NOOP 사용의 한 예는 8 바이트 간격으로 이격 된 인터럽트 서비스 루틴 (ISR)에 있습니다. 이 루틴은 9 바이트 길이로 끝났으며, 주소 공간보다 약간 더 긴 주소로 (긴) 점프로 끝났습니다. 이것은 작은 엔디안 바이트 순서를 감안할 때, 높은 주소 바이트가 00h이고 다음 ISR의 첫 번째 바이트에 슬롯으로 들어가서 (다음 ISR)이 NOOP로 시작했기 때문에 '우리'가 적합하다는 것을 의미했습니다. 제한된 공간의 코드!
따라서 NOOP가 유용합니다. 또한 인텔이 그렇게 코드를 작성하는 것이 가장 쉽다고 생각합니다. 구현하려는 명령 목록이 있었을 것입니다. 모든 목록과 마찬가지로 '1'부터 시작했습니다. NOOP 코드가 빠졌습니다. (NOOP가 컴퓨팅 과학 이론의 필수 부분이라고 주장하는 기사를 본 적이 없습니다. (수학자들이 그룹 이론의 제로와 구별되는 바와 같이 수학자가 있습니까?)
0x00
에 대한 nop
내 대답에. 간단히 말해 명령 디코딩이 절약됩니다 xchg ax, ax
. 명령 디코딩이 작동하는 방식에서 자연스럽게 흐르고 "노피"기능을 수행하므로이를 사용하지 않고 호출하십시오 nop
. :) 명령어 디코딩을 위해 실리콘에 약간의 비트를 저장하는 데 사용됩니다 ...
일부 아키텍처에서는 NOP
사용되지 않은 지연 슬롯 을 차지하는 데 사용됩니다 . 예를 들어 분기 명령어가 파이프 라인을 지우지 않으면 어쨌든 여러 명령어가 실행됩니다.
JMP .label
MOV R2, 1 ; these instructions start execution before the jump
MOV R2, 2 ; takes place so they still get executed
그러나 유용한 지침이 없다면 JMP
? 이 경우 NOP
s 를 사용해야 합니다.
지연 슬롯은 점프로 제한되지 않습니다. 일부 아키텍처에서는 CPU 파이프 라인의 데이터 위험 이 자동으로 해결되지 않습니다. 이는 레지스터를 수정하는 각 명령어 뒤에 레지스터의 새 값에 아직 액세스 할 수없는 슬롯이 있음을 의미합니다. 다음 명령어에 해당 값이 필요한 경우 슬롯은 다음에 의해 점유되어야합니다 NOP
.
ADD R1, R1
NOP ; The new value of R1 is not ready yet
ADD R1, R3
또한 일부 조건부 실행 명령어 ( If-True-False 및 유사)는 각 조건에 대해 슬롯을 사용하며 특정 조건에 연관된 조치가없는 경우 해당 슬롯은 다음에 의해 점유되어야합니다 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
2 바이트 NOP 사용의 또 다른 예 : http://blogs.msdn.com/b/oldnewthing/archive/2011/09/21/10214405.aspx
MOV EDI, EDI 명령어는 2 바이트 NOP이며, 점프 명령어로 패치하기에 충분한 공간이므로 즉시 기능을 업데이트 할 수 있습니다. MOV EDI, EDI 명령은 2 바이트 JMP $ -5 명령으로 대체되어 기능 시작 직전에 오는 5 바이트의 패치 공간으로 제어를 재 지정합니다. 5 바이트는 전체 점프 명령에 충분하며 주소 공간의 다른 곳에 설치된 대체 기능으로 제어를 보낼 수 있습니다.