Files
picovga-RGsB/examples/vga_life/src/main.cpp
2025-09-28 17:21:28 -07:00

484 lines
9.6 KiB
C++

// ****************************************************************************
//
// Main code
//
// ****************************************************************************
#include "main.h"
#include "pico/printf.h"
#include <string.h>
// copy of tiles images
u8 TilesImg_Copy[sizeof(Tiles16Img)] __attribute__ ((aligned(4)));
// game board
u8 Board[MAPSIZE];
// neighbors
u8 Neigh[MAPSIZE];
// slots
u8 Slot[MAPSIZE*SLOTNUM];
int SelSlot;
// clipboard
u8 Clip[MAPSIZE];
// cursor
int CurX, CurY;
// samples (first 2 bytes = width, height)
const u8 Samples[] = {
// 0: Life
5,5,
1,1,1,1,1,
1,1,1,1,1,
1,1,1,1,1,
1,1,1,1,1,
1,1,1,1,1,
// 1: penta-decathlon
3,12,
1,1,1,
0,1,0,
0,1,0,
1,1,1,
0,0,0,
1,1,1,
1,1,1,
0,0,0,
1,1,1,
0,1,0,
0,1,0,
1,1,1,
// 2: R-pentomino
3,3,
0,1,1,
1,1,0,
0,1,0,
// 3: Diehard
8,3,
0,0,0,0,0,0,1,0,
1,1,0,0,0,0,0,0,
0,1,0,0,0,1,1,1,
// 4. Acorn
7,3,
0,1,0,0,0,0,0,
0,0,0,1,0,0,0,
1,1,0,0,1,1,1,
// 5: Glider
3,3,
0,0,1,
1,0,1,
0,1,1,
// 6: spaceship
5,4,
1,0,0,1,0,
0,0,0,0,1,
1,0,0,0,1,
0,1,1,1,1,
// 7: pulsar
15,15,
0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,
0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,
0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,1,1,0,0,1,1,0,1,1,0,0,1,1,1,
0,0,1,0,1,0,1,0,1,0,1,0,1,0,0,
0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,
0,0,1,0,1,0,1,0,1,0,1,0,1,0,0,
1,1,1,0,0,1,1,0,1,1,0,0,1,1,1,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,
0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,
0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,
// 8: Gosper glider gun
38, 9,
0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,1,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,1,0,1,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0, 0,0,0,1,1,0,0,0,0,0, 0,1,1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0, 0,0,1,0,0,0,1,0,0,0, 0,1,1,0,0,0,0,0,0,0, 0,0,0,0,0,1,1,0,
0,1,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,1,0,0, 0,1,1,0,0,0,0,0,0,0, 0,0,0,0,0,1,1,0,
0,1,1,0,0,0,0,0,0,0, 0,1,0,0,0,1,0,1,1,0, 0,0,0,1,0,1,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,1,0,0, 0,0,0,0,0,1,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0, 0,0,1,0,0,0,1,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0, 0,0,0,1,1,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
// 9: Simkin glider gun
33,21,
1,1,0,0,0,0,0,1,1,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,
1,1,0,0,0,0,0,1,1,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,
0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,
0,0,0,0,1,1,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,
0,0,0,0,1,1,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,
0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,
0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,
0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,
0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,
0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,1,1,0,1,1,0,0,0, 0,0,0,
0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,1,0,0, 0,0,0,
0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,1,0, 0,1,1,
0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,1,1,1,0,0,0,1,0,0, 0,1,1,
0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,1,0,0,0, 0,0,0,
0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,
0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,
0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,
0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 1,1,0,0,0,0,0,0,0,0, 0,0,0,
0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 1,0,0,0,0,0,0,0,0,0, 0,0,0,
0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,1,1,1,0,0,0,0,0,0, 0,0,0,
0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,1,0,0,0,0,0,0, 0,0,0,
};
/*
// initialize videomode
void VideoInit()
{
// setup videomode
VgaCfgDef(&Cfg); // get default configuration
Cfg.video = &DRV; // video timings
Cfg.width = WIDTH; // screen width
Cfg.height = HEIGHT; // screen height
VgaCfg(&Cfg, &Vmode); // calculate videomode setup
// initialize base layer 0 to tiles
ScreenClear(pScreen);
sStrip* t = ScreenAddStrip(pScreen, HEIGHT);
sSegm* g = ScreenAddSegm(t, WIDTH);
ScreenSegmTile2(g, Board, TilesImg_Copy, TILESIZE, TILESIZE, TILESIZE*TILE_NUM, MAPW);
// initialize system clock
set_sys_clock_pll(Vmode.vco*1000, Vmode.pd1, Vmode.pd2);
// initialize videomode
VgaInitReq(&Vmode);
}
*/
// display help
void DispHelp()
{
printf("\n");
printf("%c ... right\n", KEY_R);
printf("%c ... up\n", KEY_U);
printf("%c ... left\n", KEY_L);
printf("%c ... down\n", KEY_D);
printf("space ... flip state\n");
printf("Enter .. run life\n");
printf("0..9 ... select slot\n");
printf("%c ... copy to clipboard\n", KEY_COPY);
printf("%c ... paste from clipboard\n", KEY_PASTE);
printf("%c ... clear board\n", KEY_CLEAR);
printf("Selected slot: %u\n", SelSlot);
printf("Rules: 3->new, 2->stable, other->dead\n");
}
// get character from keyboard (0 = no key)
char GetChar()
{
char c = getchar_timeout_us(0);
if (c == (char)PICO_ERROR_TIMEOUT) c = 0;
if ((c >= 'a') && (c <= 'z')) c -= 32;
return c;
}
// flush characters from keyboard
void FlushChar()
{
while (GetChar() != 0) {}
}
// set cursor on
void CurOn()
{
u8* d = &Board[CurX + CurY*MAPW];
u8 b = *d;
if (b == TILE_FULL)
b = TILE_SELFULL;
else if (b == TILE_EMPTY)
b = TILE_SELEMPTY;
*d = b;
}
// set cursor off
void CurOff()
{
u8* d = &Board[CurX + CurY*MAPW];
u8 b = *d;
if (b == TILE_SELFULL)
b = TILE_FULL;
else if (b == TILE_SELEMPTY)
b = TILE_EMPTY;
*d = b;
}
// flip cursor
void CurFlip()
{
u8* d = &Board[CurX + CurY*MAPW];
u8 b = *d;
if (b == TILE_SELFULL)
b = TILE_SELEMPTY;
else if (b == TILE_SELEMPTY)
b = TILE_SELFULL;
*d = b;
}
// get cell on coordinates (0 or 1)
int Cell(int x, int y)
{
if ((x < 0) || (x >= MAPW) || (y < 0) || (y >= MAPH)) return 0;
u8* d = &Board[x + y*MAPW];
u8 b = *d;
return (b == TILE_FULL) ? 1 : 0;
}
// save to current slot
void SaveSlot()
{
memcpy(&Slot[MAPSIZE*SelSlot], Board, MAPSIZE);
}
// load from current slot
void LoadSlot()
{
memcpy(Board, &Slot[MAPSIZE*SelSlot], MAPSIZE);
}
// run life
void Run()
{
int x, y;
u8 b;
u8* d;
u8* s;
FlushChar();
// break with a key
while (GetChar() == 0)
{
// calculate neighbors
d = Neigh;
for (y = 0; y < MAPH; y++)
{
for (x = 0; x < MAPW; x++)
{
*d++ = Cell(x-1, y-1) +
Cell(x , y-1) +
Cell(x+1, y-1) +
Cell(x-1, y ) +
Cell(x+1, y ) +
Cell(x-1, y+1) +
Cell(x , y+1) +
Cell(x+1, y+1);
}
}
// update cells
s = Neigh;
d = Board;
for (x = MAPSIZE; x > 0; x--)
{
b = *s++;
// 3 -> new cell
if (b == 3)
*d = TILE_FULL;
// 2 -> unchanged
else if (b == 2)
;
// else -> dead
else
*d = TILE_EMPTY;
d++;
}
// delay
sleep_ms(SPEED);
}
}
// initialize slots by samples
void InitSlot()
{
int slot;
u8* d;
const u8* s = Samples;
int w, h;
for (slot = 0; slot < SLOTNUM; slot++)
{
// destination slot
d = &Slot[MAPSIZE*slot];
// sample dimension
w = *s++;
h = *s++;
// center image
d += (MAPW-w)/2 + (MAPH-h)/2*MAPW;
// copy sample
for (; h > 0; h--)
{
memcpy(d, s, w);
d += MAPW;
s += w;
}
}
}
int main()
{
// initialize stdio
stdio_init_all();
char ch;
// copy tiles images to RAM buffer (flash would be too slow)
memcpy(TilesImg_Copy, Tiles16Img, sizeof(Tiles16Img));
// clear buffer
memset(Board, TILE_EMPTY, MAPSIZE);
memset(Slot, TILE_EMPTY, MAPSIZE*SLOTNUM);
memset(Clip, TILE_EMPTY, MAPSIZE);
CurX = MAPW/2;
CurY = MAPH/2;
SelSlot = 1;
// fill slots with samples
InitSlot();
// load current slot
LoadSlot();
// run VGA core
// StartVgaCore();
// initialize videomode
// VideoInit();
Video(DEV_VGA, RES_VGA, FORM_TILE16, Board, TilesImg_Copy);
// display help
DispHelp();
// set cursor on
CurOn();
// loop with demo scene
while (True)
{
// keyboard
ch = GetChar();
if (ch != 0)
{
switch (ch)
{
// right
case KEY_R:
CurOff(); // set cursor off
CurX++;
if (CurX >= MAPW) CurX = 0;
CurOn(); // set cursor on
break;
// left
case KEY_L:
CurOff(); // set cursor off
CurX--;
if (CurX < 0) CurX = MAPW-1;
CurOn(); // set cursor on
break;
// down
case KEY_D:
CurOff(); // set cursor off
CurY++;
if (CurY >= MAPH) CurY = 0;
CurOn(); // set cursor on
break;
// up
case KEY_U:
CurOff(); // set cursor off
CurY--;
if (CurY < 0) CurY = MAPH-1;
CurOn(); // set cursor on
break;
// toggle
case ' ':
CurFlip(); // flip cursor
break;
// run
case 13:
CurOff(); // set cursor off
SaveSlot(); // save state
Run(); // run life
LoadSlot(); // restore state
CurOn(); // set cursor on
break;
// select slot
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
CurOff(); // set cursor off
SaveSlot();
SelSlot = ch - '0';
LoadSlot();
CurOn(); // set cursor on
printf("Selected slot: %u\n", SelSlot);
break;
// copy to clipboard
case KEY_COPY:
CurOff(); // set cursor off
memcpy(Clip, Board, MAPSIZE);
CurOn(); // set cursor on
break;
// paste from clipboard
case KEY_PASTE:
CurOff(); // set cursor off
memcpy(Board, Clip, MAPSIZE);
CurOn(); // set cursor on
break;
// clear board
case KEY_CLEAR:
CurOff(); // set cursor off
memset(Board, TILE_EMPTY, MAPSIZE);
CurOn(); // set cursor on
break;
// help
default:
DispHelp();
break;
}
}
}
}