راهنماهای
سگمنت
هر برنامه
شامل يک يا چند سگمنت است. هنگامی که برنامه دارد اجرا می شود ثبات های سگمنت به
سگمنت های جاری اشاره می کنند. چهار سگمنت را در آن واحد می توان داشت؛ کد، داده،
پشته و اضافی. در مد حقيقی هر سگمنت حداکثر 64KB است.
البته معمولا برنامه ها کمتر از 64KB را استفاده می کنند. اسمبلر اندازه سگمنت را بر اساس تعداد بايت
های مورد استفاده سگمنت تنظيم می کند. بنابراين اگر برنامه ای برای نمونه تنها 10KB برای
ذخيره داده نياز دارد سگمنت داده 10KB می شود نه 64KB.
برنامه های .exe سه
سگمنت اول را بايد داشته باشند.
• سگمنت داده برای ذخيره متغيرهاست. آدرس متغيرها به صورت
آفستی از شروع اين سگمنت محاسبه می شوند.
• سگمنت کد شامل دستورالعمل های اجرائی برنامه است.
• سگمنت پشته برای نگهداری داده های موقتی و آدرس های برگشتی از برنامه پشته رزرو
می شود. آدرس های پشته به صورت آفستی از ابتدای اين سگمنت محاسبه می شوند.
وقتی اجرای
برنامه آغاز می شود سيستم عامل دو ثبات سگمنت CS و SS را برای
اشاره به کد برنامه و سگمنت پشته مقداردهی می کند. برای دسترسی به سگمنت داده ثبات
ds بايد حاوی آدرس سگمنت داده باشد. قبل از دسترسی به هر داده ای
برنامه بايد آدرس سگمنت را در ثبات DS ذخيره کند.
سگمنت ها در
برنامه اسمبلی توسط راهنماهای segment و ends مشخص می شوند. يک سگمنت به فرم کلی زير مشخص می شود:
segmentname
segment {READONLY} {align} {combine} {use} {'class'}
segmentname ends
segmentname شناسه ای است که نام سگمنت معين می کند. نام سگمنت برای بدست
آوردن آدرس آنها توسط اسمبلر استفاده می شود. نام سگمنت بايد در راهنمای ends هم مشخص
شود.
align می تواند يکی از کلمات byte، word، dword، para يا page باشد.
اين پارامتر مشخص می کنند سگمنت در محدوده بايت، کلمه، کلمه مضاعف، پاراگراف يا
صفحه بار شود. اگر بايت باشد سگمنت از اولين بايت آزاد بعد از آخرين سگمنت ذخيره
می شود. اين فيلد می تواند حذف شود. پيش فرض پاراگراف است. پاراگراف مضربی از 16
بايت است.
فيلد combine ترتيبی
را که سگمنت های هم نام در فايل مقصد توسط اسمبلر نوشته می شوند را کنترل می کند و
می تواند يکی از کلمات public، stack، common يا memory باشد. نوع stack برای سگمنت های پشته و public برای بقيه سگمنت
ها استفاده می شود.
مثال.
DSEG segment
Item1 byte 0
Item2 word 0
DSEG ends
CSEG segment
mov AX, 10
add AX, Item1
ret
CSEG ends
هرزمان نام
سگمنت به عنوان عملوند دستوری بکار برود اسمبلر بلافاصله آدرس سگمنت را جايگزين می
کند.
مثال. دستور
زير آدرس سگمنت داده را در ثبات DS قرار می دهد.
mov AX,
dseg ;Loads AX with segment address of dseg.
mov DS, AX ;Point ds at
dseg.
راهنماهای .stack، .data و .code
راهنماهای ساده شده سگمنت هستند که محل شروع سگمنت های پشته، داده و کد را مشخص می
کنند. راهنمای .stack فضائی را برای پشته برنامه رزرو می کند. اندازه پشته در مقابل آن
ذکر می شود. پيش فرض مقدار پشته 512 بايت درنظر گرفته می شود.
سگمنت ها به
ترتيبی که در برنامه تعريف شده اند در حافظه بار می شوند.
ساختار کلی
يک برنامه
SSeg
SEGMENT PARA
DW 32 dup(0)
SSeg ENDS
DSeg SEGMENT PARA
;declarations
DSeg ENDS
CSeg SEGMENT PARA
Main PROC FAR
ASSUME SS:SSeg, DS:DSeg, CS:CSeg
mov AX,DSeg
mov DS,AX
...
mov AX,4c00h
int 21h
Main ENDP
CSeg ENDS
END Main
ساختار کلی
برنامه با راهنماهای ساده شده سگمنت
.MODEL
small
.STACK [size]
.DATA
;declarations
.CODE
Main:
mov AX,@Data
mov DS,AX
...
mov AX,4c00h
int 21h ;return to DOS
END Main
نکته. ابتدای
هر برنامه اسمبلی بايد آدرس سگمنت داده در ثبات DS قرار
گيرد.
مثال. برنامه
first.asm برای نمايش پيغام روی صفحه.
;
First.asm
;
.MODEL small
.STACK [size]
.DATA
message db "Hello world, I'm learning Assembly !!!", "$"
.CODE
main PROC
mov AX,seg message
mov DS,AX
mov AH,09
lea DX,message
int 21h
mov AX,4c00h
int 21h ;return to DOS
main ENDP
END main
ثابت ها و
متغيرها
تعريف داده
ها در سگمنت داده صورت می گيرد که با راهنمای .data شروع می
شود.
ثابت ها
يک ثابت
واقعی ثابتی است که مقدارش صريحا ذکر شده است. ثابت های واقعی نمايش آنچه هستند که
معمولا برای مقدار دنيای واقعی انتظار داريم. ماکرو اسمبلر دارای انواع مختلفی از
ثابت های صحيح، حقيقی، رشته و غيره است.
مثال.
123
3.14159
"Literal String Constant"
0FABCh
'A'
يک ثابت عددی
مقداری است که می تواند در مبنای 2، 10 يا 16 نوشته شود. برای مشخص کردن مبنای عدد
از پسوندهای جدول زير استفاده می شود. اگر مبنا صريحا ذکر نشود پيش فرض مبنای 10
است.
|
مبنا
|
پسوند
|
|
Binary
|
B يا b
|
|
decimal
|
D يا d يا T يا t
|
|
hexadecimal
|
H يا h
|
ثابت های
رشته ای درون گيومه (") يا تک گيومه (') قرار می گيرند.
مثال. ثابت
های عددی.
0F000h
12345d
0110010100b
مثال. ثابت
های رشته ای.
"This
is a string"
'So is this'
'Doesn''t this look weird?'
"Doesn't this look weird?"
"Microsoft claims ""Our software is very fast."" Do
you believe them?"
'Microsoft claims "Our software is very fast." Do you believe them?'
ثابت نامدار
(named constant) نام سمبليکی است که نشانگر مقدار ثابتی طی فرآيند اسمبلی است.
ثابت ها به صورت کلی زير تعريف می شوند:
ConstantName
EQU Value
ConstantName = Value
ConstantName نام ثابت است و Value مقداری است که به ثابت اختصاص داده می شود.
مثال.
One
equ 1
Minus1 equ -1
TryAgain equ 'Y'
String equ "Hello there"
Num = 16
Size = Count * Element
نکته. علامت
مساوی تنها برای مقدارهای عددی بکار می رود.
متغيرها
متغيرها را
در هر سگمنتی می توان تعريف کرد اما اکثر برنامه نويسان همه آنها را در سگمنت داده
تعريف می کنند. هر متغير به فرم کلی زير تعريف می شود:
VariableName
Type InitialValue|?
Type نوع متغير را مشخص می کند که می تواند يکی از نوع های جدول زير
باشد. نوع هائی که اغلب مورد استفاده قرار می گيرند DB و DW هستند. InitialValue مقداراوليه متغير است. اگر نخواهيم مقدار اوليه بدهيم علامت سوال
(?) می گذاريم.
|
تعداد بايت
|
نوع
|
|
1
|
byte/sbyte/db
|
|
2
|
word/sword/dw
|
|
4
|
dword/sdword/dd
|
|
8
|
qword/dq
|
|
10
|
tbyte/dt
|
مثال.
num db
25h
sum dd ?
ANum db -4
مثال. محل
های پشت سر هم که دارای يک نوع هستند آرايه ناميده می شود. رشته ها توسط راهنمای db اعلان
می شوند.
X dw
040Ch,10b,-13,0
Y db 'This is an array'
Z dd 10, 13, 'A','B','C'
مثال. برای
تعريف يک متغير آرايه از راهنمای dup استفاده می شود.
Memory
db 30 dup('$')
BigAry dw 100 dup(?)
برنامه اصلی
دستورالعمل
های برنامه در سگمنت کد قرار می گيرند. کد معمولا در زير برنامه نوشته می شود.
برای پايان اجرای برنامه و بازگشت به محيط سيستم عامل تابع 4c از وقفه
21h در انتهای هر برنامه بايد فراخوانی شود. اگر کد صفر در ثبات AL ذخيره
شود به معنی اينستکه برنامه با موفقيت به پايان رسيده است. در انتهای هر برنامه
دستورات زير بايد اضافه شود.
Mov
AX,4c00h
Int 21h
راهنمای end انتهای
سگمنت کد و پايان برنامه را برای اسمبلر مشخص می کند. عملوندی که در مقابل آن ذکر
می شود نقطه آغاز اجرا يا نام تابع اصلی برنامه را به سيستم عامل می گويد و باعث
می شود انتقال کنترل اجرا هنگام شروع اجرای برنامه می شود. در واقع ثبات های CS و IP را
تنظيم می کند. نقطه شروع الزاما بلافاصله بعد از راهنمای .code نيست.
اگر اين عملوند نباشد سيستم عامل از اولين بايت سگمنت کد شروع به اجرا می کند.
اسمبل و
اجرای برنامه
برای ايجاد
برنامه به سه ابزار نياز است: يک اديتور متن، يک اسمبلر برای تبديل برنامه به فايل
مقصد و يک لينکر برای توليد فايل اجرائی.
برنامه
اسمبلی را در يک اديتور متن نوشته و با پسوند .asm ذخيره
کنيد. فراموش نکنيد که حتما از يک اديتور اسکی استفاده کنيد. توسط اسمبلر (masm.exe يا tasm.exe) از
فايل مبدا .asm فايل مقصد .obj را ايجاد کنيد. اسمبلر برنامه زبان اسمبلی را به کد ماشين تبديل
می کند. اگر خطائی در برنامه وجود داشته باشد اسمبلر خطا را گزارش می دهد. لينکر (link.exe يا tlink.exe) از يک يا ترکيب چند فايل .obj يک
برنامه قابل اجرا از نوع .exe يا .com را می سازد.
مثال. برنامه
first.asm را در اديتور متن ذخيره کنيد. سپس در خط فرمان سيستم عامل، در
محلی که اسبلر نصب شده است، دستورات زير را به ترتيب وارد کنيد تا فايل اجرائی first.exe ايجاد شود.
C:\masm>masm
first
C:\masm>link first
C:\masm>first
مثال: در
برنامه زير دو عدد با هم جمع می شود.
.MODEL
small
.STACK [size]
.DATA
number1 DW 0800h ;=128
number2 DW ffebh ;=-493
sum DW ?
;store result
.CODE
begin:
mov AX,@Data
mov DS,AX
mov AX,number1 ;get first number in AX
add AX,number2 ;add AX with second number
mov sum,AX ;store result in Sum
mov AX,4c00h
int 21h
END begin
مثال: تکه
برنامه زير اعداد 1 تا 10 را در آرايه ای از نوع word ذخيره
می نمايد.
.MODEL
small
.STACK [size]
.DATA
Array DW 10 dup(?)
.CODE
begin:
mov AX,@Data
mov DS,AX
mov CX,1
mov SI, offset Array
Forl:
mov [SI], CX
inc SI
inc SI
cmp CX,10
je endf
inc CX
jmp forl
Endf:
mov AX,4c00h
int 21h
END begin
برای دسترسی
به عناصر آرايه معمولا از عملوند غيرمستقيم ثباتی استفاده می شود. برای تخصیص آدرس
آفست يک آرايه می توان از دستور LEA هم استفاده کرد. دقت کنيد چون عناصر آرايه دو بايتی هستند هربار
دو واحد به SI اضافه می شود. در حالتی که عناصر آرايه از نوع بايت تعريف می شوند
به اشاره گر آرايه يک واحد اضافه می شود.
مثال:
کاراکتر * را نمايش می دهد.
.MODEL small
.STACK [size]
.CODE
main PROC
mov AH,2h
mov DL,2ah
int 21h
mov AX,4c00h
int 21h
main ENDP
END main
ماکرو
ماکرو مجموعه ای از دستورات است که مشابه
زيربرنامه يکبار نوشته می شود و چندين بار استفاده می شود.
تعريف ماکرو
کتابخانه ماکرو
ماکرو (macro) نام مخففی برای مجموعه ای از دستورالعمل ها،
راهنماها يا ماکروهای ديگر است که يکبار نوشته می شود و به هر تعداد دفعات لازم
قابل استفاده است.
اسمبلر هنگام ترجمه برنامه در مواجهه با نام ماکرو
دستورات معادل را قرار می دهد.
تعريف ماکرو
تعريف ماکرو توسط برنامه نويس و با استفاده از
راهنمای macro
صورت می گيرد. فرم کلی تعريف ماکرو به شکل زير است:
MacroName MACRO
[parameter1, parameter2...]
...
MacroName ENDM
تعريف ماکرو با راهنمای macro شروع و با راهنمای endm پايان می پذيرد. نام ماکرو
قبل از هر دو راهنمای بايد يکسان باشد.
در ماکرو، برخلاف زيربرنامه، ارسال پارامتر امکان
پذير است.
مثال. تعريف ماکرو ExitPgm برای خروج از برنامه و
برگشت به محيط سيستم عامل.
; ExitPgm- Returns
control to MS-DOS
ExitPgm MACRO
mov AH, 4ch
int 21h
ExitPgm ENDM
مثال. ماکرو که مکان نما را به موقعيت داده شده
منتقل می کند.
Position MACRO Row,
Column
push AX
push BX
push DX
mov AH, 02H
mov DH, Row
mov DL, Column
mov BH, 0
int 10H
pop DX
pop BX
pop AX
Position ENDM
برای استفاده از ماکرو تنها کافی است نام آنرا
مشابه هر دستور ديگری فراخوانی کنيم. فراخوانی ماکرو مانند زيربرنامه نيازمند
دستور call
نيست.
ماکرو فوق به صورت زير فراخوانی می شود.
Position 8, 6
کتابخانه ماکرو
يکی از مزايای استفاده از ماکرو ها ايجاد کتابخانه
ای از ماکروهاست که می تواند در برنامه ضميمه شود. برای ايجاد کتابخانه کافی است
کليه ماکروها را درون يک فايل متن جداگانه ذخيره کنيد. برای استفاده از اين
ماکروها بايد ابتدا فايل ماکرو به فايل برنامه با راهنمای include ضميمه کنيد..دستور include معمولا در ابتدای برنامه
قبل از راهنمای .model
نوشته می شود.
INCLUDE NameOfTheFile
زيربرنامه
اين صفحه
نگاهی به زيربرنامه ها در زبان اسمبلی دارد. چگونگی تعريف زيربرنامه، دستورات
فراخوانی و برگشت از زيربرنامه و نحوه ارسال و دريافت پارامترها شرح داده خواهد
شد.
تعريف
زيربرنامه
زيربرنامه
های near و far
دستورات
فراخوانی و بازگشت زيربرنامه
ارسال
و دريافت پارمترها
زير برنامه (procedure) مجموعه ای از دستورات است که يکبار تعريف و به دفعات استفاده می
شود. با بکارگيری زيربرنامه خوانائی برنامه بالاتر رفته و از تکرار دستورات مشابه
جلوگيری می شود. علاوه براين اشکال زدائی و تغيير برنامه آسان تر انجام گيرد.
وقتی يک
زيربرنامه فراخوانی می شود کنترل اجرای برنامه به زيربرنامه هدايت می شود. آدرس
دستورالعمل بعدی در پشته ذخيره می شود بنابراين هنگامی که زيربرنامه اجرا شد کنترل
اجرا قادر خواهد بود به خط بعد از فراخوانی زيربرنامه بر می گردد.
تعريف
زيربرنامه
تعريف
زيربرنامه بايد در سگمنت کد انجام بگيرد. از دو راهنمای proc و endp برای
تعيين بلاک زيربرنامه استفاده می شود.
ProcedureName
PROC [NEAR|FAR]
...
RET
ProcedureName ENDP
Procedurename نام زيربرنامه است که قبل از راهنماهای proc و endp قرار می
گيرد و بايد يکسان باشد. عملوند near يا far اختياری است. کلمه near به اسمبلر می گويد که زيربرنامه از نوع داخلی است. برای تعريف يک
زيربرنامه خارجی از کلمه far به جای near استفاده می شود.
دستور ret باعث
خروج از زيربرنامه و برگشت به فراخواننده می شود.
نکته. اگر عملوندی مقابل تعريف زيربرنامه قرار نگيرد از
نوع near در نظر گرفته می شود.
نکته. اگرچه تعريف يک زيربرنامه که عملا داخلی است به صورت خارجی اشکالی ايجاد نمی
کند ولی بهتر است اين کار را نکنيد.
مثال.
زيربرنامه جمع دو عدد در AH و AL و نگهداری مجموع در ثبات BX.
Adding
PROC near
mov BX, AL
add BX, AH
ret
Adding ENDP
مثال:
زيربرنامه Putc برای نمايش کاراکتری که در ثبات al قرار
دارد.
Putc
PROC
mov DL,AL
mov AH,02
int 21h
ret
Putc ENDP
زيربرنامه
های near و far
دو نوع
زيربرنامه وجود دارد داخلی (intrasegment) و خارجی (intersegment).
• زيربرنامه های داخلی در همان سگمنتی که تعريف شده اند
قابل فراخوانی هستند و در تعريف آنها از صفت near استفاده
می شود.
• زيربرنامه های خارجی روال هائی که در سگمنت ديگری قرار دارند و از ساير سگمنت ها
قابل فراخوانی می باشند و در تعريف آنها از صفت far استفاده
می شود. زيربرنامه های خارجی درفايل جداگانه ای قرار دارد و هنگام لينک کردن بايد
به برنامه پيوند داده شوند. نتيجه کار بعد از لينک مانند زيربرنامه داخلی است.
فراخوانی از
نوع near کنترل را درون همان سگمنت کد جابجا می کند وتنها مقدار IP در پشته
ذخيره می شود. فراخوانی far کنترل را بين سگمنت های مختلف عبور می دهد. هر دو مقادير CS و IP در پشته
قرار می گيرند.
نکته.
دستورات call و ret نوع فراخوانی را مشخص نمی کنند بلکه عملوند near|far راهنمای
proc به اسمبلر می گويد فراخوانی از کدام نوع است.
دستورات
فراخوانی و بازگشت زيربرنامه
دودستورالعمل
که پشته را استفاده می کنند و فراخوانی و برگشت زيربرنامه را انجام می دهند call و ret هستند.
برای هدايت کنترل اجرا به زيربرنامه بايد آنرا فراخوانی کرد. زيربرنامه ها در هر
کجای برنامه که به آن نياز داريم با دستور call فراخوانی می
شوند. دستور call به صورت زير است:
call
ProcedureName
دستورالعمل call باعث يک
پرش غير شرطی به زيربرنامه می شود و آدرس دستورالعمل بعدی را در پشته ذخيره می
کند. CPU در برخورد با دستور call به آدرس شروع
زيربرنامه رجوع می کند و دستورات آنرا اجرا می نمايد. با برخورد به دستور ret به
برنامه فراخوان بر می گردد و دستورات بعد از call را اجرا
می نمايد.
مثال.
فراخوانی زيربرنامه Putc.
call
Putc
CPU در برخورد با دستور Call عمليات زير را
انجام می دهد:
فراخوانی از
نوع داخلی
1. مقدار ثبات IP (که حاوی آدرس
دستور بعد از call است ) را در پشته ذخيره می کند.
2. آدرس ذکر شده مقابل دستور call را در ثبات IP قرار می دهد.
فراخوانی از
نوع خارجی
1. مقدار ثبات CS را در پشته
ذخيره می کند.
2. بخش سگمنت آدرس ذکر شده مقابل دستور call را در ثبات CS قرار می
دهد.
3. مقدار ثبات IP را در پشته ذخيره می کند.
4. بخش آفست آدرس ذکر شده در جلوی دستور call را در ثبات IP قرار می
دهد.
دستورالعمل ret آدرس
ذخيره شده IP را از پشته بر می دارد و به برنامه اصلی بر می گردد. CPU در
برخورد با دستور Ret عمليات زير را انجام می دهد:
بازگشت از
زيربرنامه داخلی
1. مقدار
ذخيره شده در پشته را در داخل ثبات IP قرار می دهد.
بازگشت از
زيربرنامه خارجی
1. مقدار ذخيره شده در پشته را در داخل ثبات IP قرار می
دهد.
2. مقدار ذخيره شده در پشته را در داخل ثبات CS قرار می
دهد.
نکته. اگر دستور ret در انتهای
زيربرنامه حذف شود کنترل اجرای برنامه به زيربرنامه بعدی می رود نه دستورالعمل
بعدی در برنامه اصلی.
نکته. معمولا در ابتدای هر زيربرنامه بهتر است مقادير ثبات هائی که تغيير می کنند
را در پشته ذخيره نمائيم و در انتهای زيربرنامه و قبل از دستور ret مقادير
آنها را از پشته بازيابی کنيم. بايد توجه کنيم که دستورات pop متناظر
با دستورات push باشند و کليه داده هائی که در زيربرنامه در پشته push شده اند
بايد pop شوند وگرنه به با دستور ret به آدرس درست
پرش نمی کند.
مثال.
زيربرنامه برای نمايش 40 کاراکتر space. توجه کنيد زيربرنامه Putc درون زيربرنامه PrintSpaces فراخوانی شده است.
PrintSpaces
PROC near
push
AX
push
CX
mov
AL, ' '
mov
cx, 40
PSLoop: call putc
loop
PSLoop
pop
CX
pop
AX
ret
PrintSpaces ENDP
در ابتدای
زيربرنامه ثبات های AX و CX در پشته قرار می گيرند و در انتها به ترتيب عکس بازيابی می شوند.
زيربرنامه فوق به صورت زير فراخوانی می شود.
call
PrintSpaces
ارسال و
دريافت پارمترها
پارامترها
مقاديری هستند که می توانيد به زيربرنامه بدهيد يا بگيريد. برای ارسال يا دريافت
پارامترها معمولا از ثبات، متغيرهای سراسری يا پشته استفاده می شود.
ارسال
پارامتر از طريق ثبات
مثال.
زيربرنامه زير طول يک رشته را محاسبه و در ثبات CX
برمیگرداند. آدرس شروع رشته در ثبات SI قرار دارد.
StrLen
PROC
push SI
mov CX,0
Whl: cmp Byte Ptr[SI],'$'
jc EndW
inc CX
inc SI
jmp Whl
EndW: pop SI
ret
StrLen ENDP
ارسال
پارامتر از طريق پشته
پارامترهائی
که به زيربرنامه داده می شوند را می توان قبل از فراخوانی زيربرنامه در پشته اضافه
کرد. پارامترها در زيربرنامه pop نمی شوند بلکه مستقيما از پشته دسترسی می شوند زيرا قبل از دستور call در پشته
اضافه شده اند و آدرس برگشتی بعد از آن اضافه می شود. علاوه براين چون ممکن است در
چندين جای زيربرنامه استفاده شوند معمولا درون ثبات نگهداری نمی شوند و بهتر است
در حافظه پشته باقی بمانند.
يک برنامه
خارجی که يک پارامتر از طريق پشته را ارسال می کند در نظربگيريد. وقتی زيربرنامه
درخواست می شود پارامتر می تواند با آدرس دهی غيرمستقيم [SP+4] دسترسی
شود. اگر پشته هم در زيربرنامه برای ذخيره داده استفاده شود عدد بيشتری بايد به SP اضافه
شود. ثبات BP را برای ارجاع به داده های درون پشته می توان به کار برد. ثبات SP با هر push و pop تغيير
می کند اما BP ابتدا برابر با SP می شود و سپس ثابت می ماند در انتهای زيربرنامه مقدار اوليه BP بايد
برگردانده شود. بعد از اينکه زيربرنامه تمام شد پارامترهائی که در پشته اضافه شده
اند بايد حذف شوند.
مثال. تابع
زير طول رشته را محاسبه و آدرس شروع رشته از طريق پشته به زيربرنامه
ارسال می شود.
StrLen
PROC
push BP
mov BP,SP
mov SI,[BP+4]
sub CX,0
Whl: cmp byte ptr [SI],'$'
jc Endw
inc CX
inc SI
jmp Whl
EndW: pop BP
ret
StrLen ENDP
مثال. محاسبه
مجموع سه عدد که از طريق پشته به زيربرنامه ارسال شده اند