آزمایشگاه 5 (AARCH64) – آزمایشگاه زبان مونتاژ 64 بیتی

مقدمه
سلام به همه! در آزمایشگاه 5 ، ما از جلو حرکت می کنیم 6502
زبان مونتاژ به پردازنده های مدرن مانند x86
وت aarch64
بشر هر چند 6502
IS یک مجموعه دستورالعمل حداقل ارائه می دهد ، این پردازنده های مدرن دارای قابلیت ها و معماری بسیار پیشرفته تری هستند. در این آزمایشگاه ، ما به بررسی زبانهای مونتاژ در آنها خواهیم پرداخت.
این پست وبلاگ عمدتا در مورد AArch64
سرور یک قسمت 2 برای این آزمایشگاه وجود دارد که هر آنچه در اینجا کدگذاری شده است در آن کدگذاری می شود x86
بشر
تمام آزمایشات انجام خواهد شد x86
وت aarch64
سرورهای از راه دور.
تنظیم کردن
در سرورهای کلاس ما ، نمونه های کد از طریق مسیر به دست می آیند: /public/spo600-assembler-lab-examples.tgz
بشر
ما این را استخراج خواهیم کرد .tgz
پرونده با استفاده از دستور tar
بشر
tar xvf /public/spo600-assembler-lab-examples.tgz
کد در این ساختار ارائه خواهد شد:
spo600
└── examples
└── hello # "hello world" example programs
├── assembler
│ ├── aarch64 # aarch64 gas assembly language version
│ │ ├── hello.s
│ │ └── Makefile
│ ├── Makefile
│ └── x86_64 # x86_64 assembly language versions
│ ├── hello-gas.s # ... gas syntax
│ ├── hello-nasm.s # ... nasm syntax
│ └── Makefile
└── c # Portable C versions
├── hello2.c # ... using write()
├── hello3.c # ... using syscall()
├── hello.c # ... using printf()
└── Makefile
AArch64
برنامه مجمع
اول ، ما نگاه خواهیم کرد aarch64
سرور وارد شوید و به فهرست مثال مونتاژ AARCH64 بروید:
cd ~/spo600/examples/hello/assembler/aarch64
وجود دارد hello.s
پرونده این پرونده منبع کد ماست!
بعد ، خواهید دید که وجود دارد Makefile
در این مکان این بدان معنی است که ما می توانیم اجرا کنیم make
و برنامه ما را کامپایل کنید. برنامه را با استفاده از make
فرمان
بر اساس وابستگی های تعریف شده در
Makefile
باmake
تعیین می کند که کدام قسمت از کد تغییر کرده و نیاز به بازسازی مجدد دارد ، سپس دستورات لازم را برای به روزرسانی فقط آن قسمت ها اجرا می کند.
make
پس از این ، یک پرونده باینری به نام خواهید دید hello
در فهرست این پرونده باینری حاصل را اجرا کنید.
./hello
این به شما نشان می دهد “سلام ، جهان!” در ترمینال شما
خوب ، حالا بیایید با استفاده از کد نگاهی بیندازیم objdump
فرمان من می خواهم خروجی جدا شده پرونده شی را مقایسه کنم (hello.o
) به پرونده منبع (hello.s
).
objdump -d hello.o > hello_disassembled.txt
-
خروجی جدا شده (
hello.o
)
hello.o: file format elf64-littleaarch64
Disassembly of section .text:
0000000000000000 <_start>:
0: d2800020 mov x0, #0x1 // #1
4: 10000001 adr x1, 0 <_start>
8: d28001c2 mov x2, #0xe // #14
c: d2800808 mov x8, #0x40 // #64
10: d4000001 svc #0x0
14: d2800000 mov x0, #0x0 // #0
18: d2800ba8 mov x8, #0x5d // #93
1c: d4000001 svc #0x0
.text
.globl _start
_start:
mov x0, 1 /* file descriptor: 1 is stdout */
adr x1, msg /* message location (memory address) */
mov x2, len /* message length (bytes) */
mov x8, 64 /* write is syscall #64 */
svc 0 /* invoke syscall */
mov x0, 0 /* status -> 0 */
mov x8, 93 /* exit is syscall #93 */
svc 0 /* invoke syscall */
.data
msg: .ascii "Hello, world!\n"
len= . - msg
با نگاهی به این دو پرونده ، فایل جداسازی یک ترجمه دقیق دستورالعمل توسط دستورالعمل به کد دستگاه از پرونده منبع ما است. تنها تفاوت این است که ما از آن استفاده می کنیم msg
برچسب در adr
دستورالعمل با این حال ، این ناشی از ماهیت نحوه نمایش نمادها در کد جدا شده است.
اصلاح AArch64
برنامه مجمع
در اینجا یک حلقه اساسی در مونتاژ AARCH64 وجود دارد.
.text
.globl _start
min = 0 /* starting value for the loop index; **note that this is a symbol (constant)**, not a variable */
max = 6 /* loop exits when the index hits this number (loop condition is i
_start:
mov x19, min
loop:
/* ... body of the loop ... do something useful here ... */
add x19, x19, 1 /* increment the loop counter */
cmp x19, max /* see if we've hit the max */
b.ne loop /* if not, then continue the loop */
mov x0, 0 /* set exit status to 0 */
mov x8, 93 /* exit is syscall #93 */
svc 0 /* invoke syscall */
کد 6 بار در حال حلقه است (max
6) است. شاخص حلقه را در ثبت 19 ذخیره می کند (x19
) برای پیگیری تکرارها. بدنه حلقه در حال حاضر خالی است.
☑ حلقه چاپ
بیایید این کد را به طوری تغییر دهیم که “حلقه” را در هر تکرار چاپ کند. ما اضافه کردیم .data
بخش و چاپ را در بدنه حلقه اضافه کنید.
تغییر hello.s
مانند زیر:
.text
.globl _start
min = 0 /* starting value for the loop index */
max = 6 /* loop exits when the index hits this number */
_start:
mov x19, min /* initialize loop counter */
loop:
/* Print "Loop" message */
mov x0, 1 /* file descriptor: 1 is stdout */
adr x1, msg /* message location (memory address) */
mov x2, len /* message length (bytes) */
mov x8, 64 /* write is syscall #64 */
svc 0 /* invoke syscall */
add x19, x19, 1 /* increment the loop counter */
cmp x19, max /* see if we've hit the max */
b.ne loop /* if not, then continue the loop */
mov x0, 0 /* set exit status to 0 */
mov x8, 93 /* exit is syscall #93 */
svc 0 /* invoke syscall */
.data
msg: .ascii "Loop\n"
len= . - msg
با استفاده از این کد جدید را آزمایش کنید make
و دویدن
make clean
make
./hello
خروجی:
[kzaw@aarch64-002 aarch64]$ ./hello
Loop
Loop
Loop
Loop
Loop
Loop
☑ حلقه چاپ و شماره فهرست
بیایید دوباره کد را تغییر دهیم تا چاپ شود Loop: #
جایی که '#' شماره شاخص فعلی است.
برای انجام این کار ، ما باید شماره پیشخوان حلقه خود را به نمایش شخصیت ASCII آن تبدیل کنیم. در ASCII/ISO-8859-1/UNICODE UTF-8 ، شخصیت های رقمی در محدوده 48-57 (0x30-0x39) قرار دارند.
add x20, x19, #48 /* x20 = x19 + 48 (ASCII '0') */
این پیشخوان حلقه را در Register 19 تبدیل می کند (x19
) با افزودن 48 مقدار (کد ASCII برای “0”) و مقدار جدید را در آن ذخیره می کند x20
بشر به عنوان مثال ،
- کی
x19
= 0 ،x20
48 می شود (ASCII '0'). - کی
x19
= 1 ،x20
49 می شود (ASCII '1'). - و غیره …
بعد ، پیام ما را تعریف کنید.
msg: .ascii "Loop: #\n" /* # is a placeholder for the digit */
من این کد زیر را به بدنه حلقه اضافه کردم. آنچه در اینجا اتفاق می افتد این است که من آدرس آن را می گیرم msg
، و سپس اضافه کردن یک جبران 6 بایت برای اشاره به موقعیت بعد از “حلقه:”. (که طول آن 6 کاراکتر است).
در
strb
دستورالعمل یک بایت را از یک رجیستر به یک حافظه می نویسد.
w20
نمای 32 بیتی است x20
بشر وقتی با استفاده از strb
، این بدان معنی است که ما کمترین 8 بیت (1 بایت) را از آن ثبت می کنیم. در این زمینه ، کجا x20
با اشاره به یک رقم ASCII (که 1 بایت طول می کشد) ، دستورالعمل 24 بیت دیگر را نادیده می گیرد و اطمینان می دهد که فضای غیر ضروری صورت نمی گیرد.
[x21]
موقعیت مکانی برای ذخیره رقم ASCII در. اکنون این همه چیز به این معنی است که دستورالعمل می نویسد که رقم ASCII به محل حافظه که به آن اشاره شده است x21
('#').
adr x21, msg /* Get address of message */
add x21, x21, #6 /* Position of the digit character (after "Loop: ") */
strb w20, [x21] /* Store the ASCII character at that position */
✨
کد اصلاح شده:
.text
.globl _start
min = 0 /* starting value for the loop index */
max = 6 /* loop exits when the index hits this number */
_start:
mov x19, min /* initialize loop counter */
loop:
/* Convert loop counter to ASCII character */
add x20, x19, #48 /* x20 = x19 + 48 (ASCII '0') */
/* Store the ASCII digit in the message */
adr x21, msg /* Get address of message */
add x21, x21, #6 /* Position of the digit character (after "Loop: ") */
strb w20, [x21] /* Store the ASCII character at that position */
/* Print message with loop counter */
mov x0, 1 /* file descriptor: 1 is stdout */
adr x1, msg /* message location (memory address) */
mov x2, len /* message length (bytes) */
mov x8, 64 /* write is syscall #64 */
svc 0 /* invoke syscall */
add x19, x19, 1 /* increment the loop counter */
cmp x19, max /* see if we've hit the max */
b.ne loop /* if not, then continue the loop */
mov x0, 0 /* set exit status to 0 */
mov x8, 93 /* exit is syscall #93 */
svc 0 /* invoke syscall */
.data
msg: .ascii "Loop: #\n" /* # is a placeholder for the digit */
len= . - msg
خروجی:
[kzaw@aarch64-002 aarch64]$ ./hello
Loop: 0
Loop: 1
Loop: 2
Loop: 3
Loop: 4
Loop: 5
☑ حلقه از 00 – 32
نیاز بعدی حلقه از 00 – 32 است ، چاپ در اعداد اعشاری 2 رقمی. علاوه بر تغییر max
نماد 33 ، تغییرات مهم دیگری نیز وجود دارد که باید انجام دهیم.
پیشخوان حلقه را در x22 کپی می کنیم.
mov x22, x19 /* Copy loop counter to x22 */
در مرحله بعد ، ما باید کد خود را تغییر دهیم تا برای تبدیل دو رقمی تهیه کنیم.
پیش از این ، فقط شماره ASCII را به پیشخوان ما اضافه می کرد و همین بود. اکنون پیچیده تر است.
نحوه محاسبه رقم TENS اساساً توسط 10 تقسیم می شود. udiv
دستورالعمل در بخش تقسیم می شود.
اینقدر x20 = x22 / 10
/* Calculate tens digit: quotient of division by 10 */
mov x23, #10 /* Set divisor to 10 */
udiv x20, x22, x23 /* x20 = x22 / 10 (quotient = tens digit) */
اکنون که رقم TENS استخراج شده است ، می توانیم با بدست آوردن باقیمانده ، رقم آن را بدست آوریم. برای انجام این کار ، ما استفاده خواهیم کرد mul
برای چند برابر با 10. این مقدار از مقدار اصلی برای بدست آوردن باقی مانده از مقدار اصلی کم می شود.
/* Calculate ones digit: remainder of division by 10 */
mul x24, x20, x23 /* x24 = quotient * 10 */
sub x21, x22, x24 /* x21 = original - (quotient * 10) = remainder */
بعد از همه اینها ، می توانیم همه چیز را به ASCII تبدیل کنیم.
/* Convert digits to ASCII */
add x20, x20, #48 /* Convert tens digit to ASCII */
add x21, x21, #48 /* Convert ones digit to ASCII */
بگذارید یک جدول از ثبت ها را به شما نشان دهم ، با مقایسه قدیمی و جدید ، تا بتوانیم بهتر درک کنیم.
ثبت نام | هدف اصلی | هدف جدید |
---|---|---|
X19 | شمارنده حلقه | پیشخوان حلقه (بدون تغییر) |
x20 | رقم ASCII | ده رقمی (پس از تبدیل به ASCII) |
x21 | نشانگر پیام | رقم آن (پس از تبدیل به ASCII) |
x22 | – | کپی پیشخوان حلقه برای محاسبات |
x23 | – | مقدار ثابت 10 (تقسیم کننده) |
x24 | – | اشاره گر برای دستکاری بافر پیام |
محاسبه تکمیل شده است! منطقی را تغییر دهید که پیام را تغییر می دهیم تا دو رقم را در خود جای دهد.
/* Store the ASCII digits in the message */
adr x24, msg /* Get address of message */
add x24, x24, #6 /* Position of the first digit (after "Loop: ") */
strb w20, [x24] /* Store the tens digit */
add x24, x24, #1 /* Move to the position of the second digit */
strb w21, [x24] /* Store the ones digit */
✨
کد اصلاح شده:
.text
.globl _start
min = 0 /* starting value for the loop index */
max = 33 /* loop exits when the index hits this number */
_start:
mov x19, min /* initialize loop counter */
loop:
/* Convert loop counter to two ASCII digits */
mov x22, x19 /* Copy loop counter to x22 */
/* Calculate tens digit: quotient of division by 10 */
mov x23, #10 /* Set divisor to 10 */
udiv x20, x22, x23 /* x20 = x22 / 10 (quotient = tens digit) */
/* Calculate ones digit: remainder of division by 10 */
mul x24, x20, x23 /* x24 = quotient * 10 */
sub x21, x22, x24 /* x21 = original - (quotient * 10) = remainder */
/* Convert digits to ASCII */
add x20, x20, #48 /* Convert tens digit to ASCII */
add x21, x21, #48 /* Convert ones digit to ASCII */
/* Store the ASCII digits in the message */
adr x24, msg /* Get address of message */
add x24, x24, #6 /* Position of the first digit (after "Loop: ") */
strb w20, [x24] /* Store the tens digit */
add x24, x24, #1 /* Move to the position of the second digit */
strb w21, [x24] /* Store the ones digit */
/* Print message with loop counter */
mov x0, 1 /* file descriptor: 1 is stdout */
adr x1, msg /* message location (memory address) */
mov x2, len /* message length (bytes) */
mov x8, 64 /* write is syscall #64 */
svc 0 /* invoke syscall */
add x19, x19, 1 /* increment the loop counter */
cmp x19, max /* see if we've hit the max */
b.ne loop /* if not, then continue the loop */
mov x0, 0 /* set exit status to 0 */
mov x8, 93 /* exit is syscall #93 */
svc 0 /* invoke syscall */
.data
msg: .ascii "Loop: ##\n" /* ## are placeholders for the two digits */
len= . - msg
خروجی:
[kzaw@aarch64-002 aarch64]$ ./hello
Loop: 00
Loop: 01
...
Loop: 09
Loop: 10
...
Loop: 32
☑ حلقه بدون دنباله صفرا
تغییر بعدی که ما ایجاد می کنیم حذف صفر پیشرو برای شماره های تک رقمی است.
برای تحقق این امر ، ما باید پیاده سازی کنیم مشروط منطقی که تشخیص می دهد که آیا ما با یک شماره تک رقمی یا دو رقمی سر و کار داریم و بر این اساس خروجی را قالب بندی می کنیم.
ما باید بسته به مقدار شماره از قالب های مختلف پیام استفاده کنیم:
- برای اعداد 0-9: از “حلقه: #” استفاده کنید (به دو فضای بعد از روده بزرگ توجه کنید)
- برای شماره های 10-32: از “حلقه: ##” استفاده کنید (به یک فضا بعد از روده بزرگ توجه کنید)
.data
msg1: .ascii "Loop: #\n" /* Single-digit format (note: two spaces after colon) */
len1= . - msg1
msg2: .ascii "Loop: ##\n" /* Double-digit format (note: one space after colon) */
len2= . - msg2
در کلید تغییر این بررسی مشروط است. ما ده رقم را با مقدار ASCII “0” مقایسه می کنیم. اگر این “0” نباشد ، می دانیم که یک شماره دو رقمی داریم. ما جدید خواهیم ساخت double_digit
عملکرد برای شرایط ما.
/* Determine if number is single or double digit */
cmp x20, #48 /* Compare tens digit to ASCII '0' */
b.ne double_digit /* If not '0', it's a double-digit number */
اگر این رقم دو رقمی نباشد ، ما به پرونده تک رقمی ادامه می دهیم. استفاده کردن msg1
و در موقعیت 6 ذخیره کنید. پس از منطق ، به سمت مشترک پرش کنید print_msg
روال
/* Single-digit case (0-9) */
adr x24, msg1 /* Get address of single-digit message */
strb w21, [x24, #6] /* Store ones digit at position after "Loop: " */
mov x1, x24 /* Set message address for print */
mov x2, len1 /* Set message length */
b print_msg /* Jump to print routine */
اگر این رقم دو رقمی باشد ، ما به آن می پرید double_digit
روال استفاده کردن msg2
، طول و آدرس را برای چاپ تنظیم کنید و سپس به منطق چاپ بروید.
double_digit:
/* Double-digit case (10-32) */
adr x24, msg2 /* Get address of double-digit message */
strb w20, [x24, #6] /* Store tens digit */
strb w21, [x24, #7] /* Store ones digit */
mov x1, x24 /* Set message address for print */
mov x2, len2 /* Set message length */
این منطق چاپ زیر است.
print_msg:
/* Print message with loop counter */
mov x0, 1 /* file descriptor: 1 is stdout */
mov x8, 64 /* write is syscall #64 */
svc 0 /* invoke syscall */
✨
کد اصلاح شده:
.text
.globl _start
min = 0 /* starting value for the loop index */
max = 33 /* loop exits when the index hits this number */
_start:
mov x19, min /* initialize loop counter */
loop:
/* Convert loop counter to two ASCII digits */
mov x22, x19 /* Copy loop counter to x22 */
/* Calculate tens digit: quotient of division by 10 */
mov x23, #10 /* Set divisor to 10 */
udiv x20, x22, x23 /* x20 = x22 / 10 (quotient = tens digit) */
/* Calculate ones digit: remainder of division by 10 */
mul x24, x20, x23 /* x24 = quotient * 10 */
sub x21, x22, x24 /* x21 = original - (quotient * 10) = remainder */
/* Convert digits to ASCII */
add x20, x20, #48 /* Convert tens digit to ASCII */
add x21, x21, #48 /* Convert ones digit to ASCII */
/* Determine if number is single or double digit */
cmp x20, #48 /* Compare tens digit to ASCII '0' */
b.ne double_digit /* If not '0', it's a double-digit number */
/* Single-digit case (0-9) */
adr x24, msg1 /* Get address of single-digit message */
strb w21, [x24, #7] /* Store ones digit at position after "Loop: " */
mov x1, x24 /* Set message address for print */
mov x2, len1 /* Set message length */
b print_msg /* Jump to print routine */
double_digit:
/* Double-digit case (10-32) */
adr x24, msg2 /* Get address of double-digit message */
strb w20, [x24, #6] /* Store tens digit */
strb w21, [x24, #7] /* Store ones digit */
mov x1, x24 /* Set message address for print */
mov x2, len2 /* Set message length */
print_msg:
/* Print message with loop counter */
mov x0, 1 /* file descriptor: 1 is stdout */
mov x8, 64 /* write is syscall #64 */
svc 0 /* invoke syscall */
add x19, x19, 1 /* increment the loop counter */
cmp x19, max /* see if we've hit the max */
b.ne loop /* if not, then continue the loop */
mov x0, 0 /* set exit status to 0 */
mov x8, 93 /* exit is syscall #93 */
svc 0 /* invoke syscall */
.data
msg1: .ascii "Loop: #\n" /* Single-digit format (note: two spaces after colon) */
len1= . - msg1
msg2: .ascii "Loop: ##\n" /* Double-digit format (note: one space after colon) */
len2= . - msg2
خروجی:
Loop: 0
Loop: 1
Loop: 2
...
Loop: 9
Loop: 10
Loop: 11
...
Loop: 32
☑ حلقه با خروجی هگز (0 – 20)
حال ، بیایید بگوییم که می خواهیم به جای اعشاری ، در هگز بازده کنیم. شاخه تک در مقابل دو رقمی را بردارید. این تغییراتی است که می توانید ایجاد کنید.
اکنون به جای 10 به جای 10 تقسیم خواهیم شد. این باعث می شود که مقدار “نیبل بالا” (اولین رقم شش ضلعی) و مابقی “نوک پستان” باشد.
mov x23, #16
برای هر نوک پستان ، بررسی کنید که آیا مقدار آن کمتر از 10 است. اگر هست ، با اضافه کردن 48 آن را به ASCII تبدیل کنید.'A'
) ، 11 می شود 66 ('B'
) ، و غیره
در اینجا این منطق برای نوک پستان بالا آورده شده است. همین مورد در مورد نیبل کم اعمال می شود.
cmp x20, #10 /* Check if high nibble is less than 10 */
blt high_digit_decimal
add x20, x20, #55 /* For hex A-F */
b high_digit_done
high_digit_decimal:
add x20, x20, #48 /* For digits 0-9 */
high_digit_done:
✨
کد اصلاح شده:
.text
.globl _start
min = 0 /* starting value for the loop index */
max = 33 /* loop exits when the index hits this number */
_start:
mov x19, min /* initialize loop counter */
loop:
/* Convert loop counter to two hexadecimal digits */
mov x22, x19 /* Copy loop counter to x22 */
/* Calculate high nibble: quotient of division by 16 */
mov x23, #16 /* Set divisor to 16 for hex */
udiv x20, x22, x23 /* x20 = x22 / 16 (high nibble) */
/* Calculate low nibble: remainder of division by 16 */
mul x24, x20, x23 /* x24 = high nibble * 16 */
sub x21, x22, x24 /* x21 = original - (high nibble * 16) = low nibble */
/* Convert high nibble to ASCII */
cmp x20, #10 /* Check if high nibble is less than 10 */
blt high_digit_decimal
/* For hex A-F: add 55 (10+55=65 -> 'A') */
add x20, x20, #55
b high_digit_done
high_digit_decimal:
add x20, x20, #48 /* For digits 0-9: add 48 ('0') */
high_digit_done:
/* Convert low nibble to ASCII */
cmp x21, #10 /* Check if low nibble is less than 10 */
blt low_digit_decimal
/* For hex A-F */
add x21, x21, #55
b low_digit_done
low_digit_decimal:
add x21, x21, #48 /* For digits 0-9 */
low_digit_done:
/* Store the ASCII characters in the message template */
adr x24, msg /* Get address of the message template */
strb w20, [x24, #6] /* Store high nibble at position after "Loop: " */
strb w21, [x24, #7] /* Store low nibble */
/* Print message */
mov x1, x24 /* Set message address for print */
mov x2, len /* Set message length */
mov x0, 1 /* file descriptor: 1 is stdout */
mov x8, 64 /* write is syscall #64 */
svc 0 /* invoke syscall */
add x19, x19, 1 /* increment the loop counter */
cmp x19, max /* see if we've hit the max */
b.ne loop /* if not, then continue the loop */
mov x0, 0 /* set exit status to 0 */
mov x8, 93 /* exit is syscall #93 */
svc 0 /* invoke syscall */
.data
msg: .ascii "Loop: ##\n" /* Message format for hexadecimal output */
len= . - msg
خروجی:
[kzaw@aarch64-002 aarch64]$ ./hello
Loop: 00
Loop: 01
Loop: 02
Loop: 03
Loop: 04
Loop: 05
Loop: 06
Loop: 07
Loop: 08
Loop: 09
Loop: 0A
Loop: 0B
Loop: 0C
Loop: 0D
Loop: 0E
Loop: 0F
Loop: 10
Loop: 11
Loop: 12
Loop: 13
Loop: 14
Loop: 15
Loop: 16
Loop: 17
Loop: 18
Loop: 19
Loop: 1A
Loop: 1B
Loop: 1C
Loop: 1D
Loop: 1E
Loop: 1F
Loop: 20
افکار نهایی
این آزمایشگاه به من کمک کرد تا به زبان مونتاژ مدرن بیشتر درک و کدگذاری کنم. در داخل AArch64
، من به چندین مفاهیم منطق از دستکاری حافظه ، حلقه ها تا رمزگذاری شخصیت عادت کردم! این به من کمک کرد تا درک خود را محکم کنم.
یکی از مهمترین چیزها این است که همه چیز در مونتاژ صریح است. هر شخصیت چاپ شده و هر تکرار حلقه باید با دقت ساخته شود. من کاملاً آگاه شدم که این مونتاژ سطح پایین است و BTS وجود ندارد. مجبور شدم مکانهای حافظه خودم را مدیریت کنم و با پیشخوان های شاخص خود محتاط باشم. این سطح بسیار پایین تر از C است!
در قسمت 2 این آزمایشگاه ، من همان مراحل را اجرا می کنم x86_64
مونتاژ در حالی که منطق ممکن است مشابه باقی بماند ، کنوانسیون های تماس نحوی و سیستم کاملاً متفاوت خواهند بود.
از وقت شما متشکرم به زودی می بینیم! 😄