شرح الأخطاء التي تقع في البرامج و طريقة إصلاحها تجعلك خبير في البرمجة
وهنا أشرح أخطاء و مشاكل مهمين يمكن أن تعترظك في البرمجة بالأسمبلي
المشكل الرئيسي هو في كتابة الأرقام ذو حرفين أو أكثر (مثل 10,200,5000,10000...)
طرح مشكلة تطبيقية :
نختار رقم في برنامجنا (سأختار 5), نطلب من المستعمل أن يدخل أي رقم
*-إذا كان الرقم مخالف للذي إخترناه, نطبع رسالة خطأ تقول
بأنه ليس الرقم الصحيح ثم يعاود الإدخال
*-إذا كان نفس الرقم الذي إخترناه, نطبع رسالة أنه وجد الرقم الصحيح وكذلك عدد مرات المحاولات
قم بإنجاز برنامج يحل هذه المشكلة ثم إرجع إلى هذا الموضوع و سترى الأخطاء التي يتظمنها برنامجك
الأخطاء التي يمكن أن يعترضك:
أظن أن المشكلة سهلة, لكن الصعب هو في إظهار عدد مرات المحاولات
أنت تقول لماذا ؟ (نستطيع إستعمال أي مسجل كعداد وكل مرة يدخل فيها رقم, نضيف 1 إلى المسجل العداد)
أنا أيضا قلت نفس الكلام لكن المشكلة ليس في حساب عدد مرات المحاولات بل المشكل في كتابة عدد مرات المحاولات
أنت تقول لماذا ؟ (نستطيع أن نطبع مباشرة عدد المرات من المسجل العداد)
أنا أيضا قلت هذا الكلام لكن ظهر خاطأ, وسأضع لك أكواد كل الأخطاء التي مررت بها
وإليك البرنامج الأول الذي كتبته لحل المشكلة :
.model small
.code
Msg1 db 0Ah,"Enter a number in [0..9] :",0Ah,0Dh,"$"
Msg2 db 0Ah,"Congratulation!, You find the right number",0Ah,"$"
Msg3 db 0Ah,"Your number is wrong, try again",0Ah,"$"
Msg4 db 0Ah,"Number Of trying is : ","$"
start:
PUSH CS
POP DS
MOV CL,00h
OPERATION:
MOV AH,9h
MOV DX,OFFSET Msg1
INT 21h
MOV AH,1h
INT 21h
CMP AL,35h
JNZ WRONG
INC CL
MOV AH,9h
MOV DX,OFFSET Msg2
INT 21h
MOV AH,9h
MOV DX,OFFSET Msg4
INT 21h
MOV AH,2h
MOV DL,CL
INT 21h
MOV AH,7h
INT 21h
MOV AH,4Ch
INT 21h
WRONG:
MOV AH,9h
MOV DX,OFFSET Msg3
INT 21h
INC CL
JMP OPERATION
end start
بعد ما إنتهيت, قلت هذا هو الحل J, عندما جربت البرنامج وجدته يشتغل بطريقة صحيحة لكن في عدد مرات المحاولات دائما يطبع لي رموز مثل ☺
هل هذا عدد المرات, طبعا لا لأن عدد المرات سيكون أرقام ليس رموز
راجعت الكود عديد المرات, وتوصلت بأن الخطأ في السطر التالي:
MOV CL,00h
وهو عندما وضعت القيمة 00 في CL (عداد مرات المحاولات)
لنفترظ أننا حاولنا 9 مرات لندخل الرقم الصحيح (من المفروض أن يطبع حرف 9)
قيمة CL ستصبح 09هيكس, وعند طباعة محتواه سيطبع البرنامج كود ASCII لــ 09هيكس
رجعت إلى الكود ASCII, وجدت أن مقابل 9هيكس في الأحرف هو :
Char
HEX
DEC
TAB (horizontal tab)
9
9
حسنا, أظن أن الخطأ أصبح واضحا أنا لا أريد طباعة TAB بل حرف 9
بحثت قليلا في الكود ASCII عن حرف 9 ووجدت:
Char
HEX
DEC
9
39
57
عندها توضح لي الخطأ أن حرف 9 يساوي 39هيكس و ليس 9هيكس
الذي يحمله CL
تصحيح الخطأ هو أن نضع القيمة 30هيكس في CL (30هيكس = حرف 0)
يعني يصبح هكذا :
MOV CL,30h
أو يمكن ان نضع مباشرتا حرف 0 هكذا
MOV CL,'0'
بعد تصحيح الخطأ جربت البرنامج, فتحته وحاولت 9مرات لأدخل الرقم الصحيح وفعلا طبع حرف 9. أنا قلت هذا هو الحل النهائي
فتحت البرنامج مجددا وحاولت 10مرات لأدخل الرقم الصحيح و النتيجة طبع لي حرف ":"
لماذا ؟ : نعلم أن كود ASCIIلحرف 9 يساوي 39هيكس, ونحن حاولنا 10مرات يعني قيمة CL (عداد مرات المحاولات) ستصبح :
39هيكس + 1هيكس = 3Aهيكس
نلقي نظرة على مقابل 3A هيكس في الحروف :
Char
HEX
DEC
:
3A
58
عرفنا سبب حرف ':'
نحن الآن نريد أن يطبع لنا 10, كيف ؟ (هذه هي المشكلة الكبيرة)
10 يتكون من حرفين (في كود ASCII لايوجد شيئ يتكون من حرفين)
الحل هو أن نكتب كل حرف وحده يعني نكتب 1 ثم نكتب 0 ليظهر لنا 10 في الشاشة. لكن كيف؟
إليك الفكرة :
هنا سنستعمل عدادين, واحد للآحاد و الآخر للعشرات. يعني عندما يصل عداد الآحاد إلى آخر حرف في الأرقام وهو 9 نضيف 1 لعداد العشرات و نصفر عداد الآحاد ليبدأ من جديد, وفي الأخير نطبع عداد العشرات أولا ثم عداد الآحاد
أنا إخترت CL كعداد للأحاد و CH عداد للعشرات, إليكم الكود الذي كتبته:
.model small
.code
Msg1 db 0Ah,"Enter a number in [0..9] :",0Ah,0Dh,"$"
Msg2 db 0Ah,"Congratulation!, You find the right number",0Ah,"$"
Msg3 db 0Ah,"Your number is wrong, try again",0Ah,"$"
Msg4 db 0Ah,"Number Of trying is : ","$"
start:
PUSH CS
POP DS
MOV CL,30h ; نضع حرف 0 في عداد الأحاد
MOV CH,30h ; نضع حرف 0 في عداد العشرات
OPERATION:
MOV AH,9h
MOV DX,OFFSET Msg1
INT 21h
CMP CL,39h ; نقارن الآحاد مع حرف 9
JG COUNTER ; إذا كان أكبر إقفز إلى COUNTER
MOV AH,1h
INT 21h
CMP AL,35h ; نقارن الحرف المدخل مع 5
JNZ WRONG ; إذا كان غير مساوي إقفز إلى WRONG
INC CL ; إذا كان مساوي نضيف 1 إلى عداد الأحاد
MOV AH,9h
MOV DX,OFFSET Msg2
INT 21h
MOV AH,9h
MOV DX,OFFSET Msg4
INT 21h
MOV AH,2h ;\
MOV DL,CH ;| نطبع عدد العشرات
INT 21h ;/
MOV AH,2h ;\
MOV DL,CL ;| نطبع عدد الآحاد
INT 21h ;/
MOV AH,7h
INT 21h
MOV AH,4Ch
INT 21h
WRONG:
MOV AH,9h
MOV DX,OFFSET Msg3
INT 21h
INC CL ; إذا كان الرقم المدخل غير مساوي نضيف 1 إلى عداد الآحاد
JMP OPERATION
COUNTER:
MOV CL,30h ; إذا كان عداد الآحاد أكبر من حرف 9 نضع فيه حرف صفر
INC CH ; ثم نضيف 1 إلى عداد العشرات
JMP OPERATION ; نقفز إلى OPERATION
end start
مثل كل مرة أقول أن هذا هو الحل النهائي, فتحت البرنامج وجربت أن أدخل 14 مرة رقم خاطأ ثم أدخل الرقم الصحيح يعني عدد مرات المحاولات أصبح 15
وبالفعل طبع لي 15مرة. J
أردت أن أتأكد, أدخلت 9مرات رقم خاطأ ثم أدخلت الرقم الصحيح يعني عدد مرات المحاولات هو 10, فطبع لي "0:" L, هذا ليس عدد المرات الصحيح
لنحلل المشكل :
لنفترض أننا وصلنا إلى عدد مرات المحاولات 9 هنا قيمة عداد الآحاد تساوي 39هيكس (39هيكس = حرف 9)
ثم أدخلنا الرقم الصحيح, و لأوضح هذه المرحلة سنقوم بعملية فحص الكود وتتبعه خطوة خطوة لنراقب التغيرات التي تحصل في كلا العدادين لنكشف الخطأ. وذلك من خلال هذا الجدول :
لنفترض أن قيمة CL تساوي 39 و قيمة CH تساوي 30
في هذا التنفيذ سندخل الرقم الصحيح يعني عدد مرات المحاولات ستصبح 10
حالة المسجلات
عداد الآحاد : CL
عداد العشرات : CH
حالة القفز
الكود بالأسمبلي
39
30
لايوجد
CMP CL,39h
39
30
لايقفز
JG COUNTER
30
لايوجد
MOV AH,1h
INT 21h
هنا ندخل الرقم 5
39
30
لايوجد
CMP AL,35h
39
30
لايقفز
JNZ WRONG
40
30
لايوجد
INC CL
40
30
لايوجد
يطبع لنا عداد العشرات
30هيكس
حرف 0
40
30
لايوجد
يطبع لنا عداد الآحاد
40هيكس
حرف :
في النهاية سيطبع لنا :0
أظن أن المشكل أصبح واضح مكانه من خلال الجدول. قيمة CL أصبحت 40 عندما نفذ أمر INC CL, ونعلم عندما تفوق قيمة CL 39 نقفز إلى إجراء يصفر CL و يضيف واحد لــ CH ليعدل العدادات.
لكن في الكود لايوجد إجراء يقارن حالة المسجلات ليعدل قيم العدادت بعد تنفيذ أمر INC CL وذلك عند إدخال الرقم الصحيح
كيف نصلح هذه المشكلة :
بعد أمر INC CL نضع أمر يقوم بمقارنة CL مع القيمة 39هيكس(حرف 9) ثم أمر يقفز إذا كانت أكبر. حيث سيصبح كود البرنامج كالتالي :
.model small
.code
Msg1 db 0Ah,"Enter a number in [0..9] :",0Ah,0Dh,"$"
Msg2 db 0Ah,"Congratulation!, You find the right number",0Ah,"$"
Msg3 db 0Ah,"Your number is wrong, try again",0Ah,"$"
Msg4 db 0Ah,"Number Of trying is : ","$"
start:
PUSH CS
POP DS
MOV CL,30h
MOV CH,30h
OPERATION:
MOV AH,9h
MOV DX,OFFSET Msg1
INT 21h
CMP CL,39h
JG COUNTER
MOV AH,1h
INT 21h
CMP AL,35h
JNZ WRONG
INC CL
;----------------------------------------------
; هنا سأضع الإجراء الجديد, لاحظ بعد تعليمة INC CL
;----------------------------------------------
CMP CL,39h ; نقارن الآحاد مع حرف 9
JG COUNTER ; إذا كان أكبر إقفز إلى COUNTER
MOV AH,9h
MOV DX,OFFSET Msg2
INT 21h
MOV AH,9h
MOV DX,OFFSET Msg4
INT 21h
MOV AH,2h ;\
MOV DL,CH ;| نطبع عدد العشرات
INT 21h ;/
MOV AH,2h ;\
MOV DL,CL ;| نطبع عدد الآحاد
INT 21h ;/
MOV AH,7h
INT 21h
MOV AH,4Ch
INT 21h
WRONG:
MOV AH,9h
MOV DX,OFFSET Msg3
INT 21h
INC CL ; إذا كان الرقم المدخل غير مساوي نضيف 1 إلى عداد الآحاد
JMP OPERATION
COUNTER:
MOV CL,30h ; إذا كان عداد الآحاد أكبر من حرف 9 نضع فيه حرف صفر
INC CH ; ثم نضيف 1 إلى عداد العشرات
JMP OPERATION ; نقفز إلى OPERATION
end start
لنجرب البرنامج, أدخل 9 مرات رقم خاطأ ثم أدخل الرقم الصحيح.
و النتيحة أن في المرة العاشرة وهي التي أدخلنا فيها الرقم الصحيح يعاود يقول لك أدخل رقم, ندخل الرقم الصحيح مجددا فيطبع لنا عدد المحاولات 11
ملاحظة :
*- البرنامج عندما أدخلنا الرقم الصحيح في المحاولة 10 يعني قيمة عداد الآحاد فاقت حرف 9 فتجاهل البرنامج أننا أدخلنا الرقم الصحيح و إنشغل في تعديل قيم العددات ثم عاد ليقول لنا أدخل رقم
*- لاحظ أنه لم يطبع لنا رسالة الرقم خاطأ, هذا يعني أنه لم يعد ليكمل الروتين OPERATION
ولمعرفة مكان الخطأ كما فعلنا سابقا, سنتتبع البرنامج خطوة خطوة من خلال هذا الجدول :
قيمة CL تساوي 39 و قيمة CH تساوي 30
في هذا التنفيذ سندخل الرقم الصحيح يعني عدد مرات المحاولات ستصبح 10
حالة المسجلات
عداد الآحاد : CL
عداد العشرات : CH
حالة القفز
الكود بالأسمبلي
39
30
لايوجد
OPERATION:
39
30
لاوجد
3 أسطر :
لطباعة رسالة أدخل رقم
39
30
لايوجد
CMP CL,39h
39
30
لايقفز
JG COUNTER
39
30
لايوجد
MOV AH,1h
INT 21h
هنا ندخل رقم 5
39
30
لايوجد
CMP AL,35h
39
30
لايقفز
JNZ WRONG
40
30
لاوجد
INC CL
40
30
لايوجد
CMP CL,39h
40
30
يقفز
JG COUNTER
40
30
لايوجد
COUNTER:
30
30
لايوجد
MOV CL,30h
30
31
لايوجد
INC CH
30
31
يقفز
JMP OPERATION
في النهاية سيقفز البرنامج إلى OPERATION وسيقول لك أدخل رقم ثم تدخل رقم وقوم يبنفس الروتين الذي شرحته لكن قيم العددات تغيرت
بعد هذا الجدول إكتشفت أن البرنامج فيه الكثير و الكثير من الأخطاء وهي:
الخطأ الأول : مكان أمر مقارنة عداد الآحاد و القفز
الخطأ الثاني : عدد مرات أمر إضافة 1 إلى عداد الآحاد و أمكنتها
الخطأ الثالث : مكان الرجوع بعد تعديل حالة العددات
أنت الآن تقول : ماهذه الأخطاء, هذا الكاتب خاطئ في كلامه الخطأ ليس هنا. في الحقيقة الحق معك لأن هذه الأخطاء لن تظهر لك إلا إذا تمعنت كثيرا في الكود و نفذته خطوة خطوة من أوله إلى آخره.
سوف أبذل مجهود لكي أوضح لك الأخطاء, لكن يجب أن تتمعن في الكود مرارا و تكرارا لكي تفهم شرح هذه الأخطاء
الخطأ الأول :
في الكود لدينا أمرين مقارنة لعداد الآحاد يليهما القفز لتعديل العددات, الأول في بداية روتين OPERATION و الثاني بعد إدخال الرقم الصحيح.
في الحقيقة مقارنة واحدة تفي بالغرض. نحن إستعملنا 2, و أسوء من هذا أن المقارنة الثانية زادتنا مشكلة و هي عندما تستهلك 10 محاولات لإدخال الرقم الصحيح يتجاهل البرنامج أنك أدخلت الرقم الصحيح لأنه سيقفز إلى إجرائية تعديل قيم العددات ثم يعاود طلب إدخال رقم (فسرت هذه المشكلة سابقا)
أنت الآن تقول كيف علمت هذا ؟ , لو جربت تنفيذ البرنامج خطوة خطوة في العشر محاولات لإدخال الرقم صحيح ستعرف كيف عرفت هذا.
تصحيح الخطأ :
كما قلت مقارنة واحدة تفي بالغرض, نحذف المقارنة الثانية و نضع الأولى قبل مقارنة الرقم المدخل مع الرقم الصحيح
; نضع المقارنة هنا
CMP AL,35h
JNZ WRONG
INC CL
ستصبح هكذا :
CMP CL,39h
JG COUNTER
CMP AL,35h
JNZ WRONG
INC CL
إنتهينا من الخطأ الأول تماما
الخطأ الثاني :
لدينا أمرين يقومان بإضافة 1 إلى عداد الآحاد, الأول عندما يكون الرقم المدخل صحيح و الثاني عندما يكون الرقم المدخل خاطأ
الأول هنا :
CMP AL,35h
JNZ WRONG
INC CL ; أمر إظافة 1 إلى عداد الآحاد
والثاني هنا :
WRONG:
MOV AH,9h
MOV DX,OFFSET Msg3
INT 21h
INC CL ; أمر إظافة 1 إلى عداد الآحاد
JMP OPERATION
حسنا, التمرين يطلب منا أن نحسب عدد المرات التي إستهلكها المستعمل لكي يدخل الرقم الصحيح و بدون إستثناء (يعني نحسب عدد المرات التي أدخل فيها المستعمل رقم سوى كان الرقم صحيح أو خاطئ)
المنطق يقول أمر واحد يظيف 1 إلى عداد الآحاد يفي بالغرض و يكون بعد أن يدخل المستعمل أي رقم يعني بعد دالة إدخال حرف. نحذف الأمرين الذين يزيدان قيمة CL ونعوضهما بالتالي :
MOV AH,1h
INT 21h
; هنا نضع الأمر الذي يضيف 1 إلى عداد الأحاد
الكود يصبح كالتالي :
MOV AH,1h
INT 21h
INC CL
وهكذا حللنا مشكلة إظافة 1 لعداد الآحاد و مكان وضعه
الخطأ الثالث :
من المفروض عند تعديل العددات يجب أن يعود البرنامج إلى السطر الذي يلي القفزة إلى إجراء تعديل العددات (نستطيع إستعمال CALL لتتوفر لنا هذه الخاصية أوتوماتيكيا, و هذا لو أننا في مستوى متقدم L)
هنا سنعمل على الكود الذي نقحناه و ليس القديم وهو التالي :
.model small
.code
Msg1 db 0Ah,"Enter a number in [0..9] :",0Ah,0Dh,"$"
Msg2 db 0Ah,"Congratulation!, You find the right number",0Ah,"$"
Msg3 db 0Ah,"Your number is wrong, try again",0Ah,"$"
Msg4 db 0Ah,"Number Of trying is : ","$"
start:
PUSH CS
POP DS
MOV CL,30h
MOV CH,30h
OPERATION:
;--------------------------
; كل الأكواد التي غيرنا فيها
; سأكتب أمامها تغيير
;--------------------------
MOV AH,9h
MOV DX,OFFSET Msg1
INT 21h
MOV AH,1h
INT 21h
INC CL ; تغيير
CMP CL,39h ;\
JG COUNTER ;/ تغيير
CMP AL,35h
JNZ WRONG
MOV AH,9h
MOV DX,OFFSET Msg2
INT 21h
MOV AH,9h
MOV DX,OFFSET Msg4
INT 21h
MOV AH,2h
MOV DL,CH
INT 21h
MOV AH,2h
MOV DL,CL
INT 21h
MOV AH,7h
INT 21h
MOV AH,4Ch
INT 21h
WRONG:
MOV AH,9h
MOV DX,OFFSET Msg3
INT 21h
JMP OPERATION
COUNTER:
MOV CL,30h
INC CH
JMP OPERATION
end start
لنفترض أننا أدخلنا 9مرات عدد خاطأ و في المرة العاشرة أدخلنا رقم صحيح, قيمة CL ستفوق 39هيكس و لو تتبعنا البرنامج سنجده سينفذ القفزة إلى إجراء تعديل قيم العددات و بعدها سنقفز إلى بداية الروتين OPERATION, يعني سيطلب منا مجددا إدخال رقم رغم أننا أدخلنا الرقم الصحيح ثم يزيد قيمة عداد الآحاد... وبذلك سيتجاهل أننا أدخلنا الرقم الصحيح لأن مكان الرجوع بعد تعديل العددات خاطأ.
تصحيح الخطأ :
كما قلت سابقا أن بعد تعديل العددات يجب أن يعود البرنامج إلى السطر الذي يلي القفزة إلى إجراء تعديل العددات لكي يعود البرنامج إلى إكمال الروتين بمقارنة الرقم المدخل مع الصحيح وبذلك لا يتجاهله ...
هنا يلزمنا Label جديد نضعه بعد قفزة إجراء تعديلات على العددات ولنسميه BACK :
CMP CL,39h
JG COUNTER
BACK:
; بقية الكود
ثم نعدل قفزة العودة من JMP OPERATION إلى JMP BACK
الكود النهائي للبرنامج سيكون كالتالي :
.model small
.code
Msg1 db 0Ah,"Enter a number in [0..9] :",0Ah,0Dh,"$"
Msg2 db 0Ah,"Congratulation!, You find the right number",0Ah,"$"
Msg3 db 0Ah,"Your number is wrong, try again",0Ah,"$"
Msg4 db 0Ah,"Number Of trying is : ","$"
start:
PUSH CS ;\
POP DS ;/ تجهيز المقاطع
MOV CL,30h ;\ القيمة الأولية لعداد الآحاد
MOV CH,30h ;/ القيمة الأولية لعداد العشرات
OPERATION:
MOV AH,9h
MOV DX,OFFSET Msg1
INT 21h
MOV AH,1h
INT 21h
INC CL ; نزود الآحاد 1
CMP CL,39h
JG COUNTER ; يقفز إذا كان CL أكبر من 39هيكس
BACK:
CMP AL,35h
JNZ WRONG ; إذا كان الرقم خاطأ إقفز
MOV AH,9h
MOV DX,OFFSET Msg2
INT 21h
MOV AH,9h
MOV DX,OFFSET Msg4
INT 21h
MOV AH,2h ;\
MOV DL,CH ;| طبع قيمة عداد العشرات
INT 21h ;/
MOV AH,2h ;\
MOV DL,CL ;| طبع قيمة عداد الآحاد
INT 21h ;/
MOV AH,7h
INT 21h
MOV AH,4Ch
INT 21h
WRONG:
MOV AH,9h
MOV DX,OFFSET Msg3
INT 21h
JMP OPERATION
COUNTER:
MOV CL,30h ; نضع القيمة الأولية لعداد الآحاد
INC CH ; نزود عداد العشرات 1
JMP BACK ; نقفز لإكمال الروتين
end start
نجرب البرنامج, نحاول 10 مرات لإدخال الكود الصحيح و النتيجة سيطبع لنا 10. أظن هكذا أصبح الكود صحيحا. لكن هناك مشكل ................, لاتقلق لايوجد أي مشكل كنت أمزح J, لكن لا تدري يمكن يكون هناك خطأ لست أعلم عنه, عموما إن وجدت خطأ أخبرني عنه و سوف أشرحه إن شاء الله.
الحمد لله إنتهى الموضوع
ملاحظة : هذا الموضوع طويل, و أرجو من مشرفي المنتدى أن لا يحذفو الموضوع لهذا السبب.
الموضوع أرهقني جدا أمضيت يومين لكتابته, وذلك لصعوبة الشرح و إيصال المعلومة لذهن القارئ. كنت قادر أن أختصر كل هذا في أسطر معدودة و أعطيكم الكود النهائي لحل هذه المشكلة, لكنني أردت أن أستفيد و أفهم أكثر الأخطاء التي إعترضتني و في نفس الوقت أفيد القارئ من تجربتي حيث بدأت بالكود الأول الذي كتبته مرورا بالتغييرات التي صارت عليه إلى أن وصلت إلى الكود النهائي لحل المشكل.
هل من شخص آخر يشرح لنا الأخطاء التي تعترضه و يفيدنا من تجربته ؟
السلام عليكم و رحمة الله و بركاته
