كيف يتم تحويل كود السي بلس بلس الى لغة الاسمبلي
تحويل كود بلغة السي بلس الى الاسمبلي
.data bb dw 64 dup (0) ; int[] bb .data? ct dw ? ; int ct .. it could also be dd j dw ? ; int j .. it could also be dd .code mov ct, 0 ; ct = 0 mov j, 0 ; j = 0 mov di, offset bb _while: cmp j, 64 ; j >= 64 ? jge _endWhile ; jump to _endWhile if yes ; no movsx bx, j cmp [di+bx*2], 40 ; bb[j] < 40 ? jl _next ; get next if yes ; no cmp [di+bx*2], 50 ; bb[j] >= 50 ? jge _next ; get next if yes ; no inc ct ; ct++ _next: inc j ; j++ jmp _while _endWhile:
* افترضت أن المسجلات بطول 16 بت.
* افترضت قيمة بدائية صفرية لكامل محتوى المصفوفة bb[].
* يمكن كتابة الكود بصورة أبسط من خلال استخدام Conditional Control Flow كالمدعومة في الإصدار 6.1+ من MASM مثلاً، لكني أبقيته pure assembly.
Do as I say, not as I do
We are Anonymous. We are Legion. We don't forgive. We don't forget
أنا لا اريد جل ولكن اريد شرح لاساسيات العمل
لديك طريقتين، في الامتحان إحداهما غير ممكنة:
1- كتابة البرنامج بـ C++ ومن ثم استخدام debugger لقراءة التعليمات التي تم تشكيلها بالأسمبلي :P وهذا غير ممكن في الامتحان.
2- قراءة كل سطر من سطور البرنامج وكتابة مثيلاتها بالمقابل في الأسمبلي، لا يوجد عصا الساحر هنا ليكون هناك قاعدة للتحويل، الأمر يحتاج إلى معرفة بلغة C++ حتى تستطيعين معرفة ما تقرأين وبالتالي كتابة المقابل في الأسمبلي.
الآن بشكل عام:
- لدينا تصريح عن متحولات:
علينا معرفة عدد البايتات التي يقوم بحجزها كل نمط type فمثلاً char سيحجز بايت واحد بالتالي نستخدم db لحجز بايت واحد في الأسمبلي، بينما int يلزمها 2 بايت أو أكثر (الأمر متعلق بالمعالج الذي تنفذ عليه، فإن كانت مسجلاته بطول 16 بت فالـ int تحجز بايتين وتكون القيمة العظمى هي 65,565 أي 0FFFFh .. وهكذا)
في الحل افترضت أنك تتعاملين مع مسجلات بطول 16 بت وعليه استخدمت dw لحجز word للمتحولين ct و j
- لدينا كتلة التحكم بالتدفق WHILE:
هذه التعليمة مبنية على شرط يجب تحقيقه حتى يتم تنفيذ الكتلة المحصورة بين القوسين { }
في مثالنا هنا الشرط هو أن تكون قيمة j أصغر تماماً من 64 (حتى لا يخرج الـ index خارج المجال الخاص بالمصفوفة bb[] كما تلاحظين)
ومن المعروف أنه في حال انتفاء الشرط، لا يتم تنفيذ الكتلة بين القوسين ويتم تجاوزها تماماً، إذاً سأقوم بكتابة التالي:
cmp j, 64
jge _endWhile
;{
; ...
; ...
;}
_endWhile:من الواضح أني أقوم هنا بمقارنة قيمة j مع 64، هل j أكبر أو تساوي 64 ؟ إن كانت الإجابة نعم فعليك الانتقال إلى اللافتة _endWhile والتي تتخطى الكتلة وتتجاوزها لأن شرط تنفيذ الكتلة هو أن تكون j أصغر تماماً من 64
- لدينا كتلة تحكم بالتدفق IF:
الشرط الذي يجب تحقيقه بـ C++ هو:
if (bb[j] >= 40 && bb[j] < 50)
السؤال الأهم هنا: كيف يتم التعبير عن المصفوفة bb[j] في الأسمبلي؟
لنتخيل المصفوفة في الذاكرة وهي عبارة عن مربعات متراصة إلى جانب بعضها البعض.
يجب فهم النقاط التالية للحصول على أفضل جودة عرض في التخيل :P:
- كل مربع من هذه المربعات له طول محدد بنمط المصفوفة (مثلاً: int[] bb فتكون المصفوفة لأعداد صحيحة كل واحد بطول 2 بايت كما افترضنا سابقاً)
- كل مربع من هذه المربعات يحمل قيمة.
- كل مربع من هذه المربعات له عنوان في الذاكرة.
- وكل مربع من هذه المربعات له ترتيب محدد، يتم الاصطلاح على أن يحمل المربع الأول في المصفوفة الترتيب 0 والمربع الثاني الترتيب 1 وهكذا .. ويكون المربع الأخير ذو ترتيب n-1 حيث n تساوي طول المصفوفة (في السؤال هنا n = 64)
إذاً نستطيع تخيل bb[] في الذاكرة بالشكل التالي:
كيف نعبر عن bb[4] بتعليمات أسمبلي؟
كما نرى من الصورة نحتاج إلى Base address وهو يمثل عنوان أول بايت في المصفوفة bb[]
يمكن الحصول على هذا العنوان باستخدام تعليمة LEA (Load Effective Address) أو MOV مع التوجيه OFFSET بالشكل:
lea di, bb ; is equivalent to mov di, offset bb
نحتاج أيضاً إلى معرفة ترتيب (index) العنصر المطلوب الحصول عليه وهنا index = 4
نحتاج أيضاً إلى معرفة حجم نمط المتحول الذي نسعى إليه وهو هنا int وقد افترضنا أن int = word بالحجم أي 2، لكننا نستطيع أن ندع الأسمبلر يقوم بحساب الحجم المناسب بشكل اوتوماتيكي من خلال المعامل المسمى sizeof
التعليمات المناسبة بالأسمبلي تكون:
lea di, bb mov ax, [di + 4 * sizeof(word)]
(للمزيد من المعلومات حول هذه النقطة بشكل عام يجب دراسة مفاهيم العنونة)
سنقوم بإضافة هذا المفهوم إلى الهيكل الذي أنشأناه سابقاً لكتلة التحكم WHILE:
cmp j, 64
jge _endWhile
;{
mov di, offset bb
movsx bx, j
cmp word ptr [di+bx*2], 40
jl _next
cmp word ptr [di+bx*2], 50
jge _next
; ..
; ..
_next:
; ..
;}
_endWhile:لكن العنوان القاعدي للمصفوفة bb[] ثابت لذلك سننقل التعليمة التالية إلى خارج الحلقة حتى لا يتم تكرار تنفيذها بدون داع
mov di, offset bb
ويصبح الكود
mov di, offset bb
cmp j, 64
jge _endWhile
;{
movsx bx, j
cmp word ptr [di+bx*2], 40
jl _next
cmp word ptr [di+bx*2], 50
jge _next
; ..
; ..
_next:
; ..
;}
_endWhile:الشرط الذي أضفناه باستخدام التعليمة CMP وتعليمات القفز المناسبة يختبر محتوى العنوان bb[j] فإن كان أصغر تماماً من 40 و أكبر أو يساوي 50 يتم تخطي الأمر ct++ بالقفز إلى اللافتة _next
لإكمال الكود بشكله النهائي نضيف تعليمتي ct++ و j++ في الأمكنة المناسبة
mov di, offset bb
_while:
cmp j, 64
jge _endWhile
;{
movsx bx, j
cmp word ptr [di+bx*2], 40
jl _next
cmp word ptr [di+bx*2], 50
jge _next
inc ct
_next:
inc j
jmp _while
;}
_endWhile:Do as I say, not as I do
We are Anonymous. We are Legion. We don't forgive. We don't forget
اوك.
- بدلاً من أن يقوم بحجز متغير j كما فعلت أنا. قام باعتماد المسجل ESI لتمثل قيمته قيمة المتحول j وهذا جائز شرعاً وقانوناً :)
لذلك قام بتنفيذ int j = 0 من خلال السطر التالي:
mov esi, 0
- بدلاً من أن يقوم بزيادة قيمة j بمقدار 1 ومن ثم ضربها بـ 2 كما فعلت أنا في السطر
[di+bx*2]
حيث j = bx
قام بزيادة قيمة j بمقدار 2 مباشرة من خلال السطر
add esi, 2
- بما أنه اعتمد هذه الطريقة أصبح شرط الكتلة WHILE أن تكون قيمة ESI كحد أقصى تساوي حيث n=64 (حجم المصفوفة) و 2 يمثل حجم النمط (int).
- لأن ESI أصبحت تمثل قيمة عددية (imm) وهي j أصبح بالإمكان اتباع العنونة بالشكل [reg+imm] لتمثيل bb[j] بدلاً من [reg+index*size] لذلك استخدم السطر التالي:
cmp bb[esi], 40
وهي ذاتها لو كتبتها بالشكل
cmp [esi+bb], 40
- القفزات التالية لها التأثير ذاته:
jb <-> jl
jae <-> jge
حيث نستخدم: JG و JL ومشتقاتها (JGE, JLE, ...) مع الأعداد المؤشرة
ونستخدم: JA و JB ومشتقاتها (JAE, JBE, ...) مع الأعداد الغير مؤشرة
وعلى الرغم من أني حاولت مراعاة هذه النقطة لورودها في السؤال (unsigned word values) عند كتابتي لطريقتي إلا أنه يبدو أني قد كبرت وخانتني ذاكرتي فكتبت JGE بدلاً من JAE وكتبت JL بدلاً من JB :blush:
طبعاً النتيجة واحدة بما أننا نتعامل مع القيم غير مؤشرة، لكن في حال تعاملنا مع قيم مؤشرة فيجب استخدام التعليمات المناسبة حصراً.
انتهى :)
Do as I say, not as I do
We are Anonymous. We are Legion. We don't forgive. We don't forget
شكرا اتعبتك معي