รหัสเครื่อง

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

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

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

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

รหัสของชุดของคำสั่งเครื่องอาจมีความยาวเท่ากันหมดทุกคำสั่งหรือมีความยาวแปรผันก็ได้ วิธีการจัดการแบบรูปของรหัสเครื่องขึ้นอยู่กับสถาปัตยกรรมนั้น ๆ เป็นอย่างยิ่ง และมักจะขึ้นอยู่กับชนิดของคำสั่ง คำสั่งเครื่องส่วนมากมีฟีลด์ออปโคด (opcode) หนึ่งฟีลด์หรือมากกว่าซึ่งใช้ระบุชนิดของคำสั่งพื้นฐาน (เช่นเลขคณิต ตรรกศาสตร์ การกระโดด ฯลฯ) และการดำเนินการแท้จริง (เช่นการบวก การเปรียบเทียบ) และมีฟีลด์อื่น ๆ ที่อาจใช้สำหรับระบุชนิดของตัวถูกดำเนินการ (operand) ภาวะการกำหนดตำแหน่งที่อยู่ (addressing mode) ออฟเซตของตำแหน่งที่อยู่หรือดัชนี หรือค่าแท้จริงโดยตัวมันเอง (ตัวถูกดำเนินการที่เป็นค่าคงตัวที่บรรจุอยู่ในคำสั่งเครื่องเช่นนั้นเรียกว่า "ค่าใช้ทันที", immediate)

ไม่ใช่ว่าทุกเครื่องหรือทุกคำสั่งจะมีตัวถูกดำเนินการชัดแจ้ง เครื่องที่ใช้ตัวสะสม (accumulator machine) มีตัวถูกดำเนินการข้างซ้ายแบบผสม และคืนค่าผลลัพธ์ในตัวสะสม (accumulator) ปริยาย สำหรับคำสั่งเลขคณิตส่วนใหญ่ สถาปัตยกรรมอื่น (เช่น 8086 และตระกูล x86) มีคำสั่งธรรมดาในรุ่นของตัวสะสมอยู่ด้วย ซึ่งคำสั่งที่ยาวกว่าจะทำเรจิสเตอร์อันหนึ่งในเรจิสเตอร์ทั่วไปเป็นตัวสะสม เครื่องที่ใช้กองซ้อน (stack machine) มีตัวถูกดำเนินการส่วนใหญ่หรือทั้งหมดอยู่บนกองซ้อนปริยาย คำสั่งเครื่องที่มีจุดประสงค์พิเศษก็มักจะขาดตัวถูกดำเนินการชัดแจ้ง (ตัวอย่างเช่น ซีพียูไอดีในสถาปัตยกรรม x86 เขียนค่าต่าง ๆ ลงในเรจิสเตอร์ปลายทางเป็นปริยาย 4 เรจิสเตอร์) ความแตกต่างระหว่างตัวถูกดำเนินการชัดแจ้งกับปริยายเช่นนี้สำคัญต่อโปรแกรมสร้างรหัสเครื่อง โดยเฉพาะอย่างยิ่งในเรื่องการจัดสรรเรจิสเตอร์และส่วนติดตามพิสัยแบบสด โปรแกรมทำรหัสให้เหมาะที่สุด (code optimizer) ที่ดีสามารถติดตามตัวถูกดำเนินการทั้งชัดแจ้งและปริยาย ซึ่งอาจช่วยให้เกิดการแพร่กระจายค่าคงตัว (constant propagation) ได้บ่อยยิ่งขึ้น การพับทบค่าคงตัว (constant folding) ของเรจิสเตอร์ (เรจิสเตอร์ที่กำหนดให้เป็นผลลัพธ์จากนิพจน์ของค่าคงตัวจะถูกแทนที่ด้วยค่าคงตัวนั้น) และการปรับปรุงรหัสอื่น ๆ ให้ดีขึ้น

โปรแกรมคอมพิวเตอร์คือลำดับของคำสั่งเครื่องที่กระทำการโดยหน่วยประมวลผลกลาง ขณะที่หน่วยประมวลผลอย่างง่ายกระทำการคำสั่งเครื่องทีละคำสั่ง แต่หน่วยประมวลผลซูเปอร์สเกลาร์ (superscalar) สามารถกระทำการคำสั่งเครื่องได้พร้อม ๆ กันทีละหลายคำสั่ง

การถอดความรหัสเครื่องที่สามารถอ่านได้ง่ายขึ้นอย่างมากอย่างหนึ่งคือภาษาแอสเซมบลี ซึ่งเป็นภาษาที่ใช้รหัสช่วยจำ (mnemonic code) อ้างถึงรหัสของคำสั่งเครื่อง แทนที่จะใช้ค่าตัวเลขของคำสั่งเครื่องโดยตรง ตัวอย่างเช่น หน่วยประมวลผล ซิล็อก แซด80 (Zilog Z80) รหัสเครื่อง codice_1 คือคำสั่งที่ทำให้หน่วยประมวลผลกลางลดค่าเรจิสเตอร์ B ลงหนึ่ง จะเขียนแทนด้วยภาษาแอสเซมบลีว่า codice_2 เป็นต้น

สถาปัตยกรรมมิปส์ (MIPS architecture) ได้ให้ตัวอย่างรหัสเครื่องไว้ ซึ่งคำสั่งเครื่องของมันจะมีขนาด 32 บิตเสมอ ชนิดของคำสั่งกำหนดด้วยฟีลด์ "op" (การดำเนินการ) คือบิตบนสุด 6 บิต คำสั่งชนิด J (กระโดด) และชนิด I (ค่าใช้ทันที) จะระบุไว้อย่างชัดเจนด้วย "op" ส่วนคำสั่งชนิด R (เรจิสเตอร์) ต้องมีฟีลด์ "funct" (ฟังก์ชัน) เพิ่มขึ้นมาเพื่อเจาะจงการดำเนินการที่แน่นอน ฟีลด์ที่ใช้ในคำสั่งสามชนิดมีดังนี้

"rs", "rt", และ "rd" คือเรจิสเตอร์ที่ถูกดำเนินการ; "shamt" คือปริมาณการเลื่อน; "address" หรือ "immediate" คือค่าที่ถูกดำเนินการโดยตรง

ตัวอย่าง การบวกเรจิสเตอร์ 1 กับเรจิสเตอร์ 2 แล้วเก็บผลลัพธ์ไว้ในเรจิสเตอร์ 6 เขียนได้ดังนี้

การบรรจุค่าให้กับเรจิสเตอร์ 8 โดยนำค่ามาจากเซลล์หน่วยความจำที่ 68 ถัดจากตำแหน่งที่ระบุในเรจิสเตอร์ 3 เขียนได้ดังนี้

การกระโดดไปที่ตำแหน่ง 1024 เขียนได้ดังนี้

รหัสเครื่องไม่สามารถอ่านได้อย่างยิ่ง ดังเช่นที่สำนักงานลิขสิทธิ์แห่งสหรัฐอเมริกา (United States Copyright Office) ไม่สามารถระบุได้ว่า โปรแกรมที่เข้ารหัสแล้วแต่ละโปรแกรมเป็นผลงานต้นฉบับของผู้ถือลิขสิทธิ์หรือไม่ อย่างไรก็ตามสำนักงานลิขสิทธิ์ก็อนุญาตให้จดทะเบียนลิขสิทธิ์ของโปรแกรมคอมพิวเตอร์ได้ ดักลาส ฮอฟสตัดเทอร์ (Douglas Hofstadter) ได้เปรียบเทียบรหัสเครื่องกับรหัสพันธุกรรมว่า "การมองดูโปรแกรมที่ถูกเขียนเป็นรหัสเครื่อง ก็เทียบได้อย่างกว้าง ๆ กับการมองดูโมเลกุลดีเอ็นเอทีละอะตอม"