( 도대체 누가 .iris를 커밋할 수 없도록 프로그래밍한 걸까? )
RISC-V 32비트 어셈블리로 작성된 텍스트 기반 RPG로, 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 # 1장 로직; 인트로 문자열 로드 후 printl 호출
│ └── dialogue/
│ └── intro.s # 인트로 시퀀스용 문자열 데이터
└── build/ # 컴파일된 .o 파일 (src/ 구조 반영)
아키텍처
엔진은 QEMU 사용자 모드 에뮬레이션을 통해 Linux ABI를 대상으로 하는 순수 RISC-V 32비트 어셈블리입니다.
스택 프레임 규칙
모든 함수는 src/include/function.s의 startF / endF 매크로를 사용하여 ra, s0, s1, s2를 스택에 저장하고 복원합니다:
startF # push: 16바이트 할당, ra/s0/s1/s2 저장
...
endF # pop: 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 fd) |
buf 주소 | 길이 | Linux는 null에서 멈추지 않음 — 바이트 길이 전달 |
| 문자 출력 | 11 |
64 |
1 |
문자 buf | 1 |
문자를 메모리에 저장 후 주소 전달 |
| 정수 출력 | 1 |
— | 정수 | — | — | Linux에 해당 없음; 문자열로 변환 후 출력 |
| 16진수 정수 출력 | 34 |
— | 정수 | — | — | Linux에 해당 없음; 직접 변환 필요 |
| 부호 없는 정수 출력 | 36 |
— | 정수 | — | — | Linux에 해당 없음; 직접 변환 필요 |
입력
| 용도 | RARS a7 |
Linux a7 |
a0 |
a1 |
a2 |
참고사항 |
|---|---|---|---|---|---|---|
| 문자열 읽기 | 8 |
63 |
0 (stdin fd) |
buf 주소 | 최대 바이트 | Linux는 개행 문자를 포함한 원시 바이트 반환 |
| 문자 읽기 | 12 |
63 |
0 |
buf 주소 | 1 |
a0 = 읽은 바이트 수; 문자는 buf에 저장 |
| 정수 읽기 | 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는 다르게 작동 — 크기가 아닌 새 상한값 설정 |
파일 I/O
| 용도 | RARS a7 |
Linux a7 |
a0 |
a1 |
a2 |
a3 |
참고사항 |
|---|---|---|---|---|---|---|---|
| 파일 열기 | 13 |
56 |
dirfd (-100=CWD) |
파일명 주소 | 플래그 | 모드 | Linux는 openat 사용 |
| 파일 읽기 | 14 |
63 |
fd | buf 주소 | 최대 바이트 | — | 읽은 바이트 수 반환 |
| 파일 쓰기 | 15 |
64 |
fd | buf 주소 | 바이트 수 | — | 쓴 바이트 수 반환 |
| 파일 닫기 | 16 |
57 |
fd | — | — | — | 성공 시 0 반환 |
시간
| 용도 | RARS a7 |
Linux a7 |
a0 |
a1 |
참고사항 |
|---|---|---|---|---|---|
| 시간 | 30 |
113 |
클록 ID (1=REALTIME) |
timespec* buf |
RARS는 lo/hi를 a0/a1로 분할 반환; Linux는 buf에 구조체 작성 |
| 대기 | 32 |
115 |
클록 ID | timespec* |
RARS는 a0에 밀리초 사용; Linux는 구조체로 clock_nanosleep 사용 |
콘텐츠 추가
- 새로운 대화 문자열은
src/dialogue/에 추가합니다. - 새로운 챕터는
src/chapters/에 추가하고, 필요한 챕터 파일에서.include로 포함시킵니다. - Makefile은
src/아래의 모든.s파일을 자동으로 찾습니다 (src/include/제외).
