Extremis
Assembly

Extremis

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

GitHub|

( يا إلهي، من برمج .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/).