(Meu Deus, quem programou o .iris para que não fosse possível fazer commit?)
Um RPG baseado em texto escrito em linguagem de montagem RISC-V de 32 bits, simulado via QEMU.
"O universo decidiu que não queria que eu fizesse nada melhor, como, talvez, arranjar uma vida."
Dependências
riscv32-elf-as— Assembler RISC-Vriscv32-elf-ld— Linker RISC-Vqemu-riscv32— Emulador de modo de usuário RISC-V
No Arch Linux:
sudo pacman -S riscv64-elf-binutils qemu-user
Compilar e Executar
make build # compila e vincula em main.bin
make run # compila + executa via qemu-riscv32
make debug # executa com -strace para inspecionar chamadas de sistema
Estrutura do projeto
Extremis/
├── main.s # Ponto de entrada (_start), chama run_chapter_1
├── src/
│ ├── include/
│ │ └── function.s # macros de estrutura de pilha startF / endF
│ ├── engine/
│ │ ├── print.s # print — imprime uma única string terminada em null (a0)
│ │ └── utils.s # printl — imprime uma lista de strings (a0=tabelas de endereços, a1=contagem)
│ ├── chapters/
│ │ └── chapter_1.s # Lógica do Capítulo 1; carrega strings de introdução e chama printl
│ └── dialogue/
│ └── intro.s # Dados de string para a sequência de introdução
└── build/ # Arquivos .o compilados (espelha o layout de src/)
Arquitetura
O motor é uma linguagem assembly RISC-V de 32 bits pura, voltada para a ABI do Linux por meio da emulação em modo de usuário do QEMU.
Convenção de quadro de pilha
Todas as funções utilizam as macros startF / endF de src/include/function.s para salvar e restaurar ra, s0, s1, s2 na pilha:
startF # push: alocar 16 bytes, salvar ra/s0/s1/s2
...
endF # pop: restaurar ra/s0/s1/s2, desalocar 16 bytes
ret
Chamadas de sistema
Aviso: Os números de chamada de ambiente RARS/MARS não funcionam no
qemu-riscv32. Este projeto usa os números de chamada de sistema da ABI RISC-V do Linux.
O número da chamada de sistema vai para a7, invocada com ecall. O valor de retorno é retornado em a0.
Saída
| Intenção | RARS a7 |
Linux a7 |
a0 |
a1 |
a2 |
Notas |
|---|---|---|---|---|---|---|
| Imprimir string | 4 |
64 |
1 (fd stdout) |
endereço do buffer | comprimento | O Linux NÃO para no nulo — passe o comprimento em bytes |
| Imprimir caractere | 11 |
64 |
1 |
buffer de caracteres | 1 |
armazene o caractere na memória, passe seu endereço |
| Imprimir inteiro | 1 |
— | inteiro | — | — | sem equivalente no Linux; converter primeiro para string |
| Imprimir inteiro hexadecimal | 34 |
— | inteiro | — | — | sem equivalente no Linux; converter manualmente |
| Imprimir inteiro sem sinal | 36 |
— | inteiro | — | — | sem equivalente no Linux; converter manualmente |
Entrada
| Intenção | RARS a7 |
Linux a7 |
a0 |
a1 |
a2 |
Notas |
|---|---|---|---|---|---|---|
| Ler string | 8 |
63 |
0 (stdin fd) |
endereço do buffer | bytes máximos | O Linux retorna bytes brutos, incluindo quebra de linha |
| Ler caractere | 12 |
63 |
0 |
endereço do buffer | 1 |
retorna a0 = bytes lidos; o caractere está no buffer |
| Leitura de inteiro | 5 |
— | — | — | — | sem equivalente no Linux; leia a string e analise-a |
Processo
| Intenção | RARS a7 |
Linux a7 |
a0 |
Notas |
|---|---|---|---|---|
| Saída | 10 |
93 |
— | RARS ignora a0; o Linux o lê como status de saída |
| Saída(código) | 17 |
93 |
código de saída | Linux exit_group |
Memória
| Intenção | RARS a7 |
Linux a7 |
a0 |
Retorna | Notas |
|---|---|---|---|---|---|
| Alocar (sbrk) | 9 |
214 |
bytes (RARS) / novo endereço brk (Linux) | a0 = início do bloco alocado |
O brk do Linux funciona de maneira diferente — você define o novo topo, não um tamanho |
E/S de arquivo
| Intenção | RARS a7 |
Linux a7 |
a0 |
a1 |
a2 |
a3 |
Notas |
|---|---|---|---|---|---|---|---|
| Abrir arquivo | 13 |
56 |
dirfd (-100=CWD) |
endereço do nome do arquivo | sinalizadores | modo | O Linux usa openat |
| Ler arquivo | 14 |
63 |
fd | endereço do buffer | bytes máximos | — | retorna os bytes lidos |
| Gravar arquivo | 15 |
64 |
fd | endereço do buffer | contagem de bytes | — | retorna os bytes gravados |
| Fechar arquivo | 16 |
57 |
fd | — | — | — | retorna 0 em caso de sucesso |
Tempo
| Intenção | RARS a7 |
Linux a7 |
a0 |
a1 |
Notas |
|---|---|---|---|---|---|
| Tempo | 30 |
113 |
id do relógio (1=TEMPO REAL) |
timespec* buf |
RARS retorna lo/hi divididos em a0/a1; Linux grava a estrutura em buf |
| Sleep | 32 |
115 |
id do relógio | timespec* |
RARS recebe milissegundos em a0; Linux usa clock_nanosleep com uma estrutura |
Adicionando Conteúdo
- Novas strings de diálogo vão para
src/dialogue/. - Novos capítulos vão para
src/chapters/e são incluídos via.includeno arquivo de capítulo que os necessita. - O Makefile detecta automaticamente todos os arquivos
.semsrc/(excluindosrc/include/).
