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:
Thu Jun 06 20:11:28 2013 +0000
Revision:
2:bb0f631a6068
Parent:
0:5fa232ee5fdf
Child:
3:a6a0cd726ca0
More code brought in from Maple version. Now uses fancy character set and displays Robotric logo. Started re-writing I2C EEPROM code as SPI EEPROM code since now using an SPI EEPROM but not finished yet.

Who changed what in which revision?

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