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
Diff: HighScoreTable.cpp
- Revision:
- 2:bb0f631a6068
- Parent:
- 0:5fa232ee5fdf
- Child:
- 3:a6a0cd726ca0
--- a/HighScoreTable.cpp Wed Jun 05 22:05:41 2013 +0000 +++ b/HighScoreTable.cpp Thu Jun 06 20:11:28 2013 +0000 @@ -1,259 +1,259 @@ -/* - * 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 I2C EEPROM which contains the high scores. -HighScoreTable::HighScoreTable( I2CEEPROM *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 ); +/* + * 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 ); +}