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

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

saalek110

Well-Known Member
http://burks.brighton.ac.uk/burks/language/asm/asmtut/asm1.htm

براي شروع اين سايت خيلي خوبه.
مبتدي شروع كرده و با شكل و مثالهاي ساده شروع كرده.
------------------------------------------------------------------
از همين سايت:

خانواده 80x86 با 8086 در سال 1981 شروع شد و جديد ترين عضو آن پنتيوم است كه 13 سال بعد در 1994 عرضه شد.
همه آنها بكوارد كامپتيبل هستند.(يعني قبلي را پشتيباني مي كنند.) و با اضافه شدن فيچورهايي به آنها. و سريع تر از قبلي ها.
امروزه كامپيوترهاي كمي با 8088 و 8086 كار مي كنند. و آنها خيلي از دور خارج و كند هستند.
تعداد 286 ها و 386 ها هم روز به روز كم مي شوند. حتي 486 هم با پنتيوم جايگزين شده.
پنتيوم پرو و mmx based باعث سرعت و توانايي بالا مي باشد.
== = = = = = = =
اعداد در كامپوتر به شكل باينري ذخيره مي شوند. يعني مبناي 2 .
يك بيت ساده ترين حالت اطلاعات است كه دو حالت صفر و يك دارد.
يك نيبل NIBBLE عبارت است از 4 بيت.
يعني 0000 .

پس نيبل نيم بايت است. (بايت 8 بيت است.)
پس نيبل تا عدد 15 (در مبناي 2 ميشه 1111 ) را ظرفيت دارد.
و اين رقم بيس مبناي هگزا دسيمال است. (مبناي 16) .
مبناي 16 خوبي اش در فهم راحت تر آن نسبت به مبناي 2 است. يعني به نوعي آشتي مبناي 2 و مبناي 10 .
اعداد هگزا دسيمال از 1 تا f ادامه مي يابند. (عدد 10 ميشه a و 11 ميشه b و الي آخر)
در انتهاي اعداد هگزا دسيمال يك h مي گذارند. و اگر با حرف شروع بشه قبلش يك 0 (صفر) مي گذارند.
يك بايت 8 بيت است. يا 2 نيبل.
بايت حداكثر 255 مي تواند بشود. خودتان 2 را به توان 8 برسانيد.مي فهميد. از صفر تا 255 ميشه 256 حالت.
بزرگترين عدد يك بايت همان ff است.
يك word يا كلمه دو بايت است. مي تواند تا ffff برود. 65536 ميشه.
= = = == ==
رجيسترها:
Registers are a place in the CPU where a number can be stored and manipulated.
تعريفش را قبلا گفتيم. اين هم متن انگليسي آن.
در 386 ها ما دو نوع رجيستر داريم. 8 بيت و 16 بيت.
و در مدلهاي بالاتر 32 بيتي هم داريم.

رجيسترها 4 نوعند:
رجيسترهاي با مقاصد عمومي.
رجيسترهاي سگمنت.
رجيسترهاي ايندكس.
رجيسترهاي پشته.

رجيسترهاي استك (stack = پشته) و رجيسترهاي سگمنت بعدا گفته ميشه. بقيه را شرح مي دهد.


General Purpose Registers:
شامل: ax bx cx dx
كه 16 بيتي يعني يك word (كلمه) است. و آنها قابل بخش به دو نيمه هستند.(همه رجيسترها قابل نصف كردن نيستند) پس مثلا ax را ميشه به ah و al تقسيم كرد و bx cx dx هم به همين ترتيب قابل نصف كردن هستند. H يعني high و l يعني low .
در كامپيوترهاي بالاي 386 ما داراي رجيسترهاي 32 بيتي با همين نامه هستيم كه يك حرف E اولشان دارند. مثل EAX EBX ECX EDX .
شما مي توانيد AH و AL و AX و EAX را جداگانه براي منظورهايي بكار بريد.چون اين جا حساسه جمله انگليسي اش را بكار مي برم. منظورش را اگر كسي توانست شرح دهد بفرمايد.
You can use AL, AH, AX and EAX separately and treat them as separate registers for some tasks.

رجيسترهاي SI, DI, SP and BP هم مي تواند به عنوان رجيستر همه منظوره بكار رود.
ولي أنها وظايف خاص خود را دارند و به دو نيمه هم اسپليت(شكسته) نمي شوند. (مترجم: اون قبلي ها يعني AX BX CX DX هم وظايف خاص دارند. )

Index Registers:
رجيسترهاي SI DI IP (3 رجيستر) اولي سورس ايندكسه اسمش دومي دستينيشن ايندكسه اسمش و سومي اينستراكشن پوينتر.
در بالاي 386 داريم: EDI and ESI

رجيستر BX هم به عنوان ايندكس استفاده ميشه.
منظور از ايندكس چيه؟اينجا نگفته ولي مثلا اگر بخواهيم چيزي را به جايي منتقل كنيم اين DI و SI نشانه روي مي كنند. از RAM به AX مثلا. بعدا بايد بحث بشه.
رجيستر IP هم خاص است چون شماره خطي از برنامه را كه بايد اجرابشه را در خود نگه مي دارد. يا دستكاري نكنيد يا بدانيد چه كار مي كنيد چون CPU دستور بعدي را از روي IP پيدا مي كند و اگر اشتباه باشه CPU يعني گم ميشه توي RAM و يعني كامپيوتر شما ديگه كامپيوتر بشو نيست و قاطي مي كنه.


Stack registers:
رجيسترهاي BP and SP هستند.
رجيسترهاي پشته است و اينجا گفته بعدا مي گيم.

Segments and offsets:
در 8088 ديده بودند كه فقط به يك مگا بايت حافظه بايد پوشش بدهند. و براي اين پوشش 20 بيت أدرس لازم بوده.خودتان حساب كنيد. ببينيد 2 به توان 20 چند ميشه. حدود يك مگا ميشه.
ولي اون موقع رجيسترها 16 بيتي بودند. (كمبود امكانات)
پس اومدند دو رجيستر را براي آدرس دهي استفاده كردند. يكي را نامش را سگمنت و ديگري را نامش را آفست گذاشتند.
مثل كوچه و پلاك براي ram . ولي چطوري 32 بيت (يعني 2 رجيستر) شد 20 بيت؟
جواب اين كه
OFFSET = SEGMENT * 16
SEGMENT = OFFSET / 16 (the lower 4 bits are lost)
يعني 4 بيت از يك رجيستر را با ضرب انداختند.
اگه كسي توضيح بيشتر خواست بگه.
مثال:
DS stores the Segment and SI stores the offset.
اين دو با هم كار مي كنند.
رجيسترهااي سگمنت اينها هستند:
CS, DS, ES, SS
در بالاي 386 اينها هم هستند:
FS and GS

آفست ها اين ها هستند:
BX, DI, SI, BP, SP, IP
In 386+ protected mode, ANY general register (not a segment register) can be used as an Offset register. (Except IP, which you can't manipulate directly).


The Stack:
وقتي 6 رجيستر براي كارها استفاده مي شوند چطور كارها ممكن است؟ جواب: پشته محلي براي ذخيره و بازيابي اطلاعات است.

پشته مثل تپه است هر چيزي را اول بگذاريد آخر بايد برداريد.
چيزي كه دوم بگذاريد آدرسش كوچك تر است از آنكه اول گذاشته ايد يعني پشته رو به پايين رشد مي كند.

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

saalek110

Well-Known Member
با سلام. سايتي كه پست قبل معرفي كردم در 7 صفحه page آموزش اسمبلي داده كه page 1 آنرا در پست قبلي گفتيم.

می رویم page 2

== = = = = == =
An Introduction to Assembly Instructions
مقدمه اي بر آموزش اسمبلي

در اين قسمت دستوراتي كه زياد استفاده مي شود گفته مي شود.

در قسمتهاي قبلي گفتيم كه يك سري رجيستر داريم كه حافظه هايي داخل cpu هستند. خوب خوبه كه بتوانيم مقدار دهي كنيم آنها را. به دستورات زير دقت كنيد.

کد:
mov ax,10; put 10 into ax
mov bx,20 ; put 20 into bx
mov cx,30 ; put 30 into cx
mov dx,40 ; put 40 into dx

مي بينيد كه با دستورات بالا ما رجيسترهاي ax تا dx را مقدار دهي كرده ايم.
بعد سمي كلون در اسمبلي همه چيز كامنت است يعني متني كه براي شرح براي خود برنامه نويس و ديگر برنامه نويسان است و اجرا نمي شود.
فعلا براي اجرا نرويد. ولي اگر مي خواهيد كه اثر اينها را ببينيد با ديباگ كار كنيد.
استارت را بزنيد تا منوي بالا رونده برود بالا . بعد روي run كليك كنيد تا باز بشه. بعد داخلش بنويسيد debug و ok را بزنيد. اين طوري برنامه ديباگ ويندوز راه ميافته. راه ديگش اينه كه باز از استارت برويد به programs تا برسيد به dos prompt يا در ايكس پي فكر كنم command promp و اونجا بنويسيد debug و اينتر كنيد تا وارد محيط ديباگ بشويد و با حرف q هم از محيط ديباگ باز به dos prompt برمي گرديد. وقتي در محيط ديباگ هستيد يك منها داريم كه يك منهاي چشمك زن داره. ولي در محيط داس پرومپت معلومه كه نام درايو را داريم مثل C: . وقتي از run (روش اول) ديباگ را باز كنيد با زدن q ديگه وارد پرومپت داس نمي شويد بلكه فقط بالاي پنجره مي نويسد finished . يعني اين پنجره ديگه كارش تمام شده.

البته برنامه ها را بهتره داخل فايلهايي با پسوند asm بنويسيد و با اسمبلرها و لينكرها به فايل اجرايي تبديل كنيد ولي ديباگ براي كار آموزشي خيلي خوبه.

حالا كه وارد محيط ديباگ هستيد بنويسيد :
کد:
a 100
و اينتر كنيد. اين طوري به شما اجازه داده ميشه در حافظه 100 شروع به نوشتن دستورات بكنيد. وقتي من همين a 100 را نوشتم به خط زير رسيدم. البته عدد اولي در كامپيوتر شما به احتمال زياد فرق داره كه بسته به ساختار سخت افزار شماست.
کد:
129d:0100

مي توانم با زدن اينتر بدون نوشتن هيچ چيز از حالت دستور تايپ كردن خارج بشوم . ولي من مي خواهم دستور بنويسم. يكي از دستورات بالا را مي نويسم و اينتر مي كنم.
کد:
129d:0100 mov ax,10
ميخوانم: mov بعدش ax بعدش ويرگول بعدش 10 .
بعد اينتر دارم:
کد:
129d:0103
و باز منتظر دستور بعدي است كه مي توانم باز با اينتر در حالت خالي از حالت دستور نويسي خارج شوم.

حالا يك سري نكته:

چرا 100 شد 103 ؟ علتش اينه كه دستور من 3 بايت از ram را اشغال كرده. بعدا ياد مي گيريد كه بعضي دستورات يك بايتي اند بعضي دو بايتي و بعضي 3 بايتي و همين طور مي رويم بالا.

نكته بعدي: چرا عدد اول يعني 129d عوض نميشه؟ جواب اين كه اين شماره قطعه است. ما الان داريم يك فايل com مي سازيم. در فايلهاي com همه قسمتهاي برنامه داخل يك قطعه قرار مي گيرند. و شماره قطعه ما الان 129d است. يعني قطعه اي در ram . بعدا خواندن هارد را هم خواهيد آموخت ولي فعلا فقط به ram فكر كنيد. مي دانيد كه هارد براي ذخيره است و همه اتفاقات در ram مي افتد.

نكته سوم: چرا ما از 100 شروع مي كنيم به نوشتن و از اول قطعه شروع نمي كنيم ؟ چون كه قبل 100 در فايلهاي com 256 بايت(100 در مبناي 16 است. براي تبديل يك رقم مبناي 16 به مبناي 10 اولي را با ضرب دومي در 16 و ضرب سومي در 256 جمع كنيد) براي كارهاي ديگري رزرو شده و وقتي هم كه برنامه را run مي كنيم cpu مي رود از شماره 100 شروع به اجرا مي كند.

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

a1.jpg

مي بيند كه رجيستر ax شده 10 . و bx تا dx همان طور صفرند. نام بقيه رجيسترها كه د رپستهاي قبلي نام برديم هم هست. همچنين نام فلاگها را كه زيرش خط آبي كشيدم را مي بينيد. بعدا راجع به آنها بحث ميشه. همچنين مي بيند كه دستوري كه در خط 103 است شده push si . ما دستوري ننوشتيم براي خط سوم بلكه cpu چون در منطقه دستورات است هر چي مي بينه دستور برداشت مي كنه و بطور تصادفي از اطلاعات اون قسمت اين دستور ايجاد شده. پس ديگه t نزنيد. چون معلوم نيست اين دستورات چه بلايي ايجاد مي كنند. ممكن است مجبور به ريست كامپيوتر شويد. اگر خواستيد دستور خط 100 را دوباره اجرا كنيد بايد ip را به 100 تنظيم كنيد. در پست بعد اين كارها را تمرين مي كنيم. البته شايد لازم نباشد كار با ديباگ را ياد بگيريد و با ساختن سورس كار كنيد.
 
آخرین ویرایش:

saalek110

Well-Known Member
ادامه:
از page 2 اين سايت در پست قبلي فقط 7 خط گفتيم و بقيه اش شد شرح كار در محيط ديباگ. شايد لازم نباشه راجع به ديباگ صحبت كنيم و شما هم اگر دوست نداشتيد نخوانيد. جزو اين سايت هم نبود اين شرح ديباگ.

اما در ديباگ يك دستور r داريم كه وقتي بنويسيم و اينتر كنيم همين ليست رجيسترها مياد. بعد اجراي برنامه تك خطي بالا هر چه اين r را بزنيد مي بيند كه ax همان 10 است كه ما مقدار دهي كرده بوديم. تا كامپيوتر را ريست نكنيد همين وضع هست. يا بايد برنامه ديگري بنويسيد تا ax را تغيير دهيد. ولي چرا وقتي ما هر بار به رجيستر ها سر مي زنيم هميشه صفرند؟ مگه نگفتيم رجيسترها دروازه هاي cpu با دنياي خارجند. مگه كامپيوتر كار ديگه نداره كه هميشه رجيسترها ثابتند؟ من دقيقا جواب را نمي دانم. ولي بجاي t ما يك دستور داريم كه فكر كنم g بود. بعدا نگاه مي كنم و باهاش كار مي كنيم. وقتي بجاي t با g كار مي كنيم همه دستورات ما را اجرا مي كند و بعد همه رجيسترها را به حالت اول برمي گرداند. به خاطر همين گفتم t حالت آموزشي بيشتري دارد. چون g نمي گذارد تغيير رجيسترها را ببينيم. و يك فرق ديگه هم داره g با t و اونم اينه كه يك دستور را اجرا نمي كنه بلكه تا به دستور end نرسه همين جوري هر چند بايت را براي خود دستور فرض مي كنه و اجرا مي كنه. پس اگر خواستيد از آن استفاده كنيد حتما بايد برنامه شما دستور اختتام داشته باشه. كه البته end نيست. وقفه هاي dos است كه مثل تابعي فراخوانده مي شود و برنامه را خاتمه مي دهد. خاتمه برنامه يعني اينكه كنترل را به ديباگ يا ويندوز برمي گرداند.
بخاطر ديدن همين دستور g بود كه من فكر مي كنم اكثر برنامه ها همين طوري اند كه رجيسترها تغييري از خود ظاهرا نشان نمي دهند. مثلا روش كارشون اينه كه قبل هر كاري همه رجيسترها را در پشته ذخيره مي كنند و بعد كارهاي خود را مي كنند و بعد از پشته دوباره همه رجيسترها را مقدار دهي مي كنند.

برگرديم به دستورات: حرف r علاوه بر اينكه مي تواند ليست رجيسترها و فلاگها را نشان دهد براي تنظيم يك رجيستر هم خوبه. براي اين كار اول بنويسيد r بعد نام رجيستر مورد نظر و بعد اينتر كنيد. مثلا r ax و اينتر . بعد اينتر كردن مي نويسه ax و بعد مقدار فعلي اون را مي نويسه و بعد منتظر مقدار جديد ميشه. من مي دهم 1234 . بعد دوباره r خالي مي زنم تا ليست رجيسترها را ببينم. مهم نيست چي بگذاريد داخل رجيسترها . اين كاري كه الان من با r ax كردم هيچ فرقي با mov ax,10 نداشت. ولي اولي دستي بود و دومي كدهاي درون برنامه.
حالا چرا r را الان گفتم. چون كه بايد ip را تنظيم كنيم. چون مي بينيد كه شده 103 و دستور ما در 100 است پس بنويسيد r ip و اينتر و مقدار 100 را بدهيد و اينتر كنيد.
حالا با r نگاه كنيد . من مي بينم كه ax شده 1234 و ip 100 است. هميشه در ليستي كه با r مي گيريم اولين دستور هم قابل مشاهده است. يعني دستوري كه اگر t بزنيم اجرا ميشه. كه الان mov ax,10 است. كه اگه الان من t بزنم باز ax ميشه 10 . كه بهتره بخوانيم 0010 . يعني ah صفره و al است كه 10 است.
= = == = = = == =
برگرديم به آموزش سايت.
دستور بعدي push و pop است.
اولي يك رجيستر را به پشته مي فرستد و دومي از پشته به رجيستر مي فرستند.
و معلومه كه بعد هر دو بايد نام رجيستر بياد. مثلا
کد:
push cx ; put cx on the stack
push ax ; put ax on the stack
pop cx ; put value from stack into cx
pop ax; put value from stack into ax

فرض كنيد برنامه بالا را اجرا كنيم كه شامل 4 خط است. اين برنامه باعث جابجايي cx و ax ميشه. چرا؟
چون كه اول cx را مي گذاريم كف پشته. بعد ax را مي گذاريم روي آن. بعد روئي يه را مي گذاريم در cx و بعد اون كه كف پشته بود را مي گذاريم در ax .

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

کد:
xchg ax,cx

من الان براي چكش با r در ديباگ به cx دادم 3333 و به ax دادم 1111 . بعد در خط 100 همين دستور بالا را نوشتم. درست بود. باعث شد دو رجيسترجايشان عوض شود. پس ديباگ هم يك خوبي هايي دارد. ديديد چه ساده يك دستور را چك كردم. اگر الان مي خواستم فايل asm بسازم و tasm كنم و link كنم خيلي وقت مي گرفت.
= = = = = =
حدود يك چهارم page 2 را گفتيم كه شامل 3 دستور mov و push و Pop بود. اين xchg زياد من نديدم استفاده بشه. براي همين گفتم 3 دستور. در پستهاي بعدي ادامه خواهيم داد بحث را.
 

saalek110

Well-Known Member
اما در مورد پشته متاسفانه ديباگ محتواي پشته رانمايش نمي دهد. البته زياد هم مهم نيست كه ببينيم پشته را. فقط نظم را رعايت كنيم در ارسال و تحويل گيري از پشته كافيه.
من سعي كردم با ارسال رجيستري به پشته و بعد پيگيري با دستور e كه محتواي بايتها را نشان مي دهد در آخر قطعه دنبال پشته باشم ولي نتوانستم.
اموليتورها با شبيه سازي پشته محيط آموزشي خوبي فراهم مي كنند.

= = = == = = = = ==
اما سه نوع مقدار مي توانيم به رجيستر بدهيم. يكي همان عدد است كه با mov داديم به رجيستر در پست قبلي. ديگري قرار دادن مقدار رجيستر ديگر در يك رجيستر و ديگري از ram در رجيستر. به مثالهاي زير دقت كنيد:

کد:
mov ax,10 ; moves an immediate value into ax 
mov bx,cx ; moves value from cx into bx
mov dx,Number ; moves the value of Number into dx

در كد بالا ، اولي را كه قبلا كار كرديم.
دومي مياد cx را در bx مي گذارد كه ساده است و زياد فرقي با اولي ندارد.
در سومي محتواي number را در dx ميگذارد. بعدا به طرز استفاده سومي خواهيم پرداخت.

بحث بعدي طرز صدا كردن وقفه هاست كه مثل تابع كارهايي را براي ما انجام مي دهند. هر وقفه مي تواند بيش از يك دستور اسمبلي باشد كه ما كاري باهاش نداريم و فقط كاري كه اين تابع بلده را ازش مي خواهيم. از كلمه int كه مخفف اينتراپت به معني وقفه است در اين مورد استفاده مي شود.

کد:
int 21h; Calls DOS service
int 10h ; Calls the Video BIOS interrupt

در كد بالا ما در اولي وقفه داس را صدا زده ايم و در دومي وقفه بيوس را ولي طرز صدا زدنشان فرقي ندارد. با كار آنها هم فعلا كار نداريم.

اما يك وقفه خاص مي تواند كارهاي متنوعي انجام دهد. مثلا همين int 21 دهها كار متنوع انجام مي دهد. براي اين كه يك كار خاص ازش بخواهيم بايد قبلش تنظيمش كنيم. مثل حالت كد زير:

کد:
mov ah,9 ; subroutine number 9
int 21h ; call the interrupt

در كد بالا با تنظيم ah داريم به وقفه 21 مي گوييم كه چه كاري ازش انتظار داريم. يعني اين دو دستور با هم باعث مي شوند كه وقفه 21 بياد و كار مثلا شماره 25 از مثلا 100 كار مختلف خود را انجام دهد و برود.

در انتهاي page 2 اين سايت يك برنامه گذاشته به عنوان اولين برنامه كه ما اجرا مي كنيم.
براي اجرا من در اديتورم كدش را مي نويسم و با پسوند asm ذخيره مي كنم و بعد با tasm و link به فايل اجرايي تبديل مي كنم. در اول تاپيك لينك دانلود اين دو هست.
حالا بايد مقداري شرح بدهيم قضايا را تا اين برنامه را درك شود.
اين برنامه از جهاتي براي ما خيلي مهم است.
يكي اين كه دستور end را دارد. كه عبارت است از :

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

اين نوع end براي فايل exe مناسب است. در اين دو خط اين جوري مي گوييم كه داريم از سرويس 4c از وقفه 21 استفاده مي كنيم براي اختتام برنامه.
در خط اول مي شد بنويسيم mov ah,4c و فرقي با اين نداره چون كه ax از ah و al تشكيل شده. و مي بينيد كه در كد بالا هم دو تا صفر در محل al هست.
زياد چيز پيچيده اي نيست اين دو خط كه بشه بيشتر توضيحش داد. فقط لازمه كه برنامه ما اين end را حتما داشته باشه. و بعد اين دو خط ديگه اجرا ادامه پيدا نمي كنه ، پس در جاي مناسب end را قرار بدهيد.

نكته بعدي برنامه ما اينه كه داره در قطعه اي داده ذخيره مي كنه. و داده ما hello world يعني يك رشته است. كل برنامه را حالا مي گذارم تا از خط اول روي آن بحث كنيم.
کد:
.model small
.stack 
.data 

Message db "Hello World!$" ; message to be display

.code 
start proc
mov dx,OFFSET Message ; offset of Message is in DX 
mov ax,SEG Message ; segment of Message is in AX
mov ds,ax ; DS:DX points to string 

mov ah,9 ; function 9 - display string 
int 21h ; call dos service 
mov ax,4c00h ; return to dos DOS 
int 21h 

start endp
     end start

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

خط دوم پشته را تعريف كرده. چون عددي جلوي آن نيامده مقدار ديفالت براش قرار داده ميشه كه فكر كنم 1024 بايت است. بعدا نگاه مي كنم ميگم. فعلا مهم نيست. و كافي است اين مقدار. چون شما قراره كه هر چي در پشته مي گذاريد بعدا برداريد و پشته هر چه برنامه كار مي كنه نبايد با بي نظمي پر بشه.

بعد آمده نوشته data كه متغيري به نام Message تعريف كرده و بعدش db گذاشته و بعد مقدار آن را داده. و يك علامت $ هم در انتهايش گذاشته.
اين علامت دلار در اسمبلي نشانه اختتام رشته است .
ما در كدهايمان از وقفه اي استفاده مي كنيم كه به اين علامت دلار حساس است. بعدش هم هر چي بعد سمي كلون ; گذاشته كامنت است كه فقط شرح است و مي دانيد كه اجرا نميشه.

آن جا كه code نوشته تازه محلي است كه ما مي توانيم دستورات اسمبلي را بنويسيم.
آن دستور start proc را من اضافه كردم. در سايت نبود. همچنين start endp را. كلمه start را سايت براي اين انتخاب كرده چون اولين برنامه اي است كه داره عرضه ميشه و چيز خاصي نيست.
حالا چرا من اين دو خط را اضافه كردم؟ چون كه هر كامپايلري به راهنماهاي خاصي نياز داره و چون من با tasm كار مي كردم و ديدم اينها را مي خواهد اضافه كردم. شما شايد جور ديگه كار مي كنيد. اينها مهم نيست. چون همه راهنماها توسط كامپايلر برداشته ميشه و فقط كدهاي اسمبلي داخل فايل اجرايي اطراق مي كند.
شايد بپرسيد آن hello world!$ پس چي؟ يعني اگر فقط كدها اطراق مي كنند داخل فايل اجرايي ما اين رشته كجا مي رود.
جواب اين سئوال بحث جالبي دارد و آن بحث قطعه ها در فايل exe است. گفتيم كه در فايل com همه اجزا در يك قطعه جا مي گيرند ولي در فايل exe در قطعات مختلف مي توانند باشند. پس اين hello در قطعه خاصي قرار مي گيرد.
و قسمتي از كد ما هم حول همين محور ميچرخد.
بگذاريد واضح تر بگويم. اين دو خط كد برنامه را نگاه كنيد:

کد:
mov ah,9 ; function 9 - display string 
int 21h ; call dos service

در اينجا باز وقفه اي احضار شده و در خط اولش هم شماره سرويس ذكر شده.
مواظب باشيد اين دو خط را با دو خط بعدي كه براي end برنامه كار مي كرد را اشتباه نكنيد.
حالا مي خواهيم راجع به سرويس 9 از وقفه 21 صحبت كنيم.
اين سرويس به ram در نقطه DS : DX نگاه مي كنه. Ds و dx شماره قطعه و آفست(آدرس بايت داخل قطعه) است. مثل همان كوچه و پلاك.
و چون اين سرويس به ds و dx نگاه مي كند بايد قبل آتش كردن اين سرويس بياييم dx و ds را تنظيم كنيم.

حالا روش تنظيم كردن dx و ds :
اولا بگم كه نميشده مستقيما به ds مقدار داد. چون يك جورهايي قابل انجام نيست اين كار و برمي گرده به طراحي اوليه. پس يك خط به كد ما اضافه شده سر همين.
يعني اول ريخته اند در ax و بعد از ax ريخته اند داخل ds . اين از اين.

فقط مي مونه اين كه آمده اند در اين ds:dx چي بريزند؟ جواب مختصر اين است مختصات Message را.
بعدا همه اينها را باز تمرين مي كنيم و شرح مي دهيم.
- - --
خط آخر برنامه يعني end start فقط كارش اينه كه بگه برنامه اصلي كدوم بود. ما فقط يك برنامه داريم و تابع نساختيم. تابع و برنامه اصلي دقيقا شبيه هم ساخته مي شوند. با همين proc و endp . پس end start داره ميگه كه start برنامه اصليه بوده.

براي اولين مثال ، مثال خوبي را انتخاب نكرده بود. سنگين بود. ولي اجزاي همين برنامه را در مثالهاي كوچك تر بعدا تمرين كنيم بهتر است.

= = = = = = = =
خوب. صفحه دوم هم تقريبا تمام شد. چند خط كوچك مونده كه در پست بعدي مي گويم.
 

saalek110

Well-Known Member
نكاتي از page 2 مونده كه در اين پست مي گوييم:

يكي سينتكس كاملتر push :

کد:
pop register (or variable)

يعني به جز رجيستر ميشه چيز ديگه هم به پشته فرستاد. بعدا تمرين مي كنيم.

= = = =
يك نكته هم اينه كه حرف h را يادتان نره بعد اعداد مبناي 16 بگذاريد. در محيط ديباگ مهم نيست ولي وقتي سورس مي سازيد مهم است. مثلا int 21h كه عدد 21 در مبناي 16 است. چند ميشه در مبناي 10 ؟ يك بعلاوه 2 ضربدر 16 .

= == = = ==
راجع به تعيين سرويس گفته usually put in AH . يعني معمولا سرويس با تنظيم ah تعيين ميشه. پس رجيسترهاي ديگري هم مي توانند تعيين كننده سرويس وقفه ها باشند.
= = == == = =
داريم:
DS : DX to be a far pointer
اشاره دو نوع است near و far . بعدا بحث ميشه. فقط بدانيد كه near درون يك قطعه است و far ميان قطعه ها.
= == = = = = ==
در عبارت:

کد:
Message DB "Hello World!$"

حروف db مخفف declare byte است . يعني Message آرايه اي از ((بايت ها)) است.

همچنين به عبارات زير دقت كنيد:

کد:
Number1 db ?
Number2 dw ?

علامت ؟ يعني كه متغير تعريف شده ولي مقدار اوليه داده نشده(اينيتيالايز نشده)
مي شد به راحتي به جاي علامت سئوال اعدادي مثل 0 و 1 را گذاشت.
همچنين به db و dw دقت كنيد. كلا 3 حالت داريم. Db و dw و dd كه به ترتيب بايت ، كلمه(دو بايت) و ((دو كلمه))(dd ) است.
اين dd فعلا براي ما استفاده نداره . براي رجيسترهاي 32 بيتي مثل eax كاربرد داره.

به كد زير دقت كنيد:

کد:
Number1 db 0
Number2 dw 1
…………
………
…………
mov al,Number1 ; ok
mov ax,Number1 ; error

mov bx,Number2 ; ok
mov bl,Number2 ; error
مي بينيد كه نميشه در يك رجيستر يك بايتي بياييم متغير دو بايتي بگذاريم
يا برعكس در يك رجيستر دو بايتي نميشه بياييم متغير يك بايتي بگذاريم.

== == = = = = =
نكاتي راجع به كامپايلرها:
کد:
Turbo Assembler

tasm file.asm
tlink file [/t]

در مورد توربو اسمبلر گفته كه با گذاشتن حرف t ميشه فايل com ساخت به شرطي كه مدل فايل در كدها tiny باشه.
من اين را تست نكردم. فقط براي اين كه چيزي جا نيفتد گفتم.

در مورد a86 :
کد:
a86 file.asm

گفته كه ميشه فايل com ساخت با a86 و برايش مدل فرقي نداره. متن اصلي:
This will compile your program to a .COM file. It doesn't matter what the memory model is.

= = = = == = = = = =
خوب. Page 2 هم تمام شد.
من حداقل 10 برابر از خودم توضيح اضافه كردم . اميدوارم در page هاي بعدي نياز به توضيح ديگه نباشه و سريع تمام بشه.
 

saalek110

Well-Known Member
خوشبختانه در page 3 كدنويسي را شروع كرده و بحث شيريني است. پس سعي مي كنم همين امشب اين صفحه را نقل كنم.
اما قبلش مي خواهم از يك تاپيك آموزش اسمبلي در سايت ديگري مطالبي را نقل كنم.
لينك تاپيك:
http://www.myimei.com/forum/showthread.php?tid=112

در شروع وظيفه خود مي دانم كه از استادان imei و كرگدن تشكر كنم كه در چندين زبان استاد و راهنماي من بوده اند.
 

saalek110

Well-Known Member
مطلب اول دهمين پست اين تاپيك است نوشته كرگدن راجع به قطعه ها.
من در اين تاپيك كمي تنبلي كردم و يا سوادم كم بوده با نقل اين مطلب جبران ميشه.
== = = = = == = =
سگمنت:

نا حیه ای از حافظه است که آدرس شروع آن بر 16 قابل قسمت است و اندازه آن می تواند تا 64k باشد.

هر برنامه اسمبلی می تواند تا چهار نوع سگمنت داشته باشد.

سگمنت کد (code segment):
دستورهای زبان ماشین که باید اجرا شوند در این سگمنت قرار خواهند گرفت
در صورتی که کد برنامه از 64K بیشتر شد می توان از چند سگمنت کد استفاده کرد

سگمنت داده (data segment):
متغیر ها و داده ها ی برنامه در این قسمت قرار می گیرند
در صورت نیاز میی توان از چند سگمنت داده در برنامه استفاده کرد

سگمنت پشته (stack segment):
حاوی آدرس بازگشت از زیربرنامه و به طور کلی حاوی تمام اطلاعاتی که برای فراخوانی زیربرنامه ها لازم است

سگمنت اضافی:
این سگمنت برای انجام عملیات بر روی رشته ها مورد استفاده قرار می گیرد


مرجع:کتاب برنامه نویسی به زبان اسمبلی از 8086 تا پنتیوم جعفر نژاد قمی- رمضان عباسپور
 

saalek110

Well-Known Member
باز نقل از اين تاپيك پست 12:
هر زبانی کاربرد خودشو داره
هیچ زبانی برای از بین رفتن زبان قبلی به وجود نیومده

خیلی کارها با اسمبلی راحت تر از زبانهای دیگر هست
سرعت اسمبلی را هیچ زبانی ندارد
برای ماشین ها با کارایی محدود بهترین گزینه اسمبلی است
وقتی برای ماشینی بخواهیم کامپایلری ایجاد کنیم باز هم بهترین انتخاب اسمبلی است.
= = = = = = = = =
پست 14:
با دونستن اینکه هر کدی که در هر زبان مینویسید به صورت اسمبلی به چه شکلی است میتونید برنامه خودتون رو بهینه کنید

مثلاً

a=a+1;
a+=1;
a++;

فرق بین اینا چیه؟
کدوم سریعتر هستند؟
اینو فک کنم ایمی قبلاً تو تاپیک سی گفته بود
(از پایین به بالا کندتر میشن)
یا مثلاً فرق ضرب در 2 یا شیفت به چپ چیه؟
کدوم سریعتره؟
(شیفت به چپ)
و......
 

saalek110

Well-Known Member
از همين تاپيك ، پست 16 :
راجع به رجيسترها و فلاگها:

== = == = = == = = ==
ثبات ها

ثبات ها حافظه های 8 16 32 و 64 بیتی در داخل پردازنده هستند (به تاپیک cpu &chip مراجعه کنید) که سرعت بسار بالایی دارند و می توان از آنها در ساختارهایی که به شمارنده و محاسبات زیادی نیاز دارند استفاده کرد
در این بخش با ثباتهای 16 بیتی کار داریم

ثباتهای عمومی:
AX-BX-CX-DX
که هر کدام از دو قسمت 8 بیتی کم ارزش و پر ارزش تشکیل شدند
مثلاً AL بخش کم ارزش و AH بخش پر ارزش AX می باشد
و برای DX نیز DL, DH
خانه ها را از 0 شماره گذاری می کنیم (بنابر این هر ثبات 16 بیتی از 0 تا 15 شماره گذاری میشه)
از ثبات CX در شمارنده ها استفاده میشه ( متغییر کنترلی اتمام حلقه ها هستند)
نتیجه ضرب و تقسیم اعداد بزرگ در DX قرار می گیره
به ثبات BX هم ثبات پایه می گن وبه عنوان اندیس ازش استفاده می کنن
ثبات AX هم در محاسبات زیاد مورد استفاده قرار می گیره

ثباتهای سگمنت:
ES-SS-DS-CS
در این ثباتها آدرس شروع سگمنت هایی که قبلاً گفتم ذخیره می شه

(SS (stack segment برای سگمنت پشته
DS (data segment برای سگمنت داده
(CS (code segment برای سگمنت کد
(ES (extra segment برای سگمنت اضافی

ثباتهای اندیس:
BP-SP-SI-DI

اول یک توضیح در مورد آفست بدم
در قسمت قبلی در مورد آدرس سگمنت ها حرف زدیم
هر سگمنت یک آدرس شروعی داره که در ثبات سگمنت ذخیره میشه
به فاصله ای که از این آدرس داریم آفست میگیم

ثبات (BP (base pointer شامل آفستی از ثبات SS هست
ثبات (SP (stack pointer حاوی آفست بالای پشته است
بالای پشته= آخرین ورودی
پشته در این زبان از بالا بسته است و از پایین پر میشود (از نظر آدرسی)

ثبات (SI(source index برای عملیات بر روی رشته ها هست و آدرس رشته منبع رو نگهداری میکنه
ثبات (DI(destination index برای عملیات بر روی رشته ها هست و آدرس رشته مقصد رو نگهداری میکنه

ثباتهای وضعیت و کنترلی:
IP-FLAG

ثبات IP آفست دستور بعدی اجرایی برنامه را دارد

ثبات فلگها
CF-PF-AF-ZF-SF-TF-IF-DF-OF
یک ثبات 16 بیتی که 9 بیت آن به طور مشخص نامگذاری شده و وضعیت پردازنده رو نشون میده



بیت (c (carry برای نشان دادن وجود رقم نقلی در محاسبات که آنرا CF می نامند
بیت (D (direction به معنی جهت می باشد و جهت پردازش روی رشته ها رو مشخص میکنه (از چپ به راست یا برعکس) و با DF نشون داده میشه
بیت (P (parity) اگر صفر باشه نشاندهنده اینه که تعداد شیفت ها زوج بوده وگرنه فرد بوده و با PF نشون داده میشه
بیت (A (auxiliary carry رقم نقلی کمکی هست و در محاسبات 8 بیتی اگه رقم نقلی استفاده میشه و با AF نمایش داده میشه
بیت (Z(zero اگر در نتیجه محاسبات صفر ایجاد بشه این بیت برابر یک میشه و با ZF نشون داده میشه
بیت (S (sign اگر نتیجه محاسبات منفی باشه این بیت 1 میشه و با SF نشون داده میشه
بیت (T(trap اگر این بیت برابر یک باشه اجرای برنامه به صورت دستور به دستور در میاد و با TF نشون داده میشه
بیت (I(interrupt اگر این بیت 1 باشه سیستم به وقفه ها پاسخ میده وگرنه اونارو نادیده می گیره که با IF نشون داده میشه
بیت (O(overflow اگه در محاسبات بیت پر ارزش سرریز بشه بیت O صفر میشه , با OF نشون داده میشه
= = = = = = = = == = = = = =
پست 21:

Saalek :
يك سئوال هم داشتم . فرق sp با bp چيه. اگر قبلا گفته بوديد و من دقت نكردم شرمنده.
. کرگدن :
bp میتونه ثابت باشه
ولی sp با اضافه و کم شدن عضو ها تغییر میکنه و آخرین عضو رو نشون میده
(در صورت خالی بودن پشته قبل از ss رو نشون میده)
مثلاً bp میتونه فقط روی خونه 4 ام پشته باشه و تغییری نکنه ولی sp حتماً خونه آخر رو نشون میده
 

saalek110

Well-Known Member
پست 26 از همين تاپيك:
= = = = == = = == =
داده ها در اسمبلی:

همونطور که گفتم هر برنامه از سگمنت های مختلفی تشکیل شده که یکی از اونا سگمنت داده هست
تعریف داده هایی که در برنامه استفاده میشه ، در این قسمت صورت میگیره.

نکته :اعداد نوشته شده در اسمبلی به طور پیش فرض در مبنای 10 هستند مگر اینکه اونارو با پسوند های h (برای مبنای 16) ، b (برای مبنای 2)، q و o (برای مبنای 8) بنویسیم

نکته: اگر عدد ما در مبنای 16 با حرف شروع بشه قبل از اون یه 0 (صفر)باید بذاریم
به این شکل FFF5h --> 0FFF5h

برای داده های 1 بایتی از DB ، 2 بایتی DW ،4 بایتی از DD استفاده می کنیم.
برای داده های بزرگتر هم دستورهای دیگه ای داریم که فعلاً با اونا کاری نداریم
نحوه تعریف داده:

کد:
<مقدار> <DD یا Dwیا DB>  <نام>

مقادیر ما در حافظه در مبنای 16 یا هگز ذخیره میشوند
مثال:
کد:
byte1 db 255

در حافظه به صورت FF ذخیره می شود

توجه:
متغییر هایی که بیشتر از یک بایت هستند در حافظه به صورت معکوس ذخیره می شوند

کد:
word1 dw  1234h

در حافظه به صورت12 34 ذخیره می شود (آفست 34 کمتر از 12 هست)

با این دستور ها میشه چند متغییر رو هم زمان تعریف کرد (که بعداً در مورد دسترسی بهشون صحبت می کنیم)

کد:
p1 db 12,13,15

اگر متغییری داشته باشیم که می خوایم در طول برنامه مقدار دهی بشه از علامت ؟ در قسمت مقدار استفاده می کنیم

کد:
byte1 db ?

به متغییر ها رشته هم میشه داد که بین دوتا " یا ' قرار می گیرند
انتخاب " یا ' به رشته مون بر میگرده
اگه " در رشتمون باشه از ' استفاده میکنیم و بر عکس
کد:
str1 db "a"

str2 dw "bb"
 

saalek110

Well-Known Member
پست 49 از همين تاپيك:
= == == = = = = = =
عملگر dup:

با استفاده از این عملگر در DB,DW,DB شما می تونید چندین خونه از اونا رو مورد استفاده قرار بدین
مثال:
کد:
test dw 100 dup(0)

که 100 خونه dw با مقدار 0 در نظر میگیره
مثال:

کد:
test db 10 dup(' ~'), ' ~'
این دستور 22 بایت از حافظه رو می گیره که شامل ~ , فاصله هستند


دستور mov :

باین دستور دو عملوندی هست که مقدار عملوند دوم رو به عملوند اول منتقل می کنه
چندتا شرط داره
1-طول و اندازه دو عملوند باید یکی باشه (هردو بایت باشن یا کلمه یا ...)
2- دو عملوند نمی تونن حافظه باشن (که توسط db , dw و... تعریفشون کردیم)
3- هردو عملوند نمی تونن ثبات سگمنت باشند
4-مقدار دهی ثابت به ثبات ثابت هم مجاز نیست
مثلاً

کد:
count dw 20h

mov al,count
]چون اندازه ها رعایت نشده

کد:
mov c1,c2

]c1 , c2 هردو حافظه هستند

کد:
mov ds,4

انتقال عدد ثابت به ثبات سگمنت

کد:
mov ds, datasg

datasg آدرس سگمنت هست (خودمون انتخاب کردیم و بعداً بیشتر در موردش حرف می زنیم)

همگی غیر مجاز هستند.
 

saalek110

Well-Known Member
در 5 پست بالا از تاپيك مذكور مطالبي نقل شد به علت دقيق بودن نوشته ها.
ولي ما روال كار را روي همان سايت مي گذاريم. يعني در پستهاي بعدي صفحه 3 سايت را شروع به تمرين كدهايش مي كنيم.
.
 

saalek110

Well-Known Member
با سلام.
صفحه سوم سايت را مي خواهيم ادامه دهيم.
اول صفحه يك نكته راجع به همان راهنماها گفته كه بررسي مي كنيم.
مي دانيد كه من با tasm فقط كار مي كنم. شايد بعدا ديگر اسمبلرها را هم دانلود كردم و كار با آنها را شرح دادم ولي فعلا فقط tasm .
اما نكته اين صفحه:
بحث سر اين 3 خط است كه در برنامه قبلي داشت:

کد:
mov dx,OFFSET MyMessage 
mov ax,SEG MyMessage 
mov ds,ax

مي خواهد بگويد جور ديگر هم ميشه نوشت.
ميگه كه اول برنامه اين دو خط را اضافه كنيد:

کد:
mov ax,@data 
mov ds,ax

و بعد اين خط را ، :
کد:
mov dx,OFFSET MyMessage

خوب باز هم كه شد 3 خط كد!
من با tasm الان اين حالت را چك مي كنم ببينم درست كار مي كنه يا نه.
آره درست بود. كل برنامه را در زير مي آورم:

کد:
.model small
.stack 
.data 

Message db "Hello World!$" ; message to be display

.code 
start proc
mov ax,@data 
mov ds,ax
mov dx,OFFSET Message

mov ah,9 ; function 9 - display string 
int 21h ; call dos service 
mov ax,4c00h ; return to dos DOS 
int 21h 

start endp
     end start

فقط به جاي mymessage گذاشتم message تا به نام متغير من بخوره.
= = = == =
براي اسمبلر a86 گفته اين جوري بنويسيد:
کد:
mov ax,data

يعني اون علامت @ را قبل data نمي خواهد.
من اين a86 را الان ندارم و نمي توانم چك كنم ببينم درست ميگه يا نه.
كلا اسمبلرهاي مختلف اين راهنماهايشان كمي متفاوت است ولي در كدهاي اسمبلي فكر نكنم فرقي داشته باشند.

كلا راجع به اين راهنماها و طرز چيده شدن قطعه ها و ((طرز دستور دادن توسط راهنماها ، براي چيدن قطعات)) من زياد مسلط نيستم و نمي توانم الان توضيح زيادي بدهم. پس اجازه دهيد برويم جلو و با منابع مختلف اينها را بعدا تمرين كنيم.

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

saalek110

Well-Known Member
ادامه صفحه سوم:
در اين صفحه هم مثل صفحه دوم يك برنامه بزرگ در آخر صفحه گذاشته و در طول صفحه كدهاي آن را شرح داده.

من الان برنامه انتهايي را اجرا كردم. و اجرا شد.
فقط يك نكته:
برنامه اي كه اون گذاشته اين است:
کد:
; a program to demonstrate program flow and input/output

.model tiny
.code 
org 100h
start:

mov dx,OFFSET Message ; display a message on the screen
mov ah,9 ; using function 09h
int 21h ; of interrupt 21h

mov dx,OFFSET Prompt ; display a message on the screen 
mov ah,9 ; using function 09h
int 21h ; of interrupt 21h

jmp First_Time
Prompt_Again:

mov dx,OFFSET Another ; display a message on the screen 
mov ah,9 ; using function 09h
int 21h ; of interrupt 21h 

First_Time:

mov dx,OFFSET Again ; display a message on the screen 
mov ah,9 ; using function 09h
int 21h ; of interrupt 21h
xor ah,ah ; function 00h of

int 16h ; interrupt 16h gets a character 
mov bl,al ; save to bl 
mov dl,al ; move al to dl
mov ah,02h ; function 02h - display character
int 21h ; call DOS service 

cmp bl,'Y' ; is al=Y?
je Prompt_Again ; if yes then display it again
cmp bl,'y' ; is al=y?
je Prompt_Again ; if yes then display it again

theEnd:

mov dx,OFFSET GoodBye ; print goodbye message
mov ah,9 ; using function 9
int 21h ; of interrupt 21h

mov ah,4Ch ; terminate program 
int 21h
 
.DATA 
CR equ 13 ; enter 
LF equ 10 ; line-feed

Message DB "A Simple Input/Output Program$" 
Prompt  DB CR,LF,"Here is your first prompt.$" 
Again   DB CR,LF,"Do you want to be prompted again? $"
Another DB CR,LF,"Here is another prompt!$" 
GoodBye DB CR,LF,"Goodbye then.$"

end start

در اين برنامه براي ساخت يك برنامه com تدارك ديده. و فكر كنم با tlink فايل com ساخته كه موقع لينك يك حرف t مي گذارد كه فايل com ساخته بشه ولي من tlink را نداشتم و link را داشتم كه پارامتر t را نمي شناخت.
پس اومدم برنامه را تغيير دادم تا مثل برنامه صفحه 2 يك فايل exe بسازه.
برنامه خودم را مي گذارم. اين برنامه با tasm و link بدون اشكال به exe تبديل شد و درست كار مي كرد:

کد:
; a program to demonstrate program flow and input/output

.model small
.stack 
.code 

start:
mov ax,@data 
mov ds,ax

mov dx,OFFSET Message ; display a message on the screen
mov ah,9 ; using function 09h
int 21h ; of interrupt 21h

mov dx,OFFSET Prompt ; display a message on the screen 
mov ah,9 ; using function 09h
int 21h ; of interrupt 21h

jmp First_Time
Prompt_Again:

mov dx,OFFSET Another ; display a message on the screen 
mov ah,9 ; using function 09h
int 21h ; of interrupt 21h 

First_Time:

mov dx,OFFSET Again ; display a message on the screen 
mov ah,9 ; using function 09h
int 21h ; of interrupt 21h
xor ah,ah ; function 00h of

int 16h ; interrupt 16h gets a character 
mov bl,al ; save to bl 
mov dl,al ; move al to dl
mov ah,02h ; function 02h - display character
int 21h ; call DOS service 

cmp bl,'Y' ; is al=Y?
je Prompt_Again ; if yes then display it again
cmp bl,'y' ; is al=y?
je Prompt_Again ; if yes then display it again

theEnd:

mov dx,OFFSET GoodBye ; print goodbye message
mov ah,9 ; using function 9
int 21h ; of interrupt 21h

mov ah,4Ch ; terminate program 
int 21h
 
.DATA 
CR equ 13 ; enter 
LF equ 10 ; line-feed

Message DB "A Simple Input/Output Program$" 
Prompt  DB CR,LF,"Here is your first prompt.$" 
Again   DB CR,LF,"Do you want to be prompted again? $"
Another DB CR,LF,"Here is another prompt!$" 
GoodBye DB CR,LF,"Goodbye then.$"

end start

حالا فرقهايشان را اول مي گويم و بعد كدها را خط به خط شرح مي دهيم.
در برنامه هاي com نيازي به تعريف پشته نيست و خودش پشته را در انتهاي قطعه خود به خود قرار مي دهد. ولي در برنامه هاي exe در كدمان بايد با كلمه stack پشته را ذكر كنيم و گفتيم كه اگر جلوي stack چيزي ننويسيم خودش 1024 را كه ديفالت است را تنظيم مي كند.
نكته ديگه اين كه در برنامه com با نوشتن org 100h باعث ميشه كه معلوم بشه فايل com است و يكسري تنظيماتي انجام ميشه با اين كد.
نكته ديگر اين كه بايد در كدهاي ما در فايل com نوع مدل tiny باشد.
دو خط:
mov ax,@data
mov ds,ax
را هم من به فايل خود اضافه كردم تا data ها در قطعه خودش پيدا كنه. اين data ها همان پيامهاي رشته اي است كه موقع اجرا چاپ ميشه و كاربر مي بينه.
براي امتحان من اين دو خط را برداشتم و exe ساختم كه برنامه قاطي مي كرد . يعني مي رفت جاي ديگر را مي خواند و حروف عجيب و غريب چاپ ميشد.

در برنامه سايت يك اشكال هم داشت كه من رفع كردم. و آن اين بود كه بعد Goodbye then. علامت دلار را نگذاشته بود و باعث ميشد خواندن پيام خاتمه پيدا نكند و ادامه بدهد به خواندن ram و باز حروف عجيب و غريب چاپ ميشد . كه با گذاشتن $ حل شد.

اما شرح برنامه:
اول چه نوع خروجي ئي مي دهد:
اول سه جمله چاپ مي شود به اين شكل:
A Simple Input/Output Program
Here is your first prompt
Do you want to be prompted again?

كه با y مي زنيم يا چيز ديگه. اگر y بزنيم اين جمله چاپ ميشه:
Here is another prompt!
و اگر چيز ديگه بزنيم ميگه:
Goodbye then.
يعني ((پس خداحافظ)) و برنامه خاتمه پيدا مي كند.

= = = = = = = = = = =
اما آموزش اين صفحه همان طور كه معلومه راجع به ورودي گرفتن از كاربر است.
در برنامه صفحه دوم اين سايت فقط يك hello world چاپ ميشد ولي در برنامه صفحه سوم ، برنامه با كاربر ارتباط برقرار مي كنه و از او نظر مي خواهد كه چه كار كنم. در اينجا شما مي توانيد با زدن y هاي متوالي مدام برنامه را وادار به تكرار كنيد يا با زدن حرف ديگر باعث اختتام برنامه شويد.

اين توضيحات را پست مي كنم و شرح جزئي كدها را در پست بعدي مي زنم.
 

saalek110

Well-Known Member
من براي اينكه بتوانيم فايل com هم بسازيم رفتم سرچ كردم و برنامه اي به نام tlink16 پيدا كردم كه توانست به درستي برنامه پست قبلي(برنامه اول پست قبلي ) را به فايل com تبديل كند.
آدرس دانلود:
http://www.cs.umsl.edu/~schulte/cs2710/

ولي فقط tlink16 را دانلود نكنيد چون بعدش ميگه كه rtm.exe را هم مي خواهم.
پس اين دو تا را دانلود كنيد.
==================
سينتكس تبديل:
کد:
tlink16 filename.obj/t

اگر اين t را نگذاريد مي رود فايل exe مي سازد به جاي com و اخطاري هم tlink16 مي دهد كه پشته چرا نداريد. و مهم تر از همه فايل درست كار نمي كند چون قطعه هايش تنظيم نيست.
البته همه اينها را براي برنامه اول پست قبل مي گيم نه برنامه دومي. برنامه دوم پست قبل را همين الان با tlink16 (بدون نوشتن حرف t ) تبديل كردم و بخوبي كار مي كرد. يعني همون كاري كه با link قبلا كرده بودم.
=======================
پس توانستيم فايل com هم بسازيم. و فرق برنامه هاي com و exe را هم كمي شرح داديم.
شرح كدهاي برنامه بالا را در پست بعدي پي مي گيريم
 

saalek110

Well-Known Member
اولا بايد بگويم كه در كد برنامه هاي com و exe بالا ، كدهاي اسمبلي يكي است.
به جز دو خط :
mov ax,@data
mov ds,ax
كه در exe داريم و در com نداريم كه محل داده ها را مشخص مي كنه ولي در فايل com چون همه اجزا در يك قطعه اند نيازي به مشخص سازي قطعه نيست.

حالا بقيه كدها:

چنين كدي:
mov dx,OFFSET Message
mov ah,9
int 21h
چند بار تكرار شده.كه كارش اينه كه با خط اولش روي يك رشته اول تنظيم ميشه و بعد با خط دومش سرويس وقفه تنظيم ميشه و با خط سومش وقفه احضار ميشه.
يعني در كل اين قطعه هاي سه خطي كارشان نمايش يك جمله به كاربر است.

مي رسيم به :
xor ah,ah

int 16h

خط اول باز تنظيم سرويس است اما اين بار با يك روش جديد. قرار بوده در ah صفر قرار داده بشه و براي صفر كردن آن ، آمده ah رابا خودش xor كرده. شما and و or را مي شناسيد. اولي وقتي 1 ميده كه هر دو يك باشند و دومي وقتي يك ميده كه يكي از آنها يك باشد. و اينجا xor وقتي كه هر دو يك نوع باشند صفر ميدهد و معلومه كه ah يا خودش هميشه يكي است پس xor آن با خودش صفر مي دهد بيرون. يعني يا يك است كه ((يك با يك)) صفر بيرون ميده و يا صفر است كه ((صفر با صفر)) باز صفر بيرون مي دهد.

حالا خط دوم كد بالا، وقفه 16 كارش اين است كه از كاربر كليدي را دريافت مي كند. از صفحه كليد. و انتظار هم مي كشد براي گرفتن كليد. وقفه ديگري براي گرفتن كليد هست كه انتظار نمي كشد و اگر كليدي ندهيم رد مي شود به كد بعدي.
و وقتي اين وقفه(سرويس صفر آن) كليد را دريافت كرد كدهاي اسكي و اسكن آن را در ah و al قرار مي دهد.

كد بعدي:
mov bl,al ; save to bl
همان طور كه در كامنت جلوي آن نوشته براي اين كه al ممكنه بعدا دستخوش تغيير بشه ، محتواي آن را در bl ذخيره مي كنه. به نوعي داره از al بك آپ مي گيره.

كد بعدي:
mov dl,al ; move al to dl
حالا al در dl قرار داده مي شود. چرا؟ چون كد زير:
mov ah,02h ; function 02h - display character
int 21h ; call DOS service
كه سرويس 2 وقفه 21 است كارش اينه كه نگاه مي كنه به dl و عدد داخلش را كد اسكي يك كاراكتر تفسير مي كند و آن را چاپ مي كند. براي تمرين به dl اعداد مختلفي بدهيد و با دو خط كد بالا چاپش كنيد.

كد بعدي:
cmp bl,'Y' ; is al=Y?
je Prompt_Again ; if yes then display it again
اينجا bl كه قبلا بك آپ al درونش ريخته شده بود با كد اسكي Y مقايسه مي شود. Cmp نوعي تفريقي است كه مثل a منهاي b كه بعد تفريق a و b تغييري نمي كنند. در حقيقت فقط مقايسه اين دو است. ولي فلاگها تفريق در نظر مي گيرند قضيه را.
فلاگها را نگفتيم و كم كم بايد آشنا بشويم با آنها.
هر فلاگ يك حافظه يك بيتي(نه بايت) است كه يا روشن است و يا خاموش. فرض كنيد يك چراغ جلوي دفتر كار شما نصب باشه كه وقتي وارد اتاق كار خود مي شويد روشن مي كنيد و وقتي خارج مي شويد خاموش مي كنيد. فلاگها چنين چراغهايي هستند براي راهنمايي.
پرش ها در اسمبلي دو نوعند. بي شرط و با شرط. پرشهاي با شرط به فلاگها نگاه مي كنند. در دو خط كد بالا je وقتي پرش مي كند كه نتيجه تفريق صفر باشد. يعني وقتي كه در bl كد اسكي Y باشد. كه bl هم از al گرفته بود محتواي خود را و al را هم وقفه 16 پر كرده بود. وقفه 16 هم كاراكتري را كه كاربر زده بود را در al ريخته. پس اينجا اين دو خط كد يعني كه اگر كاربر Y را زده بود پرش كن به ليبل Prompt_Again .

بحث ليبل:
ليبل همان طور كه در كدها مي بينيد يك اسم است كه هر چي دوست داشتيد مي توانيد قرار دهيد و جلوي آن دو نقطه بايد بگذاريد.
و اين جوري ليبل ميشه يك نقطه مشخص كه ميشه به آنجا دستور پرش داد. و پرشهاي شرطي و غير شرطي مي توانند بگويند كه به فلان ليبل بپر.
در كد زير:
cmp bl,'y' ; is al=y?
je Prompt_Again ; if yes then display it again
هم كه معلومه ديگه كه داره y را بررسي مي كنه . چون ممكنه كپس لاك كاربر روشن بوده باشه يا مورد ديگر كه او حرف ايگرگ را بزرگ يا كوچك بزند. و مي دانيد كه كد اسكي اين دو با هم متفاوت است.

كد بعدي:
mov ah,4Ch ; terminate program
int 21h
اين هم كه كد خاتمه دهنده برنامه است.

كد بعدي:
CR equ 13 ; enter
LF equ 10 ; line-feed
عبارت equ همان اكوئال يعني مساوي است . يعني CR ميشه مشابه 13 . كه بعدا استفاده ميشه.
بهتر زياد از اعداد استفاده نكنيد در برنامه خود چون بعدا وقتي يك 13 مي بينيد نمي دانيد چيست. به اين گونه اعداد ، ((اعداد جادويي)) مي گويند. البته فكر كنم خوب معني نشده و بايد اسمش را اعداد گنگ يا اعداد گيج كننده بگذاريم. چون موقعيكه خودمان آنرا در برنامه مي نويسيم مي دانيم چيه ولي چند روز بعد نمي دانيم اين 13 چيه. موضوع وقتي پيچيده ميشه كه يك 13 ديگه هم در برنامه باشه. و اگر روزي بخواهيم CR را از برنامه برداريد ممكنه آن 13 نوع دوم را هم برداريد و برنامه كلا قاطي بشه.
پس بهتره كه به اعداد خود اسم هاي مناسب بدهيد و بعد استفاده كنيد. اما كار اين 13 و در خط دوم كد بالا ، آن 10 چيه ؟ كدهاي بعدي را با هم مي بينيم:

Message DB "A Simple Input/Output Program$"
Prompt DB CR,LF,"Here is your first prompt.$"
Again DB CR,LF,"Do you want to be prompted again? $"
Another DB CR,LF,"Here is another prompt!$"
GoodBye DB CR,LF,"Goodbye then.$"

مي بينيد كه CR و LF در رشته هاي ما استفاده شده اند. مي دانيد كه وقتي DB را بكار برده ايم داريم بايت پر مي كنيم داخل ram . پس 13 و 10 هم كه حالا با اسم CR و LF شناخته مي شوند مي نشينند در ram . البته به شكل همان 13 و 10 و مي شوند اول رشته هاي ما. حالا كارشان چيه؟ اينه كه اينتر مي كنند و لاين فيد(نرفتم امتحان كنم ببينم چيه) مي كنند. پس جملات پشت سر هم چاپ نمي شوند و بعد اينتر در خط بعدي چاپ مي شوند و ظاهر برنامه شكيل تر ميشه.
علامت دلار را هم در انتهاي هر رشته فراموش نكنيد چون وقفه مربوطه تا آن را نبيند همين طور ادامه مي دهد. شما كه نمي خواهيد هزارها بايت كاراكتر بي معني چاپ بشه روي صفحه. پس با گذاشتن علامت دلار در انتهاي رشته كه مي رود در ram مي نشيند وقفه مربوطه را هدايت كنيد.

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

saalek110

Well-Known Member
تكميل نكات صفحه سوم:

سرويس صفر وقفه 16 كه با آن كار كرديم كد اسكن را در ah و كد اسكي را در al قرار مي دهد.
توضيح اينكه بعضي كليدهاي صفحه كليد كد اسكن هم دارند.
مي توانيد براي اينكه اين قضيه را تمرين كنيد برنامه اي بسازيد و كليدهاي مختلف را بزنيد و ah و al را بررسي كنيد. در زبانهاي ديگر مثل سي هم مي توانيد اين تمرين را بكنيد. كليدهاي f1 تا f12 و كليدهاي ديلت و arrow ها(بالا – پايين - ..) و اين نوع كليد ها را بررسي كنيد.
البته در جداول استاندارد هم اينها هست.

در برنامه ما چون كليد مورد نظر ما y بود كاري با اين بحث ها نداشتيم. چون كد اسكي را كافي بود بررسي كنيم.

= = = = = = = = = = = = = = = =
وقتي در رجيستري مقداري هست و بايد حفظ شود و همان لحظه به آن رجيستر نياز مي شود دو راه براي حفظ آن مقدار هست. يكي اين كه در رجيستر ديگري بريزيم و ديگر اين كه push كنيم به پشته و هر وقت لازم داشتيم pop كنيم.

در برنامه بالا ديديد كه وقفه 16 كه ورودي از صفحه كليد مي گرفت آن را در ah و al مي ريخت. حالا اگر در اين رجيسترها چيزي بود كه بايد از نابودي آن جلوگيري ميشد مي توانستيم با يكي از اين دو راه آن را حفظ كنيم.
در برنامه بالا ديديم كه al را به bl ريختيم. من راجع به اين كه اين كار چه نفعي داشته فكر نكردم چون بنا به اعتمادي كه كردم به نويسنده اين آموزش گفتم احتمالا al در معرض خطر بوده. شما خودتان بررسي كنيد كه آيا ريختن al به bl لازم بوده يا نه. بايد بدانيد كه ابدا لازم نيست براي حل چنين مسئله اي شما همين راه را برويد . بلكه مي توانيد سليقه اي از رجيسترها و پشته استفاده كنيد. ولي با اين وجود از رجيسترها به طور خاصي استفاده مي شود و نوعي استاندارد در اين مورد ايجاد شده. ولي شما اگر نخواستيد اين استاندارد ها را رعايت كنيد حداقل سعي كنيد كه استانداردهاي شخصي براي خود داشته باشيد تا در مرور برنامه هاي خود دچار سختي نشويد.

در برنامه بالا ديديد كه رجيستر ah و al كارهاي خاصي به عهده اش بود. در اول كار تنظيم سرويس با ah بود و بعد اجراي وقفه هم كد اسكن داخل ah ريخته ميشد.
پس مي بينيد كه ax تا dx هم وظايف خاصي گاهي دارند. كار خاص dl را هم ديديد كه در سرويس 2 وقفه 21 نگه دارنده كاراكتر بود. در صفحه هاي بعدي اين سايت آموزشي كه ما تابحال تا صفحه 3 آمده ايم مي بينيم كه cx هم به عنوان كانتر يعني شمارنده استفاده مي شود يعني وقتي حلقه اي مي سازيم اين رجيستر مي شود شمارش گر تعداد چرخش. حرف c هم اول نام counter است. و حرف aاز ax به معني آكومولاتور به معني انباره يا جمع كننده.

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

== = = = = = = =
كد زير هم نمايش ساده اي از پرش غير شرطي و ليبل هدف آن است:
کد:
jmp ALabel
.
.
.
ALabel:
دستور jmp دستور پرش بي شرط است. بقيه دستورات شرطي با نگاه به فلاگها پرش مشروط مي كنند. در برنامه پست قبل je وقتي bl مساوي y بود پرش مي كرد. چون اجراي دستور cmp باعث تغيير فلاگي شده بود كه تغيير آن فلاگ به je دستور پرش را دستور مي دهد.
ليست بعضي پرشهاي شرطي را در زير مي آوريم:
a1.jpg


نكته:
Note: the jump can only be a maximum of 127 bytes in either direction.
پرش به هر سو بايد زير 127 بايت باشد.
هم در مورد پرش شرطي و هم پرش غير شرطي اين قضيه را امتحان كنيد. بايد warning بدهد كامپايلر شما.
= = = = = = == == =
سينتكس كامل تر cmp :
کد:
CMP register or variable, value

پس مي بينيد كه فقط رجيستر در طرف چپ قرار نمي گيرد بلكه متغير هم مي تواند قرار گيرد.
مثال cmp :
کد:
cmp ax,3; is AX = 3?
je correct; yes

کد:
cmp al,'Y' ; compare the value in al with Y
je ItsYES ; if it is equal then jump to ItsYES

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

saalek110

Well-Known Member
صفحه چهارم:

در اين صفحه يك سري دستورات مورد نياز رايج گفته مي شود.

اولي دستور جمع است:
کد:
ADD operand1,operand2

باعث جمع دو تا اپرند ميشه و حاصل در اولي گذاشته ميشه.

تفريق:
کد:
SUB operand1,operand2

دومي از اولي كم ميشه.

ضرب:
کد:
MUL register or variable

چيزي كه جلوي اين دستور باشه در al يا ax ضرب ميشه و در همان جا قرار مي گيره . اگر بزرگتر از اندازه آن مكان باشه ، اگر al باشه به كل ax ميريزه و اگر ax باشه بقيه اش به dx ريخته ميشه.

دستور ضرب بالا براي بي علامت يا unsigned است.
براي علامت دارها دستور زير استفاده ميشه:
کد:
IMUL register or variable

تقسيم: (براي علامت دار و بي علامت )
کد:
DIV register or variable
IDIV register or variable

شبيه ضرب است . آنجا اضافه مي رفت به ah و dx و در اينجا باقيمانده مي رود اينجاها.
= = = == = = == = = = = =

زير برنامه:

مقداري كد است كه در زيربرنامه قرار مي دهيم و نگران طرز كارش ديگه نيستيم. در اكثر زبانها زيربرنامه را داره.

کد:
PROC AProcedure
.
. ; some code to do something 
.
ret; if this is not here then your computer will crash 
ENDP Aprocedure

ساختارش را مي بينيد كه با proc شروع ميشه ، بعدش اسم زيربرنامه.
بعد كدها را مي نويسيم.
آخرش يك ret داره و
بعدش هم يك endp و باز اسم زيربرنامه.

طرز صدا كردن(فراخواني ) :
کد:
call Aprocedure

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

Start: 

call Display_Hi ; Call the procedure
mov ax,4C00h ; return to DOS
int 21h 

Display_Hi PROC 

mov dx,OFFSET HI 
mov ah,9 
int 21h 

ret
Display_Hi ENDP 

HI DB "Hello World!$" ; define a message

end Start

شرح برنامه:
اولا ساختار برنامه com است پس موقع لينك با tlink16 و پارامتر t تبديل كنيد.
دوما تنها كاري كه كرده آمده كل برنامه را در يك زيربرنامه قرار داده و در برنامه اصلي زيربرنامه را صدا زده و بعد صدا زدن برنامه هم دستورات اختتام را گذاشته. همين.
 

saalek110

Well-Known Member
ادامه صفحه چهار:

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

در اسمبلي ارسال مقادير به 3 روش انجام مي شود:
يك از طريق رجيستر
دو از طريق پشته
سه از طريق حافظه

= = = = == = =
حالت اول : ارسال از طريق رجيستر
اين حالت ساده و سريع است و فقط كافي است قبل صدا كردن زيربرنامه مقدار مورد نظر را در رجيستر قرار دهيد.

نكته اي كه بايستي در مورد زيربرنامه ها گفته بشود اين است كه زير برنامه بايد تمام رجيسترهايي را كه دستكاري مي خواهد بكند را قبل هر كاري به پشته بفرستند و در آخر زيربرنامه همه را از پشته بازيابي كند. اين كار براي اين است كه ما بي هيچ نگراني زيربرنامه ها را بكار بريم.
ترتيب push و pop را هم يادتان نرود. اوني كه اول مي فرستيد را در آخر بازيابي بايد بكنيد.

مثالي كه براي ارسال از طريق رجيستر اينجا زده شده يك مثال طولاني است و به نظر من براي آموزش اصلا خوب نيست ولي من نقل مي كنم.
کد:
.model tiny
.code
org 100h

Start:

mov dh,4 ; row to print character on
mov dl,5 ; column to print character on
mov al,254 ; ascii value of block to display
mov bl,4 ; colour to display character

call PrintChar ; print our character
mov ax,4C00h ; terminate program
int 21h

PrintChar PROC NEAR

push bx ; save registers to be destroyed
push cx

xor bh,bh ; clear bh - video page 0
mov ah,2 ; function 2 - move cursor
int 10h ; row and col are already in dx

pop bx ; restore bx

xor bh,bh ; display page - 0
mov ah,9 ; function 09h write char & attrib
mov cx,1 ; display it once
int 10h ; call bios service

pop cx ; restore registers

ret ; return to where it was called
PrintChar ENDP

end Start

در اين مثال قبل call زير برنامه رجيسترها را تنظيم كرده. اين برنامه از وقفه ها استفاده مي كند براي يك كار گرافيكي . من اجراش كردم . يك مستطيل چشمك زن در قسمتي از صفحه داشت. خودتان كامنت ها را بخوانيد تا متوجه وظيفه هر كد بشويد. ولي نياز نيست واقعا روي اين كد طولاني كار كنيد. مهم همان ماهيت كار بود كه توضيح داده شد. يعني تنظيم رجيسترها قبل call زيربرنامه.

= = == = = =
روش دوم ارسال از طريق حافظه:
اين روش ساده است ولي برنامه را بزرگ مي كند و كند.

نفس كار اين است كه چيزهايي كه قراره ارسال بشه در يك متغير ذخيره ميشه و دقيقا مثل حالتي كه با رجيستر كار ميشد با اين متغير كار ميشه . ولي كار با رجيستر مسلما سريعتره.(سرعت اجراي برنامه منظوره)

کد:
; this a procedure to print a block on the screen using memory
; to pass parameters (cursor position of where to print it and
; colour).

.model tiny
.code
org 100h

Start:

mov Row,4 ; row to print character
mov Col,5 ; column to print character on
mov Char,254 ; ascii value of block to display
mov Colour,4 ; colour to display character

call PrintChar ; print our character
mov ax,4C00h ; terminate program
int 21h

PrintChar PROC NEAR

push ax cx bx ; save registers to be destroyed

xor bh,bh ; clear bh - video page 0
mov ah,2 ; function 2 - move cursor
mov dh,Row
mov dl,Col
int 10h ; call Bios service

mov al,Char
mov bl,Colour
xor bh,bh ; display page - 0
mov ah,9 ; function 09h write char & attrib
mov cx,1 ; display it once
int 10h ; call bios service

pop bx cx ax ; restore registers

ret ; return to where it was called
PrintChar ENDP

; variables to store data

Row db ?
Col db ?
Colour db ?
Char db ?

end Start

برنامه بالا را هم من تست كردم اجرا شد. همان برنامه قبلي است فقط با متغير كار شده.
شرح برنامه: مي بينيد كه به جاي اينكه رجيسترها را قبل call پر كند ، متغيرها را پر كرده. متغيرها هم در آخر كدها مي بينيد كه بدون مقدار اوليه دادن تعريف شده اند.


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

حالا مثال:
کد:
; this a procedure to print a block on the screen using the
; stack to pass parameters (cursor position of where to print it
; and colour).

.model tiny
.code
org 100h

Start:

mov dh,4 ; row to print string on
mov dl,5 ; column to print string on
mov al,254 ; ascii value of block to display
mov bl,4 ; colour to display character
push dx ax bx ; put parameters onto the stack

call PrintString ; print our string

pop bx ax dx ;restore registers
mov ax,4C00h ;terminate program
int 21h

PrintString PROC NEAR

push bp ; save bp
mov bp,sp ; put sp into bp
push cx ; save registers to be destroyed

xor bh,bh ; clear bh - video page 0
mov ah,2 ; function 2 - move cursor
mov dx,[bp+8] ; restore dx
int 10h ; call bios service

mov ax,[bp+6] ; character
mov bx,[bp+4] ; attribute
xor bh,bh ; display page - 0
mov ah,9 ; function 09h write char & attrib
mov cx,1 ; display it once
int 10h ; call bios service

pop cx ; restore registers
pop bp

ret ; return to where it was called
PrintString ENDP

end Start

برنامه بالا را امتحان كردم. يك مربع قرمز چاپ شد. حالا نمي دانم اين نشانه سلامت است يا خطا . بگذريم.
شرح: براي بهره برداري ازپشته تنها چيزي كه بايد بدانيم اين است كه آن چيز در كجاي پشته است.
مي دانيد كه پشته به سمت پايين رشد مي كند. يعني اگر ما سه چيز را به طور متوالي بياندازيم داخل پشته ، آدرس اولي بزرگتر از دومي و آدرس دومي بزرگتر از سومي است.
دقيق تر بگوييم آدرس اولي تا سومي عبارت است از bp+6 و bp+4 و bp+2 .
بايد بدانيد كه شما مي توانيد ax را به پشته بفرستيد ولي نمي توانيد ah را به پشته بفرستيد. پس مستاجرين پشته همه 2 بايتي هستند. براي همين اين اعداد 2 و 4 و 6 توليد شده است.
اما برنامه پيچيده بالا باز هم نياز به توضيح دارد كه بايد مثل معمايي حل شود. مي سپارم به شما حلش را. خودم هم روي آن فكر مي كنم اگر معما را توانستم حل كنم در همين پست مي نويسم. از عكس زير هم كه براي فهم اين قضيه در همين صفحه آمده كمك بگيريد. مشكل من اينه كه تفاوت sp و Bp را خوب نمي دانم.


a1.jpg
 
آخرین ویرایش:

saalek110

Well-Known Member
ادامه صفحه چهار:

مدلهاي حافظه:
Memory Models

سينتكس:
کد:
.MODEL MemoryModel
مثال:
کد:
.model tiny

انواع:
SMALL, COMPACT, MEDIUM, LARGE, HUGE, TINY or FLAT.

= = = == = = = =
مدل tiny :
در اين مدل ، يك قطعه واحد براي كد و data داريم.
اين نوع برنامه مي تواند به فايل com تبديل شود.

مدل small :
كد در يك قطعه و data در يك قطعه ديگر.
و اين به اين معناست كه تمام زيربرنامه ها و متغيرها با آدرس دهي near و فقط توسط آفست ، آدرس دهي مي شوند.

مدل compact :
كد در يك قطعه و data در چند قطعه. پس اولي near ميشه و دومي far . يعني در دومي هم آدرس قطعه لازم دارد و هم آفست. ولي اولي فقط آفست لازم دارد.

مدل medium :
برعكس compact است. يعني data ميشه near و code ميشه far .

مدل large :
هر دو far .

مدل flat :
اين مدل نياز به DOS extender دارد.
 
وضعیت
موضوع بسته شده است.

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

بالا