คลังเก็บหมวดหมู่: หนังสือเกี่ยวกับแอสเซมเบลอร์ พื้นฐานการประกอบ

เราปลูกฝังแนวคิดนี้มาเป็นเวลานาน เราอาจจะบุกโจมตีมันจากทุกทิศทุกทางเป็นเวลาหลายปี และทุกครั้งที่มีบางอย่างขวางทางเรา ในอีกด้านหนึ่ง แอสเซมเบลอร์นั้นเจ๋งพอ ๆ กับความสามารถในการสื่อสารกับคอมพิวเตอร์ในภาษาของมันซึ่งก็เจ๋งสำหรับแฮ็กเกอร์ผู้อ่านของเรา (แครกเกอร์, ย้อนกลับ) อีกด้านหนึ่ง - คู่มือปัจจุบันตามข้อมูลของ ASMU รวมถึงสิ่งพิมพ์ในศตวรรษนี้มีเพียงพอและทุกวันนี้เป็นพวกเสรีนิยม แฮกเกอร์เว็บและผู้ชื่นชอบ JS อาจไม่เข้าใจหรือเห็นด้วยกับเรา 🙂 ความสำเร็จยุติความขัดแย้งระหว่างนักฟิสิกส์ นักแต่งเนื้อร้อง ผู้ศรัทธาเก่า ชาวนิคอน ชาวแฮกเกอร์เว็บ และผู้แคร็กแรงงาน ปรากฎว่าในศตวรรษที่ 21 แครกเกอร์แรงงานยังคงไม่ละทิ้งตำแหน่งและผู้อ่านของเราสนใจสิ่งนี้!

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

แต่ไม่ว่าเราจะเขียนโปรแกรมประเภทใดก็ตาม ความสำเร็จขึ้นอยู่กับทักษะการปฏิบัติควบคู่กับความรู้ พื้นฐานและทฤษฎี ทฤษฎีและการปฏิบัติ การศึกษาและการทำงาน สิ่งเหล่านี้เป็นรากฐานที่สำคัญของความสำเร็จ

ใน เมื่อเร็วๆ นี้แอสเซมเบลอร์ไม่สมควรอยู่ภายใต้เงาของภาษาอื่น นี่เป็นเพราะการค้าระดับโลกที่มุ่งเป้าไปที่การเพิ่มสูงสุด ระยะเวลาอันสั้นได้รับผลกำไรมากที่สุดจากผลิตภัณฑ์ กล่าวอีกนัยหนึ่ง คุณลักษณะของมวลชนมีชัยเหนือลัทธิอภิสิทธิ์ ในความคิดของฉันแอสเซมเบลอร์นั้นใกล้เคียงกับอย่างหลังมากกว่า การฝึกอบรมนักเรียนในภาษาต่างๆ เช่น C++, C#, PHP, Java, JavaScript, Python ในเวลาอันสั้นนั้นให้ผลกำไรมากกว่ามาก เพื่อให้เขาสามารถสร้างซอฟต์แวร์ระดับผู้บริโภคได้ไม่มากก็น้อย โดยไม่ถามคำถามว่าทำไมและทำไมเขาถึงทำเช่นนั้นมากกว่าที่จะปล่อยตัว ผู้เชี่ยวชาญที่ดีในแอสเซมเบลอร์ ตัวอย่างนี้คือตลาดที่กว้างขวางสำหรับหลักสูตรการเขียนโปรแกรมทุกประเภทในทุกภาษา ยกเว้นการประกอบ แนวโน้มเดียวกันนี้สามารถเห็นได้ทั้งในการสอนในมหาวิทยาลัยและในวรรณกรรมด้านการศึกษา ทั้งสองกรณีจนถึง วันนี้วัสดุส่วนใหญ่ใช้โปรเซสเซอร์ซีรีส์ 8086 รุ่นแรกๆ ที่เรียกว่าโหมดการทำงาน 16 บิต "ของจริง" การทำงาน สภาพแวดล้อม MS-DOS- เป็นไปได้ว่าสาเหตุหนึ่งก็คือ ในแง่หนึ่ง ด้วยการถือกำเนิดของคอมพิวเตอร์พีซี IBM ครูต้องเปลี่ยนไปใช้แพลตฟอร์มนี้เนื่องจากผู้อื่นไม่สามารถเข้าถึงได้ ในทางกลับกันเมื่อสาย 80x86 พัฒนาขึ้นความสามารถในการรันโปรแกรมในโหมด DOS ยังคงอยู่ซึ่งทำให้สามารถประหยัดเงินในการซื้อคอมพิวเตอร์เพื่อการศึกษาเครื่องใหม่และรวบรวมหนังสือเรียนเพื่อศึกษาสถาปัตยกรรมของโปรเซสเซอร์ใหม่ อย่างไรก็ตาม ขณะนี้การเลือกแพลตฟอร์มสำหรับการศึกษาดังกล่าวเป็นสิ่งที่ยอมรับไม่ได้โดยสิ้นเชิง MS-DOS ในฐานะสภาพแวดล้อมการทำงานของโปรแกรมนั้นล้าสมัยไปอย่างสิ้นหวังในช่วงกลางทศวรรษที่ 1990 และเมื่อเปลี่ยนไปใช้โปรเซสเซอร์ 32 บิตโดยเริ่มจากโปรเซสเซอร์ 80386 ระบบคำสั่งเองก็มีเหตุผลมากขึ้น จึงไม่มีประโยชน์ที่จะเสียเวลาศึกษาและอธิบายความแปลกประหลาดของสถาปัตยกรรม โหมดจริงซึ่งจะไม่ปรากฏบนโปรเซสเซอร์ใด ๆ อย่างแน่นอน

สำหรับการเลือกสภาพแวดล้อมการทำงานสำหรับการเรียนรู้แอสเซมเบลอร์ ถ้าเราพูดถึงระบบคำสั่งแบบ 32 บิต ตัวเลือกนั้นค่อนข้างน้อย สิ่งเหล่านี้กำลังทำงานอยู่ ระบบวินโดวส์หรือตัวแทนของตระกูล UNIX

คุณควรพูดสองสามคำเกี่ยวกับแอสเซมเบลอร์ที่จะเลือกสำหรับสภาพแวดล้อมการทำงานเฉพาะ ดังที่คุณทราบ ไวยากรณ์แอสเซมเบลอร์สองประเภทใช้เพื่อทำงานกับโปรเซสเซอร์ x86 - ไวยากรณ์ AT&T และไวยากรณ์ของ Intel ไวยากรณ์เหล่านี้แสดงถึงคำสั่งเดียวกันในรูปแบบที่แตกต่างกันโดยสิ้นเชิง ตัวอย่างเช่น คำสั่งในรูปแบบไวยากรณ์ของ Intel มีลักษณะดังนี้:

Mov eax,ebx

ไวยากรณ์ของ AT&T จะมีรูปแบบที่แตกต่างออกไป:

Movl %eax,%ebx

อย่างไรก็ตามไวยากรณ์ประเภท AT&T ได้รับความนิยมมากกว่าในสภาพแวดล้อมระบบปฏิบัติการ UNIX สื่อการสอนไม่มีข้อมูลเกี่ยวกับมัน มีการอธิบายไว้เฉพาะในเอกสารอ้างอิงและเอกสารทางเทคนิค ดังนั้นจึงสมเหตุสมผลที่จะเลือกแอสเซมเบลอร์ตามไวยากรณ์ของ Intel สำหรับระบบ UNIX จะมีแอสเซมเบลอร์หลักสองตัว ได้แก่ NASM (Netwide Assembler) และ FASM (Flat Assembler) สำหรับ เส้นหน้าต่าง FASM และ MASM (Macro Assembler) จาก Microsoft ได้รับความนิยม และยังมี TASM (Turbo Assembler) จาก Borland ซึ่งหยุดสนับสนุนการสร้างของตัวเองมานานแล้ว

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

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

แอสเซมเบลอร์คืออะไร?

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

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

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

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

ไวยากรณ์

ไม่มีมาตรฐานที่ยอมรับโดยทั่วไปสำหรับไวยากรณ์ของภาษาแอสเซมบลี อย่างไรก็ตาม นักพัฒนาภาษาแอสเซมบลีส่วนใหญ่ปฏิบัติตามแนวทางดั้งเดิมทั่วไป มาตรฐานหลักดังกล่าวคือ ไวยากรณ์ของอินเทลและ ไวยากรณ์ของ AT&T.

รูปแบบทั่วไปสำหรับคำแนะนำในการบันทึกจะเหมือนกันสำหรับทั้งสองมาตรฐาน:

[ฉลาก:] opcode [ตัวถูกดำเนินการ] [;ความคิดเห็น]

จริงๆ แล้ว opcode เป็นคำสั่งแอสเซมบลี ซึ่งเป็นตัวช่วยจำคำสั่งของโปรเซสเซอร์ สามารถเพิ่มคำนำหน้าได้ (เช่น การซ้ำ การเปลี่ยนแปลงประเภทการกำหนดแอดเดรส) ตัวถูกดำเนินการอาจเป็นค่าคงที่ ชื่อรีจิสเตอร์ ที่อยู่ใน RAM และอื่นๆ ความแตกต่างระหว่างมาตรฐาน Intel และ AT&T ส่วนใหญ่เกี่ยวข้องกับลำดับของการแจกแจงตัวถูกดำเนินการและไวยากรณ์เมื่อ วิธีการที่แตกต่างกันที่อยู่

คำสั่งที่ใช้มักจะเหมือนกันสำหรับโปรเซสเซอร์ทั้งหมดที่มีสถาปัตยกรรมเดียวกันหรือตระกูลสถาปัตยกรรมเดียวกัน (คำสั่งที่รู้จักกันดีคือคำสั่งสำหรับโปรเซสเซอร์ Motorola, ARM, x86 และตัวควบคุม) มีการอธิบายไว้ในข้อมูลจำเพาะของโปรเซสเซอร์

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

คำอธิบายโดยย่อของภาษาแอสเซมบลี

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

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

สั้น ๆ เกี่ยวกับโครงสร้างของภาษา

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

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

โปรเซสเซอร์แต่ละบรรทัดมีกระบวนการของตัวเอง ในสถานการณ์นี้ รวมถึงกระบวนการที่แปลจะถูกต้องด้วย

ภาษาแอสเซมบลีมีไวยากรณ์หลายประการที่จะกล่าวถึงในบทความ

ข้อดีของภาษา

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

ไดรเวอร์, ระบบปฏิบัติการ, BIOS, คอมไพเลอร์, ล่าม ฯลฯ ล้วนเป็นโปรแกรมในภาษาแอสเซมบลี

เมื่อใช้เครื่องแยกชิ้นส่วนที่ดำเนินการแปลจากเครื่องหนึ่งไปอีกเครื่องหนึ่ง คุณสามารถเข้าใจวิธีการทำงานของระบบนี้หรืองานนั้นได้อย่างง่ายดาย แม้ว่าจะไม่มีคำอธิบายก็ตาม อย่างไรก็ตาม สิ่งนี้จะเกิดขึ้นได้ก็ต่อเมื่อโปรแกรมนั้นง่าย น่าเสียดายที่รหัสที่ไม่ซับซ้อนนั้นค่อนข้างเข้าใจยาก

ข้อเสียของภาษา

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

เพื่อที่จะเขียนโปรแกรมที่ง่ายที่สุดได้ โปรแกรมเมอร์จะต้องมีคุณสมบัติและระดับความรู้ของเขาสูงเพียงพอ น่าเสียดายที่ผู้เชี่ยวชาญทั่วไปมักจะเขียนโค้ดที่ไม่ถูกต้อง

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

คำสั่งภาษา

ตามที่กล่าวไว้ข้างต้น โปรเซสเซอร์แต่ละตัวมีชุดคำสั่งของตัวเอง องค์ประกอบที่ง่ายที่สุดที่ประเภทใด ๆ รู้จักคือรหัสต่อไปนี้:


การใช้คำสั่ง

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

อันดับแรก จำเป็นต้องเน้นย้ำว่าคำสั่งไม่ได้แปลเป็นภาษาเครื่อง พวกเขาควบคุมวิธีการทำงานของคอมไพเลอร์ ต่างจากคำสั่งตรงที่พารามิเตอร์เหล่านี้มี ฟังก์ชั่นต่างๆ, ไม่ได้แตกต่างกันเนื่องจากโปรเซสเซอร์ที่แตกต่างกัน แต่เนื่องจากนักแปลที่แตกต่างกัน ในบรรดาคำสั่งหลักมีดังต่อไปนี้:


ที่มาของชื่อ

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

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

แมคโครหมายถึง

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

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

ต้นฉบับ: เริ่มต้นใช้งานในภาษาแอสเซมบลี ส่วนที่ 1
ผู้เขียน: ไมค์ ซอนเดอร์ส
วันที่เผยแพร่: 30 ตุลาคม 2558
แปล: อ. ภานิน
วันที่แปล: 10 พฤศจิกายน 2558

ส่วนที่ 1: เอาชนะข้อจำกัดของภาษาการเขียนโปรแกรมระดับสูง และทำความเข้าใจว่าหน่วยประมวลผลกลางทำงานอย่างไร

มีไว้เพื่ออะไร?

  • เพื่อทำความเข้าใจวิธีการทำงานของคอมไพเลอร์
  • เพื่อทำความเข้าใจคำสั่งของ CPU
  • เพื่อเพิ่มประสิทธิภาพโค้ดของคุณเพื่อประสิทธิภาพ

คนส่วนใหญ่เชื่อว่าภาษาแอสเซมบลีไม่ได้แตกต่างจากมนต์ดำมากนักและเป็นส่วนหนึ่งของความมืดและ โลกที่น่ากลัวซึ่งมีเพียง 0.01% ของนักพัฒนาที่ดีที่สุดเท่านั้นที่เสี่ยงที่จะเข้าร่วม ซอฟต์แวร์- แต่ในความเป็นจริงแล้ว มันเป็นภาษาโปรแกรมที่สวยงามและเข้าถึงได้ง่ายมาก อย่างน้อยคุณควรศึกษาพื้นฐานของมันเพื่อทำความเข้าใจกลไกการสร้างโค้ดโดยคอมไพเลอร์ หลักการทำงานของโปรเซสเซอร์กลาง และเพื่อให้เข้าใจหลักการทำงานของคอมพิวเตอร์ดีขึ้น ภาษาแอสเซมบลีนั้นเป็นการแสดงข้อความของคำสั่งที่ดำเนินการเป็นหลัก ซีพียู, กับบางอย่าง คุณลักษณะเพิ่มเติมทำให้ขั้นตอนการเขียนโปรแกรมง่ายขึ้น

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

ในบทความในชุดนี้ เราจะมาสำรวจโลกแห่งภาษาแอสเซมบลีโดยละเอียด ในบทความนี้เราจะดูเฉพาะเทคนิคการเขียนโปรแกรมพื้นฐานเท่านั้น ในบทความจากนิตยสารฉบับถัดไปเราจะจัดการกับปัญหาที่ซับซ้อนมากขึ้น หลังจากนั้นเราจะพิจารณาภาษาแอสเซมบลีให้เสร็จสิ้นโดยการเขียนการโหลดแบบง่าย ระบบปฏิบัติการ- เธอจะไม่สามารถแสดงใดๆ ได้ งานที่มีประโยชน์แต่จะขึ้นอยู่กับโค้ดของคุณและทำงานโดยตรงกับฮาร์ดแวร์โดยไม่จำเป็นต้องดาวน์โหลดระบบปฏิบัติการของบริษัทอื่น ฟังดูดีใช่มั้ย? เริ่มกันเลย

โปรแกรมภาษาแอสเซมบลีแรกของคุณ

บทเรียนการเขียนโปรแกรมภาษาแอสเซมบลีจำนวนมากเริ่มต้นด้วยส่วนที่ยาว สับสน และน่าเบื่อ ซึ่งดำเนินไปเกี่ยวกับเลขคณิตไบนารีและทฤษฎีการออกแบบ CPU โดยไม่มีโค้ดจริงใดๆ เลย ฉันเชื่อว่าเนื้อหาดังกล่าวทำให้ความสนใจของผู้อ่านเป็นโมฆะ ดังนั้นเราจะเริ่มต้นด้วยการทบทวนโค้ดโดยตรง โปรแกรมจริง- หลังจากนั้นเราจะดูโค้ดแต่ละบรรทัดของโปรแกรมนี้เพื่อให้คุณเข้าใจหลักการของภาษาแอสเซมบลีตามตัวอย่างที่ใช้งานได้จริง

โปรแกรมแก้ไขข้อความบางตัว เช่น Vim มีการเน้นไวยากรณ์ภาษาแอสเซมบลี (ลองใช้คำสั่ง set syn=nasm)

คัดลอกโค้ดต่อไปนี้ลงในช่องข้อความของรายการใดก็ได้ โปรแกรมแก้ไขข้อความและบันทึกลงในไฟล์ชื่อ myfirst.asm ในโฮมไดเร็กตอรี่ของคุณ:

ส่วน .text global _start _start: mov ecx, ข้อความ mov edx, ความยาว mov ebx, 1 mov eax, 4 int 0x80 mov eax, 1 int 0x80 ส่วน .data ข้อความ db "Assembly Rules!", 10 ความยาว equ $ - ข้อความ

(หมายเหตุ: คุณสามารถใช้ช่องว่างหรือแท็บเพื่อเยื้องโค้ดของคุณได้ - ไม่สำคัญ) โปรแกรมนี้เพียงพิมพ์บรรทัด "กฎการประกอบ!" ไปที่หน้าจอแล้วออก

เครื่องมือที่เราจะใช้ในการแปลงรหัสภาษาแอสเซมบลีนี้เป็นไฟล์ไบนารีที่ปฏิบัติการได้นั้นมีชื่อที่ค่อนข้างน่าขบขันว่า "แอสเซมเบลอร์" มีผู้ประกอบที่แตกต่างกันมากมาย แต่ผู้ประกอบที่ฉันชอบคือ NASM; มันอยู่ในที่เก็บแพ็คเกจซอฟต์แวร์ของการแจกจ่ายเกือบทุกชนิด ดังนั้นคุณจึงสามารถติดตั้งได้โดยใช้ตัวจัดการแพ็คเกจซอฟต์แวร์ด้วย อินเตอร์เฟซแบบกราฟิก, yum install nasm , apt-get install nasm หรือคำสั่งอื่นใดที่เกี่ยวข้องกับการแจกจ่ายของคุณ

ตอนนี้เปิดหน้าต่างเทอร์มินัลอีมูเลเตอร์แล้วป้อนคำสั่งต่อไปนี้:

Nasm -f เอลฟ์ -o myfirst.o myfirst.asm ld -m elf_i386 -o myfirst myfirst.o

คำสั่งแรกคือการสร้างไฟล์โค้ดอ็อบเจ็กต์ชื่อ myfirst.o โดยใช้ NASM (รูปแบบไฟล์ปฏิบัติการที่ใช้ใน Linux) คุณอาจถามว่า: "เหตุใดจึงสร้างไฟล์รหัสอ็อบเจ็กต์ เนื่องจากการสร้างไฟล์พร้อมคำแนะนำสำหรับโปรเซสเซอร์กลางที่ควรดำเนินการจะมีเหตุผลมากกว่า" คุณสามารถใช้ไฟล์ปฏิบัติการกับคำสั่ง CPU ในระบบปฏิบัติการตั้งแต่ยุค 80 ได้ แต่ระบบปฏิบัติการสมัยใหม่มีความต้องการไฟล์ปฏิบัติการมากกว่า ไบนารีของ ELF รวมถึงข้อมูลการดีบักและอนุญาตให้รหัสและข้อมูลแยกกันโดยมีส่วนแยกกันเพื่อป้องกันไม่ให้ข้อมูลในส่วนเหล่านั้นถูกเขียนทับ

ต่อมาในกระบวนการพิจารณาวิธีการเขียนโค้ดให้ทำงานโดยตรงกับฮาร์ดแวร์ (สำหรับระบบปฏิบัติการแบบมินิมัลลิสต์ของเรา) ซึ่งเป็นส่วนหนึ่งของบทความชุดนี้ เราจะให้ความสนใจกับไฟล์ไบนารี่ดังกล่าวพร้อมคำแนะนำจากโปรเซสเซอร์กลาง

มองเข้าไปในอดีต

ในขณะนี้ เรามีไฟล์ myfirst.o พร้อมโค้ดปฏิบัติการของโปรแกรมของเรา อย่างไรก็ตามกระบวนการสร้างโปรแกรมยังไม่แล้วเสร็จ เมื่อใช้ ld linker เราต้องเชื่อมโยงโค้ดจากไฟล์นี้กับโค้ดเริ่มต้นระบบพิเศษ (เช่น โค้ดสำเร็จรูปที่รันเมื่อแต่ละโปรแกรมถูกเรียกใช้งาน) เพื่อสร้าง ไฟล์ปฏิบัติการชื่อ myfirst (พารามิเตอร์ elf_i386 อธิบายประเภทของรูปแบบไบนารี - นิ้ว ในกรณีนี้ซึ่งหมายความว่าคุณสามารถใช้โค้ดแอสเซมบลี 32 บิตได้แม้ว่าคุณจะใช้การกระจายแบบ 64 บิตก็ตาม)

หากเป็นกระบวนการสร้าง โปรแกรมจะเกิดขึ้นสำเร็จแล้ว คุณจะสามารถรันโปรแกรมของคุณโดยใช้คำสั่งต่อไปนี้:

ด้วยเหตุนี้ คุณควรเห็นผลลัพธ์: "กฎการประกอบ!" ซึ่งหมายความว่าคุณบรรลุเป้าหมายแล้ว - คุณได้สร้างโปรแกรมอิสระเต็มรูปแบบสำหรับ Linux ซึ่งโค้ดนี้เขียนด้วยภาษาแอสเซมบลีทั้งหมด แน่นอน โปรแกรมนี้ไม่ได้ดำเนินการใดๆ การกระทำที่เป็นประโยชน์แต่ในขณะเดียวกันก็เป็นตัวอย่างที่ดีเยี่ยมในการสาธิตโครงสร้างของโปรแกรมในภาษาแอสเซมบลีและช่วยให้คุณสามารถติดตามกระบวนการแปลงได้ รหัสแหล่งที่มาไปยังไฟล์ไบนารี

ก่อนที่เราจะเจาะลึกโค้ด เป็นความคิดที่ดีที่จะทราบขนาดของไฟล์ไบนารี่ของโปรแกรมของเรา หลังจากรันคำสั่ง ls -l myfirst คุณจะเห็นว่าไฟล์ไบนารี่มีขนาดประมาณ 670 ไบต์ ทีนี้ลองประมาณขนาดของโปรแกรมที่เทียบเท่าในภาษา C:

#รวม int main() ( put("กฎการประกอบ!"); )

หากคุณบันทึกโค้ดนี้ในไฟล์ชื่อ test.c ให้คอมไพล์มัน (gcc -o test test.c ) และดูที่พารามิเตอร์ของไฟล์ไบนารี่ผลลัพธ์ที่เรียกว่า test คุณจะพบว่าไฟล์นี้มีข้อมูลมากมาย ขนาดใหญ่ขึ้น- 8.4k คุณสามารถลบออกจากไฟล์นี้ได้ ข้อมูลการดีบัก(strip -s test) แต่แม้หลังจากนี้ขนาดจะลดลงเล็กน้อยเหลือเพียง 6 k เท่านั้น อธิบายได้โดย คอมไพเลอร์ GCCเพิ่มโค้ดจำนวนมากที่กล่าวถึงข้างต้นเพื่อเริ่มและหยุดแอปพลิเคชัน และยังเชื่อมโยงแอปพลิเคชันกับไลบรารีภาษาการเขียนโปรแกรม C ขนาดใหญ่- ขอบคุณ ตัวอย่างนี้สรุปได้ง่ายว่าภาษาแอสเซมบลีเป็นภาษาโปรแกรมที่ดีที่สุดสำหรับการพัฒนาแอปพลิเคชันที่มีไว้สำหรับใช้ สภาวะที่ไม่เอื้ออำนวยข้อจำกัดความจุสื่อจัดเก็บข้อมูล

เป็นที่น่าสังเกตว่านักพัฒนาภาษาแอสเซมบลีจำนวนมากได้รับเงินเดือนที่ดีเยี่ยมสำหรับการพัฒนาโค้ดสำหรับอุปกรณ์ฝังตัวที่มีทรัพยากรจำกัด และนั่นคือเหตุผลว่าทำไมภาษาแอสเซมบลีจึงเป็นเพียงภาษาเดียว ตัวเลือกที่แท้จริงเพื่อพัฒนาเกมสำหรับคอนโซล 8 บิตรุ่นเก่าและคอมพิวเตอร์ที่บ้าน

การแยกส่วนรหัส

การพัฒนาโค้ดใหม่เป็นเรื่องสนุกแต่ยิ่งกว่านั้นอีก กิจกรรมที่น่าสนใจอาจกลายเป็นการค้นคว้าผลงานของคนอื่นได้ ด้วยเครื่องมือที่เรียกว่า objdump (จากแพ็คเกจ Binutils) คุณสามารถ "แยกส่วน" ไฟล์ปฏิบัติการได้นั่นคือแปลงคำสั่ง CPU ให้เป็นข้อความที่เทียบเท่ากัน ลองใช้เครื่องมือนี้กับไบนารีแรกของฉันที่เราทำงานในบทช่วยสอนนี้ เช่นนี้:

Objdump -d -M intel myfirst

คุณจะเห็นรายการคำแนะนำจากส่วนโค้ดของไฟล์ไบนารี่ ตัวอย่างเช่น คำสั่งแรกซึ่งเราใส่ข้อมูลเกี่ยวกับตำแหน่งของสตริงของเราในการลงทะเบียน ecx มีลักษณะดังนี้:

ย้าย ecx,0x80490a0

ในระหว่างกระบวนการประกอบ NASM ได้แทนที่ป้ายกำกับบรรทัด "ข้อความ" ด้วย ค่าตัวเลขซึ่งสอดคล้องกับตำแหน่งของบรรทัดนี้ในส่วนข้อมูลของไฟล์ไบนารี ดังนั้นผลลัพธ์ของการแยกส่วนไฟล์ไบนารี่จึงมีประโยชน์น้อยกว่าโค้ดต้นฉบับ เนื่องจากขาดสิ่งต่าง ๆ เช่น ความคิดเห็นและบรรทัด แต่ก็ยังมีประโยชน์ในการทำความคุ้นเคยกับการใช้งานฟังก์ชันที่มีความสำคัญต่อเวลาหรือทำลายระบบความปลอดภัยของแอปพลิเคชัน ตัวอย่างเช่น ในยุค 80 และ 90 นักพัฒนาจำนวนมากใช้เครื่องมือแยกชิ้นส่วนซอฟต์แวร์เพื่อระบุและทำให้ระบบป้องกันการคัดลอกเกมเป็นกลาง

คุณยังสามารถแยกส่วนโปรแกรมที่พัฒนาโดยใช้ภาษาการเขียนโปรแกรมอื่นได้ แต่ผลลัพธ์การแยกส่วนที่ได้อาจซับซ้อนกว่ามาก ตัวอย่างเช่น คุณสามารถรันคำสั่ง objdump ด้านบนกับไฟล์ไบนารี่ /bin/ls และประเมินส่วนโค้ดหลายพันบรรทัดที่สร้างโดยคอมไพเลอร์โดยอิงตามซอร์สโค้ด C ดั้งเดิมของยูทิลิตี้อย่างอิสระ

การวิเคราะห์โค้ด

ตอนนี้เรามาพูดถึงวัตถุประสงค์ของโค้ดแต่ละบรรทัดในโปรแกรมของเรากัน เริ่มต้นด้วยสองบรรทัดนี้:

ส่วน .text global _start

นี่ไม่ใช่คำสั่งของ CPU แต่เป็นคำสั่งของแอสเซมเบลอร์ นาสเอ็ม- คำสั่งแรกระบุว่าโค้ดด้านล่างควรอยู่ในส่วนโค้ด "ข้อความ" ของไฟล์ปฏิบัติการขั้นสุดท้าย สิ่งที่ไม่ชัดเจนเล็กน้อยคือความจริงที่ว่าส่วนที่เรียกว่า "ข้อความ" ไม่มีข้อมูลข้อความปกติ (เช่นบรรทัด "กฎการประกอบ!" ของเรา) แต่ รหัสปฏิบัติการเช่น คำแนะนำจากโปรเซสเซอร์กลาง ถัดไปคือคำสั่ง global _start ซึ่งจะบอก ld linker ว่าจุดใดที่โค้ดจากไฟล์ของเราควรเริ่มต้น คำสั่งนี้จะมีประโยชน์อย่างยิ่งหากเราต้องการเริ่มรันโค้ดไม่ใช่จากจุดเริ่มต้นของส่วนโค้ด แต่จากบางจุดที่กำหนด พารามิเตอร์โกลบอลอนุญาตให้อ่านคำสั่งนี้ไม่เพียงแต่โดยแอสเซมเบลอร์เท่านั้น แต่ยังอ่านโดยเครื่องมืออื่นๆ ด้วย ดังนั้นจึงถูกประมวลผลโดย ld linker

ตามที่กล่าวไว้ข้างต้น การเรียกใช้โค้ดจะต้องเริ่มต้นที่ตำแหน่ง _start ด้วยเหตุนี้ เราจึงระบุตำแหน่งที่เกี่ยวข้องในโค้ดของเราอย่างชัดเจน:

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

Mov ecx ข้อความ

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

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

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

เป็นรีจิสเตอร์แบบ 32 บิต (จึงสามารถเก็บตัวเลขได้ตั้งแต่ 0 ถึง 4,294,967,295) โดยการแก้ไข บรรทัดต่อไปนี้รหัสคุณจะเห็นว่าเรากำลังทำงานร่วมกับการลงทะเบียน edx , ebx และ eax ด้วย - เหล่านี้คือการลงทะเบียน จุดประสงค์ทั่วไปซึ่งสามารถนำไปใช้ในการทำงานใดๆ ก็ได้ ซึ่งแตกต่างจากการลงทะเบียนพิเศษที่เราจะได้เรียนรู้ เดือนหน้า- และนี่คือคำอธิบายเล็กน้อยสำหรับผู้ที่ต้องการทราบที่มาของชื่อรีจิสเตอร์: ecx register ถูกเรียกว่า c ในระหว่างการเปิดตัวโปรเซสเซอร์ 8 บิต หลังจากนั้นจึงเปลี่ยนชื่อเป็น cx เพื่อจัดเก็บค่า 16 บิตและ ecx สำหรับจัดเก็บค่า 32 บิต ดังนั้น แม้ว่าชื่อการลงทะเบียนในปัจจุบันจะดูแปลกไปเล็กน้อย แต่เมื่อย้อนกลับไปในสมัยของ CPU รุ่นเก่า นักพัฒนาก็ใช้การลงทะเบียนทั่วไปที่มีชื่อที่แตกต่างกัน a, b, c และ d

เมื่อคุณเริ่มต้นแล้ว คุณจะไม่สามารถหยุดได้

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

ลองนึกภาพว่าคุณมีถุง Pringles ที่ว่างเปล่า และคุณใส่สิ่งของต่างๆ ลงไปตามลำดับต่อไปนี้: แครกเกอร์ 2 ชั้น ชิป Alpha และแผ่นดิสก์ GameCube หากคุณเริ่มถอดสิ่งเหล่านี้ คุณจะต้องถอดดิสก์ GameCube ก่อน จากนั้นจึงถอดชิปอักขระอัลฟ่า และอื่นๆ เมื่อทำงานกับภาษาแอสเซมบลี สแต็กจะใช้ดังนี้:

กด 2 กด 5 กด 10 ป๊อป eax ป๊อป ebx ป๊อป ecx

หลังจากดำเนินการหกคำสั่งเหล่านี้แล้ว eax register จะมีค่า 10, ebx register จะมีค่า 5 และ ecx register จะมีค่าเป็น 2 ดังนั้น การใช้ stack จึงเป็นวิธีที่ยอดเยี่ยมในการปลดปล่อยชั่วคราว ลงทะเบียน; ตัวอย่างเช่น หากมีค่าที่สำคัญในรีจิสเตอร์ eax และ ebx แต่คุณจำเป็นต้องทำงานปัจจุบันก่อนที่จะประมวลผล คุณสามารถส่งค่าเหล่านั้นลงบนสแต็ก ทำงานปัจจุบัน และนำค่าเหล่านั้นออกจาก กองกลับไปที่ สถานะก่อนหน้าลงทะเบียน

นอกจากนี้ สแต็กยังใช้เมื่อเรียกรูทีนย่อยเพื่อจัดเก็บที่อยู่ผู้ส่งไปยังโค้ดหลัก ด้วยเหตุนี้ คุณต้องใช้ความระมัดระวังเป็นพิเศษเมื่อทำงานกับสแต็ก - หากคุณเขียนทับข้อมูลที่เก็บไว้ในนั้น คุณจะไม่สามารถกลับไปยังตำแหน่งก่อนหน้าในโค้ดหลักของแอปพลิเคชันได้ โดยตั้งค่าตัวเองให้เป็นแบบทางเดียว ชน!

เดินหน้าต่อไป

กลับไปที่โค้ด: คำสั่ง mov จะย้าย (จริงๆ แล้วเป็นการคัดลอก) ตัวเลขจากที่หนึ่งไปอีกที่หนึ่ง จากขวาไปซ้าย ดังนั้นในกรณีนี้ เรากำลังพูดว่า "ควรวางข้อความไว้ใน ecx register" แต่ "ข้อความ" คืออะไร? นี่ไม่ใช่การลงทะเบียนอื่น แต่เป็นตัวชี้ไปยังตำแหน่งของข้อมูล ใกล้กับจุดสิ้นสุดของโค้ด ในส่วน "ข้อมูล" คุณจะพบป้ายกำกับข้อความตามด้วยพารามิเตอร์ db ซึ่งระบุว่าควรวางไบต์สองสามไบต์ในโค้ดแทนที่จะเป็นป้ายกำกับข้อความ สะดวกมากเนื่องจากเราไม่จำเป็นต้องทราบตำแหน่งที่แน่นอนของบรรทัด "กฎการชุมนุม!" ในส่วนข้อมูล - เราสามารถอ้างอิงได้โดยใช้ป้ายกำกับข้อความ (เลข 10 หลังเส้นของเราเป็นเพียงสัญลักษณ์ของการเคลื่อนตัวไป) บรรทัดใหม่คล้ายกับอักขระ \n ที่เพิ่มในสตริงเมื่อทำงานกับภาษาการเขียนโปรแกรม C)

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

ดังนั้นเราจะขอให้เคอร์เนลระบบปฏิบัติการทำสิ่งนี้ให้เรา เคอร์เนลลินุกซ์ทำให้สามารถใช้งานได้กับแอปพลิเคชันระดับต่ำ จำนวนมากการเรียกระบบซึ่งแอปพลิเคชันสามารถเริ่มต้นการดำเนินการต่างๆ ในระดับเคอร์เนลได้ หนึ่งในการเรียกของระบบเหล่านี้มีไว้สำหรับเอาต์พุต สตริงข้อความ- เมื่อใช้การเรียกระบบนี้ เคอร์เนล OS จะทำงานที่จำเป็นทั้งหมด - และแน่นอนว่า มันให้ระดับนามธรรมที่ลึกยิ่งขึ้นอีก โดยที่บรรทัดสามารถส่งออกโดยเทอร์มินัลข้อความปกติ โปรแกรมจำลองเทอร์มินัลระบบ X window หรือ แม้กระทั่งเขียนลงในไฟล์ที่เปิดไว้ก่อนหน้านี้

อย่างไรก็ตาม ก่อนที่เราจะบอกให้เคอร์เนล OS พิมพ์สตริงข้อความ เราจะต้องบอกก่อน ข้อมูลเพิ่มเติมนอกเหนือจากข้อมูลเกี่ยวกับตำแหน่งของสตริงที่มีอยู่ในการลงทะเบียน ecx แล้ว เราจะต้องบอกด้วยว่าต้องส่งออกอักขระจำนวนเท่าใด เพื่อไม่ให้เอาต์พุตของบรรทัดดำเนินต่อไปหลังจากสิ้นสุด นี่คือสิ่งที่บรรทัดจากส่วนข้อมูลใกล้กับส่วนท้ายของโค้ดแอปพลิเคชันใช้สำหรับ:

ความยาวเท่ากับ $ - ข้อความ

บรรทัดนี้ใช้ป้ายกำกับความยาวที่แตกต่างกัน แต่แทนที่จะใช้พารามิเตอร์ db เพื่อเชื่อมโยงป้ายกำกับนั้นกับข้อมูลบางอย่าง เราใช้พารามิเตอร์ equ เพื่อระบุว่าป้ายกำกับนี้เทียบเท่ากับบางสิ่งบางอย่าง (ซึ่งคล้ายกับ #define preprocessor directive ใน C ภาษาโปรแกรม) เครื่องหมายดอลลาร์สอดคล้องกับตำแหน่งปัจจุบันในโค้ด ดังนั้นในกรณีนี้ เราพูดว่า "ป้ายกำกับความยาวต้องเทียบเท่ากับตำแหน่งปัจจุบันในโค้ดลบด้วยตำแหน่งของบรรทัดที่มีป้ายกำกับว่า "ข้อความ""

กลับไปที่ส่วนของรหัสแอปพลิเคชันที่เราวางไว้ มูลค่าที่กำหนดในทะเบียน edx:

mov edx ความยาว

ทุกอย่างเป็นไปด้วยดี: รีจิสเตอร์สองตัวเต็มไปด้วยข้อมูลเกี่ยวกับตำแหน่งของบรรทัดและจำนวนอักขระของบรรทัดที่จะส่งออก แต่ก่อนที่เราจะบอกให้เคอร์เนล OS ทำงานส่วนหนึ่งของงาน เราต้องให้ข้อมูลเพิ่มเติมอีกเล็กน้อย ขั้นแรก เราต้องบอกเคอร์เนลระบบปฏิบัติการว่าควรใช้ "ตัวจัดการไฟล์" ใด - กล่าวอีกนัยหนึ่งคือควรกำหนดทิศทางเอาต์พุตไว้ที่ใด หัวข้อนี้นั้นอยู่นอกเหนือขอบเขตของคู่มือภาษาแอสเซมบลี ดังนั้นสมมติว่าเราจำเป็นต้องใช้เอาต์พุตสตรีมมาตรฐาน stdout ซึ่งหมายความว่า: พิมพ์สตริงลงบนหน้าจอ สตรีมมาตรฐานเอาต์พุตใช้หมายเลขอ้างอิงคงที่เป็น 1 ซึ่งเราใส่ไว้ในรีจิสเตอร์ ebx

ขณะนี้เราใกล้จะทำการเรียกระบบแล้ว แต่ยังต้องมีการลงทะเบียนอีกหนึ่งรายการที่ต้องกรอก เคอร์เนล OS สามารถดำเนินการต่างๆ จำนวนมาก เช่น การติดตั้งระบบไฟล์ การอ่านข้อมูลจากไฟล์ การลบไฟล์ และอื่นๆ กลไกที่เกี่ยวข้องถูกเปิดใช้งานโดยใช้การเรียกของระบบที่กล่าวถึง และก่อนที่เราจะถ่ายโอนการควบคุมไปยังเคอร์เนล OS เราจะต้องบอกว่าควรใช้การเรียกของระบบใด ในหน้านี้คุณจะพบข้อมูลเกี่ยวกับการโทรของระบบบางอย่าง ใช้ได้กับโปรแกรมต่างๆ- ในกรณีของเราจำเป็น การโทรของระบบ sys_write ("เขียนข้อมูลไปยัง file descriptor") ด้วยหมายเลข 4 ดังนั้นเราจะวางหมายเลขไว้ใน eax register:

และนั่นคือทั้งหมด! เราได้เตรียมการที่จำเป็นทั้งหมดสำหรับการเรียกระบบ ดังนั้นตอนนี้เราจะถ่ายโอนการควบคุมไปยังเคอร์เนล OS ดังต่อไปนี้:

คำสั่ง int ย่อมาจาก "interrrupt" และขัดจังหวะการทำงานของโปรแกรมที่กำหนดอย่างแท้จริง โดยย้ายเข้าสู่พื้นที่เคอร์เนลของ OS (ในกรณีนี้คือใช้ ค่าเลขฐานสิบหก 0x80 - คุณไม่จำเป็นต้องกังวลเกี่ยวกับมันในตอนนี้) เคอร์เนล OS จะพิมพ์สตริงที่ชี้ไปตามค่าในรีจิสเตอร์ ecx จากนั้นจึงส่งคืนการควบคุมไปยังโปรแกรมของเรา

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

เราได้กล่าวถึงบางแง่มุมของการใช้ภาษาแอสเซมบลีในบทช่วยสอนนี้ และตามที่กล่าวไว้ข้างต้น เราสามารถมุ่งเน้นไปที่เพียงเท่านั้นแทน ข้อมูลทางทฤษฎี- แต่ฉันก็ยังหวังอย่างนั้น ตัวอย่างจริงคุณพบว่าโปรแกรมนี้มีประโยชน์ และในนิตยสารฉบับหน้า เราจะใช้เวลามากขึ้นในการอธิบายแนวคิดบางส่วนที่จะกล่าวถึงในคู่มือนี้ นอกจากนี้ เราจะปรับปรุงโปรแกรมของเราโดยการเพิ่มตรรกะและรูทีนย่อย - คำสั่ง if และ goto เวอร์ชันภาษาแอสเซมบลี

ในขณะที่ทำความคุ้นเคยกับโค้ดของโปรแกรมนี้ คุณสามารถลองแก้ไขด้วยตัวเองเพื่อดำเนินการต่อไปนี้:

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

หากคุณประสบปัญหาและต้องการความช่วยเหลือ โปรดไปที่ฟอรั่มของเราที่ http://forums.linuxvoice.com ผู้เขียนคู่มือนี้จะอยู่ใกล้ๆ และยินดีที่จะแนะนำคุณในเส้นทางที่ถูกต้อง ขอให้มีความสุขในการเขียนโปรแกรม!

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

ในบทช่วยสอนนี้ เราจะพูดถึง opcode ต่างๆ ที่เกี่ยวข้องกับการคำนวณ การดำเนินการระดับบิต ฯลฯ opcodes อื่นๆ: กระโดด เปรียบเทียบ ฯลฯ คำแนะนำจะมีการหารือในภายหลัง

ความคิดเห็นในโปรแกรมของคุณจะทิ้งไว้หลังเครื่องหมายอัฒภาค เช่นเดียวกับใน Delphi หรือ C ผ่าน //

ตัวเลขในภาษาแอสเซมบลีสามารถแสดงในรูปแบบไบนารี ทศนิยม หรือเลขฐานสิบหกได้ หากต้องการแสดงว่าใช้หมายเลขในระบบใด คุณต้องวางตัวอักษรไว้หลังตัวเลข สำหรับระบบไบนารี่จะมีการเขียนตัวอักษร b (ตัวอย่าง: 0000010b, 001011010b) สำหรับระบบทศนิยมคุณไม่สามารถระบุสิ่งใดหลังตัวเลขหรือระบุตัวอักษร d (ตัวอย่าง: 4589, 2356d) สำหรับ ระบบเลขฐานสิบหกคุณต้องระบุตัวอักษร h เลขฐานสิบหกจำเป็นต้องเขียนด้วยศูนย์ที่จุดเริ่มต้น (ตัวอย่าง: 00889h, 0AC45h, 056Fh, F145Ch, C123h ไม่ถูกต้อง)

คำสั่งแรกสุดจะเป็น MOV ที่รู้จักกันดีคำสั่งนี้ใช้เพื่อคัดลอก (ละเว้นชื่อคำสั่ง) ค่าจากที่หนึ่งไปยังอีกที่หนึ่ง "ตำแหน่ง" นี้อาจเป็นรีจิสเตอร์ ตำแหน่งหน่วยความจำ หรือค่าทันที (ตราบเท่าที่ มูลค่าเดิม- ไวยากรณ์คำสั่ง:

ตัวรับ Mov, แหล่งที่มา

คุณสามารถคัดลอกค่าจากรีจิสเตอร์หนึ่งไปยังอีกรีจิสเตอร์หนึ่งได้

ย้าย edx, ecx

คำสั่งดังกล่าวคัดลอกเนื้อหาของ ecx ไปยัง edx ขนาดของแหล่งกำเนิดและตัวรับสัญญาณจะต้องเท่ากัน

ตัวอย่างเช่น: คำสั่งนี้ไม่ถูกต้อง:

ย้ายอัล, ecx; ผิด

opcode นี้พยายามปรับค่า DWORD (32 บิต) ให้พอดีกับไบต์ (8 บิต) ไม่สามารถทำได้ด้วยคำสั่ง mov (มีคำสั่งอื่นสำหรับสิ่งนี้)

และคำสั่งเหล่านี้ถูกต้องเนื่องจากต้นทางและปลายทางมีขนาดไม่แตกต่างกัน:

Mov อัล, bl mov cl, dl mov cx, dx mov ecx, ebx

คุณยังสามารถรับค่าจากหน่วยความจำและใส่ลงในรีจิสเตอร์ได้ ตัวอย่างเช่น ใช้วงจรหน่วยความจำต่อไปนี้:

อคติ34 35 36 37 38 39 3เอ3B3ซี3 มิติ3อี3เอฟ40 41 42
ข้อมูล0D0เอ50 32 44 57 25 7เอ5E72 อีเอฟ7Dเอฟเอฟค.ศC7
(แต่ละบล็อกแทนไบต์)

ค่าออฟเซ็ตจะแสดงที่นี่เป็นไบต์ แต่ในความเป็นจริงแล้ว ค่าออฟเซ็ตจะเป็นค่า 32 บิต ลองใช้ 3A เป็นตัวอย่าง ซึ่งก็คือค่า 32 บิตเช่นกัน: 0000003Ah เพียงเพื่อประหยัดพื้นที่ บางคนใช้การชดเชยเล็กน้อย

ดูออฟเซ็ต 3A ในตารางด้านบน ข้อมูลที่ออฟเซ็ตนี้คือ 25, 7A, 5E, 72, EF เป็นต้น หากต้องการใส่ค่าออฟเซ็ต 3A ลงในรีจิสเตอร์ คุณยังใช้คำสั่ง mov:

ย้าย eax, dword ptr

หมายถึง: วางค่าที่มีขนาด DWORD (32 บิต) จากหน่วยความจำที่ออฟเซ็ต 3Ah ลงใน eax register หลังจากรันคำสั่งนี้ eax จะมีค่า 725E7A25h คุณอาจสังเกตเห็นว่านี่คือสิ่งที่ตรงกันข้ามกับสิ่งที่อยู่ในหน่วยความจำ: 25 7A 5E 72 เนื่องจากค่าต่างๆ จะถูกเก็บไว้ในหน่วยความจำโดยใช้รูปแบบ endian เล็กน้อย ซึ่งหมายความว่าไบต์ที่มีนัยสำคัญน้อยที่สุดจะถูกจัดเก็บไว้ในไบต์ที่สำคัญที่สุด: เรียงกลับไปด้านหน้า ฉันคิดว่าตัวอย่างเหล่านี้จะแสดง:

  • ค่า dword (32 บิต) 10203040 เลขฐานสิบหกถูกเก็บไว้ในหน่วยความจำเป็น: 40, 30, 20, 10
  • ค่า word (16 บิต) เลขฐานสิบหก 4050 ถูกเก็บไว้ในหน่วยความจำเป็น: 50, 40

กลับไปที่ตัวอย่างข้างต้น คุณสามารถทำเช่นนี้กับขนาดอื่นๆ ได้ด้วย:

Mov cl, ไบต์ ptr ; cl จะได้รับค่า 0Dh mov dx, คำว่า ptr ; dx จะได้ค่า 7DEFh

คุณคงเข้าใจแล้วว่าคำนำหน้า ptr หมายความว่าคุณต้องดึงขนาดที่แน่นอนจากหน่วยความจำ และคำนำหน้าก่อน ptr ระบุขนาดข้อมูล:

ไบต์ - 1 ไบต์ Word - 2 ไบต์ Dword - 4 ไบต์

บางครั้งอาจไม่ได้ระบุขนาด:

ย้ายเอ้าซ์,

เนื่องจาก eax เป็นรีจิสเตอร์แบบ 32 บิต แอสเซมเบลอร์จึงรู้ว่าต้องใช้ค่า 32 บิตด้วย ในกรณีนี้จากหน่วยความจำออฟเซ็ต 403045h

คุณยังสามารถใช้ค่าโดยตรงได้:

มูฟ เอ็ดเอ็กซ์, 5006 ชม

คำสั่งนี้จะเขียนลงในรีจิสเตอร์ edx ค่า 5006 วงเล็บ [ และ ] ใช้ในการรับค่าจากหน่วยความจำ (ออฟเซ็ตอยู่ในวงเล็บ) หากไม่มีวงเล็บ ก็เป็นเพียงค่าในทันที

คุณยังสามารถใช้รีจิสเตอร์เป็นตำแหน่งหน่วยความจำได้ (ต้องเป็น 32 บิตในโปรแกรม 32 บิต):

ย้าย eax, 403045h; เขียนถึง eax ค่า 403045 mov cx, ; ใส่ค่า ( ขนาดคำ) จากความทรงจำ; ระบุใน EAX (403045)

ใน mov cx, ขั้นแรกโปรเซสเซอร์จะดูค่า (=เซลล์หน่วยความจำ) ที่มี eax จากนั้นค่าใดที่อยู่ในเซลล์หน่วยความจำนั้น และใส่ค่านั้น (คำ 16 บิต เนื่องจากปลายทาง cx เป็นแบบ 16 บิต ลงทะเบียน) ใน CX

การดำเนินการสแต็ก - PUSH, POP ก่อนที่จะบอกคุณเกี่ยวกับการดำเนินการของสแต็ก ฉันได้อธิบายให้คุณฟังแล้วว่าสแต็กคืออะไร สแต็กเป็นพื้นที่ในหน่วยความจำที่รีจิสเตอร์สแต็ก ESP ชี้ไป สแต็กเป็นสถานที่สำหรับจัดเก็บที่อยู่ผู้ส่งและค่าชั่วคราว มีสองคำสั่งสำหรับการพุชค่าลงบนสแต็กและแตกค่าออกจากสแต็ก: PUSH และ POP คำสั่ง PUSH จะส่งค่าไปยังสแต็ก เช่น วางค่าในตำแหน่งหน่วยความจำที่รีจิสเตอร์ ESP ชี้ไป หลังจากนั้นค่าของรีจิสเตอร์ ESP จะเพิ่มขึ้น 4 คำสั่ง Pop จะแสดงค่าจากสแต็ก เช่น ป๊อปค่าจากตำแหน่งหน่วยความจำที่รีจิสเตอร์ ESP ชี้ไป จากนั้นลดค่าของรีจิสเตอร์ ESP ลง 4 ค่าสุดท้ายที่ผลักลงบนสแต็กจะถูกป๊อปก่อน เมื่อค่าถูกผลักลงบนสแต็ก ตัวชี้สแต็กจะลดลง และเมื่อมีการป๊อปค่า ค่านั้นจะเพิ่มขึ้น ลองดูตัวอย่าง:

(1) mov ecx, 100 (2) mov eax, 200 (3) ดัน ecx ; บันทึก ecx (4) push eax (5) xor ecx, eax (6) เพิ่ม ecx, 400 (7) mov edx, ecx (8) pop ebx (9) pop ecx

  • 1: ใส่ 100 ใน ecx
  • 2: ใส่ 200 ใน eax
  • 3: ดันค่าจาก ecx (=100) ลงบนสแต็ก (กดก่อน)
  • 4: ดันค่าจาก eax (=200) ลงบนสแต็ก (กดสุดท้าย)
  • 5/6/7: ดำเนินการกับ ecx ค่าในการเปลี่ยนแปลง ecx
  • 8: ป๊อปค่าจากสแต็กลงใน ebx: ebx จะกลายเป็น 200 (ผลักครั้งสุดท้าย, โผล่ครั้งแรก)
  • 9: ป๊อปค่าจากสแต็กลงใน ecx: ecx จะกลายเป็น 100 อีกครั้ง (กดครั้งแรก, โผล่ครั้งสุดท้าย)

หากต้องการดูว่าเกิดอะไรขึ้นในหน่วยความจำเมื่อมีการวางค่าและป๊อปอัปบนสแต็ก ดูรูปด้านล่าง:

(สแต็กที่นี่เต็มไปด้วยเลขศูนย์ แต่นั่นไม่ใช่กรณีนี้จริงๆ) ESP อยู่ในตำแหน่งที่ชี้ไป)

Mov ax, 4560h push ax mov cx, FFFFh push cx pop edx edx ตอนนี้เป็น 4560FFFFh

การเรียกรูทีนย่อยและกลับมา - CALL, RET คำสั่ง call ถ่ายโอนการควบคุมไปยังขั้นตอนใกล้หรือไกล โดยจัดเก็บที่อยู่จุดส่งกลับไว้ในสแต็ก คำสั่ง ret ส่งคืนการควบคุมจากโพรซีเดอร์ไปยังโปรแกรมที่เรียกใช้ และรับที่อยู่ผู้ส่งคืนจากสแต็ก ตัวอย่าง:

รหัส.. โทร 0455659 ..รหัสเพิ่มเติม.. รหัสจากที่อยู่ 455659: เพิ่ม eax, 500 mul eax, edx ret

เมื่อดำเนินการคำสั่งการโทร ตัวประมวลผลจะถ่ายโอนการควบคุมไปยังโค้ดที่อยู่ 455659 และดำเนินการจนกระทั่งคำสั่ง ret จากนั้นจึงส่งคืนการควบคุมไปยังคำสั่งหลังจากการเรียก รหัสที่เรียกโดยคำสั่ง call เรียกว่าโพรซีเดอร์ คุณสามารถใส่โค้ดที่คุณใช้บ่อยในโพรซีเดอร์และเรียกมันทุกครั้งที่คุณต้องการด้วยคำสั่งการโทร

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

กด value_1 กด value_2 ขั้นตอนการโทร

ภายในโพรซีเดอร์ อาร์กิวเมนต์สามารถอ่านจากสแต็กและนำไปใช้ได้ ตัวแปรท้องถิ่น เช่น ข้อมูลที่จำเป็นเฉพาะภายในขั้นตอนสามารถจัดเก็บไว้ในสแต็กได้ ฉันจะไม่ลงรายละเอียดเกี่ยวกับเรื่องนี้เพราะสามารถทำได้ง่ายในแอสเซมเบลอร์ MASM และ TASM เพียงจำไว้ว่าคุณสามารถสร้างขั้นตอนและสามารถใช้พารามิเตอร์ได้

หนึ่ง โน๊ตสำคัญ: eax register มักใช้เพื่อจัดเก็บผลลัพธ์ของโพรซีเดอร์เกือบทุกครั้ง

นอกจากนี้ยังใช้กับฟังก์ชันของ Windows ด้วย แน่นอนว่าคุณสามารถใช้กรณีอื่นในขั้นตอนของคุณเองได้ แต่นี่คือมาตรฐาน

นั่นคือจุดสิ้นสุดของบทเรียนอื่น ในบทต่อไป เราจะเขียนโปรแกรมแรกเป็นภาษาแอสเซมบลี

กลับ | สารบัญ |

ทิ้งข้อความไว้

ความคิดเห็น

สแต็กจะขยายไปที่ด้านล่างและยิ่งมีขนาดใหญ่ ESP ก็จะยิ่งเล็กลง

แล้วแบบนี้ล่ะ?:
- เหมือนกองที่ว่างเปล่า
วี
- |อีเอสพี
| |6
| |5
| |4
| |3
| |2
| |1
| |0

หลังโค้ด
กด 0fh
กด 0ffh

- ประเภทสแต็ก
วี
|0ฟฮ|7
|0ffh|6
- |อีเอสพี
| |4
| |3
| |2
| |1
| |0

ถ้าเป็นเช่นนั้น
ESP หมายถึงอะไรเมื่อสแต็กว่างเปล่า