Port of Artekit space invaders demo (http://www.artekit.eu/space-invaders-for-stm32/) for the STM32F3 Discovery board. Also shows game of life if started with the user button pressed.

Dependencies:   STM32F3-Discovery

conway.c

Committer:
MartinJohnson
Date:
2016-05-17
Revision:
2:1c1f7677ac17
Parent:
1:1b37c4b989b4

File content as of revision 2:1c1f7677ac17:

#include "stm32f30x.h"
#include "video.h"

typedef unsigned Unit;

#define cols 416
#define rows 300
#define bits 32

extern char *fb[VID_VSIZE*2];

typedef struct {
  Unit sum;
  Unit carry;
} AddResult;

static void half_add(AddResult *c,Unit a, Unit b) {
  c->sum=a^b;
  c->carry=a&b;
}

static void full_add(AddResult *d, Unit a, Unit b, Unit c) {
  AddResult r0,r1;
  half_add(&r0,a, b);
  half_add(&r1,r0.sum, c);
  d->sum=r1.sum;
  d->carry=r0.carry | r1.carry;
}

static Unit col_step(Unit above[3],
                     Unit current[3],
                     Unit below[3]) {
  AddResult a_inf,b_inf,c_inf,next0,next1a,next1b;
  /*
   * Compute row-wise influence sums.  This produces 96 2-bit sums (represented
   * as three pairs of 32-vectors) giving the number of live cells in the 1D
   * Moore neighborhood around each position.
   */
  full_add(&a_inf,(above[1] << 1) | (above[0] >> (bits - 1)),
                             above[1],
                             (above[1] >> 1) | (above[2] << (bits - 1)));
  half_add(&c_inf,(current[1] << 1) | (current[0] >> (bits - 1)),
                             /* middle bits of current[1] don't count */
                             (current[1] >> 1) | (current[2] << (bits - 1)));
  full_add(&b_inf,(below[1] << 1) | (below[0] >> (bits - 1)),
                             below[1],
                             (below[1] >> 1) | (below[2] << (bits - 1)));

  /*
   * Sum the row-wise sums into a two-dimensional Moore neighborhood population
   * count.  Such a count can overflow into four bits, but we don't care: Conway
   * has the same result for 8/9 and 0/1 (the cell is cleared in both cases).
   *
   * Thus, we don't need a four-bit addition.  Instead, we just retain the
   * carry output from the two intermediate additions and use it as a mask.
   */
  full_add(&next0,a_inf.sum, c_inf.sum, b_inf.sum);
  full_add(&next1a,a_inf.carry, next0.carry, b_inf.carry);
  half_add(&next1b,c_inf.carry, next1a.sum);

  /*
   * Apply Niemiec's optimization: OR the current cell state vector into the
   * 9-cell neighborhoold population count to derive the new state cheaply.  The
   * cell is set iff its three-bit sum is 0b011.
   */
  return (next0.sum | current[1])
       & next1b.sum
       & ~next1a.carry
       & ~next1b.carry;
}



static void step(Unit const *current_map,
                 Unit *next_map,
                 Unit width,
                 Unit height) {
  // We keep sliding windows of state in these arrays.
  Unit above[3]={ 0, 0, 0 };
  Unit current[3]={ 0, 0, 0 };
  Unit below[3]={ 0, 0, 0 };
  unsigned x,y;

  // Bootstrap for first column of first row.
  current[0] = current[1] = 0;
  current[2] = current_map[0];

  below[0] = below[1] = 0;
  below[2] = current_map[width];

  #define ADV(name, next) \
    name[0] = name[1]; \
    name[1] = name[2]; \
    name[2] = (next)

  // First row, wherein above[x] = 0, less final column
  for (x = 0; x < width - 1; ++x) {
    ADV(current, current_map[x + 1]);
    ADV(below,   current_map[width + x + 1]);
    next_map[x] = col_step(above, current, below);
  }


  // Final column of first row, wherein we cannot fetch next values.
  ADV(current, 0);
  ADV(below, 0);
  next_map[width - 1] = col_step(above, current, below);

  // Remaining rows except the last.
  for (y = 1; y < height - 1; ++y) {
    unsigned offset = y * width;

    // Bootstrap row like we did for row 1.
    above[0] = above[1] = 0;
    current[0] = current[1] = 0;
    below[0] = below[1] = 0;

    above[2] = current_map[offset - width];
    current[2] = current_map[offset];
    below[2] = current_map[offset + width];

    for (x = 0; x < width - 1; ++x) {
      ADV(above, current_map[offset - width + x + 1]);
      ADV(current, current_map[offset + x + 1]);
      ADV(below, current_map[offset + width + x + 1]);
      next_map[offset + x] = col_step(above, current, below);
    }

    // Last column.
    ADV(above, 0);
    ADV(current, 0);
    ADV(below, 0);
    next_map[offset + width - 1] = col_step(above, current, below);
  }

  // Final row, wherein below[x] = 0.
  unsigned offset = width * (height - 1);
  above[0] = above[1] = 0;
  current[0] = current[1] = 0;
  below[0] = below[1] = below[2] = 0;

  above[2] = current_map[offset - width];
  current[2] = current_map[offset];

  for (x = 0; x < width - 1; ++x) {
    ADV(above, current_map[offset - width + x + 1]);
    ADV(current, current_map[offset + x + 1]);
    next_map[offset + x] = col_step(above, current, below);
  }

  // Final column
  ADV(above, 0);
  ADV(current, 0);
  next_map[offset + width - 1] = col_step(above, current, below);

  #undef ADV
}

extern unsigned fboffset;

void conway_demo() {
	Unit *current=(Unit *)fb[0];
	Unit *next=(Unit *)fb[rows];
	SPI1->CR1 |= SPI_FirstBit_LSB;

	while(1) {
		step(current,next,cols/bits,rows);
		fboffset=300*52;
		sysDelayMs(1);
		step(next,current,cols/bits,rows);
		fboffset=0;
		sysDelayMs(1);
	}
}