คอมพิวเตอร์ หน้าต่าง อินเทอร์เน็ต

โรงเรียนการประกอบ: ภาษาแอสเซมบลีสำหรับโปรเซสเซอร์กลางสถาปัตยกรรม ARM โรงเรียนการประกอบ: ภาษาแอสเซมบลีสำหรับโปรเซสเซอร์กลางสถาปัตยกรรม ARM มันทำงานอย่างไร

สวัสดีทุกคน!
โดยอาชีพฉันเป็นโปรแกรมเมอร์ Java งานหลายเดือนสุดท้ายทำให้ฉันต้องทำความคุ้นเคยกับการพัฒนาสำหรับ Android NDK และด้วยเหตุนี้จึงเขียนแอปพลิเคชันเนทิฟในภาษา C ที่นี่ฉันประสบปัญหาในการเพิ่มประสิทธิภาพไลบรารี Linux หลายคนกลับกลายเป็นว่าไม่ได้รับการปรับให้เหมาะสมที่สุดสำหรับ ARM และโหลดโปรเซสเซอร์อย่างหนัก ก่อนหน้านี้ฉันไม่เคยเขียนโปรแกรมในภาษาแอสเซมบลีเลย ดังนั้นในตอนแรกจึงเป็นเรื่องยากที่จะเริ่มเรียนภาษานี้ แต่ฉันก็ยังตัดสินใจลอง บทความนี้เขียนขึ้นเพื่อพูดตั้งแต่เริ่มต้นสำหรับผู้เริ่มต้น ฉันจะพยายามอธิบายพื้นฐานที่ฉันได้เรียนรู้ไปแล้ว ฉันหวังว่าจะมีคนสนใจ นอกจากนี้ฉันยินดีที่จะรับคำวิจารณ์ที่สร้างสรรค์จากผู้เชี่ยวชาญ

การแนะนำ
ก่อนอื่น เรามาดูกันว่า ARM คืออะไร Wikipedia ให้คำจำกัดความนี้:

สถาปัตยกรรม ARM (เครื่อง RISC ขั้นสูง, เครื่อง Acorn RISC, เครื่อง RISC ขั้นสูง) เป็นตระกูลของคอร์ไมโครโปรเซสเซอร์แบบ 32 บิตและ 64 บิตที่ได้รับลิขสิทธิ์ซึ่งพัฒนาโดย ARM Limited บริษัทพัฒนาเคอร์เนลและเครื่องมือสำหรับเคอร์เนลโดยเฉพาะ (คอมไพเลอร์ เครื่องมือแก้ไขจุดบกพร่อง ฯลฯ) สร้างรายได้โดยการออกใบอนุญาตสถาปัตยกรรมให้กับผู้ผลิตที่เป็นบุคคลที่สาม

หากใครยังไม่รู้ ขณะนี้อุปกรณ์เคลื่อนที่และแท็บเล็ตส่วนใหญ่ได้รับการพัฒนาบนสถาปัตยกรรมโปรเซสเซอร์นี้ ข้อได้เปรียบหลักของตระกูลนี้คือการใช้พลังงานต่ำเนื่องจากมักใช้ในระบบฝังตัวต่างๆ สถาปัตยกรรมมีการพัฒนาไปตามกาลเวลา และเริ่มต้นด้วย ARMv7 มีการกำหนดโปรไฟล์ไว้ 3 แบบ: 'A'(แอปพลิเคชัน) - แอปพลิเคชัน, 'R' (เรียลไทม์) - เรียลไทม์, 'M' (ไมโครคอนโทรลเลอร์) - ไมโครคอนโทรลเลอร์ คุณสามารถอ่านประวัติความเป็นมาของการพัฒนาเทคโนโลยีนี้และข้อมูลที่น่าสนใจอื่น ๆ ได้ใน Wikipedia หรือโดย Google บนอินเทอร์เน็ต ARM รองรับโหมดการทำงานที่แตกต่างกัน (Thumb และ ARM นอกจากนี้ Thumb-2 เพิ่งปรากฏขึ้นซึ่งเป็นส่วนผสมของ ARM และ Thumb) ในบทความนี้ เราจะดูที่โหมด ARM เอง ซึ่งมีการดำเนินการชุดคำสั่งแบบ 32 บิต

โปรเซสเซอร์ ARM แต่ละตัวถูกสร้างขึ้นจากบล็อกต่อไปนี้:

  • 37 รีจิสเตอร์ (ซึ่งมีเพียง 17 รายการเท่านั้นที่มองเห็นได้ในระหว่างการพัฒนา)
  • หน่วยตรรกะทางคณิตศาสตร์ (ALU) - ดำเนินงานทางคณิตศาสตร์และตรรกะ
  • Barrel shifter - อุปกรณ์ที่ออกแบบมาเพื่อย้ายบล็อกข้อมูลตามจำนวนบิตที่กำหนด
  • CP15 เป็นระบบพิเศษที่ควบคุมโปรเซสเซอร์ร่วม ARM
  • ตัวถอดรหัสคำสั่ง - เกี่ยวข้องกับการแปลงคำสั่งให้เป็นลำดับของการดำเนินการระดับย่อย
สิ่งเหล่านี้ไม่ใช่ส่วนประกอบทั้งหมดของ ARM แต่การเจาะลึกเข้าไปในโครงสร้างของโปรเซสเซอร์นั้นอยู่นอกเหนือขอบเขตของบทความนี้
การดำเนินการไปป์ไลน์
โปรเซสเซอร์ ARM ใช้ไปป์ไลน์ 3 ขั้นตอน (เริ่มต้นด้วย ARM8 ซึ่งเป็นไปป์ไลน์ 5 ขั้นตอน) ลองดูตัวอย่างไปป์ไลน์อย่างง่ายโดยใช้โปรเซสเซอร์ ARM7TDMI การดำเนินการแต่ละคำสั่งประกอบด้วยสามขั้นตอน:

1. ขั้นตอนการสุ่มตัวอย่าง (F)
ในขั้นตอนนี้ คำแนะนำจะไหลจาก RAM ไปยังไปป์ไลน์ของโปรเซสเซอร์
2. ขั้นตอนการถอดรหัส (D)
คำแนะนำจะถูกถอดรหัสและประเภทนั้นได้รับการยอมรับ
3. ขั้นตอนการดำเนินการ (E)
ข้อมูลเข้าสู่ ALU และดำเนินการ และค่าผลลัพธ์จะถูกเขียนไปยังรีจิสเตอร์ที่ระบุ

แต่เมื่อพัฒนา เราต้องคำนึงว่ามีคำสั่งที่ใช้หลายรอบการดำเนินการ เช่น โหลด (LDR) หรือจัดเก็บ ในกรณีนี้ ขั้นตอนการดำเนินการ (E) จะถูกแบ่งออกเป็นขั้นตอน (E1, E2, E3...)

การดำเนินการตามเงื่อนไข
หน้าที่ที่สำคัญที่สุดอย่างหนึ่งของแอสเซมเบลอร์ ARM คือการดำเนินการตามเงื่อนไข แต่ละคำสั่งสามารถดำเนินการได้ตามเงื่อนไขและใช้ส่วนต่อท้ายสำหรับสิ่งนี้ หากมีการเพิ่มส่วนต่อท้ายชื่อของคำสั่ง พารามิเตอร์จะถูกตรวจสอบก่อนที่จะดำเนินการ หากพารามิเตอร์ไม่ตรงตามเงื่อนไข คำสั่งจะไม่ถูกดำเนินการ คำต่อท้าย:
MI - จำนวนลบ
PL - บวกหรือศูนย์
AL - ดำเนินการตามคำสั่งเสมอ
มีคำต่อท้ายการดำเนินการแบบมีเงื่อนไขอีกมากมาย อ่านส่วนต่อท้ายและตัวอย่างที่เหลือในเอกสารอย่างเป็นทางการ: เอกสารประกอบ ARM
ตอนนี้ถึงเวลาที่จะต้องพิจารณา...
ไวยากรณ์แอสเซมเบลอร์ ARM พื้นฐาน
สำหรับผู้ที่เคยทำงานกับแอสเซมเบลอร์มาก่อนสามารถข้ามจุดนี้ไปได้ สำหรับคนอื่นๆ ฉันจะอธิบายพื้นฐานของการทำงานกับภาษานี้ ดังนั้นโปรแกรมภาษาแอสเซมบลีทุกโปรแกรมจึงประกอบด้วยคำสั่ง คำสั่งถูกสร้างขึ้นในลักษณะนี้:
(ฉลาก) (คำสั่ง|ตัวถูกดำเนินการ) (@ ความคิดเห็น)
ป้ายกำกับเป็นพารามิเตอร์ทางเลือก Instruction เป็นตัวช่วยจำคำสั่งโดยตรงที่ส่งไปยังโปรเซสเซอร์ คำแนะนำพื้นฐานและการใช้งานจะกล่าวถึงด้านล่าง ตัวดำเนินการ - ค่าคงที่, ที่อยู่การลงทะเบียน, ที่อยู่ใน RAM ความคิดเห็นเป็นพารามิเตอร์ทางเลือกที่ไม่ส่งผลต่อการทำงานของโปรแกรม
ลงทะเบียนชื่อ
อนุญาตให้ใช้ชื่อทะเบียนต่อไปนี้:
1.r0-r15

3.v1-v8 (รีจิสเตอร์ตัวแปร, r4 ถึง r11)

4.sb และ SB (การลงทะเบียนแบบคงที่, r9)

5.sl และ SL (r10)

6.fp และ FP (r11)

7.ip และ IP (r12)

8.sp และ SP (r13)

9.lr และ LR (r14)

10.pc และ PC (ตัวนับโปรแกรม, r15)

ตัวแปรและค่าคงที่
ในแอสเซมเบลอร์ ARM ก็เหมือนกับภาษาการเขียนโปรแกรมอื่นๆ (ในทางปฏิบัติ) สามารถใช้ตัวแปรและค่าคงที่ได้ แบ่งออกเป็นประเภทต่างๆ ดังต่อไปนี้:
  • ตัวเลข
  • ช่วยพัฒนาสมอง
  • สตริง
ตัวแปรตัวเลขจะถูกเตรียมใช้งานดังนี้:
เซต้า 100; ตัวแปรตัวเลข "a" ถูกสร้างขึ้นด้วยค่า 100
ตัวแปรสตริง:
Improb SETS "ตัวอักษร"; ตัวแปร Improb ถูกสร้างขึ้นด้วยค่า "ตัวอักษร" ความสนใจ! ค่าตัวแปรต้องมีความยาวไม่เกิน 5120 อักขระ
ตัวแปรบูลีนใช้ค่า TRUE และ FALSE ตามลำดับ
ตัวอย่างคำสั่งการประกอบ ARM
ในตารางนี้ ฉันได้รวบรวมคำแนะนำพื้นฐานที่จำเป็นสำหรับการพัฒนาเพิ่มเติม (ในขั้นตอนพื้นฐานที่สุด:):

เพื่อสนับสนุนการใช้คำสั่งพื้นฐาน เรามาเขียนตัวอย่างง่ายๆ กันก่อน แต่ก่อนอื่นเราจะต้องมี arm toolchain ฉันทำงานบน Linux ดังนั้นฉันจึงเลือก: frank.harvard.edu/~coldwell/toolchain (arm-unknown-linux-gnu toolchain) สามารถติดตั้งได้ง่ายเหมือนกับโปรแกรมอื่นๆ บน Linux ในกรณีของฉัน (Russian Fedora) ฉันจำเป็นต้องติดตั้งแพ็คเกจ rpm จากเว็บไซต์เท่านั้น
ตอนนี้ได้เวลาเขียนตัวอย่างง่ายๆ แล้ว โปรแกรมจะไม่มีประโยชน์อย่างแน่นอน แต่สิ่งสำคัญคือมันจะใช้งานได้ :) นี่คือรหัสที่ฉันเสนอให้คุณ:
start: @ บรรทัดเสริมที่ระบุจุดเริ่มต้นของโปรแกรม mov r0, #3 @ โหลด register r0 ด้วยค่า 3 mov r1, #2 @ ทำเช่นเดียวกันกับ register r1 เฉพาะตอนนี้ด้วยค่า 2 เพิ่ม r2, r1, r0 @ เพิ่มค่าของ r0 และ r1 คำตอบถูกเขียนไปที่ r2 mul r3, r1, r0 @ คูณค่าของ register r1 ด้วยค่าของ register r0 คำตอบถูกเขียนไปที่ r3 stop: b stop @ เส้นสิ้นสุดของโปรแกรม
เรารวบรวมโปรแกรมเพื่อรับไฟล์ .bin:
/usr/arm/bin/arm-unknown-linux-gnu-as -o arm.o arm.s /usr/arm/bin/arm-unknown-linux-gnu-ld -Ttext=0x0 -o ​​arm. แขนเอลฟ์ .o /usr/arm/bin/arm-unknown-linux-gnu-objcopy -O ไบนารี arm.elf arm.bin
(โค้ดอยู่ในไฟล์ arm.s และ toolchain ในกรณีของฉันอยู่ในไดเร็กทอรี /usr/arm/bin/)
หากทุกอย่างเป็นไปด้วยดี คุณจะมี 3 ไฟล์: arm.s (โค้ดจริง), arm.o, arm.elf, arm.bin (โปรแกรมปฏิบัติการจริง) ในการตรวจสอบการทำงานของโปรแกรมนั้นไม่จำเป็นต้องมีอุปกรณ์แขนของคุณเอง ติดตั้ง QEMU ก็เพียงพอแล้ว สำหรับการอ้างอิง:

QEMU เป็นโปรแกรมโอเพ่นซอร์สฟรีสำหรับจำลองฮาร์ดแวร์ของแพลตฟอร์มต่างๆ

รวมการจำลองโปรเซสเซอร์ Intel x86 และอุปกรณ์ I/O สามารถจำลอง 80386, 80486, Pentium, Pentium Pro, AMD64 และโปรเซสเซอร์อื่นๆ ที่รองรับ x86 ได้ PowerPC, ARM, MIPS, SPARC, SPARC64, m68k - เพียงบางส่วนเท่านั้น

ใช้งานได้กับพยางค์, FreeBSD, FreeDOS, Linux, Windows 9x, Windows 2000, Mac OS X, QNX, Android, ฯลฯ

ดังนั้นในการเลียนแบบ arm คุณจะต้องมี qemu-system-arm แพ็คเกจนี้อยู่ใน yum ดังนั้นสำหรับผู้ที่มี Fedora คุณไม่จำเป็นต้องกังวลและเพียงแค่รันคำสั่ง:
ยำติดตั้ง qemu-system-arm

ต่อไป เราต้องเปิดตัวจำลอง ARM เพื่อให้รันโปรแกรม arm.bin ของเรา ในการดำเนินการนี้ เราจะสร้างไฟล์ flash.bin ซึ่งจะเป็นหน่วยความจำแฟลชสำหรับ QEMU การทำเช่นนี้ง่ายมาก:
dd if=/dev/zero ของ=flash.bin bs=4096 นับ=4096 dd if=arm.bin ของ=flash.bin bs=4096 conv=notrunc
ตอนนี้เราโหลด QEMU ด้วยหน่วยความจำแฟลชที่ได้:
qemu-system-arm -M connex -pflash flash.bin -nographic -serial /dev/null
ผลลัพธ์จะเป็นดังนี้:

$ qemu-system-arm -M connex -pflash flash.bin -nographic -serial /dev/null
จอภาพ QEMU 0.15.1 - พิมพ์ "help" เพื่อดูข้อมูลเพิ่มเติม
(เคมู)

โปรแกรม arm.bin ของเราต้องเปลี่ยนค่าของรีจิสเตอร์ทั้งสี่ ดังนั้นเพื่อตรวจสอบการทำงานที่ถูกต้อง เรามาดูรีจิสเตอร์เดียวกันนี้กัน ทำได้โดยใช้คำสั่งง่ายๆ: ลงทะเบียนข้อมูล
ที่เอาต์พุต คุณจะเห็นการลงทะเบียน ARM ทั้งหมด 15 รายการ และสี่รายการจะมีการเปลี่ยนแปลงค่า ตรวจสอบ :) ค่ารีจิสเตอร์ตรงกับค่าที่สามารถคาดหวังได้หลังจากการทำงานของโปรแกรม:
(qemu) การลงทะเบียนข้อมูล R00=00000003 R01=00000002 R02=00000005 R03=00000006 R04=00000000 R05=00000000 R06=00000000 R07=00000000 R08=00000000 R09=00 000000 R10=00000000 R11=00000000 R12=00000000 R13=00000000 R14= 00000000 R15=00000010 PSR=400001d3 -Z-- svc32

ป.ล. ในบทความนี้ ฉันพยายามอธิบายพื้นฐานของการเขียนโปรแกรมในแอสเซมเบลอร์ ARM ฉันหวังว่าคุณจะสนุกกับมัน! นี่จะเพียงพอที่จะเจาะลึกเข้าไปในป่าของภาษานี้และเขียนโปรแกรมลงไป หากทุกอย่างเรียบร้อยดี ฉันจะเขียนเพิ่มเติมเกี่ยวกับสิ่งที่ฉันค้นพบด้วยตัวเอง หากมีข้อผิดพลาดกรุณาอย่าเตะผมนะครับ เนื่องจากผมยังใหม่กับการประกอบ

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

ในทางตรงกันข้าม RISC มีระบบคำสั่งที่ค่อนข้างง่าย โดยมีการแบ่งส่วนที่ชัดเจนตามประเภทของการทำงาน:

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

ตามกฎแล้ว (แต่ไม่เสมอไป และเฉพาะในกรณีที่โค้ดโปรแกรมเข้าสู่หน่วยความจำแคชของคอนโทรลเลอร์) คำสั่งเดียวจะถูกดำเนินการภายในหนึ่งรอบของโปรเซสเซอร์ ความยาวของคำสั่งโปรเซสเซอร์ ARM ได้รับการแก้ไข - 4 ไบต์ (หนึ่งคำในคอมพิวเตอร์) ในความเป็นจริง โปรเซสเซอร์ ARM สมัยใหม่สามารถเปลี่ยนไปใช้โหมดการทำงานอื่นได้ เช่น โหมด THUMB เมื่อความยาวคำสั่งกลายเป็น 2 ไบต์ ซึ่งจะทำให้โค้ดมีขนาดกะทัดรัดมากขึ้น อย่างไรก็ตาม เราไม่กล่าวถึงโหมดนี้ในบทความนี้ เนื่องจากไม่รองรับโปรเซสเซอร์ Amber ARM v2a ด้วยเหตุผลเดียวกัน เราจะไม่พิจารณาโหมดเช่น Jazelle (ปรับให้เหมาะสมสำหรับการรันโค้ด Java) และเราจะไม่พิจารณาคำสั่ง NEON - คำสั่งสำหรับการดำเนินการกับข้อมูลหลาย ๆ ตัว ท้ายที่สุดแล้ว เรากำลังศึกษาระบบการสอน ARM ล้วนๆ

การลงทะเบียนโปรเซสเซอร์ ARM

โปรเซสเซอร์ ARM มีชุดการลงทะเบียนหลายชุด ซึ่งปัจจุบันมีเพียง 16 ชุดเท่านั้นที่พร้อมใช้งานสำหรับโปรแกรมเมอร์ มีโหมดการทำงานของโปรเซสเซอร์หลายโหมด ขึ้นอยู่กับโหมดการทำงาน ธนาคารแห่งการลงทะเบียนที่เหมาะสมจะถูกเลือก โหมดการทำงานเหล่านี้:

  • โหมดแอปพลิเคชัน (USR, โหมดผู้ใช้),
  • โหมดหัวหน้างานหรือโหมดระบบปฏิบัติการ (SVC, โหมดหัวหน้างาน)
  • โหมดการประมวลผลขัดจังหวะ (IRQ, โหมดขัดจังหวะ) และ
  • โหมดการประมวลผล "การหยุดชะงักเร่งด่วน" (FIRQ, โหมดการขัดจังหวะอย่างรวดเร็ว)

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

โปรเซสเซอร์ ARM รุ่นเก่า นอกเหนือจากโหมดการทำงานข้างต้นแล้ว ยังมีโหมดเพิ่มเติม:

  • ยกเลิก (ใช้เพื่อจัดการข้อยกเว้นการเข้าถึงหน่วยความจำ)
  • ไม่ได้กำหนด (ใช้เพื่อนำตัวประมวลผลร่วมไปใช้งานในซอฟต์แวร์) และ
  • โหมดงานพิเศษของระบบปฏิบัติการ ระบบ

โปรเซสเซอร์ Amber ARM v2a ไม่มีโหมดเพิ่มเติมสามโหมดเหล่านี้

สำหรับ Amber ARM v2a ชุดของรีจิสเตอร์สามารถแสดงได้ดังนี้:

รีจิสเตอร์ r0-r7 จะเหมือนกันในทุกโหมด
รีจิสเตอร์ r8-r12 เป็นเรื่องปกติสำหรับโหมด USR, SVC, IRQ เท่านั้น
Register r13 เป็นตัวชี้สแต็ก เขาเป็นของตัวเองในทุกรูปแบบ
ลงทะเบียน r14 - การลงทะเบียนส่งคืนจากรูทีนย่อยก็แตกต่างกันในทุกโหมด
Register r15 เป็นตัวชี้ไปยังคำแนะนำในการปฏิบัติการ เป็นเรื่องปกติสำหรับทุกโหมด

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

ควรให้ความสนใจเป็นพิเศษในการลงทะเบียน r15 หรือที่เรียกว่า pc (Program Counter) ซึ่งเป็นตัวชี้ไปยังคำสั่งที่ปฏิบัติการได้ คุณสามารถดำเนินการทางคณิตศาสตร์และตรรกะต่างๆ กับเนื้อหาได้ ดังนั้นการทำงานของโปรแกรมจะย้ายไปยังที่อยู่อื่น อย่างไรก็ตาม โดยเฉพาะสำหรับโปรเซสเซอร์ ARM v2a ที่ใช้งานในระบบ Amber มีรายละเอียดปลีกย่อยบางประการในการตีความบิตของรีจิสเตอร์นี้

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

บิต 31:28 - แฟล็กสำหรับผลลัพธ์ของการดำเนินการทางคณิตศาสตร์หรือตรรกะ
บิต 27 - มาสก์ IRQ ขัดจังหวะ การขัดจังหวะจะถูกปิดใช้งานเมื่อตั้งค่าบิต
บิต 26 - มาสก์ขัดจังหวะ FIRQ การขัดจังหวะอย่างรวดเร็วจะถูกปิดใช้งานเมื่อบิตถูกตั้งค่า
บิต 25:2 - ตัวชี้ที่แท้จริงไปยังคำสั่งโปรแกรมใช้เวลาเพียง 26 บิต
บิต 1:0 - โหมดการทำงานของโปรเซสเซอร์ปัจจุบัน
3 - หัวหน้างาน
2 - ขัดจังหวะ
1 - ขัดจังหวะอย่างรวดเร็ว
0 - ผู้ใช้

ในโปรเซสเซอร์ ARM รุ่นเก่า แฟล็กและบิตบริการทั้งหมดจะอยู่ในรีจิสเตอร์แยกต่างหาก การลงทะเบียนสถานะโปรแกรมปัจจุบัน(cpsr) และ Saved Program Status Register (spsr) สำหรับการเข้าถึงซึ่งมีคำสั่งพิเศษแยกต่างหาก การทำเช่นนี้เพื่อขยายพื้นที่ที่อยู่ที่มีอยู่สำหรับโปรแกรม

ปัญหาอย่างหนึ่งในการเรียนรู้แอสเซมเบลอร์ ARM คือชื่ออื่นของรีจิสเตอร์บางตัว ตามที่กล่าวไว้ข้างต้น r15 เป็นพีซีเครื่องเดียวกัน นอกจากนี้ยังมี r13 - นี่คือ sp เดียวกัน (Stack Pointer), r14 คือ lr (Link Register) - การลงทะเบียนที่อยู่ผู้ส่งจากขั้นตอน นอกจากนี้ r12 ยังเป็น ip เดียวกัน (Intra-Procedure -call scratch register) ที่ใช้โดยคอมไพเลอร์ C ในวิธีพิเศษในการเข้าถึงพารามิเตอร์บนสแต็ก การตั้งชื่อทางเลือกดังกล่าวบางครั้งอาจสร้างความสับสนเมื่อคุณดูโค้ดโปรแกรมของผู้อื่น - พบการกำหนดการลงทะเบียนทั้งสองนี้อยู่ที่นั่น

คุณสมบัติของการรันโค้ด

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

ท้ายที่สุดแล้วไปป์ไลน์คืออะไร? ตอนนี้เลือกคำสั่งตัวประมวลผลหนึ่งคำสั่งจากโค้ดโปรแกรม คำสั่งก่อนหน้ากำลังถูกถอดรหัสแล้ว และคำสั่งก่อนหน้ากำลังดำเนินการอยู่ นี่เป็นกรณีของไปป์ไลน์ 3 ขั้นตอนของโปรเซสเซอร์ Amber A23 ซึ่งเราใช้ในโครงการของเราสำหรับบอร์ด Mars Rover2Mars Rover2 การดัดแปลงโปรเซสเซอร์ Amber A25 มีไปป์ไลน์ 5 ขั้นตอนซึ่งมีประสิทธิภาพมากยิ่งขึ้น แต่มีหนึ่ง BUT ที่ยิ่งใหญ่ คำสั่ง Jump บังคับให้โปรเซสเซอร์ล้างไปป์ไลน์และเติมใหม่ ดังนั้นจึงเลือกคำสั่งใหม่ แต่ยังไม่มีอะไรให้ถอดรหัสและยิ่งกว่านั้นไม่มีอะไรให้ดำเนินการทันที ประสิทธิภาพของการเรียกใช้โค้ดจะลดลงเมื่อมีการเปลี่ยนผ่านบ่อยครั้ง โปรเซสเซอร์สมัยใหม่มีกลไกการทำนายสาขาทุกประเภทที่เพิ่มประสิทธิภาพการเติมไปป์ไลน์ แต่โปรเซสเซอร์ของเราไม่มีสิ่งนี้ ไม่ว่าในกรณีใด ARM ก็ควรที่จะทำให้แต่ละคำสั่งสามารถดำเนินการตามเงื่อนไขได้

บนโปรเซสเซอร์ ARM ในคำสั่งประเภทใดก็ตาม บิตสี่บิตของเงื่อนไขการดำเนินการคำสั่งจะถูกเข้ารหัสในสี่บิตสูงสุดของรหัสคำสั่ง:

มีแฟล็กเงื่อนไขทั้งหมด 4 แฟล็กในโปรเซสเซอร์:
. เชิงลบ - ผลลัพธ์ของการดำเนินการเป็นลบ
. ศูนย์ - ผลลัพธ์คือศูนย์
. Carry - การพกพาเกิดขึ้นเมื่อดำเนินการกับหมายเลขที่ไม่ได้ลงนาม
. oVerflow - โอเวอร์โฟลว์เกิดขึ้นเมื่อดำเนินการกับหมายเลขที่เซ็นชื่อ ผลลัพธ์ไม่พอดีกับการลงทะเบียน)

แฟล็กทั้ง 4 เหล่านี้ก่อให้เกิดการรวมเงื่อนไขที่เป็นไปได้หลายอย่าง:

รหัส คำต่อท้าย ความหมาย ธง
4"h0 สมการ เท่ากัน ชุดซี
4"h1 ne ไม่เท่ากับ ซี เคลียร์
4"h2 ซีเอส/เอช ชุดยก/ไม่มีลายเซ็น สูงหรือเท่าเดิม ชุดซี
4"h3 ซีซี/ลอ หิ้วใส/ไม่มีลายเซ็นล่าง ซี ชัดเจน
4"h4 ไมล์ ลบ/ลบ เอ็นเซ็ต
4"h5 กรุณา บวก / บวกหรือศูนย์ ชัดเจน
4"h6 เทียบกับ ล้น ชุดวี
4"h7 วีซี ไม่มีล้น วี ชัดเจน
4"h8 สวัสดี ไม่ได้ลงนามสูงกว่า ชุด C และ Z ชัดเจน
4"h9 LS ไม่ได้ลงนามต่ำกว่าหรือเท่ากัน C clear หรือ Z set
4"ฮ่า ge ลงนามมากกว่าหรือเท่ากับ ยังไม่มีข้อความ == วี
4"ฮ lt ลงนามน้อยกว่า ยังไม่มีข้อความ != วี
4"ซ กท ลงนามมากกว่า. Z == 0,ยังไม่มีข้อความ == V
4"เอชดี เลอ ลงนามน้อยกว่าหรือเท่ากับ Z == 1 หรือ N != V
4"เขา อัล เสมอ (ไม่มีเงื่อนไข)
4"ซ - สภาพไม่ถูกต้อง

ตอนนี้นำไปสู่ความยากลำบากในการเรียนรู้คำสั่งโปรเซสเซอร์ ARM อีกครั้ง - ส่วนต่อท้ายจำนวนมากที่สามารถเพิ่มลงในโค้ดคำสั่งได้ ตัวอย่างเช่น การเพิ่มที่ตั้งค่าแฟล็ก Z คือคำสั่ง addeq เป็น add + ส่วนต่อท้าย eq ข้ามไปที่รูทีนย่อยหาก flag N=0 เป็น blpl เป็น bl + ส่วนต่อท้าย pl

ธง (เชิงลบ, ศูนย์, แคร์รี่, oVerflow)สิ่งเดียวกันนี้ไม่ได้ตั้งค่าไว้เสมอไปในระหว่างการดำเนินการทางคณิตศาสตร์หรือตรรกะดังเช่นที่เกิดขึ้นในโปรเซสเซอร์ x86 แต่เมื่อโปรแกรมเมอร์ต้องการเท่านั้น ด้วยเหตุนี้จึงมีคำต่อท้ายคำสั่งช่วยในการจำอื่น: "s" (ในโค้ดคำสั่งจะถูกเข้ารหัสด้วยบิต 20) ดังนั้น คำสั่งเพิ่มเติมจะไม่เปลี่ยนแฟล็ก แต่คำสั่ง adds จะเปลี่ยนแฟล็ก หรืออาจมีคำสั่งเพิ่มเติมแบบมีเงื่อนไขแต่จะเปลี่ยนแฟล็ก ตัวอย่างเช่น: addgts เป็นที่ชัดเจนว่าจำนวนชื่อคำสั่งที่เป็นไปได้ที่มีส่วนต่อท้ายที่แตกต่างกันสำหรับการดำเนินการตามเงื่อนไขและการตั้งค่าสถานะทำให้โค้ดแอสเซมบลีของโปรเซสเซอร์ ARM แปลกมากและอ่านยาก อย่างไรก็ตาม เมื่อเวลาผ่านไป คุณจะคุ้นเคยและเริ่มเข้าใจข้อความนี้

การดำเนินการทางคณิตศาสตร์และตรรกะ (การประมวลผลข้อมูล)

โปรเซสเซอร์ ARM สามารถดำเนินการทางคณิตศาสตร์และตรรกะได้หลากหลาย

รหัสการดำเนินการสี่บิตจริง (Opcode) มีอยู่ในบิตคำสั่งโปรเซสเซอร์

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

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

ตัวอย่างคำสั่งแอสเซมเบลอร์อย่างง่ายจะมีลักษณะดังนี้:

เพิ่ม r0,r1,r2 @ วางผลรวมของค่าของรีจิสเตอร์ r1 และ r2 ลงในรีจิสเตอร์ r0
ย่อย r5,r4,#7 @ วางส่วนต่าง (r4-7) ในรีจิสเตอร์ r5

การดำเนินการที่ทำนั้นมีการเข้ารหัสดังนี้:

4"h0 และตรรกะ AND Rd:= Rn และ shifter_operand
4"h1 หรือทางลอจิคัลพิเศษหรือ Rd:= Rn XOR shifter_operand
4"h2 การลบเลขคณิตย่อย Rd:= Rn - shifter_operand
4"h3 rsb การลบแบบย้อนกลับทางคณิตศาสตร์ Rd:= shifter_operand - Rn
4"h4 เพิ่มการบวกเลขคณิต Rd:= Rn + shifter_operand
4"h5 adc การบวกเลขคณิตบวกแฟล็กพกพา Rd:= Rn + shifter_operand + Carry Flag
4"h6 sbc การลบเลขคณิตพร้อม Carry Rd:= Rn - shifter_operand - NOT(Carry Flag)
4"h7 rsc การลบแบบย้อนกลับทางคณิตศาสตร์ด้วย Carry Rd:= shifter_operand - Rn - NOT(Carry Flag)
4"h8 tst แบบลอจิคัล AND แต่หากไม่มีการจัดเก็บผลลัพธ์ เฉพาะแฟล็ก Rn และ shifter_operand S บิตที่ตั้งค่าไว้เสมอเท่านั้นที่จะถูกเปลี่ยน
4"h9 teq Logical เอกสิทธิ์เฉพาะบุคคลหรือแต่ไม่ได้จัดเก็บผลลัพธ์ เฉพาะแฟล็ก Rn EOR shifter_operand เท่านั้นที่จะถูกเปลี่ยน
S บิตตั้งค่าไว้เสมอ
การเปรียบเทียบ 4"ha cmp หรือการลบทางคณิตศาสตร์โดยไม่เก็บผลลัพธ์ มีเพียงค่าสถานะ Rn เท่านั้นที่เปลี่ยน - shifter_operand S บิตจะถูกตั้งค่าเสมอ
4"hb cmn การเปรียบเทียบการผกผันหรือการบวกเลขคณิตโดยไม่เก็บผลลัพธ์ เฉพาะแฟล็ก Rn + shifter_operand S บิตเท่านั้นที่ตั้งค่าการเปลี่ยนแปลงเสมอ
4"hc orr ตรรกะหรือ Rd:= Rn หรือ shifter_operand
4"hd mov ค่าการคัดลอก Rd:= shifter_operand (ไม่มีตัวถูกดำเนินการตัวแรก)
4 "เขารีเซ็ตบิต Rd:= Rn และไม่ใช่ (shifter_operand)
4"hf mvn คัดลอกค่าผกผัน Rd:= ไม่ใช่ shifter_operand (ไม่มีตัวถูกดำเนินการตัวแรก)

ชิฟเตอร์บาร์เรล.

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

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

@คูณด้วย 9 คือการคูณตัวเลขด้วย 8
@ โดยเลื่อนไปทางซ้าย 3 บิตบวกอีกหมายเลขหนึ่ง
เพิ่ม r0, r1, r1, lsl #3 @ r0= r1+(r1<<3) = r1*9

@ การคูณด้วย 15 คือการคูณด้วย 16 ลบจำนวน
rsb r0, r1, r1, lsl #4 @ r0= (r1<<4)-r1 = r1*15

@ เข้าถึงตารางคำ 4 ไบต์ โดยที่
@r1 เป็นที่อยู่พื้นฐานของตาราง
@r2 คือดัชนีขององค์ประกอบในตาราง
ldr r0,

นอกจาก lsl การเลื่อนไปทางซ้ายแบบลอจิคัลแล้ว ยังมี lsr การเลื่อนไปทางขวาแบบลอจิคัล และ asr การเลื่อนทางขวาทางคณิตศาสตร์อีกด้วย (การเลื่อนเพื่อรักษาเครื่องหมาย บิตที่สำคัญที่สุดจะถูกคูณทางด้านซ้ายพร้อมกับการเลื่อน)

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

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

เพิ่ม r0, r1, r1, lsr r3 @ นี่คือ r0 = r1 + (r1>>r3);
เพิ่ม r0, r0, r1, lsr r3 @ นี่คือ r0 = r0 + (r1>>r3);

ดังนั้น shifter_operand คือสิ่งที่เราอธิบายในคำสั่งแอสเซมเบลอร์ เช่น "r1, lsr r3" หรือ "r2, lsl #5"

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

การใช้ตัวถูกดำเนินการตัวเลข

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

น่าเสียดายที่มีข้อจำกัดที่สำคัญประการหนึ่งที่นี่ เนื่องจากคำสั่งทั้งหมดมีความยาวคงที่ 4 ไบต์ (32 บิต) จึงไม่สามารถเข้ารหัสหมายเลข "ใดๆ" ในนั้นได้ ในรหัสการดำเนินการ 4 บิตถูกครอบครองโดยรหัสเงื่อนไขการดำเนินการ (Cond), 4 บิตสำหรับรหัสการดำเนินการเอง (Opcode) จากนั้น 4 บิต - การลงทะเบียนตัวรับ Rd และอีก 4 บิต - การลงทะเบียนของตัวถูกดำเนินการตัวแรก Rn บวกกับแฟล็กต่างๆ I 25 (เพียงหมายถึงค่าคงที่ตัวเลขในโค้ดการดำเนินการ) และ S 20 (การตั้งค่าแฟล็กหลังการดำเนินการ) โดยรวมแล้วเหลือเพียง 12 บิตสำหรับค่าคงที่ที่เป็นไปได้ซึ่งเรียกว่า shifter_operand - เราเห็นสิ่งนี้ด้านบน เนื่องจาก 12 บิตสามารถเข้ารหัสตัวเลขได้เฉพาะในช่วงแคบเท่านั้น ผู้พัฒนาโปรเซสเซอร์ ARM จึงตัดสินใจเข้ารหัสค่าคงที่ดังนี้ shifter_operand สิบสองบิตแบ่งออกเป็นสองส่วน: ตัวบ่งชี้การหมุนสี่บิต encode_imm และค่าตัวเลขจริงแปดบิต imm_8

บนโปรเซสเซอร์ ARM ค่าคงที่ถูกกำหนดให้เป็นตัวเลข 8 บิตภายในตัวเลข 32 บิต ซึ่งหมุนไปทางขวาด้วยจำนวนบิตคู่ นั่นคือ:

imm_32 = imm_8 ROR (encode_imm *2)

มันกลายเป็นเรื่องยุ่งยากทีเดียว ปรากฎว่าไม่สามารถใช้ตัวเลขคงที่ทุกตัวในคำสั่งแอสเซมเบลอร์ได้

คุณสามารถเขียน

เพิ่ม r0, r2, #255 @ ค่าคงที่ในรูปแบบทศนิยม
เพิ่ม r0, r3, #0xFF @ ค่าคงที่เป็นเลขฐานสิบหก

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

0: e28200ff เพิ่ม r0, r2, #255 ; 0xff
4: e28300ff เพิ่ม r0, r3, #255 ; 0xff

และคุณยังสามารถเขียนได้

เพิ่ม r0, r4, #512
เพิ่ม r0, r5, 0x650000

โค้ดที่คอมไพล์แล้วจะมีลักษณะดังนี้:

0: e2840c02 เพิ่ม r0, r4, #512 ; 0x200
4: e2850865 เพิ่ม r0, r5, #6619136 ; 0x650000

ในกรณีนี้แน่นอนว่าตัวเลข 512 นั้นไม่พอดีกับไบต์ แต่แล้วเราก็จินตนาการว่ามันอยู่ในรูปแบบเลขฐานสิบหก 32'h00000200 และเห็นว่านี่คือ 2 ขยายไปทางขวาอีก 24 บิต (1 ror 24) ค่าสัมประสิทธิ์การหมุนน้อยกว่า 24 สองเท่านั่นคือ 12 ดังนั้นปรากฎว่า shifter_operand = ( 4'hc , 8'h02 ) - นี่คือบิตที่มีนัยสำคัญน้อยที่สุดสิบสองบิตของคำสั่ง เช่นเดียวกับหมายเลข 0x650000 สำหรับเขา shifter_operand = ( 4'h8, 8'h65 )

เห็นได้ชัดว่าคุณไม่สามารถเขียนได้

เพิ่ม r0, r1,#1234567

หรือคุณไม่สามารถเขียนได้

mov r0, #511

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

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

mov r0, #511
ย่อย r0,r0,#1

วิธีที่สองในการโหลดหมายเลขเฉพาะลงในรีจิสเตอร์คือการอ่านจากตัวแปรที่สงวนไว้เป็นพิเศษซึ่งอยู่ในหน่วยความจำ:

ldr r7,my_var
.....
my_var: .word 0x123456

วิธีเขียนที่ง่ายที่สุดมีดังนี้:

ldr r2,=511

ในกรณีนี้ (สังเกตเครื่องหมาย "=") หากค่าคงที่สามารถแสดงเป็น imm_8 และ encode_imm หากสามารถใส่ลงในบิต 12 ของ shifter_operand ได้ คอมไพลเลอร์แอสเซมบลีจะคอมไพล์ ldr ลงในคำสั่ง mov โดยอัตโนมัติ แต่ถ้าไม่สามารถแสดงตัวเลขด้วยวิธีนี้ได้ คอมไพลเลอร์จะจองเซลล์หน่วยความจำในโปรแกรมสำหรับค่าคงที่นี้ และจะตั้งชื่อให้กับเซลล์หน่วยความจำนี้เองและคอมไพล์คำสั่งลงใน ldr

นี่คือสิ่งที่ฉันเขียน:

ldr r7,my_var
ldr r8,=511
ldr r8,=1024
ldr r9,=0x3456
........
My_var: .word 0x123456

หลังจากรวบรวมฉันได้รับสิ่งนี้:

18: e59f7030 ldr r7, ; 50
1c: e59f8030 ldr r8, ; 54
20: e3a08b01 mov r8, #1024 ; 0x400
24: e59f902c ldr r9, ; 58
.............
00000050 :
50: 00123456 .คำ 0x00123456
54: 000001ff .คำ 0x000001ff
58: 00003456 .คำ 0x00003456

โปรดทราบว่าคอมไพลเลอร์ใช้การกำหนดแอดเดรสหน่วยความจำโดยสัมพันธ์กับการลงทะเบียนพีซี (หรือที่เรียกว่า r15)

การอ่านเซลล์หน่วยความจำและการเขียนรีจิสเตอร์ลงในหน่วยความจำ

ดังที่ฉันเขียนไว้ข้างต้น โปรเซสเซอร์ ARM สามารถดำเนินการทางคณิตศาสตร์หรือตรรกะกับเนื้อหาของรีจิสเตอร์เท่านั้น ข้อมูลสำหรับการดำเนินการจะต้องอ่านจากหน่วยความจำ และผลลัพธ์ของการดำเนินการจะต้องถูกเขียนกลับเข้าไปในหน่วยความจำ มีคำสั่งพิเศษสำหรับสิ่งนี้: ldr (อาจมาจากการรวม "LoaD Register") สำหรับการอ่าน และ str (อาจเป็น "STore Register") สำหรับการเขียน

ดูเหมือนจะมีแค่สองทีมแต่จริงๆแล้วมีหลายทีม เพียงดูวิธีการเข้ารหัสคำสั่ง ldr /str บนโปรเซสเซอร์ Amber ARM เพื่อดูว่ามีบิตแฟล็กเสริมจำนวนเท่าใด L 20, W 21, B 22, U 23, P 24, I 25 - และพวกมันจะกำหนดพฤติกรรมเฉพาะของ คำสั่ง:

  • บิต L 20 กำหนดการเขียนหรืออ่าน 1 - ldr, อ่าน, 0 - str, เขียน
  • บิต B 22 กำหนดการอ่าน/เขียนคำแบบ 32 บิตหรือไบต์ 8 บิต 1 หมายถึงการดำเนินการแบบไบต์ เมื่อไบต์ถูกอ่านเข้าไปในรีจิสเตอร์ บิตที่สำคัญที่สุดของรีจิสเตอร์จะถูกรีเซ็ตเป็นศูนย์
  • บิต I 25 กำหนดการใช้ฟิลด์ออฟเซ็ต ถ้าฉัน 25 ==0 ออฟเซ็ตจะถูกตีความว่าเป็นออฟเซ็ตตัวเลขที่ต้องเพิ่มไปยังที่อยู่ฐานจากรีจิสเตอร์หรือลบออก แต่การเพิ่มหรือการลบขึ้นอยู่กับบิต U 23

(Cond) - เงื่อนไขในการดำเนินการ ตีความในลักษณะเดียวกับคำสั่งเชิงตรรกะ/เลขคณิต - การอ่านหรือการเขียนอาจเป็นแบบมีเงื่อนไขได้

ดังนั้นใน Assembly Text คุณสามารถเขียนได้ดังนี้:

ldr r1, @ ใน register r1 อ่านคำตามที่อยู่จาก register r0
ldrb r1, @ เข้าสู่ register r1 อ่านไบต์ตามที่อยู่จาก register r0
ldreq r2, @ การอ่านคำแบบมีเงื่อนไข
ldrgtb r2, @ อ่านไบต์แบบมีเงื่อนไข
ldr r3, @ อ่านคำที่ที่อยู่ 8 สัมพันธ์กับที่อยู่จากการลงทะเบียน r4
ldr r4, @ อ่านคำที่ที่อยู่ -16 สัมพันธ์กับที่อยู่จากการลงทะเบียน r5

เมื่อรวบรวมข้อความนี้แล้ว คุณสามารถดูรหัสจริงของคำสั่งเหล่านี้ได้:

0: e5901000 ldr r1,
4: e5d01000 ldrb r1,
8: 05912000 ldreq r2,
ค: c5d12000 ldrbgt r2,
10: e5943008 ldr r3,
14: e5154010 ldr r4,

ในตัวอย่างด้านบนฉันใช้แค่ ldr เท่านั้น แต่ str ก็ใช้ในลักษณะเดียวกันมาก

มีโหมดการเข้าถึงหน่วยความจำเขียนกลับก่อนดัชนีและหลังดัชนี ในโหมดเหล่านี้ ตัวชี้การเข้าถึงหน่วยความจำจะได้รับการอัปเดตก่อนหรือหลังดำเนินการคำสั่ง หากคุณคุ้นเคยกับภาษาการเขียนโปรแกรม C คุณจะคุ้นเคยกับโครงสร้างการเข้าถึงพอยน์เตอร์ เช่น ( *แหล่งที่มา++;) หรือ ( a=**** psource;). โปรเซสเซอร์ ARM ใช้โหมดการเข้าถึงหน่วยความจำนี้ เมื่อดำเนินการคำสั่งอ่าน รีจิสเตอร์สองตัวจะถูกอัพเดตพร้อมกัน - รีจิสเตอร์ตัวรับจะได้รับค่าที่อ่านจากหน่วยความจำ และค่าในรีจิสเตอร์ตัวชี้ไปยังเซลล์หน่วยความจำจะถูกเลื่อนไปข้างหน้าหรือข้างหลัง

ในความคิดของฉันการเขียนคำสั่งเหล่านี้ค่อนข้างไร้เหตุผล ใช้เวลานานในการทำความคุ้นเคย

ldr r3, ! @psrc++; r3 = *psrc;
ldr r3, , #4 @ r3 = *psrc; PSRC++;

คำสั่ง ldr คำสั่งแรกจะเพิ่มตัวชี้ก่อน จากนั้นจึงอ่าน คำสั่งที่สองจะอ่านก่อน จากนั้นจึงเพิ่มตัวชี้ ค่าของตัวชี้ psrc อยู่ในรีจิสเตอร์ r0

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

นี่คือตัวอย่างของรูปแบบโค้ดที่เป็นไปได้:

0: e7921003 ldr r1, @ read address - ผลรวมของค่าจากรีจิสเตอร์ r2 และ r3
4: e7b21003 ldr r1, ! @ เหมือนกัน แต่หลังจากอ่าน r2 จะเพิ่มขึ้นตามค่าจาก r3
8: e6932004 ldr r2, , r4 @ ก่อนอื่นจะมีการอ่านที่ที่อยู่ r3 จากนั้น r3 จะเพิ่มขึ้น r4
c: e7943185 ldr r3, @ อ่านที่อยู่ r4+r5*8
10: e7b43285 ldr r3, ! @ อ่านที่อยู่ r4+r5*32 หลังจากอ่าน r4 จะถูกตั้งค่าเป็นค่าของที่อยู่นี้
14: e69431a5 ldr r3, , r5, lsr #3 @ ที่อยู่สำหรับอ่าน r4 หลังจากดำเนินการคำสั่ง r4 จะถูกตั้งค่าเป็น r4+r5/8

นี่คือรูปแบบต่างๆ ของคำสั่งอ่าน/เขียนในโปรเซสเซอร์ ARM v2a

ในโปรเซสเซอร์ ARM รุ่นเก่า คำสั่งที่หลากหลายนี้จะยิ่งใหญ่ยิ่งขึ้น
นี่เป็นเพราะความจริงที่ว่าโปรเซสเซอร์อนุญาตให้อ่านไม่เพียงแต่คำ (ตัวเลข 32 บิต) และไบต์ แต่ยังรวมถึงครึ่งคำ (16 บิต, 2 ไบต์) จากนั้นคำต่อท้าย "h" จากคำว่าครึ่งคำจะถูกเพิ่มลงในคำสั่ง ldr / str คำสั่งจะมีลักษณะเหมือน ldrh หรือ strh นอกจากนี้ยังมีคำสั่งสำหรับการโหลดครึ่งคำ ldrsh หรือไบต์ ldrsb ตีความว่าเป็นตัวเลขที่เซ็นชื่อ ในกรณีเหล่านี้ บิตที่สำคัญที่สุดของคำหรือไบต์ที่โหลดจะถูกคูณเข้ากับบิตที่สำคัญที่สุดของคำทั้งหมดในรีจิสเตอร์ตัวรับ ตัวอย่างเช่น การโหลด halfword 0xff25 ด้วยคำสั่ง ldrsh ใน register ปลายทางจะให้ผลลัพธ์เป็น 0xffffff25

อ่านและเขียนได้หลายรายการ

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

การช่วยจำคำสั่งการถ่ายโอนบล็อกเริ่มต้นที่รูท ldm (LoaD Multiple) หรือ stm (Store Multiple) แต่ตามปกติใน ARM เรื่องราวที่มีคำต่อท้ายก็เริ่มต้นขึ้น

โดยทั่วไป คำสั่งจะมีลักษณะดังนี้:

op(cond)(โหมด) ถ(, {Register list} !}

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

รายการรีจิสเตอร์ระบุอยู่ในเครื่องหมายปีกกาคั่นด้วยเครื่องหมายจุลภาคหรือเป็นช่วง ตัวอย่างเช่น:

เอสทีเอ็ม r0,(r3,r1, r5-r8)

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

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

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

ใครจะคิดว่ามีสแต็กหน่วยความจำสี่ประเภท

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

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

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

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

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

หากคุณใช้สแต็กโดยใช้คำแนะนำของโปรเซสเซอร์ ARM คุณจะได้ภาพต่อไปนี้:

ทำไมทีมเดียวกันถึงต้องใช้ชื่อต่างกัน? ฉันไม่เข้าใจเลย... แน่นอนว่าควรสังเกตว่ามาตรฐานสแต็กสำหรับ ARM ยังคงเป็น Full Descending

ตัวชี้สแต็กในโปรเซสเซอร์ ARM คือการลงทะเบียน sp หรือ r13 นี่คือข้อตกลงโดยทั่วไป แน่นอนว่า การเขียน stm หรือการอ่าน ldm สามารถทำได้ด้วยรีจิสเตอร์ฐานอื่นๆ เช่นกัน อย่างไรก็ตาม คุณต้องจำไว้ว่า sp register แตกต่างจากรีจิสเตอร์อื่นอย่างไร - อาจแตกต่างกันในโหมดการทำงานของโปรเซสเซอร์ที่แตกต่างกัน (USR, SVC, IRQ, FIRQ) เนื่องจากมีธนาคารลงทะเบียนของตัวเอง

และอีกหนึ่งหมายเหตุ เขียนบรรทัดแบบนี้ในโค้ดแอสเซมบลี ARM ดัน (r0-r3), แน่นอนคุณสามารถ. แต่ในความเป็นจริงแล้วมันจะเป็นทีมเดียวกัน เอสทีเอ็มเอฟเอสพี!,(r0-r3).

สุดท้ายนี้ ฉันจะยกตัวอย่างโค้ดแอสเซมบลีและข้อความที่คอมไพล์แล้วแบบแยกส่วน เรามี:


เอสทีเอ็มเอฟเอสพี!,(r0-r3)
stmdb sp!,(r0-r3)
ดัน (r0-r3)

@ คำแนะนำทั้งสามนี้เหมือนกันและทำสิ่งเดียวกัน
ป๊อป(r0-r3)
แอลดีเมีย sp!,(r0-r3)
ldmfd r13!,(r0-r3)

เอสทีเอ็มเอฟดี r4,(r0-r3,r5,r8)
stmea r4!,(r0-r3,r7,r9,lr,พีซี)
ldm r5,(r0,พีซี)

หลังจากการรวบรวมเราได้รับ:

0: e92d000f กด (r0, r1, r2, r3)
4: e92d000f กด (r0, r1, r2, r3)
8: e92d000f กด (r0, r1, r2, r3)
ค: e8bd000f ป๊อป (r0, r1, r2, r3)
10: e8bd000f ป๊อป (r0, r1, r2, r3)
14: e8bd000f ป๊อป (r0, r1, r2, r3)
18: e904012f stmdb r4, (r0, r1, r2, r3, r5, r8)
1c: e8a4c28f stmia r4!, (r0, r1, r2, r3, r7, r9, lr, พีซี)
20: e8958001 ldm r5, (r0, พีซี)

การเปลี่ยนผ่านในโปรแกรม

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

โปรเซสเซอร์ Amber ARM v2a มีเพียงสองคำสั่ง: b (จากคำว่า Branch - สาขา, การเปลี่ยนแปลง) และ bl (สาขาที่มีลิงก์ - การเปลี่ยนแปลงในขณะที่ยังคงรักษาที่อยู่ผู้ส่ง)

ไวยากรณ์คำสั่งนั้นง่ายมาก:

ข(เงื่อนไข)ฉลาก
ป้ายกำกับ BL (เงื่อนไข)

เป็นที่ชัดเจนว่าการเปลี่ยนแปลงใด ๆ สามารถมีเงื่อนไขได้นั่นคือโปรแกรมอาจมีคำแปลก ๆ เช่นนี้ซึ่งเกิดจากราก "b" และ "bl" และส่วนต่อท้ายของเงื่อนไข (Cond):

beq, bne, bcs, bhs, bcc, blo, bmi, bpl, bvs, bvc, bhi, bls, bge, bgt, ble, bal, b

bleq, blne, blcs, blhs, blcc, bllo, blmi, blpl, blvs, blvc, blhi, blls, blge, blgt, blle, blal, bl

ความหลากหลายนั้นน่าทึ่งมากใช่ไหม?

คำสั่ง Jump ประกอบด้วยออฟเซ็ตออฟเซ็ต 24 บิต ที่อยู่การกระโดดจะคำนวณเป็นผลรวมของค่าปัจจุบันของตัวชี้ pc และหมายเลขออฟเซ็ตเลื่อนไปทางซ้าย 2 บิต ซึ่งแปลเป็นตัวเลขที่เซ็นชื่อ:

พีซีใหม่ = พีซี + ออฟเซ็ต*4

ดังนั้นช่วงของการเปลี่ยนภาพคือ 32MB ไปข้างหน้าหรือข้างหลัง

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

มาดูขั้นตอนเอาท์ไบต์จากโปรเจ็กต์สวัสดีโลกสำหรับ Amber SoC

000004a0<_outbyte>:
4a0: e59f1454 ldr r1, ; 8เอฟซี< адрес регистра данных UART >
4a4: e59f3454 ldr r3, ; 900< адрес регистра статуса UART >
4a8: e5932000 ldr r2, ; อ่านสถานะปัจจุบัน
4ac: e2022020 และ r2, r2, #32
4b0: e3520000 cmp r2, #0 ; ตรวจสอบว่า UART ไม่ยุ่ง
4b4: 05c10000 strbeq r0, ; เขียนอักขระลงใน UART เฉพาะในกรณีที่ไม่ยุ่ง
4b8: 01b0f00e ย้ายพีซี, lr ; การส่งคืนแบบมีเงื่อนไขจากขั้นตอนหาก UART ไม่ยุ่ง
4bc: 1afffff9 bne 4a8<_outbyte+0x8>; วนซ้ำเพื่อตรวจสอบสถานะ UART

ฉันคิดว่าจากความคิดเห็นในส่วนนี้ มันชัดเจนว่ากระบวนการนี้ทำงานอย่างไร

หมายเหตุสำคัญอีกประการหนึ่งเกี่ยวกับการเปลี่ยนภาพ รีจิสเตอร์ r15 (pc) สามารถใช้ในการดำเนินการทางคณิตศาสตร์หรือตรรกะทั่วไปเป็นรีจิสเตอร์ปลายทางได้ ดังนั้นคำสั่งอย่าง add pc,pc,#8 จึงเป็นคำสั่งสำหรับการย้ายไปยังที่อยู่อื่น

ต้องมีหมายเหตุอีกประการหนึ่งเกี่ยวกับการเปลี่ยนภาพ โปรเซสเซอร์ ARM รุ่นเก่ายังมีคำสั่งสาขาเพิ่มเติม bx, blx และ blj คำสั่งเหล่านี้เป็นคำสั่งสำหรับการข้ามไปยังส่วนของโค้ดด้วยระบบคำสั่งอื่น Bx /blx อนุญาตให้คุณสลับไปใช้รหัส THUMB 16 บิตของโปรเซสเซอร์ ARM Blj เป็นการเรียกขั้นตอนระบบคำสั่ง Jazelle (รองรับภาษา Java ในโปรเซสเซอร์ ARM) Amber ARM v2a ของเราไม่มีคำสั่งเหล่านี้

คำแนะนำโปรเซสเซอร์ ARM อื่น ๆ

ส่วนนี้จะอธิบายชุดคำสั่งของโปรเซสเซอร์ ARM7TDMI

4.1 คำอธิบายโดยย่อของรูปแบบ

ในส่วนนี้จะอธิบายโดยย่อเกี่ยวกับชุดคำสั่ง ARM และ Thumb

กุญแจสำคัญในตารางชุดคำสั่งแสดงอยู่ในตาราง 1.1

โปรเซสเซอร์ ARM7TDMI ใช้สถาปัตยกรรม ARMv4T คำอธิบายที่สมบูรณ์ยิ่งขึ้นของชุดคำสั่งทั้งสองมีอยู่ในคู่มืออ้างอิงสถาปัตยกรรม ARM

ตารางที่ 1.1. กุญแจสู่ตาราง

รูปแบบชุดคำสั่ง ARM แสดงในรูปที่ 1.5

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

รูปที่ 1.5. รูปแบบชุดคำสั่ง ARM

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

4.2 คำอธิบายโดยย่อของคำสั่ง ARM

ชุดคำสั่ง ARM แสดงไว้ในตารางที่ 1.2

ตารางที่ 1.2 การแนะนำคำสั่ง ARM โดยย่อ

การดำเนินงาน ไวยากรณ์แอสเซมบลี
การส่งสินค้า การส่งสินค้า MOV (cond)(S) ถ.
ไม่ส่งต่อ MVN (cond)(S) ถ.
การส่งต่อ SPSR ไปยังทะเบียน MRS (cond) ถ. SPSR
ส่งต่อ CPSR เพื่อลงทะเบียน MRS (cond) Rd, CPSR
การโอนทะเบียน SPSR MSR (cond) SPSR (สนาม), Rm
การส่งต่อ CPSR MSR (cond) CPSR (สนาม), Rm
การส่งผ่านค่าคงที่ไปยังแฟล็ก SPSR MSR (เงื่อนไข) SPSR_f, #32bit_Imm
การส่งผ่านค่าคงที่ไปยังแฟล็ก CPSR MSR (เงื่อนไข) CPSR_f, #32bit_Imm
เลขคณิต ส่วนที่เพิ่มเข้าไป เพิ่ม (cond)(S) Rd, Rn,
นอกจากนี้ด้วยการพกพา ADC (cond)(S) ถ. Rn,
การลบ ย่อย (cond)(S) ถ. Rn,
การลบด้วยการยก SBC (cond)(S) ถ. Rn,
การลบการลบแบบย้อนกลับ RSB (cond)(S) ถ. Rn,
การลบการลบแบบย้อนกลับด้วยการยก RSC (cond)(S) ถ. Rn,
การคูณ MUL (cond)(S) Rd, Rm, Rs
การคูณ-การสะสม มลา (cond)(S) ถ. Rm, Rs, Rn
การคูณตัวเลขยาวที่ไม่ได้ลงนาม UMULL
การคูณ - การสะสมของค่ายาวที่ไม่ได้ลงนาม UMLAL (cond)(S) RdLo, RdHi, Rm, Rs
การคูณลองเซ็นชื่อ SMULL (cond)(S) RdLo, RdHi, Rm, Rs
การคูณ - การสะสมค่าแบบยาวที่เซ็นชื่อ SMLAL (cond)(S) RdLo, RdHi, Rm, Rs
การเปรียบเทียบ ถ.ซีเอ็มพี (คอน)
การเปรียบเทียบเชิงลบ ถ.ซีเอ็มเอ็น (คอน)
ช่วยพัฒนาสมอง การตรวจสอบ TST (เงื่อนไข) Rn,
การตรวจสอบความเท่าเทียมกัน TEQ (เงื่อนไข) Rn,
บันทึก. และ และ (cond)(S) ถ. Rn
ไม่รวม หรือ EOR (cond)(S) ถ. Rn,
อ.อ ORR (cond)(S) ถ. Rn,
รีเซ็ตบิต BIC (cond)(S) ถ. Rn, >
การเปลี่ยนแปลง การเปลี่ยนแปลง (เงื่อนไข)ฉลาก
ตามลิงค์ครับ (เงื่อนไข)ฉลาก
การเปลี่ยนผ่านและการเปลี่ยนแปลงชุดคำสั่ง (เงื่อนไข)ร
การอ่าน คำ ถ.แอลดีอาร์ (คอน)
LDR(cond)ถนน T,
ไบต์ LDR (cond)Bถ.
LDR (cond)ถนน BT,
ไบต์ที่ลงนาม LDR(cond)ถนน เอสบี,
ครึ่งคำ LDR(cond)ถนน H,
ครึ่งคำพร้อมเครื่องหมาย LDR(cond)ถนน SH,
การดำเนินการกับบล็อกข้อมูลหลายบล็อก -
  • ด้วยการเพิ่มขึ้นล่วงหน้า
  • LDM (cond)ถนน IB(, !} {^}
  • ที่มีการเพิ่มขึ้นตามมา
  • LDM (เงื่อนไข)IA Rd(, !} {^}
  • โดยมีการลดลงเบื้องต้น
  • LDM (cond)DB ถ(, !} {^}
  • ตามด้วยการลดลง
  • LDM (cond)DAถ(, !} {^}
  • การทำงานของสแต็ก
  • LDM (เงื่อนไข) ถ.(, !}
  • การดำเนินการสแต็กและการกู้คืน CPSR
  • LDM (เงื่อนไข) ถ.(, !} ^
    การดำเนินการสแต็กกับการลงทะเบียนผู้ใช้ LDM (เงื่อนไข) ถ.(, !} ^
    บันทึก คำ STR (cond) ถ.
    คำที่มีความได้เปรียบในโหมดผู้ใช้ STR (cond)T ถ.
    ไบต์ STR (cond)B ถ.
    ไบต์พร้อมการตั้งค่าโหมดผู้ใช้ STR (cond)ถนน BT,
    ครึ่งคำ STR(cond)ถนน H,
    การดำเนินการกับบล็อกข้อมูลหลาย ๆ บล็อก -
  • ด้วยการเพิ่มขึ้นล่วงหน้า
  • STM (cond)ถนน IB(, !} {^}
  • ที่มีการเพิ่มขึ้นตามมา
  • STM (เงื่อนไข)IA Rd(, !} {^}
  • โดยมีการลดลงเบื้องต้น
  • STM (cond)DB ถ(, !} {^}
    o ตามด้วยการลดลง STM (cond)ถนน DA(, !} {^}
  • การทำงานของสแต็ก
  • STM(เงื่อนไข) ถ.(, !}
  • การดำเนินการสแต็กกับการลงทะเบียนผู้ใช้
  • STM(เงื่อนไข) ถ.(, !} ^
    แลกเปลี่ยน คำ SWP (cond) ถ. RM,
    ไบต์ SWP (cond)B Rd, Rm,
    โปรเซสเซอร์ร่วม การดำเนินงานเกี่ยวกับข้อมูล CDP(เงื่อนไข)หน้า , , CRd, CRn, CRm,
    ถ่ายโอนไปยังการลงทะเบียน ARM จากโปรเซสเซอร์ร่วม MRC(เงื่อนไข)หน้า , , ถ., CRn, CRm,
    ถ่ายโอนไปยังโปรเซสเซอร์ร่วมจากการลงทะเบียน ARM MCR(เงื่อนไข)หน้า , , ถ., CRn, CRm,
    การอ่าน LDC(เงื่อนไข)หน้า , CRd,
    บันทึก เอสทีซี(เงื่อนไข)หน้า , CRd,
    ซอฟต์แวร์ขัดจังหวะ SWI 24bit_Imm

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

    โหมดที่อยู่

    โหมดการกำหนดแอดเดรสเป็นขั้นตอนที่ใช้โดยคำสั่งต่าง ๆ เพื่อสร้างค่าที่ใช้ตามคำแนะนำ โปรเซสเซอร์ ARM7TDMI รองรับโหมดการกำหนดแอดเดรส 5 โหมด:

    • โหมด 1 - Shift ตัวถูกดำเนินการสำหรับคำแนะนำในการประมวลผลข้อมูล
    • โหมด 2 - อ่านและเขียนคำหรือไบต์ที่ไม่ได้ลงนาม
    • โหมด 3 - อ่านและเขียนไบต์ครึ่งคำหรือโหลดเครื่องหมาย
    • โหมด 4 - อ่านและเขียนหลายรายการ
    • โหมด 5 - อ่านและเขียนตัวประมวลผลร่วม

    โหมดการกำหนดแอดเดรสที่ระบุประเภทและรหัสช่วยในการจำแสดงอยู่ในตารางที่ 1.3

    ตารางที่ 1.3. โหมดที่อยู่

    โหมดที่อยู่ ที่อยู่ประเภทหรือโหมด รหัสช่วยในการจำหรือประเภทสแต็ก
    โหมด 2 ค่าคงที่ออฟเซ็ต
    การลงทะเบียนออฟเซ็ต
    การลงทะเบียนออฟเซ็ตสเกล
    ออฟเซ็ตที่จัดทำดัชนีไว้ล่วงหน้า -
    คงที่ !
    ลงทะเบียน !
    ทะเบียนมาตราส่วน !
    !
    !
    !
    !
    -
    คงที่ , #+/-12bit_Offset
    ลงทะเบียน , +/-฿
    ทะเบียนมาตราส่วน
    โหมด 2 สิทธิพิเศษ ค่าคงที่ออฟเซ็ต
    การลงทะเบียนออฟเซ็ต
    การลงทะเบียนออฟเซ็ตสเกล
    ออฟเซ็ตตามด้วยการจัดทำดัชนี -
    คงที่ , #+/-12bit_Offset
    ลงทะเบียน , +/-฿
    ทะเบียนมาตราส่วน , +/-Rm, LSL #5bit_shift_imm
    , +/-Rm, LSR #5bit_shift_imm
    , +/-Rm, ASR #5bit_shift_imm
    , +/-Rm, ROR #5bit_shift_imm
    โหมด 3, > ค่าคงที่ออฟเซ็ต
    !
    การจัดทำดัชนีภายหลัง , #+/-8bit_Offset
    ลงทะเบียน
    การจัดทำดัชนีล่วงหน้า !
    การจัดทำดัชนีภายหลัง , +/-฿
    โหมด 4 การอ่าน IA เพิ่มขึ้นในภายหลัง FD ลงเต็ม
    ED ว่างเปล่าจากมากไปหาน้อย
    DA การลดลงตามมา FA ขึ้นเต็มที่
    DB ลดลงล่วงหน้า EA ว่างเปล่าจากน้อยไปมาก
    โหมด 4, การบันทึก IA เพิ่มขึ้นในภายหลัง FD ลงเต็ม
    IB, เพิ่มล่วงหน้า ED ว่างเปล่าจากมากไปหาน้อย
    DA การลดลงตามมา FA ขึ้นเต็มที่
    DB ลดลงล่วงหน้า EA ว่างเปล่าจากน้อยไปมาก
    โหมด 5 การถ่ายโอนข้อมูลโปรเซสเซอร์ร่วม ค่าคงที่ออฟเซ็ต
    การจัดทำดัชนีล่วงหน้า !
    การจัดทำดัชนีภายหลัง , #+/-(8bit_Offset*4)

    ตัวดำเนินการ 2

    ตัวถูกดำเนินการเป็นส่วนหนึ่งของคำสั่งที่อ้างถึงข้อมูลหรืออุปกรณ์ต่อพ่วง ตัวถูกดำเนินการ 2 แสดงไว้ในตาราง 1.4

    ตารางที่ 1.4. ตัวดำเนินการ 2

    ฟิลด์ต่างๆ แสดงไว้ในตารางที่ 1.5

    ตารางที่ 1.5 เขตข้อมูล

    ฟิลด์เงื่อนไข

    ฟิลด์เงื่อนไขแสดงไว้ในตารางที่ 1.6

    ตารางที่ 1.6. ฟิลด์เงื่อนไข

    ประเภทฟิลด์ คำต่อท้าย คำอธิบาย เงื่อนไข
    สภาพ (เงื่อนไข) อีคิว เท่ากับ ซี=1
    NE ไม่เท่ากับ ซี=0
    ซี.เอส. ไม่ได้ลงนามมากกว่าหรือเท่ากับ ค=1
    ซีซี ไม่ได้ลงนามน้อยลง ค=0
    มิชิแกน เชิงลบ ยังไม่มีข้อความ=1
    พี.แอล. บวกหรือศูนย์ ยังไม่มีข้อความ=0
    VS ล้น วี=1
    วี.ซี. ไม่มีล้น วี=0
    สวัสดี ไม่ได้ลงนามเพิ่มเติม ค=1, ซี=0
    แอล.เอส. ไม่ได้ลงนามน้อยกว่าหรือเท่ากับ ค=0, ซี=1
    จีอี มากกว่าหรือเท่ากัน N=V (N=V=1 หรือ N=V=0)
    ร.ท น้อย NV (N=1 และ V=0) หรือ (N=0 และ V=1)
    กท มากกว่า Z=0, N=V (N=V=1 หรือ N=V=0)
    แอล.อี. น้อยกว่าหรือเท่ากัน Z=0 หรือ NV (N=1 และ V=0) หรือ (N=0 และ V=1)
    อัล จริงเสมอ ธงจะถูกละเว้น

    4.3 คำอธิบายโดยย่อของชุดคำสั่ง Thumb

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


    รูปที่ 1.6. รูปแบบชุดคำสั่ง Thumb

    ชุดคำสั่ง Thumb แสดงไว้ในตารางที่ 1.7

    ตารางที่ 1.7. คำอธิบายโดยย่อของชุดคำสั่ง Thumb

    การดำเนินการ ไวยากรณ์แอสเซมบลี
    การส่งต่อ (การคัดลอก) ค่าคงที่ ถ. MOV #8bit_Imm
    อาวุโสถึงรุ่นน้อง ถ. MOV, Hs
    รุ่นน้องถึงรุ่นพี่ MOV HD 1,000 บาท
    อาวุโสถึงอาวุโส MOV Hd, Hs
    เลขคณิต ส่วนที่เพิ่มเข้าไป เพิ่มถนน ฿ #3bit_Imm
    เพิ่มผู้เยาว์ไปยังผู้เยาว์ เพิ่ม ถ. ฿ ร
    เพิ่มคนโตไปหาคนสุดท้อง เพิ่มถ.เอช
    เพิ่มรุ่นน้องถึงรุ่นพี่ เพิ่ม Hd, 3,000 บาท
    เพิ่มคนโตเป็นคนโต เพิ่ม HD, Hs
    นอกจากนี้ด้วยค่าคงที่ เพิ่มถนน #8bit_Imm
    เพิ่มมูลค่าให้กับ SP เพิ่ม SP, #7bit_Imm เพิ่ม SP, #-7bit_Imm
    นอกจากนี้ด้วยการพกพา ถ.เอดีซี, 1,000 บาท
    การลบ ถนนย่อย, ฿, Rn ถนนย่อย, ฿, #3bit_Imm
    การลบค่าคงที่ ถ.ย่อย #8bit_Imm
    การลบด้วยการยก ถ.SBC, 3,000 บาท
    ลงชื่อผกผัน ถนน NEG, 3,000 บาท
    การคูณ ถนน MUL, Rs.
    เปรียบเทียบอายุน้อยกว่ากับอายุน้อยกว่า ถ.ซีเอ็มพี 1,000 บาท
    เปรียบเทียบรุ่นน้องและผู้อาวุโส ถ.ซีเอ็มพี,Hs
    เปรียบเทียบแก่กว่าและอายุน้อยกว่า CMP HD มูลค่า 1,000 บาท
    เปรียบเทียบเก่าและเก่า CMP HD, Hs
    เปรียบเทียบเชิงลบ ถนน CMN ราคา 3,000 บาท
    เปรียบเทียบกับค่าคงที่ ถ.ซีเอ็มพี #8bit_Imm
    ช่วยพัฒนาสมอง และ และถ., Rs
    ไม่รวม หรือ ถนน EOR, Rs
    หรือ ถ.ORR, Rs
    รีเซ็ตบิต ถนน BIC, Rs.
    ไม่ส่งต่อ ถนนเอ็มวีเอ็น 1,000 บาท
    การทดสอบบิต ถ.ทีเอสที, 3,000 บาท
    กะ/หมุน การเปลี่ยนแปลงทางลอจิคัลไปทางซ้าย ถนน LSL ฿ #5bit_shift_imm ถนน LSL ฿
    การเปลี่ยนแปลงทางตรรกะไปทางขวา ถนน LSR ฿ #5bit_shift_imm ถนน LSR ฿
    การเลื่อนเลขคณิตไปทางขวา ถนน ASR, ฿, #5bit_shift_imm ถนน ASR, ฿
    หมุนไปทางขวา ถ.ร.ร. 1,000 บาท
    การเปลี่ยนแปลง การกระโดดแบบมีเงื่อนไข -
    ป้ายบีอีคิว
    ป้ายบีเอ็นอี
    ป้ายบีซีเอส
    ป้ายบีซีซี
    ฉลากค่าดัชนีมวลกาย
    ป้าย BPL
    ป้ายบีวีเอส
    ป้ายบีวีซี
  • ค=1, ซี=0
  • ป้ายบีเอชไอ
  • ค=0, ซี=1
  • ป้ายบีแอลเอส
  • N=1, V=1 หรือ N=0, V=0
  • ป้ายบีจีอี
  • N=1, V=0 หรือ N=0, V=1
  • ป้ายบีแอลที
  • Z=0 และ ((N หรือ V=1) หรือ (N หรือ V=0))
  • ป้ายบีจีที
  • Z=1 หรือ ((N=1 หรือ V=0) หรือ (N=0 และ V=1))
  • ฉลาก BLE
    การกระโดดแบบไม่มีเงื่อนไข ป้ายบี
    ลิงค์ยาวๆ คลิกเลย ป้ายบีแอล
    การเปลี่ยนแปลงสถานะเพิ่มเติม -
  • ตามที่อยู่เป็นมล. ลงทะเบียน
  • BX อาร์เอส
  • ตามที่อยู่ในเซนต์ ลงทะเบียน
  • BX Hs
    การอ่าน ด้วยค่าคงที่ออฟเซ็ต -
  • คำ
  • ถนนแอลดีอาร์
  • ครึ่งคำ
  • ถนนแอลดีอาร์เอช
  • ไบต์
  • ถนนแอลดีบีอาร์
    ด้วยการลงทะเบียนออฟเซ็ต -
  • คำ
  • ถนนแอลดีอาร์
  • ครึ่งคำ
  • ถนนแอลดีอาร์เอช
  • ลงชื่อครึ่งคำ
  • ถนนแอลดีเอสเอช
    ถนนแอลดีบีอาร์
  • ไบต์ที่ลงนาม
  • ถนนแอลดีอาร์เอสบี,
    สัมพันธ์กับตัวนับโปรแกรมพีซี ถนนแอลดีอาร์
    สัมพันธ์กับสแต็กพอยน์เตอร์ SP ถนนแอลดีอาร์
    ที่อยู่ -
  • ผ่านทางพีซี
  • เพิ่ม Rd, PC, #10bit_Offset
  • โดยใช้เอสพี
  • เพิ่มถนน, SP, #10bit_Offset
    การอ่านหลาย ๆ แอลดีเมีย อาร์บี!,
    บันทึก ด้วยค่าคงที่ออฟเซ็ต -
  • คำ
  • ถนนเอสอาร์
  • ครึ่งคำ
  • ถนนเอสอาร์เอช
  • ไบต์
  • ถนน STRB
    ด้วยการลงทะเบียนออฟเซ็ต -
  • คำ
  • ถนนเอสอาร์
  • ครึ่งคำ
  • ถนนเอสอาร์เอช
  • ไบต์
  • ถนน STRB
    สัมพันธ์กับ SP ถนนเอสอาร์
    เข้าได้หลายรายการ เอสทีเมีย อาร์บี!,
    การผลัก/การแตกออกจากสแต็ก พุชรีจิสเตอร์ลงบนสแต็ก ดัน
    กด LR และลงทะเบียนลงบนสแต็ก ดัน
    ป๊อปลงทะเบียนจากสแต็ก โผล่
    ป๊อปรีจิสเตอร์และพีซีจากสแต็ก โผล่
    ซอฟต์แวร์ขัดจังหวะ - SWI 8bit_Imm

    ตอนแรก แขนแอสเซมเบลอร์ที่ค่อนข้างผิดปกติ (หากคุณเรียนรู้ใหม่จาก x86, เอ็มซีเอส51หรือ เอวีอาร์). แต่มีการจัดระบบที่ค่อนข้างเรียบง่ายและสมเหตุสมผล ดังนั้นจึงเรียนรู้ได้อย่างรวดเร็ว

    มีเอกสารภาษารัสเซียน้อยมากสำหรับแอสเซมเบลอร์ ฉันแนะนำให้คุณไปที่ 2 ลิงก์ (บางทีคุณอาจพบมากกว่านี้และบอกฉันฉันจะขอบคุณ):
    สถาปัตยกรรมและระบบคำสั่งของโปรเซสเซอร์ RISC ของตระกูล ARM - http://www.gaw.ru/html.cgi/txt/doc/micros/arm/index.htm
    ทำความเข้าใจกับแอสเซมเบลอร์ ARM จากบทความชุด “GBA ASM” โดยผู้แต่ง Mike H, trans อาควิลา - http://wasm.ru/series.php?sid=21

    ลิงค์สุดท้ายช่วยผมได้มาก เคลียร์หมอก =) สิ่งที่สองที่สามารถช่วยได้คือคอมไพเลอร์ C อย่างผิดปกติ IAR Embedded Workbench สำหรับ ARM(ต่อไปนี้จะเรียกง่ายๆว่า ไอเออาร์ อีดับเบิลยูอาร์ม). ความจริงก็คือตั้งแต่สมัยโบราณเขาสามารถ (เช่นเดียวกับคอมไพเลอร์ที่เคารพตนเองทั้งหมด) ในการรวบรวมโค้ด C เป็นโค้ดแอสเซมเบลอร์ ซึ่งในทางกลับกันก็สามารถคอมไพล์โดยแอสเซมเบลอร์ IAR ให้เป็นโค้ดอ็อบเจ็กต์ได้อย่างง่ายดายพอ ๆ กัน ดังนั้นจึงไม่มีอะไรดีไปกว่าการเขียนฟังก์ชันง่ายๆ ใน C โดยคอมไพล์ลงในแอสเซมเบลอร์ และจะชัดเจนทันทีว่าคำสั่งแอสเซมเบลอร์ใดทำอะไร ส่งผ่านอาร์กิวเมนต์อย่างไร และผลลัพธ์จะถูกส่งกลับอย่างไร คุณฆ่านกสองตัวด้วยหินนัดเดียว - คุณเรียนรู้แอสเซมเบลอร์และในเวลาเดียวกันก็รับข้อมูลเกี่ยวกับวิธีการรวมโค้ดแอสเซมบลีเข้ากับโปรเจ็กต์ C ฉันฝึกฝนเกี่ยวกับฟังก์ชันการนับ CRC16 และด้วยเหตุนี้ฉันจึงได้รับเวอร์ชันเต็มของแอสเซมเบลอร์ .

    นี่คือฟังก์ชันดั้งเดิมในภาษา C (u16 หมายถึง short ที่ไม่ได้ลงนาม, u32 หมายถึง int ที่ไม่ได้ลงนาม, u8 หมายถึงถ่านที่ไม่ได้ลงนาม):
    // ไฟล์ crc16.c
    u16 CRC16 (โมฆะ* databuf, ขนาด u32)
    {
    u16 tmpWord, crc16, idx;
    u8bitCnt;
    #กำหนด CRC_POLY 0x1021;

    ซีอาร์ซี16 = 0;
    idx=0;
    ในขณะที่ (ขนาด!=0)
    {
    /* xor ไบต์สูงของ crc16 และไบต์อินพุต */
    tmpWord = (crc16>>8) ^ (*(((u8*)databuf)+idx));
    /* เขียนผลลัพธ์ไปที่ crc16 ไบต์สูง */
    tmpWord<<= 8;
    crc16 = tmpWord + (0x00FF & crc16);
    สำหรับ (bitCnt=8;bitCnt!=0;bitCnt--)
    {
    /* ตรวจสอบแบตเตอรี่ลำดับสูง CRC */
    ถ้า (crc16 & 0x8000)
    {
    ซีอาร์ซี16<<= 1;
    crc16 ^= CRC_POLY;
    }
    อื่น
    ซีอาร์ซี16<<= 1;
    }
    idx++;
    ขนาด--;
    }
    กลับ crc16;
    }

    การรับรหัสแอสเซมบลี IAR EW ARM เพื่อสร้างนั้นง่ายมาก ในตัวเลือกของไฟล์ crc16.c (เพิ่มในโครงการ) ฉันทำเครื่องหมายที่ช่อง แทนที่การตั้งค่าที่สืบทอดมาจากนั้นบนแท็บรายการ ให้เลือกช่องทำเครื่องหมาย 3 ช่อง - ไฟล์แอสเซมเบลอร์เอาต์พุต, รวมแหล่งที่มาและ รวมข้อมูลเฟรมการโทร(แม้ว่าคุณอาจไม่จำเป็นต้องทำเครื่องหมายในช่องสุดท้าย แต่ก็ทำให้เกิดสิ่งที่ไม่จำเป็นมากมาย ซีเอฟไอ-คำสั่ง) หลังจากการคอมไพล์ ไฟล์ผลลัพธ์คือ project_folder\ewp\at91sam7x256_sram\List\crc16.s ไฟล์นี้สามารถเพิ่มลงในโปรเจ็กต์ได้อย่างง่ายดายเหมือนกับไฟล์ C (มันจะคอมไพล์ตามปกติ)

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

    ; ไฟล์ crc16.s
    ชื่อ crc16
    ซีอาร์ซีสาธารณะ16

    CRC_POLY EQU 0x1021

    ส่วน `.text`:รหัส:NOROOT(2)
    แขน

    // u16 CRC16 (โมฆะ* databuf, ขนาด u32)
    ;R0 - ส่งคืนผลลัพธ์, CRC16
    ;R1 - พารามิเตอร์ขนาด
    ;R2 - พารามิเตอร์ databuf (อยู่ที่นั่นเมื่อป้อน R0)
    ;R3, R12 - การลงทะเบียนชั่วคราว

    ซีอาร์ซี16:
    PUSH (R3,R12) โดยสุ่มพบว่าควรบันทึก R3 และ R13
    ; ไม่จำเป็น. แต่ฉันตัดสินใจที่จะบันทึกมันไว้เผื่อไว้
    ; เกิดขึ้น
    MOVS R2,R0 ;ตอนนี้ R2==ฐานข้อมูล
    MOV R3,#+0
    MOVS R0,R3 ;crc16 = 0
    CRC16_LOOP:
    CMP R1, #+0 ;ประมวลผลไบต์ทั้งหมดแล้ว (ขนาด==0)?
    BEQ CRC16_RETURN ;ถ้าใช่ ให้ออก
    LSR R3, R0, #+8 ;R3 = crc16>>8
    LDRB R12, ;R12 = *ฐานข้อมูล
    EOR R3, R3, R12 ;R3 = *databuf ^ สูง (crc16)
    LSL R3, R3, #+8 ;R3<<= 8 (tmpWord <<= 8)
    และ R0, R0, #+255 ;crc16 &= 0x00FF
    เพิ่ม R0, R0, R3 ;crc16 = tmpWord + (0x00FF & crc16)
    MOV R12, #+8 ;bitCnt = 8
    CRC16_BIT_LOOP:
    BEQ CRC16_NEXT_BYTE ;bitCnt == 0?
    TST R0,#0x8000 ;บิตทั้งหมดยังไม่ได้รับการประมวลผล
    BEQ CRC16_BIT15ZERO ;ตรวจสอบบิตที่สำคัญที่สุดของ crc16
    LSL R0,R0,#+1 ;crc16<<= 1
    MOV R3, #+(ต่ำ (CRC_POLY)) ;crc16 ^= CRC_POLY
    ORR R3,R3,#+(สูง(CRC_POLY)<< 8) ;
    อีโออาร์ R0,R3,R0 ;
    B CRC16_NEXT_BIT

    CRC16_BIT15ศูนย์:
    LSL R0,R0,#+1 ;crc16<<= 1
    CRC16_NEXT_BIT:
    ย่อย R12,R12,#+1 ;bitCnt--
    B CRC16_BIT_LOOP ;

    CRC16_NEXT_BYTE:
    เพิ่ม R2,R2,#+1 ;databuf++
    ย่อย R1,R1,#+1 ;ขนาด--
    B CRC16_LOOP ; วนซ้ำทุกไบต์

    CRC16_RETURN:
    POP (R3,R12); คืนค่าการลงทะเบียน
    BX LR ;รูทีนย่อย exit, R0==crc16

    คอมไพเลอร์ C ของ IAR สร้างโค้ดที่ดีอย่างน่าประหลาดใจ ฉันประสบความสำเร็จเพียงเล็กน้อยในการเพิ่มประสิทธิภาพ ฉันเพิ่งโยนรีจิสเตอร์ชั่วคราวเพิ่มเติมที่คอมไพเลอร์ต้องการใช้ออกไป (ด้วยเหตุผลบางอย่างมันจึงใช้ LR เป็นรีจิสเตอร์ชั่วคราวเพิ่มเติม แม้ว่า R3 และ R12 ก็เพียงพอแล้ว) และยังลบคำสั่งพิเศษสองสามคำสั่งที่ตรวจสอบตัวนับและตั้งค่าสถานะ ( เพียงเพิ่มส่วนต่อท้าย S ให้กับทีมที่จำเป็น)

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

    ในทางตรงกันข้าม RISC มีระบบคำสั่งที่ค่อนข้างง่าย โดยมีการแบ่งส่วนที่ชัดเจนตามประเภทของการทำงาน:

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

    ตามกฎแล้ว (แต่ไม่เสมอไป และเฉพาะในกรณีที่โค้ดโปรแกรมเข้าสู่หน่วยความจำแคชของคอนโทรลเลอร์) คำสั่งเดียวจะถูกดำเนินการภายในหนึ่งรอบของโปรเซสเซอร์ ความยาวของคำสั่งโปรเซสเซอร์ ARM ได้รับการแก้ไข - 4 ไบต์ (หนึ่งคำในคอมพิวเตอร์) ในความเป็นจริง โปรเซสเซอร์ ARM สมัยใหม่สามารถเปลี่ยนไปใช้โหมดการทำงานอื่นได้ เช่น โหมด THUMB เมื่อความยาวคำสั่งกลายเป็น 2 ไบต์ ซึ่งจะทำให้โค้ดมีขนาดกะทัดรัดมากขึ้น อย่างไรก็ตาม เราไม่กล่าวถึงโหมดนี้ในบทความนี้ เนื่องจากไม่รองรับโปรเซสเซอร์ Amber ARM v2a ด้วยเหตุผลเดียวกัน เราจะไม่พิจารณาโหมดเช่น Jazelle (ปรับให้เหมาะสมสำหรับการรันโค้ด Java) และเราจะไม่พิจารณาคำสั่ง NEON - คำสั่งสำหรับการดำเนินการกับข้อมูลหลาย ๆ ตัว ท้ายที่สุดแล้ว เรากำลังศึกษาระบบการสอน ARM ล้วนๆ

    การลงทะเบียนโปรเซสเซอร์ ARM

    โปรเซสเซอร์ ARM มีชุดการลงทะเบียนหลายชุด ซึ่งปัจจุบันมีเพียง 16 ชุดเท่านั้นที่พร้อมใช้งานสำหรับโปรแกรมเมอร์ มีโหมดการทำงานของโปรเซสเซอร์หลายโหมด ขึ้นอยู่กับโหมดการทำงาน ธนาคารแห่งการลงทะเบียนที่เหมาะสมจะถูกเลือก โหมดการทำงานเหล่านี้:

    • โหมดแอปพลิเคชัน (USR, โหมดผู้ใช้),
    • โหมดหัวหน้างานหรือโหมดระบบปฏิบัติการ (SVC, โหมดหัวหน้างาน)
    • โหมดการประมวลผลขัดจังหวะ (IRQ, โหมดขัดจังหวะ) และ
    • โหมดการประมวลผล "การหยุดชะงักเร่งด่วน" (FIRQ, โหมดการขัดจังหวะอย่างรวดเร็ว)

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

    โปรเซสเซอร์ ARM รุ่นเก่า นอกเหนือจากโหมดการทำงานข้างต้นแล้ว ยังมีโหมดเพิ่มเติม:

    • ยกเลิก (ใช้เพื่อจัดการข้อยกเว้นการเข้าถึงหน่วยความจำ)
    • ไม่ได้กำหนด (ใช้เพื่อนำตัวประมวลผลร่วมไปใช้งานในซอฟต์แวร์) และ
    • โหมดงานพิเศษของระบบปฏิบัติการ ระบบ

    โปรเซสเซอร์ Amber ARM v2a ไม่มีโหมดเพิ่มเติมสามโหมดเหล่านี้

    สำหรับ Amber ARM v2a ชุดของรีจิสเตอร์สามารถแสดงได้ดังนี้:

    รีจิสเตอร์ r0-r7 จะเหมือนกันในทุกโหมด
    รีจิสเตอร์ r8-r12 เป็นเรื่องปกติสำหรับโหมด USR, SVC, IRQ เท่านั้น
    Register r13 เป็นตัวชี้สแต็ก เขาเป็นของตัวเองในทุกรูปแบบ
    ลงทะเบียน r14 - การลงทะเบียนส่งคืนจากรูทีนย่อยก็แตกต่างกันในทุกโหมด
    Register r15 เป็นตัวชี้ไปยังคำแนะนำในการปฏิบัติการ เป็นเรื่องปกติสำหรับทุกโหมด

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

    ควรให้ความสนใจเป็นพิเศษในการลงทะเบียน r15 หรือที่เรียกว่า pc (Program Counter) ซึ่งเป็นตัวชี้ไปยังคำสั่งที่ปฏิบัติการได้ คุณสามารถดำเนินการทางคณิตศาสตร์และตรรกะต่างๆ กับเนื้อหาได้ ดังนั้นการทำงานของโปรแกรมจะย้ายไปยังที่อยู่อื่น อย่างไรก็ตาม โดยเฉพาะสำหรับโปรเซสเซอร์ ARM v2a ที่ใช้งานในระบบ Amber มีรายละเอียดปลีกย่อยบางประการในการตีความบิตของรีจิสเตอร์นี้

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

    บิต 31:28 - แฟล็กสำหรับผลลัพธ์ของการดำเนินการทางคณิตศาสตร์หรือตรรกะ
    บิต 27 - มาสก์ IRQ ขัดจังหวะ การขัดจังหวะจะถูกปิดใช้งานเมื่อตั้งค่าบิต
    บิต 26 - มาสก์ขัดจังหวะ FIRQ การขัดจังหวะอย่างรวดเร็วจะถูกปิดใช้งานเมื่อบิตถูกตั้งค่า
    บิต 25:2 - ตัวชี้ที่แท้จริงไปยังคำสั่งโปรแกรมใช้เวลาเพียง 26 บิต
    บิต 1:0 - โหมดการทำงานของโปรเซสเซอร์ปัจจุบัน
    3 - หัวหน้างาน
    2 - ขัดจังหวะ
    1 - ขัดจังหวะอย่างรวดเร็ว
    0 - ผู้ใช้

    ในโปรเซสเซอร์ ARM รุ่นเก่า แฟล็กและบิตบริการทั้งหมดจะอยู่ในรีจิสเตอร์แยกต่างหาก การลงทะเบียนสถานะโปรแกรมปัจจุบัน(cpsr) และ Saved Program Status Register (spsr) สำหรับการเข้าถึงซึ่งมีคำสั่งพิเศษแยกต่างหาก การทำเช่นนี้เพื่อขยายพื้นที่ที่อยู่ที่มีอยู่สำหรับโปรแกรม

    ปัญหาอย่างหนึ่งในการเรียนรู้แอสเซมเบลอร์ ARM คือชื่ออื่นของรีจิสเตอร์บางตัว ตามที่กล่าวไว้ข้างต้น r15 เป็นพีซีเครื่องเดียวกัน นอกจากนี้ยังมี r13 - นี่คือ sp เดียวกัน (Stack Pointer), r14 คือ lr (Link Register) - การลงทะเบียนที่อยู่ผู้ส่งจากขั้นตอน นอกจากนี้ r12 ยังเป็น ip เดียวกัน (Intra-Procedure -call scratch register) ที่ใช้โดยคอมไพเลอร์ C ในวิธีพิเศษในการเข้าถึงพารามิเตอร์บนสแต็ก การตั้งชื่อทางเลือกดังกล่าวบางครั้งอาจสร้างความสับสนเมื่อคุณดูโค้ดโปรแกรมของผู้อื่น - พบการกำหนดการลงทะเบียนทั้งสองนี้อยู่ที่นั่น

    คุณสมบัติของการรันโค้ด

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

    ท้ายที่สุดแล้วไปป์ไลน์คืออะไร? ตอนนี้เลือกคำสั่งตัวประมวลผลหนึ่งคำสั่งจากโค้ดโปรแกรม คำสั่งก่อนหน้ากำลังถูกถอดรหัสแล้ว และคำสั่งก่อนหน้ากำลังดำเนินการอยู่ นี่เป็นกรณีของไปป์ไลน์ 3 ขั้นตอนของโปรเซสเซอร์ Amber A23 ซึ่งเราใช้ในโครงการของเราสำหรับบอร์ด Mars Rover2Mars Rover2 การดัดแปลงโปรเซสเซอร์ Amber A25 มีไปป์ไลน์ 5 ขั้นตอนซึ่งมีประสิทธิภาพมากยิ่งขึ้น แต่มีหนึ่ง BUT ที่ยิ่งใหญ่ คำสั่ง Jump บังคับให้โปรเซสเซอร์ล้างไปป์ไลน์และเติมใหม่ ดังนั้นจึงเลือกคำสั่งใหม่ แต่ยังไม่มีอะไรให้ถอดรหัสและยิ่งกว่านั้นไม่มีอะไรให้ดำเนินการทันที ประสิทธิภาพของการเรียกใช้โค้ดจะลดลงเมื่อมีการเปลี่ยนผ่านบ่อยครั้ง โปรเซสเซอร์สมัยใหม่มีกลไกการทำนายสาขาทุกประเภทที่เพิ่มประสิทธิภาพการเติมไปป์ไลน์ แต่โปรเซสเซอร์ของเราไม่มีสิ่งนี้ ไม่ว่าในกรณีใด ARM ก็ควรที่จะทำให้แต่ละคำสั่งสามารถดำเนินการตามเงื่อนไขได้

    บนโปรเซสเซอร์ ARM ในคำสั่งประเภทใดก็ตาม บิตสี่บิตของเงื่อนไขการดำเนินการคำสั่งจะถูกเข้ารหัสในสี่บิตสูงสุดของรหัสคำสั่ง:

    มีแฟล็กเงื่อนไขทั้งหมด 4 แฟล็กในโปรเซสเซอร์:
    . เชิงลบ - ผลลัพธ์ของการดำเนินการเป็นลบ
    . ศูนย์ - ผลลัพธ์คือศูนย์
    . Carry - การพกพาเกิดขึ้นเมื่อดำเนินการกับหมายเลขที่ไม่ได้ลงนาม
    . oVerflow - โอเวอร์โฟลว์เกิดขึ้นเมื่อดำเนินการกับหมายเลขที่เซ็นชื่อ ผลลัพธ์ไม่พอดีกับการลงทะเบียน)

    แฟล็กทั้ง 4 เหล่านี้ก่อให้เกิดการรวมเงื่อนไขที่เป็นไปได้หลายอย่าง:

    รหัส คำต่อท้าย ความหมาย ธง
    4"h0 สมการ เท่ากัน ชุดซี
    4"h1 ne ไม่เท่ากับ ซี เคลียร์
    4"h2 ซีเอส/เอช ชุดยก/ไม่มีลายเซ็น สูงหรือเท่าเดิม ชุดซี
    4"h3 ซีซี/ลอ หิ้วใส/ไม่มีลายเซ็นล่าง ซี ชัดเจน
    4"h4 ไมล์ ลบ/ลบ เอ็นเซ็ต
    4"h5 กรุณา บวก / บวกหรือศูนย์ ชัดเจน
    4"h6 เทียบกับ ล้น ชุดวี
    4"h7 วีซี ไม่มีล้น วี ชัดเจน
    4"h8 สวัสดี ไม่ได้ลงนามสูงกว่า ชุด C และ Z ชัดเจน
    4"h9 LS ไม่ได้ลงนามต่ำกว่าหรือเท่ากัน C clear หรือ Z set
    4"ฮ่า ge ลงนามมากกว่าหรือเท่ากับ ยังไม่มีข้อความ == วี
    4"ฮ lt ลงนามน้อยกว่า ยังไม่มีข้อความ != วี
    4"ซ กท ลงนามมากกว่า. Z == 0,ยังไม่มีข้อความ == V
    4"เอชดี เลอ ลงนามน้อยกว่าหรือเท่ากับ Z == 1 หรือ N != V
    4"เขา อัล เสมอ (ไม่มีเงื่อนไข)
    4"ซ - สภาพไม่ถูกต้อง

    ตอนนี้นำไปสู่ความยากลำบากในการเรียนรู้คำสั่งโปรเซสเซอร์ ARM อีกครั้ง - ส่วนต่อท้ายจำนวนมากที่สามารถเพิ่มลงในโค้ดคำสั่งได้ ตัวอย่างเช่น การเพิ่มที่ตั้งค่าแฟล็ก Z คือคำสั่ง addeq เป็น add + ส่วนต่อท้าย eq ข้ามไปที่รูทีนย่อยหาก flag N=0 เป็น blpl เป็น bl + ส่วนต่อท้าย pl

    ธง (เชิงลบ, ศูนย์, แคร์รี่, oVerflow)สิ่งเดียวกันนี้ไม่ได้ตั้งค่าไว้เสมอไปในระหว่างการดำเนินการทางคณิตศาสตร์หรือตรรกะดังเช่นที่เกิดขึ้นในโปรเซสเซอร์ x86 แต่เมื่อโปรแกรมเมอร์ต้องการเท่านั้น ด้วยเหตุนี้จึงมีคำต่อท้ายคำสั่งช่วยในการจำอื่น: "s" (ในโค้ดคำสั่งจะถูกเข้ารหัสด้วยบิต 20) ดังนั้น คำสั่งเพิ่มเติมจะไม่เปลี่ยนแฟล็ก แต่คำสั่ง adds จะเปลี่ยนแฟล็ก หรืออาจมีคำสั่งเพิ่มเติมแบบมีเงื่อนไขแต่จะเปลี่ยนแฟล็ก ตัวอย่างเช่น: addgts เป็นที่ชัดเจนว่าจำนวนชื่อคำสั่งที่เป็นไปได้ที่มีส่วนต่อท้ายที่แตกต่างกันสำหรับการดำเนินการตามเงื่อนไขและการตั้งค่าสถานะทำให้โค้ดแอสเซมบลีของโปรเซสเซอร์ ARM แปลกมากและอ่านยาก อย่างไรก็ตาม เมื่อเวลาผ่านไป คุณจะคุ้นเคยและเริ่มเข้าใจข้อความนี้

    การดำเนินการทางคณิตศาสตร์และตรรกะ (การประมวลผลข้อมูล)

    โปรเซสเซอร์ ARM สามารถดำเนินการทางคณิตศาสตร์และตรรกะได้หลากหลาย

    รหัสการดำเนินการสี่บิตจริง (Opcode) มีอยู่ในบิตคำสั่งโปรเซสเซอร์

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

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

    ตัวอย่างคำสั่งแอสเซมเบลอร์อย่างง่ายจะมีลักษณะดังนี้:

    เพิ่ม r0,r1,r2 @ วางผลรวมของค่าของรีจิสเตอร์ r1 และ r2 ลงในรีจิสเตอร์ r0
    ย่อย r5,r4,#7 @ วางส่วนต่าง (r4-7) ในรีจิสเตอร์ r5

    การดำเนินการที่ทำนั้นมีการเข้ารหัสดังนี้:

    4"h0 และตรรกะ AND Rd:= Rn และ shifter_operand
    4"h1 หรือทางลอจิคัลพิเศษหรือ Rd:= Rn XOR shifter_operand
    4"h2 การลบเลขคณิตย่อย Rd:= Rn - shifter_operand
    4"h3 rsb การลบแบบย้อนกลับทางคณิตศาสตร์ Rd:= shifter_operand - Rn
    4"h4 เพิ่มการบวกเลขคณิต Rd:= Rn + shifter_operand
    4"h5 adc การบวกเลขคณิตบวกแฟล็กพกพา Rd:= Rn + shifter_operand + Carry Flag
    4"h6 sbc การลบเลขคณิตพร้อม Carry Rd:= Rn - shifter_operand - NOT(Carry Flag)
    4"h7 rsc การลบแบบย้อนกลับทางคณิตศาสตร์ด้วย Carry Rd:= shifter_operand - Rn - NOT(Carry Flag)
    4"h8 tst แบบลอจิคัล AND แต่หากไม่มีการจัดเก็บผลลัพธ์ เฉพาะแฟล็ก Rn และ shifter_operand S บิตที่ตั้งค่าไว้เสมอเท่านั้นที่จะถูกเปลี่ยน
    4"h9 teq Logical เอกสิทธิ์เฉพาะบุคคลหรือแต่ไม่ได้จัดเก็บผลลัพธ์ เฉพาะแฟล็ก Rn EOR shifter_operand เท่านั้นที่จะถูกเปลี่ยน
    S บิตตั้งค่าไว้เสมอ
    การเปรียบเทียบ 4"ha cmp หรือการลบทางคณิตศาสตร์โดยไม่เก็บผลลัพธ์ มีเพียงค่าสถานะ Rn เท่านั้นที่เปลี่ยน - shifter_operand S บิตจะถูกตั้งค่าเสมอ
    4"hb cmn การเปรียบเทียบการผกผันหรือการบวกเลขคณิตโดยไม่เก็บผลลัพธ์ เฉพาะแฟล็ก Rn + shifter_operand S บิตเท่านั้นที่ตั้งค่าการเปลี่ยนแปลงเสมอ
    4"hc orr ตรรกะหรือ Rd:= Rn หรือ shifter_operand
    4"hd mov ค่าการคัดลอก Rd:= shifter_operand (ไม่มีตัวถูกดำเนินการตัวแรก)
    4 "เขารีเซ็ตบิต Rd:= Rn และไม่ใช่ (shifter_operand)
    4"hf mvn คัดลอกค่าผกผัน Rd:= ไม่ใช่ shifter_operand (ไม่มีตัวถูกดำเนินการตัวแรก)

    ชิฟเตอร์บาร์เรล.

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

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

    @คูณด้วย 9 คือการคูณตัวเลขด้วย 8
    @ โดยเลื่อนไปทางซ้าย 3 บิตบวกอีกหมายเลขหนึ่ง
    เพิ่ม r0, r1, r1, lsl #3 @ r0= r1+(r1<<3) = r1*9

    @ การคูณด้วย 15 คือการคูณด้วย 16 ลบจำนวน
    rsb r0, r1, r1, lsl #4 @ r0= (r1<<4)-r1 = r1*15

    @ เข้าถึงตารางคำ 4 ไบต์ โดยที่
    @r1 เป็นที่อยู่พื้นฐานของตาราง
    @r2 คือดัชนีขององค์ประกอบในตาราง
    ldr r0,

    นอกจาก lsl การเลื่อนไปทางซ้ายแบบลอจิคัลแล้ว ยังมี lsr การเลื่อนไปทางขวาแบบลอจิคัล และ asr การเลื่อนทางขวาทางคณิตศาสตร์อีกด้วย (การเลื่อนเพื่อรักษาเครื่องหมาย บิตที่สำคัญที่สุดจะถูกคูณทางด้านซ้ายพร้อมกับการเลื่อน)

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

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

    เพิ่ม r0, r1, r1, lsr r3 @ นี่คือ r0 = r1 + (r1>>r3);
    เพิ่ม r0, r0, r1, lsr r3 @ นี่คือ r0 = r0 + (r1>>r3);

    ดังนั้น shifter_operand คือสิ่งที่เราอธิบายในคำสั่งแอสเซมเบลอร์ เช่น "r1, lsr r3" หรือ "r2, lsl #5"

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

    การใช้ตัวถูกดำเนินการตัวเลข

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

    น่าเสียดายที่มีข้อจำกัดที่สำคัญประการหนึ่งที่นี่ เนื่องจากคำสั่งทั้งหมดมีความยาวคงที่ 4 ไบต์ (32 บิต) จึงไม่สามารถเข้ารหัสหมายเลข "ใดๆ" ในนั้นได้ ในรหัสการดำเนินการ 4 บิตถูกครอบครองโดยรหัสเงื่อนไขการดำเนินการ (Cond), 4 บิตสำหรับรหัสการดำเนินการเอง (Opcode) จากนั้น 4 บิต - การลงทะเบียนตัวรับ Rd และอีก 4 บิต - การลงทะเบียนของตัวถูกดำเนินการตัวแรก Rn บวกกับแฟล็กต่างๆ I 25 (เพียงหมายถึงค่าคงที่ตัวเลขในโค้ดการดำเนินการ) และ S 20 (การตั้งค่าแฟล็กหลังการดำเนินการ) โดยรวมแล้วเหลือเพียง 12 บิตสำหรับค่าคงที่ที่เป็นไปได้ซึ่งเรียกว่า shifter_operand - เราเห็นสิ่งนี้ด้านบน เนื่องจาก 12 บิตสามารถเข้ารหัสตัวเลขได้เฉพาะในช่วงแคบเท่านั้น ผู้พัฒนาโปรเซสเซอร์ ARM จึงตัดสินใจเข้ารหัสค่าคงที่ดังนี้ shifter_operand สิบสองบิตแบ่งออกเป็นสองส่วน: ตัวบ่งชี้การหมุนสี่บิต encode_imm และค่าตัวเลขจริงแปดบิต imm_8

    บนโปรเซสเซอร์ ARM ค่าคงที่ถูกกำหนดให้เป็นตัวเลข 8 บิตภายในตัวเลข 32 บิต ซึ่งหมุนไปทางขวาด้วยจำนวนบิตคู่ นั่นคือ:

    imm_32 = imm_8 ROR (encode_imm *2)

    มันกลายเป็นเรื่องยุ่งยากทีเดียว ปรากฎว่าไม่สามารถใช้ตัวเลขคงที่ทุกตัวในคำสั่งแอสเซมเบลอร์ได้

    คุณสามารถเขียน

    เพิ่ม r0, r2, #255 @ ค่าคงที่ในรูปแบบทศนิยม
    เพิ่ม r0, r3, #0xFF @ ค่าคงที่เป็นเลขฐานสิบหก

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

    0: e28200ff เพิ่ม r0, r2, #255 ; 0xff
    4: e28300ff เพิ่ม r0, r3, #255 ; 0xff

    และคุณยังสามารถเขียนได้

    เพิ่ม r0, r4, #512
    เพิ่ม r0, r5, 0x650000

    โค้ดที่คอมไพล์แล้วจะมีลักษณะดังนี้:

    0: e2840c02 เพิ่ม r0, r4, #512 ; 0x200
    4: e2850865 เพิ่ม r0, r5, #6619136 ; 0x650000

    ในกรณีนี้แน่นอนว่าตัวเลข 512 นั้นไม่พอดีกับไบต์ แต่แล้วเราก็จินตนาการว่ามันอยู่ในรูปแบบเลขฐานสิบหก 32'h00000200 และเห็นว่านี่คือ 2 ขยายไปทางขวาอีก 24 บิต (1 ror 24) ค่าสัมประสิทธิ์การหมุนน้อยกว่า 24 สองเท่านั่นคือ 12 ดังนั้นปรากฎว่า shifter_operand = ( 4'hc , 8'h02 ) - นี่คือบิตที่มีนัยสำคัญน้อยที่สุดสิบสองบิตของคำสั่ง เช่นเดียวกับหมายเลข 0x650000 สำหรับเขา shifter_operand = ( 4'h8, 8'h65 )

    เห็นได้ชัดว่าคุณไม่สามารถเขียนได้

    เพิ่ม r0, r1,#1234567

    หรือคุณไม่สามารถเขียนได้

    mov r0, #511

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

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

    mov r0, #511
    ย่อย r0,r0,#1

    วิธีที่สองในการโหลดหมายเลขเฉพาะลงในรีจิสเตอร์คือการอ่านจากตัวแปรที่สงวนไว้เป็นพิเศษซึ่งอยู่ในหน่วยความจำ:

    ldr r7,my_var
    .....
    my_var: .word 0x123456

    วิธีเขียนที่ง่ายที่สุดมีดังนี้:

    ldr r2,=511

    ในกรณีนี้ (สังเกตเครื่องหมาย "=") หากค่าคงที่สามารถแสดงเป็น imm_8 และ encode_imm หากสามารถใส่ลงในบิต 12 ของ shifter_operand ได้ คอมไพลเลอร์แอสเซมบลีจะคอมไพล์ ldr ลงในคำสั่ง mov โดยอัตโนมัติ แต่ถ้าไม่สามารถแสดงตัวเลขด้วยวิธีนี้ได้ คอมไพลเลอร์จะจองเซลล์หน่วยความจำในโปรแกรมสำหรับค่าคงที่นี้ และจะตั้งชื่อให้กับเซลล์หน่วยความจำนี้เองและคอมไพล์คำสั่งลงใน ldr

    นี่คือสิ่งที่ฉันเขียน:

    ldr r7,my_var
    ldr r8,=511
    ldr r8,=1024
    ldr r9,=0x3456
    ........
    My_var: .word 0x123456

    หลังจากรวบรวมฉันได้รับสิ่งนี้:

    18: e59f7030 ldr r7, ; 50
    1c: e59f8030 ldr r8, ; 54
    20: e3a08b01 mov r8, #1024 ; 0x400
    24: e59f902c ldr r9, ; 58
    .............
    00000050 :
    50: 00123456 .คำ 0x00123456
    54: 000001ff .คำ 0x000001ff
    58: 00003456 .คำ 0x00003456

    โปรดทราบว่าคอมไพลเลอร์ใช้การกำหนดแอดเดรสหน่วยความจำโดยสัมพันธ์กับการลงทะเบียนพีซี (หรือที่เรียกว่า r15)

    การอ่านเซลล์หน่วยความจำและการเขียนรีจิสเตอร์ลงในหน่วยความจำ

    ดังที่ฉันเขียนไว้ข้างต้น โปรเซสเซอร์ ARM สามารถดำเนินการทางคณิตศาสตร์หรือตรรกะกับเนื้อหาของรีจิสเตอร์เท่านั้น ข้อมูลสำหรับการดำเนินการจะต้องอ่านจากหน่วยความจำ และผลลัพธ์ของการดำเนินการจะต้องถูกเขียนกลับเข้าไปในหน่วยความจำ มีคำสั่งพิเศษสำหรับสิ่งนี้: ldr (อาจมาจากการรวม "LoaD Register") สำหรับการอ่าน และ str (อาจเป็น "STore Register") สำหรับการเขียน

    ดูเหมือนจะมีแค่สองทีมแต่จริงๆแล้วมีหลายทีม เพียงดูวิธีการเข้ารหัสคำสั่ง ldr /str บนโปรเซสเซอร์ Amber ARM เพื่อดูว่ามีบิตแฟล็กเสริมจำนวนเท่าใด L 20, W 21, B 22, U 23, P 24, I 25 - และพวกมันจะกำหนดพฤติกรรมเฉพาะของ คำสั่ง:

    • บิต L 20 กำหนดการเขียนหรืออ่าน 1 - ldr, อ่าน, 0 - str, เขียน
    • บิต B 22 กำหนดการอ่าน/เขียนคำแบบ 32 บิตหรือไบต์ 8 บิต 1 หมายถึงการดำเนินการแบบไบต์ เมื่อไบต์ถูกอ่านเข้าไปในรีจิสเตอร์ บิตที่สำคัญที่สุดของรีจิสเตอร์จะถูกรีเซ็ตเป็นศูนย์
    • บิต I 25 กำหนดการใช้ฟิลด์ออฟเซ็ต ถ้าฉัน 25 ==0 ออฟเซ็ตจะถูกตีความว่าเป็นออฟเซ็ตตัวเลขที่ต้องเพิ่มไปยังที่อยู่ฐานจากรีจิสเตอร์หรือลบออก แต่การเพิ่มหรือการลบขึ้นอยู่กับบิต U 23

    (Cond) - เงื่อนไขในการดำเนินการ ตีความในลักษณะเดียวกับคำสั่งเชิงตรรกะ/เลขคณิต - การอ่านหรือการเขียนอาจเป็นแบบมีเงื่อนไขได้

    ดังนั้นใน Assembly Text คุณสามารถเขียนได้ดังนี้:

    ldr r1, @ ใน register r1 อ่านคำตามที่อยู่จาก register r0
    ldrb r1, @ เข้าสู่ register r1 อ่านไบต์ตามที่อยู่จาก register r0
    ldreq r2, @ การอ่านคำแบบมีเงื่อนไข
    ldrgtb r2, @ อ่านไบต์แบบมีเงื่อนไข
    ldr r3, @ อ่านคำที่ที่อยู่ 8 สัมพันธ์กับที่อยู่จากการลงทะเบียน r4
    ldr r4, @ อ่านคำที่ที่อยู่ -16 สัมพันธ์กับที่อยู่จากการลงทะเบียน r5

    เมื่อรวบรวมข้อความนี้แล้ว คุณสามารถดูรหัสจริงของคำสั่งเหล่านี้ได้:

    0: e5901000 ldr r1,
    4: e5d01000 ldrb r1,
    8: 05912000 ldreq r2,
    ค: c5d12000 ldrbgt r2,
    10: e5943008 ldr r3,
    14: e5154010 ldr r4,

    ในตัวอย่างด้านบนฉันใช้แค่ ldr เท่านั้น แต่ str ก็ใช้ในลักษณะเดียวกันมาก

    มีโหมดการเข้าถึงหน่วยความจำเขียนกลับก่อนดัชนีและหลังดัชนี ในโหมดเหล่านี้ ตัวชี้การเข้าถึงหน่วยความจำจะได้รับการอัปเดตก่อนหรือหลังดำเนินการคำสั่ง หากคุณคุ้นเคยกับภาษาการเขียนโปรแกรม C คุณจะคุ้นเคยกับโครงสร้างการเข้าถึงพอยน์เตอร์ เช่น ( *แหล่งที่มา++;) หรือ ( a=**** psource;). โปรเซสเซอร์ ARM ใช้โหมดการเข้าถึงหน่วยความจำนี้ เมื่อดำเนินการคำสั่งอ่าน รีจิสเตอร์สองตัวจะถูกอัพเดตพร้อมกัน - รีจิสเตอร์ตัวรับจะได้รับค่าที่อ่านจากหน่วยความจำ และค่าในรีจิสเตอร์ตัวชี้ไปยังเซลล์หน่วยความจำจะถูกเลื่อนไปข้างหน้าหรือข้างหลัง

    ในความคิดของฉันการเขียนคำสั่งเหล่านี้ค่อนข้างไร้เหตุผล ใช้เวลานานในการทำความคุ้นเคย

    ldr r3, ! @psrc++; r3 = *psrc;
    ldr r3, , #4 @ r3 = *psrc; PSRC++;

    คำสั่ง ldr คำสั่งแรกจะเพิ่มตัวชี้ก่อน จากนั้นจึงอ่าน คำสั่งที่สองจะอ่านก่อน จากนั้นจึงเพิ่มตัวชี้ ค่าของตัวชี้ psrc อยู่ในรีจิสเตอร์ r0

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

    นี่คือตัวอย่างของรูปแบบโค้ดที่เป็นไปได้:

    0: e7921003 ldr r1, @ read address - ผลรวมของค่าจากรีจิสเตอร์ r2 และ r3
    4: e7b21003 ldr r1, ! @ เหมือนกัน แต่หลังจากอ่าน r2 จะเพิ่มขึ้นตามค่าจาก r3
    8: e6932004 ldr r2, , r4 @ ก่อนอื่นจะมีการอ่านที่ที่อยู่ r3 จากนั้น r3 จะเพิ่มขึ้น r4
    c: e7943185 ldr r3, @ อ่านที่อยู่ r4+r5*8
    10: e7b43285 ldr r3, ! @ อ่านที่อยู่ r4+r5*32 หลังจากอ่าน r4 จะถูกตั้งค่าเป็นค่าของที่อยู่นี้
    14: e69431a5 ldr r3, , r5, lsr #3 @ ที่อยู่สำหรับอ่าน r4 หลังจากดำเนินการคำสั่ง r4 จะถูกตั้งค่าเป็น r4+r5/8

    นี่คือรูปแบบต่างๆ ของคำสั่งอ่าน/เขียนในโปรเซสเซอร์ ARM v2a

    ในโปรเซสเซอร์ ARM รุ่นเก่า คำสั่งที่หลากหลายนี้จะยิ่งใหญ่ยิ่งขึ้น
    นี่เป็นเพราะความจริงที่ว่าโปรเซสเซอร์อนุญาตให้อ่านไม่เพียงแต่คำ (ตัวเลข 32 บิต) และไบต์ แต่ยังรวมถึงครึ่งคำ (16 บิต, 2 ไบต์) จากนั้นคำต่อท้าย "h" จากคำว่าครึ่งคำจะถูกเพิ่มลงในคำสั่ง ldr / str คำสั่งจะมีลักษณะเหมือน ldrh หรือ strh นอกจากนี้ยังมีคำสั่งสำหรับการโหลดครึ่งคำ ldrsh หรือไบต์ ldrsb ตีความว่าเป็นตัวเลขที่เซ็นชื่อ ในกรณีเหล่านี้ บิตที่สำคัญที่สุดของคำหรือไบต์ที่โหลดจะถูกคูณเข้ากับบิตที่สำคัญที่สุดของคำทั้งหมดในรีจิสเตอร์ตัวรับ ตัวอย่างเช่น การโหลด halfword 0xff25 ด้วยคำสั่ง ldrsh ใน register ปลายทางจะให้ผลลัพธ์เป็น 0xffffff25

    อ่านและเขียนได้หลายรายการ

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

    การช่วยจำคำสั่งการถ่ายโอนบล็อกเริ่มต้นที่รูท ldm (LoaD Multiple) หรือ stm (Store Multiple) แต่ตามปกติใน ARM เรื่องราวที่มีคำต่อท้ายก็เริ่มต้นขึ้น

    โดยทั่วไป คำสั่งจะมีลักษณะดังนี้:

    op(cond)(โหมด) ถ(, {Register list} !}

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

    รายการรีจิสเตอร์ระบุอยู่ในเครื่องหมายปีกกาคั่นด้วยเครื่องหมายจุลภาคหรือเป็นช่วง ตัวอย่างเช่น:

    เอสทีเอ็ม r0,(r3,r1, r5-r8)

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

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

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

    ใครจะคิดว่ามีสแต็กหน่วยความจำสี่ประเภท

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

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

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

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

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

    หากคุณใช้สแต็กโดยใช้คำแนะนำของโปรเซสเซอร์ ARM คุณจะได้ภาพต่อไปนี้:

    ทำไมทีมเดียวกันถึงต้องใช้ชื่อต่างกัน? ฉันไม่เข้าใจเลย... แน่นอนว่าควรสังเกตว่ามาตรฐานสแต็กสำหรับ ARM ยังคงเป็น Full Descending

    ตัวชี้สแต็กในโปรเซสเซอร์ ARM คือการลงทะเบียน sp หรือ r13 นี่คือข้อตกลงโดยทั่วไป แน่นอนว่า การเขียน stm หรือการอ่าน ldm สามารถทำได้ด้วยรีจิสเตอร์ฐานอื่นๆ เช่นกัน อย่างไรก็ตาม คุณต้องจำไว้ว่า sp register แตกต่างจากรีจิสเตอร์อื่นอย่างไร - อาจแตกต่างกันในโหมดการทำงานของโปรเซสเซอร์ที่แตกต่างกัน (USR, SVC, IRQ, FIRQ) เนื่องจากมีธนาคารลงทะเบียนของตัวเอง

    และอีกหนึ่งหมายเหตุ เขียนบรรทัดแบบนี้ในโค้ดแอสเซมบลี ARM ดัน (r0-r3), แน่นอนคุณสามารถ. แต่ในความเป็นจริงแล้วมันจะเป็นทีมเดียวกัน เอสทีเอ็มเอฟเอสพี!,(r0-r3).

    สุดท้ายนี้ ฉันจะยกตัวอย่างโค้ดแอสเซมบลีและข้อความที่คอมไพล์แล้วแบบแยกส่วน เรามี:


    เอสทีเอ็มเอฟเอสพี!,(r0-r3)
    stmdb sp!,(r0-r3)
    ดัน (r0-r3)

    @ คำแนะนำทั้งสามนี้เหมือนกันและทำสิ่งเดียวกัน
    ป๊อป(r0-r3)
    แอลดีเมีย sp!,(r0-r3)
    ldmfd r13!,(r0-r3)

    เอสทีเอ็มเอฟดี r4,(r0-r3,r5,r8)
    stmea r4!,(r0-r3,r7,r9,lr,พีซี)
    ldm r5,(r0,พีซี)

    หลังจากการรวบรวมเราได้รับ:

    0: e92d000f กด (r0, r1, r2, r3)
    4: e92d000f กด (r0, r1, r2, r3)
    8: e92d000f กด (r0, r1, r2, r3)
    ค: e8bd000f ป๊อป (r0, r1, r2, r3)
    10: e8bd000f ป๊อป (r0, r1, r2, r3)
    14: e8bd000f ป๊อป (r0, r1, r2, r3)
    18: e904012f stmdb r4, (r0, r1, r2, r3, r5, r8)
    1c: e8a4c28f stmia r4!, (r0, r1, r2, r3, r7, r9, lr, พีซี)
    20: e8958001 ldm r5, (r0, พีซี)

    การเปลี่ยนผ่านในโปรแกรม

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

    โปรเซสเซอร์ Amber ARM v2a มีเพียงสองคำสั่ง: b (จากคำว่า Branch - สาขา, การเปลี่ยนแปลง) และ bl (สาขาที่มีลิงก์ - การเปลี่ยนแปลงในขณะที่ยังคงรักษาที่อยู่ผู้ส่ง)

    ไวยากรณ์คำสั่งนั้นง่ายมาก:

    ข(เงื่อนไข)ฉลาก
    ป้ายกำกับ BL (เงื่อนไข)

    เป็นที่ชัดเจนว่าการเปลี่ยนแปลงใด ๆ สามารถมีเงื่อนไขได้นั่นคือโปรแกรมอาจมีคำแปลก ๆ เช่นนี้ซึ่งเกิดจากราก "b" และ "bl" และส่วนต่อท้ายของเงื่อนไข (Cond):

    beq, bne, bcs, bhs, bcc, blo, bmi, bpl, bvs, bvc, bhi, bls, bge, bgt, ble, bal, b

    bleq, blne, blcs, blhs, blcc, bllo, blmi, blpl, blvs, blvc, blhi, blls, blge, blgt, blle, blal, bl

    ความหลากหลายนั้นน่าทึ่งมากใช่ไหม?

    คำสั่ง Jump ประกอบด้วยออฟเซ็ตออฟเซ็ต 24 บิต ที่อยู่การกระโดดจะคำนวณเป็นผลรวมของค่าปัจจุบันของตัวชี้ pc และหมายเลขออฟเซ็ตเลื่อนไปทางซ้าย 2 บิต ซึ่งแปลเป็นตัวเลขที่เซ็นชื่อ:

    พีซีใหม่ = พีซี + ออฟเซ็ต*4

    ดังนั้นช่วงของการเปลี่ยนภาพคือ 32MB ไปข้างหน้าหรือข้างหลัง

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

    มาดูขั้นตอนเอาท์ไบต์จากโปรเจ็กต์สวัสดีโลกสำหรับ Amber SoC

    000004a0<_outbyte>:
    4a0: e59f1454 ldr r1, ; 8เอฟซี< адрес регистра данных UART >
    4a4: e59f3454 ldr r3, ; 900< адрес регистра статуса UART >
    4a8: e5932000 ldr r2, ; อ่านสถานะปัจจุบัน
    4ac: e2022020 และ r2, r2, #32
    4b0: e3520000 cmp r2, #0 ; ตรวจสอบว่า UART ไม่ยุ่ง
    4b4: 05c10000 strbeq r0, ; เขียนอักขระลงใน UART เฉพาะในกรณีที่ไม่ยุ่ง
    4b8: 01b0f00e ย้ายพีซี, lr ; การส่งคืนแบบมีเงื่อนไขจากขั้นตอนหาก UART ไม่ยุ่ง
    4bc: 1afffff9 bne 4a8<_outbyte+0x8>; วนซ้ำเพื่อตรวจสอบสถานะ UART

    ฉันคิดว่าจากความคิดเห็นในส่วนนี้ มันชัดเจนว่ากระบวนการนี้ทำงานอย่างไร

    หมายเหตุสำคัญอีกประการหนึ่งเกี่ยวกับการเปลี่ยนภาพ รีจิสเตอร์ r15 (pc) สามารถใช้ในการดำเนินการทางคณิตศาสตร์หรือตรรกะทั่วไปเป็นรีจิสเตอร์ปลายทางได้ ดังนั้นคำสั่งอย่าง add pc,pc,#8 จึงเป็นคำสั่งสำหรับการย้ายไปยังที่อยู่อื่น

    ต้องมีหมายเหตุอีกประการหนึ่งเกี่ยวกับการเปลี่ยนภาพ โปรเซสเซอร์ ARM รุ่นเก่ายังมีคำสั่งสาขาเพิ่มเติม bx, blx และ blj คำสั่งเหล่านี้เป็นคำสั่งสำหรับการข้ามไปยังส่วนของโค้ดด้วยระบบคำสั่งอื่น Bx /blx อนุญาตให้คุณสลับไปใช้รหัส THUMB 16 บิตของโปรเซสเซอร์ ARM Blj เป็นการเรียกขั้นตอนระบบคำสั่ง Jazelle (รองรับภาษา Java ในโปรเซสเซอร์ ARM) Amber ARM v2a ของเราไม่มีคำสั่งเหล่านี้