Nand2Tetris — الجزء الأول: من المنطق إلى المعالج › المشروع 6: المُجمِّع 6 / 6 السابق

المشروع 6: المُجمِّع

المشروع 6: المُجمِّع

خلفية

نادرًا ما تُكتب برامج الآلة منخفضة المستوى بيد البشر. فعادةً ما تُولّدها المصرّفات. لكن يمكن للبشر فحص الشيفرة المترجمة واستخلاص دروس مهمة حول كيفية كتابة برامجهم عالية المستوى على نحو أفضل، بطريقة تتجنّب مزالق المستوى المنخفض وتستغل العتاد الأساسي استغلالًا أفضل. وأحد اللاعبين الرئيسيين في عملية الترجمة هذه هو المُجمِّع -- وهو برنامج صُمّم لترجمة الشيفرة المكتوبة بلغة آلة رمزية إلى شيفرة مكتوبة بلغة آلة ثنائية.

يُشكّل هذا المشروع معلمًا مثيرًا في رحلة Nand2Tetris خاصتنا: إذ يتعامل مع بناء الدرجة الأولى في سلّم التسلسل البرمجي، الذي سينتهي في النهاية ببناء مصرِّف للغة عالية المستوى تشبه Java. لكن، أول الأشياء أولًا.

الهدف

اكتب برنامج مُجمِّع يترجم البرامج المكتوبة بلغة التجميع الرمزية Hack إلى شيفرة ثنائية قادرة على التنفيذ على منصة Hack العتادية المبنية في المشاريع السابقة.

العقد

هناك ثلاث طرق لوصف السلوك المطلوب من مُجمِّعك: (i) عند تحميله إلى مُجمِّعك، يجب أن يُترجم ملف Prog.asm الذي يحتوي على برنامج صحيح مكتوب بلغة التجميع Hack إلى شيفرة Hack الثنائية الصحيحة، ويُخزَّن في ملف Prog.hack. (ii) يجب أن يكون الخرج الذي يُنتجه مُجمِّعك مطابقًا للخرج الذي يُنتجه المُجمِّع المرفق مع حزمة برامج Nand2Tetris. (iii) يجب أن ينفّذ مُجمِّعك مواصفة الترجمة الواردة في الفصل 6، القسم 2.

الاستخدام

حسب لغة البرمجة التي تستخدمها، يجب استدعاء المُجمِّع باستخدام شيء مثل "Assembler fileName.asm"، حيث تمثّل السلسلة fileName.asm مُدخل المُجمِّع، أي اسم ملف نصي يحتوي على أوامر تجميع Hack. وينشئ المُجمِّع ملفًا نصيًا للخرج باسم fileName.hack. ويتألف كل سطر في ملف الخرج من ستة عشر حرفًا من 0 و1. ويُخزَّن ملف الخرج في نفس مجلد ملف الإدخال. وقد يحتوي اسم ملف الإدخال على مسار ملف.

الموارد

القراءة ذات الصلة بهذا المشروع هي الفصل 6. ويمكن كتابة تنفيذ مُجمِّعك بأي لغة برمجة (Java وPython خياران شائعان). وأداتان مفيدتان هما المُجمِّع (Assembler) ومحاكي CPU (CPU Emulator) المرفقان، وكلاهما متاح في مجلد الأدوات خاصتك. وتتيح هاتان الأداتان التجريب مع مُجمِّع عامل قبل الشروع في بناء واحد بنفسك. وبالإضافة إلى ذلك، يوفر المُجمِّع المرفق واجهة رسومية مرئية للترجمة على مستوى السطر، ويتيح مقارنة الشيفرة مع المخارج التي سيولّدها مُجمِّعك. ولمزيد من المعلومات حول هذه القدرات، راجع درس المُجمِّع (Assembler Tutorial) المرفق (PPT، PDF)

التنفيذ المقترح

يتضمّن الفصل 6 واجهة برمجة تطبيقات (API) مقترحة للمُجمِّع، مستقلة عن اللغة، يمكن أن تكون بمثابة مخطط تنفيذك. ونقترح بناء المُجمِّع في مرحلتين. أولًا، اكتب مُجمِّعًا أساسيًا مصممًا لترجمة برامج التجميع التي لا تحتوي على أي رموز. ثم وسّع مُجمِّعك الأساسي بإمكانيات معالجة الرموز، فتحصل على المُجمِّع النهائي. وبرامج الاختبار التي نرفقها أدناه مصممة لدعم استراتيجية التنفيذ المتدرّج هذه.

برامج الاختبار

يأتي كل برنامج اختبار فيما عدا الأول في نسختين: Prog.asm هو برنامج تجميع؛ وProgL.asm هو البرنامج نفسه تمامًا، ناقص الرموز (كل رمز مستبدل بعنوان ذاكرة صريح).

البرنامج الرمزي بدون رموز الوصف
Add.asm يجمع ثابتَي 2 و3 ويضع الناتج في R0.
Max.asm MaxL.asm يحسب max(R0,R1) ويضع الناتج في R2.
Rect.asm RectL.asm يرسم مستطيلًا في أعلى يسار الشاشة. المستطيل عرضه 16 بكسل وارتفاعه R0 بكسل.
Pong.asm PongL.asm لعبة Pong للاعب واحد. ترتد الكرة عن "جدران" الشاشة. ويحاول اللاعب ضرب الكرة بمجداف بالضغط على مفتاحي السهمين الأيسر والأيمن في لوحة المفاتيح. وعلى كل ضربة ناجحة يكسب اللاعب نقطة واحدة ويصغر المجداف قليلًا، لتجعل اللعبة أصعب قليلًا. وإذا أخطأ اللاعب الكرة، تنتهي اللعبة. وللخروج من اللعبة، اضغط مفتاح ESC.

كُتب برنامج Pong المرفق أعلاه بلغة Jack عالية المستوى الشبيهة بـJava، وترجم إلى لغة التجميع Hack على يد مصرِّف Jack (كلاهما موصوف في الفصل 9 والفصول 10-11 على الترتيب). وعلى الرغم من أن برنامج Jack الأصلي لا يزيد سوى على نحو 300 سطر من شيفرة Jack، فإن شيفرة Pong القابلة للتنفيذ أطول بطبيعة الحال بكثير. وتشغيل هذا البرنامج التفاعلي في محاكي CPU المرفق أمر بطيء، فلا تتوقع لعبة Pong قوية الأداء. وهذا البطء فضيلة في الحقيقة، إذ يتيح لعينك تتبع السلوك الرسومي للبرنامج. ولا تقلق! فمع استمرارنا في بناء المنصة البرمجية في المشاريع القليلة القادمة، ستُشغّل Pong وغيرها من الألعاب أسرع بكثير.

الأدوات

إنّ مُجمِّع Hack المرفق الموضح أدناه مضمون توليد شيفرة ثنائية صحيحة. ويمكن الاستفادة من هذا الأداء المضمون لاختبار ما إذا كان مُجمِّع آخر، مثل الذي كتبته أنت مثلًا، يولّد شيفرة صحيحة أيضًا. وتوضّح لقطة الشاشة التالية عملية المقارنة:

منطق المقارنة: لنفرض أن Prog.asm برنامج ما مكتوب بلغة التجميع الرمزية Hack. ولنفترض أننا نترجم هذا البرنامج باستخدام المُجمِّع المرفق، فينتج ملف ثنائي اسمه Prog.hack. ثم نستخدم مُجمِّعًا آخر (مثل الذي كتبته أنت) لترجمة البرنامج نفسه إلى ملف آخر، لنقل MyProg.hack. فإذا كان المُجمِّع الأخير يعمل بصورة صحيحة، ترتّب على ذلك أن الملف الذي أنتجه مطابق للملف الذي أنتجه المُجمِّع المرفق. وبذلك، إحدى طرق اختبار مُجمِّع مكتوب حديثًا هي كالآتي: (i) حمّل ملف Prog.asm إلى المُجمِّع المرئي المرفق بصفته برنامجًا مصدريًا، والملف الآخر بصفته ملف مقارنة، (ii) ترجم البرنامج المصدري، و(iii) قارن الشيفرة الثنائية الناتجة بملف المقارنة (انظر الشكل أعلاه). فإن فشلت المقارنة، فالمُجمِّع الذي أنتج الملف الأخير لا بدّ أن يكون معطوبًا؛ وإلا، فقد يكون سليمًا.