‏إظهار الرسائل ذات التسميات Assembly. إظهار كافة الرسائل
‏إظهار الرسائل ذات التسميات Assembly. إظهار كافة الرسائل

2015/08/01

الإجراءات الفرعية Procedures في الأسمبلي

السلام عليكم ورحمة الله وبركاته
تتمّة لكود الأستاذ Z3r0n3 في هذه المشاركة
http://arabteam2000-...ر/#entry1107066
أثناء تطبيق استخدام Procedure حدثت معي بعض الأخطاء , ورغبت أن ألخصها للحذر من الوقوع فيها .. ثم رأيت  أنه يمكن استغلالها لكتابة كود بطريقة مبتكرة :)
فهرس المقال :
1- مقدمة عن الإجراء الفرعي وكيفية استخدامه
2- مخاطر الاستخدام الخاطئ للإجراء الفرعي
3- طرق مبتكرة للاستغلال الخاطئ لـ call  و ret

مقدمة عن الإجراءات الفرعية :
الإجراء الفرعي عملياً هو مجرد عملية قفز إلى موضع معين من الكود في الذاكرة وتنفيذ الخطوات حتى الوصول إلى التعليمة ret

ويمكن الاستعاضة عنه تقريباً بتعليمة jmp للقفز إلى بداية الإجراء وتعليمة jmp  أخرى للعودة إلى مكان الاستدعاء .. ولكن استخدام call واستدعاء procedure أسهل بكثير
لا يوجد تعريف للإجراء بشكل صريح داخل الكود , ولكنه يحتوي على تعليمة ret في نهايته غالبا .
مثال على استخدامه :
13DC:0100 mov ah,2
13DC:0102 mov dl,30
13DC:0104 int 21
13DC:0106 call 10D
13DC:0109 int 20
13DC:010B nop
13DC:010C nop
13DC:010D mov dl,31
13DC:010F int 21
13DC:0111 mov dl,32
13DC:0113 int 21
13DC:0115 ret
حيث استخدمنا call نعتبر التعليمات بين العنوان 10D وبين الوصول إلى ret إجراءا فرعيا ..

وسيناريو استدعاء إجراء وتنفيذه هو التالي :
أولا: يتم دفع مسجل مقطع الكود ثم مسجل مؤشر التعليمات (يؤشر على التعليمة التالية لتعليمة الاستدعاء) في المكدس لحفظ المكان الحالي في الكود
       ثم يتم تحميل القيم الجديدة لموضع الإجراء الفرعي الذي نرغب في تنفيذه .
(ملاحظة : في حالة الانتقال إلى إجراء في نفس المقطع يتم دفع مؤشر التعليمات فقط في المكدس .. وهذا ما نناقشه في هذه المشاركة )
ثانياً : يتم تنفيذ التعليمات بشكل طبيعي إلى أن نصل إلى التعليمة ret
ثالثاً : يتم اخراج قيمة مؤشر التعليمات ثم قيمة مسجل مقطع الكود المحفوظين في المكدس إلى مسجلاتهما الخاصة
رابعا : لو قمنا بإعطاء التعليمة ret  وسيطاً , فسيتم زيادة قيمة مؤشر المكدّس بقيمة هذا الوسيط , ثم سنتابع تنفيذ تعليمات الكود التي يشير لها IP بشكل طبيعي .

المخاطر تأتي من استخدام الخطوات السابقة بشكل مختلف كما يلي :

الخطر الأول : إذا قمنا باستخدام المكدّس داخل كود الإجراء الفرعي بشكل خاطئ .. فسيؤدي هذا إلى احتمال عدم رجوعنا إلى مكان الاستدعاء حتى بعد الوصول إلى التعليمة ret
فقد نقوم بتحميل قيم مغايرة لقيم المؤشر ومسجل المقطع إن قمنا بتخزين قيم إضافية في المكدّس ولم نخرجها قبل انتهاء الإجراء الفرعي .

الخطر الثاني : إذا لم نكتب التعليمة ret فسيتم متابعة تنفيذ الكود المخزّن بعد انتهاء الإجراء ..
ولن نرجع إلى الكود الأصلي (الذي قام بالاستدعاء ) إلى في حال واجهنا ret مصادفة .. وطبعا هذا سيؤدي لتنفيذ تعليمات غير مرغوبة

الخطر الثالث : قد نقوم بإعطاء أي قيمة كوسيط للتعليمة ret مما قد يؤدي بنا إلى تجاوز حجم المكدّس كله والانتقال إلى قمته فوراً ..(مثلا لو كان مؤشر المكدس على FFFC وكان الوسيط 20 مثلاً .. فسننتقل إلى 001E )

والآن : ما رأيك في استخدام المخاطر السابقة في زيادة التحكم بالكود :)

----- يمكننا الآن تغيير قيمة المؤشر ip كما نريد بالطريقة التالية :
1- ندفع القيمة الجديدة في المكدس
2- ننفذ التعليمة ret
سيتم تحميل آخر قيمة في المكدّس إلى المسجّل IP ( مؤشر التعليمات ) مباشرة بفضل ret

انظر إلى المثال التالي :
13DC:0100 mov ax,100
13DC:0103 push ax
13DC:0104 ret
المثال السابق يستخدم push  ثم ret  للقفز .. لاحظ أننا نحدد العنوان الحقيقي عوضا عن تحديد الفرق بين عنوان jmp والعنوان المراد الوصول له في الحالة المعتادة .

كما يمكننا استخدام call للقفز مع التضحية بخانة أو اثنتين من المكدّس ..
انظر المثال التالي :
13DC:0100 mov ah,2
13DC:0102 mov dl,41
13DC:0104 int 21
13DC:0106 call 100
الكود السابق يدخل في حلقة لا نهائية من طباعة A ولكنه يخرج بعد امتلاء المكدس بالعنوان 106 :)

يمكننا مثلاً أن نستعمل قيمة العنوان في عملياتنا وذلك كما يلي :
13DC:0100 pop dx
13DC:0101 mov ah,2
13DC:0103 int 21
13DC:0105 nop
13DC:0106 nop
13DC:0107 nop
13DC:0108 nop
13DC:0109 call 100
جعلنا الكود الموجود في 100 إجراء فرعيا يقوم بسحب الIP من المكدس وطباعة المحرف الموافق له
(انتبه : الكود في حلقة لا نهائية ولكنه مجرد توضيح )

هناك عدد غير محدود من الأفكار التي يمكننا بها استغلال خواص ret و call  الفريدة .. وسأختم بالفكرة التالية :
ما رأيك لو نستدعي الإجراء بشكل عودي بدون ret ولكن مع وضع شرط على مؤشر المكدس :)
13DC:0100 call 103
13DC:0103 mov bx,sp
13DC:0105 cmp bx,FFCC
13DC:0108 jle 114
13DC:010A push bx
13DC:010B mov ah,2
13DC:010D mov dl,31
13DC:010F int 21
13DC:0111 call 103
13DC:0114 int 20
اقرأ الكود السابق وحاول معرفة كيف يمكنك تحديد عدد الاستدعاءات ...

فيما يلي مثال يوضّح استخدام الإجراءات .. (الكود يقوم بعملية ضرب رقمين )
;هذا الكود يوضّح عملية ضرب رقمين من الدخل وطباعتهما باستخدام العمليات
;Procedures
; ويمكنك قراءته وفهمه بسهولة بسبب القاعدة : فرّق تسد :) إنها السيطرة
.model small
.stack 100h
.data
.code;كل استدعاء يدل على وظيفته من اسمه
    call read_number
    call write_Multiplication_Sign
    call read_number
    call write_Equal_Sign
    call find_Result
    call print_Result
    call End_program
    
read_number proc
    mov ax,0100h;نقوم بمقاطعة الدخل لحرف واحد
    int 21h
    sub al,30h;نحول الحرف الى رقم
    mov ah,00h;نصفّر الجزء العلوي من المسجل حتى يختفظ المسجّل بشكل كامل بالقيمة لنتمكن من دفعه للمكدّس
    pop bx;المكدّس يحتفظ بقيمة مؤشّر التعليمات لذلك يجب أن نحافظ عليه
    push ax;نصع نتيجة الإجراء في المكدّس
    push bx;ونعيد مؤشر التعليمات الى المكدّس
    ret;ونعود إلى التعليمة التي يشير لها مؤشر التعليمات
read_number endp

write_Multiplication_Sign proc
    mov ah,02h;تخزين قيمة مقاطعة الخرج لحرف واحد
    mov dl,2Ah;تخزين قيمة الآسكي لإشارة الجمع
    int 21h;طباعة الحرف المخزّن في المسجل السابق
    ret;العودة لحيث يؤشّر مؤشر التعليمات
write_Multiplication_Sign endp

write_Equal_Sign proc;نفس الإجراء السابق ولكن مع تغيير قيمة الآسكي لتطبع إشارة المساواة
    mov ah,02h
    mov dl,3Dh
    int 21h
    ret
write_Equal_Sign endp

find_Result proc
    pop cx;نحتفظ بمؤشر التعليمات
    pop ax;نأخذ الرقم الأول من المكدّس
    pop bx;نأخذ الرقم الثاني من المكدّس
    push cx;نرجع مؤشر التعليمات
    call multiply_Ax_Bx;نستعدي إجراء ضرب هذين المسجّلين
    ret
find_Result endp

multiply_Ax_Bx proc
    mul bx;سنستخدم الضرب العادي
    ret
multiply_Ax_Bx endp

print_Result proc;ax;يقوم بطباعة النتيجة الموجودة في
    mov cl,0Ah;نخزن الرقم 10
    div cl;على 10;ax;نقسم
    ;حسب آلية عمل تعليمة القسمة;ah;وباقي القسمة في;al;ستخزّن نتيجة القسمة في
    ;al;والعشرات في;ah;الآحاد في;
    mov dx,ax;
    mov ah,02h;نضع قيمة تعليمة طباعة حرف
    
    add dh,30h;نحوّل الآحاد من رقم إلى حرف
    add dl,30h;نحول العشرات من رقم إلى حرف
    
    int 21h;نطبع العشرات
    mov dl,dh;
    int 21h;نطبع الآحاد
    ;dl;الطباعة تتم للقيمة الموجودة في
    ret
print_Result endp

End_Program proc;يقوم بتنفيذ مقاطعة الخروج من البرنامج
    mov ah,4Ch
    int 21h
    ret
End_Program endp
end
والآن سأبين أهمية "تفصيص" البرنامج لإجراءات ...
تخيّل أننا نريد تغيير الآلية الأساسية لعملية الضرب التي استخدمناها ..
يمكننا ببساطة كتابة إجراء جديد .. بدلا من القديم .. دون المساس بأجزاء أخرى من الكود .. ودون عناء البحث عن موضع عملية الضرب :
الكود القديم :
multiply_Ax_Bx proc
    mul bx
    ret
multiply_Ax_Bx endp
نريد تغييره إلى  :
multiply_Ax_Bx_2 proc
mov cx,ax
mov ax,0
mab2start:
    cmp bx,0
    je ret_
    dec bx
    add ax,cx
    jmp mab2start
ret_:
    ret
multiply_Ax_Bx_2 endp
(قمت بتغيير الاسم لهدف سأبينه لاحقا)
الكود السابق يقوم بالجمع لتنفيذ الضرب ضمن حلقة :)
ما رأيك لو أحببت القيام بالعملية بطريقة أخرى :
multiply_Ax_Bx_3 proc
mov cx,bx
mov bx,ax
mov ax,0
cmp cx,0
je ret_
mab3start:
    add ax,bx
    loop mab2start
ret_2:
    ret
multiply_Ax_Bx_3 endp
الكود السابق يستخدم loop لإجراء الحلقة ..

الآن لدينا 3 إجرائيات تقوم بنفس الوظيفة ولها نفس الواجهة (أي طريقة إدخال الوسطاء والرجوع بالنتيجة )
ولنا حرية اختيار أي منها داخل برنامجنا ... وهذا يبيّن أهمية تجزئة البرنامج ... فرّق تسُد :)

والله ولي التوفيق

الرابط الأصلي

كود أسمبلي تحويل العدد من الترميز العشري إلى الترميز الثنائي

السلام عليكم ورحمة الله وبركاته
تتمّة لبرنامج الأخ Z3r0n3 في هذه المشاركة ..
البرنامج التالي يقوم بأخذ عدد بالترميز العشري وتحويله إلى الترميز الثنائي ولكن بشكل معكوس
وفق الخوارزمية التالية :
1- نقرأ رقماً من العدد (القراءة من اليسار إلى اليمين )
2- إذا كانت القيمة المقروءة هي نهاية السطر ننتقل للخطوة 4
3- نضرب العدد المحفوظ حتى الآن بـعشرة ونضيف له العدد المقروء وننتقل للخطوة 1
4- نقسم العدد على 2 ونطبع باقي القسمة ونكرر الخطوة حتى يصبح العدد صفراً .

وهذا هو الكود :
.model small
.stack 10h
.data
.code
;bx;هو الذي سيحتفظ بالرقم المدخل
GetTheNumber:
    mov ah,01h;تخزين رقم مقاطعة ادخال الحرف من المستخدم
    int 21h;
    cmp al,2Fh;مقارنة الدخل مع القيمة التي تأتي تحت الصفر مباشرة في جدول الآسكي
        jle end_;القفز إذا كانت القيمة أصغر أو تساوي .. أي أنها ليست رقما
    
    mov ch,al; نخزّن الدخل في مسجل آخر
    
    ;ضرب مسجل القاعدة بعشرة
    mov ax,bx;
    mov cl,0Ah;
    mul cl;
    mov bx,ax;
    
    ;نطرح قيمة الآسكي للرقم صفر .. فتصبح القيمة هي قيمة العدد تماما وليس قيمته في الآسكي
    sub ch,30h
    mov cl,ch
    mov ch,00h
    add bx,cx;نضيف المنزلة الجديدة إلى مسجل القاعدة الذي يخزّن القيمة الكلّية

jmp GetTheNumber
        
        end_:
printNewLine:
        mov ah,02h;قيمة المقاطعة الخاصة بطباعة حرف
        mov dl,0Dh;نضع الحرف المراد طباعته في مسجل البيانات
        int 21h;ننفذ المقاطعة ونطبع القيمة
        mov dl,0Ah
        int 21h
end printNewLine
            print:
                mov dx,bx;نضع قيمة العدد في مسجل البيانات
                and dx,1;نختبر إذا كان يقبل القسمة على اثنين
                add dx,30h;ونضيف له رقم الآسكي الخاص بالصفر فيصبح الآن يحتفظ بقيمة الآسكي للواحد أو الصفر
                int 21h;ننفذ مقاطعة طباعة الحرف الموجود في مسجل البيانات
                shr bx,1;نقسم القيمة المحفوظة على اثنين عن طريق الإزاحة
                cmp bx,0;نقارن هل صار الرقم صفرا
                    je kill;ننهي البرنامج في حال انتهاء الرقم
            jmp print;أو نكرر العملية حتى ينتهي الرقم
                    kill:
                        mov ah,4Ch;تخزين قيمة المقاطعة الخاصة بالخروج من البرنامج
                        int 21h;تنفيذ المقاطعة السابقة
end
 والكود التالي يقوم بطباعة العدد بالشكل الصحيح (وليس معكوس ) باستخدام push و pop  في المكدّس .
.model small
.stack 10h
.data
.code
;bx;هو الذي سيحتفظ بالرقم المدخل
GetTheNumber:
    mov ah,01h;تخزين رقم مقاطعة ادخال الحرف من المستخدم
    int 21h;
    cmp al,2Fh;مقارنة الدخل مع القيمة التي تأتي تحت الصفر مباشرة في جدول الآسكي
        jle end_;القفز إذا كانت القيمة أصغر أو تساوي .. أي أنها ليست رقما
    
    mov ch,al; نخزّن الدخل في مسجل آخر
    
    ;ضرب مسجل القاعدة بعشرة
    mov ax,bx;
    mov cl,0Ah;
    mul cl;
    mov bx,ax;
    
    ;نطرح قيمة الآسكي للرقم صفر .. فتصبح القيمة هي قيمة العدد تماما وليس قيمته في الآسكي
    sub ch,30h
    mov cl,ch
    mov ch,00h
    add bx,cx;نضيف المنزلة الجديدة إلى مسجل القاعدة الذي يخزّن القيمة الكلّية

jmp GetTheNumber
        
        end_:
printNewLine:
        mov ah,02h;قيمة المقاطعة الخاصة بطباعة حرف
        mov dl,0Dh;نضع الحرف المراد طباعته في مسجل البيانات
        int 21h;ننفذ المقاطعة ونطبع القيمة
        mov dl,0Ah
        int 21h
end printNewLine
                mov cl,0;نصفّر المسجل الذي سنستعمله كعدّاد
            reverse:;والآن سنقوم بعكس العدد الثنائي
                    mov dl,bl;\;
                    and dl,1 ;| نضع في المكدّس البت الأقل وزنا من العدد المحفوظ
                    push dx  ;/
                    inc cl;نزيد العداد
                    shr bx,1;نقسم العدد على اثنين
                    cmp bx,0;اذا وصلنا للصفر نذهب للطباعة
                    je print
                    jmp reverse;أو نكرر العملية
                print:
                cmp cl,0;اذا صار العداد صفر اخرج
                je kill
                mov ah,02;تخزين قيمة مقاطعة طباعة الرقم
                pop dx;نخرج من المكدّس البتّات بترتيب معاكس للإدخال
                add dx,30h ;نضيف قيمة الآسكي للصفر لنحصل على قيمة الآسكي للرقم المخزّن
                int 21h ;ننفذ مقاطعة طباعة الرقم وستطبع واحد أو صفر
                dec cl ;ننقص العداد
                jmp print
                    kill:
                        mov ah,4Ch ;مقاطعة انهاء البرنامج
                        int 21h
end
 والله وليّ التوفيق

هل المقاطعات هي لبّ الأسمبلي على أنظمة 16 بت ؟

السلام عليكم ورحمة الله وبركاته
سؤالي لا يتعلّق بالتفكير البرمجي بل بالأدوات البرمجية
لي فترة أدرس الأسمبلي 16 بت , وأريد أن أتأكد من أن فهمي صحيح ..
الأدوات التي يوفّرها نظام الدوز للبرمجة فيه هي المقاطعات فقط لا غير ..  وبالمثل تماماً فهو يعتمد على الـ BIOS والذي لا يوفّر (كأدوات جاهزة) سوى المقاطعات ..
(بالمقابل .. يوفّر نظام ويندوز مكاتب ربط ديناميكي dll عديدة تتيح برمجة واجهات والتحكم في الطرفيات و أمور أخرى  )

الهدف  من السؤال :
الصفحة التالية تحوي جميع مقاطعات الدوز والبيوس .. وأريد أن أعرف ماذا ينتظرني في أنظمة 16 بت بعد الانتهاء منها (كدراسة على الأقل )

======================
الإجابة:
كل نظام يوفّر دوال API للمبرمجين عليه .. ونظام DOS يوفر خدمات المقاطعة 21   والـ Bios يوفّر باقي المقاطعات المعروفة

الصفحة التي أرفقتها لا تحوي جميع المقاطعات فهي تحوي فقط المقاطعات التي تمت محاكاتها في الـ Emulater المشروح في الموقع المرفق

إذا كان تعلم Win API هو لب البرمجة في ويندوز فجواب سؤالك : نعم .. المقاطعات هي لب Dos ..

والله ولي التوفيق

أكوادي في أسمبلي 16 بت

السلام عليكم
هذه هي الصفحة المخصصة لـ16 كود أسمبلي 16 بت قمت بكتابتها عند تعلّمي للغة , نبدأ بالوصف ثم الكود:
======================================================
هذا هو كودي الأول ..
برنامج يقوم بطباعة Hello World في حلقة لا نهائية .. وينتظر من المستخدم أن يضغط أي زر في كل دورة للحلقة 
.model small
.stack 100h

.data
msg db "Hello World!",10,13,"$"  ;تعريف متغير يحوي العبارة المطلوبة
;10 ,13 هي محارف نهاية السطر
;$ محرف يدل على نهاية المتغيّر
.code
start: ;دليل يمكننا استعماله للعودة الى هذه النقطة

mov ax,@data ;ax;نحفظ عنوان بداية قسم البيانات في
mov ds,ax  ;data segment;ds;في المسجّل ;ax;نحفظ العنوان الموجود في ;

lea dx,msg ;<==>mov dx,offset msg;dx;وتعني حفظ عنوان المتحول في المسجل

mov ah,9 ;dx;حفظ رقم المفاطعة الخاصة بطباعة النص الموجود عنوانه في
int 21h ;ah;تنفيذ المقاطعة المحفوظة في

mov ah,7;حفظ رقم المقاطعة التي تنتظر حرف من المستخدم
int 21h ;ah;تنفيذ المقاطعة المحفوظة في

jmp start ; قفز إلى الدليل أعلاه
;لن نصل إلى هنا أبدا
mov ax,0c07h;حفظ رقم المقاطعة التي تنهي البرنامج
int 21h ;ah;تنفيذ المقاطعة المحفوظة في

end start; دليل نهاية الملف

======================================================
الكود الثاني :
برنامج يأخذ محرفا من المستخدم .. فإن كان هو الرقم 1 يطبع one وإلا يطبع zero .. ( بمكن تطوير البرنامج لأي عدد من المحارف )
.model small
.stack 100h
.data ;حفظ بعض البيانات
    zer     db 9,"zero",10,"$"
    one     db 9,"one",10,"$"
    ;10; هو رقم الآسكي لنهاية السطر
    ;9;tabهو رقم الآسكي ل;
.code
start:
    mov ax,@data
    mov ds,ax
        
    MOV AH,1;مقاطعة أخذ حرف من المستخدم
    INT 21h

    CMP AL,49;مقارنة الحرف المدخل مع قيمة الآسكي للرقم واحد
    JE ony;اذا كان ناتج المقارنة هو التساوي قم بعملية القفز
;سندخل هنا ان لم تنجح المقارنة
    lea dx,zer;نضع عنوان العبارة 'صفر' في مسجل البيانات
    jmp endy;ونقفز كي لا ننفذ السطر التالي
    ony:
        lea dx,one;نضع عنوان العبارة 'واحد' في مسجل البيانات
    endy:
        mov ah,9;مقاطعة طباعة النص الموجود عنوانه في مسجل البيانات
        int 21h
        
jmp start;العودة لتكرار البرنامج
;لن نصل الى هنا ابدا
mov ax,4c00h
int 21h
end start
======================================================
الكود التالي يقرأ حرف واحد من المستخدم .. ثم يقوم بطباعته .. وينتظر زرا آخر ليخرج
.model small
.stack 100h
.data ;لا يوجد بيانات
.code
start:
    MOV AH,01;مقاطعة أخذ حرف من المستخدم
    INT 21h
    
    MOV DL,AL ;نقل الدخل الى المسجل المستخدم للخرج
    MOV AH,02; تخزين رقم المقاطعة الخاصة بطباعة حرف
    INT 21h

    MOV AH,07h; تخزين رقم المقاطعة الخاصة بإنهاء البرنامج
    INT 21h
    MOV AH,4Ch; تخزين رقم المقاطعة الخاصة بإنهاء البرنامج
    INT 21h
end start
======================================================
وهذا الكود مشابه للسابق .. ولكنه يدخل في حلقة : يطبع كل حرف يدخله المستخدم إلى أن ندخل enter
.model small
.stack 100h
.data ;لا يوجد بيانات
.code
start:
    MOV AH,01;مقاطعة أخذ حرف من المستخدم
    INT 21h

    CMP AL,10;مقارنة مع الآسكي المساوي للانتر
    JE endy;اذا تساوت اقفز الى النهاية
    CMP AL,13;مقارنة مع الآسكي المساوي للانتر
    JE endy;اذا تساوت اقفز الى النهاية
    
;سندخل هنا ان لم تنجح المقارنة
    MOV DL,AL ;نقل الدخل الى المسجل المستخدم للخرج
    MOV AH,02; تخزين رقم المقاطعة الخاصة بطباعة حرف
    INT 21h
jmp start;العودة لتكرار البرنامج
endy:
    MOV AH,4Ch; تخزين رقم المقاطعة الخاصة بإنهاء البرنامج
    INT 21h
end start 
======================================================
الكود الخامس .
البرنامج التالي يأخذ نصّا من المستخدم ويطبعه بشكل معكوس عن طريق تخزينه في المكدس ثم اخراجه :

.model small
.stack 100h
.data
.code
mov ah,01;قيمة مقاطعة أخذ حرف من المستخدم
mov cx,sp;نخزّن قيمة مؤشر المكدس في مسجل العداد
start:
int 21h;تنفيذ مقاطعة قراءة حرف من المستخدم
cmp al,0Dh;هل هو نهاية السطر؟
jle end_
    mov bl,al;نضغ في مسجل آخر لأن الدفع في المكدس يتطلب دفع مسجل 16 بت كاملة
    push bx ;نحفظ الدخل في المكدس
    jmp start
end_:
mov ah,02;تخزين قيمة مقاطعة طباعة حرف
mov dl,0Ah;تخزين قيمة محرف نهاية السطر
int 21h;تنفيذ مقاطعة طباعة الحرف والذي هو نهاية السطر
print:
    cmp cx,sp;طالما لم يصبح مؤشر المكدس كقيمته قبل بدء التخزين
    je end__;اذا اصبح كقيمته القديمة اخرج
    pop dx;أخرج آخر حرف تم تخزينه
    int 21h;ونفذ مقاطعة الطباعة
    jmp print;وكرر العملية
end__:
mov ah,4ch;حفظ قيمة مقاطعة الانهاء
int 21h; تنفيذ مقاطعة الإنهاء
end

_____________________إضافة : __________
لم أستطع منع نفسي من ذكر أن برنامج بلغة BrainF**k للقيام بنفس المهمة هو التالي :
,+[->,+]<[.<]
======================================================
الكود 6 : استخدام طريقة العنونة باستخدام الإزاحة[ disp[bx
;هذا الكود لتوضيح طريقة العنونة التالية
;displacement[bx]
;والتي تشبه الوصول إلى عنصر في مصفوفة
.model small
.stack 100h
.data
    numbers db "ABCDEFGHIJKLMNOPQSTUVWXYZ$";نحفظ الحروف في مقطع البيانات
.code
mov ax,@data;\;نحفظ عنوان مقطع البيانات في مسجل مقطع البيانات
mov ds,ax   ;/

mov ax,0100h;\;مقاطعة قراءة حرف
int 21h     ;/

sub al,30h;تحول الحرف الى رقم
mov ah,0  
mov bx,ax ;نقل الناتج لمسجل القاعدة الذي يُستعمل في العنونة

mov dl,numbers[bx];طريقة العنونة التي تقوم بالوصول للعنصر الذي عنوانه مجموع عنوان المتحول خارج الأقواس مع قيمة المسجل داخل الأقواس
;هو الذي تتم طباعة محتواه في مقاطعة طباعة الحرف;dl;المسجل
;ونضع فيه قيمة أحد الحروف المخزّنة والتي نصل اليها عن طريق عنوان المتحول مضافا اليه إزاحة
mov ax,0200h;مقاطعة طباعة حرف
int 21h

mov ah,4ch;مقاطعة الخروج من البرنامج
int 21h
end
======================================================
 الكود السابع :
الكود التالي يستخدم الإجراءات في عملية ضرب عددين كما أنه ينسّق العملية بشكل مرتب
لاحظ أنه يمكنك فهم الكود من أسماء الاستدعاءات calls دون عناء , ثم تعود إلى كل إجراء بشكل مستقل
مع العلم أنني استخدمت المكدّس لإعادة الناتج , ثم لأخذ الوسطاء في الإجراءات التالية
;هذا الكود يوضّح عملية ضرب رقمين من الدخل وطباعتهما باستخدام العمليات
;Procedures
; ويمكنك قراءته وفهمه بسهولة بسبب القاعدة : فرّق تسد :) إنها السيطرة
.model small
.stack 100h
.data
.code;كل استدعاء يدل على وظيفته من اسمه
    call read_number
    call write_Multiplication_Sign
    call read_number
    call write_Equal_Sign
    call find_Result
    call print_Result
    call End_program
    
read_number proc
    mov ax,0100h;نقوم بمقاطعة الدخل لحرف واحد
    int 21h
    sub al,30h;نحول الحرف الى رقم
    mov ah,00h;نصفّر الجزء العلوي من المسجل حتى يختفظ المسجّل بشكل كامل بالقيمة لنتمكن من دفعه للمكدّس
    pop bx;المكدّس يحتفظ بقيمة مؤشّر التعليمات لذلك يجب أن نحافظ عليه
    push ax;نصع نتيجة الإجراء في المكدّس
    push bx;ونعيد مؤشر التعليمات الى المكدّس
    ret;ونعود إلى التعليمة التي يشير لها مؤشر التعليمات
read_number endp

write_Multiplication_Sign proc
    mov ah,02h;تخزين قيمة مقاطعة الخرج لحرف واحد
    mov dl,2Ah;تخزين قيمة الآسكي لإشارة الجمع
    int 21h;طباعة الحرف المخزّن في المسجل السابق
    ret;العودة لحيث يؤشّر مؤشر التعليمات
write_Multiplication_Sign endp

write_Equal_Sign proc;نفس الإجراء السابق ولكن مع تغيير قيمة الآسكي لتطبع إشارة المساواة
    mov ah,02h
    mov dl,3Dh
    int 21h
    ret
write_Equal_Sign endp

find_Result proc
    pop cx;نحتفظ بمؤشر التعليمات
    pop ax;نأخذ الرقم الأول من المكدّس
    pop bx;نأخذ الرقم الثاني من المكدّس
    push cx;نرجع مؤشر التعليمات
    call multiply_Ax_Bx;نستعدي إجراء ضرب هذين المسجّلين
    ret
find_Result endp

multiply_Ax_Bx proc
    mul bx;سنستخدم الضرب العادي
    ret
multiply_Ax_Bx endp

print_Result proc;ax;يقوم بطباعة النتيجة الموجودة في
    mov cl,0Ah;نخزن الرقم 10
    div cl;على 10;ax;نقسم
    ;حسب آلية عمل تعليمة القسمة;ah;وباقي القسمة في;al;ستخزّن نتيجة القسمة في
    ;al;والعشرات في;ah;الآحاد في;
    mov dx,ax;
    mov ah,02h;نضع قيمة تعليمة طباعة حرف
    
    add dh,30h;نحوّل الآحاد من رقم إلى حرف
    add dl,30h;نحول العشرات من رقم إلى حرف
    
    int 21h;نطبع العشرات
    mov dl,dh;
    int 21h;نطبع الآحاد
    ;dl;الطباعة تتم للقيمة الموجودة في
    ret
print_Result endp

End_Program proc;يقوم بتنفيذ مقاطعة الخروج من البرنامج
    mov ah,4Ch
    int 21h
    ret
End_Program endp
end
======================================================
الكود الثامن . الهدف منه توضيح استخدام الخدمة 2 في المقاطعة 10 .. وهي خدمة نقل مؤشر الكتابة ... بدون مبالغة يمكن باستخدام هذه الخدمة رسم كل أحلامك على الشاشة :) .. الأكواد القادمة ستكون فيها رسومات جميلة :D
.model small
.stack 100h
.data
.code

mov ch,00
mov bl,41
label0:
    mov ah,2h;خدمة نقل المؤشر في المقاطعة 10
    mov dh,ch;نحدد السطر
    mov dl,ch;نحدد العمود
    int 10h;تنفيذ المقاطعة الخاصة بنقل المؤشر

    mov ah,2h;خدمة طباعة حرف في المقاطعة 21
    mov dl,41h;نضع أول حروف الأبجدية في المسجل
    add dl,ch;ونزيد قيمته حسب عداد الحلقة
    int 21h;تنفيذ المقاطعة الخاصة بطباعة الحرف
    
    inc ch;نزيد عداد المؤشر
    cmp ch,30;نقارن هل وصلنا إلى 30
    jne label0;إن لم نصل نقفز عائدين إلى بداية العملية
mov ah,1;خدمة أخذ حرف من المستخدم سنستخدمها للانتظار
int 21h;تنفيذ مقاطعة أخذ الحرف من المستخدم
mov ah,4ch;خدمة إنهاء البرنامج
int 21h;تنفيذ خدمة الإنهاء
end label0
end
======================================================
يا جماعة .. السلام عليكم :)
الكود التالي سيقوم برسم دائرة :) باستخدام تحريك المؤشر (مقاطعة الـBIOS رقم 10 الخدمة 2 )
الفكرة هنا هي في كيفية الاستفادة من db أي تحديد البيانات data التي سنحتاجها في الكود
.model small
.stack 100h
.code
jmp X
;فيما يلي أزواج النقاط أولا السينات ثم العينات _الصادات_تحدد الشكل المطلوب رسمه
;Home Made Circle By Mostafa36a2
db 1,10,1,11,1,12,1,13,1,14,1,15,1,16,1,17,1,18,1,19
db 1,20,2,8,2,9,2,10,2,20,2,21,2,22,3,7,3,8,3,22
db 3,23,4,5,4,6,4,24,4,25,5,4,5,5,5,25,5,26,6,4
db 6,26,7,3,7,27,8,2,8,3,8,27,8,28,9,2,9,28,10,1
db 10,2,10,28,10,29,11,1,11,29,12,1,12,29,13,1,13,29,14,1
db 14,29,15,1,15,29,15,30,16,1,16,29,17,1,17,29,18,1,18,29
db 19,1,19,29,20,1,20,2,20,28,20,29,21,2,21,28,22,2,22,3
db 22,27,22,28,23,3,23,27,24,4,24,26,25,4,25,5,25,25,25,26
db 26,5,26,6,26,24,26,25,27,7,27,8,27,22,27,23,28,8,28,9
db 28,10,28,20,28,21,28,22,29,10,29,11,29,12,29,13,29,14,29,15
db 29,16,29,17,29,18,29,19,29,20
y db 105;متحول يحفظ عدد النقاط
X:
mov bx,@code
mov ds,bx
mov ch,00
mov bx,03
dec bx
label0:
    inc bx
    mov dh,[bx];نحدد السطر
    inc bx
    mov dl,[bx];نحدد العمود
    mov ah,2h;خدمة نقل المؤشر في المقاطعة 10
    int 10h;تنفيذ المقاطعة الخاصة بنقل المؤشر

    mov ah,2h;خدمة طباعة حرف في المقاطعة 21
    mov dl,4Dh;نضع أول حرف آسكي نريد طباعته
    int 21h;تنفيذ المقاطعة الخاصة بطباعة الحرف
    
    inc ch;نزيد عداد المؤشر
    cmp ch,y;قارن هل وصلنا لآخر نقطة
    jne label0;إن لم نصل نقفز عائدين إلى بداية العملية
mov ah,1;خدمة أخذ حرف من المستخدم سنستخدمها للانتظار
int 21h;تنفيذ مقاطعة أخذ الحرف من المستخدم
mov ah,4ch;خدمة إنهاء البرنامج
int 21h;تنفيذ خدمة الإنهاء

end
 ======================================================
 10 - إجراء لطباعة القيمة العددية المحتواة في المسجل ah .. (وهو إجراء هام جدّاً لمن لا يعلم )
.model small
.stack 100h
.data
.code

mov al,0FFh;al;حفظ قيمة ما في
call PrintAL;استدعاء دالة الطباعة
mov ah,4ch;خدمة انهاء البرنامج
int 21h;تنفيذ المقاطعة

PrintAL proc
mov ah,00h
mov bl,0ah
;;;;;;;;;;;;;;;
div bl
push ax;ah;حفظ باقي القسمة وهو موجود داخل
mov ah,00h

div bl
push ax
mov ah,00h

div bl
push ax
;;;;;;;;;;;;;;;;;;;
mov ax,0200h;خدمة طباعة حرف
;;;;;;;;;;;;;;;;;;;
pop dx;نخرج الآحاد
mov dl,dh;تذكر أنها محفوظة في الجزء العلوي ومقاطعة الطباعة تطبع الموجود في الجزء السفلي
add dx,30h;تحويل الرقم إلى حرف
int 21h;طباعة الآحاد

pop dx;نخرج العشرات
mov dl,dh
add dx,30h
int 21h;طباعة العشرات

pop dx;نخرج المئات
mov dl,dh
add dx,30h
int 21h;طباعة المئات

mov dl,10;محرف نهاية السطر
int 21h
;;;;;;;;;;;;;;;
PrintAL endp

end
 ======================================================
 الكود الحادي عشر .. فتح ملف باستخدام الخدمة 3D ثم قراءة عشر بايتات منه باستخدام الخدمة 3F من مقاطعة الدوز رقم 21
.model small
.data
filename db "test.txt",0;انتبه لاسم الملف
;ملاحظة : يجب أن يكون الملف موجوداً في نفس مكان وجود البرنامج
.code

mov dx,@data
mov ds,dx

mov ah,3dh;رقم خدمة فتح الملف
mov al,00h;تحديد نوع فتح الملف للقراءة
mov dx,offset filename;حفظ عنوان اسم الملف في مسجل البيانات
int 21h
;ax;يتم حفظ المقبض في المسجل

mov bx,ax;نقل المقبض إلى مسجل القاعدة
mov ah,3fh;رقم خدمة القراءة من ملف
mov cx,0ah;قراءة عشر بايتات
int 21h;تنفيذ المقاطعة التي ستخزن عشر بايتات في العنوان الذي يشير له مسجل البيانات
;ملاحظة : عندما نستعمل مسجل البيانات فإنه يرتبط بمقطع البيانات
mov ah,9;رقم خدمة طباعة سلسلة موجودة في مسجل البيانات
int 21h;تنفيذ مقاطعة الدوس

mov ah,4ch;رقم خدمة انهاء البرنامج
int 21h;تنفيذ مقاطعة الدوس
end
 ======================================================
الكود الثاني عشر
النسخة المحسنة من الكود 10 (ليست محسنة كثيراً)
البرنامج التالي يحوي إجراء Procedure لطباعة القيمة العشرية للمسجل AX (هام جداً )
يمكن للكسابى نسخ الـprocedure وإضافته لكودهم مباشرة .. وهو يترك cx دون تأثير ويستعمل ax,bx,dx
الفكرة بسيطة جداً القسمة 5 مرات على 10 وحفظ الباقي في المكدس ثم إخراجه وطباعته
.model small
.stack 100h
.data
.code
mov ax,0FFFFh
call PrintAX

mov ah,4ch
int 21h

PrintAX proc
mov dx,0000h
mov bx,0Ah
;;;;;;;;;;;;;;;
div bx
push dx
mov dx,0000h

div bx
push dx
mov dx,0000h

div bx
push dx
mov dx,0000h

div bx
push dx
mov dx,0000h

div bx
push dx
mov dx,0000h
;;;;;;;;;;;;;;;;;;;
mov ax,0200h
;;;;;;;;;;;;;;;;;;;
pop dx
add dx,30h
int 21h

pop dx
add dx,30h
int 21h

pop dx
add dx,30h
int 21h

pop dx
add dx,30h
int 21h

pop dx
add dx,30h
int 21h

mov dl,10
int 21h
;;;;;;;;;;;;;;;
ret
PrintAX endp
end
  ======================================================
الكود الثالث عشر : طباعة القيمة العشرية لمحتوى ax (الكود المحسّن)
يمكنك أخذ الإجراء مباشرة مع العلم أنه لا يؤثر على أي مسجّل بداخله , استعمله وأنت مرتاح
.model small
.stack 100h
.data
.code
mov ax,0FFFFh
call PrintAX
mov ah,4ch
int 21h

PrintAX proc
push ax;حفظ قيم المسجلات في المكدس
push bx
push cx
push dx

mov bx,0Ah;نقسم على 10 لذلك نحفظه في مسجل القاعدة
;;;;;;;;;;;;;;;
mov cx,5;we will loop 5 times
StartDiv:
mov dx,0000h;dx should be zero when deviding
div bx;devide on 10
push dx;حفظ باقي القسمة في المكدس
loop StartDiv

mov ax,0200h;print charachter function number

mov cx,5;we will loop 5 times
StartPop:
pop dx
add dx,30h
int 21h
loop StartPop

mov dl,10
int 21h
;;;;;;;;;;;;;;;
pop dx;استعادة جميع المسجلات المحفوظة بترتيب معاكس للحفظ
pop cx
pop bx
pop ax
ret
PrintAX endp
end
 ======================================================
الكود الرابع عشر
يحتوي إجراء قراءة عدد (يجب أن يكون العدد أقل من 65536 )
الكود التالي يقرأ عدداً ثم يقوم بطباعته باستخدام الإجراء الذي كتبناه سابقاً
;get unsigned short integer
.model small
.stack 100h
.data
.code
call readUSI;;get unsigned short integer
;the result stored in bx
call printEndOfLINE;

mov ax,bx
call printAX;
mov ax,4c00h;الخروج من البرنامج
int 21h;

printEndOfLINE proc
mov ah,02h;
mov dl,0Ah;
int 21h;print end of line
ret
printEndOfLINE endp

readUSI proc

mov bx,0;we will save result in bx
mov cx,0Ah;we will multiply by 10 in every loop cycle
getChar:
mov ah,01;read charachter function
int 21h;now charachter stored in al
cmp al,30h;compare with zero
jl endRead;if we get less than zero
xchg ax,bx;ax=result,bl=character
mul cx;now ax=result*10
mov bh,0;bx=character
sub bx,30h;turn from ascii value to integer value
add ax,bx;ax=result

mov bx,ax;bx=result
jmp getChar
endRead:
ret
readUSI endp

PrintAX proc
push ax;حفظ قيم المسجلات في المكدس
push bx
push cx
push dx

mov bx,0Ah;نقسم على 10 لذلك نحفظه في مسجل القاعدة
;;;;;;;;;;;;;;;
mov cx,5;we will loop 5 times
StartDiv:
mov dx,0000h;dx should be zero when deviding
div bx;devide on 10
push dx;حفظ باقي القسمة في المكدس
loop StartDiv

mov ax,0200h;print charachter function number

mov cx,5;we will loop 5 times
StartPop:
pop dx
add dx,30h
int 21h
loop StartPop

call printEndOfLINE;
;;;;;;;;;;;;;;;
pop dx;استعادة جميع المسجلات المحفوظة بترتيب معاكس للحفظ
pop cx
pop bx
pop ax
ret
PrintAX endp
end
 ======================================================
الكود الخامس عشر
استخدمت الإجراءات الخاصة بالقراءة والطباعة لقراءة عددين من المستخدم وطباعة المجموع
;get unsigned short integer
.model small
.stack 100h
.data
getA_Message db "Please Enter The First  Number   $"
getB_Message db "Please Enter The Second Number   $"
resultIs_Message db "The result is $"
.code
mov ax,@data
mov ds,ax
mov ah,09h;print string function
mov dx,offset getA_Message
int 21h
call readUSI;;read unsigned short integer
push bx;save a

call printEndOfLINE;

mov dx,offset getB_Message
int 21h
call readUSI;;read seconf number
push bx;save b
;the result stored in bx
call printEndOfLINE;

mov dx,offset resultIs_Message
int 21h

pop ax
pop bx
add ax,bx

call printAX;
mov ax,4c00h;الخروج من البرنامج
int 21h;

printEndOfLINE proc
push ax
push dx
mov ah,02h;
mov dl,0Ah;
int 21h;print end of line
pop dx
pop ax
ret
printEndOfLINE endp

readUSI proc;save result in bx
push ax
push cx
push dx
mov bx,0;we will save result in bx
mov cx,0Ah;we will multiply by 10 in every loop cycle
getChar:
mov ah,01;read charachter function
int 21h;now charachter stored in al
cmp al,30h;compare with zero
jl endRead;if we get less than zero
xchg ax,bx;ax=result,bl=character
mul cx;now ax=result*10
mov bh,0;bx=character
sub bx,30h;turn from ascii value to integer value
add ax,bx;ax=result

mov bx,ax;bx=result
jmp getChar
endRead:
pop dx
pop cx
pop ax
ret
readUSI endp

PrintAX proc
push ax;حفظ قيم المسجلات في المكدس
push bx
push cx
push dx

mov bx,0Ah;نقسم على 10 لذلك نحفظه في مسجل القاعدة
;;;;;;;;;;;;;;;
mov cx,5;we will loop 5 times
StartDiv:
mov dx,0000h;dx should be zero when deviding
div bx;devide on 10
push dx;حفظ باقي القسمة في المكدس
loop StartDiv

mov ax,0200h;print charachter function number

mov cx,5;we will loop 5 times
StartPop:
pop dx
add dx,30h
int 21h
loop StartPop

call printEndOfLINE;
;;;;;;;;;;;;;;;
pop dx;استعادة جميع المسجلات المحفوظة بترتيب معاكس للحفظ
pop cx
pop bx
pop ax
ret
PrintAX endp
end
 
 ======================================================
الرابط الأصلي

الكود السادس عشر في الرابط التالي  
;get unsigned short integer
.model small
.stack 100h
.data
getA_Message db "Please Enter The First  Number   $"
getB_Message db "Please Enter The Second Number   $"
getFunction_Message db "Please Enter The Operator (+,-,*,/)    $"
resultIs_Message db 10,13,"The result is    $"

UnknownOperator db "Unknown Operator !!",10,13,'$'
ErrorDivideByZero db "Error , Divide by Zero !!",10,13,'$'
.code
mov ax,@data
mov ds,ax
mov ah,09h;print string function

mov dx,offset getA_Message
int 21h
call readUSI;;read unsigned short integer
push bx;save a

call printEndOfLINE;

mov dx,offset getB_Message
int 21h
call readUSI;;read second number
push bx;save b

call printEndOfLINE;

mov dx,offset getFunction_Message
int 21h
call getOperator;return cl={'*','+','-','/'}={42,43,45,47}

mov dx,offset resultIs_Message
int 21h

pop bx
pop ax
call doOperatorAXClBX;

call printAX;
mov ax,4c00h;الخروج من البرنامج
int 21h;

getOperator proc;return cl={*,+,-,/}
push ax
mov ah,1
int 21h
mov cl,al
pop ax
ret
getOperator endp

doOperatorAXClBX proc;{*,+,-,/}={42,43,45,47}
isSum:
cmp cl,43
jne isMul
add ax,bx
ret
isMul:
cmp cl,42
jne isSub
mul bx
ret
;By Mostafa 0x36a2
isSub:
cmp cl,45
jne isDiv
sub ax,bx
ret
isDiv:
cmp cl,47
jne unKnown
mov dx,00h
cmp bx,0
jne doDiv
mov dx,offset ErrorDivideByZero
mov ah,9
int 21h
ret
doDiv:
div bx
ret
unKnown:
mov dx,offset UnknownOperator
mov ah,9
int 21h
ret
doOperatorAXClBX endp

printEndOfLINE proc
push ax
push dx
mov ah,02h;
mov dl,0Ah;
int 21h;print end of line
pop dx
;arabteam2000
pop ax
ret
printEndOfLINE endp

readUSI proc;save result in bx
push ax
push cx
push dx
mov bx,0;we will save result in bx
mov cx,0Ah;we will multiply by 10 in every loop cycle
getChar:
mov ah,01;read charachter function
int 21h;now charachter stored in al
cmp al,30h;compare with zero
jl endRead;if we get less than zero
xchg ax,bx;ax=result,bl=character
mul cx;now ax=result*10
mov bh,0;bx=character
sub bx,30h;turn from ascii value to integer value
add ax,bx;ax=result

mov bx,ax;bx=result
jmp getChar
endRead:
pop dx
pop cx
pop ax
ret
readUSI endp

PrintAX proc
push ax;حفظ قيم المسجلات في المكدس
push bx
push cx
push dx

mov bx,0Ah;نقسم على 10 لذلك نحفظه في مسجل القاعدة
;;;;;;;;;;;;;;;
mov cx,5;we will loop 5 times
StartDiv:
mov dx,0000h;dx should be zero when deviding
div bx;devide on 10
push dx;حفظ باقي القسمة في المكدس
loop StartDiv

mov ax,0200h;print charachter function number

mov cx,5;we will loop 5 times
StartPop:
pop dx
add dx,30h
int 21h
loop StartPop

call printEndOfLINE;
;;;;;;;;;;;;;;;
pop dx;استعادة جميع المسجلات المحفوظة بترتيب معاكس للحفظ
pop cx
pop bx
pop ax
ret
PrintAX endp
end
 ==================================================
انتهى نقل الأكواد, الأكواد السابقة كتبتها أثناء تعلمي للغة وهي الآن مرجع مفيد لمن يريد البدء بها

أكواد أسمبلي 16 بت لـلمبرمج Z3r0n3

السلام عليكم
سأضع في هذه الصفحة أكواداً قام الأخ Z3r0n3 بنشرها (وبفضله دخلت إلى لغة أسمبلي ولم أستطع الخروج :)
الأكواد منظمة بحيث يوضع الشرح ثم الكود. =============================================
هدا كودي اللأول للبرنامج الشهير Hello World

.model small
.stack 100h

.data
msg db "Hello World!",10,13,"$"

.code
start:

mov ax,@data
mov ds,ax

mov dx,offset msg
mov ah,9
int 21

mov ax,0c07h
int 21

end start
 ================================================================
هدا نفس البرنامج Hello World لكن استعملت في مكان التعليمة mov (التي نضع 
بها محتوى المتغير msg في dx) بتعليمة أخرى وهي lea و حدفت كلمة offset قبل
 اسم المتغير
كود البرنامج:
.model small
.stack 100h

.data ;قسم البيانات
msg db "Hello World!",10,13,"$" 

.code ; قسم الكود
start: ; بداية الكود

mov ax,@data ; نضع مقطع البيانلت في المسجل ax
mov ds,ax ; نضع قيمة ax في ds (data segment)

lea dx,msg ; كما قلت استعملت lea لوضع قيمة msg في dx و حدفت كلمة offset
mov ah,9 ; اجراءات اضهار نص في الشاشة
int 21 ; تنفيد الأمر

mov ax,0c07h ; هنا يقوم البرنامج بإنتضار زر مدخل ثم يقفل البرنامج
int 21 ; تنفيد الأمر

end start ; نهاية الكود
=================================================================
لكن في هذا المثال سأقوم بتكبير حجم شاشة البرنامج  Hello World هذا الكود الثالث و هو نفس البرنامج
و إظهار الرسالة, كذلك سنتعرف على كيفية كتابة الرسالة على أكثر من سطر
: الكود  
; ArabTeam2000
; Program CodedBy Z3r0n3
.model small
.stack 100h
 
.data ; قسم البيانات
msg     db "--------------------",10,13 ; لاحظ أن المتغير إنتهى ب 10,13 وهي تعني الرجوع الى السطر  
            db "| Hello World!",10,13 ; عندها يرجع الى السطر و يكتب محتوى هذا ثم يقوم بالرجوع الى السطر
            db "| CodedBy Z3r0n3 ",10,13 ; نفس الشيئ
            db "| ArabTeam2000",10,13 ; نفس الشيئ
            db "--------------------","$" ; لاحظ هنا أنه انتهى بـ "$" وهي تعني نهاية المتغير

.code ; قسم الكود
start: ; بداية الكود
                        mov ax,@data ;ax نضع قسم البيانات في
                        mov ds,ax ; (data segment) ds في ax نضع قيمة
                        mov ax,13h ; (Full Screen)هنا سنقوم بتغيير شاشة البرنامج على حجم الشاشة
                        int 10h ; تنفيذ الأمر
                        mov dx,offset msg ; dx في msg نضع قيمة
                        mov ah,9 ;\  إضهار
                        int 21h  ;/ الرسالة
                        mov ah,7 ; يقوم بانتضار زر مدخل
                        int 21h ; تنفيذ أمر
                        mov ax,4c00h ; (Inactif) هنا يقوم بإيقاف تشغيل البرنامج لاحظ ان شاشة البرنامج سيكتب فيها 
                        int 21h ; تنفيذ أمر
end start ; نهاية الكود
=============================================================== 
فكرة عن البرنامج : البرنامج سيسألك 4 أسئلة و أنتم ستجيبون عنها
طبعا الفائدة لن تكون في الإجابة عن الأسئلة بل ستكون في معرفة كيف برمج و ماهو الجديد في البرنامج و ماذا سأستفيد من البرنامج
في هذا البرنامج استعملت شيئ جديد و هو ال Procedure
; ArabTeam2000
; Program CodedBy Z3r0n3
.model small
.stack 100h

.data ; قسم البيانات
question1 db "Whats your name? : ","$"
question2 db 10,13,"Where are you from ? : ","$"             ;\         
question3 db 10,13,"How old are you ? : ","$"                ;|  لاحظ أن المتغيرات تبدأ بـ 10,13 لكي يكتب النص في سطر جديد
question4 db 10,13,"Do you like Assembly (Y/N) ? : ","$"     ;/

.code ; قسم الكود
start: ; بداية الكود
mov ax,@data              ; ax نضع قسم البيانات في
mov ds,ax                     ; (data segment) ds في ax نضع قيمة
mov dx,offset question1 ; \ dx نضع محتوى المتغير في المسجل
call write                       ; /     كتابة نص procedure نطلب
call read                       ; قرائة نص procedure نطلب
call waiter                     ; تنتظر الضغط على أي زر procedure نطلب
mov dx,offset question2 ; \ dx نضع محتوى المتغير في المسجل 
call write                       ; / كتابة نص procedure نطلب
call read                       ; قرائة نص procedure نطلب
call waiter                     ; تنتظر الضغط على أي زر procedure نطلب
mov dx,offset question3 ; \ dx نضع محتوى المتغير في المسجل
call write                       ; / كتابة نص procedure نطلب
call read                       ; قرائة نص procedure نطلب
call waiter                     ; تنتظر الضغط على أي زر procedure نطلب
mov dx,offset question4 ; \  نضع محتوى المتغير في المسجل dx
call write              ;  /procedure  كتابة نص  نطلب 
call read                       ; قرائة نص procedure نطلب
call Exit                        ; إيقاف البرنامج procedure نطلب

write proc                                                         ; كتابة نص procedure بداية
mov ah,9                                  ; الرقم 9 هو رقم الخاص بكتابة نص على الشاشة
int 21h                                      ; تنفيذ الأمر السابق
Ret                                                       ; الرجوع للأمر الذي يلي طلب تنفيذ هذه الدالة
write endp                                                        ; كتابة نص procedure نهاية                                             

waiter proc                                                       ; إنتظار الضغط على الزر procedure بداية
mov ah,7                                  ; الرقم 7 هو رقم الخاص بانتظار ادخال زر
int 21h                                      ; تنفيذ الأمر السابق
Ret                                                       ; الرجوع للأمر الذي يلي طلب تنفيذ هذه الدالة
waiter endp                                                      ; إنتظار الضغط على الزر procedure نهاية

read proc                                                         ; قرائة نص  بداية procedure
mov ah,0ah                               ; رقم خاص بقرائة نص : ha0
int 21h                                      ; تنفيذ الأمر السابق
Ret                                                       ; الرجوع للأمر الذي يلي طلب تنفيذ هذه الدالة
read endp                                                         ; قرائة نص procedure نهاية

Exit proc                                                          ; إيقاف البرنامج procedure بداية
mov ax,4c00h                           ; هذا الرقم خاص بإقاف البرنامج و غالبا ما ستجده في البرامج الأخرى
int 21h                                      ; تنفيذ الأمر السابق
Ret                                                       ; الرجوع للأمر الذي يلي طلب تنفيذ هذه الدالة
Exit endp                                                          ; إيقاف البرنامج procedure نهاية
end start                                                           ; و أخيرا نهاية الكود
========================================================
نهاية أكواد Z3r0n3 
الرابط الأصلي