Chris Dick
/
Gameduino_Invaders_game
Invaders game for the Gameduino
Diff: utils.cpp
- Revision:
- 0:8a7c58553b44
- Child:
- 1:f44175dd69fd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/utils.cpp Thu Jun 21 19:13:34 2012 +0000 @@ -0,0 +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); +}