To store the state of the game board we used an unsigned integer for each board row. However, we could not use the full 16 bits for the board because our CheckOverlap routine needed space to work with and would crash if the "block space" was ever to move outside the game board. Therefore the game board needs some "walls" to keep the block space the game space. To do this we reserved the 3 most significant and 3 least significant bits of the top 16 rows of the game board and locked them to be "1" (representing a taken space) and the start of the game. The two bottom rows of the board, board and board, are locked walls.
To integrate the block space and the board space we used lots of shifts, and's and or's. To orient the block space in relation to the game board four variables are used, current_x, current_y, current_block and current_rotation. Current_x stores the current x-coordinate position of the top left corner of the block space in reference to the game board. Current_y is the current y-coordinate position. Current_block refers to the current active block and current_rotation is used to call the correct array value.
One of the most important functions in the game is void CheckOverlap(unsigned char Movement). It checks the current block position and according to the variable Movement calculates the next desired position. It then runs a loop to check each of the 4 rows of the game board to check for overlaps. If there is an overlap, a 1 is returned, otherwise a 0 is returned. This function is used for all block movement and to check if the game is over. If a freshly called block is overlapping, the game is over.
Our initialization function configured the LCD memory to map the first 1000 bytes of memory to the character space using 8X8 characters, giving us 40 lines of 25 characters each. To get a character address we use:
Address = 40 * Line + Character Offset
The next 8K of memory is the graphics space. Since the only graphics ever drawn for the game involve either blocks, we wrote the void DrawBlock(unsigned char Action) routine which allows us to either draw or undraw a block given the current_x, current_y and current_block and current_rotation parameters. Since the blocks are 12 pixels high and the game screen is on the right side of the screen, the address is obtained using:
Address = 1000 + 12*40*(17 - current_y) + 20 + current_x
Using a for loop and a bit-shifting algorithm it was easy to draw any block we wanted. To update the entire game screen (after a line clear or a multiplayer penalty), void UpdateScreen(void) is used. It redraws the entire game screen for top to bottom.
Timer0 is used to sample the Genesis gamepad on Port C at about 30Hz. Even with a 2-cycle debounce the gamepad was still too fast so we instead opted to freeze the gamepad after a user presses a button, until it is released. Button detection was easy; we simply scanned Port C for zeroes. This is what the buttons do in each part of the game:
Button A - Select mode
Start - Start the game
Left / Right / Down - Left / Right / Quick Drop
Start - Pause
A - Scroll through music
B - Rotate counterclockwise
C - Rotate clockwise
Start - Reset the game
Timer 1 is used to time dropping blocks. We calculated the amount of timer 1 ticks for each drop at different game levels (running at the slowest prescalar) and wrote them to an array. When the level increases we reset timer 1 and load a new value into OCR1A. At Level 1 it takes 1.5 seconds per drop, at Level 10 it drops 4 times a second! Neither of us managed to go beyond Level 8. Incidentally it is possible to beat the game in single player mode; you need to score 50000 points. Good luck!
Timer 2 is a random number generator. We set OCR2 to 6, turned off the interrupt and ran it as fast as possible. When we need a new block or a penalty is inflicted, we sample timer 2.
When a player clears multiple lines the opponent is punished by having the same number of lines cleared minus one inserted at the bottom of the board. Those blocks have a space inserted at random (using timer 2) into them. This makes the gameplay much more exciting.
The music was transcribed by ear and metronome. Tons of fun! Current musical selection:
p.s. We realise that all of these musical selections (with the exception of the erstwhile USSR's national anthem)
are probably under copyright, which means that we could possibly get into serious trouble for using them without due
permission from the creators/owners of said musical pieces. In lieu of this, we would like to present the following defense: