CHATGPT.md (7611B)
1 # Contexto: programar juegos en C para la PicoCalc 2 3 Pegá este documento entero al principio de la conversación. 4 5 Sos mi ayudante para escribir programas en C para la **ClockworkPi PicoCalc** 6 con una **Raspberry Pi Pico 1 (RP2040, Cortex-M0+, 133 MHz, 264 KB de RAM, sin 7 FPU)**. Pantalla 320x320 color, teclado QWERTY, tarjeta SD. 8 9 Uso un kit que ya resuelve todo el hardware (pantalla, teclado, SD, arranque 10 desde el cargador de la SD, linker script para flash+200K). **Yo solo cambio 11 `app/app.c`** (y a veces `app/app_config.h`). Todos los `.c` de `app/` se 12 compilan solos. 13 14 ## Reglas (importantes) 15 16 1. **Solo usá la API de abajo** (`hal.h` y `gfx.h`) para pantalla, teclado y 17 guardado. **No** uses el SDK de la Pico directamente para el LCD, el 18 teclado, SPI, I2C ni la SD: ya están inicializados por `hal_init()` y 19 tocarlos rompe todo. Nada de `printf` para mostrar cosas (sale por un 20 puerto serie que no veo). No escribir la flash de la Pico. 21 2. **No escribas `main()`**. El punto de entrada es `void app_main(void)`, que 22 **primero** llama a `hal_init()` y después nunca retorna (bucle infinito). 23 3. **`#include "hal.h"`** alcanza (ya incluye `gfx.h`, `input.h`, `str.h`, `<stdint.h>`, 24 `<stdbool.h>`). Se puede usar `<string.h>`, `<stdlib.h>` y `snprintf` de 25 `<stdio.h>`, pero preferí `str_put`/`str_int` (más livianos). 26 4. **Dibujo por franjas**: `hal_draw(paint, ctx)` llama a `paint` **10 veces por 27 cuadro**, una por cada franja de 32 filas (el recorte lo hace solo). Por eso 28 `paint` tiene que dibujar la pantalla **entera** a partir del estado y **no 29 modificar nada** (no mover cosas, no llamar a `rand`, no contar cuadros adentro 30 de `paint`). La lógica va en el bucle de `app_main`. 31 5. **Pila chica** (unos pocos KB): nada de arreglos grandes como variables 32 locales; usá `static` o globales. Evitá `malloc`. 33 6. **Sin FPU**: `float` funciona pero es lento; preferí enteros / punto fijo. 34 7. Velocidad de pantalla: repintar la pantalla entera tarda ~100 ms (≈10 fps). 35 `hal_draw` solo manda por SPI las franjas que cambiaron, así que un juego de 36 acción anda bien si la mayor parte de la pantalla queda quieta. Llamá a 37 `hal_draw` solo cuando algo cambió. 38 8. Teclado: `hal_key()` devuelve **solo el momento en que se aprieta** una tecla 39 (no hay "tecla sostenida" ni soltar). Lee como mucho una vez cada 15 ms. 40 Devuelve `-1` si no hay nada y **`0` = "redibujá la pantalla"** (no es una 41 tecla: tratalo como pedido de repintar). 42 9. Todo el código C debe ser C11 que compile con `arm-none-eabi-gcc -Wall` sin 43 warnings. Devolveme siempre `app.c` **completo**, no fragmentos. 44 45 ## API: `hal.h` 46 47 ```c 48 void hal_init(void); /* llamar primero; muestra un log de arranque */ 49 uint32_t hal_ms(void); /* milisegundos desde el arranque */ 50 void hal_sleep(int ms); 51 void hal_draw(void (*paint)(Surface *s, void *ctx), void *ctx); /* ver regla 4 */ 52 int hal_key(void); /* -1 nada, 0 redibujar, K_* o caracter ASCII */ 53 bool hal_store_save(const char *data, int n); /* guarda en la SD (hasta ~40 KB) */ 54 int hal_store_load(char *buf, int max); /* bytes leídos, 0 si no hay nada */ 55 int hal_battery(void); /* porcentaje, o -1 */ 56 uint32_t hal_random(void); /* 32 bits al azar (hardware) */ 57 void hal_log(const char *line); /* diagnóstico: escribe en el .LOG de la SD 58 y lo muestra en pantalla (tapa el juego) */ 59 void hal_log_clear(void); 60 void app_main(void); /* LA DEFINE LA APP */ 61 62 /* teclas especiales (el resto llegan como su caracter: 'a', 'A', '1', ' ', ...) */ 63 enum Key { K_UP = 0x100, K_DOWN, K_LEFT, K_RIGHT, K_OK /* Enter */, K_BACK /* Esc */, 64 K_TAB, K_PLUS, K_MINUS, K_BKSP }; 65 ``` 66 67 ## API: `gfx.h` (dibujo, coordenadas en píxeles, 0,0 arriba a la izquierda) 68 69 ```c 70 typedef uint16_t Color; /* RGB565 */ 71 #define RGB(r, g, b) ... /* r,g,b de 0 a 255 */ 72 /* colores listos: C_BLACK C_WHITE C_RED C_TEXT C_DIM C_ACCENT C_OK C_ERR C_PANEL C_PANEL2 C_CURSOR */ 73 74 extern const Font font_small; /* 5x7 px por letra */ 75 extern const Font font_normal; /* 6x12 */ 76 extern const Font font_bold; /* 6x13 */ 77 78 void gfx_rect(Surface *s, int x, int y, int w, int h, Color c); /* rectángulo lleno */ 79 void gfx_frame(Surface *s, int x, int y, int w, int h, Color c); /* borde de 1 px */ 80 void gfx_box(Surface *s, int x, int y, int w, int h, Color bg, Color border); 81 void gfx_line(Surface *s, int x0, int y0, int x1, int y1, int thick, Color c); 82 void gfx_disc(Surface *s, int cx, int cy, int r, Color c); /* círculo lleno */ 83 void gfx_ring(Surface *s, int cx, int cy, int r, int thick, Color c); 84 void gfx_poly16(Surface *s, const int32_t *xs, const int32_t *ys, int n, Color c); 85 /* polígono convexo lleno; coordenadas en 1/16 de píxel (x*16) */ 86 int gfx_text(Surface *s, const Font *f, int x, int y, const char *utf8, Color c); 87 /* y = borde de arriba del texto; devuelve la x final */ 88 int gfx_text_w(const Font *f, const char *utf8); /* ancho en px */ 89 int gfx_text_center(Surface *s, const Font *f, int x, int y, int w, const char *t, Color c); 90 void gfx_clip(Surface *s, int x, int y, int w, int h); /* limita el dibujo a ese rect */ 91 void gfx_noclip(Surface *s); 92 Color gfx_dim(Color c); /* mitad de brillo */ 93 ``` 94 95 Texto sin stdio (`str.h`): escriben en `buf` desde `pos` y devuelven la nueva posición. 96 97 ```c 98 int str_put(char *buf, int n, int pos, const char *s); 99 int str_int(char *buf, int n, int pos, long v); 100 const char *str_scan_int(const char *s, long *out); /* NULL si no hay número */ 101 /* ej: int p = str_put(b, sizeof b, 0, "Puntos: "); str_int(b, sizeof b, p, score); */ 102 ``` 103 104 Azar determinista (`rng.h`, incluir aparte): `Rng r; rng_seed(&r, hal_random(), 1); 105 rng_below(&r, 6)` → 0..5. 106 107 ## Esqueleto de un programa 108 109 ```c 110 #include "hal.h" 111 112 typedef struct { int x, y, score; bool over; } Game; 113 114 static void paint(Surface *s, void *ctx) /* solo dibuja; ver regla 4 */ 115 { 116 const Game *g = ctx; 117 gfx_rect(s, 0, 0, 320, 320, C_BLACK); 118 gfx_rect(s, g->x, g->y, 10, 10, C_OK); 119 } 120 121 void app_main(void) 122 { 123 static Game g; 124 hal_init(); 125 g.x = 155; g.y = 155; 126 bool dirty = true; 127 uint32_t next_tick = hal_ms(); 128 for (;;) { 129 int k = hal_key(); 130 if (k == 0) dirty = true; 131 if (k == K_LEFT) { g.x -= 10; dirty = true; } 132 /* ... */ 133 if ((int32_t)(hal_ms() - next_tick) >= 0) { /* lógica a ritmo fijo */ 134 next_tick += 100; 135 /* avanzar el juego */ 136 dirty = true; 137 } 138 if (dirty) { hal_draw(paint, &g); dirty = false; } 139 hal_sleep(5); 140 } 141 } 142 ``` 143 144 ## Guardar 145 146 `hal_store_save` escribe en el archivo `.SAV` de la SD (definido en 147 `app_config.h`, tiene que existir: lo crea el kit). Es un blob de bytes: guardá 148 texto simple (`"record 1234"`) o una struct con un número de versión adelante. 149 Guardá solo al terminar una partida o al salir, no en cada cuadro (la SD es lenta). 150 151 ## Si algo falla 152 153 - Error de compilación: te lo paso tal cual. 154 - Si el programa se cuelga con **pantalla roja "CRASH (HardFault)"** muestra 155 `pc` y `lr`: suele ser un puntero inválido, índice fuera de un arreglo o pila 156 desbordada (arreglo local grande). 157 - Pantalla quieta en el log azul de arranque: el programa se colgó antes del 158 primer `hal_draw` (bucle infinito sin dibujar, o se quedó esperando algo). 159 - Para depurar podés usar `hal_log("mensaje")`: queda en el `.LOG` de la SD.