جمّع

جمّع

صعب

التعليمات

في هذا التمرين، ستُحدِّد الصيغة ونموذج التنفيذ للغة صغيرة مستوحاة من لغة التجميع x86-64.

الهدف هو إضفاء صيغة رسمية على كيفية كتابة الكود وتحليله وتنفيذه داخل بيئة مقيَّدة.

نموذج التنفيذ

سيُكتب كود التجميع الخاص بك داخل تركيب خاص:

let program := assemble!(
    -- assembly code here
)

يجب أن يُنتج هذا التركيب برنامجًا يحاكي دالةً تنفِّذ كود التجميع وتُرجع قيمة. ثم يُستدعى هذا البرنامج بالصيغة التالية، حيث a وb وc وغيرها وسائط:

let result := program(a, b, c, ...)

جميع الوسائط والقيمة المُرجَعة هي أعداد صحيحة موقَّعة بطول 64 بت. ويُسمح بـ 6 وسائط كحدٍّ أقصى.

السجلات

تُمرَّر القيمة المُرجَعة وجميع الوسائط عبر السجلات. يجب أن تدعم لغتك السجلات التالية:

السجل الدور
rdi الوسيط الأول
rsi الوسيط الثاني
rdx الوسيط الثالث
rcx الوسيط الرابع
r8 الوسيط الخامس
r9 الوسيط السادس
rax القيمة المُرجَعة

تبدأ السجلات بقيمة افتراضية هي 0، إلا إذا كانت تُستخدم لتمرير الوسائط إلى الدالة.

على سبيل المثال، في البرنامج التالي، تكون قيمة جميع السجلات 0 باستثناء rdi، الذي يُهيَّأ بالقيمة 10، وrsi، الذي يُهيَّأ بالقيمة -20:

program(10, -20)

تُخزَّن القيمة المُرجَعة للبرنامج دائمًا في rax.

Note

أسماء السجلات لا تُميّز بين حالة الأحرف. هذا يعني أن rax وRAX وrAx تشير كلها إلى السجل نفسه.

كود التجميع

يمكن أن يأخذ كل سطر في كود التجميع أحد شكلين:

  • الوسوم تُحدِّد مواضع معيّنة في البرنامج لتستخدمها بعض التعليمات.
  • التعليمات تمثّل عمليات ينفِّذها البرنامج.

ما لم تُعدِّلها تعليمة ما، يسير تدفّق تنفيذ البرنامج بشكل خطي. هذا يعني أن السطر السابق يُنفَّذ بالكامل قبل تنفيذ السطر الذي يليه.

الوسوم

للوسوم الصيغة التالية:

<label>:

لا تُغيِّر قيمة أي سجل، ولا يكون لها أي تأثير على البرنامج غير تحديد مواضع معيّنة في الكود لتتمكن التعليمات من استخدامها.

Note

الوسوم تُميّز بين حالة الأحرف. هذا يعني أن Start وstart وsTart وسوم مختلفة.

تعليمات بطرفَي عملية

لمعظم التعليمات الصيغة التالية:

<opcode> <destination>, <source>

يشير opcode إلى العملية الجاري تنفيذها، باستخدام قيم طرفَي العملية: الهدف والمصدر. وتُخزَّن نتيجة العملية في طرف العملية الهدف.

على سبيل المثال، تجمع التعليمة add القيمتين في طرفَي العملية الهدف والمصدر وتخزّن هذه النتيجة في طرف العملية الهدف.

طرف العملية الهدف دائمًا سجل، بينما قد يكون طرف العملية المصدر سجلًا أو عددًا حرفيًا.

هذه قائمة بالتعليمات التي يجب أن يدعمها برنامجك:

رمز العملية العملية المنفَّذة
mov الهدف := المصدر
add الهدف := الهدف + المصدر
sub الهدف := الهدف - المصدر
mul الهدف := الهدف * المصدر
div الهدف := الهدف / المصدر
xor الهدف := الهدف ^^^ المصدر
and الهدف := الهدف &&& المصدر
or الهدف := الهدف ||| المصدر
shl الهدف := الهدف <<< المصدر
shr الهدف := الهدف >>> المصدر

وإلى جانب تلك، يجب أن يدعم برنامجك التعليمة cmp ذات طرفَي العملية.

لا تُعدِّل هذه التعليمة أي سجل، بل تقارن طرفَي العملية الهدف والمصدر، وتضبط حالة داخلية في البرنامج على أحد ثلاثة خيارات:

  • أكبر من، إذا كان destination > source
  • يساوي، إذا كان destination == source
  • أصغر من، إذا كان destination < source

وكيفية تتبّعك لهذه الحالة داخليًا متروكة لك.

تعليمات بطرف عملية واحد

هناك بعض التعليمات التي تُغيِّر تدفّق البرنامج، فتَنقُل التنفيذ إلى موضع آخر في الكود. وتُسمّى تعليمات القفز.

تأخذ جميعها طرف عملية واحدًا فقط، وهو وسم يشير إلى هدف القفز، أي الموضع في الكود الذي سيستمر التنفيذ منه:

<opcode> <label>

تُجري التعليمة jmp القفز إلى الوسم الهدف دائمًا.

أما تعليمات القفز الأخرى فلا تُجري القفز إلا إذا كانت الحالة الداخلية للبرنامج مضبوطة على قيمة معيّنة. هذا يعني أنها تُسبق عادةً بتعليمة cmp.

هذه هي تعليمات القفز التي يجب أن يدعمها برنامجك:

التعليمة العملية المنفَّذة
jmp قفز غير مشروط. يُنفَّذ القفز دائمًا
je يقفز إذا كانت الحالة الداخلية يساوي
jl يقفز إذا كانت الحالة الداخلية أصغر من
jg يقفز إذا كانت الحالة الداخلية أكبر من
Note

رموز التعليمات، سواء كانت ذات طرفَي عملية أو طرف عملية واحد، لا تُميّز بين حالة الأحرف. هذا يعني أن add وADD وaDd تشير كلها إلى التعليمة نفسها.

تعديل عبر GitHub يفتح الرابط في نافذة أو علامة تبويب جديدة
Lean Exercism

مستعد لبدء جمّع؟

سجّل في Exercism لتتعلّم وتتقن Lean عبر 100 تمرين، وإرشاد بشري حقيقي، وكل ذلك مجانًا.