Extremis
Assembly

Extremis

RPG- пригода, написана на мові RISC-V Assembly

GitHub|

( боже, хто запрограмував .iris так, що його не можна комітити? )

Текстова RPG, написана на асемблері RISC-V 32-біт, симульована через 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  — вивести один рядок, що закінчується нулем (a0)
│   │   └── utils.s             # printl — вивести список рядків (a0=адреса таблиці, a1=кількість)
│   ├── chapters/
│   │   └── chapter_1.s         # Логіка глави 1; завантажує вступні рядки та викликає printl
│   └── dialogue/
│       └── intro.s             # Дані рядків для вступної послідовності
└── build/                      # Скомпільовані файли .o (віддзеркалює структуру src/)

Архітектура

Рушій написаний повністю на асемблері RISC-V 32-біт, орієнтований на Linux ABI через емуляцію QEMU в режимі користувача.

Конвенція кадру стеку

Кожна функція використовує макроси startF / endF з src/include/function.s для збереження та відновлення ra, s0, s1, s2 у стеку:

startF    # push: виділити 16 байт, зберегти ra/s0/s1/s2
...
endF      # pop:  відновити ra/s0/s1/s2, звільнити 16 байт
ret

Системні виклики

Увага: Номери викликів середовища RARS/MARS не працюють під qemu-riscv32. Цей проєкт використовує номери системних викликів Linux ABI для RISC-V.

Номер системного виклику поміщається в a7, викликається за допомогою ecall. Значення повернення надходить у a0.

Виведення

Призначення RARS a7 Linux a7 a0 a1 a2 Примітки
Вивести рядок 4 64 1 (stdout fd) адреса буфера довжина Linux НЕ зупиняється на нулі — передавайте довжину в байтах
Вивести символ 11 64 1 буфер символу 1 зберегти символ у пам'яті, передати його адресу
Вивести ціле число 1 — ціле число — — немає еквівалента в Linux; спочатку конвертуйте в рядок
Вивести шістнадцяткове ціле число 34 — ціле число — — немає еквівалента в Linux; конвертуйте вручну
Вивести беззнакове ціле число 36 — ціле число — — немає еквівалента в Linux; конвертуйте вручну

Введення

Призначення RARS a7 Linux a7 a0 a1 a2 Примітки
Зчитати рядок 8 63 0 (stdin fd) адреса буфера макс. байт 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 dirfd (-100=CWD) адреса імені файлу прапори режим Linux використовує openat
Читати файл 14 63 fd адреса буфера макс. байт — повертає прочитані байти
Писати у файл 15 64 fd адреса буфера кількість байт — повертає записані байти
Закрити файл 16 57 fd — — — повертає 0 при успіху

Час

Призначення RARS a7 Linux a7 a0 a1 Примітки
Час 30 113 ідентифікатор годинника (1=REALTIME) буфер 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/).