Space invaders with a nRF2401A wireless joypad
Dependencies: Gameduino mbed nRF2401A
Fork of Gameduino_Invaders_game by
Gameduino and an nRF2401A hooked up to an mbed on an mbeduino:
utils.h@1:f44175dd69fd, 2012-09-29 (annotated)
- Committer:
- TheChrisyd
- Date:
- Sat Sep 29 13:01:42 2012 +0000
- Revision:
- 1:f44175dd69fd
- Parent:
- 0:8a7c58553b44
- Child:
- 2:20a89dc286d5
saved progress
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
TheChrisyd | 1:f44175dd69fd | 1 | #ifndef COM_ARTLUM_GDUTILS_INCLUDED |
TheChrisyd | 1:f44175dd69fd | 2 | #define COM_ARTLUM_GDUTILS_INCLUDED |
TheChrisyd | 1:f44175dd69fd | 3 | #include "arduino.h" |
TheChrisyd | 1:f44175dd69fd | 4 | #include "GD.h" |
TheChrisyd | 1:f44175dd69fd | 5 | //#include "WProgram.h" |
TheChrisyd | 1:f44175dd69fd | 6 | |
TheChrisyd | 1:f44175dd69fd | 7 | /*--------------------------------------------------- |
TheChrisyd | 1:f44175dd69fd | 8 | Coprocessor controller - makes it easy to do |
TheChrisyd | 1:f44175dd69fd | 9 | raster effects (eg. split screens) without |
TheChrisyd | 1:f44175dd69fd | 10 | writing any FORTH. |
TheChrisyd | 1:f44175dd69fd | 11 | |
TheChrisyd | 1:f44175dd69fd | 12 | The way this works is that you build a list of |
TheChrisyd | 1:f44175dd69fd | 13 | instructions in memory (a "copperlist") and |
TheChrisyd | 1:f44175dd69fd | 14 | the coprocessor will execute the list on every |
TheChrisyd | 1:f44175dd69fd | 15 | video frame. |
TheChrisyd | 1:f44175dd69fd | 16 | |
TheChrisyd | 1:f44175dd69fd | 17 | Code samples: |
TheChrisyd | 1:f44175dd69fd | 18 | |
TheChrisyd | 1:f44175dd69fd | 19 | void setup() { |
TheChrisyd | 1:f44175dd69fd | 20 | ... |
TheChrisyd | 1:f44175dd69fd | 21 | // Make a copperlist to change a palette color on line 100 |
TheChrisyd | 1:f44175dd69fd | 22 | // The copperlist is stored on the Gameduino at address 0x3f80 |
TheChrisyd | 1:f44175dd69fd | 23 | Coprocessor::reset(); |
TheChrisyd | 1:f44175dd69fd | 24 | CopperlistBuilder cp; |
TheChrisyd | 1:f44175dd69fd | 25 | cp.begin(0x3f80); |
TheChrisyd | 1:f44175dd69fd | 26 | cp.wait(100); |
TheChrisyd | 1:f44175dd69fd | 27 | cp.write16(PALETTE4A, 0x7fff); |
TheChrisyd | 1:f44175dd69fd | 28 | cp.end(); // And start using the new copperlist |
TheChrisyd | 1:f44175dd69fd | 29 | } |
TheChrisyd | 1:f44175dd69fd | 30 | |
TheChrisyd | 1:f44175dd69fd | 31 | Modifying an existing copperlist: |
TheChrisyd | 1:f44175dd69fd | 32 | |
TheChrisyd | 1:f44175dd69fd | 33 | // Make a copperlist to set the horizontal scroll register |
TheChrisyd | 1:f44175dd69fd | 34 | // on line 220 and modify the copperlist. |
TheChrisyd | 1:f44175dd69fd | 35 | // a) Build the list |
TheChrisyd | 1:f44175dd69fd | 36 | unsigned int XscrollInst; |
TheChrisyd | 1:f44175dd69fd | 37 | void setup() { |
TheChrisyd | 1:f44175dd69fd | 38 | ... |
TheChrisyd | 1:f44175dd69fd | 39 | CopperlistBuilder cp; |
TheChrisyd | 1:f44175dd69fd | 40 | cp.begin(0x3f80); |
TheChrisyd | 1:f44175dd69fd | 41 | cp.wait(220); |
TheChrisyd | 1:f44175dd69fd | 42 | XscrollInst = cp.position(); // Get current output location |
TheChrisyd | 1:f44175dd69fd | 43 | cp.write16(SCROLL_X, 0); |
TheChrisyd | 1:f44175dd69fd | 44 | cp.end(); |
TheChrisyd | 1:f44175dd69fd | 45 | } |
TheChrisyd | 1:f44175dd69fd | 46 | // b) Modify the list with current 'xscroll' |
TheChrisyd | 1:f44175dd69fd | 47 | void loop() { |
TheChrisyd | 1:f44175dd69fd | 48 | CopperlistBuilder cp; |
TheChrisyd | 1:f44175dd69fd | 49 | cp.begin(XscrollInst) // Where the 'write16()' instruction is |
TheChrisyd | 1:f44175dd69fd | 50 | cp.write16(SCROLL_X, xscroll); // Overwrite the previous instruction |
TheChrisyd | 1:f44175dd69fd | 51 | } |
TheChrisyd | 1:f44175dd69fd | 52 | |
TheChrisyd | 1:f44175dd69fd | 53 | Notes: |
TheChrisyd | 1:f44175dd69fd | 54 | The Coprocessor object uses the "COMM" block |
TheChrisyd | 1:f44175dd69fd | 55 | of memory at 0x2980 so don't mess with it. |
TheChrisyd | 1:f44175dd69fd | 56 | |
TheChrisyd | 1:f44175dd69fd | 57 | The coprocessor also provides support for the |
TheChrisyd | 1:f44175dd69fd | 58 | sound functions below. |
TheChrisyd | 1:f44175dd69fd | 59 | ---------------------------------------------------*/ |
TheChrisyd | 1:f44175dd69fd | 60 | class Coprocessor { |
TheChrisyd | 1:f44175dd69fd | 61 | public: |
TheChrisyd | 1:f44175dd69fd | 62 | // This initializes everything and load the coprocessor code. |
TheChrisyd | 1:f44175dd69fd | 63 | // You should call this in your setup() function after "GD.begin();" |
TheChrisyd | 1:f44175dd69fd | 64 | // |
TheChrisyd | 1:f44175dd69fd | 65 | // The sample page is the page of Gameduino memory to |
TheChrisyd | 1:f44175dd69fd | 66 | // use as a buffer for sample playback. It *must* be |
TheChrisyd | 1:f44175dd69fd | 67 | // on a page boundry (ie. bottom 8 bits are 0) and |
TheChrisyd | 1:f44175dd69fd | 68 | // the whole page will be used so you can't use it |
TheChrisyd | 1:f44175dd69fd | 69 | // for anything else. |
TheChrisyd | 1:f44175dd69fd | 70 | static void reset(unsigned int samplePage=0x3f00); // Default: Use memory from 0x3f00 to 0x3fff |
TheChrisyd | 1:f44175dd69fd | 71 | |
TheChrisyd | 1:f44175dd69fd | 72 | // Select a copperlist. The list will start running on the next video frame. |
TheChrisyd | 1:f44175dd69fd | 73 | // nb. The "CopperlistBuilder::end()" function normally calls this automatically |
TheChrisyd | 1:f44175dd69fd | 74 | // (unless you tell it not to...) |
TheChrisyd | 1:f44175dd69fd | 75 | static void setCopperlist(unsigned int addr); |
TheChrisyd | 1:f44175dd69fd | 76 | |
TheChrisyd | 1:f44175dd69fd | 77 | // Where the sample playback page is located |
TheChrisyd | 1:f44175dd69fd | 78 | static unsigned int samplePage(); |
TheChrisyd | 1:f44175dd69fd | 79 | |
TheChrisyd | 1:f44175dd69fd | 80 | // Where the coprocessor is currently reading samples |
TheChrisyd | 1:f44175dd69fd | 81 | // from in the sample page. Use this info to keep the |
TheChrisyd | 1:f44175dd69fd | 82 | // page full of fresh sample data for playback |
TheChrisyd | 1:f44175dd69fd | 83 | static byte sampleReadPos(); |
TheChrisyd | 1:f44175dd69fd | 84 | |
TheChrisyd | 1:f44175dd69fd | 85 | // A copy of the coprocessor YLINE register |
TheChrisyd | 1:f44175dd69fd | 86 | static int yline(); |
TheChrisyd | 1:f44175dd69fd | 87 | }; |
TheChrisyd | 1:f44175dd69fd | 88 | |
TheChrisyd | 1:f44175dd69fd | 89 | // This object helps you make a copperlist in memory |
TheChrisyd | 1:f44175dd69fd | 90 | class CopperlistBuilder { |
TheChrisyd | 1:f44175dd69fd | 91 | unsigned int out,start; |
TheChrisyd | 1:f44175dd69fd | 92 | void put(byte); |
TheChrisyd | 1:f44175dd69fd | 93 | void put16(unsigned int); |
TheChrisyd | 1:f44175dd69fd | 94 | public: |
TheChrisyd | 1:f44175dd69fd | 95 | // Available commands are: |
TheChrisyd | 1:f44175dd69fd | 96 | // * Wait for a raster line |
TheChrisyd | 1:f44175dd69fd | 97 | // * Write a byte to a memory location |
TheChrisyd | 1:f44175dd69fd | 98 | // * Write a 16-bit word to a memory location |
TheChrisyd | 1:f44175dd69fd | 99 | // * Copy a block of memory from one place to another |
TheChrisyd | 1:f44175dd69fd | 100 | void begin(unsigned int dest); |
TheChrisyd | 1:f44175dd69fd | 101 | void wait(int line); |
TheChrisyd | 1:f44175dd69fd | 102 | void write(unsigned int addr, byte val); |
TheChrisyd | 1:f44175dd69fd | 103 | void write16(unsigned int addr, unsigned int val); |
TheChrisyd | 1:f44175dd69fd | 104 | void copy(unsigned int src, unsigned int dst, unsigned int numBytes); |
TheChrisyd | 1:f44175dd69fd | 105 | void end(bool executeNow=true); |
TheChrisyd | 1:f44175dd69fd | 106 | |
TheChrisyd | 1:f44175dd69fd | 107 | // Where I'm currently outputting |
TheChrisyd | 1:f44175dd69fd | 108 | unsigned int position(); |
TheChrisyd | 1:f44175dd69fd | 109 | }; |
TheChrisyd | 1:f44175dd69fd | 110 | |
TheChrisyd | 1:f44175dd69fd | 111 | |
TheChrisyd | 1:f44175dd69fd | 112 | /*------------------------------------------------------------------ |
TheChrisyd | 1:f44175dd69fd | 113 | Sound functions - play synthesized sounds and sound samples |
TheChrisyd | 1:f44175dd69fd | 114 | |
TheChrisyd | 1:f44175dd69fd | 115 | These functions require support from the Coprocessor object |
TheChrisyd | 1:f44175dd69fd | 116 | above. If you load different microcode they'll stop working... |
TheChrisyd | 1:f44175dd69fd | 117 | ------------------------------------------------------------------*/ |
TheChrisyd | 1:f44175dd69fd | 118 | |
TheChrisyd | 1:f44175dd69fd | 119 | /*------------------------------------------------------------------ |
TheChrisyd | 1:f44175dd69fd | 120 | Synthesized sounds |
TheChrisyd | 1:f44175dd69fd | 121 | ------------------------------------------------------------------*/ |
TheChrisyd | 1:f44175dd69fd | 122 | #define SYNTHSOUNDS 0 |
TheChrisyd | 1:f44175dd69fd | 123 | #if SYNTHSOUNDS |
TheChrisyd | 1:f44175dd69fd | 124 | // ADSR volume envelope |
TheChrisyd | 1:f44175dd69fd | 125 | struct ADSR { |
TheChrisyd | 1:f44175dd69fd | 126 | byte attack; // Attack time (in clock ticks) |
TheChrisyd | 1:f44175dd69fd | 127 | byte decay; // Decay time (in clock ticks) |
TheChrisyd | 1:f44175dd69fd | 128 | byte sustain; // Sustain level [0..255] |
TheChrisyd | 1:f44175dd69fd | 129 | byte release; // Release time (in clock ticks) |
TheChrisyd | 1:f44175dd69fd | 130 | ADSR(byte a=4, byte d=4, byte s=90, byte r=30); |
TheChrisyd | 1:f44175dd69fd | 131 | byte evaluate(unsigned int elapsedTime, unsigned int releaseTime); |
TheChrisyd | 1:f44175dd69fd | 132 | }; |
TheChrisyd | 1:f44175dd69fd | 133 | |
TheChrisyd | 1:f44175dd69fd | 134 | // Change pitch of a sound as it plays |
TheChrisyd | 1:f44175dd69fd | 135 | struct PitchModulator { |
TheChrisyd | 1:f44175dd69fd | 136 | // Vibrato |
TheChrisyd | 1:f44175dd69fd | 137 | byte vibratoDelay; // Delay before vibrato starts (in clock ticks) |
TheChrisyd | 1:f44175dd69fd | 138 | byte vibrato; // Amount of vibrato (percentage of note pitch) |
TheChrisyd | 1:f44175dd69fd | 139 | char vibratoDelta; // Added to 'vibrato' every clock tick |
TheChrisyd | 1:f44175dd69fd | 140 | // Pitch Sweep |
TheChrisyd | 1:f44175dd69fd | 141 | byte sweepDelay; // Delay before sweep starts (in clock ticks) |
TheChrisyd | 1:f44175dd69fd | 142 | byte sweep; |
TheChrisyd | 1:f44175dd69fd | 143 | char sweepDelta; |
TheChrisyd | 1:f44175dd69fd | 144 | PitchModulator() { |
TheChrisyd | 1:f44175dd69fd | 145 | vibratoDelay = 0; |
TheChrisyd | 1:f44175dd69fd | 146 | vibrato = 0; |
TheChrisyd | 1:f44175dd69fd | 147 | vibratoDelta = 0; |
TheChrisyd | 1:f44175dd69fd | 148 | } |
TheChrisyd | 1:f44175dd69fd | 149 | int evaluate(int startpitch, unsigned int time); |
TheChrisyd | 1:f44175dd69fd | 150 | }; |
TheChrisyd | 1:f44175dd69fd | 151 | |
TheChrisyd | 1:f44175dd69fd | 152 | // All the parameters for a sound |
TheChrisyd | 1:f44175dd69fd | 153 | struct Sound { |
TheChrisyd | 1:f44175dd69fd | 154 | ADSR adsr; |
TheChrisyd | 1:f44175dd69fd | 155 | PitchModulator pm; |
TheChrisyd | 1:f44175dd69fd | 156 | }; |
TheChrisyd | 1:f44175dd69fd | 157 | |
TheChrisyd | 1:f44175dd69fd | 158 | class SoundPlayer { |
TheChrisyd | 1:f44175dd69fd | 159 | // Playback parameters |
TheChrisyd | 1:f44175dd69fd | 160 | Sound sound; |
TheChrisyd | 1:f44175dd69fd | 161 | byte volume; |
TheChrisyd | 1:f44175dd69fd | 162 | unsigned int pitch; |
TheChrisyd | 1:f44175dd69fd | 163 | unsigned int duration; |
TheChrisyd | 1:f44175dd69fd | 164 | // Internal vars |
TheChrisyd | 1:f44175dd69fd | 165 | unsigned int ticks; |
TheChrisyd | 1:f44175dd69fd | 166 | bool active, releasing; |
TheChrisyd | 1:f44175dd69fd | 167 | // Sound players form a linked list |
TheChrisyd | 1:f44175dd69fd | 168 | bool isLinked; |
TheChrisyd | 1:f44175dd69fd | 169 | SoundPlayer *link; |
TheChrisyd | 1:f44175dd69fd | 170 | // The SoundPlayer is updated from SoundController |
TheChrisyd | 1:f44175dd69fd | 171 | void update(); |
TheChrisyd | 1:f44175dd69fd | 172 | friend class SoundController; |
TheChrisyd | 1:f44175dd69fd | 173 | // You can't copy me... |
TheChrisyd | 1:f44175dd69fd | 174 | SoundPlayer(const SoundPlayer&); |
TheChrisyd | 1:f44175dd69fd | 175 | void operator=(const SoundPlayer&); |
TheChrisyd | 1:f44175dd69fd | 176 | public: |
TheChrisyd | 1:f44175dd69fd | 177 | SoundPlayer(); |
TheChrisyd | 1:f44175dd69fd | 178 | |
TheChrisyd | 1:f44175dd69fd | 179 | // Set the sound parameters |
TheChrisyd | 1:f44175dd69fd | 180 | SoundPlayer& setSound(const Sound&); |
TheChrisyd | 1:f44175dd69fd | 181 | |
TheChrisyd | 1:f44175dd69fd | 182 | // Set playback volume in range [0..255], default = 255 |
TheChrisyd | 1:f44175dd69fd | 183 | SoundPlayer& setVolume(byte); |
TheChrisyd | 1:f44175dd69fd | 184 | |
TheChrisyd | 1:f44175dd69fd | 185 | // Play a sound, pitch in Hz, duration in clock ticks. |
TheChrisyd | 1:f44175dd69fd | 186 | // If duration is 'infinite' then the sound plays until you call 'release()' |
TheChrisyd | 1:f44175dd69fd | 187 | // One "clock tick" is equivalent to one call to "SoundController::update()" |
TheChrisyd | 1:f44175dd69fd | 188 | enum { infinite=0x7fff }; |
TheChrisyd | 1:f44175dd69fd | 189 | void play(unsigned int pitchHz, unsigned int duration=infinite); |
TheChrisyd | 1:f44175dd69fd | 190 | |
TheChrisyd | 1:f44175dd69fd | 191 | // Make the current sound enter the 'release' phase |
TheChrisyd | 1:f44175dd69fd | 192 | void release(); |
TheChrisyd | 1:f44175dd69fd | 193 | |
TheChrisyd | 1:f44175dd69fd | 194 | // Stop sound playback immediately |
TheChrisyd | 1:f44175dd69fd | 195 | void stop(); |
TheChrisyd | 1:f44175dd69fd | 196 | }; |
TheChrisyd | 1:f44175dd69fd | 197 | #endif |
TheChrisyd | 1:f44175dd69fd | 198 | |
TheChrisyd | 1:f44175dd69fd | 199 | /*------------------------------------------------------------------ |
TheChrisyd | 1:f44175dd69fd | 200 | Main sound controller object |
TheChrisyd | 1:f44175dd69fd | 201 | ------------------------------------------------------------------*/ |
TheChrisyd | 1:f44175dd69fd | 202 | struct SoundController { |
TheChrisyd | 1:f44175dd69fd | 203 | // Reset all sounds, return to silence |
TheChrisyd | 1:f44175dd69fd | 204 | static void reset(); |
TheChrisyd | 1:f44175dd69fd | 205 | |
TheChrisyd | 1:f44175dd69fd | 206 | // Update all sound - call this at least once every video frame. |
TheChrisyd | 1:f44175dd69fd | 207 | // The frequency at which you call this function is one "clock tick" for synthesised sounds |
TheChrisyd | 1:f44175dd69fd | 208 | static void update(); |
TheChrisyd | 1:f44175dd69fd | 209 | |
TheChrisyd | 1:f44175dd69fd | 210 | // Play a sound sample from program memory |
TheChrisyd | 1:f44175dd69fd | 211 | // Samples are eight bits and will be played at |
TheChrisyd | 1:f44175dd69fd | 212 | // the system sample rate (see "Coprocessor"). |
TheChrisyd | 1:f44175dd69fd | 213 | // if "numBytes" is negative, the sample will repeat until you play |
TheChrisyd | 1:f44175dd69fd | 214 | // another sample on the channel (eg. pass a null pointer to "playSample()"...) |
TheChrisyd | 1:f44175dd69fd | 215 | static void playSample(prog_char*, int numBytes, byte channel); |
TheChrisyd | 1:f44175dd69fd | 216 | }; |
TheChrisyd | 1:f44175dd69fd | 217 | |
TheChrisyd | 1:f44175dd69fd | 218 | |
TheChrisyd | 1:f44175dd69fd | 219 | |
TheChrisyd | 1:f44175dd69fd | 220 | /*------------------------------------------------------------ |
TheChrisyd | 1:f44175dd69fd | 221 | Useful little functions |
TheChrisyd | 1:f44175dd69fd | 222 | ------------------------------------------------------------*/ |
TheChrisyd | 1:f44175dd69fd | 223 | // Show a number on screen |
TheChrisyd | 1:f44175dd69fd | 224 | void showNumber(int n, byte x, byte y); |
TheChrisyd | 1:f44175dd69fd | 225 | void showHexNumber(unsigned int n, byte x, byte y); |
TheChrisyd | 1:f44175dd69fd | 226 | |
TheChrisyd | 1:f44175dd69fd | 227 | // Send screenshot to serial port |
TheChrisyd | 1:f44175dd69fd | 228 | |
TheChrisyd | 1:f44175dd69fd | 229 | void sendScreenshot(); |
TheChrisyd | 1:f44175dd69fd | 230 | |
TheChrisyd | 1:f44175dd69fd | 231 | // COM_ARTLUM_GDUTILS_INCLUDED |
TheChrisyd | 1:f44175dd69fd | 232 | #endif |