برنامه نويسی اسمبلی
در اين صفحه ساختار کلی يک برنامه به زبان اسمبلی توضيح داده می شود. به نحوه تعريف متغيرها و ثابت ها، استفاده از راهنماهای اسمبلر و اسمبل کردن و اجرای برنامه نيز اشاره شده است. .
ساختار يک برنامه اسمبلی
شناسه ها
مدل حافظه
راهنماهای سگمنت
ثابت ها و متغيرها
برنامه اصلی
اسمبل و اجرای برنامه
ساختار يک برنامه اسمبلی
هربرنامه اسمبلی از تعدادی خط تشکيل شده است (line oriented). هر خط اگر توضيح نباشد شامل چهار فيلد می تواند باشد: برچسب، نماد سمبليک، عملوندها و توضيحات. قالب کلی يک عبارت به صورت زير است:
[label] mnemonic [operand][;comment]
مثال.
MovInstruction: mov AX,BX ;this is a MOV instruction
برچسب
برچسب ها در برنامه اسمبلی مانند زبان های سطح بالا بکار گرفته می شوند. برچسب (label) يک فيلد اختياری است که نامی را به عبارت اختصاص می دهد وامکان مراجعه و پرش به اين دستور را از دستورات ديگر فراهم می کند.
برچسب های درون سگمنت کد به علامت کولن (:) ختم می شوند و اغلب به عنوان مقصد در دستورات پرش استفاده می شوند.
مثال.
mov cx, 25
Loop1: mov ax, cx
call PrintInteger
loop Loop1
يک برچسب می تواند حداکثر 31 کاراکتر باشد. کاراکترهای معتبر برچسب عبارتند از: حروف، ارقام و کاراکترهای $ _ @ و . هستند. برچسب نبايد از کلمات کليدی باشد و با رقم شروع شود و کاراکتر نقطه تنها می تواند به عنوان اولين کاراکتر آن استفاده شود.
نکته. برای خوانائی برنامه بهتر است برچسب از ستون اول آغاز شود.
نکته. بعضی از نمادهای سمبليک نيازمند يک برچسب هستند.
نکته. اسمبلر حساس به متن نيست. بين کاراکترهای کوچک و بزرگ تفاوتی قائل نمی شود.
نماد سمبليک
فيلد نماد (mnemonic) شامل يک دستورالعمل است. دستورالعمل ها به دو دسته تقسيم می شوند: دستورالعمل های زبان اسمبلی، راهنماهای اسمبلر(assembler directives).
منظور از دستورالعمل های زبان اسمبلی دستورالعمل های 80x86 معرفی شده در بخش قبل هستند (مانند mov، add و ...). در زمان ترجمه برنامه اسمبلر معادل کد زبان ماشين آنها را پيدا کرده و جايگزين می کند.
راهنماهای اسمبلر دستورات خاصی هستند که از دستورالعمل های معتبر 80x86 نيستند و در فايل ترجمه شده ظاهر نمی شوند بلکه هنگام ترجمه برنامه به اسمبلر مطالبی را می فهمانند (model، .stack، .data،.code، dw و end).
عملوند
فيلد عملوند (operand) شامل پارامترهای موردنياز دستورالعمل مشخص شده در فيلد نماد هستند. هر دستورالعمل با توجه به عملکردش ممکن است شامل هيچ، يک يا دو عملوند باشد. عملوند مکان داده مورد نياز پردازنده جهت انجام عمل خواسته شده را مشخص می کند. فيلد عملوند در راهنماهای اسمبلر معمولا شامل اطلاعات بيشتری است.
مثال.
nop ;no operands -- does nothing
inc AX ;one operand -- adds 1 to the contents of AX
add Word1,2 ;two operands -- adds 2 to the contents
; of memory word WORD1
نکته. عملوندها هرگز تنها ظاهر نمی شوند. نوع و تعداد عملوندها بستگی به دستورالعمل دارد.
نکته. عملوندها با کاما از هم جدا می شود.
نکته. در دستورات دو عملوندی، عملوند اول مقصد (destination) و عملوند دوم مبدا (source) ناميده می شود.
توضيح
فيلد توضيح (comment) اجازه می دهد برای هر خط برنامه يادداشتی را بنويسيد. فيلد توضيح هميشه با (;) شروع می شود. وقتی اسمبلر يک خط را پردازش می کند هر چيزی که با سميکولن شروع شود را نديده می گيرد. درک برنامه اسمبلی بدون توضيحات تقربا غرر ممکن است. برنامه های خوب برای هر دستور توضيحی می نويسند .
مثال.
; Initialize registers
mov AX,0
mov BX,0
mov CX,0 ;CX counts terms, initially 0
نکته. هر عبارت زبان اسمبلی در يک خط ظاهر می شود. نمی توانيد چند عبارت زبان اسمبلی را در يک خط داشته باشيد.
نکته. می توانيد بين خط های برنامه خط خالی قرار دهيد. خط های خالی برای جدا کردن بخش های برنامه مفيد هستند.
نکته. فيلدهای مختلف می توانند در هر ستونی قرار بگيرند. هر تعداد فضای خالی می تواند فيلدها را از هم جدا کند. استفاده درست از فضای خالی در برنامه باعث خوانايی بيشتر برنامه می شود.
نکته. بين فيلدهای يک عبارت حداقل يک فاصله وجود دارد.
نکته. تنها فيلدی که بايد در هر دستور زبان اسمبلی وجود داشته باشد نماد سمبليک است.
شناسه ها
شناسه (identifier)، سمبل ، يا برچسب اسمی است که به مقدار مشخصی نسبت داده می شود. اين مقدار می تواند آفستی در يک سگمنت، يک مقدار ثابت، يک آدرس سگمنت، آفستی درون يک رکورد يا حتی عملوند يک دستورالعمل باشد. در هر صورت شناسه امکان ارائه چيزی را با نام آشنا و قابل فهمی را می دهد.
نام شناسه از حروف، ارقام و کاراکترهای خاص تشکيل شده است. با محدوديت های زير:
• يک شناسه نمی تواند با يک رقم عددی شروع شود.
• يک اسم می تواند هر ترکيبی از حروف بزرگ و کوچک باشد. اسمبلر با حساس به متن نيست.
• يک شناسه ممکن است هر تعداد کاراکتری باشد ولی تنها 31 کاراکتر اول آن استفاده می شود. اسمبلر کاراکترهای بعدی را نديده می گيرد.
• کاراکترهای _، $، ? و @ ممکن است درون يک سمبل ظاهر شوند. البته کارکترهای $ و? خاص هستند و نمی توانيد شناسه را منحصرا با اين کاراکترها بسازيد.
• يک شناسه نمی تواند از اسامی رزرو شده باشد. دستورالعمل های 80x86 و نام ثبات ها رزرو شده هستند.
مثال. چند شناسه مجاز.
| Item1 |
Bletch |
RightHere |
Right_Here |
| __Special |
$1234 |
@Home |
Dollar$ |
| WhereAmI? |
@1234 |
.TEST |
SUM_OF_DIGITS |
مثال. بعضی شناسه های غيرمجاز.
| 1TooMany |
Hello.There |
$ |
| LABEL |
Right Here |
Hi,There |
مدل حافظه
مدل حافظه برنامه توسط راهنمای .model مشخص می شود. عملوند مقابل آن می تواند يکی از انتخاب های زير باشد:
| تعداد سگمنت کد |
تعداد سگمنت داده |
مدل حافظه |
| 1 |
- |
Tiny |
| 1 |
1 |
Small |
| بيشتر از يکی |
1 |
Medium |
| 1 |
بيشتر از يکی |
Campact |
| بيشتر از يکی |
بيشتر از يکی |
Large |
| آرايه های بزرگتر از 64K |
Huge |
| بدون سگمنت، تنها در مد محافظت شده |
Flat |
راهنماهای سگمنت
هر برنامه شامل يک يا چند سگمنت است. هنگامی که برنامه دارد اجرا می شود ثبات های سگمنت به سگمنت های جاری اشاره می کنند. چهار سگمنت را در آن واحد می توان داشت؛ کد، داده، پشته و اضافی. در مد حقيقی هر سگمنت حداکثر 64KB است. البته معمولا برنامه ها کمتر از 64KB را استفاده می کنند. اسمبلر اندازه سگمنت را بر اساس تعداد بايت های مورد استفاده سگمنت تنظيم می کند. بنابراين اگر برنامه ای برای نمونه تنها 10KB برای ذخيره داده نياز دارد سگمنت داده 10KB می شود نه 64KB.
برنامه های .exe سه سگمنت اول را بايد داشته باشند.
• سگمنت داده برای ذخيره متغيرهاست. آدرس متغيرها به صورت آفستی از شروع اين سگمنت محاسبه می شوند.
• سگمنت کد شامل دستورالعمل های اجرائی برنامه است.
• سگمنت پشته برای نگهداری داده های موقتی و آدرس های برگشتی از برنامه پشته رزرو می شود. آدرس های پشته به صورت آفستی از ابتدای اين سگمنت محاسبه می شوند.
وقتی اجرای برنامه آغاز می شود سيستم عامل دو ثبات سگمنت CS و SS را برای اشاره به کد برنامه و سگمنت پشته مقداردهی می کند. برای دسترسی به سگمنت داده ثبات ds بايد حاوی آدرس سگمنت داده باشد. قبل از دسترسی به هر داده ای برنامه بايد آدرس سگمنت را در ثبات DS ذخيره کند.
سگمنت ها در برنامه اسمبلی توسط راهنماهای segment و ends مشخص می شوند. يک سگمنت به فرم کلی زير مشخص می شود:
segmentname segment {READONLY} {align} {combine} {use} {'class'}
<statements>
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
|