마이크로 프로세서 8085에 "nop"즉, No operation 명령이 필요한 이유는 무엇입니까?


19

마이크로 프로세서 8085 명령에는 기계 제어 작동 "nop"(작동 없음)이 있습니다. 내 질문은 왜 우리에게 수술이 필요하지 않습니까? 프로그램을 끝내야하는 경우 HLT 또는 RST 3을 사용합니다. 또는 다음 명령으로 이동하려면 다음 명령을 제공합니다. 그러나 왜 조작이 없습니까? 무엇이 필요합니까?


5
NOP는 프로그램 디버깅 및 업데이트에 자주 사용됩니다. 나중에 프로그램에 몇 줄을 추가하려면 NOP를 덮어 쓰면됩니다. 그렇지 않으면 라인을 삽입해야하며 삽입은 전체 프로그램을 이동시키는 것을 의미합니다. 또한 잘못된 인수 (잘못된)는 동일한 인수에 따라 NOP로 대체 (간단히 덮어 쓰기) 될 수 있습니다.
플루토늄 밀수업자

알았어 그러나 nop을 사용하면 공간이 늘어나고 있습니다. 우리의 주요 목표는 거의 자리를 차지하지 않는 것입니다.
Demietra95

* 우리의 목표는 문제를 작게 만드는 것입니다. 문제가되지 않습니까?
Demietra95

2
그것이 현명하게 사용해야하는 이유입니다. 그렇지 않으면 전체 프로그램은 단지 NOP입니다.
플루토늄 밀수업자

답변:


34

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는 (내가 그 데이터를 얻을 것이라고 인정 완전히 신뢰할 수없는 데이터를 얻기 위해 위험을 충분히 기다리지 않을 경우 빠른 : 오 불구하고).π


3
많은 것들 (특히 uC 외부의 IC를 구동하는 출력)은 'D는 안정적이며 클럭 에지는 최소 100us이다'또는 'IR LED는 1MHz로 깜박 여야한다'와 같은 타이밍 제약 조건이 적용된다. 따라서 (정확한) 지연이 종종 요구됩니다.
Wouter van Ooijen

6
NOP는 직렬 프로토콜을 비트 뱅킹 할 때 타이밍을 정확하게하는 데 유용 할 수 있습니다. 또한 사용하지 않는 코드 공간을 채우고 프로그램 카운터가 손상된 경우 (예 : PSU 글리치, 희귀 한 감마선 이벤트의 영향 등) 드문 상황에서 콜드 스타트 ​​벡터로 점프하여 코드 실행을 시작하는 데 유용 할 수 있습니다. 그렇지 않으면 코드 공간의 일부가 비어 있습니다.
Techydude

6
Atari 2600 비디오 컴퓨터 시스템 (카트리지에 저장된 프로그램을 실행하는 두 번째 비디오 게임 콘솔)에서, 프로세서는 각 스캔 라인마다 정확히 76주기를 실행하며 많은 작업이 시작된 후 정확한주기 수만큼 수행되어야합니다. 스캔 라인. 해당 프로세서에서 문서화 된 "NOP"명령은 두 사이클을 필요로하지만 코드가 달리 쓸모없는 3 사이클 명령을 사용하여 지연을 정확한 수의 사이클로 채우는 것도 일반적입니다. 코드를 더 빠르게 실행하면 완전히 왜곡 된 표시가 나타납니다.
supercat

1
지연에 NOP를 사용하면 I / O 장치가 연속 작업간에 최소 시간을 부과하지만 최대 시간이 아닌 경우 비 실시간 시스템에서도 의미가 있습니다. 예를 들어, 많은 컨트롤러에서 SPI 포트의 바이트를 이동하면 CPU 사이클이 8 회 걸립니다. 메모리에서 바이트를 가져 와서 SPI 포트로 출력하는 것 외에는 아무것도 수행하지 않는 코드가 약간 빠르게 실행될 수 있지만 SPI 포트가 각 바이트에 대해 준비되었는지 테스트하는 로직을 추가하면 불필요하게 느려질 수 있습니다. NOP 또는 2 개를 추가하면 코드가 최대 가용 속도를 달성 할 수 있습니다.
supercat

1
... 인터럽트가없는 경우. 인터럽트가 발생하면 NOP는 시간을 불필요하게 낭비하지만 1 개 또는 2 개의 NOP가 낭비한 시간은 인터럽트로 인해 불필요한 시간을 결정하는 데 필요한 시간보다 짧습니다.
supercat

8

다른 답변은 실제로 어느 시점에서 실행되는 NOP 만 고려하고 있습니다. 이는 일반적으로 사용되지만 NOP의 유일한 사용은 아닙니다.

패치 할 수있는 코드를 작성할 때 비 실행 NOP는 매우 유용합니다 - 기본적으로, 당신거야 패드 몇 NOP를 가진 기능 RET (또는 유사한 명령). 실행 파일을 패치해야 할 때, 원래부터 시작 RET하여 필요한만큼 많은 NOP를 사용하고 (예 : 긴 점프 또는 인라인 코드와 같은) 다른 코드로 마무리 하는 기능에 코드를 쉽게 추가 할 수 있습니다 RET.

이 사용 사례에서 noöne NOP은 실행을 기대합니다 . 유일한 요점은 실행 파일 패치를 허용하는 것입니다. 이론적 인 패딩되지 않은 실행 파일에서는 실제로 함수 코드를 변경해야합니다 (때로는 원래 경계에 맞을 수도 있지만 어쨌든 점프가 필요합니다) )-특히 수동으로 작성된 어셈블리 또는 최적화 컴파일러를 고려하면 훨씬 더 복잡합니다. 중요한 코드에서 지적했을 수있는 점프와 유사한 구성을 존중해야합니다. 대체로 까다 롭습니다.

물론 이것은 작고 온라인 과 같은 패치를 만드는 것이 유용했던 옛날에 훨씬 더 많이 사용되었습니다 . 오늘은 재 컴파일 된 바이너리를 배포하고 완료합니다. 패치 NOP를 사용하는 사람도 있습니다 (실행 여부에 관계없이 항상 리터럴은 아님) NOP– 예를 들어 Windows가 MOV EDI, EDI온라인 패치에 사용함 -시스템을 실제로 다시 시작하지 않고도 시스템 라이브러리를 업데이트 할 수있는 종류입니다).

마지막 질문은 왜 실제로 아무것도하지 않는 것에 대한 헌신적 인 지시를 받는가입니다.

  • 실제 명령입니다-어셈블리 디버깅 또는 핸드 코딩시 중요합니다. 같은 지침 MOV AX, AX은 정확히 동일하지만 의도를 명확하게 나타내는 것은 아닙니다.
  • 패딩-정렬에 의존하는 코드의 전반적인 성능을 향상시키기 위해 존재하는 "코드"입니다. 결코 실행을 의미하지 않습니다. 일부 디버거는 단순히 분해시 패딩 NOP를 숨 깁니다.
  • 컴파일러 최적화를위한 더 많은 공간을 제공합니다. 여전히 사용되는 패턴은 두 단계의 컴파일이 있다는 것입니다. 첫 번째 단계는 간단하고 불필요한 어셈블리 코드를 많이 생성하는 반면 두 번째 단계는 정리하고 주소 참조를 다시 배선하고 제거합니다 외부 지침. 이것은 종종 JIT 컴파일 언어에서도 볼 수 있습니다. .NET의 IL과 JVM의 바이트 코드는 NOP상당히 많이 사용됩니다. 실제 컴파일 된 어셈블리 코드에는 더 이상 해당 코드가 없습니다. 그러나 x86은 아닙니다 NOP.
  • 온라인 디버깅은 읽기 (0이 NOP아닌 메모리가 모두 사용 되어서 읽기가 훨씬 쉬워 짐) 및 핫 패치 (Visual Studio : P에서 편집 및 계속을 선호하지만)를 모두 쉽게 수행 할 수 있습니다.

NOP를 실행하기 위해서는 물론 몇 가지 사항이 더 있습니다.

  • 물론 성능-이것이 8085에 있었던 이유는 아니지만 80486조차 이미 파이프 라인 된 명령 실행을 가지고있어 "아무것도하지 않는"것을 조금 까다롭게 만듭니다.
  • 에서 볼 수 있듯이 MOV EDI, EDI리터럴 이외의 다른 효과적인 NOP가 NOP있습니다. MOV EDI, EDIx86에서 2 바이트 NOP로 최고의 성능을 발휘합니다. 두 NOP개의을 사용한 경우 두 개의 명령이 실행됩니다.

편집하다:

실제로 @DmitryGrigoryev와의 토론을 통해 이것에 대해 조금 더 생각하게되었고, 이것이이 질문 / 답변에 귀중한 추가라고 생각합니다.

먼저, 분명히 지적하십시오. 왜 같은 명령을 내릴 수 mov ax, ax있습니까? 예를 들어 8086 머신 코드 (386 머신 코드보다 오래된)의 경우를 살펴 보겠습니다.

  • opcode와 함께 전용 NOP 명령이 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를을 , 메모 순서입니다 - 후입니다 ,axdixchg ax, axax, cx, dx, bx, sp, bp, si, dibxdxax; 레지스터 이름은 순 서식 이름이 아닌 니모닉입니다 (누산기, 카운터, 데이터, 기본, 스택 포인터, 기본 포인터, 소스 인덱스, 대상 인덱스). 다른 피연산자 (예 : mov someRegister, immediate세트) 에도 동일한 접근 방식이 사용되었습니다 . 어떤 방식으로, opcode가 실제로 전체 바이트가 아닌 것처럼 생각할 수 있습니다. 마지막 몇 비트는 "실제"피연산자의 "인수"입니다.

이 모든 것은 x86에서 nop실제 명령으로 간주되거나 그렇지 않을 수 있습니다. 원래의 마이크로 아키텍처는 그것을 xchg올바르게 기억한다면 변형으로 취급 했지만 실제로 nop는 사양에서 명명 되었습니다. 그리고 xchg ax, ax명령어로서 실제로 의미가 없기 때문에 , 8086의 설계자들이 0x90자연스럽게 "아주"무언가에 자연스럽게 매핑 한다는 사실을 이용하여 명령어 디코딩에서 트랜지스터와 경로를 어떻게 절약했는지 알 수 있습니다 .

한편, i8051은 완전히 디자인 된 연산 코드에 대한이 nop- 0x00. 실용적입니다. 명령 디자인은 기본적 작동 높은 니블 및 피연산자를 선택하기위한 로우 니블 사용 - 예를 들어, add a0x2Y, 및 0xX8수단은 "0 직접 등록"이므로 0x28이다 add a, r0. 실리콘을 많이 절약 :)

CPU 디자인 (컴파일러 디자인 및 언어 디자인은 말할 것도없고)은 매우 광범위한 주제이므로 여전히 계속 진행할 수 있지만, 디자인에 대한 다양한 관점을 그대로 보여준 것 같습니다.


사실, NOP일반적으로 별칭 MOV ax, ax, ADD ax, 0또는 이와 유사한 지침을 제공합니다. 왜 충분한 것이 없을 때 아무것도하지 않는 전용 명령을 설계하겠습니까?
Dmitry Grigoryev 2016 년

@DmitryGrigoryev 그것은 실제로 CPU 언어 (잘, 마이크로 아키텍처) 자체의 디자인에 들어갑니다. 대부분의 CPU (및 컴파일러)는 MOV ax, ax거리 를 최적화하는 경향이 있습니다. NOP항상 고정주기의 실행주기를 갖습니다. 그러나 나는 그것이 어쨌든 내 대답에 쓴 것과 어떻게 관련이 있는지 알지 못합니다.
Luaan

CPU는 명령이 이미 파이프 라인에 있음 MOV ax, ax을 알기 때문에 CPU를 실제로 최적화 할 수 없습니다 MOV.
Dmitry Grigoryev 2016 년

@DmitryGrigoryev 그것은 당신이 말하는 CPU의 종류에 달려 있습니다. 최신 데스크톱 CPU는 명령 파이프 라이닝뿐만 아니라 많은 작업을 수행합니다. 예를 들어, CPU는 캐시 라인을 무효화 할 필요가 없다는 것을 알고 실제로 실제로 어떤 작업도 수행 할 필요가 없다는 것을 알고 있습니다 (하이퍼 스레딩 및 일반적으로 관련된 여러 파이프에 매우 중요). (즉, 아마에 대해 동일 것이지만 그것은 또한 분기 예측에 영향을 경우 나는 놀라지 않을 것이다 NOPMOV ax, ax). 최신 CPU는 oldschool C 컴파일러보다 훨씬 복잡합니다. :))
Luaan

1
Noone에서 noone으로 최적화하기위한 +1! 우리 모두 협력하고이 철자 스타일로 돌아 가야한다고 생각합니다! Z80 (및 8080)에는 LD r, r명령어 r와 유사한 단일 레지스터가있는 7 개의 명령어 가 있습니다 MOV ax, ax. 그것이 8이 아닌 7 인 이유는 명령 중 하나가 과부하되어 있기 때문 HALT입니다. 따라서 8080 및 Z80에는 적어도 7 개의 다른 명령이 있습니다 NOP. 흥미롭게도, 이러한 명령어는 비트 패턴과 논리적으로 관련이 없지만 실행하는 데 4 개의 T 상태가 필요하므로 LD명령어 를 사용할 이유가 없습니다 !
CJ 데니스

5

70 년대 후반에, 우리 (저는 어린 연구생이었습니다)는 1024 바이트의 코드 (즉, 단일 UVEPROM)에서 실행되는 작은 dev 시스템 (메모리가 제공되는 경우 8080)을 가졌습니다.로드 할 명령은 4 개뿐이었습니다 (L ), 저장 (S), 인쇄 (P) 및 기타 기억할 수없는 사항이 있습니다. 실제 텔레타이프 및 펀치 테이프로 구동되었습니다. 그것은 단단히 코딩되었습니다!

NOOP 사용의 한 예는 8 바이트 간격으로 이격 된 인터럽트 서비스 루틴 (ISR)에 있습니다. 이 루틴은 9 바이트 길이로 끝났으며, 주소 공간보다 약간 더 긴 주소로 (긴) 점프로 끝났습니다. 이것은 작은 엔디안 바이트 순서를 감안할 때, 높은 주소 바이트가 00h이고 다음 ISR의 첫 번째 바이트에 슬롯으로 들어가서 (다음 ISR)이 NOOP로 시작했기 때문에 '우리'가 적합하다는 것을 의미했습니다. 제한된 공간의 코드!

따라서 NOOP가 유용합니다. 또한 인텔이 그렇게 코드를 작성하는 것이 가장 쉽다고 생각합니다. 구현하려는 명령 목록이 있었을 것입니다. 모든 목록과 마찬가지로 '1'부터 시작했습니다. NOOP 코드가 빠졌습니다. (NOOP가 컴퓨팅 과학 이론의 필수 부분이라고 주장하는 기사를 본 적이 없습니다. (수학자들이 그룹 이론의 제로와 구별되는 바와 같이 수학자가 있습니까?)


모든 CPU가 0x00으로 인코딩 된 NOP를 갖는 것은 아닙니다 (8085, 8080 및 내가 가장 친숙한 CPU 인 Z80 모두가 그러합니다). 그러나 프로세서를 설계하는 경우 프로세서를 넣을 위치입니다! 편리한 다른 점은 메모리가 일반적으로 모든 0x00으로 초기화되므로 CPU가 0이 아닌 메모리에 도달 할 때까지 코드로 실행하면 아무것도 수행하지 않는다는 것입니다.
CJ 데니스

는 x86 CPU를 사용하지 않는 이유를 설명했습니다 @CJDennis 0x00에 대한 nop내 대답에. 간단히 말해 명령 디코딩이 절약됩니다 xchg ax, ax. 명령 디코딩이 작동하는 방식에서 자연스럽게 흐르고 "노피"기능을 수행하므로이를 사용하지 않고 호출하십시오 nop. :) 명령어 디코딩을 위해 실리콘에 약간의 비트를 저장하는 데 사용됩니다 ...
Luaan

5

일부 아키텍처에서는 NOP사용되지 않은 지연 슬롯 을 차지하는 데 사용됩니다 . 예를 들어 분기 명령어가 파이프 라인을 지우지 않으면 어쨌든 여러 명령어가 실행됩니다.

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

그러나 유용한 지침이 없다면 JMP? 이 경우 NOPs 를 사용해야 합니다.

지연 슬롯은 점프로 제한되지 않습니다. 일부 아키텍처에서는 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

+1. 물론, 이전 버전과의 호환성에 신경 쓰지 않는 아키텍처에만 표시되는 경향이 있습니다 .x86이 명령 파이프 라이닝을 도입 할 때 이와 같은 것을 시도하면 거의 모든 사람들이 단순히 잘못 호출합니다 (결국 CPU를 업그레이드하고 응용 프로그램 작동이 중지되었습니다!). 86 그래서 있도록해야합니다 응용 프로그램 수행이 같은 개선은 CPU에 추가하지 통지 - 우리가 어쨌든 멀티 코어 CPU를 가지고 때까지 ... : D
Luaan

2

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 바이트는 전체 점프 명령에 충분하며 주소 공간의 다른 곳에 설치된 대체 기능으로 제어를 보낼 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.