
#define SSD1306_128X32
#include "ch32v003fun.h"
#include <stdio.h>
#include "ssd1306_i2c.h"
#include "ssd1306.h"

#define PIN_LED PC4
#define PIN_BTN PC7

#define u8 uint8_t
#define u16 uint16_t

uint8_t gol_tmp[sizeof(ssd1306_buffer)];

uint32_t lfsr = 1; // PRNG state

u8 rand8();
u8 get_pixel(u8 x, u8 y);
void gol_step();
void r_pentomino();
void randomise();

int main()
{
	SystemInit();
	funGpioInitAll();
	funPinMode(PIN_LED, GPIO_CFGLR_OUT_10Mhz_PP);
	funPinMode(PIN_BTN, GPIO_CFGLR_IN_PUPD);

	ssd1306_i2c_init();
	ssd1306_init();
	randomise();

	while (1)
	{
		ssd1306_refresh();
		gol_step();
		memcpy(ssd1306_buffer, gol_tmp, sizeof(ssd1306_buffer));

		if (funDigitalRead(PIN_BTN))
		{
			randomise();
			ssd1306_refresh();
			funDigitalWrite(PIN_LED, FUN_HIGH);
			Delay_Ms(100);
			funDigitalWrite(PIN_LED, FUN_LOW);
		}
	}
}

void r_pentomino()
{
	ssd1306_setbuf(0);
	ssd1306_drawPixel(20, 20, 1);
	ssd1306_drawPixel(21, 20, 1);
	ssd1306_drawPixel(21, 21, 1);
	ssd1306_drawPixel(22, 21, 1);
	ssd1306_drawPixel(21, 22, 1);
}

void randomise()
{
	lfsr = SysTick->CNT;
	for (u16 b = 0; b < sizeof(ssd1306_buffer); b++)
	{
		ssd1306_buffer[b] = rand8();
	}
}

u8 get_pixel(u8 x, u8 y)
{
	x = x & 127;
	y = y & 31;
	u8 slice = ssd1306_buffer[(y / 8) * 128 + x];
	return (slice & (1 << (y & 7))) != 0;
}

void gol_step()
{
	// slow pixel-by-pixel implementation
	for (u8 x = 0; x < 128; x++)
	{
		for (u8 y = 0; y < 32; y++)
		{
			u8 this = get_pixel(x, y);
			u8 sum = 0;
			sum += get_pixel(x + 127, y + 31);
			sum += get_pixel(x + 127, y);
			sum += get_pixel(x + 127, y + 1);
			sum += get_pixel(x, y + 31);
			sum += get_pixel(x, y + 1);
			sum += get_pixel(x + 1, y + 31);
			sum += get_pixel(x + 1, y);
			sum += get_pixel(x + 1, y + 1);

			u8 new_state = (sum == 3) | ((sum == 2) & this);
			u16 addr = x + (y / 8) * 128;

			if (new_state)
				gol_tmp[addr] |= (1 << (y & 7));
			else
				gol_tmp[addr] &= ~(1 << (y & 7));
		}
	}
}

/* White Noise Generator State */
#define NOISE_BITS 8
#define NOISE_MASK ((1 << NOISE_BITS) - 1)
#define NOISE_POLY_TAP0 31
#define NOISE_POLY_TAP1 21
#define NOISE_POLY_TAP2 1
#define NOISE_POLY_TAP3 0
/*
 * random byte generator, taken from ch32v003fun examples
 */
uint8_t rand8(void)
{
	uint8_t bit;
	uint32_t new_data;

	for (bit = 0; bit < NOISE_BITS; bit++)
	{
		new_data = ((lfsr >> NOISE_POLY_TAP0) ^
					(lfsr >> NOISE_POLY_TAP1) ^
					(lfsr >> NOISE_POLY_TAP2) ^
					(lfsr >> NOISE_POLY_TAP3));
		lfsr = (lfsr << 1) | (new_data & 1);
	}

	return lfsr & NOISE_MASK;
}