Files

276 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ASCII — Referencia del intérprete Lua
Documento extraído del código fuente en `c:/mingw/gitea/ascii/` (principalmente `ascii.cpp`, `lua.cpp`, `play.cpp`, `ascii.h`). Sirve como guía para portar Pepe Runner desde Turbo Pascal a Lua.
> Versión analizada del intérprete: v0.6.1 aprox (según el mensaje del boot ROM en `lua.cpp`).
---
## 1. Modelo de ejecución
Cada juego/programa es **un solo fichero `.lua`** que define dos funciones globales:
```lua
function init()
-- se llama una sola vez al arrancar
end
function update()
-- se llama cada frame (~60 FPS, vsync)
end
```
- El intérprete se invoca como `ascii.exe nombre_juego.lua`. Si no se pasa argumento, intenta cargar `game.lua`.
- También se puede **arrastrar y soltar** un `.lua` sobre la ventana para cargarlo.
- **F5** reinicia el juego (re-llama a `init()` y vuelve a empezar el bucle).
- **ESC** pausa la ejecución y abre una consola de depuración (`> ` prompt). Los comandos `run` y `cont` la cierran. Comandos prefijados con `?` evalúan e imprimen (ej.: `?1+1`).
- Se usa la versión estándar de Lua que está vendorizada en `ascii/lua/` (con `luaL_openlibs`), así que están disponibles `string`, `math`, `table`, etc.
---
## 2. Modos de pantalla — `mode(n)`
| Modo | Resolución carácter | Resolución pixel | Notas |
|------|--------------------:|-----------------:|-------|
| 0 | 80 × 30 | 640 × 240 | Color único global (no por-carácter) — usado para depuración / texto. `current_color` aplica a toda la pantalla. |
| 1 | 40 × 30 | 320 × 240 | **Modo por defecto.** Color por carácter. |
| 2 | 20 × 15 | 160 × 120 | Mitad de resolución. Cómodo para tiles grandes (ej.: sokoban). |
| 3 | 32 × 24 | 256 × 192 | Estilo "ZX Spectrum" (con bordes anchos). |
Cada carácter es **8×8 píxeles**. Los gráficos son texto coloreado, no píxeles libres — el "lienzo" es una matriz de celdas (carácter + atributo de color).
---
## 3. Paleta de colores (16, CGA/EGA)
Constantes Lua predefinidas (`lua.cpp` líneas 610-625):
| Código | Constante | Aprox. |
|-------:|------------------------|---------------|
| 0 | `COLOR_BLACK` | #000000 |
| 1 | `COLOR_BLUE` | #0000AA |
| 2 | `COLOR_GREEN` | #00AA00 |
| 3 | `COLOR_CYAN` | #00AAAA |
| 4 | `COLOR_RED` | #AA0000 |
| 5 | `COLOR_MAGENTA` | #AA00AA |
| 6 | `COLOR_BROWN` | #AA5500 |
| 7 | `COLOR_LIGHT_GRAY` | #AAAAAA |
| 8 | `COLOR_DARK_GRAY` | #555555 |
| 9 | `COLOR_LIGHT_BLUE` | #5555FF |
| 10 | `COLOR_LIGHT_GREEN` | #55FF55 |
| 11 | `COLOR_LIGHT_CYAN` | #55FFFF |
| 12 | `COLOR_LIGHT_RED` | #FF5555 |
| 13 | `COLOR_LIGHT_MAGENTA` | #FF55FF |
| 14 | `COLOR_YELLOW` | #FFFF55 |
| 15 | `COLOR_WHITE` | #FFFFFF |
El atributo de color de una celda es 1 byte: nibble bajo = INK (tinta), nibble alto = PAPER (fondo).
---
## 4. API — Funciones expuestas a Lua
### Pantalla y color
| Función | Descripción |
|---------|-------------|
| `mode(n)` | Cambia modo de pantalla (0-3) y hace cls. |
| `cls([chr=32])` | Limpia con el carácter dado (32 = espacio). En modo ≠0 además rellena el color attr. |
| `ink(c)` | Color de tinta (0-15). |
| `paper(c)` | Color de fondo (0-15). |
| `border(c)` | Color del borde de la ventana. |
| `color(ink, paper, [border])` | Combina los tres. |
| `locate(x, y)` | Posiciona cursor en celda (x, y). |
| `print(str, [x, y])` | Imprime `str` (sin salto de línea). Si se dan x,y, primero hace `locate`. |
| `crlf()` | CR + LF (mueve cursor a inicio de siguiente línea). |
### Entrada
| Función | Descripción |
|---------|-------------|
| `btn(k)` | `true` si la tecla `k` está pulsada *en este frame* (estado SDL_GetKeyboardState). |
| `btnp(k)` | `true` solo en el frame en que la tecla se pulsa (edge). |
| `mousex()` / `mousey()` | Posición del ratón en **coordenadas de carácter** (ya escalado al modo). |
| `mousewheel()` | Delta de la rueda en este frame. |
| `mousebutton(i)` | `true` si el botón `i` está pulsado (1=izq, 2=medio, 3=der; usa `SDL_BUTTON(i)`). |
> **Nota**: `whichbtn()` está declarado en `ascii.h` y existe en C++, pero **no está expuesto a Lua** (no aparece en los `lua_setglobal` de `lua.cpp`). Para detectar qué tecla se ha pulsado en un frame hay que iterar con `btnp()` sobre las constantes `KEY_*`.
> **Bug de `tostr` con negativos**: la implementación de `tostr()` en `lua.cpp` (función `intToStr`) hace `(x % 10) + '0'` que con `x = -1` produce `-1 + 48 = 47`, o sea `'/'`. Por tanto `tostr(-1)` devuelve `"/"`, `tostr(-2)` devuelve `"."`, etc. Si vas a imprimir un número que puede ser negativo, usa `string.format("%d", n)` (que sí maneja signo) o clampa con `max(0, n)` antes de pasar a `tostr`.
Códigos de tecla — todos definidos como globales `KEY_*` en Lua. Lista completa (de `lua.cpp` 502-608):
```
KEY_A..KEY_Z = 4..29
KEY_1..KEY_0 = 30..39 (1=30, 2=31, ..., 9=38, 0=39)
KEY_RETURN=40 KEY_ESCAPE=41 KEY_BACKSPACE=42 KEY_TAB=43 KEY_SPACE=44
KEY_MINUS=45 KEY_EQUALS=46 KEY_LEFTBRACKET=47 KEY_RIGHTBRACKET=48
KEY_BACKSLASH=49 KEY_NONUSHASH=50 KEY_SEMICOLON=51 KEY_APOSTROPHE=52
KEY_GRAVE=53 KEY_COMMA=54 KEY_PERIOD=55 KEY_SLASH=56 KEY_CAPSLOCK=57
KEY_F1..KEY_F12 = 58..69
KEY_PRINTSCREEN=70 KEY_SCROLLLOCK=71 KEY_PAUSE=72
KEY_INSERT=73 KEY_HOME=74 KEY_PAGEUP=75 KEY_DELETE=76 KEY_END=77 KEY_PAGEDOWN=78
KEY_RIGHT=79 KEY_LEFT=80 KEY_DOWN=81 KEY_UP=82
KEY_NUMLOCKCLEAR=83 KEY_KP_DIVIDE=84 KEY_KP_MULTIPLY=85 KEY_KP_MINUS=86 KEY_KP_PLUS=87 KEY_KP_ENTER=88
KEY_KP_1..KEY_KP_0 = 89..98 KEY_KP_PERIOD=99
KEY_NONUSBACKSLASH=100 KEY_APPLICATION=101
KEY_LCTRL=224 KEY_LSHIFT=225 KEY_LALT=226 KEY_LGUI=227
KEY_RCTRL=228 KEY_RSHIFT=229 KEY_RALT=230 KEY_RGUI=231
```
(Son los SDL2 scancodes.)
### Matemáticas
`abs(x)`, `ceil(x)`, `flr(x)`, `sgn(x)`, `sin(x)`, `cos(x)`, `atan2(dx, dy)`, `sqrt(x)`, `max(a,b)`, `min(a,b)`, `mid(a,b,c)` (devuelve el del medio, equivalente a `clamp`).
`rnd(n)` devuelve un entero en `[0, n-1]` (`rand()%n`). `srand(seed)` siembra el RNG.
### Strings
| Función | Descripción |
|---------|-------------|
| `tostr(v)` | Convierte valor a string. Soporta nil, function, table (formato `{k=v,...}`), number, boolean, string. |
| `strlen(s)` | Longitud en bytes. |
| `ascii(s, i)` | Código del byte en índice `i` (0-based). |
| `chr(n)` | String de un solo carácter cuyo código es `n`. |
| `substr(s, start, length)` | Subcadena. |
> Nota: Lua estándar también está disponible, así que `string.format`, `string.sub`, etc., funcionan. Pero los demos usan estas helpers.
### Memoria
| Función | Descripción |
|---------|-------------|
| `peek(addr)` | Lee 1 byte de la VRAM/memoria (0..0x1FFF). |
| `poke(addr, val)` | Escribe 1 byte. |
| `memcpy(dst, src, size)` | Copia bytes en la memoria del fantasy console. |
| `setchar(idx, b0..b7)` | Define los 8 bytes del carácter `idx` en el char-ROM (sobrescribe la fuente). |
**Mapa de memoria** (8 KB total, `mem[8192]`):
- `0x0000` (0): char_screen (matriz de códigos de carácter por celda)
- Tras char_screen viene color_screen (offset = `screen_width * screen_height`)
- `0x0A00` (2560 = `MEM_CHAR_OFFSET`): char-ROM (definición de glifos, 8 bytes por carácter, 256 chars = 2048 bytes)
- `0x1200` (4608 = `MEM_BOOT_OFFSET`): zona de boot/recursos de ROM
Para los modos 1 y 2 los color_screen offsets son 1200 y 300 respectivamente; en modo 3 es 768; en modo 0 no hay color_screen por celda (color global).
### Audio
**Sonido simple:**
- `sound(freq, len)` — onda cuadrada a `freq` Hz durante `len` (en algo similar a centésimas de segundo; `audio_len = len*44.1`).
- `nosound()` — silencio inmediato.
**Mini-lenguaje MML — `play(str)`:**
Sintaxis tipo BASIC `PLAY` / MML. Tokens (case-sensitive, minúsculas):
| Token | Significado |
|-------|-------------|
| `c d e f g a b` | Nota. Acepta sufijo `#` o `+` (sostenido) o `-` (bemol). Luego dígito 0-9 para duración. |
| `r` | Silencio. Acepta dígito de duración. |
| `o<0-7>` | Octava absoluta. |
| `>` `<` | Sube / baja octava. |
| `l<0-9>` | Longitud por defecto para notas sin duración. |
| `v<0-9>` | Volumen (se traduce a `(d-0)<<4`). |
| `t<0-9>` | Tempo. |
Duraciones: índice 0-9 → tabla `{313,625,938,1250,1875,2500,3750,5000,7500,10000}` (de "redonda" a "trentaidosava", aproximadamente).
Ejemplo (de `breakout.lua`):
```lua
play("l0o3bagfedc") -- escala descendente como sonido de game-over
play("o5l0c") -- pitido agudo (rebote)
```
### Ficheros y portapapeles
| Función | Descripción |
|---------|-------------|
| `load([filename])` | Reinicia y carga otro `.lua` (o el mismo si filename=nil). |
| `fileout(name, addr, size)` | Vuelca `size` bytes de memoria a un binario. |
| `filein(name, addr, size)` | Carga un binario a memoria. |
| `toclipboard(str)` | Copia al portapapeles del SO. |
| `fromclipboard()` | Lee del portapapeles (máx 1023 chars). |
### Utilidades de tiempo / frame
| Función | Descripción |
|---------|-------------|
| `time()` | Milisegundos desde inicio (`SDL_GetTicks()`). |
| `cnt()` | Contador de frames desde el último `rst()`. |
| `rst()` | Resetea el contador de frames a 0. |
| `log(str)` | Imprime en la consola de debug (no en pantalla). |
---
## 5. Caracteres especiales útiles
Los demos usan códigos > 127 que corresponden a glifos definidos en `rom.c` (el char-ROM por defecto). Algunos vistos:
- `\003` (3) — un bloque relleno (usado en pong para los compases)
- `\016` (16) — cubo de caja (en sokoban)
- `\127` (127) — pared en sokoban (redefinido con `setchar(127, ...)`)
- `\143`, `\154`, `\150`, `\156`, `\149` — esquinas y trazos de marcos
- `\248`, `\250`, `\251` — sprite animado de "OK" en sokoban
- `\233` — pelota en breakout
- `\131` — pala en breakout
- `\001` — cubo de tetromino (redefinido con `setchar(1, 0xff,0x81,...)`)
Para usarlos siempre se puede hacer `setchar(idx, b0..b7)` con la bitmap deseada y luego imprimirlo con `print(chr(idx), x, y)`.
---
## 6. Patrón típico de un juego
```lua
function init()
mode(1)
cls()
-- estado inicial
player = {x=10, y=15}
score = 0
end
function update()
-- input
if btnp(KEY_LEFT) then player.x = player.x - 1 end
if btnp(KEY_RIGHT) then player.x = player.x + 1 end
-- lógica
-- ...
-- render (no hay vsync explícito; el bucle ya hace flip al final)
cls()
color(COLOR_WHITE, COLOR_BLACK)
print("\248", player.x, player.y)
print("SCORE: "..tostr(score), 0, 0)
end
```
**Cosas a recordar:**
- No se "pintan" píxeles; se imprime un código de carácter en una celda y se le asocia un atributo de color (ink + paper). Para gráficos personalizados, redefinir glifos con `setchar`.
- El bucle de render lo hace el motor C++ después de `update()` — no hay que llamar a ningún `flip`.
- El `state machine` típico se hace asignando `update = otra_funcion` (ver `demos/sokoban.lua`).
- Las coordenadas de pantalla son **enteras y por celda**, no por píxel. (0,0) es esquina superior izquierda.
- Para depurar: `log("mensaje")` o pulsar ESC y usar la consola con `?variable`.
---
## 7. Cómo compilar el intérprete
Desde `c:/mingw/gitea/ascii/`:
```sh
make windows
```
Requiere MinGW (g++) y SDL2 para Windows. Produce `ascii.exe`. Para correr Pepe Runner:
```sh
ascii.exe pepe_runner.lua
```