63 (1339)

دسته‌بندی نشده    زبان ماشین و اسمبلی زبان ماشین و اسمبلی نام استاد : جناب آقای رحمانی نام محقق : سودابه قربانی دانشگاه پیام نور تاکستان زمستان 1385 زبان ماشین و اسمبلی مقدمه : در ابتدا شما را با تاریخچه زبان های برنامه سازی آشنا می نماییم.در کل زبان های برنامه سازی به دو دسته تقسیم می شوند:۱- زبان های سطح پایینLow level languages ۲- زبان های سطح بالا  High level languages زبان های سطح پایین زبان هایی هستند در سطح ماشین و به دور از زبان طبیعی و محاوره ای انسان. این زبان ها وابسته به ماشین و سخت افزار هستند، بطوری که هر میکروپروسسور زبان خاص خود را داراست. کار کردن با این زبان ها مشکل است و خطا یابی و بررسی برنامه ها به سهولت امکان پذیر نیست، ولی به علت نزدیکی به ماشین، برنامه های نوشته شده به این زبان ها با سرعت بالایی اجرا می شوند. زبان های سطح پایین به دو دسته تقسیم می شوند:1- زبان ماشین  : در این زبان که تنها زبان قابل فهم برای کامپیوتر می باشد از ارقام صفر و یک به عنوان علایم اولیه استفاده می شود و ارقام صفر و یک در حقیقت الفبای این زبان محسوب می شوند و جهت ایجاد کدهایی برای دستورالعمل ها بکار می روند،به طوری که هر دستورالعمل به صورت رشته _ ای از صفر و یک ها نوشته می شود.2- اسمبلی  : این زبان در واقع همان زبان ماشین است، با این تفاوت که جهت ساده نمودن کار برنامه نویس، کدهای سمبلیکی به نام کد نیمانیک  در اوایل سال های 1950 بوجود آمد که در آن، از حروف برای کد گذاری کدهای زبان ماشین استفاده شد که این کدها در ریزپردازنده های مختلف با یکدیگر تفاوت دارند. این کدها توسط نرم افزار های به خصوصی بنام اسمبلر  به زبان ماشین تبدیل می گردند تا قابل درک برای ماشین باشند. اَسِمبلی یکی از زبان‌های برنامه‌نویسی با رایانه است و علامت‌های قابل خواندن توسط انسانِ زبان‌ماشین ‌می‌باشد. زبانهای برنامه‌نویسی سطح بالا علی رغم توانایی‌های زیادی که دارند همیشه برای کار با سخت افزار مناسب نیستند در حالی که اسمبلی قدرت زیادی برای کار با سخت افزار دارد. همچنین برنامه هایی که به زبان اسممبلی نوشته می‌شوند بدلیل اینکه مستقیماً دستورات را بر روی پردازنده مرکزی اجرا می‌کنند، سرعت بالایی دارند. برنامه‌های نوشته‌شده به زبان اسمبلی نسبت به برنامه‌های معادل در زبانهای سطح بالا، حافظه کمتری را اشغال می‌کنند. برنامه‌هایی که نیاز به زمان بلادرنگ دارند در اسمبلی به خوبی نوشته می‌شوند و همچنین برنامه‌های مقیم در حافظه.در ضمن برنامه نویس با اسمبلی کارهایی می‌تواند انجام دهد که با زبانهای سطح بالا نمی‌شود آن کارها را انجام داد . شرح مختصری بر کامپایلر به طور کلی , کامپایلر برنامه‌ای است که متن برنامه‌ای را که به یک زبان برنامه‌سازی نوشته شده است ,به عنوان ورودی می‌پذیرد , و خروجی آن , متن برنامه‌ای به یک زبان دیگر است , به طوری که معنای آن متن تغییر نمی‌کند. این فرآیند , در زبان طبیعی , ترجمه نام دارد. مترجمها جملات یک زبان طبیعی را به زبان طبیعی دیگر ترجمه می‌کنند. تقریبا تمام کامپایلرها ,برنامه‌ای به یک زبان منبع را گرفته به برنامه‌ای به زبان مقصد تبدیل می‌کنند . به عنوان مثال , زبان منبع می‌تواند c و زبان مقصد می‌تواند زبان ماشین برای کامپیوتر پنتیوم باشد. زبانی که خود کامپایلر با آن نوشته می‌شود, زبان پیاده ساز نام دارد. علت انجام ترجمه این است که , سخت افزار عملیاتی را انجام می‌دهد که توسط معنای برنامه توصیف می‌شود . به طور کلی , سخت افزار تنها منبع واقعی انجام محاسبات است. اجرای برنامه ترجمه شده, شامل خواندن داده‌های آن بار فرمت خاص , و چاپ نتایج با فرمت دیگری است . داده‌های ورودی می‌تواند از منابع متعددی خوانده شود , مثل فایلها , صفحه کلید و پکیج‌های شبکه‌به همین ترتیب , خروجی نیز می‌تواند در فایلها , صفحه نمایش رایانه , و چاپگر قرار گیرد . برای ترجمه برنامه,از کامپایلر استفاده می‌کنیم . کامپایلر برنامه‌ای است که ورودی آن، , فایلی با فرمت متن برنامه و خرجی آن، فایلی با فرمت کد اجرایی است . برای تهیه یک کامپایلر , کامپایلر دیگری رااجرا می‌کنیم که ورودی آن ,متن منبع این کامپایلر و خروجی آن , کد اجرایی برای این کامپایلر است . این فرآیند کامپایل است . اگر زبان منبع , همان زبان پیاده ساز باشد, و متن منبعی که کامپایل می‌شود, نسخه جدید خود کامپایلر باشد, این فرآیند خودرانی نام دارد. کامپایل کردن یک برنامه‌, با تبدیل فرمت یک فایل به فرمت دیگر , مثل EBCDIC به اسکی تفاوت عمده‌ای ندارد . در کامپایل کردن برنامه, معنای برنامه باید حفظ شود . به دو دلیل زیر کامپایلر می‌تواند کار کند: ورودی, به یک زبان برنامه سازی است و در نتیجه دارای ساختاری است که در مراجع آن زبان مشخص شده است . معنای ورودی بر اساس این ساختار توصیف می‌شود ,و به آن ساختار مربوط است. این عوامل موجب می‌شوند تا کامپایلر برنامه را "درک کند " و معنای آن را در یک نمایش معنایی جمع آوری کند .هریک از دو عامل فوق , در زبان مقصد نیز وجود دارد . بدین ترتیب , کامپایلر می‌تواند معنای جمع آوری شده را بر حسب ساختار زبان مقصد ارائه کند . بخشی از کامپایلر که متن زبان منبع را تحلیل می‌کند , پردازشگر جلویی و بخشی از کامپایلر که زبان مقصد را ترکیب می‌کند, پردازشگر نهایی نام دارد اگر کامپایلر به خوبی طراحی شده باشد, پردازشگر نهایی, کاملا از زبان مبدا و پردازشگر جلویی از زبان منبع بی خبر است . هر یک از این دو بخش از نمایش معنایی برنامه خبر دارند .این تفکیک دقیق , دلایل فنی دارد و منجر به کامپایلرهای ساخت یافته می‌شود. حالت دیگری از عملیات برای کامپایلر پیشنهاد می‌شود: اگر تمام داده‌های ورودی مورد نیاز , فراهم باشند, کامپایلر می‌تواند مفهوم مشخص شده توسط معنای برنامه را بدون تبدیل به شکل دیگر , اجرا نماید . در این صورت به جای مولد کد پردازشگر نهایی, مفسر پردازشگر نهایی قرار می‌گیرد و این برنامه, مفسر نامیده می‌شود . چند دلیل برای این کار وجود دارد . یکی از دلایل مهم این است که مفسر معمولا در ماشین خاصی اجرا می‌گردد. به عبارت دیگر , استفاده از مفسر منجر به افزایش قابلیت حمل می‌گردد. دلیل دیگر این است که نوشتن مفسر, آسانتر از پردازشگر نهاییت کامپایلر است .دلیل سوم برای استفاده از مفسر به جای کامپایلر این است که , اجرای مستقیم برنامه , عیب یابی و گزارش خطا را آسانتر می‌کند . دلیل چهارم این است که امنیت از طریق مفسرها افزایش می‌یابد . این کار , در جاوا اهمیت فراوانی دارد. اجزای کامپایلر: هر کامپایلر از قسمت های اصلی زیر تشکیل شده است: فازتحلیلگر لغوی فازتحلیلگر نحوی فازتحلیلگر معنایی فازتولید کننده کد میانی فازبهینه ساز کد فازتولید کننده کد پوسته پوسته یا شل(shell) یک رابط است با ظاهری مانند آنچه در MS-DOS آن را دیده‌اید. البته در اصل پوسته (shell) یک رابط است میان انسان و هسته سیستم عامل که زبان انسان‌ها را به زبان ماشین تبدیل می‌کند تا هسته (Kernel) سیستم عامل آن را بفهمد. به احتمال زیاد تا کنون بیشتر یا فقط با محیط های با رابط گرافیکی(GUI) کار کرده اید ، اما رابط کاربر دیگری که لینوکس و سایر سیستم عامل های شبه یونیکس یا برپایه ی یونیکس آن را دارند رابط خط فرمان یا Command Line Interface است که به اختصار CLI می نامند.قدرت حقیقی لینوکس و یونیکس در این رابط نهفته است و این رابط خط فرمان است که به مدت ۳۰ سال است که یونیکس را به عنوان قدرت مند ترین سیستم عامل مطرح کرد و اکنون این قدرت نهفته در تمام سیستم های شبیه یا برپایه ی یونیکس مانند لینوکس وجود دارد. زمانی می توان با یک سیستم عامل راحتر کار کرد که امکان کار با خط فرمان در آن فراهم شده باشد و در لینوکس این مهم صورت گرفته. پوسته یا شل(shell) یک رابط است با ظاهری مانند آنچه در MS-DOS آن را دیده اید . البته در اصل پوسته (shell) یک رابط است میان انسان و هسته ی سیستم عامل که زبان انسان ها را به زبان ماشین تبدیل می کند تا هسته (Kernel) سیستم عامل آن را بفهمد . می تواندر یک تجسم غیر رسمی گفت که میزکار های ویندوز(windows) و مکینتاش (Macintosh OS) نیز یک پوسته ی گرافیکی هستند که رابط میان انسان و هسته ی این سیستم عامل هاست و زبان انسان را به زبان ماشین که برای هسته قابل فهم باشد ترجمه می کند. ● انواع پوسته در این بخش به معرفی چند نمونمه ی معروف پوسته های مختلف خواهیم پرداخت البته در لینوکس می توان از تمام این پوسته ها استفاده کرد و در اغلب توزیع های کامل تمام این پوستهها یافت می شوند اما پوسته ی پیش فرض اغلب ( اغلب نه تمام لینوکس ها ) پوسته ی bash می باشد ، دلیل این انتخاب قدرت و به روز بودن این پوسته و علاوه بر این ها انتشار این پوسته بر اساس مجوز GNU/GPL می باشد . ما رد این جا به ترتیب پوسته های Bourn shell و C shell و Korn shell و Bourn again shell و tch shell را مورد بررسی قرار خواهیم داد. ۱. Bourne Shell یا sh در اصل این پوسته در ازمایشگاه های بل توسط استیون بورن ( Stiven Bourne) برای سیستم عامل AT&T UNIX نوشته شد البته در لینوکس از این پوسته چندان استفاده نمی شود و در سایر نسخه های یونیکس مانند FreeBSD نیز این پوسته با یک نسخه به نام POSIX جایگزین شده .POSIX از ویرایشگر خط فرمان ، تاریخچه ی خط فرمان (که نه در sh بود نه در سایرCLI ها مانند DOS) و همین طور اسامی مستعار یا aliase ها به طور پیش فرض پشتیبانی می کرد. ۲. C shell یا Csh این پوسته که اغلب در BSD ها استفاده می شود توسط گروه توسعه دهندگان نرم افزار دانشگاه برکلی که از محدودیت های Bourne shell به ستوه آمده بودند ایجاد شد. دلیل نام گذاری این پوسته به C گرامر یا Syntax این پوسته است که به زبان برنامه نویسی C بسیار شباهت دارد و این موضوع خود باعث دشواری در نوشتن برنامه های پوسته یا Shell Programs می شود .(این برنامه ها درون خود پوسته اجرا می شوند برای اجرا حتمابه پوسته یا شل مخصوص به خود احتیاج دارند در واقع پوسته این برنامه ها را تفسیر می کند.) در این پوسته سیستم کنترل فعالیت هایا Job Controling و تاریخچه ی خط فرمان به صورت پیشرفته و کامل تر وجود دارد . البته در حال حاضر در FreeBSD نسخه های پیشرفته Csh با tcsh جایگزین شده که tcsh را در قسمت‌های بعد مورد بررسی قرار داده ایم. البته در لینوکس می توان ار هر دوی این پوسته ها استفاده کرد. ۳. Korn shell یا ksh یا pdksh شرکت AT&T پوسته ی ksh را در سال ۱۹۸۶ منتشر کرد ، این پوسته که توسط دیوید کورن (David Korn)نوشته شد ، پاسخی بود برای انتشار C shell .این پوسته مانند C shell از کنترل فعالیت (Job Controlling) ، تاریخچه ی خط فرمان (Command line History) و اسامی مستعار پشتیبانی می کرد و نسبت به Bourne shell بسیار کاربر پسند تر بود .در این نسخه برنامه نویسی نیز بسیار راحتر شده بود و همین طور ابزار های آن نیز بسیار بیشتر شده بودند. pdksh نیز یک Korn shell است با این تفاوت که یک نسخه ی Public domain می باشد که از pd ابتدای آن نیز می توان متوجه این موضوع شد. ۴. Bourne Again shell یا bash بش یا Bourne again shell یک پوسته ی سازگار و شبیه (ونه برپایه ی ) sh یا Bourne shell است که توسط توسعه دهندگان بنیاد نرم افزار آزاد ( FSF ) توسعه داده شد . همان طور که قبلا هم اشاره کردیم bash پوسته ی پیش فرض اغلب لینوکس ها است. این پوسته شبیه به پوسته ی ksh است با ابزار ها و قابلیت های بیشتر . از جمله غابلیت های جدید این پوسته راهنمای درونی یا Built-in Help آن است همین طور ویرایش مستقیم خط فرمان یا Intuitive Command Line Editing و یا ویرایش تاریخچه ی خط فرمان و جستجو با کلید های جهت دار یا Arrow keys و یا متغییر های محیطی ( Environment Variables) بسیار زیاد آن .البته این پوسته در نسخه های تجاری یونیکس موجود نیست و دلیل آن هم مجوز بسیار عالی GNU/GPL است . ۵. tcsh shell این پوسته یک پوسته ی پیشرفته بر اساس پوسته ی C shell است .کلمه ی t در ابتدای نام آن اول نام سیستم عامل TENEX است که بر روی DEC PDP-۱۰ اجرا می شود. در این پوسته به C shell قابلیت های بیشتری اضافه شده مانند ویرایش تاریخچه به سبک bash و یا پرسش های قبل اعمال تغییرات ماند پرسش قبل از پاک کردن یک شاخه( Folder ).البته همان طور که قبلا اشاره کردم FreeBSD های پیشرفته از این پوسته به عنوان پوسته ی پیش فرض استفاده می کنند. III.کدام پوسته همان طور که قبلا اشاره کردیم اغلب لینوکس ها از bash به عنوان پوسته ی پیش فرض استفاده می کنند و البته لینوکس های کم حجم مثلا در حجم یک فلاپی از پوسته های کم حجم تر استفاده می کنند. از آن جایی که کار با bash بسیار آسان است و مانند کار کردن برنامه نویسی برای آن نیز راحت تر است اغلب لینوکس کار ها این پوسته را می پسندند و بنابراین اغلب اسکریپت ها ی موجود در برنامه های نصب مربوط به لینوکس هم از بر اساس این پوسته نوشته می شوند ، بنابر این برای آنکه بتوانید از کار کردن با پوسته ی خط فرمان لذت ببرید و به راحتی از دستوراتی که در سایت ها و مستندات مختلف می بینید استفاده کنید بهتر است از bash استفاده کنید . البته دلیل دیگر برای استفتده از bash همانا مجوز آن یعنی GNU/GPL است کهبرای کاربران لینوکس قوت قلبیست که از پوسته ای که تحت مجوز بنیاد نرم افزار آزاد منتشر شده است استفاده کنند. البته در نهایت این تصمیم با شماست که از کدام پوسته استفاده کنید و ما فقط یک پیشنهاد به شما دادیم و البته تمام مطالب این سایت بر اساس پوسته ی bash ارائه شده است. لازم به ذکر است که برای تعویض پوسته در صورتی که پوسته در کامپیوتر شما نصب باشد کافیست نام پوسته را تایپ کنید مثلا برای رفتن به bash از csh کافیست تا در مقابل اعلان فرمان تایپ کنیدbash تا از این پس با پوسته ی bash کار کنید اسمبلی : آموزش برنامه نویسی با اسمبلی برای یاد گرفتن اسمبلی باید با مبناهای عدد نویسی ، ساختمان داخلی کامپیوتر و برنامه نویسی آشنا باشیم . ما برنامه هایمان را مستقیما با اسمبلر Macro Assembler خواهیم نوشت و گاها از Debugاستفاده خواهیم کرد . بعلاوه چون برنامه های حجیم نخواهیم نوشت قالب اکثر رنامه های ما COM. خواهد بود . برای شروع ابتدا نگاهی به حافظه میکنیم : حافظه و آدرس دهی هر کامپیوتر مبتنی بر 8086 دارای حداقل 640 کیلوبایت حافظه است . این 640 کیلوبایت به قطعات 64 کیلوبایتی تقسیم شده و ما این قطعات را "قطعه " یا Segmentمینامیم . هر سگمنت هم به خانه های تک بایتی دیگری تقسیم شده است . برای بدست آوردن مقدار یک بایت مشخص از حافظه ما باید عد مربوط به سگمنت و همچنین شماره آن بایت در سگمنت ( که آفست Offset نامیده میشود ) را بدانیم . مثلا اگر مقدار مورد نظر در قطعه 0030h(h( یعنی عدد در مبنای 16 است ) و آفست 13C4hباشد ما باید قطعه ای که شماره آن 0030h است را بیابیم و بعد در همان قطعه مقدار باین شماره 13C4 را بخوانیم . برای نمایش این حالت بین عدد سگمنت و آفست علامت (:) قرار میدهیم . یعنی ابتدا عدد مربوط به قطعه را نوشته و سپس عدد آفست را می آوریم : Segment:Offset مثال : 4D2F:َ9000 **همیشه در آدرس دهی ها از اعداد مبنای 16 استفاده میکنیم . | CConvertional | 1 Segment=64K | | | | | Memory ثباتها Registers رجیسترها مکان هائی از CPU هستند که برای نگهداری داده ها (DATA) و کنترل اجرای برنامه بکار میروند . ما میتوانیم آنها را مقدار دهی کرده و یا بخوانیم و یا باتغییر محتوای آنها CPU را مجبور به انجام یک پروسه (رویه یا Procedure) کنیم دسته ای از رجیسترها که ما انها را "ثباتهای همه کاره یا همه منظوره " میخوانیم و شامل AX/BX/CX/DX هستند ، برای انتقال مقادیر بین رجیستر ها و CPU بکار میروند.این ثباتها را میتوانیم به هر نحوی تغییر دهیم و مقادیری را به آنهاارسال کنیم . ثباتهای دیگری هم که نام میبریم کاربردهای خاص خودشان را دارند و برای مقدار دهی آنها باید قواعد خاصی (که توضیح خواهیم داد) را بکار بریم . میکند عدد که در این ثبات وجود دارد شماره یک قطعه است و CPU برای یافتن DS : مخفف Data Segment . محل نگهداری متغییرها و ثابتهای برنامه را مشخص مقادیر لازم به آن قطعه مراجعه میکند . CS: مخفف Code Segment است و آدرس قطعه ای که برنامه در آن قرار گرفته را نشان میدهد . ES: این یک ثبات کمکی است و معمولا در آدرس دهی ها شماره قطعه را نگهداری میکند . DIDataIndex:Dبا DS/ESا مرتبط است و عدد آفست را نگهداری میکند . IP: این رجیستر معلوم میکند که برنامه در حال اجرائی که در CS قرار دارد از کدام بایت قطقه (یعنی کدام آفست ) شروع میشود . به همین دلیل همیشه این دو ثبات را با هم و بصورت CS:IP نشان میدهند. و ... تمام رجیسترهای فوق 16 بیتی (دوبایتی ) هستند و اعداد دوبایتی را نگهداری میکنند. ثباتهای همه منظوره به دو نیم ثبات تک بایتی تقسیم میشوند . بایت بالائی ب نماد H و بایت پائینی با نماد L نشان داده میشود . مثلا ثبات AX دارای دو نیم - ثبات AH/AL است : | AH - 8 Bit | AL -8 Bit | حال میخواهیم برنامه ای بنویسیم که کامل و قابل استفاده باشد . با این برنامه میتوانیم فلاپی دیسکهای خودمان را با سرعت کپی کنیم ! امروز برنامه را به شکلی مینویسیم که بتواند دیسکهای 1.44 را بوسیله درایو A کپی کند . بیشتر نیاز ما در کپی (تکثیر) دیسکها هم به همین شکل هست . با اینحال در قسمت بعدی نگارش (Version) جدیدتری از برنامه را مینویسیم و قابلیت تشخیص نوع دیسک و قابلیت مشخص کردن درایو را به آن اضافه میکنیم . بهترین کاری که میتوانیم بکنیم اینست که بتوانیم داده های خوانده شده از دیسک را در حافظه EMS بنویسیم (در این نسخه روی هارددیسک مینویسیم ) . وقتی که نحوه کار را حافظه گسترش یافته (Extended Memory) را هم یاد گرفتیم ، برنامه خود را کامل کرده و از آن بعنوان اولین دستختمان در برنامه نویسی اسمبلی لذت میبریم . لیست برنامه در زیر قرار دارد و توضیحات برنامه را روی آن میبینیم قبل از آن یاد آوری میکنم که هر دیسک HD َ1.44 دارای دو طرف و در هر طرف 80 شیار (Track) بوده و هر شیار هم به 18 بخش بنام قطاع (Sector) تقسیم میشود . برنامه ما باید محتوای تمام این قطاعها را خوانده و در فایلی روی دیسک سخت ذخیره کند. سپس همین داده ها را از فایل خوانده و مجددا روی دیسک جدید بنویسد. طول هر قطاع 512 بایت است EQU 512 SECTORSIZEتعداد شیار ها 80 شیار (79- 0-) است EQU 79 MAXTRACKهر دیسک دو طرف دارد EQU 2 NUMSIDESتعداد سکتور در هر شیار 18 تا است EQU 118 SECTOR_PER_TRACK E.MODEL SMALL.CODEORG 100HSTART:JMP MAINبافر برای ذخیره (0)BUF DB SECTORSIZE*SECTOR_PER_TRACK DUP داده ها . اندازه آن به اندازه بایتهای یک شیار است معرف رویه فعلی دیسک SIDE D DB 0 معرف تراک جاری TRACK DDB 0 هندل (مشخصه ) فایل HANDLE DW 0 اسم فایل برای دخیره موقت داده ها FILENAME DB 'C:TTEMP.$$$'/0 MSG1 DB 'ENTER A DISK INTO DRIVE A :THEN PRESS A KEY'/13/10/'$'MSG2 DB 'ENTER A NEW DISK INTO DRIVE A :THEN PRESS A KEY'/13/10/'$'رویه ReadTrack داده های یک شیار را بطور کامل میخواند . برای خواندن یک شیار کامل از Int 13h/Ah=02h استفاده کرده ایم . داده ها بعد از خوانده شدن در محلی که با ES:BX مشخص میشود ذخیره میشوند . (به مرجع اینتراپیتها مراجعه کنید) قبلا کار با این وقفه را توضیح داده ایم (برنامه Boots.asm را ببینید) READTRACK PROC ;READ A TRACKPUSH ESMOV AX/DSMOV ES/AXLEA BX/BUFMOV AH/2MOV DL/0 ;DRIVE A:MOV DH/SIDEMOV CH/TRACKMOV CL/1 ;THE 1st SECTORMOV AL/SECTOR_PER_TRACKINT 13HPOP ESRETREADTRACK ENDPاین رویه داده های موجود در BUF را خوانده و در یک شیار کامل که با متغیر Track مشخص میشود مینویسد . برای اینکار از INT 13h/AH=03h استفاده شده است . آدرس متغیر BUF را باید در ES:BX قرار بدهیم . WRITETRACK PROCLEA BX/BUFPUSH ESMOV AX/DSMOV ES/AXشماره تابع برای نوشتن MOV AH/03 تعداد سکتورها برای نوشتن MOV AL/SECTOR_PER_TRACK شماره تراک MOV CH/TRACK شماره سکتور شروع MOV CL/1 رویه دیسک (طرف دیسک ) MOV DH/SIDE شماره درایو که اینجا A است MOV DL/0 INT 13HPOP ESRETWRITETRACK ENDPاین پروسیجر به اندازه یک تراک کامل از فایل خوانده و در متغیر BUF قرار میدهد READFILE PROCMOV BX/HANDLEاندازه یک تراک MOV CX/SECTORSIZE*SECTOR_PER_TRACK آدرس بافر برای ذخیره که DS:DX است LEA DX/BUF MOV AH/3FHINT 21HRETREADFILE ENDPاین پروسیجر کلیه داده های داخل BUF که به اندازه یک تراک کامل (18*512 بایت ) است را خوانده و در فایل مینویسد تا بعدا مجددا خوانده و روی دیسک جدید بنویسد WRITEFILE PROCMOV BX/HANDLEMOV CX/SECTORSIZE*SECTOR_PER_TRACKLEA DX/BUFMOV AH/40HINT 21HRETWRITEFILE ENDPمنتظر میماند تا کلیدی فشرده شود WAIT PPROCتابع خواندن کلید MOV AH/0 INT 16HRETWAIT _ENDPاین رویه فایل با هندل مشخص شده را میبندد CLOSEFILE PROC MOV AH/3EHMOV BX/HANDLEINT 21HRETCLOSEFILE ENDP شروع برنامه اصلی . MAIN:در این قسمت اعذم میکنیم که دیسکی را در درایو A قرار دهده و کلیدی را برنند . MOV AH/9LEA DX/MSG1INT 21Hمکث برای دریافت کلید _CALL WAIT ساختن فایل برای ذخیره داده ها MOV AH/3CHLEA DX/FILENAMEMOV CX/0INT 21HMOV SIDE/0MOV HANDLE/AXMOV TRACK/1موتور دیسک خوان مدت زمانی لازم دارد تا به سرعت کافی برسد . بنا براین باید یک یا دو بار قبل از خواندن دیسک ، تابع خواندن را اجرا کنیم تا موتور دیسک در حالت مناسب قرار بگیرد. CALL READTRACK ; START UP THE CASSETTE-MOTORCOPY:MOV TRACK/0COPYTRACK:خواندن شیار CALL READTRACK نوشتن داده های خوانده شده در دیسک CALL WRITEFILE شیار بعدی INC TRACK آیا شیار80 هستیم / CMP TRACK/80 نه ، شیار بعدی TRACKS َ; COPY 80 JNZ COPYTRACK طرف بعدی دیسک INC SIDE آیا طرف دوم دیسک هستیم ? CMP SIDE/1 نه ، پس ادامه بده JZ COPY وگر نه فایل را ببند CALL CLOSEFILE حالا اعلام میکنیم که دیسک جدید را در درایو A قرار دهد و کلیدی را بزند MOV AH/09HLEA DX/MSG2INT 21HCALL WAIT_MOV SIDE/0همان فایل را برای خواندن باز میکنیم . وقتی که فایلی را میسازیم تنها میتوانیم در آن فایل بنویسیم . بنا براین برای خواندن از فایل ، باید آن را بسته و مجددا برای خواندن باز کنیم . LEA DX/FILENAMEMOV AH/3DHMOV AL/0INT 21Hمشخصه فایل در Handle قرار میگیرد MOV HANDLE/AX MOV TRACK/1MOV SIDE/0اجرای تابع نوشتن برای راه اندازی موتور دیسک CALL WRITETRACK WRITE:MOV TRACK/0WRITE_ON_TRACK:داده هارا از فایل بخوان CALL READFILE داده ها را روی شیار بنویس CALL WRITETRACK شیار بعدی INC TRACK آیا شیار 80 هستیم ? CMP TRACK/80 نه ، پس ادامه بده JNZ WRITE_ON_TRACK بله ، طرف بعدی دیسک INC SIDE آیا الان طرف دوم را هم خوانده ایم ? CMP SIDE/1 نه ، پس شیار بعدی را بنویس JZ WRITE بله ، فایل را ببند CALL CLOSEFILE فایلی که ساخته بودیم فضائی از دیسک سخت را اشغال کرده ، بنا براین بهتر است آن را با استفاده از وقفه 21h و تابع 3Ah حذف کنیم . LEA DX/FILENAMEMOV AH/3AHINT 21H ;ERASE THE TEMPORARY FILEINT 20HEND START خوب ، رجیسترها را دیدیم و آشنائی کلی با آنها پیدا کردیم . حالا میخواهیم به رجیتسرها مقدار بدهیم و آنها را بخوانیم و ... . ما معمولا در ےزبانهای دیگر از علامت =(یا =ا:) برای مقدار دهی استفاده میکنیم ولی در زبان ےاسمبلی این کار ممکن نیست . در عوض از دستورالعمل MOV کمک میگیریم . با MOV میتوانیم داده ها را بین رجیسترها یا خانه های حافظه انتقال بدهیم . به این صورت MOV in_it/Value در اینجا In_it به معنای یک رجیستر، نام یک متغیر، یا آدرس یک مکان از حافظه است و Value هم یک مقدار عددی یا حرفی ، نام یک رجیستر و ... میباشد . ےمانند MOV AX/200 که عدد 200 دسیمال را به رجیستر AX منتقل میکند . (همیشه از سمت راست به چپ )در زبان اسمبلی ما میتوانیم با مبناهای 2وَ10وَ16 کار کنیم . اعداد به طور پیش فرض مبنای 10 هستند . برای نشان دادن عدد هگزا (مبنای 16) در انتهای عدد یک حرف H ( یا h ) و در انتهای اعداد باینری علامت (b) قرار میدهیم . مثلا برای نشان دادن عدد AC1 مبنای 16 باید حتما آن را بصورت AC1h بنویسیم . به همین ترتیب عدد110bهمان عدد 6 خودمان است . با این تفاسیر برای دادن مقدار 4Ch به رجیستر AX از دستور زیر استفاده میکنیم : mov ax/4Chبه همین شکل میتوانیم به نیم ثباتها هم مقدار بدهیم . مثلا میتوانیم برای مقدار دهی AH بنویسیم : mov ah/20h . در این حالت مقدار نیم ثبات AL ثابت بوده و محتوای AH برابر 20h میشود . چون نیم ثباتها تک بایتی هستند ما نمیتوانیم عدد خارج از محدوده 0 تا 255 یا 128- تا 127 به آنها ارسال کنیم . در مورد اعداد منفی هم باید از طریق تبدیل به مکمل دو عمل کنیم که به زودی آن روش را توضیح خواهیم اد . مثلا ما نمیتوانیم mov ah/100h را انجام دهیم چون 100h برابر 256 بوده و از محدوده تعریف شده خارج است . به همین شکل میتوانیم محتوای ثباتها را هم منتقل کنیم . مثلا برای کپی کردن محتوای ثبات CXبه DX میتوانیم بنویسیم : mov dx/cx ، یعنی مقدار داخل Cx را به Dx کپی کن . ےباز هم باید به یک یا دوبایتی بودن ثباتها توجه کنیم . به عبارت دیگر ما ےنمیتوانیم مقدار یک ثبات تک بایتی را به یک ثبات کامل دوبایتی منتقل کنیم . مثلا عبارت mov DX/AL قابل قبول نیست چون AL یک بایتی بوده و DX دوبایتی است .به عبارت ساده و کامل تر دو طرف عملوند MOV باید از نظر اندازه برابر باشند. بنابر این : MOV DL/ALو MOV CL/BHوM درست ولی MOV DS/AH نادرست است . به علاوه ما فقط میتوانیم ثباتهای همه منظوره AXتا DX را به این صورت مقدار دهی ےکنیم . در صورتی که بخواهیم ثباتهائی مثل ..DS/ES/ را مقدار دهی کنیم باید از رجیستر AX به عنوان واسطه استفاده کرده و مقدار را از طریق آن انتقال دهیم . مثلا: نمیتوانیم بنویسیم mov ds/20h ولی میتوانیم داشته باشیم : mov ax/20h mov ds/ax ےبه این ترتیب مقدار 20hبه DS انتقال پیدا میکند ( گرچه تغییر دادن DS ایده خوبی نیست !) ےحالا مطالب گفته شده را تمرین میکنیم . ما میتوانیم با DEBUG اسمبلی بنویسیم و حتی برنامه های COM. درست کنیم . بنا براین در DOS، DEBUG، را اجرا کنید . D:\LNG\ASM> DEBUG ےیک خط تیره به صورت - ظاهر میشود . این خط تیره اعلان DEUBG برای وارد کردن دستورات است . حرف A (به معنی شروع وارد کردن دستورات اسمبلی ) را وارد کرده و Enter را بزنید .ےعددی بصورت xxxx:0100 ظاهر میشود . این عدد برای شما (فعلا) مهم نیست ، پس به آن توجه نکنید . حالا میتوانید دستورات زیر را وارد کنید : MOV AX/100 MOV BX/AX MOV ES/AX بعد از وارد کردن خط آخر یکبار دیگر کلید Enter را بزنید تا اعلان (-) دوباره ظاهرشود . در سطر اول ما عدد 100h ( پیش فرض اعداد در Debug هگزا است ) را به AX منتقل کردیم . بعد مقدار AXبه BX و سپس مقدار AXبه ES منتقل شده . به این ترتیب همه ثباتهای AX/BX/ES باید در نهایت برابر 100h باشند . برای دیدن صحت این مطلب دستور T ( به معنای Trace) را وارد کنید . با هر بار اجرای این دستور یکی از سطرهای برنامه اجرا میشود . بعلاوه شما میتوانیدمحتوای رجیسترها را هم ببینید . با اولین فرمان T ، سطر اول اجرا میشود . بازهم فرمان T را وارد کنید . الان مقدار100hبه BX داده شد و اگر به محتوای رجیستر AX توجه کنید خواهید دید که مقدار آن (همانطور که انتظار داشتیم ) برابر 100h است . دوبار دیگر هم فرمان T را صادر کنید و در نهایت مقدار ثباتهای AX/BX/ES را ببینید . هر سه ثبات (حالا) برابر 100hهستند . برای خارج شدن از Debug هم فرمان Q به معنی Quit را وارد کنید . ****** پس امروز یاد گرفتیم گه چطور مقادیر و داده ها را بین ثباتها منتقل کنیم . خودتان همین تمرینات را با DEBUG انجام داده و در مورد MOV مطالعه کنید . در قسمت بعد چیزهای بیشتری رو خواهیم خواند و یاد خواهیم گرفت تا اینجا یاد گرفتیم که چطور مقادیر را بین ثباتها منتقل کنیم : با فرمان MOV. با همین دستور میتوانیم مقادیر را از محلهای حافظه خوانده یا در آنجا بنویسیم . برای کار با حافظه دوحالت ممکن است وجود داشته باشد : 1- آدرس مورد نظر در سگمنت جاری باشد . در برنامه های COM. کل برنامه (غالبا) از یک سگمنت تشکیل میشود . 2- آدرس مورد نظر خارج از سگمنت جاری باشد . ثبات DS همیشه به قطعه ای اشاره میکند که داده های مورد نیاز برنامه در آن هستند . این قطعه در برنامه های EXE. یک قطعه مستقل است ولی در برنامه های COM. ، قطعه داده های و قطعه کد برنامه در یک سگمنت هستند . بنا براین مقدار ثبات DS در یک برنامه COM. ثابت است . در حالت کلی آدرس یک محل از حافظه بصورت DS:address مشخص میشود. DS حاوی آدرس سگمنت داده ها بوده و address آفست را مشخص میکند . چون همانطور که گفتیم DS در برنامه های COM. ثابت است ، پس در صورتی که آدرس مورد نظر در همین قطعه باشد از نوشتن DS صرفنظر میکنیم . به عنوان مثال اگر قطعه داده های برنامه ما 9000h باشد و ما بخواهیم آفست 24h ام در همین قطعه را بدست بیاوریم ، میتوانیم از یکی از دو شکل زیر استفاده کنیم : DS:24h or 24h البته چون اسمبلر منظور ما از نوشتن عدد 24h را نخواهد فهمید شکل دوم یک خطای هنگام ترجمه تولید خواهد کرد ولی ما روش صحیح را هم خواهیم گفت . ما آدرس ها (یا اشاره گرها) را برای این میخواهیم که بتوانیم به یک خانه از حافظه دسترسی پیدا کنیم . برای اینکه نشان بدهیم منظور ما از عدد مشخص شده ، آدرس است نه خود عدد (مثل 24h در مثال قبلی ) آن عدد را داخل [] قرار میدهیم . بنا براین : mov ah/24h عدد 24h را به AX منتقل میکند ولی .... mov ah/[24h] محتوای آفست 24h را به AX منتقل میکند . در شکل دوم هر مقداری که در آفست 24h ام سگمنت جاری موجود باشد به ثبات Ah منتقل میگردد. به همین صورت میتوانیم یک مقدار را به یک خانه از حافظه منتقل کنیم : mov [24h]/ah: محتوای ثبات AH را به آفست 24h ام منتقل میکند . ے اگر آدرس مورد نظر خارج از محدوده سگمنت جاری بوده و در قطعه ای جدا قرار داشته باشد ، میتوانیم از DSیا ESا (ترجیحا) برای دستیابی به حافظه استفاده کرد: مثال : mov ax/9000h mov ds/ax mov ah/ds:[89h] به این ترتیب ما به آفست 89h از سگمنت 9000h دسترسی پیدا میکنیم . البته دستورات فوق مارا به مقصودمان میرسانند ولی ما نمیتوانیم به دلخواه خودمان DSرا تغییر دهیم چون همانطور که گفتیم DS به قطعه داده های برنامه اشاره میکند وبرنامه ، داده ها و مقادیر متغیر ها را از سگمنتی که با DS مشخص شده میخواند . بنا براین ما نباید مقدار DS را تغییر بدهیم مگر اینکه آن را دوباره به حالت اول برگردانیم . برای ذخیره و بازیابی محتوای رجیسترها، یک روش ساده و عمومی وجود دارد که به زودی خواهیم گفت ولی در این مثال ما میتوانستیم مقدار قبلی DS را در یک رجیستر دیگر مثل CX نگهداریم : انتقال محتوای dsبه AX mov ax/ds انتقال محتوای AXبه CX mov cx/ax دادن مقدار9000hبه AX mov ax/9000h انتقال محتوای AXبه DS mov ds/ax خواندن آدرس mov ah/ds:[89h] بازیابی مقدار DS mov ax/cx mov ds/ax اگر بخواهیم آفست آدرس را با یک رجیستر مشخص کنیم باید به نکات زیر توجه کنیم : 1- اگر آدرس سگمنت با DS مشخص شده ، یا آدرس در سگمنت جاری باشد ، باید مقدار آفست را در ثبات BX قرار دهیم . مثلا mov cx/[BX]یا mov cx/ds:[bx]ا . 2- اگر از ES به عنوان مقدار سگمنت استفاده میشود باید از DI به عنوان آفست استفاده کنیم مثل mov cx/es:[di] . چون ما با برنامه های COM. سرو کار داریم ، پس از شکل اول و BX استفاده خواهیم کرد . دستیابی به مکانهای حافظه نکته های جالب دیگری هم دارد که در قسمت بعدی یاد خواهیم گرفت . وقتی که ما به روش گفته شده مقداری را از حافظه میخوانیم ، یک داده تک بایتی از حافظه گرفته میشود . اما ممکن است بخواهیم که یک کلمه یا کلمه مضاعف ( 4بایتی ) را بخوانیم یا بنویسیم . در این صورت میتوانیم از پیشوند های زیر استفاده کنیم : Byte Ptr: برای دست یابی به یک بایت Word Ptr: برای دستیابی به یک کلمه (2بایت ) Dword Ptr: برای دست یابی به یک مقدار 4 بایتی این پیشوند ها را باید قبل از آدرس مورد نظر قرار دهیم . به عنوان مثال برای خواندن یک بایت از آفست 10h میتوانیم بنویسیم : mov al/byte ptr ds:[10h] و برای خواندن دو بایت بصورت : mov ax/byte ptr ds:[10h] . میتوانیم از همین روش استفاده کرده و مقداری را به حافظه انتقال دهیم . مثلا میخواهیم یک کلمه دوبایتی را به آفست 34h (در سگمنت برنامه ) منتقل کنیم . کافی است بنویسیم : mov word ptr [34h]/1FCAh . مثال : mov bx/34h mov ax/ds mov cx/axmov ax/00h mov ds/ax mov ax/word ptr ds:[bx] mov ax/cx mov ds/ax جمع و تفریق بحث ما در مورد روشهای دستیابی و انتقال داده ها (فعلا) به پایان میرسد . حالا میخواهیم ببینیم که چطور عمل جمع و تفریق ، و بعدا ضرب و ... ، را روی مقادیر انجام دهیم . دستورالعمل ADD به میزان خواسته شده به محتوای یک رجیستر یا متغیر اضافه میکند .ےمثلا ADD AH/20 عدد 20 را به AH اضافه کرده و مجددا در AH قرار میدهد . اگر مقدار فعلی AH برابر 30 باشد بعد از اجرای آن دستور برابر 50 میشود . باید توجه کنیم که حاصل بدست آمده از محدوده مجاز تجاوز نکند . در این مثال اگر حاصل جمع عدد 20 با محتوای AH بزرگتر از 255 باشد ، خطای سرریز (Over Flow) رخ میدهد . مثال : این دستورات را در دیباگ وارد کنید : mov ax/5 add ax/4 int 20 (به معنی سطر آخر توجه نکنید) . حالا یکبار دیگر Enter را بزنید تا خط اعلان Debug ظاهر شود . حرف G را بزنید تا برنامه شما اجرا شود . حالا فرمان آشنای R را برای دیدن محتوای رجیسترها وارد کنید و مقدار AX را ببینید . دستورالعمل SUB برعکس ADD بوده و به مقدار خواسته شده از محتوای یک ثبات یا متغیر کم میکند . مثلا SUB AX/100h به اندازه 256 (100h) از AX کم کرده و نتیجه رادوباره در AX قرار میدهد . مثال : mov bbx/100h SUB bx/50 در این مثال حاصل bx را از 100 به 50 کاهش داده ایم . فرمان INC یک حالت خاص از ADD بوده و تنها یکواحد به محتوای ثبات اضافه میکندمثلا inc cx یعنی یک واحد به cx اضافه کن . و برعکس این ، دستور dec یکواحد از محتوای ثبات کم میکند . مانند : dec cx . ے باید توجه کنیم که این دستورات تنها روی ثباتهای همه منظوره DX.AX.D قابل استفاده هستند . پس امروز مطالب مربوط به اینها رو یاد گرفتیم : byte ptr / word ptr / dword ptr add / sub / inc / dec وقفه ها (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 تولید میکند . ما امروز اولین برنامه اسمبلی خودمان را نوشتیم ، در قسمت بعد یاد میگیریم که چطور از اسمبلر استفاده کنیم و امکانات آن را بکار ببریم در این قسمت طرز استفاده از ماکرواسمبلر را یاد میگیریم و برنامه هایمان را بدون استفاده از Debug مینویسیم . برای استفاده از اسمبلر باید یک ادیتور اسکی مثل EDITیا PE2ا داشته باشید تا بتوانید برنامه هایتان را توسط آن تایپ کنید . هر برنامه اسمبلی دارای یک فایل منبع (Source) حاوی دستورالعملهای اسمبلی است . ما این فایل را با یک ویرایشگر تایپ کرده و به ماکرواسمبلر MASM.EXE میدهیم تا فایل مفعولی (OBJ.) آن را بسازد . این فایل هم باید با برنامه Link.exe به فرم EXE. تبدیل شود . چون ما میخواهیم برنامه های COM. بتویسیم باید فایل exe. تولید شده را با EXE2BIN.COMیا EXE2COMا به فرم com. تبدیل کنیم . فرض کنید در محیط ویرایشگر(مثلا EDIT ) هستیم و میخواهیم یک برنامه اسمبلی بنویسیم . هر برنامه از 3 قطعه (سگمنت ) تشکیل میشود : 1-قطعه داده ها یا DATA SEGMENT . متغیرهای برنامه و سایر داده های مورد نیاز در این سگمن قرار میگیرند . 2- قطعه کد یا Code Segment . کدها و دستورات اسمبلی در این قسمت هستند . 3- بخش انباره یا Stack Segment . این قطعه زیر برنامه ها و مقادیر موقتی را نگهداری میکند . ما حتی میتوانیم محتوای ثباتها را به پشته (Stack) منتقل کرده و بعد دوباره از آن خارج کنیم . در یک برنامه COM. قطعه داده ها و قطعه مد در یک سگمنت قرار دارند بنا براین ما قطعه داده ها را تعریف نمیکنیم . بعلاوه قطعه سگمنت هم برای یک فایل COM. وجود ندارد بلکه خود DOS این محیط را فراهم میکند . به همین دلایل است که نوشتن برنامه های COM. آسانتر است . با این حال ما با محدودیتی مواجه هستیم و آن اینست که سایز یک برنامه COM. نمیتواند بیش از 64 کیلو بایت باشد . فرض کنید میخواهیم همان برنامه ای که صدای Beepتولید میکرد را با اسمبلر بنویسیم پس یک فایل (مثلا bell.asm) میسازیم : EDIT BELL.ASM حالا ما در محیط ویرایشگر هستیم . برنامه ما به این شکل خواهد بود : . MODEL SMALL . CODE MOV AH/0EH MOV AL/7 INT 10H INT 20H END در سطر اول ، جمله model small. یک رهنمود مترجم است . رهنمودهای مترجم کداجرائی نیستند ولی اسمبلر را در ترجمه برنامه راهنمائی میکنند . MODEL SMALL. به اسمبلر میگوید که ما میخواهیم برنامه com. بنویسیم و قطعه داده ها و کدها مشترک است . این جمله باید همیشه وجود داشته باشد. CODE. میگوید که قسمت کدهای اجرائی شروع میشود . ما باید همیشه دستوراتمان را بعد از یک CODE. شروع کنیم و در انتها نیز جمله END را به معنی اتمام برنامه بنویسیم . بعد از اتمام این مراحل از ویرایشگر خارج شده و با MASM.EXE فایل برنامه را ترجمه میکنیم : MASM BELL.ASM در پرسشهای masm کلید enter را بزنید . اگر برنامه را صحیح تایپ کرده باشید بایداین پیغامها را دریافت کنید :Microsoft( R )Macro Assembler Version 5.10 Copyright( C )Microsoft Corp 1981/ 1988 .All rights reserved.50084 + 396073 Bytes symbol space free 0 Warning Errors 0 Severe Errors حالا فایل BELL.OBJ ساخته شده و باید آن را لینک کنیم : LINK BELL.OBJ و نتیجه این خواهد بود:Microsoft( R )Overlay Linker Version 3.69 Copyright( C )Microsoft Corp 1983-1988 .All rights reserved.:Run File [ASM6.EXE] فقط Enter بزنید | :List File [NUL.MAP] :Libraries [.LIB] LINK : warning L4021 :no stack segment سطر آخر یک پیغام خطا است ولی دقیقا همان چیزی است که انتظار داریم . یعنی وجود نداشتن قطعه پشته (Stack) . به همین دلیل برنامه EXE. تولید شده توسط Link قابل اجرا نیست . پس با EXE2COM آن را به یک فایل COM. تبدیل میکنیم . EXE2COM BELL.EXE و داریم :EXE2COM Version 1.0( - c )Computer Magazine ASM6.EXE converted to ASM6.COM( 8 Bytes )Warning :Program begins at Offset 0( Entry point .)ASM6.COM cannot be called directly! الان فایل COM. هم تولید شد ولی EXE2COM میگوید که ما نمیتوانیم برنامه را فراخوانی و اجرا کنیم . چرا!? اگر بیاد داشته باشید وقتی میخواستیم در DEBUG اسمبلی بنویسیم ، دستوراتمان همیشه از آدرس xxxx:0100h شروع میشد. دلیل آن اینست که DOS همیشه یک فضای 256 بایتی بنام PSP در ابتدای برنامه ایجاد کرده و اطلاعات فوق العاده مهمی را در آن نگهداری میکند . بنا براین برنامه ما باید حتما از آدرس 100h شروع شود . این قانون اسمبلر برای نوشتن برنامه های COM. است . پس کد برنامه را به شکل زیر اصلاح کنید : . MODEL SMALL . CODE دستورالعمل جدید ORG 100H MOV AH/0EH MOV AL/7 INT 10H INT 20H END راهنمای Org 100hبه DOS میگوید که برنامه باید از آدرس 100h شروع شود . ما این کد را اجبارا در همه برنامه ها قرار خواهیم داد . حالا برنامه را با تغییرات اعمل شده ذخیره کرده و با انجام مراحل قبلی دوباره ترجمه کنید . پس از ترجمه فایل BELL.COMرا اجرا کرده و نتیجه را مشاهده کنید % امروز برنامه ای با اسمبلر نوشیتم . از این پس نیز تمام برنامه های را بااسمبلر مینویسیم و از توانائیهای آن استفاده میکنیم . پرشهای غیر شرطی ے اگر با زبانهائی مثل Basicیا Pascalا برنامه نویسی کرده باشید حتما از دستور Gotoے هم استفاده کرده اید . بوسیله این فرمان ، ما میتوانستیم روال اجرای برنامه را به یک نقطه مشخص انتقال بدهیم بدون اینکه نیاز به برقراری شرط خاصی باشد . در زبان اسمبلی هم چنین دستوری داریم : دستورالعمل JMP (مخفف JUMP) . دستور JMP به این شکل استفاده میشود: برچسب JMP ے منظور از برچسب مکانی از برنامه است . در اسمبلی برای اینکه یک نقطه از برنامه ے را علامت بزنیم ، نام برچسب مورد نظر را مینویسیم و برای اینکه اسمبلر آن را با ے یک دستورالعمل اجرائی اشتباه نکند، کاراکتر (:) را در مقابل آن قرار میدهیم مانند: :Start سپس میتوانیم با دستور JMP به آنحا پرش کنیم : Start :: : Jmp Start دقت کنید که بعد از Start ی که در مقابل JMP نوشتیم علامت : قرار نداده ایم . ے این JMP ها از نوع JUMP NERA هستند و نوعی دیگر بنام JUMP FAR هم داریم که بزودی آن را هم یاد میگیریم . ے مثال : برنامه ای که در مثالهای قبل نوشتیم را در نظر بگیرید . اگر ما از روی دستورالعملهای برنامه با JMP پرشی انجام دهیم هیچکدام از آن کدها اجرا نخواهندشد: 1] JMP Quit _2] mov ax/0E07h 3] int 10h 4] Quit :_5] int 20h برنامه از روی سطرهای 2وَ3 پرش خواهد کرد . ثبات پرچم (Flags) ے ثبات پرچم یک ثبات 16 بیتی است که 1یا 0ا بودن بیتهای آن نشانه درست یا ے نادرست بودن یک شرط است . مثلا اگر با دستورالعمل خاصی (میخوانیم ) تست کنیم که آیا ثبات BX مقدار 0 را دارد ، در این صورت بیت 6 برابر 0 میشود و ... . از این 16 بیت فقط 9 بیت استفاده میشود که به شرح زیر هستند : 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 * * * * * O D I T S Z * A * P * C علامت * به معنای بی استفاده بودن است . ے 1- پرچم نقلی یا (CF) . بیت 0 در نتیجه اجرای وقفه ها یا بعضی اعمال حسابی تغییرمیکند . ے 2-پرچم توازن (ZF) . بر اساس یک عمل مقایسه ای یا حسابی تغییر میکند . اگر نتیجه یک عبارت 0 باشد مقدار 1 و اگر نتیجه 1 باشد مقدار 0 میگیرد. ے 3-پرچم وقفه (IF) . اگر 0 باشد هیچ وقفه ای نمیتواند اجرا شود و


دسته‌بندی نشده

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

کلمه کلیدی را وارد کنید :

پاسخ دهید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *