Space invaders with a nRF2401A wireless joypad

Dependencies:   Gameduino mbed nRF2401A

Fork of Gameduino_Invaders_game by Chris Dick

Gameduino and an nRF2401A hooked up to an mbed on an mbeduino:

/media/uploads/TheChrisyd/2014-03-08_22.53.54.jpg

utils.cpp

Committer:
TheChrisyd
Date:
2014-03-09
Revision:
5:3ede9991d8e0
Parent:
2:20a89dc286d5

File content as of revision 5:3ede9991d8e0:

//#include <SPI.h>

#include "utils.h"
#include "shield.h"
extern GDClass GD;
SPI spiutils(ARD_MOSI, ARD_MISO, ARD_SCK); // mosi, miso, sclk
Serial pcu(USBTX, USBRX); 
/*---------------------------------------------
  Coprocessor controller
---------------------------------------------*/
enum {
  COPPERCTRL = COMM,
  COPPERLISTSTART = COPPERCTRL,
  SAMPLEREADPOS   = COPPERCTRL+2,
  SAMPLEREADPAGE  = COPPERCTRL+4,
  COPPERLISTPTR   = COPPERCTRL+6,
  YLINEECHO       = COPPERCTRL+8,
  DUMMYCOPPERLIST = COPPERCTRL+10
};

// Available commands
enum copper_commands {
  cp_halt='@',
  cp_wait,
  cp_write8,
  cp_write16,
  cp_copy
};

#include "j1.h"
void crash()
{
  unsigned int p;
  while (1) {
    GD.wr(0,p++);
  }
}
static unsigned int samplePage_, listStart_;
void Coprocessor::reset(unsigned int spg)
{
  samplePage_ = spg;
  GD.wr(J1_RESET, 1);       // Halt the coprocessor
  GD.copy(J1_CODE, copper_code, sizeof(copper_code));
  for (unsigned int i=J1_CODE; i<J1_CODE+256; i+=2) {
    unsigned int w = GD.rd16(i);
    if      (w == 0xbf00)      { GD.wr16(i,spg+0x8000); }
    else if (w == COMM+0x8000) { listStart_ = i;        }
  }
  CopperlistBuilder d;      // Set up a fake copperlist
  GD.wr(DUMMYCOPPERLIST,cp_halt);
  GD.wr16(listStart_,DUMMYCOPPERLIST+0x8000);
  GD.wr16(SAMPLEREADPAGE,samplePage_);
  SoundController::reset();
  GD.wr(J1_RESET, 0);      // Go!
  delay(10);
  SoundController::update();
}

void Coprocessor::setCopperlist(unsigned int addr)
{
  GD.wr(J1_RESET, 1);       // Halt the coprocessor
  GD.wr16(listStart_,addr+0x8000);
  GD.wr(J1_RESET, 0);      // Go!
}

int Coprocessor::yline()
{
  return GD.rd16(YLINEECHO);
}
unsigned int Coprocessor::samplePage()
{
  return samplePage_;
}
byte Coprocessor::sampleReadPos()
{
  return GD.rd(SAMPLEREADPOS);
}

// CopperlistBuilder
void CopperlistBuilder::put(byte b)
{
  GD.wr(out++,b);
}
void CopperlistBuilder::put16(unsigned int v)
{
  put(lowByte(v));
  put(highByte(v));
}

void CopperlistBuilder::begin(unsigned int dest)
{
  out = start = dest;
#if 0
  // Debugging...
  write(0,65);
  write(1,66);
  write(2,67);
  write(3,68);
  write(4,69);
  copy(0,64,4);
  copy(64,128,3);
  copy(128,131,5);
#endif
}
void CopperlistBuilder::wait(int line)
{
  if (line > 0) {
    put(cp_wait);
    put16(line);
  }
}
void CopperlistBuilder::write(unsigned int addr, byte val)
{
  put(cp_write8);
  put(val);
  put16(addr);
}
void CopperlistBuilder::write16(unsigned int addr, unsigned int val)
{
  put(cp_write16);
  put16(val);
  put16(addr);
}
void CopperlistBuilder::copy(unsigned int src, unsigned int dst, unsigned int numBytes)
{
  if (numBytes > 0) {
    put(cp_copy);
    put16(src);
    put16(dst);
    put16(numBytes);
  }
}
void CopperlistBuilder::end(bool executeNow)
{
  put(cp_halt);      // Nice end to the list
  if (executeNow) {
    Coprocessor::setCopperlist(start);
  }
}

unsigned int CopperlistBuilder::position()
{
  return out;
}

/*---------------------------------------------
  Sound
---------------------------------------------*/
// The amount of space to leave in the buffer
#define BUFFERGAP 8

/*---------------------------------------------
  Sample playback
---------------------------------------------*/
static byte sampleWritePos;
static prog_char *sampleStart0, *samplePos0, *sampleEnd0;
static prog_char *sampleStart1, *samplePos1, *sampleEnd1;
static prog_char *sampleStart2, *samplePos2, *sampleEnd2;
static prog_char *sampleStart3, *samplePos3, *sampleEnd3;
static bool repeatSample0, repeatSample1, repeatSample2, repeatSample3;

static void writeSamples(byte num)
{
  if (num > 0) {
    GD.__wstart(Coprocessor::samplePage()+sampleWritePos);
    prog_char *s0=samplePos0, *se0=sampleEnd0;
    prog_char *s1=samplePos1, *se1=sampleEnd1;
    prog_char *s2=samplePos2, *se2=sampleEnd2;
    prog_char *s3=samplePos3, *se3=sampleEnd3;
    for (byte i=0; i<num; ++i) {
      int val = 0;
      if (s0) {
        val += (char)pgm_read_byte(s0++);
        if (s0 == se0) {
          s0 = (repeatSample0)? sampleStart0: 0;
        }
      }
      if (s1) {
        val += (char)pgm_read_byte(s1++);
        if (s1 == se1) {
          s1 = (repeatSample1)? sampleStart1: 0;
        }
      }
      if (s2) {
        val += (char)pgm_read_byte(s2++);
        if (s2 == se2) {
          s2 = (repeatSample2)? sampleStart2: 0;
        }
      }
      if (s3) {
        val += (char)pgm_read_byte(s3++);
        if (s3 == se3) {
          s3 = (repeatSample3)? sampleStart3: 0;
        }
      }
      spiutils.write(val>>2);
    }
    GD.__end();
    samplePos0 = s0;
    samplePos1 = s1;
    samplePos2 = s2;
    samplePos3 = s3;
    sampleWritePos += num;
  }
}

void SoundController::playSample(prog_char *s, int n, byte ch)
{
  bool repeat = (n < 0);
  if (repeat) {
    n = -n;
  }
  switch (ch) {
    case 0:   sampleStart0 = s;
              samplePos0 = s;
              sampleEnd0 = s+n;
              repeatSample0 = repeat;
              break;
    case 1:   sampleStart1 = s;
              samplePos1 = s;
              sampleEnd1 = s+n;
              repeatSample1 = repeat;
              break;
    case 2:   sampleStart2 = s;
              samplePos2 = s;
              sampleEnd2 = s+n;
              repeatSample2 = repeat;
              break;
    case 3:   sampleStart3 = s;
              samplePos3 = s;
              sampleEnd3 = s+n;
              repeatSample3 = repeat;
              break;
  }
}

#if SYNTHSOUNDS
/*---------------------------------------------
  Synthesized sounds
---------------------------------------------*/
static SoundPlayer *soundPlayerList=0;
ADSR::ADSR(byte a, byte d, byte s, byte r) : attack(a), decay(d), sustain(s), release(r)
{
}
byte ADSR::evaluate(unsigned int t, unsigned int r)
{
  if (t > r) {
    // In release phase...
    t -= r;
    if (t > release) {
      return 0;
    }
    t = sustain*(release-t);
    return t/release;
  }
  else if (t >= (attack+decay)) {
    // In sustain phase
    return sustain;
  }
  else if (t > attack) {
    // In decay phase
    t -= attack;
    t = (255-sustain)*t;
    return 255-(t/decay);
  }
  else if (t > 0) {
    // In attack phase
    return (t*255)/attack;
  }
  return 0;
}

SoundPlayer::SoundPlayer()
{
  volume = 255;
  active = false;
  isLinked = false;
}
SoundPlayer& SoundPlayer::setVolume(byte v)
{
  volume = v;
  return *this;
}
SoundPlayer& SoundPlayer::setSound(const Sound& s)
{
  sound = s;
  return *this;
}
void SoundPlayer::play(unsigned int p, unsigned int d)
{
  if (!isLinked) {
    // Add me to the list of sounds to be updated
    isLinked = true;
    link = soundPlayerList;
    soundPlayerList = this;
  }
  ticks = 0;
  pitch = p;
  duration = d;
  releasing = false;
  active = true;
}
void SoundPlayer::release()
{
  releasing = true;
  duration = ticks;
}
void SoundPlayer::update()
{
  if (active) {
    if (releasing) {
      if (++ticks > (duration+sound.adsr.release)) {
        stop();
      }
    }
    else {
      if (ticks!=infinite) {
        ++ticks;
      }
      if ((ticks==duration) and (duration!=infinite)) {
        release();
      }
    }
    if (active) {
      GD.__wstart(VOICES);
      spimain.write(lowByte(pitch));
      spimain.write(highByte(pitch));
      unsigned int b = sound.adsr.evaluate(ticks,duration);
      b = highByte(b*volume);
      //spimain.write(b);
      //spimain.write(b);
      GD.__end();
    }
  }
}
void SoundPlayer::stop()
{
  if (active) {
    active = false;
    GD.__wstart(VOICES+2);
    spimain.write(0);
    spimain.write(0);
    GD.__end();
  }
}
#endif
/*---------------------------------------------
  SoundController object
---------------------------------------------*/
void SoundController::reset()
{
  samplePos0 = 0;
  samplePos1 = 0;
  samplePos2 = 0;
  samplePos3 = 0;
  GD.__wstart(Coprocessor::samplePage());
  for (int i=0; i<256; ++i) {
    spiutils.write(0);
  }
  GD.__end();
#if SYNTHSOUNDS
SoundPlayer *p = soundPlayerList;
  while (p) {
    p->stop();
    p->isLinked = false;
    p = p->link;
  }
#endif
}

void SoundController::update()
{
#if SYNTHSOUNDS
  {  // Synthesized sounds
    byte b = '0';
    SoundPlayer *p = soundPlayerList;
    while (p) {
      ++b;
      p->update();
      p = p->link;
    }
    GD.wr(0,b);
  }
#endif
  { // Sampled sounds
    unsigned int rp = Coprocessor::sampleReadPos();
    unsigned int wp = sampleWritePos;
    unsigned int emptySpace = (rp-wp)&255;
    if (emptySpace > BUFFERGAP) {
      emptySpace -= BUFFERGAP;
      if ((wp+emptySpace) > 256) {
        // Write would overflow the buffer - need to split it into two
        unsigned int b = 256-wp;
        writeSamples(b);
        writeSamples(emptySpace-b);
      }
      else {
        // Can write a single block
        writeSamples(emptySpace);
      }
    }
  }
}


/*------------------------------------------------------------
  Useful little functions
------------------------------------------------------------*/
void showNumber(int n, byte x, byte y)
{
  char temp[8];
  boolean neg = (n<0);
  if (neg) {
    n = -n;
  }
  char *o = temp;
  int m = 10000;
  while (m != 0) {
    int d = n/m;
    *o++ = d+'0';
    n -= d*m;
    m /= 10;
  }
  *o-- = 0;
  // Remove leading zeros
  char *s = temp;
  while ((s<o) and (*s=='0')) {
    *s++ = ' ';
  }
  if (neg) {
    *--s = '-';
  }
  GD.__wstart((64*y)+x);
  while (*s) {
    spiutils.write(*s++);
  }
  GD.__end();
}

static void hexDigit(byte b)
{
  b = (b&0x0f)+'0';
  if (b > '9') {
    b += 'a'-('9'+1);
  }
  spiutils.write(b);
}
static void hexPair(byte b)
{
  hexDigit(b>>4);
  hexDigit(b);
}
void showHexNumber(unsigned int n, byte x, byte y)
{
  GD.__wstart((64*y)+x);
  hexPair(highByte(n));
  hexPair(lowByte(n));
  GD.__end();
}
void writeColor(int c)
{
  // Colors are five bits - encode in base 32
  c = (c&31)+'0';
  if (c > '9') {
    c += 'A'-('9'+1);
  }
  pcu.printf("%d", c);
}
void sendScreenshot()
{
  pcu.baud(115200);
  for (int i=0; i<300; ++i) {
    // Ask for the line...
    GD.wr16(SCREENSHOT_Y, 0x8000|i);
    // Wait for it to appear
    while ((GD.rd(SCREENSHOT_Y+1)&0x80)==0) {
      delay(1);
    }
    // Send the line of pixels to the serial port
    for (int x=0; x<400; ++x) {
      uint16_t pixel = GD.rd16(SCREENSHOT+(x*2));
      writeColor(pixel>>10);  // Red
      writeColor(pixel>>5);   // Green
      writeColor(pixel);      // Blue
    }
    pcu.printf("\n");
  }
  // Restore sanity
  GD.wr16(SCREENSHOT_Y, 0);
}