آموزش اسمبلی - Assembly

وضعیت
موضوع بسته شده است.

saalek110

Well-Known Member
از همين تاپيك : پست بعدي.
= = == = == = = ==
نستا:
در اين قسمت نحوه دسترسي به مقادير متغير ها را ياد ميگيريم .
وقتي كه ميخواهيم مقدار يك متغير را به يك متغير يا ثبات ديگر منتقل كنيم بايد
به اندازه آن توجه داشته باشيم . مثلا اگر متغيري بصورت:
LOCATE DB 10
تعريف كرده باشيم ، به دليل تك بايتي بودن ، نميتوانيم آن را به يك ثبات كامل مثل AX يا متغير دوبايتي كه با DW تعريف شده است ارسال كنيم .
اما انتقال آن به يك نيم ثبات مثل ALيا AH و ... مجاز است مانند:
MOV BH,LOCATE

از متغيرها بيشتر براي نگهداري موقت داده ها استفاده ميشود .
مثلا وقتي كه برنامه اي براي كار با قطاعهاي ديسك مينويسيم ، بايد يك محل موقتي براي ذخيره محتواي قطاع هاي خوانده شده ايجاد كنيم .
در اين موقع يك متغير به شكل (ترجيحا)آرايه تعريف ميكنيم .
وقتي به اين شكل با متغيرها برخورد ميشود، به دانستن آدرس آن نياز پيدا ميكنيم.

فرض كنيد ميخواهيم جمله:
A QUICK START TO ASSEMBLY PROGRAMMING
را چاپ كنيم .
در قدم اول بايد متغيري تعريف كرده و اين جمله را داخل آن قرار دهيم .
پس:
MSG DB 'A QUICK START TO ASSEMBLY PROGRAMMING',13,10,'$'

اعداد 13وَ10 انتهاي رشته براي انتقال مكان نما به سطر بعد هستند و كاراكتر '$'
از اين جهت وجود دارد كه تابع چاپ رشته انتهاي رشته كاراكتري را با بودن $
تشخيص ميدهد.
براي چاپ رشته كاراكتري راه هائي وجود دارد كه يكي از آنها استفاده از تابع 9h مربوط به INT 21h ميباشد .
براي فراخواني آن بايد به اين صورت رجيستر ها را پر كنيم:
AH=09H
آدرس رشته كاراكتري DS:DX = INT 21H

عبارت DS:DX نشان ميدهد كه مقدار قطعه (Segment) رشته كاراكتري ، يعني آن قطعه اي كه متغير تعريف شده در آن قرار گرفته است ، را بايد در DS قرار بدهيم .
به همين صورت نيز مقدار آفست (Offset) آن را به DX انتقال ميدهيم .
براي بدست آوردن شماره قطعه يك متغير از عملگر SEG استفاده ميكنيم .
مثلا براي بدست آوردن شماره قطعه MSGاز:
MOV AX,Seg MSG
استفاده ميكنيم . اين دستور شماره سگمنت MSG را پيدا كرده و در AX قرار ميدهد .
براي بدست آوردن شماره آفست هم از OFFSET استفاده ميكنيم مثلا:
MOV DX,OFFSET MSG

پس براي چاپ رشته MSG بايد به اين صورت عمل كنيم :

MOV AH,09H
MOV DX,OFFSET MSG
INT 21H


اين قطعه كاري كه ما ميخواهيم را انجام ميدهد و اگر دقت كنيد متوجه ميشويد كه اصلا شماره قطعه (Segment) را محاسبه نكرده ايم . علت اينست كه متغير ما به دليل
COM
بودن برنامه در
Code Segment
كه با
CODE.
مشخص ميشود تعريف شده پس خود بخود DS حاوي مقدار سگمنت آن هست .
( باز هم ياد آوري ميكنيم كه CS حاوي شماره ثبات كد و DS حاوي ثبات داده ها است و در برنامه هاي COM. مقدار برابر دارند)

يك دستور خلاصه براي بدست آوردن عدد آفست وجود دارد بنام
LEA .
كل كاري كه اين دستورالعمل انجام ميدهد اينست كه ديگر احتياج به نوشتن
OFFSET
نخواهد بود . به عنوان مثال
MOV DX,OFFSET MSG
با
LEA DX,MSG
برابر است .

با اين تفاسير كل برنامه به اين شكل خواهد بود .
کد:
.model tiny 
.code
org 100h

START :
JMP MAIN          ; skip to main codes 
MSG DB 'A QUICK START TO ASSEMBLY PROGRAMMING',13,10,'$' 
MAIN :
LEA DX,MSG    ; get MSG offset 
MOV AH,09       ; write string function 
INT 21H             ; call interrupt 21h 
INT 20H             ; terminate program 
END START

تمرين :
براي اينكه تمرين بهتري داشته باشيم ، ميخواهيم خودمان و فقط با استفاده از وقفه
مربوط به چاپ كاراكتر همين جمله را چاپ كنيم . قبلا گفتيم كه تابع 0Eh از وقفه
10h يك كاراكتر را در محل مكان نما چاپ كرده و مكان نما را يك خانه به راست انتقال ميدهد. ميخواهيم رشته كاراكتري بالا را تا رسيدن به علامت $ چاپ كنيم . بهترين كار اينست كه عدد آفست را در BX قرار بدهيم . در اينموقع آفست اولين كاراكتر در BX است . مقدار داخل اين آفست را بصورت
MOV al,[bx]
به ثبات AL منتقل كرده و بعد چاپ ميكنيم . براي كاراكتر بعدي يك واحد به BX اضافه ميكنيم و دوباره همان كارهاي قبلي ... . اين عمليات را بايد تا رسيدن به كاراكتر '$' ادامه بدهيم .
اين برنامه را خودتان و بدون توجه به راه حل ارائه شده بنويسيد و فايل COM.
آن را بسازيد.


کد:
.model tiny 
.code
org 100h

START :
JMP MAIN             ; jump to MAIN 
MSG DB 'A QUICK START TO ASSEMBLY PROGRAMMING',13,10,'$' 
MAIN :
LEA BX,MSG         ; get MSG offset
MOV AH,0EH         ; write char function 
halgheh :
MOV AL,[BX]         ; move [BX] to AL: charactre code 
CMP AL,'$'              ; if al is equal with '$' 
JE payan                    ; then jump to END 
INT 10H                   ; otherwise call interrupt 10h 
INC BX                    ; BX=BX+1 
JMP halgheh              ; jump to next caharcter 
payan :
INT 20H                  ; terminae program 
END START
سالك: دو برنامه اين پست را هر دو را اجرا كردم . بي نقص بود.
با توضيحات داده شده فكر نمي كنم ديگه توضيح لازم باشد.
 

saalek110

Well-Known Member
پست ديگري از همين تاپيك:
مقاله اي راجع به وقفه ها از نستا:
= = = = == = == == = = = = ==
وقفه هاي (Interrupts) CPU

براي اينكه بتوانيد كارهاي مختلفي را انجام دهيد،از وقفه ها استفاده ميكنيد .

يك وقفه درخواستي از CPU است كه در طي آن زير برنامه اي اجرا ميشود.

وقتي كه وقفه فراخواني ميشود، CPU اعمال ديگر را متوقف كرده و آن اينتراپت را پردازش ميكند .

به طور كلي وقفه ها به دودسته تقسيم ميشوند:
َ1- وقفه هاي سخت افزاري (Hardware Interrupts) . وقفه هائي هستند كه از سوي ادوات سخت افزاري كامپيوتر مانند كيبورد و ... اجرا ميشوند. مثلا با فشرده
شدن هر كليد ، يكبار وقفه شماره 9 فراخواني ميشود.
2- وقفه هاي سخت افزاري (SoftWare Interrupts). اين وقفه ها در بايوس (BIOS)
كامپيوتر قرار دارند. بايوس كامپيوتر يك تراشه (IC) قابل برنامه ريزي است كه
بنا بر نوع پردازنده بر روي برد اصلي كامپيوتر قرار ميگيرد . بعلاوه خود DOS
نيز وقفه اي (وقفه 21h) را اداره ميكند كه به وقفه DOS معروف است . اين توابع توسط MSDOS.SYS تعريف ميشوند ولي در نهايت به بايوس مراجعه ميكنند.
هر وقفه داراي يك شماره خاص خود است و از صفر شروع ميشود .
وقفه 21h (سرويس DOS
) نيز داراي 255 سرويس ديگر است .
براي اينكه بتوانيم يك برنامه خوب و مفيد بنويسيم بايد بتوانيم از اينتراپتها
به نحو صحيح استفاده كنيم . پس هر برنامه نويس اسمبلي بايد يك مرجع كامل
اينتراپت در اختيار داشته باشد.
وقتي ميخواهيم يك وقفه را فراخواني كنيم ، ابتدا (درصورت لزوم ) ثباتهاي خاصي را مقدار دهي ميكنيم . معمولا نيم ثبات AH ، از اين جهت كه اكثر اينتراپتها داراي چند سرويس مختلف هستند ، شماره تابع را مشخص ميكند . به همين صورت ، واگر لازم باشد ، ثباتهاي ديگر را هم مقدار دهي ميكنيم . مثلا فرض كنيد ميخواهيم كليدي را از صفحه كليد بخوانيم . تابع شماره 0 از وقفه 16h ميتواند اين كار را انجام دهد . وقتي ميگوئيم تابع شماره 0 ، يعني بايد به AH مقدار 0 بدهيم و بعد اينتراپت 16h را فراخواني كنيم .
فراخواني اينتراپت به سادگي و با دستورالعمل INT انجام ميشود. به صورت :
INT int_no

كه int_no شماره اينتراپت ميباشد . در مورد اين مثال بايد دستورات زير را انجام
دهيم
mov ah,0
int 16h

وقتي يك وقفه فراخواني ميشود ، ممكن است روي ثباتها تاثير گذاشته و مقدار آنها
را عوض كند. به اين وسيله ما ميتوانيم وضعيت اجراي وقفه را بدست بياوريم . در مورد اين مثال ، پس از خوانده شدن كليد ، كد اسكي (ASCII) كليد در ثبات AL قرار ميگيرد .
مثلا اگر حرف A تايپ شود ، مقدار AL برابر 65 خواهد بود.
حالا اگر عدد AH را قبل از فراخواني وقفه بجاي 1 برابر Eh قرار دهيم و وقفه 10hرا اجرا كنيم ، بجاي خواندن كليد، يك كاراكتر را چاپ ميكند . به اين صورت كه كد اسكي كاراكتر در ثبات AL و عدد Eh در ثبات AH قرار گرفته و وقفه 10h فراخواني ميشود
mov AX,0E07h
in 10h

به سطر اول توجه كنيد !. وقتي ما يك عدد دوبايتي (Hex) را به AX ارسال ميكنيم ، دوبايت بالا در AH و دوبايت پائين در AL قرار ميگيرد . پس در اين مثال كاراكتر شماره 7 بايد چاپ شود و چون اين كد مربوط به كاراكتر Bell است ، صداي بيپ شنيده خواهد شد.

خاتمه دادن به برنامه :
وقتي كه يك برنامه به انتها رسيد يا اگر خواستيم اجراي برنامه را متوقف كنيم ، ميتوانيم از اينتراپت 20h استفاده كنيم . DOS هميشه و بمحض اجراي اين وقفه ، اجراي برنامه را متوقه ميكند.
اينراپت 20h فقط با برنامه هاي COM. درست كار ميكند و در مورد برنامه هاي EXE. درست جواب نميدهد . در عوض سرويس 4Ch از اينتراپت 21h در هر دونوع برنامه بخوبي كار ميكند .
خوب ، حالا با مطالبي كه ياد گرفتيم يك برنامه اسمبلي نوشته و فايل COM. آن را ميسازيم .
بنابر اين در محيط DOS، DEBUG، را اجرا كنيد .
D:\MASM>DEBUG

سپس دستور A را به معني شروع دستورات اسمبلي وارد كنيد : - A
xxxx:0100

به عدد آدرسي كه ديده ميشود توجه نكرده و دستورات زير را تايپ كنيد
mov ah,2
mov al,7
int 16
int 20

بعد از تايپ آخرين سطر، يكبار ديگر هم كليد Enter را بزنيد تا اعلان debug مجددا ظاهر شود. حالا دستور N را براي نامگذاري برنامه بكار ببريد
- N BELL.COM

بعد از آن بايد طول برنامه را ، برحسب بايت ، مشخص كنيم . طول برنامه در ثبات CX نگهداري ميشود پس از فرمان RCX براي مقدار دهي استفاده ميكنيم . (طول برنامه 8 بايت است)
. - RCX
8

و در نهايت فرمان w براي نوشتن روي ديسك و Q براي خروج . حالا ما يك فايل COM.
داريم كه به محض اجرا يك صداي Beep توليد ميكند .

ما امروز اولين برنامه اسمبلي خودمان را نوشتيم ، در قسمت بعد ياد ميگيريم كه
چطور از اسمبلر استفاده كنيم و امكانات آن را بكار ببريم .

== == ==
سالك:
برنامه :
mov ah,2
mov al,7
int 16
int 20
يك برنامه كامل است در محيط ديباگ و دستور خاتمه هم دارد كه همان int 20 است. پس براي اجراي آن مي توانيد از دستور g استفاده كنيد.
من در برنامه هايي كه در ديباگ مي نوشتم هميشه از دستور t استفاده مي كردم. چون برنامه هاي من دستور خاتمه نداشت و ثانيا اين كه من بيشتر مي خواستم تغيير رجيسترها را نشان دهم كه با g نشان نمي دهد. اين دستور g برنامه را اجرا مي كند و بلافاصله بعد خاتمه برنامه رجيسترها را به شكل حالت اول در مي آورد.
همچنين به طرز ساختن فايل با ديباگ ويندوز توجه كنيد. براي امتحان هم كه شده يك برنامه در ديباگ بنويسيد و با دستوراتي كه نستا گفته فايلش را بسازيد.
من وقتي اين جوري فايل مي ساختم .فايلها مي رفت روي دسكتاپ. تجربه جالبي بود. من هم اولين فايل خود را با ديباگ ساختم و اين پست من را به ياد آن روز انداخت. با n نام مي دهيم. بعد اندازه فايل كه برابر تعداد بايتهاي نوشته شده ماست را مي دهيم. اگر بيشتر هم بدهيد زياد مهم نيست چون برنامه شما اختتام دارد و همش اجرا نميشه و در آخر هم با w فايل ساخته ميشه. من الان همين برنامه كوچك را در ديباگ تبديل به فايل مي كنم و نتيجه را گزارش مي كنم:
با اون برنامه من چيزي گيرم نيامد. من اين جوري نوشتم:
Mov ah,2
Mov dl,7
Int 21
Int 20
كه يك دينگ دريافت كردم.
با دستورات زير رفتم براي ساخت فايل:
N ff.com[inter]
R cx[inter]
20[inter]
w[inter]
و فايل هم باز روي دسكتاپ افتاد.
 

saalek110

Well-Known Member
يك بسته 250 كيلويي براي دانلود اينجا هست :
http://www.eji.com/a86.zip

كه من دانلود كردم. يك برنامه به نام a86 داخلشه. يكي هم به نام d86 . دومي فكر كنم يك نوع ديباگر باشه. ولي اولي يك خوبي كه من ازش فعلا ديدم اينه كه مي توانيم در prompt dosاين طوري بنويسيم:
A86 myfile.asm ff.com

يعني يكباره از asm مي رود به com . و كار ما را راحت مي كند.

قبلا بحثهايي راجع به فرقهاي tasm و a86 داشتيم. البته نمي دونم كه اين a86 همان a86 است يا نه.
راستش من دنبال يك ديباگر داشتم سرچ مي كردم ولي اين ديباگر d86 را هنوز چيزي ازش نفهميدم. البته توربوديباگر را دارم. مي خواستم يك چيزي را با هم دانلود كنيم تا باهم كار كنيم.

اين طوري هم نوشتم:
A86 myfile.asm
و خودش اتوماتيك فايل com است. البته كد من براي ساخت فايل com بود. كد فايل مناسب براي exe سازي را هم دادم كه اشتباها رفت com ساخت. ولي خوب براي com ساختن كه خوبه. بيشتر برنامه هاي اخير ما هم كه شده فايل com .

= == = = == =
يك نكته جالب:
فايل exe ئي كه الان روي آن امتحان كردم داخلش اين كد بود:
mov ax,@data

كه a86 خطا گرفت و گفت نمي شناسد. كه اين جوري دادم:
mov ax,data
و قبول كرد.
و يكي از فرقهاي a86 و tasm همين بود. پس احتمال اينكه اين a86 همان a86 مورد بحث باشه بيشتره.
= = = = == =
كلا چون حجمش كوچكه فايل(250 كيلو)،
پيشنهاد مي كنم دانلود كنيد و يك نگاهي بكنيد.
 

saalek110

Well-Known Member
بحث حلقه هاي تو در تو:

برنامه اي كه جناب نستا براي رسم پيكسلها نوشتند يعني برنامه زير:
کد:
.model tiny 
.code
org 100h
START :
; -----------------------------------
MOV AH,00H 
MOV AL,13H 
MOV BX,00H ; PAGE NUMBER 
INT 10H ; SET TO 320x200 256 COLORS
; -----------------------------------
MOV AH,0CH ; PUTPIXEL FUNCTION
MOV AL,25  ; COLOR #25 
MOV DX,0   ; ROW 0 
ROW :
; ----------------------------
MOV CX,319 ; COLUMN 319 = START COLUMN 
COL :
INT 10H    ; CALL INTERRUPT 10H 
LOOP COL   ; DOWN TO CX=0
; ----------------------------
INC DX     ; DX=DX+1 
INC AL     ; AL=AL+1( COLOR NUMBER )
CMP DX,199 ; IF DX=199 
JNZ ROW    ; ELSE JUMP TO ROW 

MOV AH,00H 
INT 16H 

MOV AH,00H ; VIDEO MODE SET
MOV AL,03H ; 80x25 16 COLORS 
INT 10H    ; CALL INT .10H 
INT 20H    ; TERMINATE PROGRAM
END START

از بعد ديگري قابل توجه است و آن كاربرد حلقه هاي تو در تو است.
من براي ساده شدن كد بالا ، قسمتهاي رسم پيكسل آن را حذف مي كنم و فقط حلقه تو در تو را باقي مي گذارم.
کد:
.model tiny 
.code
org 100h
START :

MOV DX,0  
; --------------- outer loop -------------- 
ROW :
; --------- inner loop -------
MOV CX,10 
COL :

LOOP COL   ; DOWN TO CX=0
; ----------------------------
INC DX     ; DX=DX+1 

CMP DX,10 ; IF DX=10 
JNZ ROW    ; ELSE JUMP TO ROW 
; -----------------------------------------

INT 20H    ; TERMINATE PROGRAM
END START

با خطوط كامنت حلقه هاي دروني و بيروني را مشخص كرده ام. برنامه بالا بي اشكال اجرا شد ولي كاري انجام نمي دهم.

شرح برنامه كوچك شده بالا:
برنامه اول dx را مقدار صفر داده. كه اين در حلقه بيروني استفاده مي شود . چون با دستور inc dx مقدار dx افزايش مي يابد و دستور شرطي jnz با دستور مقايسه cmp با هم چك مي كنند تا از 10 بالاتر نرود dx و اگر چنين شود ، پرش صورت نمي گيرد و برنامه خاتمه مي يابد.
اما حلقه دروني با cx است كه حلقه مرسوم اسمبلي است. يعني تعداد چرخش را در cx قرار مي دهيم. كه من 10 گذاشته ام و بعد loop به تعداد 10 بار مي چرخد.

= = == == = =
نقد برنامه نستا:
نستا مي خواسته هم cx و هم dx تغيير كند. چون اينها x و y پيكسل بوده. و داخل حلقه از اين دو براي رسم پيكسل استفاده كرده. چون ايشان مي خواسته تمام صفحه را با پيكسل بپوشاند ، پس بايست همزمان x و y كه در اينجا cx و dx است ، تغيير كند.
پس حلقه تو در توي برنامه ايشان ايده آل براي اين كار است.
پس با يك آفرين بر ايشان نقد ما پايان مي يابد.

= = == = = == = = = = =
حالا من مي خواهم زيربرنامه bl_print را اضافه كنم به اين برنامه و dx را چاپ كنم.
کد:
.model tiny 
.code
org 100h
START :

MOV DX,0  
; --------------- outer loop -------------- 
ROW :

; --------- inner loop -------
MOV CX,10 
COL :

LOOP COL   ; DOWN TO CX=0
; ----------------------------
INC DX     ; DX=DX+1 
mov bl,dh
call bl_print
mov bl,dl
call bl_print
push dx
mov ah,2
mov dl,2dh
int 21h
pop dx

CMP DX,10 ; IF DX=10 
JNZ ROW    ; ELSE JUMP TO ROW 
; -----------------------------------------

INT 20H    ; TERMINATE PROGRAM
; ============= sub bl_print =====
bl_print PROC NEAR
push ax
push bx
push cx
push dx
;----------part 1
mov ah,2h
mov dl,bl  

mov cl,04h
shr dl,cl

add dl,30h

cmp dl,3ah
jl lable1

add dl,07h

lable1:
int 21h

; -----------part 2

mov dl,bl
and dl,0fh

add dl,30h

cmp dl,3ah
jl lable2

add dl,07h

lable2:
int 21h
;------------
pop dx
pop cx
pop bx
pop ax

ret ; return to where it was called
bl_print ENDP



END START

كار خاصي در اين برنامه نسبت به برنامه قبلي صورت نگرفته فقط خواستم با زيربرنامه bl_print با چاپ dx برنامه ام خروجي داشته باشد.
شرح برنامه:
آمده ام بعد INC DX كد زير را افزوده ام:
mov bl,dh
call bl_print
mov bl,dl
call bl_print

كه واضح است كارش . اول مي آيد قطعه بالايي dx يعني dh را در bl مي گذارد و بعد با خط call bl_print زيربرنامه فراخوانده مي شود كه باعث چاپ dh است و بعد هم همين كار براي dl يعني قسمت پاييني dx .
و بعد كد زير را داريم:
push dx
mov ah,2
mov dl,2dh
int 21h
pop dx

كارش فقط اين است كه يك منها بگذارد بين dx هاي چاپ شده.
كه با سرويس 2 از وقفه 21 انجام شده كه اين سرويس به dl نگاه مي كند. و در dl من كد اسكي منها را قرار داده ام.
ولي نكته مهم اينه كه dx (كه شامل dl هم هست) در حال استفاده است به عنوان شمارنده حلقه بيروني. پس نميشد بي محابا تغييرش داد. ولي من لازمش داشتم تا يك منها رسم كنم. پس با Push و pop يك پل زده ام روي اين سه خط كد:
mov ah,2
mov dl,2dh
int 21h
يعني كه قبل دستكاري dl مقدارش ذخيره و بعد اتمام دستكاري از پشته بازيابي شده.
خروجي برنامه هم اينه:
0001-0002-0003-0004-0005-0006-0007-0008-0009-000a

ولي انگار در تاپيك برعكس شده. اول 3 صفر يك است و در آخر 3 صفر آ.
= == = = == = == = == = =
حالا سئوال من اينه كه آيا نمي شد با همان فقط cx حلقه تو در تو ساخت.
منظورم با همين روش پل زدن.
من فكر مي كنم بشه. مي خواهم همين كار را الان انجام دهم. در پست بعد به اين قضيه خواهيم پرداخت.
 

saalek110

Well-Known Member
بله . امكانش بود.
اين هم برنامه اش:
کد:
.model tiny 
.code
org 100h
START :

MOV cX,6  
; --------------- outer loop -------------- 
ROW :

; --------- inner loop -------
push cx
MOV CX,10 
COL :

LOOP COL   ; DOWN TO CX=0
pop cx
; ----------------------------

mov bl,ch
call bl_print
mov bl,cl
call bl_print

mov ah,2
mov dl,2dh
int 21h

LOOP ROW    ; ELSE JUMP TO ROW 
; -----------------------------------------

INT 20H    ; TERMINATE PROGRAM
; ============= sub bl_print =====
bl_print PROC NEAR
push ax
push bx
push cx
push dx
;----------part 1
mov ah,2h
mov dl,bl  

mov cl,04h
shr dl,cl

add dl,30h

cmp dl,3ah
jl lable1

add dl,07h

lable1:
int 21h

; -----------part 2

mov dl,bl
and dl,0fh

add dl,30h

cmp dl,3ah
jl lable2

add dl,07h

lable2:
int 21h
;------------
pop dx
pop cx
pop bx
pop ax

ret ; return to where it was called
bl_print ENDP



END START
خروجي:
0006-0005-0004-0003-0002-0001-
اول از همه 0006 چاپ شده و در آخر 0001 .

شرح برنامه :
نام برنامه را اولا حلقه تو در تو با فقط cx مي گذارم. در دو طرف حلقه دروني با push و pop كردن cx سدي درست كرده ام بين cx حلقه بيروني و cx حلقه دروني. يعني انگار دو عدد cx داريم. درست ترش اينه كه از cx حلقه هاي بيروني و دروني نوبتي استفاده كرده اند. ميشه تا 10 بار هم حلقه ها را داخل هم گذاشت و مشكلي فكر نمي كنم پيش بيايد.

از دو طرف كد :
mov ah,2
mov dl,2dh
int 21h
مي بينيد كه push dx و pop dx را برداشته ام چون ديگه مثل برنامه قبلي از dx به عنوان شمارنده استفاده نميشه و اگر هم dx تغيير كنه مهم نيست.
كلا اين برنامه نسبت به قبلي كه از dx هم به عنوان شمارنده استفاده ميشد خيلي خلوت تر شده. چون inc dx حذف شده. دستور cmp حذف شده. پرش شرطي نداريم و بجاش همان دستور loop ساده را گذاشته ايم.
دقت كنيد كه در برنامه قبلي من dx را چاپ كردم و اينجا cx را چاپ كردم. در برنامه قبلي dx داشت افزايش مي يافت ولي در اين برنامه cx در حال كاهش است تا به صفر برسه تا دستور loop ديگه عمل نكند. از خروجي دو برنامه هم مشخصه . در اولي خروجي در حال افزايش و در دومي خروجي در حال كاهش است.
در آخر بگويم كه اين برنامه مشقي است كه من الان از خودم نوشتم و شايد روشهاي درست تري نسبت به اين باشد. پس از سايتي نيست و سليقه اي است.
.
 

saalek110

Well-Known Member
عمل link دو فايل:

در اين برنامه مي خواهيم دو فايل را با هم link كنيم. يعني اول فايل obj هر دو را مي سازيم و هر دو obj با هم در يك فايل exe قرار مي گيرند.

براي اين كار زير برنامه كوچكي را در فايلي به نام archive قرار مي دهيم. و فايل اصلي آن زير برنامه را با كدهاي خود صدا مي كند.

به برنامه زير دقت كنيد:
کد:
.model small
.code
public one_sub
one_sub    proc
push ax
push dx
; ---------
mov ah,2
mov dl,53h
int 21h
; ---------
pop dx
pop ax

ret ; return to where it was called
one_sub ENDP

  end

اول شرح 3 خط كدي كه بين خطوط افقي محصور است. قبلا بارها اين استفاده شده ولي باز هم مي گويم كه با استفاده از سرويس 2 از وقفه 21 كاراكتر موجود در dl را چاپ مي كند. كه همان حرف ((s )) را چاپ مي كند.
چون اين 3 خط كد dl و ah را دستكاري مي كنند من آمده ام ax و dx را با ارسال به پشته حفظ كرده ام. مي دانيد كه نميشه ah را push كرد و فقط رجيسترهاي 16 بيتي قابل push هستند. پس به جاي ah بايد ax را push كنيم.

اما برسيم به راهنماها:
اول فايل model و code گفته شده. و نه چيز ديگر مثل stack چون اين برنامه كامل نيست. فقط مي خواهيم با آن يك obj بسازيم تا با obj برنامه اصلي link بشود. و در انتها هم بعد end اسم برنامه اصلي را نداريم. چون اصلا اين كدها برنامه اصلي ندارند و برنامه اصلي در فايل ديگري است.
به جاي :
public one_sub
one_sub proc
در كتاب چاپي من اين بود:
Public one_sub
كه كامپايلر من يعني tasm قبول نمي كرد كه با سرچ كلمه public در گوگل سينتكس جديد را پيدا كردم. اگر كامپايلر شما چيز ديگري است مثل masm بايد سينتكس مناسب براي كامپايلر خود را بيابيد. شايد هم يكي از دو سينتكس بالا باهاش سازگار باشه.
ولي كلا كامپايلرها در راهنماها كمي فرق دارند و در كدهاي اسمبلي آنچنان فرقي نيست. اون مورد @data و data كه بين tasm و a86 فرق داشت هم نوعي راهنما است نه كد اسمبلي.

خوب. حالا تفسير كد بالا.
اولا در نكاتي از يك سايت گفتيم كه proc و endp بايد جفت باشند. و من الان با برداشتن proc خطاي غير مچ بودن اين دو را مي گرفتم. اسم زير برنامه هم همراه اين دو بايد باشه . مي ماند كلمه public : كه ميگه اين زيربرنامه بايد در دسترس همه باشه. من يك بار هم بدون كلمه public ساختم كه موقع link دو فايل خطايي دريافت كردم ولي متنش را نگفت . و فكر كنم چون برنامه اصلي داشت زير برنامه فايل دوم را فرامي خواند و چون اون public نبود خطا مي داد.

برنامه اصلي را حالا با هم مي بينيم:
کد:
.model small
.stack 
.data 
.code 
  EXTRN one_sub : PROC
asli proc

call one_sub

mov ax,4c00h ; return to dos DOS 
int 21h 

asli endp
     end asli

فقط اين خط داخلش جديده:
EXTRN one_sub : PROC
كه واضح هم هست.
معني اش اين است كه در اين جا قراره يك زيربرنامه اي صدا بشه(call one_sub ) كه در يك فايل ديگر قرار دارد.

سينتكس تبديلات:
کد:
tasm asli.asm
tasm archive.asm

و بعد اين كه هر دو فايل obj ساخته شد. مي نويسيم:
کد:
link asli archive
= = = == = == == = =
در برنامه پست قبلي يعني ((حلقه تو در تو فقط با cx)) هم خوبه كه زيربرنامه bl_print را در فايلي به نام bb بگذاريم. اسم آرشيو بهتره ولي چون مدام بايد اسمش تايپ بشه bb گذاشتم يعني بايگاني.
در فايل آرشيو ما ، زير برنامه ها همين طور زير هم نوشته مي شوند. هر يك با همان راهنماهاي public و proc در سرش و endp در ته آنها. يعني با همين سينتكس ولي به تعداد بالا.
و اين فايل ميشه tools شما. كه به همه برنامه هاي قبلي خود راحت دسترسي داريد و مي توانيد فقط با نام بردن اسم زيربرنامه ازش استفاده كنيد.

دو نكته يادتان نرود:
يكي اينكه در هر زير برنامه اگر رجيستري مخدوش ميشه آنرا push و pop كنيد در اول و آخر زيربرنامه.
و نكته ديگه اينكه سعي كنيد يك راهنما به شكل كامنت بالاي هر زيربرنامه بنويسيد. مثل يك تابلو بسازيد آنرا.با سليقه و زيبا.
شامل ورودي زيربرنامه. يعني چه رجيستري به عنوان ورودي دارد. يا انواع ورودي ديگري ديگر مثل متغير و پشته.
خروجي زيربرنامه. باز همان رجيستر ها و غيره.
عمل زيربرنامه. مثلا زيربرنامه بالا يك حرف s چاپ مي كند.
كلا يك شرح خلاصه خيلي خوبه تا خودتان بعدا بفهميد و راحت از زير برنامه استفاده كنيد.
يك نكته هم كه يادم رفته بود اينه كه اين برنامه از چه زير برنامه هايي استفاده مي كند.
با عنوان uses اين را هم اظافه كنيد بر تابلوي بالاي زيربرنامه.
ديگه چيزي به نظرم نمي رسه. خودتان هم با سليقه خودتان چيزهاي مفيدي اضافه كنيد .
 

saalek110

Well-Known Member
من آمدم از زير برنامه موجود در فايل archive را به دو زيربرنامه تبديل كردم. البته نيازي به اين كار نبود. ولي مي خواستم اولا چگونگي نشستن زيربرنامه ها پشت سرهم را نمايش بدهم و دوما تابلوهاي كامنت بالاي زيربرنامه ها را نمايش دهم. و سوما در ديباگ برنامه حاصل از لينك دو فايل را بررسي كنيم.

اين تابلوهايي كه من ساختم نمي گويم خوب و كامل است. خودتان رفع نقص كنيد.

اما يك نكته:
من وقتي الان فايل exe را ساختم اجرا نشد . با وجودي كه كامپايل شده بود. و مي دانيد كه در اسمبلي هم به آن صورت پيغام خطاي واضحي نداريم.
راه حل: براي رفع خطا همان فايل exe كه اجرا نميشد را در ديباگ ويندوز با دستور :
Debug asli.exe
باز كردم. برنامه debug جوري است كه انگار در همه پوشه ها هست ولي براي نوشتن دستور بالا بايد در پوشه اي باشيد كه asli.exe آنجا باشد.
بعد كه در ديباگ كدهاي خود را نگاه كردم ديدم كه ret زيربرنامه دوم موجود نيست و سريع اصلاح شد.
حالا برنامه ساخته شده:
کد:
.model small
.code
; ================================
; description    : this sub with int 21 print dl
; input register : none
; output register: none
; uses           : tanzime_dl
; action         : print 's'
; --------------------------------
public one_sub
one_sub    proc
push ax
push dx
; ---------
mov ah,2
call tanzime_dl
int 21h
; ---------
pop dx
pop ax

ret ; return to where it was called
one_sub ENDP
; ================================
; description    : this sub set dl register
; input register : none
; output register: dl
; uses           : none
; action         : set dl
; --------------------------------
public tanzime_dl
tanzime_dl    proc

mov dl,53h
ret ; return to where it was called
tanzime_dl ENDP

  end

مي بينيد كه در زيربرنامه اولي من dx را push كرده ام نه در دومي. چون دومي داره با dl خروجي به اولي برمي گرداند. وقتي در اولي dx ذخيره و بازيابي ميشه يعني هر تغيير كه دومي هم ايجاد كرده اصلاح ميشه.
براي ساخت فايل exe فايل archive را با tasm مجددا به obj تبديل مي كنيم و ديگه نياز نيست فايل obj حاصل از فايل asli.asm را بسازيم. چون اون كه تغييري نكرده. بعد با دستور
Link asli archive
فايل exe را مي سازيم.
يك برنامه به نام make داريم كه در يك فايلي مي نويسيم چه فايلي به چه فايلي قراره تبديل بشه و كلا همه مسيرها را مي نويسيم تا وقتي تايپ كنيم:
Make filename
همه آن تبديلات از لحاظ تاريخ رفرش فايلها انجام بشه . يعني وقتي برنامه make مي بيند كه archive.asm نو_جديدتره تا archive.obj اين كار را انجام مي دهد. و تمام مسرهاي بعدي را انجام مي دهد تا فايل انتهايي هم جديد بشه.

= = = == = = = =
حالا بحث جالب نگاه در ديباگ به برنامه انتهايي:


a1.jpg

در عكس بالا مي بيند كه انگار نه انگار كه ما در 2 فايل كدهايمان را نوشته ايم. فقط ساختار زيربرنامه ها در كدها منعكس شده نه چيز ديگر. اگر همه را زير هم بنويسيم و كامپايل كنيم هم همين بدست مي آيد. من الان هر دو فايل را يكي كردم. كد آن:
کد:
.model small
.stack
.data
.code

asli proc

call one_sub

mov ax,4c00h ; return to dos DOS
int 21h

asli endp
; ============= sub haa =====
one_sub PROC NEAR
push ax
push dx

mov ah,2
call tanzime_dl
int 21h

pop dx
pop ax
ret ; return to where it was called
one_sub ENDP
; --------------------------
tanzime_dl PROC NEAR

mov dl,53h

ret ; return to where it was called
tanzime_dl ENDP

     end asli

و دقيقا همين كدها را در ديباگ يافتم كه وقتي دو فايل بود برنامه.
نتيجه گيري: تمام اين راهنماها و فايل سازي ها فقط براي محيط كار ماست و به جز كدهاي اسمبلي چيزي به فايل افزوده نمي شود.
البته محل قرارگيري قطعه ها با راهنماها تغيير مي كند . البته نه اين راهنماهاي ما. اين راهنماهاي ما فقط جهت وصل كردن تكه كدهاي اسمبلي به هم است و چيز بيشتري نيست.

يك نكته كوچك تايپي:
در محيط ديباگ نوشتن حرف h بعد اعداد نياز نيست
ولي وقتي داريد با كامپايلر و محيط اديتوري كار مي كنيد ، بعد اعداد مبناي 16 حتما h را بگذاريد. اعداد زير 10 تك رقمي در دو مبناي 10 و 16 يكي هستند و من اكثرا h را نمي گذارم. ولي بالاي 10 اگر h را نگذاريد مبناي 10 در نظر گرفته ميشه و موقع كامپايل به مبناي 16 تبديل مي شود.
 
آخرین ویرایش:

saalek110

Well-Known Member
من شروع كردم به ساخت خزانه زيربرنامه هاي خود:
يعني همان فايلي كه با برنامه اصلي با هم link ميشه و حاوي ابزارهاي لازم من است.
نام فايل را bb.asm گذاشتم تا تايپ اسمش راحت باشه و در آن 3 زيربرنامه فعلا قرار دادم. اين فايل را با هم مي بينيم:
کد:
.model small
.code
; ================================
; description    : this_sub cout cx
; input register : none
; output register: none
; uses           : none
; --------------------------------
public cx_count
cx_count proc
push cx

mov cx,0ffffh
lable3:
loop lable3

pop cx
ret ; return to where it was called
cx_count ENDP

; ================================
; description    : this_sub print bx
; input register : bx
; output register: none
; uses           : bl_print
; --------------------------------
public bx_print
bx_print proc
push bx
push bx
mov bl,bh
call bl_print
pop bx
call bl_print
pop bx
ret ; return to where it was called
bx_print ENDP

; ================================
; description    : this_sub print bl
; input register : bl
; output register: none
; uses           : none
; --------------------------------
public bl_print
bl_print proc
push ax
push bx
push cx
push dx
;----------part 1
mov ah,2h
mov dl,bl

mov cl,04h
shr dl,cl

add dl,30h

cmp dl,3ah
jl lable1

add dl,07h

lable1:
int 21h

; -----------part 2

mov dl,bl
and dl,0fh

add dl,30h

cmp dl,3ah
jl lable2

add dl,07h

lable2:
int 21h
;------------
pop dx
pop cx
pop bx
pop ax

ret ; return to where it was called
bl_print ENDP

  end

زيربرنامه اول فقط كارش اينه كه cx را مي شمارد. گفتم شايد به اين وسيله بشه يك تاخير زماني بعدا ايجاد كرد. اين زيربرنامه cx را push و pop كرده تا مخدوش نشود.

زيربرنامه بعدي bx را مي گيرد و دو بار bl_print را احضار مي كند تا bx چاپ شود. در اول اين برنامه دو بار bx را push كرده ام. يكي براي همان قانون كه هر زيربرنامه بايد در اول و آخر خود رجيسترهاي مورد عمل خود را با push و pop حفظ كند و يكي هم براي اين كه وقتي bh را مي ريزم روي bl ، باعث نابودي bl مي شوم و من بعدا bl را نياز دارم تا چاپش كنم. پس قبل ارسال bl به bl_print آن را pop كرده ام. ممكنه كارهاي من اضافه باشه ولي برنامه درست كار مي كند.

زيربرنامه سوم كه همان bl_print است را هم كه مي شناسيد و نياز به توضيح نيست.

يك نكته: من با اسامي ليبل ها در bb.asm دچار مشكل شدم. يعني نبايد در اين فايل ليبل ها هم نام باشند. ولي در asli.asm من براي امتحان ليبلي هم نام يك ليبل bb.asm انتخاب كردم كه مشكلي پيش نيامد.

شروع عمل:
با دستور
Tasm bb.asm
آن را به bb.obj تبديل مي كنيم.

بعد من 2 فايل اصلي ساختم براي 2 آزمايش.
اولي نامش هست asli.asm با كد زير:
کد:
.model small
.stack
.data
.code
  EXTRN bx_print : PROC
asli proc

mov bx,4567h
call bx_print

mov ax,4c00h ; return to dos DOS
int 21h

asli endp
     end asli
كه فقط bx_print را احضار مي كند.
و دومي اسمش هست asli2.asm با كد زير:
کد:
.model small
.stack
.data
.code
  EXTRN bx_print : PROC
  EXTRN cx_count : PROC
asli proc

mov cx,10
lable1:
call cx_count
mov bx,cx
call bx_print
loop lable1

mov ax,4c00h ; return to dos DOS
int 21h

asli endp
     end asli

اين برنامه هم cx_count را احضارمي كند و هم bx_print را. مي بينيد كه دو بار extrn نوشتم بالاي برنامه.
اين برنامه cx را 10 مي گيره و آن را تا صفر مي برد و اعداد را چاپ مي كند. خروجي:
000a000900080007000600050004000300020001

ساخت:
برنامه هاي asli را هم به obj تبديل و با دستورات:
Link asli bb
Link asli2 bb
به exe تبديل مي كنيم.
شكل زير بازشده هر دو در ديباگ است. نام هر فايل داخل عكس هست.

a1.jpg

زير خط آبي bx_print است و بالاي خط زرد برنامه asli .
بين اين دو خط در برنامه asli2 ، زيربرنامه cx_count را داريم و در asli به اندازه تعداد خط كدهاي cx_count ،، كدهاي بي معني.

نتيجه گيري فعلي فرضي: تمام زيربرنامه هاي فايل بايگاني حجم اشغال مي كنند . حتي اگر استفاده نشوند. البته اين حجم خيلي كم است و فقط براي اطلاع گفته شد. بعدا بايد اين قضيه را علمي تر بررسي كنيم.
 
آخرین ویرایش:

saalek110

Well-Known Member
ليست وقفه ها از يك سايت خوب:
بالاي اين صفحه نوشته:
کد:
 The list of all interrupts that are currently supported by the 8086 assembler emulator. 
These interrupts should be compatible will IBM PC and all generations of x86, original Intel 8086 and AMD compatible microprocessors, however Windows XP may overwrite some of the original interrupts.

لينك:
http://www.emu8086.com/assembly_lan...r_reference/8086_bios_and_dos_interrupts.html

به نقل از يكي از تاپيكهاي تالار اسمبلي فروم برنامه نويس
http://barnamenevis.org/forum/forumdisplay.php?f=47

ليست وقفه هاي اين صفحه:
کد:
INT 10h/00h
INT 10h/01h
INT 10h/02h
INT 10h/03h
INT 10h/05h
INT 10h/06h
INT 10h/07h
INT 10h/08h
INT 10h/09h
INT 10h/0Ah
INT 10h/0Ch
INT 10h/0Dh
INT 10h/0Eh
INT 10h/13h
	INT 10h/1003h
INT 11h
INT 12h
INT 13h/00h
INT 13h/02h
INT 13h/03h
INT 15h/86h
INT 16h/00h
INT 16h/01h
INT 19h
INT 1Ah/00h
INT 20h
	INT 21h
INT 21h/01h
INT 21h/02h
INT 21h/05h
INT 21h/06h
INT 21h/07h
INT 21h/09h
INT 21h/0Ah
INT 21h/0Bh
INT 21h/0Ch
INT 21h/0Eh
INT 21h/19h
INT 21h/25h
INT 21h/2Ah
INT 21h/2Ch
	INT 21h/35h
INT 21h/39h
INT 21h/3Ah
INT 21h/3Bh
INT 21h/3Ch
INT 21h/3Dh
INT 21h/3Eh
INT 21h/3Fh
INT 21h/40h
INT 21h/41h
INT 21h/42h
INT 21h/47h
INT 21h/4Ch
INT 21h/56h
	INT 33h/0000h
INT 33h/0001h
INT 33h/0002h
INT 33h/0003h

=========
=========
آدرس دانلود 6 كتاب اسمبلي حجم بالا:
يك كوچكش را من دانلود كردم. يعني اين را:
Professional Assembly Language
سالم دانلود شد. بقيه را دانلود نكردم.
http://www.ebookism.net/index.php?cid=25
 

saalek110

Well-Known Member
نقل از:
http://barnamenevis.org/forum/showthread.php?t=45301

همه چیز برای یادگیری زبان اسمبلی در یک بسته!
Emu8086 ترکیبی است از یک ادیتور پیشرفته ، disassembler ، assembler ، شبیه ساز نرم افزاری (Virtual PC) با debugger و همچنین آموزش گام به گام.
این برنامه برای کسانی که قصد شروع یادگیری زبان اسمبلی را دارند بسیار مفید است. این برنامه کد اسمبلی را کامپایل و در یک شبیه ساز مرحله به مرحله اجرا می کند. شما می توانید رجیسترها ، فلگ ها ، حافظه ، ALU و پشته را در حالیکه برنامه شما در حال اجرا است ، مشاهده کنید.
این برنامه برای شبیه سازی دارای Virtual Device های متفاوتی مثل LED ، Printer (کار با پورت ها) و … می باشد. Emu8086 قالب های COM ، EXE ، BIN و BOOT را پشتیبانی کرده و کد کامپایل شده توسط این برنامه در ریز پردازنده های اینتل قابل اجرا است:

http://www.kcspot.net/upload/files/rocko/emu8086.zip

سالك:
من قبلا از اين برنامه به عنوان اموليتور استفاده مي كردم. شبيه ساز خيلي خوبي است. پشته و رجيسترها و ........... را شبيه سازي كرده و چيزهاي ديگر. و com ها را كامپايل مي كند و run در محيطي ويندوزي و ....

حجم : 2 و نيم مگا.
من الان دانلود كردم . مشكل نداشت.
 

saalek110

Well-Known Member
ضرب و جمع:
هر رجيستر 16 بيتي توانايي نگه داشتن عددي به بزرگي ffff يعني 65535(مبناي 10 ) را دارد.

دستور جمع add است و دستور ضرب mul .
در شكل زير عمل جمع انجام شده. و ده بر يك(يا بهتر بگيم 16 بر يك ) داشته.
a1.jpg

عدد 9 بعلاوه 7 ميشه 16 ، پس 16 بر يك داشته ايم. و 16 بر يك آخري رفته بيرون و داخل فلاگ carray قرار گرفته.
اگر بخواهيد از ده بر يك استفاده بشه به جاي add از adc حالا استفاده كنيد:

a1.jpg

در شكل بالا 2 بعلاوه 3 شده 6 چون فلاگ نقل پر بوده. و مي بينيم كه بعد adc خالي شده. چون جمع شده با عدد ها.
= == = == == =
اين از جمع :
فكر كنم توضيحات كافي باشه. ديگه نوشتن كدش سليقه اي است كه اگر خواستيد بياوريد با هم هم فكري كنيم.

حالا ضرب:

a1.jpg

ضرب e
(14 در مبناي 10 )
ضرب در 3
ميشه
42
كه ميشه
32 بعلاوه 10
كه ده اش ميشه a
كه در رقم اول ax مشاهده ميشه
و 32 اش ميشه
دو تا 16 تا
كه با a رقم بعدي ax كه جمع بشه ميشه c
(a+2=c )
. پس همه مي شوند c تا آخر.
و يك 2 هم بيرون ميافتد كه رفته داخل dx .
نتيجه گيري:
ارقام بزرگ به dx مي روند و ارقام كوچك در ax .
براي به توان 2 رساندن يك عدد هم من پيشنهاد مي كنم اين عدد را با قرار دادن در ax و bx و با دستور بالا در خودش ضرب كنيد. شايد هم راه بهتري باشد ولي من اين به ذهنم مي رسد.
=========
در مورد تقسيم هم دقيقا مثل ضرب است. كه حاصل تقسيم در ax و باقي مانده در dx قرار مي گيرد.
دستور:
div bx
 
آخرین ویرایش:

saalek110

Well-Known Member
اما راجع به اون اموليتور دو پست قبل : پيشنهاد مي كنم دانلود كنيد. به خاطر نمونه برنامه هاي ساده آن و آموزش دستورات اسمبلي مي گويم. هر چند كه شبيه سازش هم خيلي خوبه و اديتورش هم خوبه.
==============
==============
يك سر هم به اين سايت بزنيد شايد بشه رفرنس شما:
http://www.arl.wustl.edu/~lockwood/class/cs306/books/artofasm/toc.html
 

saalek110

Well-Known Member
اما تفريق:
در برنامه اول شكل زير مي بينيد كه هيچ فلاگي تغيير نكرده.
در شكل زير در برنامه دومي 8 از 7 كم شده. مي توانيم ffff را منفي يك در نظر بگيريم. 4 فلاگ تغيير كرده.
كلا در اسمبلي دو حالت اعداد علامت دار و بي علامت را در نظر ميگيرند. در حالت اعداد علامت دار ، آخرين بيت را علامت منفي در نظر مي گيرند. در حالت اعداد علامت دار ديگه تا 65536 در رجيستر جا نميشه و نيمي از اين مقدار را داريم. همان 32 هزار معروف . كه در زبان سي به عنوان integer مي شناسيم.
ديگه اين كه بخواهيم علامت دار كار كنيم يا بي علامت دو بحث مختلف ميشه.


a1.jpg

اما در برنامه دوست عزيز سيمون، به نظر من تعريف كردن زيربرنامه ((به توان 2 رساندن)) خيلي به خلوت شدن محيط كمك مي كند.
و يك راه براي بهتر كردن فهم برنامه استفاده از متغيرهايي با dw (كلمه) است تا نتايج را كه در ax و dx درمي آيد را در آنها بريزيم. ميشه دو word با نام sum1 و sum2 داشت براي dx و ax . و زيربرنامه توان رساندن خروجي خود را در اينها برگرداند. و ميشه دو متغير از اين نوع هم مثلا براي نتيجه كل در نظر گرفت. و شايد خوب باشه يك متغير هم براي علامت در نظر گرفت يا اينكه اين متغير علامت چيز خوبي نباشه و با روش علامت دار بودن رجيسترها كار راحت تر پيش برود.
كلا راجع به علامت در اين پست خوب كار نشده و بعدا بايد اين بحث خوب مورد تجزيه تحليل قرار گيرد. دوستان هم مطلبي اگر دارند اگر ارائه كنند خيلي خوبه.
 
آخرین ویرایش:

saalek110

Well-Known Member
نقل از Inprise :
http://barnamenevis.org/forum/showthread.php?t=21044

اديتور كروم:
کد:
Integrated Development Environment for Asm/C/Pascal and more.

Main features are:

- Adaptive to various languages.
- Visual skins for compilers/assemblers.
- Integrated PILL script engine (with scriptable wizards and scripts per language). 
- AddIn system providing more than 600 functions.
- Versatile user menus editor.
- Import/Export filters with chaining ability.
- Full integration with MS SDK / DDK / DXSDK.
- Customizable help system per language.
- Extended edition capabilities.
- APIs code completion.
- Projects manager.
- Free.

This tool is still under development and the integrated debugger and dialogs editor aren't finished yet.
It may be or may not be completed depending on the time / will / feedback i'll have / receive.	
.

chrome_capt1.gif




chrome_capt2.gif


سالك:
حجم : دو مگا.
رنگي بودن و احضار اتوماتيك كامپايلر كمك به رفاه برنامه نويس مي كند.

لينك سايت:
http://perso.orange.fr/franck.charlet/Chrome.html
 

saalek110

Well-Known Member
کد:
Hi,
These commands change the ZF for you.

POPF = Pop flags
ADD = Add
ADC = Add with Carry
SUB = Subtract
SBB = Subtract with borrow
INC = Increment
DEC = Decrement
SAL = Shift arithmetic left (SHL)
SAR = Shift arithmetic right
NEG = Negate (two-complement)
AND = Logical and
OR  = Logical or
XOR = Logical exclusive or
SHL = Shift logical left (SAL)
SHR = Shift logical right

Bye for now.
مطلب بالا نقل از دوستي عزيز.
-------------------
سالك:
من خودم يك كتاب چاپ دارم كه زير هر دستور نوشته كه كدام فلاگ را تغيير مي دهد. ولي اين طوري دسته بندي نكرده.
دستورات بالا را هم تست نكردم كه ببينم فلاگ تغيير مي كنه يا نه.

ولي در مورد پرشهاي شرطي كار ساده تره . چون اونها 10 تا بيست تا بيشتر نيستند و معمولا به يكي دو تا يا سه تا فلاگ نگاه مي كنند.

چند تا سورس به نظر من كار كنيم اينها خودش جا مي افتد. مثل يادگيري انگليسي كه با ديكشنري خواندن نميشه ياد گرفت بلكه بايد متن ديد تا جا بيافته. البته اين نظر منه. شايد درست نباشه.
 

saalek110

Well-Known Member
با سلام.
يك سايتي من پيدا كردم كه مبتدي شروع كرده. و يك خوبي هم كه داره اينه كه سينتكس tasm را با masm مقايسه كرده.

در مورد كار با tasm بعدش انگار با tlink بايد لينك كرد كه من در طول اين تاپيك با link كار كردم كه انگار مال masm بوده. ولي هيچ وقت مشكلي نداشتم.

در مورد masm هم بعدش با link كار مي كنيم.

= == = = = = = = ==
در 10 تا بيست فصل بحث كرده كه در دو صفحه زير لينك فصل هايش هست:

http://www.developer.be/index.cfm/fuseaction/tutorialList/GroupID/17/GroupName/Assembler.htm

http://www.developer.be/index.cfm/f.../GroupID/17/GroupName/Assembler/pageNum/2.htm
 

saalek110

Well-Known Member
اين هم خوش آمدش:
= = = = = = = =
Hi! Welcome to low-level world! This time I would like to explain the basic concepts of low level. This part is merely directed toward Intel PC. I'd like to add more for other computers like Mac and Amiga, but I think time wouldn't allow me to do this in near future. :)

هاي . به دنياي low-level خوش آمديد. در اينجا من مي خواهم تصور كلي ئي راجع به پايه دنياي low-level ايجاد كنم. و اين جا براي intel pc بحث مي كنيم. من دوست داشتم تا راجع به مكينتاش و آميگا هم بحث كنم ولي فكر نمي كنم وقت بكنم.
سالك: در تاپيكهاي فارسي خواندم كه برنامه اي كه براي اينتل بهينه شده بر مكينتاش بهينه نيست. بيشتر اطلاعات ندارم و جمله بالا را هم نمي توانم نقد كنم.

Registers

What is registers exactly? You can consider it as variables inside the CPU chip. Yeah! That depicts registers so close. There are several registers exist in PC:

AX, BX, CX, DX, CS, DS, ES, SS, SP, BP, SI, DI, Flags, and IP​

رجيسترها:
رجيستر واقعا چيست؟ شما مي توانيد آن را متغيري داخل cpu در نظر بگيريد. اوه، اين تعريف خيلي محدود مي كنه ما را. (س: ترجمه را ببين!) . رجيسترهاي متعددي در pc وجود دارد: ليست بالا.

They are all 16-bits. You can treat it as if they are word (or unsigned integer) variables. However, each registers has its own use.​

آنها همگي 16 بيتي هستند. شما مي توانيد آنها را word يا اينتيجر بي علامت در نظر بگيريد. ولي هر رجيستر استفاده خودش را دارد.

AX, BX, CX, and DX are general purpose registers. They can be assigned to any value you want. Of course you need to adjust it into your need.​

رجيسترهاي ax bx cx dx رجيسترهايي براي مصارف عمومي اند. آنها مي توانند هر مقداري را كه شما مي خواهيد به خود بگيرند. پس شما مي توانيد آنها را تنظيم كنيد براي نياز خود.

AX is usually called accumulator register, or just accumulator. Most of arithmatical operations are done with AX. Sometimes other general purpose registers can also be involved in arithmatical operation, such as DX.​

رجيستر ax معمولا ((رجيستر جمع كننده)) ناميده مي شود. يا به طور مخفف جمع كننده. بيشتر عمليات رياضي با ax انجام مي شود. بعضي اوقات هم رجيسترهاي همه منظوره ديگر براي اين كار استفاده مي شوند مثل dx .


The register BX is usually called base register. The common use is to do array operations. BX is usually worked with other registers, most notably SP to point to stacks.​

رجيستر bx به نام base register ناميده مي شود. كاربرد عمومي آن براي محاسبات آرايه اي است.
اين رجيستر معمولا با بقيه رجيسترها با هم بكار مي رود ، كار قابل ملاحظه اش با sp براي اشاره به پشته است.

The register CX is commonly called counter register. This register is used for counter purposes. That's why our PC can do looping.​

رجيستر cx اكثرا به نام counter register به كار مي رود. اين رجيستر براي مقاصد شمارش به كار مي رود. و اين علت اين است كه چطور pc ما مي تواند حلقه ايجاد كند.

DX register is the data register. It is usually for reserving data value.​

رجيستر dx كه data register است معمولا براي رزرو مقادير ديتا استفاد مي شود.

= = = =
The registers CS, DS, ES, and SS are called segment registers. You may not fiddle with these registers. You can only use them in the correct ways only.​

رجيسترهاي cs ds es ss به نام رجيسترهاي سگمنت ناميده مي شوند. شما نمي توانيد كارهاي نسنجيده با اينها بكنيد. شما فقط مي توانيد آنها را، فقط، با راه درست بكارگيري آنها ، بكار بريد.

CS is called code segment register. It points to the segment of the running program. We may NOT modify CS directly. Oh yes, what is "segment" anyway? It's discussed later. :)

رجيستر cs به نام code segment register ناميده مي شود. آن به سگمنت برنامه در حال اجرا اشاره مي كند. ما نبايد cs را مستقيما دستكاري كنيم. اوه ، بله ، چه هست سگمنت؟ آن بعدا شرح داده خواهد شد.

DS is called data segment register. It points to the segment of the data used by the running program. You can point this to anywhere you want as long as it contains the desired data.​

رجيستر ds به نام data segment ناميده مي شود. آن اشاره مي كند به سگمنت داده. شما مي توانيد با آن به هر جايي كه اطلاعات داريد اشاره كنيد.


ES is called extra segment register. It is usually used with DI and doing pointers things. The couple DS:SI and ES:DI are commonly used to do string operations.​

رجيستر es به نام extra segment register ناميده مي شود . آن اغلب با di استفاده مي شود و كار نشانه روي انجام مي دهد. زوج ds و si و زوج es و di عموما استفاده مي شوند براي كار با رشته ها.

SS is called stack segment register. It points to stack segment.​

رجيستر ss به نام رجيستر استك (پشته) خوانده مي شود. آن به قطعه پشته اشاره مي كند.
 

saalek110

Well-Known Member
برنامه ای که شکل زیر رو چاپ کنه.
کد:
* * * * *
* - + + *
* + - + *
* + + -*
* * * * *
دو تا حلقه می خواد که 3 تا شرط داشته باشه.

براي مقدمه اي براي شروع كار بايد بگم كه حلقه در اسمبلي دو نوع است:
يكي با loop كه به cx نگاه مي كند و ديگري با پرشهاي شرطي كه به فلاگ ها نگاه مي كنند.

در مورد loop كه ما مي آييم اول به cx مقدار مي دهيم و بعد دستور loop به تعداد عدد موجود در cx پرش مي كند .

در مورد پرشهاي شرطي هم شايد بايد بدانيم كه هر پرش شرطي به كدام فلاگها نگاه مي كند. مي دانيد كه هر فلاگ يك بيت است كه يا صفر است يا يك. هر پرش شرطي با يك شدن يكي يا بيشتر از اين فلاگها پرش مي كند.
ولي من مي گويم راه راحت تر اينه كه به تعريف پرش شرطي نگاه كنيم. مثلا يكي تعريفش اينه ((اگر بزرگتر بود)) يا ((اگر كوچك تر بود)) و اين طوري بخاطر سپاريش راحت تره.
كلا به نظر من وقتي برنامه درست كار مي كنه ، يعني وقتي همان موقعي كه ما مي خواهيم پرش مي كند كار ما درست است و نياز نيست خيلي وارد جزئيات فلاگها بشويم. براي اين كار ميشه از چند سورس به عنوان الگو استفاده كرد تا برنامه امان راه بيافته. بعدا با پرشهاي شرطي بيشتر ور برويم تا بيشتر دستمان بيايد. دوستان اگر راههاي بهتر از من دارند بفرمايند.
================
البته اينها قبلا هم گفته شده در اين تاپيك. خواستم بياد توي اين صفحه تا جلوي دست باشه.
سيمون جان من دقيقا الگوريتمي كه در ذهنم بود منطبق بر الگوريتم شماست.
حلقه هاي تو در توي آن آماده است و در زير مي بينيد . فقط مي ماند شرطهاي داخل حلقه . همان ها كه شما در پست اخير خود گفتيد كه تكميل مي كنم.
اين را هم بگويم كه من قبل اين تاپيك ماهها سي و php كار كردم و مي دانيد كه سبك رايج كدنويسي در اين دو زبان دقيقا از الگوريتم شما تبعيت مي كند. شايد در اسمبلي راههاي بهتري باشد ولي كد من و خودتان را سبك يك سي نويس بدانيد.
== == == == =
نوشته ها و كدهاي زير را من قبل خواندن پست آخر شما آماده كرده بودم كه بدون ويرايش مي زنم:
= = == == = == == = = = = =
من از تمرين بالا اين گونه استنباط مي كنم كه مي خواهد كادري با ستاره بكشد و با بعلاوه پر كند داخل كادر را و قطرش را هم با منها بكشد.
ولي در مورد اين شكل مي شود 5 رشته كاراكتري در ديتا سگمنت تعريف كرد و پشت سر هم آنها را رسم كرد. ولي موضوع بحث ما اين نيست. پس من مي روم سراغ رسم آن شكل بزرگ.

براي اين كار اول من از وقفه اي استفاده مي كنم كه مكان كرزر را تغيير دهد.
اموليتوري كه گفتم حتما دانلود كنيد يك سري آموزش همراهش است كه ليست وقفه ها را هم دارد. من به اين ليست الان مراجعه مي كنم.
به اين وقفه توجه كنيد:
کد:
INT 10h / AH = 02h - set cursor position.

input:
DH = row.
DL = column.
BH = page number (0..7).

حالا من برنامه ساده اي مي نويسم كه از اين وقفه استفاده كنم.
براي اين كار قالبي را كه قبلا استفاده كرده ام و براي ساخت يك فايل exe است را مي آورم. بين دو خطي كه با منها كشيده ام برنامه خود را مي نويسم. دو خط هم براي انتظار گذاشتن برنامه گذاشتم تا زود بسته نشود. اگر ديديد لازم نيست حذف كنيد اين دو خط را. برنامه زير فقط محل كرزر را به نقطه مورد نظر مي برد. يعني به نقطه 5 و 9 .
کد:
.model small
.stack 
.data 
.code 
asli proc
; -----------------------

mov dh,5  ;DH = row. 
mov dl,9  ;DL = column.
mov ah,2  ;service
int 10h   
;--------wait for a key
MOV AH,00H 
INT 16H 
; -----------------------
mov ax,4c00h ; return to dos DOS 
int 21h 

asli endp
     end asli

حالا با يك سرويس در اين نقطه علامت * (ستاره) را رسم مي كنم. كه كد اسكي آن 2a است.
کد:
.model small
.stack 
.data 
.code 
asli proc
; -----------------------

mov dh,5  ;DH = row. 
mov dl,9  ;DL = column.
mov ah,2  ;service
int 10h  
;----
mov dl,2ah
mov ah,2
int 21h 
;--------wait for a key
MOV AH,00H 
INT 16H 
; -----------------------
mov ax,4c00h ; return to dos DOS 
int 21h 

asli endp
     end asli

حالا حلقه ساده اي با loop مي سازم.
کد:
.model small
.stack 
.data 
.code 
asli proc
; -----------------------
mov cx,0ah
mov dh,0  ;DH = row. 
mov dl,0  ;DL = column.

lable1:
inc dl
mov ah,2  ;service
int 10h  
;----
push dx
mov dl,2ah
mov ah,2
int 21h
pop dx 

loop lable1
;--------wait for a key
MOV AH,00H 
INT 16H 
; -----------------------
mov ax,4c00h ; return to dos DOS 
int 21h 

asli endp
     end asli

در برنامه بالا از lable1 حلقه شروع مي شود و در loop انتهاي حلقه قرار دارد. قبل شروع حلقه به سطر و ستون يعني dh و dl مقدار اوليه صفر داده ايم. و cx كه برابر 10 است هم بايد قبل حلقه براي تعداد چرخش تنظيم شود. نگاه كنيد كه cx را 0ah گرفته ام نه ah .همين الان بجاش ah گذاشتم كه tasm خطا گرفت. قانون اينه كه اگر عدد مبناي 16 ئي با حرف شروع بشه بايد قبلش يك صفر گذاشت .
نكته بعدي اينه كه مقدار اوليه را داخل حلقه قرار ندهيد چون واضح است كه داخل حلقه مدام اين مقدار در آنها ريخته مي شود و حلقه ما كاري نمي تواند انجام دهد. من داخل حلقه آمده ام dl را در هر چرخش حلقه يكي افزايش داده ام چون مي خواهم اين رسم بشه:
**********
يعني 10 تا ستاره كنار هم داخل سطر اول.

نكته ديگه برنامه اينه كه چون dl جزو مختصات ستاره است نبايد دستكاري بشه پس آنجا كه push و pop كرده ام dx را براي جلوگيري از اين دستكاري است.
نكته ديگر كه inc dl را در انتهاي حلقه گذاشته ام تا اول چاپ انجام بشه و بعد افزايش صورت بگيرد. وگرنه همون اولي هم بعد يكي جلو رفتن چاپ ميشد.
نتيجه اجرا:
همان 10 ستاره به شكل كنار هم (افقي)

= == = = == = = = =
در ادامه مي خواهم 100 تا ستاره رسم كنم كه هر ده تا در يك سطره.
مي خواهم از همان حلقه تو در توي فقط با cx استفاده كنم كه قبلا شرح داده شده.

کد:
.model small
.stack 
.data 
.code 
asli proc
; -----------------------
mov cx,0ah
mov dh,0  ;DH = row. 
mov dl,0  ;DL = column.
; --------- outer loop ---
lable2:

push cx
MOV CX,10
mov dl,0  ;DL = column.
lable1:

mov ah,2  ;service
int 10h  
;----
push dx
mov dl,2ah
mov ah,2
int 21h
pop dx 

inc dl  ; increase dl...
loop lable1
pop cx
inc dh  ; increase dh...
loop lable2
;--------wait for a key
MOV AH,00H 
INT 16H 
; -----------------------
mov ax,4c00h ; return to dos DOS 
int 21h 

asli endp
     end asli

براي راحتي شما قالب حلقه تو در تو را هم اينجا مي آورم تا راحت مقايسه كنيد:
کد:
MOV cX,6  
; --------------- outer loop -------------- 
ROW :

; --------- inner loop -------
push cx
MOV CX,10 
COL :

LOOP COL   ; DOWN TO CX=0
pop cx
; ----------------------------


LOOP ROW    ; ELSE JUMP TO ROW 
; -----------------------------------------

نتيجه اجراي برنامه:
چاپ 100 ستاره كه هر ده تا در يك رديف است.

در پست هاي بعدي به تكميل برنامه مي پردازيم. يعني با شرطهايي برنامه را وادار مي كنيم كه كادر دور را ستاره و داخل را بعلاوه و قطر را منها بكشد.
 

saalek110

Well-Known Member
در برنامه پست قبلي به جاي :
push dx
mov dl,2ah
mov ah,2
int 21h
pop dx

اين را قرار مي دهيم:
کد:
cmp dh,dl
jne lable3
push dx
mov dl,2dh
mov ah,2
int 21h
pop dx
jmp lable4
lable3:
push dx
mov dl,2ah
mov ah,2
int 21h
pop dx

lable4:

اين باعث مي شود كه قطر برنامه با علامت منها پر شود و بقيه مكانها با همان ستاره پر شود.

شرح برنامه:
با دستور cmp مي آييم dh و dl را مقايسه مي كنيم.
بعد از شرط jne استفاده مي كنيم كه من معني اش مي كنم jump if not equal .
يعني ((پرش كن وقتي برابر نبود)).
برنامه اين طوريه كه وقتي برابر بود پرش نمي كنه و تكه كد بعدي كه چاپ منهاست اجرا ميشه و منها رسم ميشه. بلافاصله بعد رسم منها با پرش به lable4 من مانع شدم كه هم منها رسم بشه و هم ستاره (كدش 2a است) .
معلومه كه اگر equal نباشند اين jne پرش مي كند به lable3 و نمي گذارد كه منها چاپ بشه.نتيجه اجراي اين كد:

a1.jpg

بقيه شرطها هم به همين شكل اضافه ميشه. ميشه زيربرنامه هم استفاده كرد.
 
آخرین ویرایش:

saalek110

Well-Known Member
اين هم برنامه كامل شده:
چيز جديدي ندارد. تكرار شرط هاست.
فقط يك نكته اين كه مواظب باشيد ليبل هاي هم نام نداشته باشيد.
برنامه زير ممكنه قابل بهينه كردن باشد كه من بهينه نكردم.
کد:
.model small
.stack
.data
.code
asli proc
; -----------------------
mov cx,0ah
mov dh,0  ;DH = row.
mov dl,0  ;DL = column.
; --------- outer loop ---
lable2:

push cx
MOV CX,10
mov dl,0  ;DL = column.
lable1:

mov ah,2  ;service
int 10h
;-------- cmp's ------
; --- 1 th cmp
cmp dh,0
jne lable5
push dx
mov dl,2ah
mov ah,2
int 21h
pop dx
jmp lable4
lable5:
;----
; --- 2 th cmp
cmp dh,9
jne lable6
push dx
mov dl,2ah
mov ah,2
int 21h
pop dx
jmp lable4
lable6:
;----
; --- 3 th cmp
cmp dl,0
jne lable7
push dx
mov dl,2ah
mov ah,2
int 21h
pop dx
jmp lable4
lable7:
;----
; --- 4 th cmp
cmp dl,9
jne lable8
push dx
mov dl,2ah
mov ah,2
int 21h
pop dx
jmp lable4
lable8:
;----

; --- ghotr cmp
cmp dh,dl
jne lable3
push dx
mov dl,2dh
mov ah,2
int 21h
pop dx
jmp lable4
lable3:
; --------- end cmp's ---
push dx
mov dl,2bh
mov ah,2
int 21h
pop dx

lable4:

inc dl  ; increase dl...
loop lable1
pop cx
inc dh  ; increase dh...
loop lable2
;--------wait for a key
MOV AH,00H
INT 16H
; -----------------------
mov ax,4c00h ; return to dos DOS
int 21h

asli endp
     end asli


a1.jpg
.
.
 
آخرین ویرایش:
وضعیت
موضوع بسته شده است.

جدیدترین ارسال ها

بالا