การประกอบ - คู่มือฉบับย่อ

ภาษาแอสเซมบลีคืออะไร?

คอมพิวเตอร์ส่วนบุคคลแต่ละเครื่องมีไมโครโปรเซสเซอร์ที่จัดการกิจกรรมทางคณิตศาสตร์ตรรกะและการควบคุมของคอมพิวเตอร์

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

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

ข้อดีของภาษาแอสเซมบลี

การมีความเข้าใจในภาษาแอสเซมบลีทำให้รู้เรื่อง -

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

ข้อดีอื่น ๆ ของการใช้ภาษาแอสเซมบลีคือ -

  • ต้องใช้หน่วยความจำและเวลาดำเนินการน้อยลง

  • ช่วยให้งานที่ซับซ้อนเฉพาะฮาร์ดแวร์ทำได้ง่ายขึ้น

  • เหมาะสำหรับงานที่มีเวลาสำคัญ

  • เหมาะที่สุดสำหรับการเขียนรูทีนบริการขัดจังหวะและโปรแกรมที่อาศัยหน่วยความจำอื่น ๆ

คุณสมบัติพื้นฐานของฮาร์ดแวร์พีซี

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

หน่วยพื้นฐานของการจัดเก็บคอมพิวเตอร์เป็นบิต อาจเป็น ON (1) หรือ OFF (0) และกลุ่มของ 8 บิตที่เกี่ยวข้องจะสร้างไบต์บนคอมพิวเตอร์สมัยใหม่ส่วนใหญ่

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

โปรเซสเซอร์รองรับขนาดข้อมูลต่อไปนี้ -

  • Word: รายการข้อมูล 2 ไบต์
  • Doubleword: รายการข้อมูล 4 ไบต์ (32 บิต)
  • Quadword: รายการข้อมูล 8 ไบต์ (64 บิต)
  • ย่อหน้า: พื้นที่ 16 ไบต์ (128 บิต)
  • กิโลไบต์: 1024 ไบต์
  • เมกะไบต์: 1,048,576 ไบต์

ระบบเลขฐานสอง

ระบบตัวเลขทุกระบบใช้สัญกรณ์ตำแหน่งกล่าวคือแต่ละตำแหน่งที่เขียนตัวเลขจะมีค่าตำแหน่งต่างกัน แต่ละตำแหน่งคือพลังของฐานซึ่งก็คือ 2 สำหรับระบบเลขฐานสองและพลังเหล่านี้เริ่มต้นที่ 0 และเพิ่มขึ้นทีละ 1

ตารางต่อไปนี้แสดงค่าตำแหน่งสำหรับเลขฐานสอง 8 บิตโดยที่บิตทั้งหมดถูกตั้งค่าเป็น ON

ค่าบิต 1 1 1 1 1 1 1 1
ค่าตำแหน่งเป็นกำลังของฐาน 2 128 64 32 16 8 4 2 1
หมายเลขบิต 7 6 5 4 3 2 1 0

ค่าของเลขฐานสองขึ้นอยู่กับการมีอยู่ของ 1 บิตและค่าตำแหน่ง ดังนั้นค่าของเลขฐานสองที่ระบุคือ -

1 + 2 + 4 + 8 +16 + 32 + 64 + 128 = 255

ซึ่งเหมือนกับ 2 8 - 1

ระบบเลขฐานสิบหก

ระบบเลขฐานสิบหกใช้ฐาน 16 ตัวเลขในระบบนี้มีตั้งแต่ 0 ถึง 15 ตามแบบแผนตัวอักษร A ถึง F ใช้แทนเลขฐานสิบหกที่สอดคล้องกับค่าทศนิยม 10 ถึง 15

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

เลขฐานสิบ การแสดงไบนารี การแสดงเลขฐานสิบหก
0 0 0
1 1 1
2 10 2
3 11 3
4 100 4
5 101 5
6 110 6
7 111 7
8 1,000 8
9 1001 9
10 1010
11 1011
12 1100
13 1101
14 1110
15 1111

ในการแปลงเลขฐานสองให้เทียบเท่าเลขฐานสิบหกให้แบ่งออกเป็นกลุ่ม 4 กลุ่มติดต่อกันโดยเริ่มจากทางขวาและเขียนกลุ่มเหล่านั้นทับตัวเลขที่สอดคล้องกันของเลขฐานสิบหก

Example - เลขฐานสอง 1000 1100 1101 0001 เทียบเท่ากับเลขฐานสิบหก - 8CD1

ในการแปลงเลขฐานสิบหกเป็นเลขฐานสองเพียงแค่เขียนเลขฐานสิบหกแต่ละหลักลงในเลขฐานสองที่เทียบเท่ากัน 4 หลัก

Example - เลขฐานสิบหก FAD8 เทียบเท่ากับเลขฐานสอง - 1111 1010 1101 1000

เลขคณิตไบนารี

ตารางต่อไปนี้แสดงกฎง่ายๆสี่ข้อสำหรับการบวกไบนารี -

(ผม) (ii) (สาม) (iv)
1
0 1 1 1
+0 +0 +1 +1
= 0 = 1 = 10 = 11

กฎ (iii) และ (iv) แสดงการพก 1 บิตในตำแหน่งซ้ายถัดไป

Example

ทศนิยม ไบนารี่
60 00111100
+42 00101010
102 01100110

ค่าไบนารีที่เป็นลบจะแสดงเป็น two's complement notation. ตามกฎนี้การแปลงเลขฐานสองเพื่อค่าลบของมันคือการกลับค่าบิตและเพิ่ม 1

Example

หมายเลข 53 00110101
ย้อนกลับบิต 11001010
เพิ่ม 1 0000000 1
หมายเลข -53 11001011

ลบค่าหนึ่งจากอีกแปลงหมายเลขที่ถูกหักออกไปเป็นรูปแบบที่สมบูรณ์สองและเพิ่มตัวเลข

Example

ลบ 42 จาก 53

หมายเลข 53 00110101
หมายเลข 42 00101010
ย้อนกลับบิตของ 42 11010101
เพิ่ม 1 0000000 1
หมายเลข -42 11010110
53 - 42 = 11 00001011

ส่วนเกินของ 1 บิตสุดท้ายจะหายไป

การกำหนดแอดเดรสข้อมูลในหน่วยความจำ

กระบวนการที่โปรเซสเซอร์ควบคุมการดำเนินการตามคำสั่งเรียกว่า fetch-decode-execute cycle หรือ execution cycle. ประกอบด้วยสามขั้นตอนต่อเนื่อง -

  • ดึงคำสั่งจากหน่วยความจำ
  • การถอดรหัสหรือระบุคำสั่ง
  • ดำเนินการตามคำสั่ง

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

โปรเซสเซอร์จัดเก็บข้อมูลในลำดับไบต์ย้อนกลับกล่าวคือไบต์ลำดับต่ำจะถูกเก็บไว้ในแอดเดรสหน่วยความจำต่ำและไบต์ลำดับสูงในแอดเดรสหน่วยความจำสูง ดังนั้นหากโปรเซสเซอร์นำค่า 0725H จากรีจิสเตอร์ไปยังหน่วยความจำมันจะโอน 25 อันดับแรกไปยังที่อยู่หน่วยความจำล่างและ 07 ไปยังที่อยู่หน่วยความจำถัดไป

x: ที่อยู่หน่วยความจำ

เมื่อโปรเซสเซอร์รับข้อมูลตัวเลขจากหน่วยความจำเพื่อรีจิสเตอร์ข้อมูลจะกลับไบต์อีกครั้ง ที่อยู่หน่วยความจำมีสองประเภท -

  • ที่อยู่สัมบูรณ์ - การอ้างอิงโดยตรงของตำแหน่งเฉพาะ

  • ที่อยู่เซ็กเมนต์ (หรือออฟเซ็ต) - ที่อยู่เริ่มต้นของเซ็กเมนต์หน่วยความจำที่มีค่าออฟเซ็ต

การตั้งค่าสภาพแวดล้อมท้องถิ่น

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

  • IBM PC หรือคอมพิวเตอร์ที่เข้ากันได้เทียบเท่า
  • สำเนาของระบบปฏิบัติการ Linux
  • สำเนาของโปรแกรมแอสเซมเบลอร์ NASM

มีโปรแกรมแอสเซมเบลอร์ที่ดีมากมายเช่น -

  • Microsoft Assembler (MASM)
  • บอร์แลนด์เทอร์โบแอสเซมเบลอร์ (TASM)
  • แอสเซมเบลอร์ GNU (GAS)

เราจะใช้แอสเซมเบลอร์ NASM อย่างที่เป็น -

  • ฟรี. คุณสามารถดาวน์โหลดได้จากแหล่งเว็บต่างๆ
  • จัดทำเอกสารอย่างดีและคุณจะได้รับข้อมูลมากมายบนเน็ต
  • สามารถใช้ได้ทั้งบน Linux และ Windows

การติดตั้ง NASM

หากคุณเลือก "Development Tools" ขณะติดตั้ง Linux คุณอาจได้รับ NASM ติดตั้งพร้อมกับระบบปฏิบัติการ Linux และคุณไม่จำเป็นต้องดาวน์โหลดและติดตั้งแยกต่างหาก สำหรับการตรวจสอบว่าคุณได้ติดตั้ง NASM แล้วให้ทำตามขั้นตอนต่อไปนี้ -

  • เปิดเทอร์มินัล Linux

  • ประเภท whereis nasm และกด ENTER

  • หากติดตั้งไว้แล้วจะมีบรรทัดเช่นnasm: / usr / bin / nasmปรากฏขึ้น มิฉะนั้นคุณจะเห็นเพียงแค่nasm:จากนั้นคุณต้องติดตั้ง NASM

ในการติดตั้ง NASM ให้ทำตามขั้นตอนต่อไปนี้ -

  • ตรวจสอบเว็บไซต์ netwide แอสเซมเบลอร์ (NASM)สำหรับเวอร์ชันล่าสุด

  • ดาวน์โหลดไฟล์เก็บถาวรซอร์สของ Linux nasm-X.XX.ta.gzโดยที่X.XXหมายเลขเวอร์ชัน NASM ในไฟล์เก็บถาวร

  • nasm-X. XXแกะเก็บลงในไดเรกทอรีที่สร้างไดเรกทอรีย่อยเป็น

  • cd ถึงnasm-X.XXและพิมพ์./configure. เชลล์สคริปต์นี้จะค้นหาคอมไพเลอร์ C ที่ดีที่สุดเพื่อใช้และตั้งค่า Makefiles ตามนั้น

  • ประเภท make เพื่อสร้างไบนารี nasm และ ndisasm

  • ประเภท make install เพื่อติดตั้ง nasm และ ndisasm ใน / usr / local / bin และเพื่อติดตั้ง man pages

สิ่งนี้ควรติดตั้ง NASM ในระบบของคุณ หรือคุณสามารถใช้การกระจาย RPM สำหรับ Fedora Linux เวอร์ชันนี้ติดตั้งง่ายกว่าเพียงดับเบิลคลิกที่ไฟล์ RPM

โปรแกรมการประกอบสามารถแบ่งออกเป็นสามส่วน -

  • data มาตรา,

  • bss ส่วนและ

  • text มาตรา.

ข้อมูลมาตรา

dataส่วนใช้สำหรับการประกาศข้อมูลเริ่มต้นหรือค่าคงที่ ข้อมูลนี้ไม่เปลี่ยนแปลงขณะรันไทม์ คุณสามารถประกาศค่าคงที่ชื่อไฟล์หรือขนาดบัฟเฟอร์ ฯลฯ ได้ในส่วนนี้

ไวยากรณ์สำหรับการประกาศส่วนข้อมูลคือ -

section.data

bssมาตรา

bssส่วนใช้สำหรับการประกาศตัวแปร ไวยากรณ์สำหรับการประกาศส่วน bss คือ -

section.bss

ข้อความส่วน

textใช้สำหรับเก็บรหัสจริง ส่วนนี้ต้องเริ่มต้นด้วยการประกาศglobal _startซึ่งจะบอกเคอร์เนลที่การเรียกใช้โปรแกรมเริ่มต้นขึ้น

ไวยากรณ์สำหรับการประกาศส่วนข้อความคือ -

section.text
   global _start
_start:

ความคิดเห็น

ข้อคิดเห็นภาษาแอสเซมบลีเริ่มต้นด้วยอัฒภาค (;) อาจมีอักขระที่พิมพ์ได้รวมทั้งช่องว่าง มันสามารถปรากฏบนบรรทัดด้วยตัวมันเองเช่น -

; This program displays a message on screen

หรือในบรรทัดเดียวกันพร้อมกับคำสั่งเช่น -

add eax, ebx     ; adds ebx to eax

คำสั่งภาษาแอสเซมบลี

โปรแกรมภาษาแอสเซมบลีประกอบด้วยข้อความสามประเภท -

  • คำสั่งหรือคำสั่งปฏิบัติการ
  • คำสั่ง Assembler หรือ pseudo-ops และ
  • Macros.

executable instructions หรือเพียงแค่ instructionsบอกโปรเซสเซอร์ว่าต้องทำอะไร แต่ละคำสั่งประกอบด้วยไฟล์operation code(opcode) คำสั่งปฏิบัติการแต่ละคำสั่งจะสร้างคำสั่งภาษาเครื่องหนึ่งคำสั่ง

assembler directives หรือ pseudo-opsบอกผู้ประกอบเกี่ยวกับแง่มุมต่างๆของกระบวนการประกอบ สิ่งเหล่านี้ไม่สามารถเรียกใช้งานได้และไม่สร้างคำสั่งภาษาเครื่อง

Macros โดยพื้นฐานแล้วเป็นกลไกการแทนที่ข้อความ

ไวยากรณ์ของคำสั่งภาษาแอสเซมบลี

คำสั่งภาษาแอสเซมบลีถูกป้อนหนึ่งคำสั่งต่อบรรทัด แต่ละคำสั่งเป็นไปตามรูปแบบต่อไปนี้ -

[label]   mnemonic   [operands]   [;comment]

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

ต่อไปนี้เป็นตัวอย่างคำสั่งภาษาแอสเซมบลีทั่วไป -

INC COUNT        ; Increment the memory variable COUNT

MOV TOTAL, 48    ; Transfer the value 48 in the 
                 ; memory variable TOTAL
					  
ADD AH, BH       ; Add the content of the 
                 ; BH register into the AH register
					  
AND MASK1, 128   ; Perform AND operation on the 
                 ; variable MASK1 and 128
					  
ADD MARKS, 10    ; Add 10 to the variable MARKS
MOV AL, 10       ; Transfer the value 10 to the AL register

โครงการ Hello World ใน Assembly

รหัสภาษาแอสเซมบลีต่อไปนี้แสดงสตริง 'Hello World' บนหน้าจอ -

section	.text
   global _start     ;must be declared for linker (ld)
	
_start:	            ;tells linker entry point
   mov	edx,len     ;message length
   mov	ecx,msg     ;message to write
   mov	ebx,1       ;file descriptor (stdout)
   mov	eax,4       ;system call number (sys_write)
   int	0x80        ;call kernel
	
   mov	eax,1       ;system call number (sys_exit)
   int	0x80        ;call kernel

section	.data
msg db 'Hello, world!', 0xa  ;string to be printed
len equ $ - msg     ;length of the string

เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -

Hello, world!

การรวบรวมและเชื่อมโยงโปรแกรม Assembly ใน NASM

ตรวจสอบให้แน่ใจว่าคุณได้กำหนดเส้นทางของ nasm และ ldไบนารีในตัวแปรสภาพแวดล้อม PATH ของคุณ ตอนนี้ทำตามขั้นตอนต่อไปนี้เพื่อรวบรวมและเชื่อมโยงโปรแกรมข้างต้น -

  • พิมพ์โค้ดด้านบนโดยใช้โปรแกรมแก้ไขข้อความและบันทึกเป็น hello.asm

  • ตรวจสอบให้แน่ใจว่าคุณอยู่ในไดเร็กทอรีเดียวกับที่คุณบันทึกไว้ hello.asm.

  • ในการประกอบโปรแกรมให้พิมพ์ nasm -f elf hello.asm

  • หากมีข้อผิดพลาดใด ๆ คุณจะได้รับแจ้งในขั้นตอนนี้ มิฉะนั้นไฟล์อ็อบเจ็กต์ของโปรแกรมของคุณชื่อhello.o จะถูกสร้างขึ้น

  • หากต้องการเชื่อมโยงไฟล์ออบเจ็กต์และสร้างไฟล์ปฏิบัติการชื่อ hello ให้พิมพ์ ld -m elf_i386 -s -o hello hello.o

  • ดำเนินการโปรแกรมโดยพิมพ์ ./hello

หากคุณทำทุกอย่างถูกต้องจะแสดงข้อความ 'Hello, world!' บนหน้าจอ.

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

ที่น่าสนใจคือหากคุณแทนที่คำหลักของส่วนด้วยกลุ่มคุณจะได้รับผลลัพธ์เดียวกัน ลองใช้รหัสต่อไปนี้ -

segment .text	   ;code segment
   global _start    ;must be declared for linker 
	
_start:	           ;tell linker entry point
   mov edx,len	   ;message length
   mov ecx,msg     ;message to write
   mov ebx,1	   ;file descriptor (stdout)
   mov eax,4	   ;system call number (sys_write)
   int 0x80	   ;call kernel

   mov eax,1       ;system call number (sys_exit)
   int 0x80	   ;call kernel

segment .data      ;data segment
msg	db 'Hello, world!',0xa   ;our dear string
len	equ	$ - msg          ;length of our dear string

เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -

Hello, world!

เซ็กเมนต์หน่วยความจำ

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

จากการอภิปรายข้างต้นเราสามารถระบุส่วนหน่วยความจำต่างๆเป็น -

  • Data segment - แสดงโดย .data และส่วน .bss. ส่วน. data ใช้เพื่อประกาศขอบเขตหน่วยความจำซึ่งจัดเก็บองค์ประกอบข้อมูลสำหรับโปรแกรม ไม่สามารถขยายส่วนนี้ได้หลังจากที่มีการประกาศองค์ประกอบข้อมูลและจะยังคงคงที่ตลอดทั้งโปรแกรม

    ส่วน. bss ยังเป็นส่วนหน่วยความจำแบบคงที่ซึ่งมีบัฟเฟอร์สำหรับข้อมูลที่จะประกาศในโปรแกรมในภายหลัง หน่วยความจำบัฟเฟอร์นี้เต็มไปด้วยศูนย์

  • Code segment - แสดงโดย .textมาตรา. สิ่งนี้กำหนดพื้นที่ในหน่วยความจำที่เก็บรหัสคำสั่ง นี่เป็นพื้นที่คงที่ด้วย

  • Stack - ส่วนนี้ประกอบด้วยค่าข้อมูลที่ส่งผ่านไปยังฟังก์ชันและขั้นตอนภายในโปรแกรม

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

เพื่อเพิ่มความเร็วในการทำงานของโปรเซสเซอร์โปรเซสเซอร์จะมีที่เก็บหน่วยความจำภายในที่เรียกว่า registers.

รีจิสเตอร์เก็บองค์ประกอบข้อมูลสำหรับการประมวลผลโดยไม่ต้องเข้าถึงหน่วยความจำ ชิปประมวลผลมีการลงทะเบียนจำนวน จำกัด

รีจิสเตอร์โปรเซสเซอร์

มีการลงทะเบียนโปรเซสเซอร์ 32 บิตและ 16 บิตหกตัวในสถาปัตยกรรม IA-32 การลงทะเบียนแบ่งออกเป็นสามประเภท -

  • ทะเบียนทั่วไป
  • ทะเบียนควบคุมและ
  • การลงทะเบียนกลุ่ม

การลงทะเบียนทั่วไปแบ่งออกเป็นกลุ่มต่อไปนี้ -

  • การลงทะเบียนข้อมูล
  • ตัวชี้ลงทะเบียนและ
  • ดัชนีรีจิสเตอร์

รีจิสเตอร์ข้อมูล

การลงทะเบียนข้อมูล 32 บิตสี่ตัวใช้สำหรับการคำนวณทางคณิตศาสตร์ตรรกะและการดำเนินการอื่น ๆ การลงทะเบียน 32 บิตเหล่านี้สามารถใช้ได้สามวิธี -

  • ตามการลงทะเบียนข้อมูล 32 บิตที่สมบูรณ์: EAX, EBX, ECX, EDX

  • ครึ่งล่างของรีจิสเตอร์ 32 บิตสามารถใช้เป็นรีจิสเตอร์ข้อมูล 16 บิตสี่ตัว ได้แก่ AX, BX, CX และ DX

  • ครึ่งล่างและสูงกว่าของรีจิสเตอร์ 16 บิตสี่ตัวที่กล่าวถึงข้างต้นสามารถใช้เป็นรีจิสเตอร์ข้อมูล 8 บิต 8 ตัว: AH, AL, BH, BL, CH, CL, DH และ DL

การลงทะเบียนข้อมูลเหล่านี้บางส่วนมีการใช้งานเฉพาะในการคำนวณทางคณิตศาสตร์

AX is the primary accumulator; ใช้ในอินพุต / เอาต์พุตและคำแนะนำทางคณิตศาสตร์ส่วนใหญ่ ตัวอย่างเช่นในการดำเนินการคูณตัวถูกดำเนินการหนึ่งตัวถูกเก็บไว้ในทะเบียน EAX หรือ AX หรือ AL ตามขนาดของตัวถูกดำเนินการ

BX is known as the base registerเนื่องจากสามารถใช้ในการจัดทำดัชนีแอดเดรส

CX is known as the count registerในฐานะ ECX นั้น CX จะลงทะเบียนเก็บการนับลูปในการดำเนินการซ้ำ

DX is known as the data register. นอกจากนี้ยังใช้ในการดำเนินการอินพุต / เอาต์พุต นอกจากนี้ยังใช้กับการลงทะเบียน AX พร้อมกับ DX สำหรับการคูณและหารการดำเนินการที่เกี่ยวข้องกับค่าขนาดใหญ่

ตัวชี้รีจิสเตอร์

ตัวชี้รีจิสเตอร์คือรีจิสเตอร์ EIP, ESP และ EBP แบบ 32 บิตและส่วน IP, SP และ BP ที่เหมาะสม 16 บิต พอยน์เตอร์รีจิสเตอร์มีสามประเภท -

  • Instruction Pointer (IP)- การลงทะเบียน IP 16 บิตจะเก็บที่อยู่ออฟเซ็ตของคำสั่งถัดไปที่จะดำเนินการ IP ที่เชื่อมโยงกับทะเบียน CS (เป็น CS: IP) ให้ที่อยู่ที่สมบูรณ์ของคำสั่งปัจจุบันในส่วนรหัส

  • Stack Pointer (SP)- การลงทะเบียน SP 16 บิตให้ค่าออฟเซ็ตภายในสแตกโปรแกรม SP ที่เชื่อมโยงกับ SS register (SS: SP) หมายถึงตำแหน่งปัจจุบันของข้อมูลหรือแอดเดรสภายในสแตกโปรแกรม

  • Base Pointer (BP)- การลงทะเบียน BP 16 บิตส่วนใหญ่ช่วยในการอ้างอิงตัวแปรพารามิเตอร์ที่ส่งผ่านไปยังรูทีนย่อย ที่อยู่ใน SS register จะรวมกับค่าชดเชยใน BP เพื่อรับตำแหน่งของพารามิเตอร์ BP ยังสามารถใช้ร่วมกับ DI และ SI เป็นทะเบียนฐานสำหรับการระบุแอดเดรสพิเศษ

ดัชนีรีจิสเตอร์

ดัชนี 32 บิตลงทะเบียน ESI และ EDI และส่วนขวาสุด 16 บิต SI และ DI ใช้สำหรับการกำหนดแอดเดรสที่จัดทำดัชนีและบางครั้งใช้ในการบวกและการลบ ตัวชี้ดัชนีมีสองชุด -

  • Source Index (SI) - ใช้เป็นดัชนีต้นทางสำหรับการดำเนินการสตริง

  • Destination Index (DI) - ใช้เป็นดัชนีปลายทางสำหรับการดำเนินการสตริง

รีจิสเตอร์ควบคุม

การลงทะเบียนตัวชี้คำสั่ง 32 บิตและการลงทะเบียนแฟล็ก 32 บิตรวมกันถือเป็นการลงทะเบียนควบคุม

คำสั่งจำนวนมากเกี่ยวข้องกับการเปรียบเทียบและการคำนวณทางคณิตศาสตร์และเปลี่ยนสถานะของแฟล็กและคำแนะนำเงื่อนไขอื่น ๆ ทดสอบค่าของแฟล็กสถานะเหล่านี้เพื่อนำโฟลว์การควบคุมไปยังตำแหน่งอื่น

แฟล็กบิตทั่วไปคือ:

  • Overflow Flag (OF) - แสดงถึงการล้นของบิตลำดับสูง (บิตซ้ายสุด) ของข้อมูลหลังจากการดำเนินการทางคณิตศาสตร์ที่ลงนาม

  • Direction Flag (DF)- กำหนดทิศทางซ้ายหรือขวาสำหรับการย้ายหรือเปรียบเทียบข้อมูลสตริง เมื่อค่า DF เป็น 0 การดำเนินการสตริงจะใช้ทิศทางจากซ้ายไปขวาและเมื่อตั้งค่าเป็น 1 การดำเนินการสตริงจะใช้ทิศทางจากขวาไปซ้าย

  • Interrupt Flag (IF)- กำหนดว่าการขัดจังหวะภายนอกเช่นรายการแป้นพิมพ์ ฯลฯ จะถูกละเว้นหรือประมวลผล ปิดใช้งานการขัดจังหวะภายนอกเมื่อค่าเป็น 0 และเปิดใช้งานการขัดจังหวะเมื่อตั้งค่าเป็น 1

  • Trap Flag (TF)- อนุญาตให้ตั้งค่าการทำงานของโปรเซสเซอร์ในโหมดขั้นตอนเดียว โปรแกรมดีบักที่เราใช้ตั้งค่าแฟล็กกับดักดังนั้นเราจึงสามารถดำเนินการทีละคำสั่งได้

  • Sign Flag (SF)- แสดงสัญลักษณ์ของผลลัพธ์ของการดำเนินการทางคณิตศาสตร์ แฟล็กนี้ถูกตั้งค่าตามสัญลักษณ์ของรายการข้อมูลหลังจากการดำเนินการทางคณิตศาสตร์ เครื่องหมายจะแสดงโดยลำดับสูงของบิตซ้ายสุด ผลลัพธ์ที่เป็นบวกจะล้างค่าของ SF เป็น 0 และผลลัพธ์ที่เป็นลบจะตั้งค่าเป็น 1

  • Zero Flag (ZF)- บ่งชี้ผลลัพธ์ของการคำนวณทางคณิตศาสตร์หรือการเปรียบเทียบ ผลลัพธ์ที่ไม่ใช่ศูนย์จะล้างแฟล็กศูนย์เป็น 0 และผลลัพธ์ที่เป็นศูนย์จะตั้งค่าเป็น 1

  • Auxiliary Carry Flag (AF)- มีการพกพาจากบิต 3 ถึงบิต 4 หลังจากการดำเนินการทางคณิตศาสตร์ ใช้สำหรับการคำนวณเฉพาะทาง AF ถูกตั้งค่าเมื่อการดำเนินการทางคณิตศาสตร์ 1 ไบต์ทำให้การดำเนินการจากบิต 3 เป็นบิต 4

  • Parity Flag (PF)- ระบุจำนวน 1 บิตทั้งหมดในผลลัพธ์ที่ได้จากการคำนวณทางคณิตศาสตร์ จำนวนคู่ของ 1 บิตจะล้างแฟล็กพาริตีเป็น 0 และจำนวนคี่ของ 1 บิตตั้งค่าแฟล็กพาริตีเป็น 1

  • Carry Flag (CF)- มีการดำเนินการ 0 หรือ 1 จากบิตลำดับสูง (ซ้ายสุด) หลังจากการดำเนินการทางคณิตศาสตร์ นอกจากนี้ยังเก็บเนื้อหาของการดำเนินการกะหรือหมุนครั้งสุดท้าย

ตารางต่อไปนี้ระบุตำแหน่งของแฟล็กบิตในการลงทะเบียนแฟล็ก 16 บิต:

ธง: โอ ผม ที Z
บิตไม่: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0

การลงทะเบียนกลุ่ม

เซ็กเมนต์คือพื้นที่เฉพาะที่กำหนดไว้ในโปรแกรมสำหรับบรรจุข้อมูลโค้ดและสแต็ก มีสามส่วนหลัก -

  • Code Segment- มีคำแนะนำทั้งหมดที่จะดำเนินการ การลงทะเบียนกลุ่มรหัส 16 บิตหรือทะเบียน CS จะเก็บที่อยู่เริ่มต้นของส่วนรหัส

  • Data Segment- ประกอบด้วยข้อมูลค่าคงที่และพื้นที่ทำงาน การลงทะเบียนกลุ่มข้อมูล 16 บิตหรือการลงทะเบียน DS เก็บที่อยู่เริ่มต้นของส่วนข้อมูล

  • Stack Segment- ประกอบด้วยข้อมูลและที่อยู่ที่ส่งคืนของโพรซีเดอร์หรือรูทีนย่อย มันถูกนำไปใช้เป็นโครงสร้างข้อมูล 'สแตก' การลงทะเบียนกลุ่มสแตกหรือทะเบียน SS เก็บที่อยู่เริ่มต้นของสแต็ก

นอกเหนือจากการลงทะเบียน DS, CS และ SS แล้วยังมีการลงทะเบียนเซ็กเมนต์พิเศษอื่น ๆ - ES (เซ็กเมนต์พิเศษ), FS และ GS ซึ่งมีเซ็กเมนต์เพิ่มเติมสำหรับการจัดเก็บข้อมูล

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

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

ตัวอย่าง

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

section	.text
   global _start	 ;must be declared for linker (gcc)
	
_start:	         ;tell linker entry point
   mov	edx,len  ;message length
   mov	ecx,msg  ;message to write
   mov	ebx,1    ;file descriptor (stdout)
   mov	eax,4    ;system call number (sys_write)
   int	0x80     ;call kernel
	
   mov	edx,9    ;message length
   mov	ecx,s2   ;message to write
   mov	ebx,1    ;file descriptor (stdout)
   mov	eax,4    ;system call number (sys_write)
   int	0x80     ;call kernel
	
   mov	eax,1    ;system call number (sys_exit)
   int	0x80     ;call kernel
	
section	.data
msg db 'Displaying 9 stars',0xa ;a message
len equ $ - msg  ;length of message
s2 times 9 db '*'

เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -

Displaying 9 stars
*********

การเรียกระบบคือ API สำหรับอินเทอร์เฟซระหว่างพื้นที่ผู้ใช้และพื้นที่เคอร์เนล เราได้ใช้การโทรของระบบแล้ว sys_write และ sys_exit สำหรับเขียนลงในหน้าจอและออกจากโปรแกรมตามลำดับ

การเรียกระบบ Linux

คุณสามารถใช้การเรียกระบบ Linux ในโปรแกรมประกอบของคุณ คุณต้องทำตามขั้นตอนต่อไปนี้สำหรับการใช้การเรียกระบบ Linux ในโปรแกรมของคุณ -

  • ใส่หมายเลขการโทรของระบบในทะเบียน EAX
  • จัดเก็บอาร์กิวเมนต์สำหรับการเรียกระบบในการลงทะเบียน EBX, ECX และอื่น ๆ
  • โทรไปที่การขัดจังหวะที่เกี่ยวข้อง (80 ชม.)
  • โดยปกติผลลัพธ์จะถูกส่งกลับในทะเบียน EAX

มีการลงทะเบียนหกรายการที่เก็บอาร์กิวเมนต์ของการเรียกระบบที่ใช้ เหล่านี้คือ EBX, ECX, EDX, ESI, EDI และ EBP การลงทะเบียนเหล่านี้รับอาร์กิวเมนต์ติดต่อกันโดยเริ่มจากการลงทะเบียน EBX หากมีอาร์กิวเมนต์มากกว่าหกอาร์กิวเมนต์ตำแหน่งหน่วยความจำของอาร์กิวเมนต์แรกจะถูกเก็บไว้ในรีจิสเตอร์ EBX

ข้อมูลโค้ดต่อไปนี้แสดงการใช้การเรียกระบบ sys_exit -

mov	eax,1		; system call number (sys_exit)
int	0x80		; call kernel

ข้อมูลโค้ดต่อไปนี้แสดงการใช้การเรียกระบบ sys_write -

mov	edx,4		; message length
mov	ecx,msg		; message to write
mov	ebx,1		; file descriptor (stdout)
mov	eax,4		; system call number (sys_write)
int	0x80		; call kernel

syscalls ทั้งหมดแสดงอยู่ใน/usr/include/asm/unistd.hพร้อมกับตัวเลข (ค่าที่จะใส่ใน EAX ก่อนที่คุณจะเรียก int 80h)

ตารางต่อไปนี้แสดงการเรียกระบบบางส่วนที่ใช้ในบทช่วยสอนนี้ -

% eax ชื่อ % ebx % ecx % edx % esx % edi
1 sys_exit int - - - -
2 sys_fork โครงสร้าง pt_regs - - - -
3 sys_read int ที่ไม่ได้ลงนาม ถ่าน * size_t - -
4 sys_write int ที่ไม่ได้ลงนาม const ถ่าน * size_t - -
5 sys_open const ถ่าน * int int - -
6 sys_close int ที่ไม่ได้ลงนาม - - - -

ตัวอย่าง

ตัวอย่างต่อไปนี้อ่านตัวเลขจากแป้นพิมพ์และแสดงบนหน้าจอ -

section .data                           ;Data segment
   userMsg db 'Please enter a number: ' ;Ask the user to enter a number
   lenUserMsg equ $-userMsg             ;The length of the message
   dispMsg db 'You have entered: '
   lenDispMsg equ $-dispMsg                 

section .bss           ;Uninitialized data
   num resb 5
	
section .text          ;Code Segment
   global _start
	
_start:                ;User prompt
   mov eax, 4
   mov ebx, 1
   mov ecx, userMsg
   mov edx, lenUserMsg
   int 80h

   ;Read and store the user input
   mov eax, 3
   mov ebx, 2
   mov ecx, num  
   mov edx, 5          ;5 bytes (numeric, 1 for sign) of that information
   int 80h
	
   ;Output the message 'The entered number is: '
   mov eax, 4
   mov ebx, 1
   mov ecx, dispMsg
   mov edx, lenDispMsg
   int 80h  

   ;Output the number entered
   mov eax, 4
   mov ebx, 1
   mov ecx, num
   mov edx, 5
   int 80h  
    
   ; Exit code
   mov eax, 1
   mov ebx, 0
   int 80h

เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -

Please enter a number:
1234  
You have entered:1234

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

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

โหมดการกำหนดที่อยู่พื้นฐานสามโหมดคือ -

  • ลงทะเบียนที่อยู่
  • ที่อยู่ทันที
  • ที่อยู่หน่วยความจำ

ลงทะเบียนที่อยู่

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

ตัวอย่างเช่น,

MOV DX, TAX_RATE   ; Register in first operand
MOV COUNT, CX	   ; Register in second operand
MOV EAX, EBX	   ; Both the operands are in registers

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

ที่อยู่ทันที

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

ตัวอย่างเช่น,

BYTE_VALUE  DB  150    ; A byte value is defined
WORD_VALUE  DW  300    ; A word value is defined
ADD  BYTE_VALUE, 65    ; An immediate operand 65 is added
MOV  AX, 45H           ; Immediate constant 45H is transferred to AX

ที่อยู่หน่วยความจำโดยตรง

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

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

ในการกำหนดแอดเดรสหน่วยความจำโดยตรงหนึ่งในตัวถูกดำเนินการอ้างถึงตำแหน่งหน่วยความจำและตัวถูกดำเนินการอื่น ๆ อ้างถึงรีจิสเตอร์

ตัวอย่างเช่น,

ADD	BYTE_VALUE, DL	; Adds the register in the memory location
MOV	BX, WORD_VALUE	; Operand from the memory is added to register

ที่อยู่ตรงข้ามโดยตรง

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

BYTE_TABLE DB  14, 15, 22, 45      ; Tables of bytes
WORD_TABLE DW  134, 345, 564, 123  ; Tables of words

การดำเนินการต่อไปนี้เข้าถึงข้อมูลจากตารางในหน่วยความจำลงในรีจิสเตอร์ -

MOV CL, BYTE_TABLE[2]	; Gets the 3rd element of the BYTE_TABLE
MOV CL, BYTE_TABLE + 2	; Gets the 3rd element of the BYTE_TABLE
MOV CX, WORD_TABLE[3]	; Gets the 4th element of the WORD_TABLE
MOV CX, WORD_TABLE + 3	; Gets the 4th element of the WORD_TABLE

ที่อยู่หน่วยความจำทางอ้อม

โหมดการกำหนดแอดเดรสนี้ใช้ความสามารถของคอมพิวเตอร์ของSegment: Offset addressing โดยทั่วไปฐานจะลงทะเบียน EBX, EBP (หรือ BX, BP) และดัชนีรีจิสเตอร์ (DI, SI) ซึ่งเขียนโค้ดไว้ในวงเล็บเหลี่ยมสำหรับการอ้างอิงหน่วยความจำถูกใช้เพื่อจุดประสงค์นี้

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

ข้อมูลโค้ดต่อไปนี้แสดงวิธีการเข้าถึงองค์ประกอบต่างๆของตัวแปร

MY_TABLE TIMES 10 DW 0  ; Allocates 10 words (2 bytes) each initialized to 0
MOV EBX, [MY_TABLE]     ; Effective Address of MY_TABLE in EBX
MOV [EBX], 110          ; MY_TABLE[0] = 110
ADD EBX, 2              ; EBX = EBX +2
MOV [EBX], 123          ; MY_TABLE[1] = 123

คำแนะนำ MOV

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

ไวยากรณ์

ไวยากรณ์ของคำสั่ง MOV คือ -

MOV  destination, source

คำแนะนำ MOV อาจมีหนึ่งในห้ารูปแบบต่อไปนี้ -

MOV  register, register
MOV  register, immediate
MOV  memory, immediate
MOV  register, memory
MOV  memory, register

โปรดทราบว่า -

  • ตัวถูกดำเนินการทั้งสองในการดำเนินการ MOV ควรมีขนาดเท่ากัน
  • ค่าของตัวถูกดำเนินการต้นทางยังคงไม่เปลี่ยนแปลง

คำสั่ง MOV ทำให้เกิดความไม่ชัดเจนในบางครั้ง ตัวอย่างเช่นดูงบ -

MOV  EBX, [MY_TABLE]  ; Effective Address of MY_TABLE in EBX
MOV  [EBX], 110	      ; MY_TABLE[0] = 110

ไม่ชัดเจนว่าคุณต้องการย้ายการเทียบเท่าไบต์หรือเทียบเท่ากับคำของหมายเลข 110 ในกรณีเช่นนี้ควรใช้ a type specifier.

ตารางต่อไปนี้แสดงตัวระบุประเภททั่วไป -

Type Specifier ไบต์จ่าหน้า
ไบต์ 1
คำ 2
DWORD 4
QWORD 8
TBYTE 10

ตัวอย่าง

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

section	.text
   global _start     ;must be declared for linker (ld)
_start:             ;tell linker entry point
	
   ;writing the name 'Zara Ali'
   mov	edx,9       ;message length
   mov	ecx, name   ;message to write
   mov	ebx,1       ;file descriptor (stdout)
   mov	eax,4       ;system call number (sys_write)
   int	0x80        ;call kernel
	
   mov	[name],  dword 'Nuha'    ; Changed the name to Nuha Ali
	
   ;writing the name 'Nuha Ali'
   mov	edx,8       ;message length
   mov	ecx,name    ;message to write
   mov	ebx,1       ;file descriptor (stdout)
   mov	eax,4       ;system call number (sys_write)
   int	0x80        ;call kernel
	
   mov	eax,1       ;system call number (sys_exit)
   int	0x80        ;call kernel

section	.data
name db 'Zara Ali '

เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -

Zara Ali Nuha Ali

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

การจัดสรรพื้นที่จัดเก็บสำหรับข้อมูลเริ่มต้น

ไวยากรณ์สำหรับคำสั่งการจัดสรรพื้นที่เก็บข้อมูลสำหรับข้อมูลเริ่มต้นคือ -

[variable-name]    define-directive    initial-value   [,initial-value]...

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

คำสั่งกำหนดมีห้ารูปแบบพื้นฐาน -

คำสั่ง วัตถุประสงค์ พื้นที่จัดเก็บ
DB กำหนดไบต์ จัดสรร 1 ไบต์
DW กำหนด Word จัดสรร 2 ไบต์
DD กำหนด Doubleword จัดสรร 4 ไบต์
DQ กำหนด Quadword จัดสรร 8 ไบต์
DT กำหนดสิบไบต์ จัดสรร 10 ไบต์

ต่อไปนี้เป็นตัวอย่างบางส่วนของการใช้คำสั่งกำหนด -

choice		DB	'y'
number		DW	12345
neg_number	DW	-12345
big_number	DQ	123456789
real_number1	DD	1.234
real_number2	DQ	123.456

โปรดทราบว่า -

  • อักขระแต่ละไบต์จะถูกเก็บเป็นค่า ASCII เป็นเลขฐานสิบหก

  • ค่าทศนิยมแต่ละค่าจะถูกแปลงเป็นค่าเทียบเท่าไบนารี 16 บิตโดยอัตโนมัติและจัดเก็บเป็นเลขฐานสิบหก

  • โปรเซสเซอร์ใช้การเรียงลำดับไบต์ endian น้อย

  • จำนวนลบจะถูกแปลงเป็นการแทนค่า 2 ของมัน

  • ตัวเลขทศนิยมสั้นและยาวแสดงโดยใช้ 32 หรือ 64 บิตตามลำดับ

โปรแกรมต่อไปนี้แสดงการใช้คำสั่งกำหนด -

section .text
   global _start          ;must be declared for linker (gcc)
	
_start:                   ;tell linker entry point
   mov	edx,1		  ;message length
   mov	ecx,choice        ;message to write
   mov	ebx,1		  ;file descriptor (stdout)
   mov	eax,4		  ;system call number (sys_write)
   int	0x80		  ;call kernel

   mov	eax,1		  ;system call number (sys_exit)
   int	0x80		  ;call kernel

section .data
choice DB 'y'

เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -

y

การจัดสรรพื้นที่จัดเก็บสำหรับข้อมูลที่ไม่ได้เริ่มต้น

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

คำสั่งสงวนมีห้ารูปแบบพื้นฐาน -

คำสั่ง วัตถุประสงค์
RESB จองไบต์
RESW จอง Word
RESD จอง Doubleword
RESQ จอง Quadword
ส่วนที่เหลือ จองสิบไบต์

คำจำกัดความหลายคำ

คุณสามารถมีคำสั่งนิยามข้อมูลหลายรายการในโปรแกรม ตัวอย่างเช่น -

choice	  DB 	'Y' 		 ;ASCII of y = 79H
number1	  DW 	12345 	 ;12345D = 3039H
number2    DD  12345679  ;123456789D = 75BCD15H

แอสเซมเบลอร์จัดสรรหน่วยความจำต่อเนื่องสำหรับนิยามตัวแปรหลายตัว

การเริ่มต้นหลายครั้ง

คำสั่ง TIMES ช่วยให้การเริ่มต้นหลายครั้งเป็นค่าเดียวกัน ตัวอย่างเช่นอาร์เรย์ที่ชื่อเครื่องหมายขนาด 9 สามารถกำหนดและเริ่มต้นเป็นศูนย์โดยใช้คำสั่งต่อไปนี้ -

marks  TIMES  9  DW  0

คำสั่ง TIMES มีประโยชน์ในการกำหนดอาร์เรย์และตาราง โปรแกรมต่อไปนี้จะแสดงดอกจัน 9 ดอกบนหน้าจอ -

section	.text
   global _start        ;must be declared for linker (ld)
	
_start:                 ;tell linker entry point
   mov	edx,9		;message length
   mov	ecx, stars	;message to write
   mov	ebx,1		;file descriptor (stdout)
   mov	eax,4		;system call number (sys_write)
   int	0x80		;call kernel

   mov	eax,1		;system call number (sys_exit)
   int	0x80		;call kernel

section	.data
stars   times 9 db '*'

เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -

*********

NASM มีคำสั่งหลายอย่างที่กำหนดค่าคงที่ เราได้ใช้คำสั่ง EQU ในบทก่อนหน้าแล้ว เราจะพูดถึงคำสั่งสามข้อโดยเฉพาะ -

  • EQU
  • %assign
  • %define

คำสั่ง EQU

EQUคำสั่งใช้สำหรับกำหนดค่าคงที่ ไวยากรณ์ของคำสั่ง EQU มีดังนี้ -

CONSTANT_NAME EQU expression

ตัวอย่างเช่น,

TOTAL_STUDENTS equ 50

จากนั้นคุณสามารถใช้ค่าคงที่นี้ในโค้ดของคุณเช่น -

mov  ecx,  TOTAL_STUDENTS 
cmp  eax,  TOTAL_STUDENTS

ตัวถูกดำเนินการของคำสั่ง EQU สามารถเป็นนิพจน์ -

LENGTH equ 20
WIDTH  equ 10
AREA   equ length * width

ส่วนโค้ดด้านบนจะกำหนด AREA เป็น 200

ตัวอย่าง

ตัวอย่างต่อไปนี้แสดงให้เห็นถึงการใช้คำสั่ง EQU -

SYS_EXIT  equ 1
SYS_WRITE equ 4
STDIN     equ 0
STDOUT    equ 1
section	 .text
   global _start    ;must be declared for using gcc
	
_start:             ;tell linker entry point
   mov eax, SYS_WRITE         
   mov ebx, STDOUT         
   mov ecx, msg1         
   mov edx, len1 
   int 0x80                
	
   mov eax, SYS_WRITE         
   mov ebx, STDOUT         
   mov ecx, msg2         
   mov edx, len2 
   int 0x80 
	
   mov eax, SYS_WRITE         
   mov ebx, STDOUT         
   mov ecx, msg3         
   mov edx, len3 
   int 0x80
   
   mov eax,SYS_EXIT    ;system call number (sys_exit)
   int 0x80            ;call kernel

section	 .data
msg1 db	'Hello, programmers!',0xA,0xD 	
len1 equ $ - msg1			

msg2 db 'Welcome to the world of,', 0xA,0xD 
len2 equ $ - msg2 msg3 db 'Linux assembly programming! ' len3 equ $- msg3

เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -

Hello, programmers!
Welcome to the world of,
Linux assembly programming!

% กำหนดคำสั่ง

%assignคำสั่งสามารถใช้เพื่อกำหนดค่าคงที่เป็นตัวเลขเช่นคำสั่ง EQU คำสั่งนี้อนุญาตให้กำหนดนิยามใหม่ ตัวอย่างเช่นคุณอาจกำหนด TOTAL คงที่เป็น -

%assign TOTAL 10

ต่อมาในโค้ดคุณสามารถกำหนดใหม่เป็น -

%assign  TOTAL  20

คำสั่งนี้คำนึงถึงตัวพิมพ์เล็กและใหญ่

% กำหนด Directive

%defineคำสั่งอนุญาตให้กำหนดค่าคงที่ทั้งตัวเลขและสตริง คำสั่งนี้คล้ายกับ #define ใน C ตัวอย่างเช่นคุณอาจกำหนดค่าคงที่ PTR เป็น -

%define PTR [EBP+4]

รหัสด้านบนแทนที่PTRด้วย [EBP + 4]

คำสั่งนี้ยังช่วยให้สามารถกำหนดนิยามใหม่ได้และต้องคำนึงถึงขนาดตัวพิมพ์

คำแนะนำ INC

คำสั่ง INC ใช้สำหรับการเพิ่มตัวถูกดำเนินการทีละตัว ทำงานบนตัวถูกดำเนินการเดียวที่สามารถอยู่ในรีจิสเตอร์หรือในหน่วยความจำ

ไวยากรณ์

คำสั่ง INC มีไวยากรณ์ต่อไปนี้ -

INC destination

ปลายทางตัวถูกดำเนินการอาจเป็นตัวถูกดำเนินการ 8 บิต 16 บิตหรือ 32 บิต

ตัวอย่าง

INC EBX	     ; Increments 32-bit register
INC DL       ; Increments 8-bit register
INC [count]  ; Increments the count variable

คำสั่ง DEC

คำสั่ง DEC ใช้สำหรับการลดค่าตัวถูกดำเนินการทีละตัว ทำงานบนตัวถูกดำเนินการเดียวที่สามารถอยู่ในรีจิสเตอร์หรือในหน่วยความจำ

ไวยากรณ์

คำสั่ง DEC มีไวยากรณ์ต่อไปนี้ -

DEC destination

ปลายทางตัวถูกดำเนินการอาจเป็นตัวถูกดำเนินการ 8 บิต 16 บิตหรือ 32 บิต

ตัวอย่าง

segment .data
   count dw  0
   value db  15
	
segment .text
   inc [count]
   dec [value]
	
   mov ebx, count
   inc word [ebx]
	
   mov esi, value
   dec byte [esi]

คำแนะนำในการเพิ่มและย่อย

คำสั่ง ADD และ SUB ใช้สำหรับการเพิ่ม / ลบข้อมูลไบนารีอย่างง่ายในขนาดไบต์คำและคำคู่เช่นสำหรับการเพิ่มหรือลบตัวถูกดำเนินการ 8 บิต 16 บิตหรือ 32 บิตตามลำดับ

ไวยากรณ์

คำสั่ง ADD และ SUB มีไวยากรณ์ต่อไปนี้ -

ADD/SUB	destination, source

คำสั่ง ADD / SUB สามารถเกิดขึ้นได้ระหว่าง -

  • ลงทะเบียนเพื่อลงทะเบียน
  • หน่วยความจำในการลงทะเบียน
  • ลงทะเบียนในหน่วยความจำ
  • ลงทะเบียนกับข้อมูลคงที่
  • หน่วยความจำกับข้อมูลคงที่

อย่างไรก็ตามเช่นเดียวกับคำแนะนำอื่น ๆ การดำเนินการจากหน่วยความจำต่อหน่วยความจำจะไม่สามารถทำได้โดยใช้คำสั่ง ADD / SUB ชุดการดำเนินการ ADD หรือ SUB หรือล้างแฟล็กโอเวอร์โฟลว์และดำเนินการ

ตัวอย่าง

ตัวอย่างต่อไปนี้จะถามตัวเลขสองหลักจากผู้ใช้เก็บตัวเลขในทะเบียน EAX และ EBX ตามลำดับเพิ่มค่าเก็บผลลัพธ์ไว้ในตำแหน่งหน่วยความจำ ' res ' และสุดท้ายแสดงผลลัพธ์

SYS_EXIT  equ 1
SYS_READ  equ 3
SYS_WRITE equ 4
STDIN     equ 0
STDOUT    equ 1

segment .data 

   msg1 db "Enter a digit ", 0xA,0xD 
   len1 equ $- msg1 msg2 db "Please enter a second digit", 0xA,0xD len2 equ $- msg2 

   msg3 db "The sum is: "
   len3 equ $- msg3

segment .bss

   num1 resb 2 
   num2 resb 2 
   res resb 1    

section	.text
   global _start    ;must be declared for using gcc
	
_start:             ;tell linker entry point
   mov eax, SYS_WRITE         
   mov ebx, STDOUT         
   mov ecx, msg1         
   mov edx, len1 
   int 0x80                

   mov eax, SYS_READ 
   mov ebx, STDIN  
   mov ecx, num1 
   mov edx, 2
   int 0x80            

   mov eax, SYS_WRITE        
   mov ebx, STDOUT         
   mov ecx, msg2          
   mov edx, len2         
   int 0x80

   mov eax, SYS_READ  
   mov ebx, STDIN  
   mov ecx, num2 
   mov edx, 2
   int 0x80        

   mov eax, SYS_WRITE         
   mov ebx, STDOUT         
   mov ecx, msg3          
   mov edx, len3         
   int 0x80

   ; moving the first number to eax register and second number to ebx
   ; and subtracting ascii '0' to convert it into a decimal number
	
   mov eax, [num1]
   sub eax, '0'
	
   mov ebx, [num2]
   sub ebx, '0'

   ; add eax and ebx
   add eax, ebx
   ; add '0' to to convert the sum from decimal to ASCII
   add eax, '0'

   ; storing the sum in memory location res
   mov [res], eax

   ; print the sum 
   mov eax, SYS_WRITE        
   mov ebx, STDOUT
   mov ecx, res         
   mov edx, 1        
   int 0x80

exit:    
   
   mov eax, SYS_EXIT   
   xor ebx, ebx 
   int 0x80

เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -

Enter a digit:
3
Please enter a second digit:
4
The sum is:
7

The program with hardcoded variables −

section	.text
   global _start    ;must be declared for using gcc
	
_start:             ;tell linker entry point
   mov	eax,'3'
   sub     eax, '0'
	
   mov 	ebx, '4'
   sub     ebx, '0'
   add 	eax, ebx
   add	eax, '0'
	
   mov 	[sum], eax
   mov	ecx,msg	
   mov	edx, len
   mov	ebx,1	;file descriptor (stdout)
   mov	eax,4	;system call number (sys_write)
   int	0x80	;call kernel
	
   mov	ecx,sum
   mov	edx, 1
   mov	ebx,1	;file descriptor (stdout)
   mov	eax,4	;system call number (sys_write)
   int	0x80	;call kernel
	
   mov	eax,1	;system call number (sys_exit)
   int	0x80	;call kernel
	
section .data
   msg db "The sum is:", 0xA,0xD 
   len equ $ - msg   
   segment .bss
   sum resb 1

เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -

The sum is:
7

คำสั่ง MUL / IMUL

มีสองคำแนะนำสำหรับการคูณข้อมูลไบนารี คำสั่ง MUL (Multiply) จัดการกับข้อมูลที่ไม่ได้ลงชื่อและ IMUL (Integer Multiply) จะจัดการกับข้อมูลที่เซ็นชื่อ คำแนะนำทั้งสองมีผลต่อแฟล็ก Carry และ Overflow

ไวยากรณ์

ไวยากรณ์สำหรับคำสั่ง MUL / IMUL มีดังนี้ -

MUL/IMUL multiplier

ตัวคูณในทั้งสองกรณีจะอยู่ในตัวสะสมขึ้นอยู่กับขนาดของตัวคูณและตัวคูณและผลิตภัณฑ์ที่สร้างขึ้นจะถูกเก็บไว้ในสองรีจิสเตอร์ขึ้นอยู่กับขนาดของตัวถูกดำเนินการ ส่วนต่อไปนี้อธิบายคำแนะนำของ MUL ในกรณีต่างๆสามกรณี -

ซีเนียร์ สถานการณ์
1

When two bytes are multiplied −

ตัวคูณอยู่ในทะเบียน AL และตัวคูณเป็นไบต์ในหน่วยความจำหรือในรีจิสเตอร์อื่น ผลิตภัณฑ์อยู่ใน AX ผลิตภัณฑ์ 8 บิตลำดับสูงจะถูกเก็บไว้ใน AH และ 8 บิตลำดับต่ำจะถูกเก็บไว้ใน AL

2

When two one-word values are multiplied −

ตัวคูณควรอยู่ในทะเบียน AX และตัวคูณคือคำในหน่วยความจำหรือรีจิสเตอร์อื่น ตัวอย่างเช่นสำหรับคำสั่งเช่น MUL DX คุณต้องจัดเก็บตัวคูณใน DX และตัวคูณใน AX

ผลิตภัณฑ์ผลลัพธ์คือคำสองคำซึ่งจะต้องมีการลงทะเบียนสองรายการ ส่วนลำดับสูง (ซ้ายสุด) จะถูกเก็บไว้ใน DX และส่วนลำดับล่าง (ขวาสุด) จะถูกเก็บไว้ใน AX

3

When two doubleword values are multiplied −

เมื่อคูณค่า doubleword สองค่าตัวคูณควรอยู่ใน EAX และตัวคูณคือค่า doubleword ที่เก็บไว้ในหน่วยความจำหรือในรีจิสเตอร์อื่น ผลิตภัณฑ์ที่สร้างขึ้นจะถูกเก็บไว้ในรีจิสเตอร์ EDX: EAX นั่นคือลำดับสูง 32 บิตจะถูกเก็บไว้ในรีจิสเตอร์ EDX และลำดับต่ำ 32 บิตจะถูกเก็บไว้ในรีจิสเตอร์ EAX

ตัวอย่าง

MOV AL, 10
MOV DL, 25
MUL DL
...
MOV DL, 0FFH	; DL= -1
MOV AL, 0BEH	; AL = -66
IMUL DL

ตัวอย่าง

ตัวอย่างต่อไปนี้คูณ 3 ด้วย 2 และแสดงผลลัพธ์ -

section	.text
   global _start    ;must be declared for using gcc
	
_start:             ;tell linker entry point

   mov	al,'3'
   sub     al, '0'
	
   mov 	bl, '2'
   sub     bl, '0'
   mul 	bl
   add	al, '0'
	
   mov 	[res], al
   mov	ecx,msg	
   mov	edx, len
   mov	ebx,1	;file descriptor (stdout)
   mov	eax,4	;system call number (sys_write)
   int	0x80	;call kernel
	
   mov	ecx,res
   mov	edx, 1
   mov	ebx,1	;file descriptor (stdout)
   mov	eax,4	;system call number (sys_write)
   int	0x80	;call kernel
	
   mov	eax,1	;system call number (sys_exit)
   int	0x80	;call kernel

section .data
msg db "The result is:", 0xA,0xD 
len equ $- msg   
segment .bss
res resb 1

เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -

The result is:
6

คำแนะนำ DIV / IDIV

การดำเนินการหารสร้างสององค์ประกอบ - ก quotient และก remainder. ในกรณีของการคูณการล้นจะไม่เกิดขึ้นเนื่องจากใช้รีจิสเตอร์แบบยาวสองชั้นเพื่อเก็บผลิตภัณฑ์ไว้ อย่างไรก็ตามในกรณีของการแบ่งอาจเกิดการล้น โปรเซสเซอร์จะสร้างอินเทอร์รัปต์หากเกิดโอเวอร์โฟลว์

คำสั่ง DIV (Divide) ใช้สำหรับข้อมูลที่ไม่ได้ลงชื่อและ IDIV (Integer Divide) ใช้สำหรับข้อมูลที่เซ็นชื่อ

ไวยากรณ์

รูปแบบสำหรับคำสั่ง DIV / IDIV -

DIV/IDIV	divisor

เงินปันผลอยู่ในตัวสะสม คำสั่งทั้งสองสามารถทำงานร่วมกับตัวถูกดำเนินการ 8 บิต 16 บิตหรือ 32 บิต การดำเนินการมีผลต่อแฟล็กสถานะทั้งหกสถานะ ส่วนต่อไปนี้อธิบายกรณีการแบ่งสามกรณีที่มีขนาดตัวถูกดำเนินการต่างกัน

ซีเนียร์ สถานการณ์
1

When the divisor is 1 byte −

เงินปันผลจะถือว่าอยู่ในการลงทะเบียน AX (16 บิต) หลังจากหารแล้วผลหารจะไปที่รีจิสเตอร์ AL และส่วนที่เหลือจะไปที่รีจิสเตอร์ AH

2

When the divisor is 1 word −

การจ่ายเงินปันผลจะถือว่ามีความยาว 32 บิตและในการลงทะเบียน DX: AX 16 บิตลำดับสูงอยู่ใน DX และ 16 บิตลำดับต่ำอยู่ใน AX หลังจากหารแล้วผลหาร 16 บิตจะไปที่รีจิสเตอร์ AX และส่วนที่เหลือ 16 บิตจะไปที่รีจิสเตอร์ DX

3

When the divisor is doubleword −

การจ่ายเงินปันผลจะถือว่ายาว 64 บิตและในการลงทะเบียน EDX: EAX 32 บิตลำดับสูงอยู่ใน EDX และ 32 บิตลำดับต่ำอยู่ใน EAX หลังจากหารแล้วผลหาร 32 บิตจะไปที่รีจิสเตอร์ EAX และส่วนที่เหลือ 32 บิตจะไปที่รีจิสเตอร์ EDX

ตัวอย่าง

ตัวอย่างต่อไปนี้หาร 8 ด้วย 2 dividend 8 ถูกเก็บไว้ในไฟล์ 16-bit AX register และ divisor 2 ถูกเก็บไว้ในไฟล์ 8-bit BL register.

section	.text
   global _start    ;must be declared for using gcc
	
_start:             ;tell linker entry point
   mov	ax,'8'
   sub     ax, '0'
	
   mov 	bl, '2'
   sub     bl, '0'
   div 	bl
   add	ax, '0'
	
   mov 	[res], ax
   mov	ecx,msg	
   mov	edx, len
   mov	ebx,1	;file descriptor (stdout)
   mov	eax,4	;system call number (sys_write)
   int	0x80	;call kernel
	
   mov	ecx,res
   mov	edx, 1
   mov	ebx,1	;file descriptor (stdout)
   mov	eax,4	;system call number (sys_write)
   int	0x80	;call kernel
	
   mov	eax,1	;system call number (sys_exit)
   int	0x80	;call kernel
	
section .data
msg db "The result is:", 0xA,0xD 
len equ $- msg   
segment .bss
res resb 1

เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -

The result is:
4

ชุดคำสั่งโปรเซสเซอร์มีคำสั่ง AND, OR, XOR, TEST และ NOT Boolean logic ซึ่งจะทดสอบตั้งค่าและล้างบิตตามความต้องการของโปรแกรม

รูปแบบสำหรับคำแนะนำเหล่านี้ -

ซีเนียร์ คำแนะนำ รูปแบบ
1 และ และ operand1, operand2
2 หรือ หรือ operand1, operand2
3 XOR XOR operand1, operand2
4 ทดสอบ TEST operand1, operand2
5 ไม่ ไม่ใช่ตัวถูกดำเนินการ 1

ตัวถูกดำเนินการตัวแรกในทุกกรณีอาจอยู่ในรีจิสเตอร์หรือในหน่วยความจำ ตัวถูกดำเนินการที่สองอาจอยู่ในรีจิสเตอร์ / หน่วยความจำหรือค่า (ค่าคงที่) ในทันที อย่างไรก็ตามไม่สามารถดำเนินการจากหน่วยความจำต่อหน่วยความจำได้ คำแนะนำเหล่านี้จะเปรียบเทียบหรือจับคู่บิตของตัวถูกดำเนินการและตั้งค่าแฟล็ก CF, OF, PF, SF และ ZF

คำสั่ง AND

คำสั่ง AND ใช้สำหรับสนับสนุนนิพจน์เชิงตรรกะโดยดำเนินการบิต AND การดำเนินการ bitwise AND จะส่งกลับ 1 หากบิตที่ตรงกันจากตัวถูกดำเนินการทั้งสองเป็น 1 มิฉะนั้นจะส่งกลับ 0 ตัวอย่างเช่น -

Operand1: 	0101
             Operand2: 	0011
----------------------------
After AND -> Operand1:	0001

การดำเนินการ AND สามารถใช้สำหรับการล้างหนึ่งบิตขึ้นไป ตัวอย่างเช่นสมมติว่า BL register มี 0011 1010 หากคุณต้องการล้างบิตลำดับสูงให้เป็นศูนย์ให้คุณและด้วย 0FH

AND	BL,   0FH   ; This sets BL to 0000 1010

ลองดูตัวอย่างอื่น หากคุณต้องการตรวจสอบว่าตัวเลขที่ระบุเป็นเลขคี่หรือคู่การทดสอบง่ายๆคือการตรวจสอบจำนวนบิตที่มีนัยสำคัญน้อยที่สุด ถ้านี่คือ 1 จำนวนนั้นเป็นเลขคี่มิฉะนั้นจำนวนจะเป็นคู่

สมมติว่าหมายเลขอยู่ในทะเบียน AL เราสามารถเขียน -

AND	AL, 01H     ; ANDing with 0000 0001
JZ    EVEN_NUMBER

โปรแกรมต่อไปนี้แสดงให้เห็นถึงสิ่งนี้ -

ตัวอย่าง

section .text
   global _start            ;must be declared for using gcc
	
_start:                     ;tell linker entry point
   mov   ax,   8h           ;getting 8 in the ax 
   and   ax, 1              ;and ax with 1
   jz    evnn
   mov   eax, 4             ;system call number (sys_write)
   mov   ebx, 1             ;file descriptor (stdout)
   mov   ecx, odd_msg       ;message to write
   mov   edx, len2          ;length of message
   int   0x80               ;call kernel
   jmp   outprog

evnn:   
  
   mov   ah,  09h
   mov   eax, 4             ;system call number (sys_write)
   mov   ebx, 1             ;file descriptor (stdout)
   mov   ecx, even_msg      ;message to write
   mov   edx, len1          ;length of message
   int   0x80               ;call kernel

outprog:

   mov   eax,1              ;system call number (sys_exit)
   int   0x80               ;call kernel

section   .data
even_msg  db  'Even Number!' ;message showing even number
len1  equ  $ - even_msg odd_msg db 'Odd Number!' ;message showing odd number len2 equ $ - odd_msg

เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -

Even Number!

เปลี่ยนค่าในการลงทะเบียน ax ด้วยเลขคี่เช่น -

mov  ax, 9h                  ; getting 9 in the ax

โปรแกรมจะแสดง:

Odd Number!

ในทำนองเดียวกันการล้างทะเบียนทั้งหมดคุณสามารถทำได้ด้วย 00H

หรือคำแนะนำ

คำสั่ง OR ใช้สำหรับสนับสนุนนิพจน์เชิงตรรกะโดยดำเนินการตามบิตหรือการดำเนินการ ตัวดำเนินการ bitwise OR จะคืนค่า 1 หากบิตที่ตรงกันจากตัวถูกดำเนินการอย่างใดอย่างหนึ่งหรือทั้งสองเป็นหนึ่ง จะคืนค่า 0 ถ้าทั้งสองบิตเป็นศูนย์

ตัวอย่างเช่น,

Operand1:     0101
             Operand2:     0011
----------------------------
After OR -> Operand1:    0111

การดำเนินการ OR สามารถใช้สำหรับการตั้งค่าหนึ่งบิตขึ้นไป ตัวอย่างเช่นให้เราสมมติว่า AL register มี 0011 1010 คุณต้องตั้งค่าบิตลำดับต่ำสี่ตัวคุณสามารถหรือด้วยค่า 0000 1111 เช่น FH

OR BL, 0FH                   ; This sets BL to  0011 1111

ตัวอย่าง

ตัวอย่างต่อไปนี้แสดงให้เห็นถึงคำสั่ง OR ให้เราเก็บค่า 5 และ 3 ในการลงทะเบียน AL และ BL ตามลำดับจากนั้นคำสั่ง

OR AL, BL

ควรจัดเก็บ 7 ในทะเบียน AL -

section .text
   global _start            ;must be declared for using gcc
	
_start:                     ;tell linker entry point
   mov    al, 5             ;getting 5 in the al
   mov    bl, 3             ;getting 3 in the bl
   or     al, bl            ;or al and bl registers, result should be 7
   add    al, byte '0'      ;converting decimal to ascii
	
   mov    [result],  al
   mov    eax, 4
   mov    ebx, 1
   mov    ecx, result
   mov    edx, 1 
   int    0x80
    
outprog:
   mov    eax,1             ;system call number (sys_exit)
   int    0x80              ;call kernel
	
section    .bss
result resb 1

เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -

7

คำสั่ง XOR

คำสั่ง XOR ใช้การดำเนินการ XOR แบบบิต การดำเนินการ XOR ตั้งค่าบิตผลลัพธ์เป็น 1 ในกรณีที่บิตจากตัวถูกดำเนินการต่างกัน ถ้าบิตจากตัวถูกดำเนินการเหมือนกัน (ทั้ง 0 หรือทั้ง 1) บิตผลลัพธ์จะถูกเคลียร์เป็น 0

ตัวอย่างเช่น,

Operand1:     0101
             Operand2:     0011
----------------------------
After XOR -> Operand1:    0110

XORing ตัวถูกดำเนินการด้วยตัวเองเปลี่ยนตัวถูกดำเนินการเป็น 0. ใช้เพื่อล้างทะเบียน

XOR     EAX, EAX

คำแนะนำในการทดสอบ

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

TEST    AL, 01H
JZ      EVEN_NUMBER

ไม่ใช่คำแนะนำ

คำสั่ง NOT ใช้การดำเนินการบิตไม่ การดำเนินการ NOT จะย้อนกลับบิตในตัวถูกดำเนินการ ตัวถูกดำเนินการอาจอยู่ในรีจิสเตอร์หรือในหน่วยความจำ

ตัวอย่างเช่น,

Operand1:    0101 0011
After NOT -> Operand1:    1010 1100

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

ซีเนียร์ คำแนะนำตามเงื่อนไข
1

Unconditional jump

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

2

Conditional jump

สิ่งนี้ดำเนินการโดยชุดคำสั่งการกระโดด j <condition> ขึ้นอยู่กับเงื่อนไข คำแนะนำแบบมีเงื่อนไขจะโอนการควบคุมโดยทำลายโฟลว์ตามลำดับและดำเนินการโดยเปลี่ยนค่าออฟเซ็ตใน IP

ให้เราหารือเกี่ยวกับคำสั่ง CMP ก่อนที่จะพูดถึงคำแนะนำแบบมีเงื่อนไข

คำสั่ง CMP

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

ไวยากรณ์

CMP destination, source

CMP เปรียบเทียบฟิลด์ข้อมูลตัวเลขสองฟิลด์ ตัวถูกดำเนินการปลายทางอาจอยู่ในรีจิสเตอร์หรือในหน่วยความจำ ตัวถูกดำเนินการต้นทางอาจเป็นข้อมูลคงที่ (ทันที) รีจิสเตอร์หรือหน่วยความจำ

ตัวอย่าง

CMP DX,	00  ; Compare the DX value with zero
JE  L7      ; If yes, then jump to label L7
.
.
L7: ...

CMP มักใช้เพื่อเปรียบเทียบว่าค่าตัวนับถึงจำนวนครั้งที่ต้องรันลูปหรือไม่ พิจารณาเงื่อนไขทั่วไปดังต่อไปนี้ -

INC	EDX
CMP	EDX, 10	; Compares whether the counter has reached 10
JLE	LP1     ; If it is less than or equal to 10, then jump to LP1

กระโดดโดยไม่มีเงื่อนไข

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

ไวยากรณ์

คำสั่ง JMP ระบุชื่อเลเบลซึ่งโฟลว์ของการควบคุมจะถูกโอนทันที ไวยากรณ์ของคำสั่ง JMP คือ -

JMP	label

ตัวอย่าง

ข้อมูลโค้ดต่อไปนี้แสดงคำสั่ง JMP -

MOV  AX, 00    ; Initializing AX to 0
MOV  BX, 00    ; Initializing BX to 0
MOV  CX, 01    ; Initializing CX to 1
L20:
ADD  AX, 01    ; Increment AX
ADD  BX, AX    ; Add AX to BX
SHL  CX, 1     ; shift left CX, this in turn doubles the CX value
JMP  L20       ; repeats the statements

กระโดดตามเงื่อนไข

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

ต่อไปนี้เป็นคำแนะนำการข้ามแบบมีเงื่อนไขที่ใช้กับข้อมูลที่ลงนามซึ่งใช้สำหรับการคำนวณทางคณิตศาสตร์ -

คำแนะนำ คำอธิบาย ทดสอบธงแล้ว
JE / JZ Jump Equal หรือ Jump Zero ZF
JNE / JNZ กระโดดไม่เท่ากันหรือกระโดดไม่เป็นศูนย์ ZF
JG / JNLE กระโดดมากขึ้นหรือกระโดดไม่น้อย / เท่ากัน ของ, SF, ZF
JGE / JNL กระโดดมากกว่า / เท่ากันหรือกระโดดไม่น้อย ของ, SF
JL / JNGE กระโดดน้อยลงหรือกระโดดไม่มากกว่า / เท่ากัน ของ, SF
JLE / JNG กระโดดน้อยลง / เท่ากันหรือกระโดดไม่มาก ของ, SF, ZF

ต่อไปนี้เป็นคำแนะนำการกระโดดตามเงื่อนไขที่ใช้กับข้อมูลที่ไม่ได้ลงชื่อซึ่งใช้สำหรับการดำเนินการทางตรรกะ -

คำแนะนำ คำอธิบาย ทดสอบธงแล้ว
JE / JZ Jump Equal หรือ Jump Zero ZF
JNE / JNZ กระโดดไม่เท่ากันหรือกระโดดไม่เป็นศูนย์ ZF
JA / JNBE กระโดดขึ้นไปข้างบนหรือกระโดดไม่ต่ำกว่า / เท่ากับ CF, ZF
JAE / JNB กระโดดสูงกว่า / เท่ากันหรือกระโดดไม่ต่ำกว่า CF
เจบี / เจเอ็นเอ กระโดดด้านล่างหรือกระโดดไม่สูงกว่า / เท่ากัน CF
JBE / JNA กระโดดด้านล่าง / เท่ากันหรือกระโดดไม่เหนือ AF, CF

คำแนะนำการกระโดดตามเงื่อนไขต่อไปนี้มีการใช้งานพิเศษและตรวจสอบค่าของแฟล็ก -

คำแนะนำ คำอธิบาย ทดสอบธงแล้ว
JXCZ กระโดดถ้า CX เป็นศูนย์ ไม่มี
JC กระโดดหากพกพา CF
JNC กระโดดถ้าไม่มีการพกพา CF
กระโดดถ้าล้น ของ
JNO กระโดดถ้าไม่มีล้น ของ
JP / JPE Jump Parity หรือ Jump Parity Even PF
JNP / JPO Jump No Parity หรือ Jump Parity Odd PF
JS Jump Sign (ค่าลบ) SF
จส Jump No Sign (ค่าบวก) SF

ไวยากรณ์สำหรับชุดคำสั่ง J <condition> -

ตัวอย่าง,

CMP	AL, BL
JE	EQUAL
CMP	AL, BH
JE	EQUAL
CMP	AL, CL
JE	EQUAL
NON_EQUAL: ...
EQUAL: ...

ตัวอย่าง

โปรแกรมต่อไปนี้แสดงตัวแปรสามตัวที่ใหญ่ที่สุด ตัวแปรคือตัวแปรสองหลัก ตัวแปรทั้งสาม num1, num2 และ num3 มีค่า 47, 22 และ 31 ตามลำดับ -

section	.text
   global _start         ;must be declared for using gcc

_start:	                 ;tell linker entry point
   mov   ecx, [num1]
   cmp   ecx, [num2]
   jg    check_third_num
   mov   ecx, [num2]
   
	check_third_num:

   cmp   ecx, [num3]
   jg    _exit
   mov   ecx, [num3]
   
	_exit:
   
   mov   [largest], ecx
   mov   ecx,msg
   mov   edx, len
   mov   ebx,1	;file descriptor (stdout)
   mov   eax,4	;system call number (sys_write)
   int   0x80	;call kernel
	
   mov   ecx,largest
   mov   edx, 2
   mov   ebx,1	;file descriptor (stdout)
   mov   eax,4	;system call number (sys_write)
   int   0x80	;call kernel
    
   mov   eax, 1
   int   80h

section	.data
   
   msg db "The largest digit is: ", 0xA,0xD 
   len equ $- msg 
   num1 dd '47'
   num2 dd '22'
   num3 dd '31'

segment .bss
   largest resb 2

เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -

The largest digit is: 
47

คำสั่ง JMP สามารถใช้สำหรับการใช้งานลูป ตัวอย่างเช่นสามารถใช้ข้อมูลโค้ดต่อไปนี้เพื่อดำเนินการลูปบอดี้ได้ 10 ครั้ง

MOV	CL, 10
L1:
<LOOP-BODY>
DEC	CL
JNZ	L1

อย่างไรก็ตามชุดคำสั่งโปรเซสเซอร์ประกอบด้วยกลุ่มคำสั่งแบบวนซ้ำสำหรับการใช้งานการวนซ้ำ คำสั่ง LOOP พื้นฐานมีไวยากรณ์ต่อไปนี้ -

LOOP 	label

โดยที่labelคือเลเบลเป้าหมายที่ระบุคำแนะนำเป้าหมายตามคำแนะนำในการกระโดด คำสั่ง LOOP ถือว่าไฟล์ECX register contains the loop count. เมื่อดำเนินการคำสั่งแบบวนซ้ำการลงทะเบียน ECX จะลดลงและตัวควบคุมจะกระโดดไปที่เลเบลเป้าหมายจนกระทั่งค่าทะเบียน ECX นั่นคือตัวนับถึงค่าศูนย์

ข้อมูลโค้ดด้านบนสามารถเขียนเป็น -

mov ECX,10
l1:
<loop body>
loop l1

ตัวอย่าง

โปรแกรมต่อไปนี้จะพิมพ์หมายเลข 1 ถึง 9 บนหน้าจอ -

section	.text
   global _start        ;must be declared for using gcc
	
_start:	                ;tell linker entry point
   mov ecx,10
   mov eax, '1'
	
l1:
   mov [num], eax
   mov eax, 4
   mov ebx, 1
   push ecx
	
   mov ecx, num        
   mov edx, 1        
   int 0x80
	
   mov eax, [num]
   sub eax, '0'
   inc eax
   add eax, '0'
   pop ecx
   loop l1
	
   mov eax,1             ;system call number (sys_exit)
   int 0x80              ;call kernel
section	.bss
num resb 1

เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -

123456789:

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

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

section	.text
   global _start        ;must be declared for using gcc
	
_start:	                ;tell linker entry point
   mov	eax,'3'
   sub     eax, '0'
	
   mov 	ebx, '4'
   sub     ebx, '0'
   add 	eax, ebx
   add	eax, '0'
	
   mov 	[sum], eax
   mov	ecx,msg	
   mov	edx, len
   mov	ebx,1	         ;file descriptor (stdout)
   mov	eax,4	         ;system call number (sys_write)
   int	0x80	         ;call kernel
	
   mov	ecx,sum
   mov	edx, 1
   mov	ebx,1	         ;file descriptor (stdout)
   mov	eax,4	         ;system call number (sys_write)
   int	0x80	         ;call kernel
	
   mov	eax,1	         ;system call number (sys_exit)
   int	0x80	         ;call kernel
	
section .data
msg db "The sum is:", 0xA,0xD 
len equ $ - msg   
segment .bss
sum resb 1

เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -

The sum is:
7

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

  • แบบฟอร์ม ASCII
  • BCD หรือ Binary Coded แบบฟอร์มทศนิยม

การเป็นตัวแทนของ ASCII

ในการแทนค่า ASCII ตัวเลขทศนิยมจะถูกเก็บเป็นสตริงของอักขระ ASCII ตัวอย่างเช่นค่าทศนิยม 1234 ถูกจัดเก็บเป็น -

31	32	33	34H

โดยที่ 31H คือค่า ASCII สำหรับ 1, 32H คือค่า ASCII สำหรับ 2 และอื่น ๆ มีสี่คำสั่งสำหรับการประมวลผลตัวเลขในการแทน ASCII -

  • AAA - ASCII ปรับหลังจากเพิ่ม

  • AAS - ASCII ปรับหลังการลบ

  • AAM - ASCII ปรับหลังจากการคูณ

  • AAD - ASCII ปรับก่อนหาร

คำแนะนำเหล่านี้ไม่ใช้ตัวถูกดำเนินการใด ๆ และถือว่าตัวถูกดำเนินการที่จำเป็นต้องอยู่ในทะเบียน AL

ตัวอย่างต่อไปนี้ใช้คำสั่ง AAS เพื่อแสดงแนวคิด -

section	.text
   global _start        ;must be declared for using gcc
	
_start:	                ;tell linker entry point
   sub     ah, ah
   mov     al, '9'
   sub     al, '3'
   aas
   or      al, 30h
   mov     [res], ax
	
   mov	edx,len	        ;message length
   mov	ecx,msg	        ;message to write
   mov	ebx,1	        ;file descriptor (stdout)
   mov	eax,4	        ;system call number (sys_write)
   int	0x80	        ;call kernel
	
   mov	edx,1	        ;message length
   mov	ecx,res	        ;message to write
   mov	ebx,1	        ;file descriptor (stdout)
   mov	eax,4	        ;system call number (sys_write)
   int	0x80	        ;call kernel
	
   mov	eax,1	        ;system call number (sys_exit)
   int	0x80	        ;call kernel
	
section	.data
msg db 'The Result is:',0xa	
len equ $ - msg			
section .bss
res resb 1

เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -

The Result is:
6

การเป็นตัวแทน BCD

การเป็นตัวแทน BCD มีสองประเภท -

  • การแสดง BCD ที่ไม่ได้บรรจุ
  • การแสดง BCD แบบแพ็ค

ในการแทนค่า BCD ที่คลายแพ็กแต่ละไบต์จะเก็บค่าฐานสองที่เทียบเท่ากับเลขฐานสิบ ตัวอย่างเช่นหมายเลข 1234 ถูกจัดเก็บเป็น -

01	02	03	04H

มีสองคำแนะนำสำหรับการประมวลผลตัวเลขเหล่านี้ -

  • AAM - ASCII ปรับหลังจากการคูณ

  • AAD - ASCII ปรับก่อนหาร

คำแนะนำการปรับ ASCII สี่คำสั่งคือ AAA, AAS, AAM และ AAD สามารถใช้กับการแสดง BCD ที่ไม่ได้บรรจุหีบห่อ ในการแทนค่า BCD แต่ละหลักจะถูกจัดเก็บโดยใช้สี่บิต ทศนิยมสองหลักถูกบรรจุลงในไบต์ ตัวอย่างเช่นหมายเลข 1234 ถูกจัดเก็บเป็น -

12	34H

มีสองคำแนะนำสำหรับการประมวลผลตัวเลขเหล่านี้ -

  • DAA - ปรับทศนิยมหลังจากเพิ่ม

  • DAS - ทศนิยมปรับหลังจากการลบ

ไม่มีการสนับสนุนสำหรับการคูณและการหารในการแสดง BCD แบบบรรจุ

ตัวอย่าง

โปรแกรมต่อไปนี้จะเพิ่มตัวเลขทศนิยม 5 หลักสองตัวและแสดงผลรวม ใช้แนวคิดข้างต้น -

section	.text
   global _start        ;must be declared for using gcc

_start:	                ;tell linker entry point

   mov     esi, 4       ;pointing to the rightmost digit
   mov     ecx, 5       ;num of digits
   clc
add_loop:  
   mov 	al, [num1 + esi]
   adc 	al, [num2 + esi]
   aaa
   pushf
   or 	al, 30h
   popf
	
   mov	[sum + esi], al
   dec	esi
   loop	add_loop
	
   mov	edx,len	        ;message length
   mov	ecx,msg	        ;message to write
   mov	ebx,1	        ;file descriptor (stdout)
   mov	eax,4	        ;system call number (sys_write)
   int	0x80	        ;call kernel
	
   mov	edx,5	        ;message length
   mov	ecx,sum	        ;message to write
   mov	ebx,1	        ;file descriptor (stdout)
   mov	eax,4	        ;system call number (sys_write)
   int	0x80	        ;call kernel
	
   mov	eax,1	        ;system call number (sys_exit)
   int	0x80	        ;call kernel

section	.data
msg db 'The Sum is:',0xa	
len equ $ - msg			
num1 db '12345'
num2 db '23456'
sum db '     '

เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -

The Sum is:
35801

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

  • การจัดเก็บความยาวสตริงอย่างชัดเจน
  • ใช้อักขระยาม

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

msg  db  'Hello, world!',0xa ;our dear string
len  equ  $ - msg            ;length of our dear string

$ ชี้ไปไบต์หลังจากที่ตัวอักษรตัวสุดท้ายของตัวแปรสตริงผงชูรส ดังนั้น,$-msgให้ความยาวของสตริง เรายังสามารถเขียน

msg db 'Hello, world!',0xa ;our dear string
len equ 13                 ;length of our dear string

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

ตัวอย่างเช่น -

message DB 'I am loving it!', 0

คำแนะนำสตริง

คำสั่งสตริงแต่ละคำสั่งอาจต้องการตัวถูกดำเนินการต้นทางตัวถูกดำเนินการปลายทางหรือทั้งสองอย่าง สำหรับเซ็กเมนต์ 32 บิตคำสั่งสตริงใช้ ESI และ EDI รีจิสเตอร์เพื่อชี้ไปยังตัวถูกดำเนินการต้นทางและปลายทางตามลำดับ

อย่างไรก็ตามสำหรับเซ็กเมนต์ 16 บิต SI และ DI รีจิสเตอร์จะใช้เพื่อชี้ไปยังต้นทางและปลายทางตามลำดับ

มีคำแนะนำพื้นฐานห้าประการสำหรับการประมวลผลสตริง พวกเขาคือ -

  • MOVS - คำสั่งนี้จะย้าย 1 Byte, Word หรือ Doubleword ของข้อมูลจากตำแหน่งหน่วยความจำไปยังอีกที่หนึ่ง

  • LODS- คำสั่งนี้โหลดจากหน่วยความจำ ถ้าตัวถูกดำเนินการมีขนาดหนึ่งไบต์จะถูกโหลดลงในทะเบียน AL หากตัวถูกดำเนินการเป็นคำเดียวจะถูกโหลดลงในทะเบียน AX และคำสองคำจะถูกโหลดลงในทะเบียน EAX

  • STOS - คำสั่งนี้จัดเก็บข้อมูลจากรีจิสเตอร์ (AL, AX หรือ EAX) ไปยังหน่วยความจำ

  • CMPS- คำสั่งนี้จะเปรียบเทียบรายการข้อมูลสองรายการในหน่วยความจำ ข้อมูลอาจมีขนาดไบต์คำหรือคำสองคำ

  • SCAS - คำสั่งนี้จะเปรียบเทียบเนื้อหาของรีจิสเตอร์ (AL, AX หรือ EAX) กับเนื้อหาของรายการในหน่วยความจำ

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

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

DS: SI (หรือ ESI) และ ES: DI (หรือ EDI) จะลงทะเบียนชี้ไปยังตัวถูกดำเนินการต้นทางและปลายทางตามลำดับ ตัวถูกดำเนินการต้นทางจะถือว่าอยู่ที่ DS: SI (หรือ ESI) และตัวถูกดำเนินการปลายทางที่ ES: DI (หรือ EDI) ในหน่วยความจำ

สำหรับที่อยู่ 16 บิตจะใช้การลงทะเบียน SI และ DI และสำหรับที่อยู่ 32 บิตจะใช้การลงทะเบียน ESI และ EDI

ตารางต่อไปนี้แสดงคำแนะนำสตริงเวอร์ชันต่างๆและพื้นที่สมมติของตัวถูกดำเนินการ

คำแนะนำพื้นฐาน โอเปร่าที่ การทำงานของไบต์ การทำงานของ Word การทำงานของคำสองคำ
MOVS ES: DI, DS: SI MOVSB MOVSW MOVSD
LODS AX, DS: SI LODSB LODSW LODSD
STOS ES: DI, AX STOSB STOSW STOSD
CMPS DS: SI, ES: DI CMPSB CMPSW CMPSD
SCAS ES: DI, AX SCASB SCASW SCASD

คำนำหน้าซ้ำ

คำนำหน้า REP เมื่อตั้งค่าก่อนคำสั่งสตริงเช่น - REP MOVSB ​​ทำให้เกิดการทำซ้ำของคำสั่งตามตัวนับที่วางไว้ที่รีจิสเตอร์ CX REP ดำเนินการตามคำสั่งลด CX ลง 1 และตรวจสอบว่า CX เป็นศูนย์หรือไม่ ทำซ้ำการประมวลผลคำสั่งจนกว่า CX จะเป็นศูนย์

Direction Flag (DF) กำหนดทิศทางของการดำเนินการ

  • ใช้ CLD (Clear Direction Flag, DF = 0) เพื่อดำเนินการจากซ้ายไปขวา
  • ใช้ STD (Set Direction Flag, DF = 1) เพื่อดำเนินการจากขวาไปซ้าย

คำนำหน้า REP ยังมีรูปแบบต่อไปนี้:

  • REP: เป็นการทำซ้ำโดยไม่มีเงื่อนไข ทำซ้ำการดำเนินการจนกว่า CX จะเป็นศูนย์

  • REPE หรือ REPZ: เป็นการทำซ้ำตามเงื่อนไข ทำซ้ำการดำเนินการในขณะที่ค่าสถานะศูนย์แสดงว่าเท่ากับ / ศูนย์ หยุดเมื่อ ZF ระบุว่าไม่เท่ากัน / ศูนย์หรือเมื่อ CX เป็นศูนย์

  • REPNE หรือ REPNZ: ยังเป็นการทำซ้ำตามเงื่อนไข ทำซ้ำการดำเนินการในขณะที่ค่าสถานะศูนย์ระบุว่าไม่เท่ากัน / ศูนย์ จะหยุดเมื่อ ZF ระบุว่าเท่ากับ / ศูนย์หรือเมื่อ CX ลดลงเป็นศูนย์

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

ตัวอย่างเช่นเราสามารถกำหนดตัวแปรคำว่า "เดือน" ด้วยวิธีใดวิธีหนึ่งดังต่อไปนี้ -

MONTHS	DW	12
MONTHS	DW	0CH
MONTHS	DW	0110B

คำสั่งนิยามข้อมูลยังสามารถใช้สำหรับการกำหนดอาร์เรย์หนึ่งมิติ ให้เรากำหนดอาร์เรย์ของตัวเลขหนึ่งมิติ

NUMBERS	DW  34,  45,  56,  67,  75, 89

คำจำกัดความข้างต้นประกาศอาร์เรย์หกคำแต่ละคำเริ่มต้นด้วยตัวเลข 34, 45, 56, 67, 75, 89 ซึ่งจะจัดสรรพื้นที่หน่วยความจำต่อเนื่อง 2x6 = 12 ไบต์ ที่อยู่สัญลักษณ์ของหมายเลขแรกจะเป็น NUMBERS และของตัวเลขที่สองจะเป็น NUMBERS + 2 เป็นต้นไป

ให้เรายกตัวอย่างอื่น คุณสามารถกำหนดอาร์เรย์ชื่อสินค้าคงคลังขนาด 8 และเริ่มต้นค่าทั้งหมดด้วยศูนย์เป็น -

INVENTORY   DW  0
            DW  0
            DW  0
            DW  0
            DW  0
            DW  0
            DW  0
            DW  0

ซึ่งสามารถย่อได้ว่า -

INVENTORY   DW  0, 0 , 0 , 0 , 0 , 0 , 0 , 0

นอกจากนี้ยังสามารถใช้คำสั่ง TIMES สำหรับการเริ่มต้นหลายค่าให้เป็นค่าเดียวกัน เมื่อใช้ TIMES อาร์เรย์ INVENTORY สามารถกำหนดเป็น:

INVENTORY TIMES 8 DW 0

ตัวอย่าง

ตัวอย่างต่อไปนี้แสดงให้เห็นถึงแนวคิดข้างต้นโดยการกำหนดอาร์เรย์ 3 องค์ประกอบ x ซึ่งเก็บค่าสามค่า: 2, 3 และ 4 จะเพิ่มค่าในอาร์เรย์และแสดงผลรวม 9 -

section	.text
   global _start   ;must be declared for linker (ld)
	
_start:	
 		
   mov  eax,3      ;number bytes to be summed 
   mov  ebx,0      ;EBX will store the sum
   mov  ecx, x     ;ECX will point to the current element to be summed

top:  add  ebx, [ecx]

   add  ecx,1      ;move pointer to next element
   dec  eax        ;decrement counter
   jnz  top        ;if counter not 0, then loop again

done: 

   add   ebx, '0'
   mov  [sum], ebx ;done, store result in "sum"

display:

   mov  edx,1      ;message length
   mov  ecx, sum   ;message to write
   mov  ebx, 1     ;file descriptor (stdout)
   mov  eax, 4     ;system call number (sys_write)
   int  0x80       ;call kernel
	
   mov  eax, 1     ;system call number (sys_exit)
   int  0x80       ;call kernel

section	.data
global x
x:    
   db  2
   db  4
   db  3

sum: 
   db  0

เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -

9

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

ไวยากรณ์

ต่อไปนี้เป็นไวยากรณ์เพื่อกำหนดขั้นตอน -

proc_name:
   procedure body
   ...
   ret

โพรซีเดอร์ถูกเรียกใช้จากฟังก์ชันอื่นโดยใช้คำสั่ง CALL คำสั่ง CALL ควรมีชื่อของขั้นตอนที่เรียกว่าเป็นอาร์กิวเมนต์ดังที่แสดงด้านล่าง -

CALL proc_name

ขั้นตอนที่เรียกว่าจะส่งคืนการควบคุมไปยังขั้นตอนการเรียกโดยใช้คำสั่ง RET

ตัวอย่าง

ให้เราเขียนขั้นตอนง่ายๆที่ชื่อsumที่เพิ่มตัวแปรที่เก็บไว้ในทะเบียน ECX และ EDX และส่งกลับผลรวมในทะเบียน EAX -

section	.text
   global _start        ;must be declared for using gcc
	
_start:	                ;tell linker entry point
   mov	ecx,'4'
   sub     ecx, '0'
	
   mov 	edx, '5'
   sub     edx, '0'
	
   call    sum          ;call sum procedure
   mov 	[res], eax
   mov	ecx, msg	
   mov	edx, len
   mov	ebx,1	        ;file descriptor (stdout)
   mov	eax,4	        ;system call number (sys_write)
   int	0x80	        ;call kernel
	
   mov	ecx, res
   mov	edx, 1
   mov	ebx, 1	        ;file descriptor (stdout)
   mov	eax, 4	        ;system call number (sys_write)
   int	0x80	        ;call kernel
	
   mov	eax,1	        ;system call number (sys_exit)
   int	0x80	        ;call kernel
sum:
   mov     eax, ecx
   add     eax, edx
   add     eax, '0'
   ret
	
section .data
msg db "The sum is:", 0xA,0xD 
len equ $- msg   

segment .bss
res resb 1

เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -

The sum is:
9

โครงสร้างข้อมูลกองซ้อน

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

ภาษาแอสเซมบลีมีคำสั่งสองคำสั่งสำหรับการดำเนินการสแต็ก: PUSH และ POP คำแนะนำเหล่านี้มีไวยากรณ์เช่น -

PUSH    operand
POP     address/register

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

การใช้งานสแต็กมีลักษณะดังต่อไปนี้ -

  • เท่านั้น words หรือ doublewords สามารถบันทึกลงในสแต็กไม่ใช่ไบต์

  • สแต็กเติบโตในทิศทางย้อนกลับกล่าวคือไปยังที่อยู่หน่วยความจำส่วนล่าง

  • ด้านบนของสแต็กชี้ไปยังรายการสุดท้ายที่แทรกในสแต็ก ชี้ไปที่ไบต์ล่างของคำสุดท้ายที่แทรก

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

; Save the AX and BX registers in the stack
PUSH    AX
PUSH    BX

; Use the registers for other purpose
MOV	AX, VALUE1
MOV 	BX, VALUE2
...
MOV 	VALUE1, AX
MOV	VALUE2, BX

; Restore the original values
POP	BX
POP	AX

ตัวอย่าง

โปรแกรมต่อไปนี้แสดงชุดอักขระ ASCII ทั้งหมด โปรแกรมหลักเรียกขั้นตอนที่ชื่อว่าdisplayซึ่งแสดงชุดอักขระ ASCII

section	.text
   global _start        ;must be declared for using gcc
	
_start:	                ;tell linker entry point
   call    display
   mov	eax,1	        ;system call number (sys_exit)
   int	0x80	        ;call kernel
	
display:
   mov    ecx, 256
	
next:
   push    ecx
   mov     eax, 4
   mov     ebx, 1
   mov     ecx, achar
   mov     edx, 1
   int     80h
	
   pop     ecx	
   mov	dx, [achar]
   cmp	byte [achar], 0dh
   inc	byte [achar]
   loop    next
   ret
	
section .data
achar db '0'

เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -

0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}
...
...

กระบวนการเรียกซ้ำคือกระบวนการที่เรียกตัวเอง การเรียกซ้ำมีสองประเภท: ทางตรงและทางอ้อม ในการเรียกซ้ำโดยตรงโพรซีเดอร์จะเรียกตัวเองและในการเรียกซ้ำทางอ้อมโพรซีเดอร์แรกเรียกโพรซีเดอร์ที่สองซึ่งจะเรียกโพรซีเดอร์แรก

การเรียกซ้ำสามารถสังเกตได้ในอัลกอริทึมทางคณิตศาสตร์จำนวนมาก ตัวอย่างเช่นพิจารณากรณีของการคำนวณแฟกทอเรียลของตัวเลข แฟกทอเรียลของตัวเลขกำหนดโดยสมการ -

Fact (n) = n * fact (n-1) for n > 0

ตัวอย่างเช่นแฟกทอเรียลของ 5 คือ 1 x 2 x 3 x 4 x 5 = 5 x แฟกทอเรียลของ 4 และนี่อาจเป็นตัวอย่างที่ดีในการแสดงโพรซีเดอร์แบบวนซ้ำ อัลกอริธึมการเรียกซ้ำทุกรายการต้องมีเงื่อนไขสิ้นสุดกล่าวคือควรหยุดการเรียกโปรแกรมแบบเรียกซ้ำเมื่อเงื่อนไขสำเร็จ ในกรณีของอัลกอริทึมแฟกทอเรียลเงื่อนไขสิ้นสุดจะถึงเมื่อ n เป็น 0

โปรแกรมต่อไปนี้แสดงวิธีการใช้แฟกทอเรียล n ในภาษาแอสเซมบลี เพื่อให้โปรแกรมเรียบง่ายเราจะคำนวณแฟกทอเรียล 3

section	.text
   global _start         ;must be declared for using gcc
	
_start:                  ;tell linker entry point

   mov bx, 3             ;for calculating factorial 3
   call  proc_fact
   add   ax, 30h
   mov  [fact], ax
    
   mov	  edx,len        ;message length
   mov	  ecx,msg        ;message to write
   mov	  ebx,1          ;file descriptor (stdout)
   mov	  eax,4          ;system call number (sys_write)
   int	  0x80           ;call kernel

   mov   edx,1            ;message length
   mov	  ecx,fact       ;message to write
   mov	  ebx,1          ;file descriptor (stdout)
   mov	  eax,4          ;system call number (sys_write)
   int	  0x80           ;call kernel
    
   mov	  eax,1          ;system call number (sys_exit)
   int	  0x80           ;call kernel
	
proc_fact:
   cmp   bl, 1
   jg    do_calculation
   mov   ax, 1
   ret
	
do_calculation:
   dec   bl
   call  proc_fact
   inc   bl
   mul   bl        ;ax = al * bl
   ret

section	.data
msg db 'Factorial 3 is:',0xa	
len equ $ - msg			

section .bss
fact resb 1

เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -

Factorial 3 is:
6

การเขียนมาโครเป็นอีกวิธีหนึ่งในการสร้างความมั่นใจในการเขียนโปรแกรมแบบแยกส่วนในภาษาแอสเซมบลี

  • มาโครคือลำดับของคำสั่งที่กำหนดโดยชื่อและสามารถใช้ได้ทุกที่ในโปรแกรม

  • ใน NASM มาโครถูกกำหนดด้วย %macro และ %endmacro คำสั่ง

  • มาโครเริ่มต้นด้วย% macro directive และลงท้ายด้วย% endmacro directive

ไวยากรณ์สำหรับนิยามมาโคร -

%macro macro_name  number_of_params
<macro body>
%endmacro

โดยที่number_of_paramsระบุพารามิเตอร์ตัวเลขmacro_nameระบุชื่อของมาโคร

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

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

mov	edx,len	    ;message length
mov	ecx,msg	    ;message to write
mov	ebx,1       ;file descriptor (stdout)
mov	eax,4       ;system call number (sys_write)
int	0x80        ;call kernel

ในตัวอย่างข้างต้นของการแสดงสตริงอักขระการลงทะเบียน EAX, EBX, ECX และ EDX ถูกใช้โดยการเรียกฟังก์ชัน INT 80H ดังนั้นทุกครั้งที่คุณต้องการแสดงบนหน้าจอคุณต้องบันทึกรีจิสเตอร์เหล่านี้ในสแต็กเรียกใช้ INT 80H จากนั้นเรียกคืนค่าดั้งเดิมของรีจิสเตอร์จากสแต็ก ดังนั้นการเขียนมาโครสองตัวเพื่อบันทึกและกู้คืนข้อมูลจะเป็นประโยชน์

เราสังเกตเห็นว่าคำแนะนำบางอย่างเช่น IMUL, IDIV, INT ฯลฯ จำเป็นต้องมีข้อมูลบางอย่างเพื่อจัดเก็บไว้ในการลงทะเบียนบางรายการและส่งคืนค่าในรีจิสเตอร์เฉพาะบางรายการ หากโปรแกรมใช้การลงทะเบียนเหล่านั้นในการเก็บรักษาข้อมูลสำคัญอยู่แล้วข้อมูลที่มีอยู่จากการลงทะเบียนเหล่านี้ควรถูกบันทึกไว้ในสแต็กและเรียกคืนหลังจากดำเนินการคำสั่ง

ตัวอย่าง

ตัวอย่างต่อไปนี้แสดงการกำหนดและการใช้มาโคร -

; A macro with two parameters
; Implements the write system call
   %macro write_string 2 
      mov   eax, 4
      mov   ebx, 1
      mov   ecx, %1
      mov   edx, %2
      int   80h
   %endmacro
 
section	.text
   global _start            ;must be declared for using gcc
	
_start:                     ;tell linker entry point
   write_string msg1, len1               
   write_string msg2, len2    
   write_string msg3, len3  
	
   mov eax,1                ;system call number (sys_exit)
   int 0x80                 ;call kernel

section	.data
msg1 db	'Hello, programmers!',0xA,0xD 	
len1 equ $ - msg1 msg2 db 'Welcome to the world of,', 0xA,0xD len2 equ $- msg2 

msg3 db 'Linux assembly programming! '
len3 equ $- msg3

เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -

Hello, programmers!
Welcome to the world of,
Linux assembly programming!

ระบบจะพิจารณาข้อมูลอินพุตหรือเอาต์พุตเป็นสตรีมไบต์ มีสตรีมไฟล์มาตรฐานสามไฟล์ -

  • อินพุตมาตรฐาน (stdin)
  • เอาต์พุตมาตรฐาน (stdout) และ
  • ข้อผิดพลาดมาตรฐาน (stderr)

ตัวอธิบายไฟล์

file descriptorคือจำนวนเต็ม 16 บิตที่กำหนดให้กับไฟล์เป็น ID ไฟล์ เมื่อสร้างไฟล์ใหม่หรือเปิดไฟล์ที่มีอยู่ตัวอธิบายไฟล์จะถูกใช้เพื่อเข้าถึงไฟล์

ตัวอธิบายไฟล์ของสตรีมไฟล์มาตรฐาน - stdin, stdout และ stderr คือ 0, 1 และ 2 ตามลำดับ

ตัวชี้ไฟล์

file pointerระบุตำแหน่งสำหรับการดำเนินการอ่าน / เขียนที่ตามมาในไฟล์ในรูปของไบต์ แต่ละไฟล์ถือเป็นลำดับไบต์ ไฟล์ที่เปิดแต่ละไฟล์เชื่อมโยงกับตัวชี้ไฟล์ที่ระบุออฟเซ็ตเป็นไบต์โดยสัมพันธ์กับจุดเริ่มต้นของไฟล์ เมื่อเปิดไฟล์ตัวชี้ไฟล์จะถูกตั้งค่าเป็นศูนย์

ระบบจัดการไฟล์โทร

ตารางต่อไปนี้อธิบายสั้น ๆ เกี่ยวกับการเรียกระบบที่เกี่ยวข้องกับการจัดการไฟล์ -

% eax ชื่อ % ebx % ecx % edx
2 sys_fork โครงสร้าง pt_regs - -
3 sys_read int ที่ไม่ได้ลงนาม ถ่าน * size_t
4 sys_write int ที่ไม่ได้ลงนาม const ถ่าน * size_t
5 sys_open const ถ่าน * int int
6 sys_close int ที่ไม่ได้ลงนาม - -
8 sys_creat const ถ่าน * int -
19 sys_lseek int ที่ไม่ได้ลงนาม off_t int ที่ไม่ได้ลงนาม

ขั้นตอนที่จำเป็นสำหรับการใช้การเรียกระบบนั้นเหมือนกันดังที่เราได้กล่าวไว้ก่อนหน้านี้ -

  • ใส่หมายเลขการโทรของระบบในทะเบียน EAX
  • จัดเก็บอาร์กิวเมนต์สำหรับการเรียกระบบในการลงทะเบียน EBX, ECX และอื่น ๆ
  • โทรไปที่การขัดจังหวะที่เกี่ยวข้อง (80 ชม.)
  • โดยปกติผลลัพธ์จะถูกส่งกลับในทะเบียน EAX

การสร้างและเปิดไฟล์

สำหรับการสร้างและเปิดไฟล์ให้ดำเนินการดังต่อไปนี้ -

  • ใส่การเรียกระบบ sys_creat () หมายเลข 8 ในทะเบียน EAX
  • ใส่ชื่อไฟล์ในทะเบียน EBX
  • ใส่สิทธิ์ของไฟล์ในทะเบียน ECX

การเรียกระบบจะส่งคืนไฟล์ descriptor ของไฟล์ที่สร้างในทะเบียน EAX ในกรณีที่เกิดข้อผิดพลาดรหัสข้อผิดพลาดจะอยู่ในทะเบียน EAX

การเปิดไฟล์ที่มีอยู่

สำหรับการเปิดไฟล์ที่มีอยู่ให้ดำเนินการดังต่อไปนี้ -

  • ใส่การเรียกระบบ sys_open () หมายเลข 5 ในทะเบียน EAX
  • ใส่ชื่อไฟล์ในทะเบียน EBX
  • ใส่โหมดการเข้าถึงไฟล์ในทะเบียน ECX
  • ใส่สิทธิ์ของไฟล์ในการลงทะเบียน EDX

การเรียกระบบจะส่งคืนไฟล์ descriptor ของไฟล์ที่สร้างในทะเบียน EAX ในกรณีที่เกิดข้อผิดพลาดรหัสข้อผิดพลาดจะอยู่ในทะเบียน EAX

ในโหมดการเข้าถึงไฟล์ที่ใช้กันมากที่สุด ได้แก่ อ่านอย่างเดียว (0) เขียนอย่างเดียว (1) และอ่าน - เขียน (2)

อ่านจากไฟล์

สำหรับการอ่านจากไฟล์ให้ดำเนินการดังต่อไปนี้ -

  • ใส่การเรียกระบบ sys_read () หมายเลข 3 ในทะเบียน EAX

  • ใส่ file descriptor ในรีจิสเตอร์ EBX

  • วางตัวชี้ไปที่บัฟเฟอร์อินพุตในทะเบียน ECX

  • ใส่ขนาดบัฟเฟอร์เช่นจำนวนไบต์ที่จะอ่านในรีจิสเตอร์ EDX

การเรียกระบบจะส่งคืนจำนวนไบต์ที่อ่านในทะเบียน EAX ในกรณีที่เกิดข้อผิดพลาดรหัสข้อผิดพลาดจะอยู่ในทะเบียน EAX

การเขียนไฟล์

สำหรับการเขียนลงไฟล์ให้ดำเนินการดังต่อไปนี้ -

  • ใส่การเรียกระบบ sys_write () หมายเลข 4 ในทะเบียน EAX

  • ใส่ file descriptor ในรีจิสเตอร์ EBX

  • วางตัวชี้ไปที่บัฟเฟอร์เอาต์พุตในทะเบียน ECX

  • ใส่ขนาดบัฟเฟอร์เช่นจำนวนไบต์ที่จะเขียนในรีจิสเตอร์ EDX

การเรียกระบบจะส่งคืนจำนวนไบต์จริงที่เขียนในทะเบียน EAX ในกรณีที่เกิดข้อผิดพลาดรหัสข้อผิดพลาดจะอยู่ในทะเบียน EAX

การปิดไฟล์

สำหรับการปิดไฟล์ให้ดำเนินการดังต่อไปนี้ -

  • ใส่การเรียกระบบ sys_close () หมายเลข 6 ในทะเบียน EAX
  • ใส่ file descriptor ในรีจิสเตอร์ EBX

การเรียกระบบจะส่งคืนในกรณีที่เกิดข้อผิดพลาดรหัสข้อผิดพลาดในการลงทะเบียน EAX

การอัปเดตไฟล์

สำหรับการอัปเดตไฟล์ให้ดำเนินการดังต่อไปนี้ -

  • ใส่การเรียกระบบ sys_lseek () หมายเลข 19 ในทะเบียน EAX
  • ใส่ file descriptor ในรีจิสเตอร์ EBX
  • ใส่ค่าชดเชยในทะเบียน ECX
  • วางตำแหน่งอ้างอิงสำหรับออฟเซ็ตในรีจิสเตอร์ EDX

ตำแหน่งอ้างอิงอาจเป็น:

  • จุดเริ่มต้นของไฟล์ - ค่า 0
  • ตำแหน่งปัจจุบัน - ค่า 1
  • สิ้นสุดไฟล์ - ค่า 2

การเรียกระบบจะส่งคืนในกรณีที่เกิดข้อผิดพลาดรหัสข้อผิดพลาดในการลงทะเบียน EAX

ตัวอย่าง

โปรแกรมต่อไปนี้สร้างและเปิดไฟล์ชื่อmyfile.txtและเขียนข้อความ "ยินดีต้อนรับสู่ Tutorials Point" ในไฟล์นี้ ถัดไปโปรแกรมอ่านจากไฟล์และเก็บข้อมูลลงในบัฟเฟอร์ชื่อข้อมูล สุดท้ายก็จะแสดงข้อความตามที่จัดเก็บไว้ในข้อมูล

section	.text
   global _start         ;must be declared for using gcc
	
_start:                  ;tell linker entry point
   ;create the file
   mov  eax, 8
   mov  ebx, file_name
   mov  ecx, 0777        ;read, write and execute by all
   int  0x80             ;call kernel
	
   mov [fd_out], eax
    
   ; write into the file
   mov	edx,len          ;number of bytes
   mov	ecx, msg         ;message to write
   mov	ebx, [fd_out]    ;file descriptor 
   mov	eax,4            ;system call number (sys_write)
   int	0x80             ;call kernel
	
   ; close the file
   mov eax, 6
   mov ebx, [fd_out]
    
   ; write the message indicating end of file write
   mov eax, 4
   mov ebx, 1
   mov ecx, msg_done
   mov edx, len_done
   int  0x80
    
   ;open the file for reading
   mov eax, 5
   mov ebx, file_name
   mov ecx, 0             ;for read only access
   mov edx, 0777          ;read, write and execute by all
   int  0x80
	
   mov  [fd_in], eax
    
   ;read from file
   mov eax, 3
   mov ebx, [fd_in]
   mov ecx, info
   mov edx, 26
   int 0x80
    
   ; close the file
   mov eax, 6
   mov ebx, [fd_in]
   int  0x80 
	
   ; print the info 
   mov eax, 4
   mov ebx, 1
   mov ecx, info
   mov edx, 26
   int 0x80
       
   mov	eax,1             ;system call number (sys_exit)
   int	0x80              ;call kernel

section	.data
file_name db 'myfile.txt'
msg db 'Welcome to Tutorials Point'
len equ  $-msg

msg_done db 'Written to file', 0xa
len_done equ $-msg_done

section .bss
fd_out resb 1
fd_in  resb 1
info resb  26

เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -

Written to file
Welcome to Tutorials Point

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

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

ในกรณีที่มีข้อผิดพลาด sys_brk () จะส่งกลับค่า -1 หรือส่งกลับรหัสข้อผิดพลาดเชิงลบเอง ตัวอย่างต่อไปนี้แสดงให้เห็นถึงการจัดสรรหน่วยความจำแบบไดนามิก

ตัวอย่าง

โปรแกรมต่อไปนี้จัดสรรหน่วยความจำ 16kb โดยใช้การเรียกระบบ sys_brk () -

section	.text
   global _start         ;must be declared for using gcc
	
_start:	                 ;tell linker entry point

   mov	eax, 45		 ;sys_brk
   xor	ebx, ebx
   int	80h

   add	eax, 16384	 ;number of bytes to be reserved
   mov	ebx, eax
   mov	eax, 45		 ;sys_brk
   int	80h
	
   cmp	eax, 0
   jl	exit	;exit, if error 
   mov	edi, eax	 ;EDI = highest available address
   sub	edi, 4		 ;pointing to the last DWORD  
   mov	ecx, 4096	 ;number of DWORDs allocated
   xor	eax, eax	 ;clear eax
   std			 ;backward
   rep	stosd            ;repete for entire allocated area
   cld			 ;put DF flag to normal state
	
   mov	eax, 4
   mov	ebx, 1
   mov	ecx, msg
   mov	edx, len
   int	80h		 ;print a message

exit:
   mov	eax, 1
   xor	ebx, ebx
   int	80h
	
section	.data
msg    	db	"Allocated 16 kb of memory!", 10
len     equ	$ - msg

เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -

Allocated 16 kb of memory!