1. مقدمة
إذا كنت قد درست C فإنك تعرف ما معنى الدوال.
إذا كنت قد درست Pascal فإنك تعرف ما معنى الدوال الرجعية واللارجعية.
إذا كنت قد درست BASIC فإنك تعرف ما معنى كلمة SUB.
إذا كنت تعرف الخوارزم فإنك تعرف ما معنى الدوال.
تعريف عام : الدوال عبارة عن شُفَيرات أو شفرات تابعة للمشروع أو للشفرة الرئيسية.
الهدف : تسهيل بنية الشفرة أو المشروع ، وإذا وجد خطأ في المشروع فمن السهل تحديد مكانه.
ميزة الدوال : من خلال دراستنا للمجمع رأينا أن الخزانات تعتبر بمثابة المتغيرات، ونظرا لأهميتها فمن الواجب عند إنجازنا لمشروع معين يجب الأخذ بعين الاعتبار إرجاع قيم الخزانات (التي كانت تحتويها منذ البداية) إليها، حتى لا نمس حركيتها في النظام. وهنا تبرز أهمية أدوات المكدس PUSH و POP وغيرهما.
2. الدالة
2.1 الكتابة العامة
الكتابة الخوارزمية | الكتابة في المجمع |
اسم_الدالة(المداخيل...) : النوع ..... نهاية_الدالة |
<المداخيل> |
أمثلة :
مثال 1 : أنشئ دالة تقوم باستدعاء النظام الهندسي (الدالة 13h للقاطعة 10h)
modevga: |
مثال 2 : أنشئ دالة تقوم باستدعاء النظام النصي (الدالة 03h للقاطعة 10h)
modetext: |
2.2 استدعاء الدالة
لاستدعاء الدالة داخل المشروع أو الشفرة نستعمل الأداة CALL، ونكتب مثلا:
call modetext |
2.3 أين نضع الدوال
يمكن وضع الدوال في بداية المشروع أو في نهايته أو في ملف آخر نقوم باستدعائه.
مثال كامل 1 : نضع الدوال في آخر الشفرة
;NASM-IDE ASM Assistant Assembler Project File |
مثال كامل 2 : نضع الدوال في أول الشفرة
;NASM-IDE ASM Assistant Assembler Project File JMP albidaya ; اقفز إلى بداية الشفرة |
مثال كامل 3 : نضع الدوال في ملف آخر ثم نستدعيه بالأداة include%
ننشئ ملفا يحمل اسما معينا مثلا kiki.inc ونكتب فيه جميع الدوال التي نريدها. قم بحفظ الملف في المجلد الذي يحمل الاسم INCLUDE.
;Ismo almilaf howa: kiki.inc |
نستدعي بعد ذلك هذا الملف في المشروع الذي نود إنجازه.
;.text و .bss و .data ملاحظة : يمكنك تغير ترتيب القطع call modevga |
3. الدالة الكاملة (أو النظيفة)
مثال 1 : أنشئ دالة تقوم باستدعاء النظام الهندسي (الدالة 13h للقاطعة 10h)
modevga: |
نلاحظ أننا إذا استدعينا الدالة modevga فإن الخزان AX سيتغير محتواه، لأنها ستستعمله بطبيعة الحال.
ولتفادي هذا المشكل نستعمل أدوات المكدس للحفاظ على محتواه الأصلي أي باستعمال push و pop .
نكتب في النهاية :(هذا الشكل يسمى بالدالة الكاملة أو النظيفة)
modevga: |
مثال 2 : حالة دالة تستعمل خزانين أو ثلاث
دالة غير كاملة | دالة كاملة |
gotoxy: |
gotoxy: |
مثال 3 : حالة دالة تستعمل كثير من الخزانات
في هذه الحالة لا نستعمل push و pop مكررة بعدد الخزانات، ولكن نستعمل pusha و popa لتفادي ذلك.
دالة غير كاملة | دالة كاملة |
gotoxy: |
gotoxy: |
4. متغيرات الدالة
4.1 الطريقة العادية أو المرور عن طريق الخزان
مسألة: أنجز دالة تمكن من إظهار أي حرف يريده المستعمل.
الحل : الدالة 0Eh للقاطعة 10h تمكن من إظهار حرف على الشاشة.
الطريقة العادية | لكن يمكن أن ننشئ دالة ثم نستدعيها متى نريد | نستعمل إذا هذين السطرين عوض 3 سطور كلما احتجنا إلى ذلك |
mov ah,0eh |
putchar : |
mov al,<الحرف> |
في هذا المثال نلاحظ أن محدد هذه الدالة هو al الذي نضع فيه الحرف المراد إظهاره.
ملاحظة : قد تتطلب الدالة أكثر من محدد واحد، في هذه الحالة نضع كل الخزانات التي تتغير باستمرار قبل أن نستدعي الدالة.
4.2 استعمال قيمة المتغير
مسألة: أنجز دالة تمكن من حساب متوسط متغيرين N1 و N2 وضع النتيجة في المتغير res.
الدالة moyenne | استعمالها داخل المشروع أو الشفرة |
moyenne : |
mov ax,[N1] |
4.3 استعمال عنوان المتغير
مسألة: أنجز دالة تمكن من حساب متوسط متغيرين N1 و N2 وضع النتيجة في المتغير res.
(راجع الفقرة 9 التي تخص العنونة ولاحظ الفرق بين كلمة المحتوى والعنوان للمتغير)
نستعمل في هذه الحالة الخزانات المؤشرة أو القاعدة كـ SI و DI و ...
الدالة moyenne | استعمالها داخل المشروع أو الشفرة |
moyenne : |
mov si,N1 ;N1 نأخذ عنوان |
4.4 المرور عن طريق المكدس
طريقة Pascal
هذه الطريقة تخص دوال اللغة Pascal.
الشكل العام : لتكن x و y و z ثلاث محددات لدالة معينة. لدينا إذن:
push X |
إذا كان المحدد X مثلا عبارة عن متغير نكتب push word [x] عوض push x لكن مع تحديد حجم التحويل قبل [x].
(راجع الفقرة 10 التي تخص المكدس).
نعتبر المثال التالي : لننشئ دالة تقوم بحساب متوسط متغيرين N1 و N2 ونضع النتيجة في المتغير res.
الدالة moyenne |
الدالة moyenne داخل المشروع |
moyenne: |
..... |
شرح مفصل :
-
يجب أن تعرف أولا وقبل كل شيء أن كلمة ret تعني retour أي رجوع ، حيث أنك عندما تريد أن تستدعي دالة معينة يقوم المجمع بحفظ العنوان الذي وصل إليه عند تنفيذ المشروع في المكدس قبل أن يذهب لتنفيذ الدالة وذلك حتى يتسنى له استرجاع العنوان الذي تم تخزينه في المكدس ليواصل من خلاله تنفيذ المشروع.
-
إن الأرقام من 1 إلى 12 الملونة بالوردي تعني لنا المراحل التي يمر منها المكدس، وإليك إذن هذه المراحل:
المراحل من 1 إلى 5 |
المرحلة 9 |
المرحلة 10 |
المرحلة 11 |
المرحلة 12 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
الحالة البدئية | بعد المرور من المراحل 6 إلى 8 التي تعني بعض المعالجات فقط نقوم بحفظ النتيجة خلال المرحلة 9 في الخانة الأولى للمكدس التي كانت تحتوي على القيمة N1 | في هذه المرحلة نسترجع القيمة الأصلية لـ BP الذي استعملناه للدخول وتغيير محتوى المكدس. | ret تعني الرجوع أي أن IP يأخذ العنوان الذي سيبدأ المجمع بمواصلة تنفيذ المشروع، أما 2 فتعني استخراج خانة واحدة (ثمنين) بعد IP وطرحها جانبا (بمعنى أدق طرح الخانة التي تحمل N2 خارجا) | في هذه المرحلة يأخذ المتغير res النتيجة التي بقيت في المكدس. |
لو لاحظت جيدا لرأيت أن الدالة moyenne غير كاملة أو غير نظيفة، لأننا استعملنا الخزان AX دون أن نحفظ قيمته الأصلية، وبالتالي ستصبح الدالة moyenne على الشكل التالي :(دالة كاملة)
moyenne: |
طريقة C
هذه الطريقة تخص دوال اللغة C.
الشكل العام : لتكن x و y و z ثلاث محددات لدالة معينة. لدينا إذن:
push X |
طريقة C شبيهة إلى حد ما لطريقة Pascal إلا أن هنا فرق في إفراغ المكدس، حيث نقوم بتفريغه في نهاية الدالة أي مع ret مباشرة (مثلا ret 2) إلا أن طريقة C تفرغه بعد الخروج من الدالة مباشرة.
لنأخذ المثال السابق ونلاحظ الفرق:
الدالة moyenne |
الدالة moyenne داخل المشروع |
moyenne: |
..... |
5. تفصيل مهمة
قد لا تستوعب لماذا كتبنا ret 2 في طريقة Pascal أو add sp,2 في طريقة C.
أولا وقبل كل شيء : يجب أن تعرف أن تمارين الفقرة القادمة ' فقرة الجداول والمتسلسلات الحرفية' مليئة جدا بالدوال، ولذلك يجب أن تركز جيدا في هذه الفقرة على مبدئها.
5.1 المكدس
لو رجعنا إلى فقرة المكدس لرئينا أن كل خانة في المكدس حجمها هو : ثمنين ( تذكر هذا جيدا )
وكذلك عند تعاملنا مع خانات المكدس يمكننا استعمال الخزان المؤشر bp الذي يأخذ قيمة خزان المكدس sp حتى يتسنى لنا أن نعالج المعطيات الموجودة داخل خانات المكدس.
5.2 إنشاء الدوال
لإنشاء دالة معينة لدينا عدة طرق، والطريقة الآمنة هي أن تنجز دالة نظيفة وكاملة،
لماذا يا ترى ؟
سؤال وجيه جدا : من خلال تجربتي الخاصة كنت لا أكثرت أن أنشئ دالة نظيفة أو لا، لكن عندما وضعت هذه الدوال في مكتبة ثم استدعيها كلما احتجت إليها كانت تقع لي أخطاء بليغة جدا ، هذه الأخطاء ليس في الشفرة أي لا يكتشفها المجمع، لكني اكتشفت بنفسي أن استعمالي لبعض الخزانات التي أحفظ فيها قيما معينة يتم تغييرها من طرف الدوال غير النظيفة ==> فقمت حينها بإعادة جميع الدوال.
5.3 الأداة ret
كما قلنا سابقا أن الأداة ret تعني الرجوع.
غالبا ما نقوم بإدخال القيم ذات الحجم ثمنين إلى المكدس.
قيمة ذات الحجم ثمنين
عند إدخالنا قيمة واحدة ذات الحجم ثمنين : نكتب في نهاية الدالة ret 2 حيث (ret تعني إرجاع قيمة IP التي تم تخزنها نلقائيا في المكدس و 2 تعني طرح خانة ذات الحجم ثمنين )
أمثلة :
مثال 1 |
function1: |
push ax |
مثال 2 |
function2: |
push word adress |
مثال 3 |
function3: |
push word [valeur] |
قيمتين أو أكثر ذات الحجم ثمنين
عند إدخالنا لـ N قيمة ذات الحجم ثمنين : نكتب في نهاية الدالة ret 2N حيث (ret تعني إرجاع قيمة IP التي تم تخزنها نلقائيا في المكدس و 2N تعني طرح N خانة ذات الحجم ثمنين )
أمثلة :
مثال 1 |
function1: |
push ax |
مثال 2 |
function2: |
push word adress1 |
مثال 3 |
function3: |
push word [valeur] |
انتبه جيدا: قيمة أو أكثر ذات الحجم 4 أثمان
عند إدخالنا قيمة ذات الحجم 4 أثمان : نكتب في نهاية الدالة ret 4 حيث (ret تعني إرجاع قيمة IP التي تم تخزنها نلقائيا في المكدس و 4 تعني طرح خانتين ذات الحجم ثمنين أو بمعنى آخر خانة ذات الحجم 4 أثمان )
أمثلة :
مثال 1 |
function1: |
push eax |
مثال 2 |
function2: |
push dword [adress2] |
مثال 3 |
function3: |
push ax |
ملاحظة مهمة
كما رأينا أعلاه أننا نطرح جميع خانات المكدس التي تم ملئها من طرف القيم التي نود إدخالها.
سؤال : ماذا لو أردنا أن نحتفظ بالخانة الأخيرة التي نود أن نضع في قيمة أو نتيجة ما، حتى يأخذها متغير أو خزان آخر من خلال المشروع الذي سننجزه؟
الجواب : يكفي أن تطرح 2 من العدد الموجود أمام الأداة ret مباشرة.
مثال :
الدالة moyenne |
الدالة moyenne داخل المشروع |
توضيح |
moyenne: |
..... |
كما نلاحظ أننا أدخلنا متغيرين اللذان هما : N1 و N2 ، إذا كان علينا أن نكتب في النهاية ret 4، لكننا كتبنا فقط ret 2 ، وذلك حتى إذا خرجنا من الدالة moyenne نود أن نضع النتيجة التي بقيت في آخر خانة بالمكس في المتغير res حيث نكتب : pop word [res] |
6. مساعدة
لم أكن أود أن أمنحك هذه الدالة التي تقوم بإظهار الأعداد على الشاشة لأنني كنت أودك أن تصل إلى الفقرة التالية "الجداول والمتسلسلات الحرفية" التي تحتوي على نصف تمارين المجمع حيث ستكون هذه التمارين على هذا المنوال، أي إنجاز دوال لإظهار الأعداد والجمل إلى آخره ...
المهم : لإظهار الأعداد على الشاشة يمكنك استعمال الدالة oktob16 الموجودة في المكتبة entier.inc التي وضعتها في المجلد INCLUDE.
لإظهار أي عدد موجب من 0 إلى 65535 يجب وضعه في الخزان AX ثم استدعي الدالة oktob16 بالأمر call.
مثال للاستعمال :
مثال لجمع عددين |
mov bx,10 |
النتيجة على الشاشة |
10+20=30 |
تمارين تطبيقية
التمرين 13.1
أنجز شفرة تقوم بجمع عددين واستعمل:
- الطريقة العادية (المرور مباشرة)
- استعمال قيمة المتغير
- استعمال عنوان المتغير
- المرور عن طريق المكدس
- طريقة Pascal
- طريقة C
التمرين 13.2
أعد كتابة الشفرة التالية المكتوبة باللغة C أو المكتوبة باللغة Pascal في المجمع NASM باستعمال الطريقة التي تريدها.
في C | في Pascal |
char C; |
var C: char; |
التمرين 13.3
أعد كتابة الشفرة التالية المكتوبة باللغة C أو المكتوبة باللغة Pascal في المجمع NASM باستعمال الطريقة التي تريدها.
في C | في Pascal |
char ch[20]="Salamo 3alaikom"; |
var ch: string[20]; |
التمرين 13.4
أعد كتابة الشفرة التالية المكتوبة باللغة C أو المكتوبة باللغة Pascal في المجمع NASM باستعمال الطريقة التي تريدها.
في C | في Pascal |
char ch[30]="Jomla men fadlek\n"; |
var ch : string[30]; |
التمرين 13.5
أعد كتابة الشفرة التالية المكتوبة باللغة C أو المكتوبة باللغة Pascal في المجمع NASM باستعمال الطريقة التي تريدها.
في C | في Pascal |
char i; |
var i : integer; |
التمرين 13.6
أعد كتابة الشفرة التالية المكتوبة باللغة C أو المكتوبة باللغة Pascal في المجمع NASM باستعمال الطريقة التي تريدها.
في C | في Pascal |
char C = '0'; |
var i : integer; |
التمرين 13.7
أعد كتابة الشفرة التالية المكتوبة باللغة C أو المكتوبة باللغة Pascal في المجمع NASM باستعمال الطريقة التي تريدها.
في C | في Pascal |
char C; |
var C: char; |
التمرين 13.8
أنجز دالة تقوم بإبدال محتوى متغيرين من نوع db يحملان حرفين.
استعمل دالة لقراءة حرفين من لوحة المفاتيح ووضعهما في المتغيرين (كـ ()getchar).
استعمل دالة لإظهار محتوى المتغيرين قبل وبعد الإبدال (كـ ()putchar).
استعمل كذلك دالة لإظهار جملة على الشاشة (كـ ()printf).
مثال :
Harfaine men fadlek : |
التمرين 13.9
أنجز دالة تقوم بمعرفة طبيعة محتوى متغير من نوع db يحمل حرفا، هل هو حرف كبير أو صغير أو رقم أو رمز.
استعمل دالة لقراءة حرف من لوحة المفاتيح ووضعه في المتغير.
استعمل كذلك دالة لإظهار جملة على الشاشة.
مثال :
Harf men fadlek : A |
Harf men fadlek : 5 |
Harf men fadlek : ! |
Harf men fadlek : d |
التمرين 13.10
أنجز دالة تقوم بتحويل الحرف الكبير إلى صغير وصغير إلى كبير. لكن بشرط أن تكون هذه الدالة تستعمل دالتين أُخريين إحداهما تقوم بتحويل الحرف الكبير إلى صغير والأخر بالعكس وكذلك دالة لإظهار حرف على الشاشة . لا تنس حالة أن يدخل المستعمل رمزا أو رقما
استعمل دالة لقراءة حرف من لوحة المفاتيح ووضعه في المتغير.
استعمل كذلك دالة لإظهار جملة على الشاشة.
مثال :
Harf men fadlek : A |
Harf men fadlek : f |
Harf men fadlek : ? |
Harf men fadlek : 6 |