Files
persecucio-mortal-ascii/ASCII_API.md
T

11 KiB
Raw Blame History

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 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):

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

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/:

make windows

Requiere MinGW (g++) y SDL2 para Windows. Produce ascii.exe. Para correr Pepe Runner:

ascii.exe pepe_runner.lua