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
HighScoreTable.cpp
- Committer:
- RichardE
- Date:
- 2013-06-06
- Revision:
- 2:bb0f631a6068
- Parent:
- 0:5fa232ee5fdf
- Child:
- 3:a6a0cd726ca0
File content as of revision 2:bb0f631a6068:
/* * SOURCE FILE : HighScoreTable.cpp * * Definition of class HighScoreTable. * Maintains a table of high scores with a name and a score for each entry in the table. * * The table is stored in EEPROM at an address specified when you call the constructor. * The table is structured as follows. This shows a table with a capacity of 3. * * Byte offset Usage * ----------- ----- * 0 Index of highest score in data that follows * 1 Index of second highest score in data that follows * 2 Index of third highest score in data that follows * * 3 First character of player's name * 4 Second character of player's name * 5 Third character of player's name * 6 LSB of score (in BCD) * 7 Byte 1 of score (in BCD) * 8 Byte 2 of score (in BCD) * 9 MSB of score (in BCD) * 10 Unused * * 11 First character of player's name * 12 Second character of player's name * 13 Third character of player's name * 14 LSB of score (in BCD) * 15 Byte 1 of score (in BCD) * 16 Byte 2 of score (in BCD) * 17 MSB of score (in BCD) * 18 Unused * * 19 First character of player's name * 20 Second character of player's name * 21 Third character of player's name * 22 LSB of score (in BCD) * 23 Byte 1 of score (in BCD) * 24 Byte 2 of score (in BCD) * 25 MSB of score (in BCD) * 26 Unused * * So, assuming the capacity of the table is N, the first N bytes form an index which is used to locate * items in the remaining N*8 bytes that follow. This is done so that inserting a new entry only involves * overwriting one name/score record and updating the index. You don't have to re-write all the records * that move down the table to make room for the new one. * */ #include "HighScoreTable.h" /***************/ /* CONSTRUCTOR */ /***************/ // Pass pointer to an SPI EEPROM which contains the high scores. HighScoreTable::HighScoreTable( SPIEEPROM *e ) : eeprom( e ) { } /**************/ /* DESTRUCTOR */ /**************/ HighScoreTable::~HighScoreTable() { } /****************************************/ /* VALIDATE EEPROM USED FOR HIGH SCORES */ /****************************************/ // Checks EEPROM used for high scores and // if any of it looks like nonsense it // rewrites the whole table with defaults. void HighScoreTable::ValidateEEPROM( void ) { // Check if contents of EEPROM make sense. // If not then rewrite EEPROM with defaults. if( ! EEPROMValid() ) { WriteEEPROMDefaults(); } } /**********************************************/ /* DETERMINE POSITION OF A SCORE IN THE TABLE */ /**********************************************/ // Pass score in score. // Returns position in table (0 is top score). // If position returned is >= capacity of table then score is not high // enough to place in table. UInt8 HighScoreTable::GetPositionInTable( UInt32 score ) const { // Look through table for a score less than the one passed. PlayerName name; UInt32 tableScore = (UInt32)0; for( UInt8 i = 0; i < capacity; ++i ) { Get( i, &name, &tableScore ); if( tableScore < score ) { // Found a score that is less. // Return index at which it was found. return i; } } // No score found that is less than the one passed. // Return capacity of table to indicate not found. return capacity; } /*********************************/ /* ADD ENTRY TO HIGH SCORE TABLE */ /*********************************/ // Pass position in table to put entry in pos. // Pass name of player in name. // Pass score in score. void HighScoreTable::Add( UInt8 pos, const PlayerName *name, UInt32 score ) { // Read the entire index, high scores and names out of EEPROM. // Going to do manipulations in RAM to minimise the number of // writes we need to do to EEPROM. Remember every time we write // a single byte a whole page is written so might as well // write a whole page in one go. Only drawback is more RAM // is required. UInt8 buffer[ memoryUsed ]; if( eeprom->ReadBytes( eepromAddress, buffer, memoryUsed ) ) { // Fetch index for lowest score in the table. UInt8 index = buffer[ capacity - 1 ]; // Make sure index is within range. if( index < capacity ) { // Point to section of buffer that contains name and score for // lowest score. UInt8 *address = buffer + capacity + ( index << 3 ); // Copy characters of name into buffer. for( UInt8 i = 0; i < PlayerName::Length; ++i ) { *address++ = name->Name[ i ]; } // Copy bytes of score into buffer. *((UInt32*)address) = score; address += 4; // Move all entries in the index below insertion point down one // to make room for new entry. for( UInt8 i = capacity - 1; i > pos; --i ) { buffer[ i ] = buffer[ i - 1 ]; } // Insert index of newly written record at insertion point. buffer[ pos ] = index; // Write the buffer back to EEPROM. eeprom->WriteBytes( eepromAddress, buffer, memoryUsed ); } } } /****************************/ /* GET ENTRY FROM THE TABLE */ /****************************/ // Pass position to fetch from in pos. // Player name is returned in object pointed to by name. // Player score is returned in integer pointed to by score. void HighScoreTable::Get( UInt8 pos, PlayerName *name, UInt32 *score ) const { // Write default values to name and score. for( UInt8 i = 0; i < PlayerName::Length; ++i ) { name->Name[ i ] = (UInt8)'X'; } name->Name[ PlayerName::Length ] = 0; *score = 0; // Fetch index from EEPROM. UInt8 index; if( ! eeprom->ReadBytes( eepromAddress + pos, &index, 1 ) ) { return; } // Point to appropriate part of data table. UInt16 address = eepromAddress + capacity + ( index << 3 ); // Read out characters and store in name. if( ! eeprom->ReadBytes( address, (UInt8*)name->Name, PlayerName::Length ) ) { return; } name->Name[ PlayerName::Length ] = 0; address += PlayerName::Length; // Read out score. eeprom->ReadBytes( address, (UInt8*)score, 4 ); } /********************************/ /* DETERMINE IF EEPROM IS VALID */ /********************************/ // Returns true if EEPROM is valid. bool HighScoreTable::EEPROMValid( void ) { UInt8 b, b2; // Check all entries in the index are within range and are unique. for( UInt8 i = 0; i < capacity; ++i ) { // Read byte from EEPROM. if( ! eeprom->ReadBytes( eepromAddress + i, &b, 1 ) ) { return false; } // Check index read is less than capacity. if( b >= capacity ) { return false; } // Check if any of the following bytes in the index have // the same value. for( UInt8 j = i + 1; j < capacity; ++j ) { if( ! eeprom->ReadBytes( eepromAddress + j, &b2, 1 ) ) { return false; } if( b == b2 ) { return false; } } } // Check all entries in the data part of the table are valid. UInt16 address = eepromAddress + capacity; for( UInt8 i = 0; i < capacity; ++i ) { // Check name consists only of uppercase letters. for( UInt8 j = 0; j < PlayerName::Length; ++j ) { // Read byte from EEPROM. if( ! eeprom->ReadBytes( address++, &b, 1 ) ) { return false; } if( ( b < PlayerName::MinChar ) || ( b > PlayerName::MaxChar ) ) { return false; } } // Check score consists only of valid BCD numbers. for( UInt8 j = 0; j < 4; ++j ) { // Read byte from EEPROM. if( ! eeprom->ReadBytes( address++, &b, 1 ) ) { return false; } if( ( ( b & 0x0F ) > 0x09 ) || ( ( b & 0xF0 ) > 0x90 ) ) { return false; } } // Skip over unused byte. address++; } // EEPROM is valid return true; } /****************************/ /* WRITE DEFAULTS TO EEPROM */ /****************************/ // This may take a second or two to execute! void HighScoreTable::WriteEEPROMDefaults( void ) { UInt8 buffer[ memoryUsed ]; // Write index with ascending integers. UInt8 *ptr = buffer; for( UInt8 i = 0; i < capacity; ++i ) { *ptr++ = i; } // Write data table with zero score entries. for( UInt8 i = 0; i < capacity; ++i ) { // Write a name of "AAA". for( UInt8 j = 0; j < PlayerName::Length; ++j ) { *ptr++ = (UInt8)'A'; } // Write a score of zero. *((UInt32*)ptr) = 0; ptr += 4; // Write zero to unused byte. *ptr++ = 0; } // Write the buffer to EEPROM. eeprom->WriteBytes( eepromAddress, buffer, memoryUsed ); }