Karthik Karkala (kck23)

Vijay Paruchuru (vkp4)

ECE 476

Scorched Earth: The Mother of All Games

May 2, 2004

 

 

 

Table of Contents

 

Introduction

High Level Design

            Rationale, Sources, and Hardware/Software Tradeoffs

            Background Math

            Logical Structure

            Project Standards

            Patents, Copyrights, and Intellectual Property

Hardware and Software Design

            Hardware Design

            Software Design

Results

            Speed of Execution

            Accuracy

            Safety, Interference, and Usability

Conclusion

            Analysis of Our Design

            Conformity to Applicable Standards

            Intellectual property considerations

            Ethical Considerations

            Societal Impact

Appendix

            Schematics

            Pictures

            Parts List

            Specific Tasks

            References

            Code

 

 

Introduction

           

            For our design project, we decided to replicate the video game known as “Scorched Earth: The Mother of All Games” where two tanks fire missiles at one another by adjusting angles and power while adjusting for variable wind.

            The objective of Scorched Earth is to destroy the enemy tank before your tank is destroyed.  Two tanks are randomly positioned on their respective sides on a randomly chosen terrain.  Through a trial and error process of adjusting angles and the strength of the tank’s turret, each user can manipulate the trajectory of the bullets to eventually target the enemy tank.  With three different weapons of different blast radii and three unique difficulty levels that incorporate variable wind speeds, the users can test their abilities as the most accurately firing tank.  We wanted to recreate this game to allow players to relive childhood memories.

 

Table of Contents

 

 

High Level Design

Rationale, Sources, and Hardware/Software Tradeoffs

 

            The foremost reason we chose to simulate Scorched Earth was because no one had recreated this game as a final project during past years.  Furthermore, we wanted to create a video game because we would not have any limitations within our budget regarding additional hardware.  Since we do not have many sources for additional parts, an advantage that other groups possessed, a project that consisted entirely of code would allow us to make a project that was still fun and still extremely complex and challenging.  The necessary hardware to run our project was simply the digital to analog converter circuit that displayed our program onto the television.  After completion of the project, we also added sound (a pitch that gradually decreased as a bullet was in motion), which required very simple circuitry that also connected to the television. 

 

Table of Contents

 

 

Background Math

 

            We originally had other video game ideas in mind, but with the help of fellow ECE and housemate, Brandon Richter, we rediscovered this particularly entertaining game that immediately found its way to our hearts.  During the brainstorming processes of recreating this game, we basically decided to implement everything that we successfully completed.  We wanted to randomly draw terrains at the start of each game; however, this would have been too complicated since our code was dependent on the arrays of previously created terrains.

 

Table of Contents

 

 

Logical Structure

 

            Upon starting the game, a startup screen displays a sample terrain and the two tanks as normally seen during an actual game.  “Scorched Earth” and a difficulty of “easy” as its default setting are also shown on the screen.  By pressing the debounced button C.6, the difficulty levels rotate from “easy” to “med” (medium), “med” to “hard”, and then “hard” back to “easy”.  An easy difficulty level has zero wind effect on the bullets during the game, while a medium difficulty level has a constant wind throughout the entire game, and a hard difficulty level has a variable wind after both players have completed a round of firing a bullet.

            To begin the game, the user must press C.7 to clear the screen and display a randomly chosen but previously created terrain.  Player 1’s tank will always be located on the left half the screen while player 2’s tank will always be located on the right half of the screen.  Along the top of the screen is an information bar that displays which player’s turn it is to fire, the power of that player’s shot, the angle at which that tank’s turret is pointing, the weapon chosen, the number of remaining shots of that weapon, and the speed and direction (east or west) of wind.  By using buttons C.4 and C.5 to increase and decrease the angle of the tank’s turret and by using buttons C.2 and C.3 to increase and decrease the power of the tank’s shot, the user can adjust the trajectory of their shot.  At the default setting, weapon 1 is displayed on the screen with infinite number of shots.  Using C.1, the user can cycle through to weapon 2 or weapon 3, of which each tank only has 3 and 1 bullets available during each game.  As the user uses either weapon 2 or weapon 3, availability of the bullets, denoted with single pixels, will decrease in number.  The user must then use button C.6 to fire the bullet.  The settings used for the first player to go are saved and the second player can now perform the same procedure with the original default settings that will also be saved upon firing a shot as well.

            Once a tank has been hit, a message that reads who is the winner will show up.  To play another game or to simply see the number of accumulated wins each player has since reset, the user must press C.7.  This clears the screen and displays the number of wins that each player has.  “Play Again” is also displayed on the screen, and to do so, the user must press button C.6 to clear the screen again and redisplay another randomly chosen map and random starting positions for each of the tanks.

 

Table of Contents

 

 

Project Standards

 

            The standards that we used are the same as those in Lab 4 (Cornell University, ECE 476, Spring 2004): RS170 standards with the NTSC rate.  The RS170 standard includes three voltages for black, white, and sync pulses with an aspect ratio of 4:3.  The scan frequencies of the horizontal and vertical sync pulses are 15.75 KHz and 60 Hz respectively.  The standard includes 525 interlaced lines per frame with 60 frames per second; however, we will be outputting a duplicate line for new line created.

 

Table of Contents

 

 

Patents, Copyrights, and Intellectual Property

 

            The foremost important copyright that must be addressed is the person(s) who originally created the game known as Scorched Earth.  Unfortunately, since we are not able to find the names of those who deserve our appreciation, we can only publish the official website for the game: http://www.classicgaming.com/scorch/.  However, must also thank Cornell University’s Professor Bruce Land for providing the basic code for NTSC video, obtained from Lab 4 as previously mentioned.  Professor Land also provided the basic code for sound implementation from the STK500 to the audio input on the back of the television.  We must also obviously thank Professor Land for the knowledge that we have gained by taking his course (ECE 476).

 

Table of Contents

 

 

Hardware and Software Design

Hardware Design

 

            Since our final project is a video game, our sole hardware necessary was the microcontroller unit, located on the STK500, pushbuttons to operate the game, also located on the STK500, and the video digital to analog converter shown in the design schematic below.  We used PORT C for the pushbuttons and PORT D for the circuit to implement the television.  The circuit was connected was wired to the yellow connector on the TV using clip leads and a RCA phone jack. The TV was set to video input.

 

 

Design Schematic

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Table of Contents

 

 

Software Design

 

Since we were able to use Professor Bruce Land’s video generation code, all we had to do was write the code for the game and were able to use the video generation code to output all our date.  In general the code was pretty straightforward to write and we didn’t have any major problems with it.  Our biggest problem was timing restrictions which forced us to break up our output into multiple frames/states so that we would not get artifacts.

 

Start Screen

 

The start screen has the game name, “Scorched Earth”, and a map with two tanks as the background.  At this screen you are allowed to choose the difficulty of the game.  There are 3 difficulties: Easy which has no wind, Medium where wind is randomly generated but doesn’t change for the rest of the map, and Hard where wind changes after each player has shot.

 

Clear Screen/Draw Map

 

One of the trickier parts of the code were erasing the screen and drawing a new map. It took us a few tries to figure out how many lines we could draw or erase in each frame without causing artifacts to occur.  We finally decided on only drawing or erasing 3 lines per frame to ensure that no artifacts would occur.   We have 5 pre-generated maps that have been stored into memory of our cpu.  We used a random number generator to randomize which map would be chosen.  We also used a random number generator to randomize the starting location of P1 and P2.

 

Player Turn

 

We used a random number generator after drawing the maps to randomize which player gets to go first.  At the start of the players turn, the player is allowed to change his power, angle and weapon, and fire when he feels he is ready.  The values for power went from 0 to 99, the values for angle go from 0 to 180 and the weapon is cycled from 1 to 3.  We debounced all the buttons used to increment/decrement the power, angle and weapon.  We used a timed increment so that power and angle would be changed by one every quarter second.  First we had it such that each button press would result in a change of one, but we found it tiresome to press a button 15 times, and it was much easier just to hold the button down.  And for the weapon since it was only cycling through 3 possible values, we just decided to leave it as a simple debounced button where each press increments the value by 1.  Also weapons 2 and 3, which are considered super weapons, have a limit on the number of shots available.  Weapon 2 only has 3 shots while Weapon 1 only has 1 shot. Once the bullet is fired the trajectory of the bullet is calculated, how this is done is explained in the next section.  Also you are not allowed to fire if you have no more shots left of the selected weapon.

 

Bullet Trajectory

 

First thing we did to determine the trajectory of our bullet was to divide the power of our shot into x and y components (xpow, ypow) using the sine and cosine functions.  We had a start position (xpos, ypos) which was at the top of the turret of the tank and we changed these values by a factor of the power (xpos = xpos+xpow/25, ypos = ypos-ypow/25). Once we changed the position variables we changed the power variables, the xpow would be changed by a factor of wind/25 and ypow was reduced by one every frame to account for gravity.  This caused us to skip a few points because at our highest power of 100 it was possible to skip 3 pixels ever frame and it was possible that our shots would skip over the top of the tank or not hit the top of the terrain but instead hit somewhere lower.  In order to correct this we scaled our changes by ¼ so that we wouldn’t skip any points, for example xpos now equaled xpos+xpow/100 and xpow = xpow + wind/100 and ypow = ypow - ¼.  This ensured that at full power we would only be moving one pixel, and though this increased the time it took for the bullet to travel from one point to other, it made the point of impact much more accurate.  Also the xpos and ypos variables were stored into other variables (prevx,prevy) so that we know what the last position of the bullet was and delete it.

 

Walls

 

We decided to make the left and right walls absolute boundaries.  If any of the shots hit these boundaries that players turn would end, we assumed that if the shot has passed the edge of the screen that it was shot too far and thus will not hit.  The top of the screen however was not an absolute boundary, any shot went higher had a chance of coming back.  We just stopped our program from drawing new points or erasing old points if the bullet went higher than the top of our screen, and we resumed drawing and erasing once the bullet re-entered the screen.  This allows the player to take high shots to get over hills that they might be next to without worrying about hitting an absolute boundary.

 

End of Round

 

The round ends when one of the tanks is struck by the bullet.  Regardless of which player shot the bullet, the player whose tank is hit loses and the other player wins.  At this point we print out a message letting the players know which player has won the round.  Then by pressing fire you go to another screen, where you have the option of changing the difficulty, this screen also shows the number of games each player has won.  By pressing start at this screen you can start a new game. At this point all your weapons are refilled and once again which player starts is randomized.

 

Music

 

The final part of our code that needs to be talked about is the sound effects.  During the shot when the trajectory is being calculated we have a note which increases in pitch every quarter second. The timebase of quarter second was achieved by using a variable to count up to 15, and since each time it increments corresponds to one frame and each frame is 1/60th of a second, the counter hits 15 at approximately quarter second.  We set OCR0 to 60 and ran timer0 without interrupts enabled on a prescaler of 100, to generate our initial note and every quarter second we incremented OCR0 by 1, to increase the pitch and thus make our sound a little more realistic.  Also when a player tank is hit we have a much higher pitched sound with an OCR0 starting and 195 and going to 193 in order to create a sound for three-quarters of a second to simulate the explosion of a tank getting hit.  Though since I, Karthik, did the sound in my free time and am not musically talented the sound is most likely quite a bit lacking in their effect.

 

Challenges

 

As I mentioned earlier in this section we had no major problems with this code.  The only part of the code that was tricky to figure out was trying to keep the artifacts from occurring when our bullet crossed our boundaries.  When the bullet left the top it would erase part of the line or the last bullet location drawn would not be erased.  This was a little tricky for us because we tried to play with which point we called our boundary, this was usually the pixel before the actual line itself, and we still ended up with the stray unerased bullet.  We finally fixed this problem when we noticed that when we ended the turn due to the boundaries we would not erase the previous point because we would be skipping a state when compared to if the bullet had hit terrain.  Once we figured this out it was easy to make the change and remove the stray unerased bullet point.  Other parts of our code such as timing constraints for drawing the map, though they weren’t tricky took some experimenting to make sure that they were efficient.

 

 

Table of Contents

 

 

 

Results

 

Speed of Execution

 

Most of our code executes with no delay.  The only part of our code where you might see a bit of delay is in our drawing of the map or clearing the screen, since each of this program is divided over approximately 42 frames which equal three-quarters of a second you will see a scrolling effect when the screen is being cleared or a map is being drawn.  This is the only part of the program where the desired output isn’t output to the screen all at once but instead is broken up into smaller pieces over a lot of frames, and therefore the only part where any delay recognizable by the human eye can be seen.

 

Table of Contents

 

 

Accuracy

 

The only issue of accuracy in our program was in the trajectory calculation.  Since with our original idea it was possible to skip a few points, it was not very accurate.  But we changed it so that it wouldn’t skip any points and thus the point at which the bullet hit is now close to 100% accurate.   However this slowed down the speed at which the bullet traveled by a factor of 4. 

 

Table of Contents

 

 

Safety, Interference, and Usability

 

            We enforced safety in our design by following the applicable standards for NTSC rate and RS170.  By also carefully wiring our circuits and using low voltages, no one can be harmed by our project.  Players of the game will use the pushbuttons, located on the STK500, that cause no threat to safety.  By adhering to the no food and drink policy in the lab, we also prevented careless accidents from occurring.  Since our program was mostly software based and we did not create any RF interference or CPU noise, our project did not interfere with any one else.  Our project is usable by anyone who has the ability to watch a television screen and push buttons.  However, the ability to logically determine how to win a game by firing a missile at the enemy tank by adjusting the tank’s power and angle would also greatly help in using this project.

 

Table of Contents

 

 

Conclusion

Analysis of Our Design

 

            We were completely satisfied with our project on all levels.  Everything that we planned to implement within the video game was achieved: a startup screen; the option to choose difficulties of no wind, one wind, or variable wind throughout the game; randomly chosen terrains, tank positions, and starting player; the ability to increase and decrease the power and angle of the tanks; the ability to choose one of three weapons that created different blast radii in the terrain and two of which that were limited in number; the ability to save each player’s most recently used angle, power, and weapon settings; a line that indicated the strength and direction of the wind; and a “Play Again?” screen that would allow the players to change the difficulty level again.  As far as we know, the only thing about our project that we would have changed was the implementation of Sega Contollers to play the game.  This is extremely simple and given the access to controllers and a little more time, we would have completed this task as well.

 

Table of Contents

 

           

Conformity to Applicable Standards

 

            As stated earlier in this report, we conformed to applicable standards as well as possible.  The standards that we used are the same as those in Lab 4 (Cornell University, ECE 476, Spring 2004): RS170 standards with the NTSC rate.  The RS170 standard includes three voltages for black, white, and sync pulses with an aspect ratio of 4:3.  The scan frequencies of the horizontal and vertical sync pulses are 15.75 KHz and 60 Hz respectively.  The standard includes 525 interlaced lines per frame with 60 frames per second; however, we will be outputting a duplicate line for new line created.

 

Table of Contents

 

 

Intellectual property considerations

 

            Also stated earlier in this report, the foremost important copyright that must be addressed is the person(s) who originally created the game known as Scorched Earth.  Unfortunately, since we are not able to find the names of those who deserve our appreciation, we can only publish the official website for the game: http://www.classicgaming.com/scorch/.  However, must also thank Cornell University’s Professor Bruce Land for providing the basic code for NTSC video, obtained from Lab 4 as previously mentioned.  We must also obviously thank Professor Land for the knowledge that we have gained by taking his course (ECE 476).  Professor Land also provided the basic code for sound implementation from the STK500 to the audio input on the back of the television.  We did not use code in the public domain nor did we reverse-engineer a design.  We did not have to sign a non-disclosure to get any sample parts nor do we have any patent opportunities for our project since this is indeed a video game that recreated the idea of another person(s).

 

Table of Contents

 

 

Ethical Considerations

 

1.  to accept responsibility in making engineering decisions consistent with the safety, health and welfare of the public, and to disclose promptly factors that might endanger the public or the environment;

Our video game has not impact on society that will produce any negative effects on the safety, health and welfare of the public.  Given that we are using a television to display our game, we are following all protocols compliant with NTSC and RS170 standards.

2.  to avoid real or perceived conflicts of interest whenever possible, and to disclose them to affected parties when they do exist;

If there was ever a conflict of interest between the group members, such as a difference in opinion as to what additional features should be added to the game (should missiles reflect off of the side walls), we rationally discussed our problem and reached a solution.  We did not have any conflicts of interest outside of the group.

3.  to be honest and realistic in stating claims or estimates based on available data;

During the entire duration of this project, we projected realistic goals and successfully completed those goals within our knowledge and budget constraints.  Furthermore, the project as well as the report was created in the utmost honest manner

 

4.  to seek, accept, and offer honest criticism of technical work, to acknowledge and correct errors, and to credit properly the contributions of others;

 

During the creation of our project, we sought advice from our teaching assistant, Vladimir Kozitsky, Professor Land, and others who we felt could contribute to a more successful final project.  We accepted all criticisms as honest and altered our project as we saw fit.  All of the persons who contributed their thoughts have been acknowledged and thanked.

5.  to assist colleagues and co-workers in their professional development and to support them in following this code of ethics.

We offered our support to other teams by offering constructive criticism and advice that would help other groups succeed.  We did not break any of the rules discussed in the IEEE Code of Ethics.

 

Table of Contents

 

 

Societal Impact

 

            Our project will have a tremendous societal impact since playing this game will bring create hours upon hours of video gaming pleasures.  Reliving many childhood memories is priceless.  There are very few safety considerations.  If we decide to encase the breadboard and the exposed wires, there will be no risk to health in any way.  Furthermore, since we are using the RS170 standards with very few modifications, there is no need for problems such as epilepsy.  Two humans will be able to play the game; each player will alternate firing the tank until one of the tanks has been destroyed.  The user will solely need the ability to press buttons on a keypad in addition to being able to turn on the microcontroller unit and the television.  The ability to make appropriately adjust the strength and angle of each shot will also help in winning at the game.  The copyrights for this game have yet to be found but will be properly acknowledged soon.

 

Table of Contents

 

 

 

Appendix

Schematics

 

The drawing below shows the state diagram used for our video game.  Nested within this state diagram was the state diagram for button debouncing, shown later in this section.

 

State Diagram for Scorched Earch: The Mother of All Video Games

 

 

 

 

The following diagram illustrates the basic schema used to debounce buttons used during the execution of the game.  Two of the eight buttons used are shown below, but the same basic schema was used for the remaining six buttons.  We used this schema for use of the buttons while playing the game, as well as C.0 during both the startup and game over screens to change the difficulty settings.

 

State Machine for Button Debouncing

 

 

 

Table of Contents

 

 

Pictures

 

Project Setup

 

 

Title Screen

 

 

Game Screen

 

 

Win Screen

 

 

Play Again Screen

 

 

 

Table of Contents

 

 

Parts List

 

Given our budget constraint of $40.00, we were well within our limitations, as seen:

 

STK500 Development Board               $0.00

Television                                             $0.00

Breadboard                                          $0.00

Wires/Cables                                        $0.00

Total                                                    $0.00

 

Table of Contents

 

 

Specific Tasks

 

During the entire design of our project, we split up most our tasks equally.  Once the project was complete, Karthik implemented the sound of the video game during his spare time.  Since he also has more experience in programming and thus has a stronger background, Karthik was therefore slightly more responsible for creating some of the program code.  However, to compensate for this, Vijay was responsible for writing a slight majority of this written report.

 

Table of Contents

 

 

References

 

Atmel Mega32 Data Sheet:

http://instruct1.cit.cornell.edu/courses/ee476/AtmelStuff/full32.pdf

 

Cornell University, Spring 2004, ECE 476, Lab 4 TV Output Code:

http://instruct1.cit.cornell.edu/courses/ee476/video/index.html

http://instruct1.cit.cornell.edu/courses/ee476/video/Video32v2.c

 

Table of Contents

 

 

Code

 

//video gen and sound

//D.5 is sync:1000 ohm + diode to 75 ohm resistor

//D.6 is video:330 ohm + diode to 75 ohm resistor

//B.3 is sound  and should have a 10k resistor to gnd

 

#pragma regalloc-    //I allocate the registers myself

#pragma optsize-     //optimize for speed

 

#include <Mega32.h>

#include <stdio.h>

#include <stdlib.h>

#include <math.h>

#include <delay.h>

 

//cycles = 63.625 * 16 Note NTSC is 63.55

//but this line duration makes each frame exactly 1/60 sec

//which is nice for keeping a realtime clock

#define lineTime 1018

 

 

 

 

#define begin {

#define end   }

#define ScreenTop 30

#define ScreenBot 230

 

//main state machine vars

#define Startup 0

#define Clear 1

#define Drawmap 2

#define Drawtank 3

#define Top 4

#define P1T 5

#define Fire1 6

#define P2T 7

#define Fire2 8

#define Erase1 9

#define Erase2 10

#define HIT2 11

#define HIT1 12

#define Startagain 13

#define Clearmap 14

#define Next 15

 

// debouncing state machine vars

#define Nopush             0

#define MaybeC5     1

#define C5          2

#define NoC5        3

#define MaybeC4     4

#define C4          5

#define NoC4        6

#define MaybeC3     7

#define C3          8

#define NoC3        9

#define MaybeC2     10

#define C2          11

#define NoC2        12

#define MaybeC1     13

#define C1          14

#define NoC1        15

#define MaybeC6     16

#define C6          17

#define NoC6        18

 

//NOTE that v1 to v8 and i must be in registers!

register char v1 @4;

register char v2 @5;

register char v3 @6;

register char v4 @7;

register char v5 @8;

register char v6 @9;

register char v7 @10;

register char v8 @11;

register int i @12;

 

#pragma regalloc+

// statevariables

char statevar;

char dstate;    //debouncing

 

 

char screen[1600];

char syncON, syncOFF;

int LineCount;

char wind; // wind power

char difficulty; // dificulty level

char play1w,play2w;

char test;

int count; // random number gen

char p1x,p2x;            // horizontal position

char p1pow,p2pow;       //power of shot

unsigned char p1ang,p2ang;      // angle of shot

char p1weap, p2weap;   // weapon choice

char p1xy[2],p2xy[2]; // end turret start bullet

char temp[20];  // sprintf var

int time;  // for variable loop var

int c1,c2; // erase and map vars

char numshots;

char randmap;

char p1w2avail;      // number of shots available for each weapon

char p1w3avail;

char p2w2avail;

char p2w3avail;

 

int timer; // for inc/dec ang and pow

float xpow,ypow;     // x and y components of power

float xpos,ypos;     // current pos of bullet

float prevx,prevy;   // previous point of bullet

 

char note, musicT; // music vars

//Musical note values

//C below middle C to  C above middle C

//zeros are rests

flash char notes[] = {239,213,189,179,159,142,126,

              120,106,94,90,80,71,63,60,0,0,0,0};

 

 

//Point plot lookup table

//One bit masks

flash char pos[8]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};

 

flash char map[5][128] = {

//map1

65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,

66, 66, 66, 66, 67, 67, 67, 67, 68, 68, 68, 68, 69, 69, 69, 69,

70, 70, 70, 70, 70, 70, 70, 70, 68, 66, 64, 62, 60, 58, 56, 54,

52, 52, 52, 51, 51, 50, 51, 51, 52, 52, 52, 54, 56, 58, 60, 62,

64, 64, 66, 68, 70, 70, 70, 70, 70, 70, 70, 70, 71, 71, 71, 71,

72, 72, 72, 72, 73, 73, 73, 73, 74, 74, 74, 74, 75, 75, 75, 75,

73, 71, 69, 68, 67, 66, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,

65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,

 

//map2

50, 50, 50, 50, 50, 51, 51, 51, 52, 52, 52, 53, 53, 53, 55, 57,

59, 59, 59, 59, 59, 58, 58, 58, 57, 57, 58, 58, 58, 60, 62, 64,

65, 66, 66, 66, 66, 66, 66, 66, 67, 67, 67, 65, 63, 61, 59, 57,

55, 55, 55, 55, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66,

67, 68, 69, 70, 70, 70, 70, 70, 70, 70, 70, 70, 72, 74, 76, 77,

78, 79, 80, 81, 83, 85, 87, 87, 87, 88, 88, 88, 88, 87, 85, 83,

81, 79, 78, 77, 76, 75, 74, 73, 71, 69, 67, 65, 63, 61, 61, 61,

62, 63, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,

 

//map3

50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,

50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,

50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,

50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,

50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,

50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,

50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,

50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,

 

//map4

45, 45, 45, 45, 45, 45, 45, 45, 46, 46, 46, 47, 47, 47, 48, 48,

49, 50, 51, 52, 53, 54, 54, 55, 55, 56, 57, 58, 58, 58, 58, 58,

59, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,

61, 62, 62, 62, 62, 62, 63, 63, 63, 63, 63, 63, 63, 64, 65, 66,

67, 68, 69, 70, 71, 71, 71, 71, 71, 71, 71, 72, 73, 74, 74, 75,

75, 76, 77, 77, 77, 77, 77, 77, 78, 78, 78, 78, 79, 80, 81, 82,

82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 83, 84,

85, 86, 87, 88, 88, 88, 88, 89, 89, 90, 90, 90, 90, 90, 90, 90,

 

//map5

90, 90, 90, 90, 90, 90, 90, 89, 89, 88, 88, 88, 88, 87, 86, 85,

84, 83, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82,

82, 81, 80, 79, 78, 78, 78, 78, 77, 77, 77, 77, 77, 77, 76, 75,

75, 74, 74, 73, 72, 71, 71, 71, 71, 71, 71, 71, 70, 69, 68, 67,

66, 65, 64, 63, 63, 63, 63, 63, 63, 63, 62, 62, 62, 62, 62, 61,

60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 59,

58, 58, 58, 58, 58, 57, 56, 55, 55, 54, 54, 53, 52, 51, 50, 49,

48, 48, 47, 47, 46, 46, 46, 45, 45, 45, 45, 45, 45, 45, 45, 45

 

};

 

 

//define some character bitmaps

//5x7 characters

flash char bitmap[38][7]={

       //0

       0b01110000,

       0b10001000,

       0b10011000,

       0b10101000,

       0b11001000,

       0b10001000,

       0b01110000,

       //1

       0b00100000,

       0b01100000,

       0b00100000,

       0b00100000,

       0b00100000,

       0b00100000,

       0b01110000,

       //2

       0b01110000,

       0b10001000,

       0b00001000,

       0b00010000,

       0b00100000,

       0b01000000,

       0b11111000,

    //3

       0b11111000,

       0b00010000,

       0b00100000,

       0b00010000,

       0b00001000,

       0b10001000,

       0b01110000,

       //4

       0b00010000,

       0b00110000,

       0b01010000,

       0b10010000,

       0b11111000,

       0b00010000,

       0b00010000,

       //5

       0b11111000,

       0b10000000,

       0b11110000,

       0b00001000,

       0b00001000,

       0b10001000,

       0b01110000,

       //6

       0b01000000,

       0b10000000,

       0b10000000,

       0b11110000,

       0b10001000,

       0b10001000,

       0b01110000,

       //7

       0b11111000,

       0b00001000,

       0b00010000,

       0b00100000,

       0b01000000,

       0b10000000,

       0b10000000,

       //8

       0b01110000,

       0b10001000,

       0b10001000,

       0b01110000,

       0b10001000,

       0b10001000,

       0b01110000,

       //9

       0b01110000,

       0b10001000,

       0b10001000,

       0b01111000,

       0b00001000,

       0b00001000,

       0b00010000,

       //A

       0b01110000,

       0b10001000,

       0b10001000,

       0b10001000,

       0b11111000,

       0b10001000,

       0b10001000,

       //B

       0b11110000,

       0b10001000,

       0b10001000,

       0b11110000,

       0b10001000,

       0b10001000,

       0b11110000,

       //C

       0b01110000,

       0b10001000,

       0b10000000,

       0b10000000,

       0b10000000,

       0b10001000,

       0b01110000,

       //D

       0b11110000,

       0b10001000,

       0b10001000,

       0b10001000,

       0b10001000,

       0b10001000,

       0b11110000,

       //E

       0b11111000,

       0b10000000,

       0b10000000,

       0b11111000,

       0b10000000,

       0b10000000,

       0b11111000,

       //F

       0b11111000,

       0b10000000,

       0b10000000,

       0b11111000,

       0b10000000,

       0b10000000,

       0b10000000,

       //G

       0b01110000,

       0b10001000,

       0b10000000,

       0b10011000,

       0b10001000,

       0b10001000,

       0b01110000,

       //H

       0b10001000,

       0b10001000,

       0b10001000,

       0b11111000,

       0b10001000,

       0b10001000,

       0b10001000,

       //I

       0b01110000,

       0b00100000,

       0b00100000,

       0b00100000,

       0b00100000,

       0b00100000,

       0b01110000,

       //J

       0b00111000,

       0b00010000,

       0b00010000,

       0b00010000,

       0b00010000,

       0b10010000,

       0b01100000,

       //K

       0b10001000,

       0b10010000,

       0b10100000,

       0b11000000,

       0b10100000,

       0b10010000,

       0b10001000,

       //L

       0b10000000,

       0b10000000,

       0b10000000,

       0b10000000,

       0b10000000,

       0b10000000,

       0b11111000,

       //M

       0b10001000,

       0b11011000,

       0b10101000,

       0b10101000,

       0b10001000,

       0b10001000,

       0b10001000,

       //N

       0b10001000,

       0b10001000,

       0b11001000,

       0b10101000,

       0b10011000,

       0b10001000,

       0b10001000,

       //O

       0b01110000,

       0b10001000,

       0b10001000,

       0b10001000,

       0b10001000,

       0b10001000,

       0b01110000,

       //P

       0b11110000,

       0b10001000,

       0b10001000,

       0b11110000,

       0b10000000,

       0b10000000,

       0b10000000,

       //Q

       0b01110000,

       0b10001000,

       0b10001000,

       0b10001000,

       0b10101000,

       0b10010000,

       0b01101000,

       //R

       0b11110000,

       0b10001000,

       0b10001000,

       0b11110000,

       0b10100000,

       0b10010000,

       0b10001000,

       //S

       0b01111000,

       0b10000000,

       0b10000000,

       0b01110000,

       0b00001000,

       0b00001000,

       0b11110000,

       //T

       0b11111000,

       0b00100000,

       0b00100000,

       0b00100000,

       0b00100000,

       0b00100000,

       0b00100000,

       //U

       0b10001000,

       0b10001000,

       0b10001000,

       0b10001000,

       0b10001000,

       0b10001000,

       0b01110000,

       //V

       0b10001000,

       0b10001000,

       0b10001000,

       0b10001000,

       0b10001000,

       0b01010000,

       0b00100000,

       //W

       0b10001000,

       0b10001000,

       0b10001000,

       0b10101000,

       0b10101000,

       0b10101000,

       0b01010000,

       //X

       0b10001000,

       0b10001000,

       0b01010000,

       0b00100000,

       0b01010000,

       0b10001000,

       0b10001000,

       //Y

       0b10001000,

       0b10001000,

       0b10001000,

       0b01010000,

       0b00100000,

       0b00100000,

       0b00100000,

       //Z

       0b11111000,

       0b00001000,

       0b00010000,

       0b00100000,

       0b01000000,

       0b10000000,

       0b11111000,

       //figure1 (blank)

       0b00000000,

       0b00000000,

       0b00000000,

       0b00000000,

       0b00000000,

       0b00000000,

       0b00000000,

    /* //figure1

       0b01110000,

       0b00100000,

       0b01110000,

       0b10101000,

       0b00100000,

       0b01010000,

       0b10001000, */

       //figure2

       0b01110000,

       0b10101000,

       0b01110000,

       0b00100000,

       0b00100000,

       0b01010000,

       0b10001000};

 

 

//================================

//3x5 font numbers, then letters

//packed two per definition for fast

//copy to the screen at x-position divisible by 4

flash char smallbitmap[39][5]={

       //0

    0b11101110,

       0b10101010,

       0b10101010,

       0b10101010,

       0b11101110,

       //1

       0b01000100,

       0b11001100,

       0b01000100,

       0b01000100,

       0b11101110,

       //2

       0b11101110,

       0b00100010,

       0b11101110,

       0b10001000,

       0b11101110,

       //3

       0b11101110,

       0b00100010,

       0b11101110,

       0b00100010,

       0b11101110,

       //4

       0b10101010,

       0b10101010,

       0b11101110,

       0b00100010,

       0b00100010,

       //5

       0b11101110,

       0b10001000,

       0b11101110,

       0b00100010,

       0b11101110,

       //6

       0b11001100,

       0b10001000,

       0b11101110,

       0b10101010,

       0b11101110,

       //7

       0b11101110,

       0b00100010,

       0b01000100,

       0b10001000,

       0b10001000,

       //8

       0b11101110,

       0b10101010,

       0b11101110,

       0b10101010,

       0b11101110,

       //9

       0b11101110,

       0b10101010,

       0b11101110,

       0b00100010,

       0b01100110,

       //:

       0b00000000,

       0b01000100,

       0b00000000,

       0b01000100,

       0b00000000,

       //=

       0b00000000,

       0b11101110,

       0b00000000,

       0b11101110,

       0b00000000,

       //blank

       0b00000000,

       0b00000000,

       0b00000000,

       0b00000000,

       0b00000000,

       //A

       0b11101110,

       0b10101010,

       0b11101110,

       0b10101010,

       0b10101010,

       //B

       0b11001100,

       0b10101010,

       0b11101110,

       0b10101010,

       0b11001100,

       //C

       0b11101110,

       0b10001000,

       0b10001000,

       0b10001000,

       0b11101110,

       //D

       0b11001100,

       0b10101010,

       0b10101010,

       0b10101010,

       0b11001100,

       //E

       0b11101110,

       0b10001000,

       0b11101110,

       0b10001000,

       0b11101110,

       //F

       0b11101110,

       0b10001000,

       0b11101110,

       0b10001000,

       0b10001000,

       //G

       0b11101110,

       0b10001000,

       0b10001000,

       0b10101010,

       0b11101110,

       //H

       0b10101010,

       0b10101010,

       0b11101110,

       0b10101010,

       0b10101010,

       //I

       0b11101110,

       0b01000100,

       0b01000100,

       0b01000100,

       0b11101110,

       //J

       0b00100010,

       0b00100010,

       0b00100010,

       0b10101010,

       0b11101110,

       //K

       0b10001000,

       0b10101010,

       0b11001100,

       0b11001100,

       0b10101010,

       //L

       0b10001000,

       0b10001000,

       0b10001000,

       0b10001000,

       0b11101110,

       //M

       0b10101010,

       0b11101110,

       0b11101110,

       0b10101010,

       0b10101010,

       //N

       0b00000000,

       0b11001100,

       0b10101010,

       0b10101010,

       0b10101010,

       //O

       0b01000100,

       0b10101010,

       0b10101010,

       0b10101010,

       0b01000100,

       //P

       0b11101110,

       0b10101010,

       0b11101110,

       0b10001000,

       0b10001000,

       //Q

       0b01000100,

       0b10101010,

       0b10101010,

       0b11101110,

       0b01100110,

       //R

       0b11101110,

       0b10101010,

       0b11001100,

       0b11101110,

       0b10101010,

       //S

       0b11101110,

       0b10001000,

       0b11101110,

       0b00100010,

       0b11101110,

       //T

       0b11101110,

       0b01000100,

       0b01000100,

       0b01000100,

       0b01000100,

       //U

       0b10101010,

       0b10101010,

       0b10101010,

       0b10101010,

       0b11101110,

       //V

       0b10101010,

       0b10101010,

       0b10101010,

       0b10101010,

       0b01000100,

       //W

       0b10101010,

       0b10101010,

       0b11101110,

       0b11101110,

       0b10101010,

       //X

       0b00000000,

       0b10101010,

       0b01000100,

       0b01000100,

       0b10101010,

       //Y

       0b10101010,

       0b10101010,

       0b01000100,

       0b01000100,

       0b01000100,

       //Z

       0b11101110,

       0b00100010,

       0b01000100,

       0b10001000,

       0b11101110

       };

 

//==================================

//This is the sync generator and raster generator. It MUST be entered from

//sleep mode to get accurate timing of the sync pulses

#pragma warn-

interrupt [TIM1_COMPA] void t1_cmpA(void)

{

  //start the Horizontal sync pulse

  PORTD = syncON;

  //update the curent scanline number

  LineCount ++ ;

  //begin inverted (Vertical) synch after line 247

  if (LineCount==248)

  {

    syncON = 0b00100000;

    syncOFF = 0;

  }

  //back to regular sync after line 250

  if (LineCount==251)

  {

    syncON = 0;

    syncOFF = 0b00100000;

  }

  //start new frame after line 262

  if (LineCount==263)

  {

     LineCount = 1;

  }

 

  delay_us(2); //adjust to make 5 us pulses

  //end sync pulse

  PORTD = syncOFF;

  if (LineCount<ScreenBot && LineCount>=ScreenTop)

    {

 

       //compute byte index for beginning of the next line

       //left-shift 4 would be individual lines

       // <<3 means line-double the pixels

       //The 0xfff8 truncates the odd line bit

       //i=(LineCount-ScreenTop)<<3 & 0xfff8; //

 

       #asm

       push r16

       lds   r12, _LineCount

       lds   r13, _Linecount+1

       ldi   r16, 30

       sub  r12, r16

       ldi  r16,0

       sbc  r13, r16

       lsl  r12

       rol  r13

       lsl  r12

       rol  r13

       lsl  r12

       rol  r13

       mov  r16,r12

       andi r16,0xf0

       mov  r12,r16

       pop r16

       #endasm

 

       //load 16 registers with screen info

       #asm

       push r14

       push r15

       push r16

       push r17

       push r18

       push r19

       push r26

       push r27

 

       ldi  r26,low(_screen)   ;base address of screen

       ldi  r27,high(_screen)

       add  r26,r12            ;offset into screen (add i)

       adc  r27,r13

       ld   r4,x+          ;load 16 registers and inc pointer

       ld   r5,x+

       ld   r6,x+

       ld   r7,x+

       ld   r8,x+

       ld   r9,x+

       ld   r10,x+

       ld   r11,x+

       ld   r12,x+

       ld   r13,x+

       ld   r14,x+

       ld   r15,x+

       ld   r16,x+

       ld   r17,x+

       ld   r18,x+

       ld   r19,x

 

       pop  r27

       pop  r26

       #endasm

 

       delay_us(4);  //adjust to center image on screen

 

       //blast 16 bytes to the screen

       #asm

       ;but first a macro to make the code shorter

       ;the macro takes a register number as a parameter

       ;and dumps its bits serially to portD.6

       ;the nop can be eliminated to make the display narrower

       .macro videobits ;regnum

        BST  @0,7

       IN   R30,0x12

       BLD  R30,6

       nop

       OUT  0x12,R30

 

       BST  @0,6

       IN   R30,0x12

       BLD  R30,6

       nop

       OUT  0x12,R30

 

       BST  @0,5

       IN   R30,0x12

       BLD  R30,6

       nop

       OUT  0x12,R30

 

       BST  @0,4

       IN   R30,0x12

       BLD  R30,6

       nop

       OUT  0x12,R30

 

       BST  @0,3

       IN   R30,0x12

       BLD  R30,6

       nop

       OUT  0x12,R30

 

       BST  @0,2

       IN   R30,0x12

       BLD  R30,6

       nop

       OUT  0x12,R30

 

       BST  @0,1

       IN   R30,0x12

       BLD  R30,6

       nop

       OUT  0x12,R30

 

       BST  @0,0

       IN   R30,0x12

       BLD  R30,6

       nop

       OUT  0x12,R30

       .endm

 

       videobits r4 ;video line -- byte 1

        videobits r5 ;byte 2

        videobits r6 ;byte 3

        videobits r7 ;byte 4

        videobits r8 ;byte 5

        videobits r9 ;byte 6

        videobits r10 ;byte 7

        videobits r11 ;byte 8

        videobits r12 ;byte 9

        videobits r13 ;byte 10

        videobits r14 ;byte 11

        videobits r15 ;byte 12

        videobits r16 ;byte 13

        videobits r17 ;byte 14

        videobits r18 ;byte 15

        videobits r19 ;byte 16

       clt   ;clear video after the last pixel on the line

       IN   R30,0x12

       BLD  R30,6

       OUT  0x12,R30

 

       pop r19

       pop r18

       pop r17

       pop r16

       pop r15

       pop r14

       #endasm

 

    }

    if (timer > 0) timer--;

}

#pragma warn+

 

//==================================

//plot one point

//at x,y with color 1=white 0=black 2=invert

#pragma warn-

void video_pt(char x, char y, char c)

{

 

       #asm

       ;  i=(x>>3) + ((int)y<<4) ;   the byte with the pixel in it

 

       push r16

       ldd r30,y+2          ;get x

       lsr r30

       lsr r30

       lsr r30              ;divide x by 8

       ldd r12,y+1          ;get y

              lsl r12              ;mult y by 16

              clr r13

       lsl r12

       rol r13

       lsl r12

       rol r13

       lsl r12

       rol r13

       add r12, r30         ;add in x/8

 

       ;v2 = screen[i];   r5

        ;v3 = pos[x & 7];  r6

       ;v4 = c            r7

       ldi r30,low(_screen)

       ldi r31,high(_screen)

       add r30, r12

       adc r31, r13

       ld r5,Z              ;get screen byte

       ldd r26,y+2          ;get x

       ldi r27,0

       andi r26,0x07           ;form x & 7

       ldi r30,low(_pos*2)

       ldi r31,high(_pos*2)

       add r30,r26

       adc r31,r27

       lpm r6,Z

       ld r16,y             ;get c

 

       ;if (v4==1) screen[i] = v2 | v3 ;

       ;if (v4==0) screen[i] = v2 & ~v3;

       ;if (v4==2) screen[i] = v2 ^ v3 ;

 

       cpi r16,1

       brne tst0

       or  r5,r6

       tst0:

       cpi r16,0

       brne tst2

       com r6

       and r5,r6

       tst2:

       cpi r16,2

       brne writescrn

       eor r5,r6

       writescrn:

              ldi r30,low(_screen)

       ldi r31,high(_screen)

       add r30, r12

       adc r31, r13

       st Z, r5             ;write the byte back to the screen

 

       pop r16

       #endasm

 

}

#pragma warn+

 

//==================================

// put a big character on the screen

// c is index into bitmap

void video_putchar(char x, char y, char c)

{

    v7 = x;

    for (v6=0;v6<7;v6++)

    {

        v1 = bitmap[c][v6];

        v8 = y+v6;

        video_pt(v7,   v8, (v1 & 0x80)==0x80);

        video_pt(v7+1, v8, (v1 & 0x40)==0x40);

        video_pt(v7+2, v8, (v1 & 0x20)==0x20);

        video_pt(v7+3, v8, (v1 & 0x10)==0x10);

        video_pt(v7+4, v8, (v1 & 0x08)==0x08);

    }

}

 

//==================================

// put a string of big characters on the screen

void video_puts(char x, char y, char *str)

{

       char i ;

       for (i=0; str[i]!=0; i++)

       {

              if (str[i]>=0x30 && str[i]<=0x3a)

                     video_putchar(x,y,str[i]-0x30);

              else if (str[i] == ' ')

                     video_putchar(x,y,36);

              else video_putchar(x,y,str[i]-0x40+9);

              x = x+6;

       }

}

 

//==================================

// put a small character on the screen

// x-cood must be on divisible by 4

// c is index into bitmap

void video_smallchar(char x, char y, char c)

{

       char mask;

       i=((int)x>>3) + ((int)y<<4) ;

       if (x == (x & 0xf8)) mask = 0x0f;     //f8

       else mask = 0xf0;

 

       screen[i] =    (screen[i] & mask) | (smallbitmap[c][0] & ~mask);

       screen[i+16] = (screen[i+16] & mask) | (smallbitmap[c][1] & ~mask);

        screen[i+32] = (screen[i+32] & mask) | (smallbitmap[c][2] & ~mask);

        screen[i+48] = (screen[i+48] & mask) | (smallbitmap[c][3] & ~mask);

       screen[i+64] = (screen[i+64] & mask) | (smallbitmap[c][4] & ~mask);

}

 

//==================================

// put a string of small characters on the screen

// x-cood must be on divisible by 4

void video_putsmalls(char x, char y, char *str)

{

       char i ;

       for (i=0; str[i]!=0; i++)

       {

              if (str[i]>=0x30 && str[i]<=0x3a)

                     video_smallchar(x,y,str[i]-0x30);

              else if (str[i] == ' ')

                     video_smallchar(x,y,12);

              else video_smallchar(x,y,str[i]-0x40+12);

              x = x+4;

       }

}

 

//==================================

//plot a line

//at x1,y1 to x2,y2 with color 1=white 0=black 2=invert

//NOTE: this function requires signed chars

//Code is from David Rodgers,

//"Procedural Elements of Computer Graphics",1985

void video_line(char x1, char y1, char x2, char y2, char c)

{

       int e;

       signed char dx,dy,j, temp;

       signed char s1,s2, xchange;

        signed char x,y;

 

       x = x1;

       y = y1;

       dx = cabs(x2-x1);

       dy = cabs(y2-y1);

       s1 = csign(x2-x1);

       s2 = csign(y2-y1);

       xchange = 0;

       if (dy>dx)

       {

              temp = dx;

              dx = dy;

              dy = temp;

              xchange = 1;

       }

       e = ((int)dy<<1) - dx;

       for (j=0; j<=dx; j++)

       {

              video_pt(x,y,c) ;

              if (e>=0)

              {

                     if (xchange==1) x = x + s1;

                     else y = y + s2;

                     e = e - ((int)dx<<1);

              }

              if (xchange==1) y = y + s2;

              else x = x + s1;

              e = e + ((int)dy<<1);

       }

}

 

//==================================

//return the value of one point

//at x,y with color 1=white 0=black 2=invert

char video_set(char x, char y)

{

       //The following construction

       //detects exactly one bit at the x,y location

       i=((int)x>>3) + ((int)y<<4) ;

    return ( screen[i] & 1<<(7-(x & 0x7)));

}

 

 

void tankdraw(char x, char y)     // function to draw tank

{

       // draw tank centered around x,y

       video_line(x-4,y+1,x+4,y+1,0);

       video_line(x-3,y,x+3,y,1);

       video_pt(x-4,y,0);

       video_pt(x+4,y,0);

       y--;

       video_line(x-2,y,x+2,y,1);

       video_pt(x-3,y,0);

       video_pt(x+3,y,0);

       y--;

       video_line(x-1,y,x+1,y,1);

       video_pt(x-2,y,0);

       video_pt(x+2,y,0);

       video_pt(x,y-1,1);

}

 

void turretdraw(char pos, unsigned char ang, char y)  // function to draw/erase turret

{

       if (pos == 1)

       {

              if (ang <= 7){ p1xy[0] = p1x+3; p1xy[1] = map[randmap][p1x]-3; }

              else if (ang <= 22)  { p1xy[0] = p1x+3; p1xy[1] = map[randmap][p1x]-4; }

              else if (ang <= 37)  { p1xy[0] = p1x+3; p1xy[1] = map[randmap][p1x]-5; }

              else if (ang <= 52)  { p1xy[0] = p1x+3; p1xy[1] = map[randmap][p1x]-6; }

              else if (ang <= 67)  { p1xy[0] = p1x+2; p1xy[1] = map[randmap][p1x]-6; }

              else if (ang <= 82)  { p1xy[0] = p1x+1; p1xy[1] = map[randmap][p1x]-6; }

              else if (ang <= 97) { p1xy[0] = p1x; p1xy[1] = map[randmap][p1x]-6; }

              else if (ang <= 112) { p1xy[0] = p1x-1; p1xy[1] = map[randmap][p1x] - 6;}

              else if (ang <= 127) { p1xy[0] = p1x-2; p1xy[1] = map[randmap][p1x] - 6;}

              else if (ang <= 142) { p1xy[0] = p1x-3; p1xy[1] = map[randmap][p1x] - 6;}

              else if (ang <= 157){ p1xy[0] = p1x-3; p1xy[1] = map[randmap][p1x] - 5;}

              else if (ang <= 172){ p1xy[0] = p1x-3; p1xy[1] = map[randmap][p1x] - 4;}

              else   { p1xy[0] =  p1x-3; p1xy[1] = map[randmap][p1x] - 3; }

          video_line(p1x, map[randmap][p1x] - 3, p1xy[0],p1xy[1],y);

 

       }

       else if (pos == 2)

       {

              if (ang <= 7) { p2xy[0] =  p2x-3; p2xy[1] = map[randmap][p2x] - 3; }

              else if (ang <= 22)  { p2xy[0] = p2x-3; p2xy[1] = map[randmap][p2x] - 4;}

              else if (ang <= 37)  { p2xy[0] = p2x-3; p2xy[1] = map[randmap][p2x] - 5;}

              else if (ang <= 52)  { p2xy[0] = p2x-3; p2xy[1] = map[randmap][p2x] - 6;}

              else if (ang <= 67)  { p2xy[0] = p2x-2; p2xy[1] = map[randmap][p2x] - 6;}

              else if (ang <= 82)  { p2xy[0] = p2x-1; p2xy[1] = map[randmap][p2x] - 6;}

              else if (ang <= 97)  { p2xy[0] = p2x; p2xy[1] = map[randmap][p2x] - 6;}

              else if (ang <= 112) { p2xy[0] = p2x+1; p2xy[1] = map[randmap][p2x]-6; }

              else if (ang <= 127) { p2xy[0] = p2x+2; p2xy[1] = map[randmap][p2x]-6; }

              else if (ang <= 142) { p2xy[0] = p2x+3; p2xy[1] = map[randmap][p2x]-6; }

              else if (ang <= 157) { p2xy[0] = p2x+3; p2xy[1] = map[randmap][p2x]-5; }

              else if (ang <= 172) { p2xy[0] = p2x+3; p2xy[1] = map[randmap][p2x]-4; }

              else   { p2xy[0] = p2x+3; p2xy[1] = map[randmap][p2x]-3; }

              video_line (p2x,map[randmap][p2x] - 3, p2xy[0],p2xy[1],y);

       }

}

//==================================

// set up the ports and timers

void main(void)

{

  //init timer 1 to generate sync

  OCR1A = lineTime; //One NTSC line

  TCCR1B = 9;              //full speed; clear-on-match

  TCCR1A = 0x00;     //turn off pwm and oc lines

  TIMSK = 0x10;            //enable interrupt T1 cmp

 

  TCCR0 = 0b00000011;

 

  //init hyperterm

  UCSRB = 0x18;

  UBRRL = 103;

 

  //init pushbuttons

  DDRC = 0x00;

  PORTC = 0xff;

 

  //init ports

  DDRD = 0xf0;             //video out and switches

  //d.5 is sync:1000 ohm + diode to 75 ohm resistor

  //d.6 is video:330 ohm + diode to 75 ohm resistor

 

  //initialize synch constants

  LineCount = 1;

  syncON = 0b00000000;

  syncOFF = 0b00100000;

 

 

  #define width 126

  play1w = 0;

  play2w = 0;

  video_line(0,0,0,99,1);

  video_line(width,0,width,99,1);

  video_line(0,0,width,0,1);

  video_line(0,99,width,99,1);

 

  //init musical scale

  note = 0;

  musicT = 0;

  //use OC0 (pin B.3) for music 

  DDRB.3 = 1 ;

 

  //Start up screen

  sprintf (temp,"SCORCHED EARTH");

  video_puts(20,20,temp);

  sprintf(temp, "DIFFICULTY:");

  video_putsmalls(28,40,temp);

  for ( time = 1; time < 126; time++)

  {

       video_line (time,map[1][time], time,99,1);

  }

  randmap = 1;

  p1x = 40;

  p2x = 120;

  tankdraw(40, map[1][40]);

  turretdraw(1, 45, 1);

  tankdraw(120, map[1][120]);

  turretdraw(2, 45, 1);

 

 

 

  // init vars

  statevar = Startup;

  dstate = Nopush;

  c1=1;

  c2=1;

  p1pow = 50;

  p2pow = 50;

  p1ang = 45;

  p2ang = 45;

  p1weap = 0;

  p2weap = 0;

  wind = 0;

  difficulty = 0;

  numshots = 0;

  p1w2avail = 3;

  p1w3avail = 1;

  p2w2avail = 3;

  p2w3avail = 1;

  timer = 0;

 

  //enable sleep mode

  MCUCR = 0b10000000;

  #asm ("sei");

 

  //The following loop executes once/video line during lines

  //1-230, then does all of the frame-end processing

  while(1)

  {

 

    //stall here until next line starts

    //sleep enable; mode=idle

    //use sleep to make entry into sync ISR uniform time

 

    #asm ("sleep");

    ADCSR.6 = 1;

    //The following code executes during the vertical blanking

    //Code here can be as long as

    //a total of 60 lines x 63.5 uSec/line x 8 cycles/uSec

 

    if (LineCount==231)

    {

        switch (statevar)

        {

        case Startup:      // startup screen, allows user to change difficulty

                   switch (dstate) // state machine to debounce select button

                   {

                       case Nopush:

                                   if (~PINC == 0x01) dstate = MaybeC6;

                                   else dstate = Nopush;

                       break;

                       case MaybeC6:

                            if (~PINC == 0x01)

                            {

                                   dstate = C6;

                                   difficulty++;

                                   if (difficulty > 2) difficulty = 0;

                                   //difficulty = difficulty%3;

                            }

                            else dstate = Nopush;

                       break;

                       case C6:

                                   if (~PINC == 0x01) dstate = C6;

                                   else dstate = NoC6;

                       break;

                       case NoC6:

                                   if (~PINC     == 0x01) dstate = C6;

                                   else dstate = Nopush;

                       break;

                   }

          if (difficulty == 0)    // display the current difficulty level

          {

            sprintf(temp, "EASY");

            video_putsmalls(76,40,temp);

          }

          else if (difficulty == 1)

          {

            sprintf(temp, "MED ");

            video_putsmalls(76,40,temp);

          }

          else if (difficulty == 2)

          {

            sprintf(temp, "HARD");

            video_putsmalls(76,40,temp);

          }

          if ( ~PINC == 0x80)     // on fire go to state clear

          {

              statevar = Clear;

              c2 = 1;

 

          }

        break;

        case Drawmap:      // draw map

                     if (c1 < 126)

                     {

                           for ( time = c1; time < 3+c1; time++)

                     {

                           video_line (time,map[randmap][time], time,99,1);

                     }

                     c1 = time;

              }

              else

              {

                     statevar= Drawtank;  // draw tanks once you finish drawing the map

              }

        break;

 

 

        case Clear:        // clear the screen

              if (c2 < 126)

              {

                     for (time = c2; time < 3 + c2; time++)

                     {

                            video_line (time,11,time,98,0);

                     }

                     c2 = time;

              }

              else

              {

                video_line (126,1,126,98,1);

                statevar = Drawmap;

                // random number generation

                 srand(TCNT0);

               //random map

                     count = (rand()/(32767/999));

                     randmap = count/200;

                c1=1;

              }

              break;

              case Drawtank: // draw the tanks

                     p1x = 7 +(rand()/(32767/50));

                     p2x = 71 + (rand()/(32767/50));

                     tankdraw(p1x,map[randmap][p1x]) ;

                     tankdraw(p2x,map[randmap][p2x]);

                     statevar = Top;

              break;

              case Top:     // draw the top of the screen, nonvariable text

                     video_line(0,10,width,10,1);

                     sprintf(temp, "ANG:",);

                     video_putsmalls(44,4,temp);

                     sprintf(temp,"POW:");

                     video_putsmalls(16,4,temp);

                     sprintf(temp,"WEA:");

                     video_putsmalls(76,4,temp);

                     turretdraw(1,p1ang,1);

                     turretdraw(2,p2ang,1);

                     if (rand()/(32767/100) > 50) // randomly choose which player goes first

                           statevar = P1T;

                     else

                           statevar = P2T;

                     if (difficulty == 1)

                     {

                            wind = rand()/(32767/18);

                            wind -= 9;

                     }

                     video_line(13,0,13,10,1);

                     video_line(41,0,41,10,1);

                     video_line(73,0,73,10,1);

                     video_line(105,0,105,10,1);

                     video_line(115,0,115,2,1);

                     video_line(115,4,115,6,1);

                     video_line(115,8,115,10,1);

              break;

              case P1T:     // player 1 turn

                   sprintf (temp,"P1");  // display the values of p1's variables

                     video_putsmalls(4,4,temp);

                   sprintf (temp, "%02d", p1pow);

                   video_putsmalls(32,4,temp);

                   sprintf(temp,"%03d",p1ang);

                   video_putsmalls(60,4,temp);

                   sprintf(temp,"%01d",p1weap+1);

                   video_putsmalls(92,4,temp);

 

                     // weapons cache drawing

                   if (p1weap+1 == 1)   

                   {

                     video_pt(97,5,1);

                     video_line(98,4,99,4,1);

                     video_line(98,6,99,6,1);

                     video_pt(100,5,1);

                     video_line(101,4,102,4,1);

                     video_line(101,6,102,6,1);

                     video_pt(103,5,1);

                   }

                   else if (p1weap+1 == 2 && p1w2avail == 3)

                   {

                     video_pt(97,5,1);

                     video_pt(100,5,1);

                     video_pt(103,5,1);

                     video_line(98,4,102,4,0);

                     video_line(98,6,102,6,0);               

                   }

                   else if (p1weap+1 == 2 && p1w2avail == 2)

                   {

                     video_pt(97,5,0);

                     video_pt(100,5,1);

                     video_pt(103,5,1);

                     video_line(98,4,102,4,0);

                     video_line(98,6,102,6,0);               

                   }

                   else if (p1weap+1 == 2 && p1w2avail == 1)

                   {

                     video_pt(97,5,0);

                     video_pt(100,5,0);

                     video_pt(103,5,1);

                     video_line(98,4,102,4,0);

                     video_line(98,6,102,6,0);               

                   }

                   else if (p1weap+1 == 2 && p1w2avail == 0)

                   {

                     video_pt(97,5,0);

                     video_pt(100,5,0);

                     video_pt(103,5,0);

                     video_line(98,4,102,4,0);

                     video_line(98,6,102,6,0);               

                   }

                   else if (p1weap+1 == 3 && p1w3avail == 1)

                   {

                     video_pt(97,5,0);

                     video_pt(100,5,0);

                     video_pt(103,5,1);

                     video_line(98,4,102,4,0);

                     video_line(98,6,102,6,0);              

                   }

                   else if (p1weap+1 == 3 && p1w3avail == 0)

                   {

                     video_pt(97,5,0);

                     video_pt(100,5,0);

                     video_pt(103,5,0);

                     video_line(98,4,102,4,0);

                     video_line(98,6,102,6,0);               

                   }

 

           video_line(106,5,125,5,0);

                   video_line(115,5,115+wind,5,1); // draw the wind force and direction

 

                     // statemachine to debounce buttons for inc/dec of pow, angle and weapon choice

                   switch (dstate)

                   {

                       case Nopush:

                                   if (~PINC == 0x20) dstate = MaybeC5;

                                   else if (~PINC == 0x10) dstate = MaybeC4;

                                   else if (~PINC == 0x08) dstate = MaybeC3;

                                   else if (~PINC == 0x04) dstate = MaybeC2;

                                   else if (~PINC == 0x02) dstate = MaybeC1;

                                   else dstate = Nopush;

                       break;

                       case MaybeC5:

                            if (~PINC == 0x20)

                            { 

                                   timer = 3929;

                                   dstate = C5;

 

                            }

                            else dstate = Nopush;

                       break;

                       case C5:

                                   if (~PINC == 0x20)

                                   { 

                                         if (timer == 0)

                                         { 

                                                timer = 3929;

                                                turretdraw(1,p1ang,0);

                                                if (p1ang > 0) p1ang--;

                                                       turretdraw(1,p1ang,1);

                                         }

                                         dstate = C5;

                                   }

                                   else dstate = NoC5;

 

                       break;

                       case NoC5:

                                   if (~PINC     == 0x20) dstate = C5;

                                   else dstate = Nopush;

                       break;

                       case MaybeC4:

                            if (~PINC == 0x10)

                            { 

                                   timer = 3929;

                                   dstate = C4;

                            }

                            else dstate = Nopush;

                       break;

                       case C4:

                                   if (~PINC == 0x10)

                                   {

                                         if (timer == 0)

                                         {  

                                                 timer = 3929;

                                                 turretdraw(1,p1ang,0);

                                                 if (p1ang < 180) p1ang++;

                                                     turretdraw(1,p1ang,1);

                                         }                                 

                                      dstate = C4;

                                   }

                                   else dstate = NoC4;

                       break;

                       case NoC4:

                                   if (~PINC     == 0x10) dstate = C4;

                                   else dstate = Nopush;

                       break;

                       case MaybeC3:

                            if (~PINC == 0x08)

                            {

                                   dstate = C3;

                                   timer = 3929;

                            }

                            else dstate = Nopush;

                       break;

                       case C3:

                                   if (~PINC == 0x08)

                                   { 

                                         if (timer == 0)

                                         {

                                                timer = 3929;

                                                if (p1pow > 0) p1pow--;

                                         }

                                         dstate = C3;

                                   }

                                   else dstate = NoC3;

                       break;

                       case NoC3:

                                   if (~PINC     == 0x08) dstate = C3;

                                   else dstate = Nopush;

                       break;

                       case MaybeC2:

                            if (~PINC == 0x04)

                            {

                                   dstate = C2;

                                   timer = 3929;

                            }

                            else dstate = Nopush;

                       break;

                       case C2:

                                   if (~PINC == 0x04)

                                   { 

                                         if (timer == 0)

                                         {

                                             if (p1pow < 99) p1pow++;

                                          timer = 3929;

                                         }

                                         dstate = C2;

                                   }

                                   else dstate = NoC2;

                       break;

                       case NoC2:

                                   if (~PINC     == 0x04) dstate = C2;

                                   else dstate = Nopush;

                       break;

                       case MaybeC1:

                            if (~PINC == 0x02)

                            {

                                   dstate = C1;

                                   p1weap++;

                                   p1weap = p1weap%3;

                            }

                            else dstate = Nopush;

                       break;

                       case C1:

                                   if (~PINC == 0x02) dstate = C1;

                                   else dstate = NoC1;

                       break;

                       case NoC1:

                                   if (~PINC     == 0x02) dstate = C1;

                                   else dstate = Nopush;

                       break;

 

                   }

                   if (~PINC == 0x40)  // if fire button is pressed goto fire 1 state

                   {  

                            if( (p1weap == 0) || (p1weap == 1 && p1w2avail > 0) || ( p1weap == 2 && p1w3avail > 0))

                            {

                                   statevar = Fire1;

                                         // start position and power for the bullet

                                   xpow = cos(p1ang*PI/180)*p1pow;

                                   ypow = sin(p1ang*PI/180)*p1pow;

                                   xpos = p1xy[0]+1;           

                                   ypos = p1xy[1]-1;

                                   prevx = xpos;

                                   prevy = ypos;

                                   OCR0 = 60;

                            }

 

                   }

                   if (difficulty == 2)  // change wind every other shot if difficulty is in hard mode

                   {

                    if (numshots == 2)

                    {

                           numshots = 0;

                           wind = rand()/(32767/18);

                                   wind -= 9;

                    }

                   }

              break;

              case Fire1:          // trajectory calculation for player 1

          if (xpos >= 126 || xpos <= 1)  // if bullet hits boundary end turn and goto P2's turn

              {

                           statevar = P2T;

                           if (numshots < 2) numshots++;

                           if (p1weap == 1) p1w2avail--;

                           if (p1weap == 2) p1w3avail--;

                           video_pt((char)prevx,(char)prevy,0);

                           TCCR0 = 0b00000100;

              }

              else if ((test > 0) && !(prevx == xpos && prevy == ypos))  // if the point is black, then you hit something so goto erase state

              {

                     statevar = Erase1;

                     TCCR0 = 0b00000100;

              }

              else

              {

                                  // change the location and power of the bullet, play a note.

             prevx = xpos;

             prevy = ypos;

                     ypos -= (float)ypow/100.0;

               xpos += (float)xpow/100.0;

               ypow = ypow - .25;

               xpow += (float)wind/100.0;

                      if((musicT++) > 15)        //each note 1/4 second

                                         {

                                         musicT = 0;

                                         //TCCR0 = 0;

                                         TCCR0 = 0b00011100 ;  //not a rest

                                         OCR0++ ;

                                                 //test for end of scale

                                         }     

 

         }

         if (ypos <= 12 && prevy >12) video_pt((char)prevx,(char)prevy,0);

         if (ypos >= 12 && prevy >= 12)

         {

         test = video_set((char)xpos,(char)ypos);  //check to see if the next point is set or not(did u hit terrain)

                 video_pt((char)xpos,(char)ypos,1);

                 if((char)prevx == (char)xpos && (char)prevy == (char)ypos)

                 {

                     test = 0;

                 }

                 else

                 {

                     video_pt((char)prevx,(char)prevy,0);

                     //test = video_set((char)prevx,(char)prevy);

                 }

              }

              else test = 0;

              break;

              case Erase1:  // erase terrain depending on which weapon was chosen

               if (numshots < 2) numshots++;

                     if (p1weap == 0)

                     {

                           video_line((char)xpos-1,(char)ypos,(char)xpos+1,(char)ypos,0);

                           video_pt((char)xpos,(char)ypos+1,0);

                           video_pt((char)xpos,(char)ypos-1,0);

                     }

                     else if (p1weap == 1)

                     {

                          p1w2avail--;

                          video_line((char)xpos-2,(char)ypos,(char)xpos+2,(char)ypos,0);

                           video_line((char)xpos-1,(char)ypos+1,(char)xpos+1,(char)ypos+1,0);

                           video_line((char)xpos-1,(char)ypos-1,(char)xpos+1,(char)ypos-1,0);

                           video_pt((char)xpos,(char)ypos+2,0);

                           video_pt((char)xpos,(char)ypos-2,0);

                     }

                     else if (p1weap == 2)

                     {

                           p1w3avail--;

                           video_line((char)xpos-3,(char)ypos,(char)xpos+3,(char)ypos,0);

                          video_line((char)xpos-2,(char)ypos-1,(char)xpos+2,(char)ypos-1,0);

                          video_line((char)xpos-2,(char)ypos+1,(char)xpos+2,(char)ypos+1,0);

                           video_line((char)xpos-1,(char)ypos+2,(char)xpos+1,(char)ypos+2,0);

                           video_line((char)xpos-1,(char)ypos-2,(char)xpos+1,(char)ypos-2,0);

                           video_pt((char)xpos,(char)ypos+3,0);

                           video_pt((char)xpos,(char)ypos-3,0);

                     }

                     // check to see if any of the tanks were hit and if they were go to the corresponding state

                     if ((char)xpos >= (p2x - 3 - p1weap) && (char)xpos <= (p2x + 3 + p1weap) && (char)ypos <= (map[randmap][p2x]+1+p2weap))

                     {

                           statevar = HIT2;

                           OCR0=195;

                     }

                   else if ((char)xpos >= (p1x - 3 - p2weap) && (char)xpos <= (p1x + 3 + p2weap) && (char)ypos <= (map[randmap][p1x]+1+p1weap))

                     {

                           statevar = HIT1;

                           OCR0 = 195;

                     }

                     else

                     {

                           statevar = P2T;

               }

              break;

              case P2T:     // player 2's turn

                     // display p2's variables at the top

                     sprintf (temp,"P2");

                     video_putsmalls(4,4,temp);

                  sprintf (temp, "%02d", p2pow);

                   video_putsmalls(32,4,temp);

                   sprintf(temp,"%03d",p2ang);

                   video_putsmalls(60,4,temp);

                   sprintf(temp,"%01d",p2weap+1);

                   video_putsmalls(92,4,temp);

 

                     // draw weapon cache

                   if (p2weap+1 == 1)

                   {

                     video_pt(97,5,1);

                     video_line(98,4,99,4,1);

                     video_line(98,6,99,6,1);

                     video_pt(100,5,1);

                     video_line(101,4,102,4,1);

                     video_line(101,6,102,6,1);

                     video_pt(103,5,1);

                   }

                   else if (p2weap+1 == 2 && p2w2avail == 3)

                   {

                     video_pt(97,5,1);

                     video_pt(100,5,1);

                     video_pt(103,5,1);

                     video_line(98,4,102,4,0);

                     video_line(98,6,102,6,0);               

                   }

                   else if (p2weap+1 == 2 && p2w2avail == 2)

                   {

                     video_pt(97,5,0);

                     video_pt(100,5,1);

                     video_pt(103,5,1);

                     video_line(98,4,102,4,0);

                     video_line(98,6,102,6,0);               

                   }

                   else if (p2weap+1 == 2 && p2w2avail == 1)

                   {

                     video_pt(97,5,0);

                     video_pt(100,5,0);

                     video_pt(103,5,1);       

                     video_line(98,4,102,4,0);

                     video_line(98,6,102,6,0);               

                   }

                else if (p2weap+1 == 2 && p2w2avail == 0)

                   {

                     video_pt(97,5,0);

                     video_pt(100,5,0);

                     video_pt(103,5,0);

                     video_line(98,4,102,4,0);

                     video_line(98,6,102,6,0);               

                   }

                   else if (p2weap+1 == 3 && p2w3avail == 1)

                   {

                     video_pt(97,5,0);

                     video_pt(100,5,0);

                     video_pt(103,5,1);        

                     video_line(98,4,102,4,0);

                     video_line(98,6,102,6,0);               

                   }

                   else if (p2weap+1 == 3 && p2w3avail == 0)

                   {

                     video_pt(97,5,0);

                     video_pt(100,5,0);

                     video_pt(103,5,0);       

                     video_line(98,4,102,4,0);

                     video_line(98,6,102,6,0);               

                   }  

 

 

 

           video_line(106,5,125,5,0);

                   video_line(115,5,115+wind,5,1);     // draw wind power and direction

                     // state machine to debounce buttons for inc/dec pow,angle and weapon choice

                   switch (dstate)

                   {

                       case Nopush:

                                   if (~PINC == 0x20) dstate = MaybeC5;

                                   else if (~PINC == 0x10) dstate = MaybeC4;

                                   else if (~PINC == 0x08) dstate = MaybeC3;

                                   else if (~PINC == 0x04) dstate = MaybeC2;

                                   else if (~PINC == 0x02) dstate = MaybeC1;

                                   else dstate = Nopush;

                       break;

                       case MaybeC5:

                            if (~PINC == 0x20)

                            { 

                                   timer = 3929;

                                   dstate = C5;

                            }

                            else dstate = Nopush;

                       break;

                       case C5:

                                   if (~PINC == 0x20)

                                   { 

                                         if (timer == 0)

                                         { 

                                                turretdraw(2,p2ang,0);

                                                timer = 3929;

                                                if (p2ang > 0) p2ang--;

                                                       turretdraw(2,p2ang,1);

                                         }

                                         dstate = C5;

                                   }

                                   else dstate = NoC5;

                       break;

                       case NoC5:

                                   if (~PINC     == 0x20) dstate = C5;

                                   else dstate = Nopush;

                       break;

                       case MaybeC4:

                            if (~PINC == 0x10)

                            { 

                                   timer = 3929;

                                   dstate = C4;

                            }

                            else dstate = Nopush;

                       break;

                       case C4:

                                   if (~PINC == 0x10)

                                   { 

                                         if (timer == 0)

                                         {

                                                turretdraw(2,p2ang,0);

                                                timer = 3929;

                                                       if (p2ang < 180) p2ang++;

                                                       turretdraw(2,p2ang,1);

                                         }

                                         dstate = C4;

                                   }

                                   else dstate = NoC4;

                       break;

                       case NoC4:

                                   if (~PINC     == 0x10) dstate = C4;

                                   else dstate = Nopush;

                       break;

                       case MaybeC3:

                            if (~PINC == 0x08)

                            {

                                   dstate = C3;

                                   timer = 3929;

                            }

                            else dstate = Nopush;

                       break;

                       case C3:

                                   if (~PINC == 0x08)

                                   {

                                    dstate = C3;

                                    if (timer == 0)

                                    {

                                          timer = 3929;

                                          if (p2pow > 0) p2pow--;

                                    }

                                   }

                                   else dstate = NoC3;

                       break;

                       case NoC3:

                                   if (~PINC     == 0x08) dstate = C3;

                                   else dstate = Nopush;

                       break;

                       case MaybeC2:

                            if (~PINC == 0x04)

                            {

                                   dstate = C2;

                                   timer = 3929;

                            }

                            else dstate = Nopush;

                       break;

                       case C2:

                                   if (~PINC == 0x04)

                                   {

                                   if (timer == 0)

                                   {

                                         timer = 3929;

                                          if (p2pow < 99) p2pow++;

                                   }

                                   dstate = C2;

                                   }

                                   else dstate = NoC2;

                       break;

                       case NoC2:

                                   if (~PINC     == 0x04) dstate = C2;

                                   else dstate = Nopush;

                       break;

                       case MaybeC1:

                            if (~PINC == 0x02)

                            {

                                   dstate = C1;

                                   p2weap++;

                                   p2weap = p2weap%3;

                            }

                            else dstate = Nopush;

                       break;

                       case C1:

                                   if (~PINC == 0x02) dstate = C1;

                                   else dstate = NoC1;

                       break;

                       case NoC1:

                                   if (~PINC     == 0x02) dstate = C1;

                                   else dstate = Nopush;

                       break;

 

                   }

                   if (~PINC == 0x40)  // goto fire 2 state if fire button is pressed

                   {  

                            if( (p2weap == 0) || (p2weap == 1 && p2w2avail > 0) || ( p2weap == 2 && p2w3avail > 0))

                            {

                                   statevar = Fire2;

                                         // start power and position for the bullet

                                   xpow = cos(p2ang*PI/180)*p2pow;

                                   ypow = sin(p2ang*PI/180)*p2pow;

                                   xpos = p2xy[0]-1;

                                   ypos = p2xy[1]-1;

                                   prevx = xpos;

                                   prevy = ypos;

                                   OCR0 = 60;

               }

                   }

                   if (difficulty == 2)  // change wind every other shot if difficulty is set to hard

                   {

                    if (numshots == 2)

                    {

                           numshots = 0;

                           wind = rand()/(32767/18);

                                   wind -= 9;

                    }

                   }

              break;

              case Fire2:  // calculate trajectory of player 2's shot

              if ((xpos >= 126) || (xpos <= 1))  // end player 2's turn if boundary is hit

              {

                     statevar = P1T;

                     if (numshots < 2) numshots++;

                     if (p2weap == 1) p2w2avail--;

                     if (p2weap == 2) p2w3avail--;

                     video_pt((char)prevx,(char)prevy,0);

                     TCCR0= 0b00000100;

              }

              else if ((test > 0) && !(prevx == xpos && prevy == ypos))  // if terrain is hit goto erase2 to erase the terrain

              {

                           statevar = Erase2;

                           TCCR0= 0b00000100;

              }

              else

       {

             prevx = xpos;

              prevy = ypos;

                           // make changes to power and location of bullet.

                     ypos -= (float)ypow/100.0;

               xpos -= (float)xpow/100.0;

               ypow = ypow - .25;

               xpow -= (float)wind/100.0;

                                  if((musicT++) > 15)        //each note 1/4 second

                                         {

                                         musicT = 0;

                                         //TCCR0 = 0;

                                         TCCR0 = 0b00011100 ;  //not a rest

                                         OCR0++ ;

                                                 //test for end of scale

                                         }

         }

         if (ypos <= 12 && prevy >12) video_pt((char)prevx,(char)prevy,0);

         if (ypos >= 12 && prevy >= 12)

         {

           test = video_set((char)xpos,(char)ypos);  // check to see if the point is terrain or not

                 video_pt((char)xpos,(char)ypos,1);

                 if((char)prevx == (char)xpos && (char)prevy == (char)ypos)

                 {

                     test = 0;

                 }

                 else

                 {

                     video_pt((char)prevx,(char)prevy,0);

 

                 }

              }

              break;

              case Erase2:  // erase terrain depending on choice of weapon

                     if (numshots < 2) numshots++;

                     if (p2weap == 0)

                     {

                           video_line((char)xpos-1,(char)ypos,(char)xpos+1,(char)ypos,0);

                           video_pt((char)xpos,(char)ypos+1,0);

                           video_pt((char)xpos,(char)ypos-1,0);

                     }

                     else if (p2weap == 1)

                     {  

                           p2w2avail--;

                        video_line((char)xpos-2,(char)ypos,(char)xpos+2,(char)ypos,0);

                           video_line((char)xpos-1,(char)ypos+1,(char)xpos+1,(char)ypos+1,0);

                           video_line((char)xpos-1,(char)ypos-1,(char)xpos+1,(char)ypos-1,0);

                           video_pt((char)xpos,(char)ypos+2,0);

                           video_pt((char)xpos,(char)ypos-2,0);

                     }

                     else if (p2weap == 2)

                     {

                           p2w3avail--;

                           video_line((char)xpos-3,(char)ypos,(char)xpos+3,(char)ypos,0);

                          video_line((char)xpos-2,(char)ypos-1,(char)xpos+2,(char)ypos-1,0);

                          video_line((char)xpos-2,(char)ypos+1,(char)xpos+2,(char)ypos+1,0);

                           video_line((char)xpos-1,(char)ypos+2,(char)xpos+1,(char)ypos+2,0);

                           video_line((char)xpos-1,(char)ypos-2,(char)xpos+1,(char)ypos-2,0);

                           video_pt((char)xpos,(char)ypos+3,0);

                           video_pt((char)xpos,(char)ypos-3,0);

                     }

                     // check to see if any of the tanks were hit and if so goto respective state

                     if ((char)xpos >= (p1x - 3 - p2weap) && (char)xpos <= (p1x + 3 + p2weap) && (char)ypos <= (map[randmap][p1x]+1+p2weap))

                     {

                           statevar = HIT1;

                           OCR0=195;

                     }

                     else if ((char)xpos >= (p2x - 3 - p2weap) && (char)xpos <= (p2x + 3 + p2weap) && (char)ypos <= (map[randmap][p2x]+1+p2weap))

                     {

                           statevar = HIT2;

                           OCR0=195;

                     }

                     else

                     statevar = P1T;

            break;

            case HIT2: // player 2 is hit, display p1 wins and wait for fire button

              sprintf(temp, "PLAYER 1 WIN");

              video_putsmalls(40,20,temp);

              sprintf(temp, "HIT FIRE TO CONTINUE");

              video_putsmalls(28,32,temp);           

            if (~PINC == 0x40) // advance to clear map state when fire is pressed

                     {

                            play1w = play1w + 1; // increment player 1's win count

                            statevar = Clearmap;

                            c2 = 1;

                     }

                     else

                     {

                     statevar = HIT2;

                           // play a small note to imitate the tank blowing up

                            if((musicT++) > 15)        //each note 1/4 second

                           {

                                  musicT = 0;

                                  TCCR0 = 0b00000100;

                                  if (OCR0 > 193)

                                  {

                                         TCCR0 = 0b00011100 ;  //not a rest

                                         OCR0-- ;

                                  }

                           }

                     }

              break;

            case HIT1:  //player 1 is hit, display p2 wins and wait for fire button

              sprintf(temp, "PLAYER 2 WIN");

              video_putsmalls(40,20,temp);

              sprintf(temp, "HIT FIRE TO CONTINUE");

              video_putsmalls(28,32,temp);

              if (~PINC == 0x40) // advance to clear map state when fire is pressed

                     {

                            play2w = play2w + 1;  // increment player 2's win count

                            statevar = Clearmap;

                            c2 = 1;

                     }

 

                     else

                     {

                      statevar = HIT1;

                      // play a small note to imitate the tank blowing up

                            if((musicT++) > 15)        //each note 1/4 second

                           {

                                  musicT = 0;

                                  TCCR0 = 0b00000100;

                                  if (OCR0 > 193)

                                  {

                                   TCCR0 = 0b00011100 ;  //not a rest

                                   OCR0-- ;

                                  }

                           }                     

                     }

              break;

        case Clearmap:  // clear the screen and then advance to next state (Start again)

                     if (c2 < 126)

                     {

                           for (time = c2; time < 3 + c2; time++)

                            {

                                   video_line (time,1,time,98,0);

                            }

                            c2 = time;

                     }

                     else

                     {

                            video_line (126,1,126,98,1);

                            statevar = Startagain;

                     }

                     break;

            case Startagain:  // print out "Play again" and display p1 and p2 win counts and goto Next state

              sprintf(temp,"PLAY AGAIN");

                   video_puts(32,20,temp);

                   sprintf(temp,"PLAYER 1");

                     video_putsmalls(24,60,temp);

                     sprintf(temp,"PLAYER 2");

                     video_putsmalls(68,60,temp);

                     sprintf( temp, "%d", play1w);

                     video_putsmalls(36,68,temp);

                     sprintf(temp, "%d", play2w);

                     video_putsmalls(80,68,temp);

                     statevar = Next;

                     dstate =  Nopush;

                break;

                case Next:  // allow the user to change the difficulty and start a new game

                sprintf(temp,"DIFFICULTY:"); // display current difficulty setting

                video_putsmalls(28,40,temp);

          if (difficulty == 0)

          {

            sprintf(temp, "EASY");

            video_putsmalls(76,40,temp);

          }

          else if (difficulty == 1)

          {

            sprintf(temp, "MED ");

            video_putsmalls(76,40,temp);

          }

          else if (difficulty == 2)

          {

            sprintf(temp, "HARD");

            video_putsmalls(76,40,temp);

          }

          else

          {

              sprintf(temp,"%d",difficulty);

              video_putsmalls(76,40,temp);

          }  

                // state machine to debounce the select button

                switch (dstate)

                   {  

                       case Nopush:

                                   if (~PINC == 0x01) dstate = MaybeC6;

                                   else dstate = Nopush;

                       break;

                       case MaybeC6:

                            if (~PINC == 0x01)

                            {

                                   dstate = C6;

                                   difficulty++;

                                   if (difficulty > 2) difficulty = 0;

                                   //difficulty = difficulty % 3;

                            }

                            else dstate = Nopush;

                       break;

                       case C6:

                                   if (~PINC == 0x01) dstate = C6;

                                   else dstate = NoC6;

                       break;

                       case NoC6:

                                   if (~PINC     == 0x01) dstate = C6;

                                   else dstate = Nopush;

                       break;

                   }

 

                     if (~PINC == 0x80)  // when start is press reinitialize all the variables and start a new game

                     {

                        c1=1;

                           c2=1;

                           p1pow = 50;

                           p2pow = 50;

                           p1ang = 45;

                           p2ang = 45;

                           p1weap = 0;

                           p2weap = 0;

                           numshots = 0;

                           if (difficulty == 0) wind = 0;

                          

                        statevar = Clear;

 

 

                           numshots = 0;

                           p1w2avail = 3;

                           p1w3avail = 1;

                           p2w2avail = 3;

                           p2w3avail = 1;

                           timer = 0;

                     }

                     break;

              }

     }  //line 231

  }  //while

}  //main

 

 

Table of Contents