เหตุใดเราจึงต้องการ“ nop” Ie No คำสั่งการใช้งานในไมโครโปรเซสเซอร์ 8085


19

ในคำสั่งของไมโครโปรเซสเซอร์ 8085 มีการดำเนินการควบคุมเครื่อง "nop" (ไม่มีการดำเนินการ) คำถามของฉันคือทำไมเราไม่ต้องการดำเนินการ? ฉันหมายความว่าถ้าเราต้องจบโปรแกรมเราจะใช้ HLT หรือ RST 3 หรือถ้าเราต้องการย้ายไปยังคำสั่งถัดไปเราจะให้คำแนะนำต่อไป แต่ทำไมไม่มีการดำเนินการ? ความต้องการคืออะไร?


5
NOP มักใช้ในการดีบักและอัปเดตโปรแกรมของคุณ หากภายหลังคุณต้องการเพิ่มบางบรรทัดในโปรแกรมของคุณคุณสามารถเขียนทับ NOP ได้ มิฉะนั้นคุณจะต้องแทรกบรรทัดและการแทรกหมายถึงการเลื่อนโปรแกรมทั้งหมด นอกจากนี้ยังสามารถเปลี่ยนคำแนะนำที่ผิดพลาด (ไม่ถูกต้อง) ได้ (เพียงเขียนทับ) โดย NOP ตามอาร์กิวเมนต์เดียวกัน
ผู้ลักลอบขนพลูโตเนียม

ดาวน์ แต่การใช้ nop ก็เป็นการเพิ่มพื้นที่ ในขณะที่เป้าหมายหลักของเราคือการทำให้มันเกิดขึ้นเพียงเล็กน้อย
Demietra95

* ฉันหมายถึงเป้าหมายของเราคือทำให้ปัญหาเล็กลง ดังนั้นมันจะไม่กลายเป็นปัญหาหรือไม่
Demietra95

2
นั่นเป็นเหตุผลว่าทำไมจึงควรใช้อย่างชาญฉลาด มิฉะนั้นโปรแกรมทั้งหมดของคุณจะเป็นเพียงส่วนหนึ่งของ NOP
ผู้ลักลอบขนพลูโตเนียม

คำตอบ:


34

การใช้คำสั่ง NOP (หรือ NOOP, no-operation) หนึ่งครั้งในซีพียูและ MCUs คือการแทรกโค้ดของคุณเล็กน้อยที่คาดเดาได้และล่าช้า แม้ว่า NOP จะไม่ทำการดำเนินการใด ๆ แต่ต้องใช้เวลาในการประมวลผล (CPU ต้องดึงข้อมูลและถอดรหัส opcode ดังนั้นจึงต้องใช้เวลาเล็กน้อยในการดำเนินการ) วงจรซีพียูน้อยเพียง 1 ตัวคือ "สิ้นเปลือง" เพื่อดำเนินการคำสั่ง NOP (โดยปกติจำนวนที่แน่นอนสามารถอนุมานได้จากแผ่นข้อมูล CPU / MCU โดยทั่วไป) ดังนั้นการใส่ NOPs ตามลำดับจึงเป็นวิธีที่ง่ายในการแทรกการหน่วงเวลาที่คาดการณ์ได้

tdelay=NTclockK

โดยที่ K คือจำนวนรอบ (บ่อยที่สุด 1) ที่จำเป็นสำหรับการประมวลผลคำสั่ง NOP และคือช่วงเวลาของนาฬิกาTclock

ทำไมคุณจะทำเช่นนั้น? อาจเป็นประโยชน์ในการบังคับให้ CPU รอสักครู่สำหรับอุปกรณ์ภายนอก (อาจช้ากว่า) เพื่อทำงานและรายงานข้อมูลไปยัง CPU เช่น NOP มีประโยชน์สำหรับการซิงโครไนซ์

ดูเพิ่มเติมที่เกี่ยวข้องหน้าวิกิพีเดีย NOP

การใช้งานก็คือการจัดรหัสที่อยู่ในความทรงจำบางอย่างและอื่น ๆ "การชุมนุมเทคนิค" ตามที่อธิบายไว้ในหัวข้อนี้ใน Programmers.SEและหัวข้ออื่น ๆ นี้ใน StackOverflow

อีกบทความที่น่าสนใจเกี่ยวกับเรื่องนี้

ลิงก์นี้ไปยังหน้าหนังสือ Googleโดยเฉพาะอ้างอิงถึง 8085 CPU ข้อความที่ตัดตอนมา:

แต่ละคำสั่ง NOP ใช้สี่นาฬิกาสำหรับการดึงการถอดรหัสและการดำเนินการ

แก้ไข (เพื่อแก้ไขข้อกังวลที่แสดงออกในความคิดเห็น)

หากคุณกังวลเรื่องความเร็วโปรดจำไว้ว่าประสิทธิภาพ (เวลา) เป็นเพียงพารามิเตอร์เดียวที่ต้องพิจารณา ทุกอย่างขึ้นอยู่กับแอปพลิเคชัน: หากคุณต้องการคำนวณตัวเลข 10 พันล้านของบางทีความกังวลเพียงอย่างเดียวของคุณก็คือความเร็ว ในทางกลับกันถ้าคุณต้องการเข้าสู่ระบบข้อมูลจากเซ็นเซอร์อุณหภูมิเชื่อมต่อกับ MCU ผ่าน ADC ความเร็วมักจะไม่สำคัญ แต่การรอคอยในปริมาณที่เหมาะสมของเวลาที่จะช่วยให้เอดีซีไปได้อย่างถูกต้องเสร็จสมบูรณ์ในแต่ละการอ่านเป็นสิ่งจำเป็น ในกรณีนี้หาก MCU ไม่รอเพียงพอความเสี่ยงในการรับข้อมูลที่ไม่น่าเชื่อถืออย่างสมบูรณ์ (ฉันยอมรับว่ามันจะทำให้ข้อมูลนั้นเร็วขึ้นแม้ว่า: o)π


3
มีหลายสิ่งหลายอย่าง (โดยเฉพาะอย่างยิ่งเอาต์พุตขับรถไอซีภายนอก uC) อยู่ภายใต้ข้อ จำกัด เวลาเช่น 'เวลาต่ำสุดระหว่าง D เสถียรและขอบนาฬิกาคือ 100 us' หรือ 'IR LED ต้องกระพริบที่ 1 MHz' ดังนั้นความล่าช้า (ถูกต้อง) จึงมักจะต้องใช้
Wouter van Ooijen

6
NOPs มีประโยชน์สำหรับการกำหนดเวลาให้ถูกต้องเมื่อกัดโปรโตคอลอนุกรม พวกเขายังมีประโยชน์ในการเติมพื้นที่โค้ดที่ไม่ได้ใช้แล้วตามด้วยการข้ามไปที่เวกเตอร์เริ่มเย็นสำหรับสถานการณ์ที่หายากหากตัวนับโปรแกรมได้รับความเสียหาย (เช่นความผิดพลาดของ PSU ผลกระทบจากเหตุการณ์รังสีแกมมาที่หายาก ฯลฯ ) ส่วนที่ว่างเปล่าของพื้นที่โค้ดเป็นอย่างอื่น
Techydude

6
ในระบบคอมพิวเตอร์วิดีโออาตาริ 2600 (คอนโซลวิดีโอเกมตัวที่สองเพื่อเรียกใช้โปรแกรมที่เก็บไว้ในตลับหมึก) โปรเซสเซอร์จะทำงานอย่างแม่นยำ 76 รอบในแต่ละบรรทัดการสแกนและการดำเนินการหลายอย่างจะต้องดำเนินการตามจำนวนที่แน่นอนหลังจากเริ่ม สแกนเส้น ในโปรเซสเซอร์นั้นคำสั่ง "NOP" ที่ทำเป็นเอกสารนั้นใช้เวลาสองรอบ แต่มันก็เป็นเรื่องปกติสำหรับรหัสที่จะใช้ประโยชน์จากคำสั่งสามรอบที่ไม่มีประโยชน์อย่างอื่นเพื่อลดความล่าช้าออกไปเป็นจำนวนรอบที่แม่นยำ การรันโค้ดเร็วขึ้นจะทำให้การแสดงผลที่อ่านไม่ออก
supercat

1
การใช้ NOPs สำหรับความล่าช้าสามารถสร้างความเข้าใจได้แม้ในระบบที่ไม่ใช่เวลาจริงในกรณีที่อุปกรณ์ I / O กำหนดเวลาขั้นต่ำระหว่างการดำเนินการติดต่อกัน แต่ไม่มากที่สุด ตัวอย่างเช่นสำหรับคอนโทรลเลอร์หลายตัวการเลื่อนไบต์ออกพอร์ต SPI จะใช้เวลาแปดรอบของ CPU โค้ดที่ไม่ทำอะไรเลย แต่ดึงไบต์จากหน่วยความจำและส่งออกไปยังพอร์ต SPI สามารถทำงานได้เร็วเกินไปเล็กน้อย แต่การเพิ่มตรรกะเพื่อทดสอบว่าพอร์ต SPI พร้อมสำหรับแต่ละไบต์จะทำให้ช้าลงหรือไม่ เพิ่ม NOP หรือสองอาจเปิดช่องให้รหัสเพื่อให้บรรลุความเร็วสูงสุดที่ใช้ได้ ...
SuperCat

1
... ในกรณีที่ไม่มีการขัดจังหวะ หากการขัดจังหวะกระทบ NOPs จะเสียเวลาโดยไม่จำเป็น แต่เวลาที่เสียไปโดย NOP หนึ่งหรือสองจะน้อยกว่าเวลาที่กำหนดในการพิจารณาว่าการขัดจังหวะทำให้พวกเขาไม่จำเป็นหรือไม่
supercat

8

คำตอบอื่น ๆ เพียงแค่พิจารณา NOP ที่ดำเนินการจริงในบางจุด - ที่ใช้กันทั่วไป แต่ไม่ใช่การใช้ NOP เพียงอย่างเดียว

ไม่ใช่การดำเนิน NOP ยังเป็นประโยชน์สวยเมื่อเขียนโค้ดที่สามารถปะ - พื้นคุณจะแผ่นฟังก์ชั่นที่มีไม่กี่ nops หลังจากRET (หรือคำสั่งที่คล้ายกัน) เมื่อคุณมีการแก้ไขการปฏิบัติการคุณสามารถเพิ่มรหัสเพิ่มเติมฟังก์ชั่นเริ่มต้นจากเดิมRETและการใช้เป็นจำนวนมากของผู้ nops ตามที่คุณต้องการ (เช่นนานกระโดดหรือแม้แต่รหัสแบบอินไลน์) RETและการตกแต่งอีกด้วย

ในกรณีที่ใช้งานนี้noöneคาดว่าNOPจะดำเนินการ จุดเดียวคือการอนุญาตให้ทำการปะไฟล์ปฏิบัติการได้ - ในปฏิบัติการแบบไม่อัดในเชิงทฤษฎีคุณต้องเปลี่ยนรหัสของฟังก์ชันเอง (บางครั้งมันอาจจะพอดีกับขอบเขตดั้งเดิม แต่บ่อยครั้งที่คุณต้องกระโดดต่อไป ) - มีความซับซ้อนมากขึ้นโดยเฉพาะการพิจารณาชุดประกอบที่เขียนด้วยตนเองหรือคอมไพเลอร์ที่ปรับให้เหมาะสม คุณต้องเคารพการกระโดดและการสร้างที่คล้ายกันซึ่งอาจชี้ไปที่ชิ้นส่วนสำคัญของรหัส สรุปแล้วค่อนข้างยุ่งยาก

ของหลักสูตรนี้เป็นจำนวนมากใช้มากขึ้นอย่างมากในสมัยก่อนเมื่อมันเป็นประโยชน์เพื่อให้แพทช์เช่นนี้มีขนาดเล็กและออนไลน์ วันนี้คุณเพียงแค่แจกจ่ายไบนารีที่คอมไพล์แล้วและทำได้ ยังคงมีบางคนที่ใช้ NOP สำหรับการปะแก้ (ดำเนินการหรือไม่และไม่ได้เป็นตัวอักษรเสมอไปNOP- ตัวอย่างเช่น Windows ใช้MOV EDI, EDIสำหรับการปรับปรุงออนไลน์ - ซึ่งเป็นประเภทที่คุณสามารถอัปเดตไลบรารีระบบในขณะที่ระบบกำลังทำงานจริง

ดังนั้นคำถามสุดท้ายคือทำไมมีคำสั่งเฉพาะสำหรับสิ่งที่ไม่ได้ทำอะไรจริงๆ?

  • มันเป็นคำสั่งที่เกิดขึ้นจริง - สำคัญเมื่อทำการดีบักหรือประกอบ handcoding คำแนะนำเช่นMOV AX, AXจะทำสิ่งเดียวกัน แต่ไม่ส่งสัญญาณความตั้งใจค่อนข้างชัดเจน
  • แพ็ดดิ้ง - "รหัส" ที่มีเพียงเพื่อปรับปรุงประสิทธิภาพโดยรวมของรหัสที่ขึ้นอยู่กับการจัดตำแหน่ง มันไม่ได้หมายถึงการดำเนินการ ผู้ debuggers บางคนเพียงซ่อน padding NOPs ในการถอดแยกชิ้นส่วนของพวกเขา
  • มันให้พื้นที่เพิ่มเติมสำหรับการปรับแต่งคอมไพเลอร์ - รูปแบบที่ยังใช้อยู่คือคุณมีสองขั้นตอนในการคอมไพล์อันแรกนั้นค่อนข้างง่ายและสร้างรหัสแอสเซมบลีที่ไม่จำเป็นจำนวนมากในขณะที่อีกอันหนึ่ง คำแนะนำที่ไม่เกี่ยวข้อง สิ่งนี้มักจะเห็นในภาษาที่คอมไพล์ด้วย JIT ด้วยเช่นกัน - ทั้งโค้ด IL และ JVM ของ. NET ใช้โค้ดไบต์NOPค่อนข้างมาก รหัสแอสเซมบลีที่คอมไพล์จริงไม่มีอีกต่อไป ควรสังเกตว่ามันไม่ใช่ x86- NOPs
  • มันทำให้การดีบักออนไลน์ง่ายขึ้นทั้งสำหรับการอ่าน (หน่วยความจำที่เป็นศูนย์ล่วงหน้าจะNOPทำให้การแยกส่วนง่ายต่อการอ่านมากขึ้น) และสำหรับการปะแก้ร้อน (แม้ว่าฉันจะชอบแก้ไขและดำเนินการต่อใน Visual Studio: P)

สำหรับการดำเนินการ NOP มีจุดอีกสองสามอย่าง:

  • ประสิทธิภาพแน่นอน - นี่ไม่ใช่สาเหตุที่มันเกิดขึ้นในปี 8085 แต่แม้แต่ 80486 ก็มีการดำเนินการเรียนการสอนแบบไปป์ไลน์ซึ่งทำให้ "ไม่ต้องทำอะไรเลย" ค่อนข้างยุ่งยาก
  • เท่าที่เห็นด้วยMOV EDI, EDIมี nops ที่มีประสิทธิภาพอื่น ๆ NOPกว่าที่แท้จริง MOV EDI, EDIมีประสิทธิภาพที่ดีที่สุดในรูปแบบ 2 ไบต์ NOP ใน x86 หากคุณใช้สองNOPs นั่นจะเป็นสองคำสั่งในการดำเนินการ

แก้ไข:

ที่จริงแล้วการสนทนากับ @DmitryGrigoryev บังคับให้ฉันคิดเกี่ยวกับสิ่งนี้อีกเล็กน้อยและฉันคิดว่ามันเป็นส่วนเพิ่มเติมที่มีค่าสำหรับคำถาม / คำตอบนี้ดังนั้นให้ฉันเพิ่มบิตพิเศษบางอย่าง:

ครั้งแรกจุดที่เห็นได้ชัด - ทำไมจะมีการเรียนการสอนที่ไม่เหมือนบางสิ่งบางอย่างmov ax, ax? ตัวอย่างเช่นลองดูที่กรณีของรหัสเครื่อง 8086 (เก่ากว่าแม้แต่รหัสเครื่อง 386):

  • มีการเรียนการสอน NOP ทุ่มเทกับ 0x90opcode ยังคงเป็นเวลาที่หลายคนเขียนไว้ในใจคุณ ดังนั้นแม้ว่าจะไม่มีNOPคำสั่งเฉพาะคำNOPหลัก (นามแฝง / ตัวย่อ) จะยังคงมีประโยชน์และจะแมปกับสิ่งนั้น
  • คำแนะนำเช่นMOVแมปจริงกับ opcodes ที่แตกต่างกันมากมายเนื่องจากช่วยประหยัดเวลาและพื้นที่ตัวอย่างเช่นmov al, 42"ย้ายไบต์ทันทีไปยังการalลงทะเบียน" ซึ่งแปลว่า0xB02A( 0xB0เป็น opcode ซึ่ง0x2Aเป็นอาร์กิวเมนต์ "ทันที") ดังนั้นมันใช้เวลาสองไบต์
  • ไม่มี opcode ทางลัดสำหรับmov al, al(เนื่องจากเป็นสิ่งที่ต้องทำโดยทั่วไป) ดังนั้นคุณจะต้องใช้mov al, rmb(rmb เป็น "การลงทะเบียนหรือหน่วยความจำ") เกินพิกัด จริง ๆ แล้วใช้เวลาสามไบต์ (แม้ว่ามันอาจจะใช้ค่าที่น้อยกว่าmov rb, rmbแทนซึ่งควรใช้สองไบต์เท่านั้นmov al, al- ไบต์ของอาร์กิวเมนต์ถูกใช้เพื่อระบุทั้งแหล่งที่มาและรีจิสเตอร์เป้าหมายตอนนี้คุณรู้แล้วว่าทำไม 8086 มีเพียง 8 รีจิสเตอร์: D) เปรียบเทียบกับNOPซึ่งเป็นคำสั่งไบต์เดียว! สิ่งนี้ช่วยประหยัดหน่วยความจำและเวลาเนื่องจากการอ่านหน่วยความจำใน 8086 ยังคงมีราคาค่อนข้างสูง - ไม่ต้องพูดถึงการโหลดโปรแกรมนั้นจากเทปหรือฟลอปปี้หรือบางสิ่งบางอย่าง

แล้วxchg ax, axมาจากไหน? คุณเพียงแค่ต้องดู opcodes ของxhcgคำแนะนำอื่น ๆ คุณจะเห็น0x86, 0x87และสุดท้าย-0x91 0x97ดังนั้นnopกับมัน0x90ดูเหมือนว่าเป็นแบบที่ดีงามสำหรับxchg ax, ax(ซึ่งอีกครั้งไม่ได้เป็นxchg"เกิน" - คุณจะต้องใช้xchg rb, rmbเวลาสอง bytes) และในความเป็นจริงฉันค่อนข้างแน่ใจว่านี่เป็นผลข้างเคียงที่ดีของสถาปัตยกรรมไมโครของเวลา - ถ้าฉันจำได้อย่างถูกต้องมันเป็นเรื่องง่ายที่จะแมปช่วงทั้งหมดของ0x90-0x97"xchg, แทนการลงทะเบียนaxและax- di" ( ถูกดำเนินการเป็นสมมาตรนี้ให้คุณครบวงจรรวมทั้ง nop 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แผนที่แมปกับสิ่งที่ "noppy" โดยธรรมชาติ

บนมืออื่น ๆ , i8051 ได้รับการออกแบบในทั้งหมด opcode สำหรับ-nop 0x00ค่อนข้างเป็นประโยชน์ การออกแบบการเรียนการสอนเป็นพื้นใช้ตอดสูงสำหรับการดำเนินงานและต่ำตอดสำหรับการเลือกตัวถูกดำเนินการ - สำหรับตัวอย่างเช่นadd aเป็น0x2Yและ0xX8วิธีการที่ "ลงทะเบียน 0 โดยตรง" เพื่อให้เป็น0x28 add a, r0ช่วยประหยัดซิลิคอนได้มาก :)

ฉันยังคงดำเนินต่อไปเนื่องจากการออกแบบ CPU (ไม่พูดถึงการออกแบบคอมไพเลอร์และการออกแบบภาษา) เป็นหัวข้อที่ค่อนข้างกว้าง แต่ฉันคิดว่าฉันได้แสดงมุมมองที่แตกต่างมากมายที่เข้ามาในการออกแบบค่อนข้างดี


ที่จริงแล้วNOPเป็นเรื่องปกติที่จะนามแฝงMOV ax, ax, ADD ax, 0หรือคำแนะนำที่คล้ายกัน ทำไมคุณถึงออกแบบคำสั่งเฉพาะที่ไม่ทำอะไรเลยเมื่อมีความอุดมสมบูรณ์
Dmitry Grigoryev

@DmitryGrigoryev ที่จริง ๆ แล้วมันเป็นการออกแบบภาษาซีพียู ซีพียูที่สุด (และคอมไพเลอร์) จะมีแนวโน้มที่จะเพิ่มประสิทธิภาพการMOV ax, axไป; NOPจะมีจำนวนรอบการดำเนินการที่แน่นอนเสมอ แต่ฉันไม่เห็นว่ามันเกี่ยวข้องกับสิ่งที่ฉันเขียนในคำตอบของฉันอย่างไร
Luaan

ซีพียูไม่สามารถเพิ่มประสิทธิภาพได้อย่างแท้จริงMOV ax, axเพราะเมื่อพวกเขารู้ว่านี่เป็นMOVคำสั่งที่มีอยู่แล้ว
Dmitry Grigoryev

@DmitryGrigoryev ขึ้นอยู่กับชนิดของ CPU ที่คุณกำลังพูดถึง ซีพียูเดสก์ท็อปสมัยใหม่ทำสิ่งต่างๆมากมายไม่เพียง แต่แนะนำการวางท่อ ตัวอย่างเช่นซีพียูรู้ว่าไม่จำเป็นต้องทำการแคชไลน์ ฯลฯ ทำให้รู้ว่าไม่จำเป็นต้องทำอะไรเลย (สำคัญมากสำหรับไฮเปอร์เธรดและแม้กระทั่งสำหรับท่อหลาย ๆ ฉันจะไม่แปลกใจถ้ามันมีอิทธิพลต่อการทำนายสาขา (แม้ว่ามันอาจจะเหมือนกันสำหรับNOPและMOV ax, ax) ซีพียูรุ่นใหม่มีความซับซ้อนกว่าคอมไพเลอร์รุ่นเก่าของ C :) :)
Luaan

1
+1 สำหรับการเพิ่มประสิทธิภาพไม่มีใครเป็นnoöne! ฉันคิดว่าเราควรร่วมมือกันและกลับไปที่การสะกดแบบนี้! Z80 (และในทางกลับกัน 8080) มี 7 LD r, rคำสั่งในกรณีที่rการลงทะเบียนครั้งเดียวใด ๆ คล้ายกับMOV ax, axคำแนะนำของคุณ เหตุผลที่มันเป็นวันที่ 7 และ 8 HALTไม่ได้เป็นเพราะหนึ่งในคำแนะนำที่มีมากเกินไปจะกลายเป็น ดังนั้น 8080 และ Z80 จึงมีคำแนะนำอย่างน้อย 7 อย่างที่ทำเช่นเดียวกันNOP! ที่น่าสนใจพอถึงแม้ว่าคำแนะนำเหล่านี้จะไม่เกี่ยวข้องกับตรรกะด้วยรูปแบบบิตพวกเขาทั้งหมดใช้เวลา 4 T เพื่อดำเนินการดังนั้นจึงไม่มีเหตุผลที่จะใช้LDคำแนะนำ!
CJ Dennis

5

ย้อนกลับไปในช่วงปลายยุค 70 พวกเรา (ฉันเป็นนักเรียนวิจัยเล็ก) มีระบบ dev เล็กน้อย (8080 ถ้าหน่วยความจำทำหน้าที่) ที่วิ่งใน 1024 ไบต์ของรหัส (เช่น UVEPROM เดียว) - มีเพียงสี่คำสั่งในการโหลด (L ) บันทึก (S) พิมพ์ (P) และอย่างอื่นที่ฉันจำไม่ได้ มันขับเคลื่อนด้วยโทรพิมพ์และเทปพันสายตัวจริง มันถูกเข้ารหัสอย่างแน่นหนา!

ตัวอย่างหนึ่งของการใช้ NOOP คือในบริการรูทีนอินเทอร์รัปต์ (ISR) ซึ่งเว้นระยะไว้ในช่วง 8 ไบต์ รูทีนนี้จบลงด้วยความยาว 9 ไบต์ที่มี (ยาว) กระโดดไปยังที่อยู่ไกลขึ้นไปอีกเล็กน้อยในพื้นที่ที่อยู่ สิ่งนี้หมายความว่าหากมีการสั่งซื้อไบต์ endian เล็กน้อยว่าไบต์ที่อยู่สูงคือ 00h และ slotted เป็นไบต์แรกของ ISR ถัดไปซึ่งหมายความว่า (ISR ถัดไป) เริ่มต้นด้วย NOOP ดังนั้นเราจึงสามารถพอดี รหัสในพื้นที่ จำกัด !

ดังนั้น NOOP จึงมีประโยชน์ นอกจากนี้ฉันคิดว่ามันเป็นวิธีที่ง่ายที่สุดสำหรับ intel ที่จะเขียนโค้ดด้วยวิธีนั้น - พวกเขาอาจมีรายการคำสั่งที่พวกเขาต้องการนำไปใช้และเริ่มต้นที่ '1' เหมือนกับรายการทั้งหมดที่ทำ (นี่คือวันของ FORTRAN) ดังนั้นศูนย์ รหัส NOOP เริ่มล้มลง (ฉันไม่เคยเห็นบทความที่โต้แย้งว่า NOOP เป็นส่วนสำคัญของทฤษฎีวิทยาศาสตร์คอมพิวเตอร์ (เช่นเดียวกับ Q: นักคณิตศาสตร์มี nul op แตกต่างจากศูนย์ทฤษฎีกลุ่มหรือไม่)


ไม่ใช่ซีพียูทั้งหมดที่เข้ารหัส NOP เป็น 0x00 (แม้ว่า 8085, 8080 และซีพียูที่ฉันคุ้นเคยมากที่สุดคือ Z80, ทำทั้งหมด) อย่างไรก็ตามถ้าฉันออกแบบหน่วยประมวลผลที่ฉันจะใส่ไว้! สิ่งอื่นที่มีประโยชน์คือหน่วยความจำนั้นมักจะถูกกำหนดค่าเริ่มต้นให้กับทุก 0x00 ดังนั้นการเรียกใช้งานโค้ดนี้จะไม่ทำอะไรจนกว่า CPU จะไปถึงหน่วยความจำที่ไม่มีค่าศูนย์
CJ Dennis

@CJDennis ผมเคยอธิบายว่าทำไมซีพียู x86 ไม่ได้ใช้0x00สำหรับnopในคำตอบของฉัน ในระยะสั้นจะช่วยในการถอดรหัสคำแนะนำ - xchg ax, axไหลอย่างเป็นธรรมชาติจากวิธีการถอดรหัสการเรียนการสอนและมันจะทำอะไร "noppy" ดังนั้นทำไมไม่ใช้มันและเรียกมันว่า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

นอกจากนี้คำแนะนำในการดำเนินการตามเงื่อนไขบางอย่าง ( ถ้าเป็นจริงและคล้ายกัน) ใช้ช่องสำหรับแต่ละเงื่อนไขและเมื่อเงื่อนไขเฉพาะไม่มีการดำเนินการที่เกี่ยวข้องกับมันช่องจะต้องถูกครอบครองโดย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 แอปพลิเคชันหยุดทำงาน!) ดังนั้น x86 มีเพื่อให้แน่ใจว่าใบสมัครไม่ได้แจ้งให้ทราบเมื่อมีการปรับปรุงเช่นนี้จะมีการเพิ่ม CPU - จนกว่าเราได้ซีพียูแบบมัลติคอร์อยู่แล้ว ... : D
Luaan

2

อีกตัวอย่างหนึ่งของการใช้NOP สองไบต์ : http://blogs.msdn.com/b/oldnewthing/archive/2011/09/21/10214405.aspx

MOV EDI, คำสั่ง EDI เป็น NOP แบบสองไบต์, ซึ่งเป็นพื้นที่เพียงพอที่จะแก้ไขในคำสั่งการกระโดดเพื่อให้ฟังก์ชั่นสามารถปรับปรุงได้ทันที ความตั้งใจคือ MOV EDI คำสั่ง EDI จะถูกแทนที่ด้วยคำสั่ง JMP $ -5 แบบสองไบต์เพื่อเปลี่ยนเส้นทางการควบคุมไปยังพื้นที่เก็บข้อมูลขนาดห้าไบต์ที่มาทันทีก่อนเริ่มฟังก์ชั่น ห้าไบต์เพียงพอสำหรับคำสั่งการกระโดดเต็มซึ่งสามารถส่งการควบคุมไปยังฟังก์ชั่นการเปลี่ยนที่ติดตั้งที่อื่นในพื้นที่ที่อยู่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.