11 KiB
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:
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 cargargame.lua. - También se puede arrastrar y soltar un
.luasobre 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 comandosrunycontla 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/(conluaL_openlibs), así que están disponiblesstring,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 enascii.hy existe en C++, pero no está expuesto a Lua (no aparece en loslua_setglobaldelua.cpp). Para detectar qué tecla se ha pulsado en un frame hay que iterar conbtnp()sobre las constantesKEY_*.
Bug de
tostrcon negativos: la implementación detostr()enlua.cpp(funciónintToStr) hace(x % 10) + '0'que conx = -1produce-1 + 48 = 47, o sea'/'. Por tantotostr(-1)devuelve"/",tostr(-2)devuelve".", etc. Si vas a imprimir un número que puede ser negativo, usastring.format("%d", n)(que sí maneja signo) o clampa conmax(0, n)antes de pasar atostr.
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 afreqHz durantelen(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):
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 consetchar(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 consetchar(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
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únflip. - El
state machinetípico se hace asignandoupdate = otra_funcion(verdemos/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/:
make windows
Requiere MinGW (g++) y SDL2 para Windows. Produce ascii.exe. Para correr Pepe Runner:
ascii.exe pepe_runner.lua