Extremis
Assembly

Extremis

مغامرة RPG مكتوبة بلغة تجميع RISC-V

( يا إلهي، من برمج .iris ليكون غير قابل للإيداع؟ )

لعبة تقمّص أدوار نصية مكتوبة بلغة التجميع RISC-V 32-bit، ويتم محاكاتها عبر QEMU.

"الكون قرر أنه لا يريدني أن أفعل شيئًا أفضل، مثل ربما الحصول على حياة."

المتطلبات

  • riscv32-elf-as — مُجمّع RISC-V
  • riscv32-elf-ld — رابط RISC-V
  • qemu-riscv32 — محاكي وضع المستخدم لـ RISC-V

على Arch Linux:

sudo pacman -S riscv64-elf-binutils qemu-user

البناء والتشغيل

make build   # تجميع وربط إلى main.bin
make run     # بناء + تشغيل عبر qemu-riscv32
make debug   # تشغيل مع -strace لفحص استدعاءات النظام

هيكل المشروع

Extremis/
├── main.s                      # نقطة الدخول (_start)، تستدعي run_chapter_1
├── src/
│   ├── include/
│   │   └── function.s          # وحدات ماكرو إطار المكدس startF / endF
│   ├── engine/
│   │   ├── print.s             # print — طباعة سلسلة نصية منتهية بـ null (a0)
│   │   └── utils.s             # printl — طباعة قائمة من السلاسل النصية (a0=عنوان الجدول، a1=العدد)
│   ├── chapters/
│   │   └── chapter_1.s         # منطق الفصل الأول؛ تحميل سلاسل المقدمة واستدعاء printl
│   └── dialogue/
│       └── intro.s             # بيانات السلاسل النصية لتسلسل المقدمة
└── build/                      # ملفات .o المترجمة (تعكس هيكل src/)

البنية المعمارية

المحرك مكتوب بلغة التجميع النقية RISC-V 32-bit مستهدفًا واجهة Linux ABI عبر محاكاة وضع المستخدم لـ QEMU.

اصطلاح إطار المكدس

تستخدم كل دالة وحدات الماكرو startF / endF من src/include/function.s لحفظ واستعادة ra، s0، s1، s2 على المكدس:

startF    # دفع: تخصيص 16 بايت، حفظ ra/s0/s1/s2
...
endF      # سحب: استعادة ra/s0/s1/s2، إلغاء تخصيص 16 بايت
ret

استدعاءات النظام

تحذير: أرقام استدعاءات بيئة RARS/MARS لا تعمل تحت qemu-riscv32. يستخدم هذا المشروع أرقام استدعاءات نظام Linux RISC-V ABI.

يُوضع رقم استدعاء النظام في a7، ويُستدعى بـ ecall. تعود القيمة في a0.

الإخراج

الغرض RARS a7 Linux a7 a0 a1 a2 ملاحظات
طباعة سلسلة نصية 4 64 1 (وصف الملف stdout) عنوان المخزن المؤقت الطول Linux لا يتوقف عند null — مرر طول البايت
طباعة حرف 11 64 1 مخزن الحرف 1 خزّن الحرف في الذاكرة، مرر عنوانه
طباعة عدد صحيح 1 العدد الصحيح لا يوجد مقابل في Linux؛ حوّله إلى سلسلة نصية أولاً
طباعة عدد سداسي عشري 34 العدد الصحيح لا يوجد مقابل في Linux؛ حوّله يدويًا
طباعة عدد غير موقع 36 العدد الصحيح لا يوجد مقابل في Linux؛ حوّله يدويًا

الإدخال

الغرض RARS a7 Linux a7 a0 a1 a2 ملاحظات
قراءة سلسلة نصية 8 63 0 (وصف الملف stdin) عنوان المخزن المؤقت أقصى بايت Linux يعيد البايتات الخام بما في ذلك السطر الجديد
قراءة حرف 12 63 0 عنوان المخزن المؤقت 1 يعيد a0 = البايتات المقروءة؛ الحرف في المخزن المؤقت
قراءة عدد صحيح 5 لا يوجد مقابل في Linux؛ اقرأ سلسلة نصية، حللها

العملية

الغرض RARS a7 Linux a7 a0 ملاحظات
خروج 10 93 RARS يتجاهل a0؛ Linux يقرؤه كحالة خروج
خروج(رمز) 17 93 رمز الخروج Linux exit_group

الذاكرة

الغرض RARS a7 Linux a7 a0 يعيد ملاحظات
تخصيص (sbrk) 9 214 بايت (RARS) / عنوان brk جديد (Linux) a0 = بداية الكتلة المخصصة Linux brk يعمل بشكل مختلف — تضبط القمة الجديدة، وليس الحجم

الإدخال/الإخراج للملفات

الغرض RARS a7 Linux a7 a0 a1 a2 a3 ملاحظات
فتح ملف 13 56 واصف الدليل (-100=الدليل الحالي) عنوان اسم الملف الأعلام الوضع Linux يستخدم openat
قراءة ملف 14 63 واصف الملف عنوان المخزن المؤقت أقصى بايت يعيد البايتات المقروءة
كتابة ملف 15 64 واصف الملف عنوان المخزن المؤقت عدد البايتات يعيد البايتات المكتوبة
إغلاق ملف 16 57 واصف الملف يعيد 0 عند النجاح

الوقت

الغرض RARS a7 Linux a7 a0 a1 ملاحظات
الوقت 30 113 معرف الساعة (1=الوقت الحقيقي) timespec* المخزن المؤقت RARS يعيد lo/hi مقسمين في a0/a1؛ Linux يكتب الهيكل إلى المخزن المؤقت
سكون 32 115 معرف الساعة timespec* RARS يأخذ ميلي ثانية في a0؛ Linux يستخدم clock_nanosleep مع هيكل

إضافة محتوى

  • السلاسل النصية الجديدة للحوار توضع في src/dialogue/.
  • الفصول الجديدة توضع في src/chapters/ ويتم تضمينها (.include) في ملف الفصل الذي يحتاجها.
  • ملف Makefile يكتشف تلقائيًا جميع ملفات .s تحت src/ (باستثناء src/include/).