Version of Robotron arcade game using LPC1768, a Gameduino shield, a serial EEPROM (for high scores), two microswitch joysticks and two buttons plus a box to put it in. 20 levels of mayhem.

Dependencies:   25LCxxx_SPI CommonTypes Gameduino mbed

Committer:
RichardE
Date:
Tue Jun 04 20:16:33 2013 +0000
Revision:
0:5fa232ee5fdf
Child:
2:bb0f631a6068
Started conversion from Maple version of game. So far Gameduino seems to have been initialised OK and just displays a sign on message. Lots of commented out code.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
RichardE 0:5fa232ee5fdf 1 /*
RichardE 0:5fa232ee5fdf 2 * SOURCE FILE : HighScoreTable.cpp
RichardE 0:5fa232ee5fdf 3 *
RichardE 0:5fa232ee5fdf 4 * Definition of class HighScoreTable.
RichardE 0:5fa232ee5fdf 5 * Maintains a table of high scores with a name and a score for each entry in the table.
RichardE 0:5fa232ee5fdf 6 *
RichardE 0:5fa232ee5fdf 7 * The table is stored in EEPROM at an address specified when you call the constructor.
RichardE 0:5fa232ee5fdf 8 * The table is structured as follows. This shows a table with a capacity of 3.
RichardE 0:5fa232ee5fdf 9 *
RichardE 0:5fa232ee5fdf 10 * Byte offset Usage
RichardE 0:5fa232ee5fdf 11 * ----------- -----
RichardE 0:5fa232ee5fdf 12 * 0 Index of highest score in data that follows
RichardE 0:5fa232ee5fdf 13 * 1 Index of second highest score in data that follows
RichardE 0:5fa232ee5fdf 14 * 2 Index of third highest score in data that follows
RichardE 0:5fa232ee5fdf 15 *
RichardE 0:5fa232ee5fdf 16 * 3 First character of player's name
RichardE 0:5fa232ee5fdf 17 * 4 Second character of player's name
RichardE 0:5fa232ee5fdf 18 * 5 Third character of player's name
RichardE 0:5fa232ee5fdf 19 * 6 LSB of score (in BCD)
RichardE 0:5fa232ee5fdf 20 * 7 Byte 1 of score (in BCD)
RichardE 0:5fa232ee5fdf 21 * 8 Byte 2 of score (in BCD)
RichardE 0:5fa232ee5fdf 22 * 9 MSB of score (in BCD)
RichardE 0:5fa232ee5fdf 23 * 10 Unused
RichardE 0:5fa232ee5fdf 24 *
RichardE 0:5fa232ee5fdf 25 * 11 First character of player's name
RichardE 0:5fa232ee5fdf 26 * 12 Second character of player's name
RichardE 0:5fa232ee5fdf 27 * 13 Third character of player's name
RichardE 0:5fa232ee5fdf 28 * 14 LSB of score (in BCD)
RichardE 0:5fa232ee5fdf 29 * 15 Byte 1 of score (in BCD)
RichardE 0:5fa232ee5fdf 30 * 16 Byte 2 of score (in BCD)
RichardE 0:5fa232ee5fdf 31 * 17 MSB of score (in BCD)
RichardE 0:5fa232ee5fdf 32 * 18 Unused
RichardE 0:5fa232ee5fdf 33 *
RichardE 0:5fa232ee5fdf 34 * 19 First character of player's name
RichardE 0:5fa232ee5fdf 35 * 20 Second character of player's name
RichardE 0:5fa232ee5fdf 36 * 21 Third character of player's name
RichardE 0:5fa232ee5fdf 37 * 22 LSB of score (in BCD)
RichardE 0:5fa232ee5fdf 38 * 23 Byte 1 of score (in BCD)
RichardE 0:5fa232ee5fdf 39 * 24 Byte 2 of score (in BCD)
RichardE 0:5fa232ee5fdf 40 * 25 MSB of score (in BCD)
RichardE 0:5fa232ee5fdf 41 * 26 Unused
RichardE 0:5fa232ee5fdf 42 *
RichardE 0:5fa232ee5fdf 43 * So, assuming the capacity of the table is N, the first N bytes form an index which is used to locate
RichardE 0:5fa232ee5fdf 44 * items in the remaining N*8 bytes that follow. This is done so that inserting a new entry only involves
RichardE 0:5fa232ee5fdf 45 * overwriting one name/score record and updating the index. You don't have to re-write all the records
RichardE 0:5fa232ee5fdf 46 * that move down the table to make room for the new one.
RichardE 0:5fa232ee5fdf 47 *
RichardE 0:5fa232ee5fdf 48 */
RichardE 0:5fa232ee5fdf 49
RichardE 0:5fa232ee5fdf 50 #include "HighScoreTable.h"
RichardE 0:5fa232ee5fdf 51
RichardE 0:5fa232ee5fdf 52 /***************/
RichardE 0:5fa232ee5fdf 53 /* CONSTRUCTOR */
RichardE 0:5fa232ee5fdf 54 /***************/
RichardE 0:5fa232ee5fdf 55 // Pass pointer to an I2C EEPROM which contains the high scores.
RichardE 0:5fa232ee5fdf 56 HighScoreTable::HighScoreTable( I2CEEPROM *e ) :
RichardE 0:5fa232ee5fdf 57 eeprom( e )
RichardE 0:5fa232ee5fdf 58 {
RichardE 0:5fa232ee5fdf 59 }
RichardE 0:5fa232ee5fdf 60
RichardE 0:5fa232ee5fdf 61 /**************/
RichardE 0:5fa232ee5fdf 62 /* DESTRUCTOR */
RichardE 0:5fa232ee5fdf 63 /**************/
RichardE 0:5fa232ee5fdf 64 HighScoreTable::~HighScoreTable() {
RichardE 0:5fa232ee5fdf 65 }
RichardE 0:5fa232ee5fdf 66
RichardE 0:5fa232ee5fdf 67 /****************************************/
RichardE 0:5fa232ee5fdf 68 /* VALIDATE EEPROM USED FOR HIGH SCORES */
RichardE 0:5fa232ee5fdf 69 /****************************************/
RichardE 0:5fa232ee5fdf 70 // Checks EEPROM used for high scores and
RichardE 0:5fa232ee5fdf 71 // if any of it looks like nonsense it
RichardE 0:5fa232ee5fdf 72 // rewrites the whole table with defaults.
RichardE 0:5fa232ee5fdf 73 void HighScoreTable::ValidateEEPROM( void ) {
RichardE 0:5fa232ee5fdf 74 // Check if contents of EEPROM make sense.
RichardE 0:5fa232ee5fdf 75 // If not then rewrite EEPROM with defaults.
RichardE 0:5fa232ee5fdf 76 if( ! EEPROMValid() ) {
RichardE 0:5fa232ee5fdf 77 WriteEEPROMDefaults();
RichardE 0:5fa232ee5fdf 78 }
RichardE 0:5fa232ee5fdf 79 }
RichardE 0:5fa232ee5fdf 80
RichardE 0:5fa232ee5fdf 81 /**********************************************/
RichardE 0:5fa232ee5fdf 82 /* DETERMINE POSITION OF A SCORE IN THE TABLE */
RichardE 0:5fa232ee5fdf 83 /**********************************************/
RichardE 0:5fa232ee5fdf 84 // Pass score in score.
RichardE 0:5fa232ee5fdf 85 // Returns position in table (0 is top score).
RichardE 0:5fa232ee5fdf 86 // If position returned is >= capacity of table then score is not high
RichardE 0:5fa232ee5fdf 87 // enough to place in table.
RichardE 0:5fa232ee5fdf 88 UInt8 HighScoreTable::GetPositionInTable( UInt32 score ) const {
RichardE 0:5fa232ee5fdf 89 // Look through table for a score less than the one passed.
RichardE 0:5fa232ee5fdf 90 PlayerName name;
RichardE 0:5fa232ee5fdf 91 UInt32 tableScore = (UInt32)0;
RichardE 0:5fa232ee5fdf 92 for( UInt8 i = 0; i < capacity; ++i ) {
RichardE 0:5fa232ee5fdf 93 Get( i, &name, &tableScore );
RichardE 0:5fa232ee5fdf 94 if( tableScore < score ) {
RichardE 0:5fa232ee5fdf 95 // Found a score that is less.
RichardE 0:5fa232ee5fdf 96 // Return index at which it was found.
RichardE 0:5fa232ee5fdf 97 return i;
RichardE 0:5fa232ee5fdf 98 }
RichardE 0:5fa232ee5fdf 99 }
RichardE 0:5fa232ee5fdf 100 // No score found that is less than the one passed.
RichardE 0:5fa232ee5fdf 101 // Return capacity of table to indicate not found.
RichardE 0:5fa232ee5fdf 102 return capacity;
RichardE 0:5fa232ee5fdf 103 }
RichardE 0:5fa232ee5fdf 104
RichardE 0:5fa232ee5fdf 105 /*********************************/
RichardE 0:5fa232ee5fdf 106 /* ADD ENTRY TO HIGH SCORE TABLE */
RichardE 0:5fa232ee5fdf 107 /*********************************/
RichardE 0:5fa232ee5fdf 108 // Pass position in table to put entry in pos.
RichardE 0:5fa232ee5fdf 109 // Pass name of player in name.
RichardE 0:5fa232ee5fdf 110 // Pass score in score.
RichardE 0:5fa232ee5fdf 111 void HighScoreTable::Add( UInt8 pos, const PlayerName *name, UInt32 score ) {
RichardE 0:5fa232ee5fdf 112 // Read the entire index, high scores and names out of EEPROM.
RichardE 0:5fa232ee5fdf 113 // Going to do manipulations in RAM to minimise the number of
RichardE 0:5fa232ee5fdf 114 // writes we need to do to EEPROM. Remember every time we write
RichardE 0:5fa232ee5fdf 115 // a single byte a whole page is written so might as well
RichardE 0:5fa232ee5fdf 116 // write a whole page in one go. Only drawback is more RAM
RichardE 0:5fa232ee5fdf 117 // is required.
RichardE 0:5fa232ee5fdf 118 UInt8 buffer[ memoryUsed ];
RichardE 0:5fa232ee5fdf 119 if( eeprom->ReadBytes( eepromAddress, buffer, memoryUsed ) ) {
RichardE 0:5fa232ee5fdf 120 // Fetch index for lowest score in the table.
RichardE 0:5fa232ee5fdf 121 UInt8 index = buffer[ capacity - 1 ];
RichardE 0:5fa232ee5fdf 122 // Make sure index is within range.
RichardE 0:5fa232ee5fdf 123 if( index < capacity ) {
RichardE 0:5fa232ee5fdf 124 // Point to section of buffer that contains name and score for
RichardE 0:5fa232ee5fdf 125 // lowest score.
RichardE 0:5fa232ee5fdf 126 UInt8 *address = buffer + capacity + ( index << 3 );
RichardE 0:5fa232ee5fdf 127 // Copy characters of name into buffer.
RichardE 0:5fa232ee5fdf 128 for( UInt8 i = 0; i < PlayerName::Length; ++i ) {
RichardE 0:5fa232ee5fdf 129 *address++ = name->Name[ i ];
RichardE 0:5fa232ee5fdf 130 }
RichardE 0:5fa232ee5fdf 131 // Copy bytes of score into buffer.
RichardE 0:5fa232ee5fdf 132 *((UInt32*)address) = score;
RichardE 0:5fa232ee5fdf 133 address += 4;
RichardE 0:5fa232ee5fdf 134 // Move all entries in the index below insertion point down one
RichardE 0:5fa232ee5fdf 135 // to make room for new entry.
RichardE 0:5fa232ee5fdf 136 for( UInt8 i = capacity - 1; i > pos; --i ) {
RichardE 0:5fa232ee5fdf 137 buffer[ i ] = buffer[ i - 1 ];
RichardE 0:5fa232ee5fdf 138 }
RichardE 0:5fa232ee5fdf 139 // Insert index of newly written record at insertion point.
RichardE 0:5fa232ee5fdf 140 buffer[ pos ] = index;
RichardE 0:5fa232ee5fdf 141 // Write the buffer back to EEPROM.
RichardE 0:5fa232ee5fdf 142 eeprom->WriteBytes( eepromAddress, buffer, memoryUsed );
RichardE 0:5fa232ee5fdf 143 }
RichardE 0:5fa232ee5fdf 144 }
RichardE 0:5fa232ee5fdf 145 }
RichardE 0:5fa232ee5fdf 146
RichardE 0:5fa232ee5fdf 147 /****************************/
RichardE 0:5fa232ee5fdf 148 /* GET ENTRY FROM THE TABLE */
RichardE 0:5fa232ee5fdf 149 /****************************/
RichardE 0:5fa232ee5fdf 150 // Pass position to fetch from in pos.
RichardE 0:5fa232ee5fdf 151 // Player name is returned in object pointed to by name.
RichardE 0:5fa232ee5fdf 152 // Player score is returned in integer pointed to by score.
RichardE 0:5fa232ee5fdf 153 void HighScoreTable::Get( UInt8 pos, PlayerName *name, UInt32 *score ) const {
RichardE 0:5fa232ee5fdf 154 // Write default values to name and score.
RichardE 0:5fa232ee5fdf 155 for( UInt8 i = 0; i < PlayerName::Length; ++i ) {
RichardE 0:5fa232ee5fdf 156 name->Name[ i ] = (UInt8)'X';
RichardE 0:5fa232ee5fdf 157 }
RichardE 0:5fa232ee5fdf 158 name->Name[ PlayerName::Length ] = 0;
RichardE 0:5fa232ee5fdf 159 *score = 0;
RichardE 0:5fa232ee5fdf 160 // Fetch index from EEPROM.
RichardE 0:5fa232ee5fdf 161 UInt8 index;
RichardE 0:5fa232ee5fdf 162 if( ! eeprom->ReadBytes( eepromAddress + pos, &index, 1 ) ) {
RichardE 0:5fa232ee5fdf 163 return;
RichardE 0:5fa232ee5fdf 164 }
RichardE 0:5fa232ee5fdf 165 // Point to appropriate part of data table.
RichardE 0:5fa232ee5fdf 166 UInt16 address = eepromAddress + capacity + ( index << 3 );
RichardE 0:5fa232ee5fdf 167 // Read out characters and store in name.
RichardE 0:5fa232ee5fdf 168 if( ! eeprom->ReadBytes( address, (UInt8*)name->Name, PlayerName::Length ) ) {
RichardE 0:5fa232ee5fdf 169 return;
RichardE 0:5fa232ee5fdf 170 }
RichardE 0:5fa232ee5fdf 171 name->Name[ PlayerName::Length ] = 0;
RichardE 0:5fa232ee5fdf 172 address += PlayerName::Length;
RichardE 0:5fa232ee5fdf 173 // Read out score.
RichardE 0:5fa232ee5fdf 174 eeprom->ReadBytes( address, (UInt8*)score, 4 );
RichardE 0:5fa232ee5fdf 175 }
RichardE 0:5fa232ee5fdf 176
RichardE 0:5fa232ee5fdf 177 /********************************/
RichardE 0:5fa232ee5fdf 178 /* DETERMINE IF EEPROM IS VALID */
RichardE 0:5fa232ee5fdf 179 /********************************/
RichardE 0:5fa232ee5fdf 180 // Returns true if EEPROM is valid.
RichardE 0:5fa232ee5fdf 181 bool HighScoreTable::EEPROMValid( void ) {
RichardE 0:5fa232ee5fdf 182 UInt8 b, b2;
RichardE 0:5fa232ee5fdf 183 // Check all entries in the index are within range and are unique.
RichardE 0:5fa232ee5fdf 184 for( UInt8 i = 0; i < capacity; ++i ) {
RichardE 0:5fa232ee5fdf 185 // Read byte from EEPROM.
RichardE 0:5fa232ee5fdf 186 if( ! eeprom->ReadBytes( eepromAddress + i, &b, 1 ) ) {
RichardE 0:5fa232ee5fdf 187 return false;
RichardE 0:5fa232ee5fdf 188 }
RichardE 0:5fa232ee5fdf 189 // Check index read is less than capacity.
RichardE 0:5fa232ee5fdf 190 if( b >= capacity ) {
RichardE 0:5fa232ee5fdf 191 return false;
RichardE 0:5fa232ee5fdf 192 }
RichardE 0:5fa232ee5fdf 193 // Check if any of the following bytes in the index have
RichardE 0:5fa232ee5fdf 194 // the same value.
RichardE 0:5fa232ee5fdf 195 for( UInt8 j = i + 1; j < capacity; ++j ) {
RichardE 0:5fa232ee5fdf 196 if( ! eeprom->ReadBytes( eepromAddress + j, &b2, 1 ) ) {
RichardE 0:5fa232ee5fdf 197 return false;
RichardE 0:5fa232ee5fdf 198 }
RichardE 0:5fa232ee5fdf 199 if( b == b2 ) {
RichardE 0:5fa232ee5fdf 200 return false;
RichardE 0:5fa232ee5fdf 201 }
RichardE 0:5fa232ee5fdf 202 }
RichardE 0:5fa232ee5fdf 203 }
RichardE 0:5fa232ee5fdf 204 // Check all entries in the data part of the table are valid.
RichardE 0:5fa232ee5fdf 205 UInt16 address = eepromAddress + capacity;
RichardE 0:5fa232ee5fdf 206 for( UInt8 i = 0; i < capacity; ++i ) {
RichardE 0:5fa232ee5fdf 207 // Check name consists only of uppercase letters.
RichardE 0:5fa232ee5fdf 208 for( UInt8 j = 0; j < PlayerName::Length; ++j ) {
RichardE 0:5fa232ee5fdf 209 // Read byte from EEPROM.
RichardE 0:5fa232ee5fdf 210 if( ! eeprom->ReadBytes( address++, &b, 1 ) ) {
RichardE 0:5fa232ee5fdf 211 return false;
RichardE 0:5fa232ee5fdf 212 }
RichardE 0:5fa232ee5fdf 213 if( ( b < PlayerName::MinChar ) || ( b > PlayerName::MaxChar ) ) {
RichardE 0:5fa232ee5fdf 214 return false;
RichardE 0:5fa232ee5fdf 215 }
RichardE 0:5fa232ee5fdf 216 }
RichardE 0:5fa232ee5fdf 217 // Check score consists only of valid BCD numbers.
RichardE 0:5fa232ee5fdf 218 for( UInt8 j = 0; j < 4; ++j ) {
RichardE 0:5fa232ee5fdf 219 // Read byte from EEPROM.
RichardE 0:5fa232ee5fdf 220 if( ! eeprom->ReadBytes( address++, &b, 1 ) ) {
RichardE 0:5fa232ee5fdf 221 return false;
RichardE 0:5fa232ee5fdf 222 }
RichardE 0:5fa232ee5fdf 223 if( ( ( b & 0x0F ) > 0x09 ) || ( ( b & 0xF0 ) > 0x90 ) ) {
RichardE 0:5fa232ee5fdf 224 return false;
RichardE 0:5fa232ee5fdf 225 }
RichardE 0:5fa232ee5fdf 226 }
RichardE 0:5fa232ee5fdf 227 // Skip over unused byte.
RichardE 0:5fa232ee5fdf 228 address++;
RichardE 0:5fa232ee5fdf 229 }
RichardE 0:5fa232ee5fdf 230 // EEPROM is valid
RichardE 0:5fa232ee5fdf 231 return true;
RichardE 0:5fa232ee5fdf 232 }
RichardE 0:5fa232ee5fdf 233
RichardE 0:5fa232ee5fdf 234 /****************************/
RichardE 0:5fa232ee5fdf 235 /* WRITE DEFAULTS TO EEPROM */
RichardE 0:5fa232ee5fdf 236 /****************************/
RichardE 0:5fa232ee5fdf 237 // This may take a second or two to execute!
RichardE 0:5fa232ee5fdf 238 void HighScoreTable::WriteEEPROMDefaults( void ) {
RichardE 0:5fa232ee5fdf 239 UInt8 buffer[ memoryUsed ];
RichardE 0:5fa232ee5fdf 240 // Write index with ascending integers.
RichardE 0:5fa232ee5fdf 241 UInt8 *ptr = buffer;
RichardE 0:5fa232ee5fdf 242 for( UInt8 i = 0; i < capacity; ++i ) {
RichardE 0:5fa232ee5fdf 243 *ptr++ = i;
RichardE 0:5fa232ee5fdf 244 }
RichardE 0:5fa232ee5fdf 245 // Write data table with zero score entries.
RichardE 0:5fa232ee5fdf 246 for( UInt8 i = 0; i < capacity; ++i ) {
RichardE 0:5fa232ee5fdf 247 // Write a name of "AAA".
RichardE 0:5fa232ee5fdf 248 for( UInt8 j = 0; j < PlayerName::Length; ++j ) {
RichardE 0:5fa232ee5fdf 249 *ptr++ = (UInt8)'A';
RichardE 0:5fa232ee5fdf 250 }
RichardE 0:5fa232ee5fdf 251 // Write a score of zero.
RichardE 0:5fa232ee5fdf 252 *((UInt32*)ptr) = 0;
RichardE 0:5fa232ee5fdf 253 ptr += 4;
RichardE 0:5fa232ee5fdf 254 // Write zero to unused byte.
RichardE 0:5fa232ee5fdf 255 *ptr++ = 0;
RichardE 0:5fa232ee5fdf 256 }
RichardE 0:5fa232ee5fdf 257 // Write the buffer to EEPROM.
RichardE 0:5fa232ee5fdf 258 eeprom->WriteBytes( eepromAddress, buffer, memoryUsed );
RichardE 0:5fa232ee5fdf 259 }