Invaders game for the Gameduino

Dependencies:   Gameduino mbed

Revision:
1:f44175dd69fd
Parent:
0:8a7c58553b44
Child:
2:20a89dc286d5
--- a/utils.cpp	Thu Jun 21 19:13:34 2012 +0000
+++ b/utils.cpp	Sat Sep 29 13:01:42 2012 +0000
@@ -1,488 +1,488 @@
-//#include <SPI.h>
-
-#include "utils.h"
-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);
-}
+//#include <SPI.h>
+
+#include "utils.h"
+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);
+}