User Tools

Site Tools


lab_3

Lab 3: Rendering Video for Space Invaders via the VGA Controller

Background

In this lab, you will write the software that renders the tank, bullets, bunkers, and aliens. Let's review the game screen.

As shown, there are 5 rows of aliens, but there are only 3 different kinds of aliens. The top row contains one kind, the next two rows are another kind, and the final two bottom rows are yet another kind of alien. Each alien is displayed in two different guises, one with it's “legs” in and one with it's “legs” out. By alternating these two images, it appears as though the aliens are “walking” across the screen. The screen shot below shows the aliens in their other guise.

Note that there are only 4 major items to display (or render) to the screen: the tank, the bunkers, the aliens, and the bullets. The bunkers don't move. The aliens always move as a group, so they can be treated as one block for rendering purposes. The tank can only fire one bullet at a time, so only one tank bullet can be in flight on the screen at any given time. After watching the game for awhile, it appears that the aliens can have up to 3 (perhaps 4) bullets in flight at any given time. Let's assume a maximum of 4 bullets in flight for the aliens. Note that the aliens shoot more than one type of bullet. The bunkers don't move, of course, but they are eroded as bullets strike them. Here is a list of the items that you will need to handle.

  1. tank position (x, y)
  2. tank bullet position (x, y)
  3. alien block position (x, y)
  4. alien bullet-0 position (x, y, bullet_type)
  5. alien bullet-1 position (x, y, bullet_type)
  6. alien bullet-2 position (x, y, bullet_type)
  7. alien bullet-3 position (x, y, bullet_type)
  8. bunker 0 erosion state (right-most)
  9. bunker 1 erosion state
  10. bunker 2 erosion state
  11. bunker 3 erosion state (left-most)

In addition, you need to keep track of alien deaths. You can do this with a one-dimensional array of booleans. Assume that the origin of the aliens is top-left (the top-left alien has an address of 0. The bottom-right alien has an address of 54). Use this coordinate scheme to make it easier for the TAs to pass you off.

If you know all the state as described above, and which aliens are currently alive, you know how to render the current state of the game on to the computer screen. All coordinates are screen coordinates on the 640 x 480 screen. There is actually a bit more state, as in, what bullets are in flight, but that could be represented by using a large positive value for the coordinate that puts the bullet “off-screen”.

Specifications

You are to implement a render routine that takes the following as input:

  1. the coordinates and bullet types as listed above, and
  2. the boolean array representing alien life or death.

You can treat these as global variables as they will eventually be shared between the code that writes the coordinate values, and the code that reads them for rendering purposes (this lab).

For debugging and pass-off purposes, you will provide user I/O via the UART, as we have done in previous labs. The UART will provide the user with a simple menu of operations that allow you to update the screen coordinates of tanks, bullets, and aliens. The purpose of this UART I/O is to allow you to control and test your render routine (and to pass it off).

Commands to the UART are encoded as single-digit decimal positive integers. You must support the following commands:

  • 4 move the tank-position to the left by a constant number of pixels. This should just be the minimum amount the tank can move. Choose a value that makes sense. If you repeatedly hit this key, the tank should move smoothly across the screen.
  • 6 same as above, but move the tank to the right.
  • 8 update alien position. Each time you hit this key, the alien block should shift over as it does in the game, with the aliens switching guises on each update. Pick an x-y update value that looks similar to the game. The aliens should shift left and right, dropping down a row each time they hit the right or left side (just like the game).
  • 2 kill alien. This will query for a number between 0 and 54 (0 is upper left, 54 is lower right) that indicates which alien to kill. The selected alien should disappear.
  • 5 fire tank bullet. This sets the coordinate of the tank bullet so that it is just above the tank turret and will be drawn as if it had just fired.
  • 3 fire random alien missile. Randomly pick an alien from the bottom row and pick one of the available 4 alien bullet coordinates and update it so the missile is dropping from the randomly-chosen alien.
  • 9 update all bullets. All bullets should go up or down a constant amount each time this key is pressed. They should disappear and become available for reuse once they leave the screen.
  • 7 erode bunker. This will query for a number between 0 and 3 and erode the bunker by one step.

Notes

  1. This project will get HUGE and COMPLEX by the end of the semester. Figuring out your data types, planning out your code hierarchy, organizing your code into different files, and making good comments is key to avoiding a lot of confusion throughout the semester (and your TAs will be more happy to help you debug).
  2. To receive full credit, the routine that reads the keyboard must work as follows. It must only update the coordinates for various elements (bullets, tank, etc.) or the boolean array representing alien life or death, and subsequently call render().
  3. Note that you don't have to print the menu options to the screen. The TAs (and you) can use a cheat-sheet to remember what key does what.
  4. You don't have to erode the bunkers when bullets strike them, for example, for this lab, just draw them and incrementally erode them when the proper key is struck.
  5. You may only call the clear screen routine once: when you start up. After that, you must undraw objects before you move them. Your screen updates can be much faster that way as most of the pixels are black.
  6. You don't need to worry about artifacts, e.g., flashing, etc. that may occur as you press the keys during pass-off.
  7. You don't need to implement any form of double-buffering at this point. I only included the capability in the test code so that you could use the same test code for both Lab 3 and Lab 4.
  8. To avoid having to rewrite your code for Lab 4, try to anticipate how you will use the rendering code for Lab 4. Carefully think about ways to reduce the number of memory updates that are required when you update the screen as this will make it easier for you to achieve a smoothly-running game.
  9. After watching the game for awhile, it is clear that the bunkers are implemented with a series of square blocks. Implement them accordingly (see below). Note that in Lab 4, you will compute the intersection of a bullet within a specific block of the bunker. As soon as bullet intersects with a bunker, the bullet disintegrates and the bunker erodes by one step. After the block has been hit 4 times, the block disappears.

You will be working at VGA resolution (640 x 480) pixels. However, you won't want to draw the game objects using 1:1 game-pixel to VGA pixel ratio. You will probably want to render each game-item pixel (tank, alien, etc.) pixel as a 2×2 pixel on the 640 x 480 screen. You will either want to modify the code that was provided, or, write your own code to do this. You won't get full credit if the aliens are drawn too small. Ask the TAs if you have questions about this.

This zipped-up set of files contains the definition of the hardware system that you will use for this lab. Use this how-to file to get this build system installed and to test the installation.

You may run out of memory space in the BRAMs for your code and data. Check the tutorial page for instructions on how to move it to the DDR and run it.

Here's the source code for the vdmaTest.c file:

Example Source Files for the Basic Video Hardware

/*
 * Copyright (c) 2009 Xilinx, Inc.  All rights reserved.
 *
 * Xilinx, Inc.
 * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A
 * COURTESY TO YOU.  BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS
 * ONE POSSIBLE   IMPLEMENTATION OF THIS FEATURE, APPLICATION OR
 * STANDARD, XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION
 * IS FREE FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE
 * FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION.
 * XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO
 * THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO
 * ANY WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE
 * FROM CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 */
 
/*
 * helloworld.c: simple test application
 */
 
#include <stdio.h>
#include "platform.h"
#include "xparameters.h"
#include "xaxivdma.h"
#include "xio.h"
#include "time.h"
#include "unistd.h"
#define DEBUG
void print(char *str);
 
#define FRAME_BUFFER_0_ADDR 0xC0000000  // Starting location in DDR where we will store the images that we display.
#define MAX_SILLY_TIMER 10000000;
 
int main()
{
	init_platform();                   // Necessary for all programs.
	int Status;                        // Keep track of success/failure of system function calls.
	XAxiVdma videoDMAController;
	// There are 3 steps to initializing the vdma driver and IP.
	// Step 1: lookup the memory structure that is used to access the vdma driver.
    XAxiVdma_Config * VideoDMAConfig = XAxiVdma_LookupConfig(XPAR_AXI_VDMA_0_DEVICE_ID);
    // Step 2: Initialize the memory structure and the hardware.
    if(XST_FAILURE == XAxiVdma_CfgInitialize(&videoDMAController, VideoDMAConfig,	XPAR_AXI_VDMA_0_BASEADDR)) {
    	xil_printf("VideoDMA Did not initialize.\r\n");
    }
    // Step 3: (optional) set the frame store number.
    if(XST_FAILURE ==  XAxiVdma_SetFrmStore(&videoDMAController, 2, XAXIVDMA_READ)) {
    	xil_printf("Set Frame Store Failed.");
    }
    // Initialization is complete at this point.
 
    // Setup the frame counter. We want two read frames. We don't need any write frames but the
    // function generates an error if you set the write frame count to 0. We set it to 2
    // but ignore it because we don't need a write channel at all.
    XAxiVdma_FrameCounter myFrameConfig;
    myFrameConfig.ReadFrameCount = 2;
    myFrameConfig.ReadDelayTimerCount = 10;
    myFrameConfig.WriteFrameCount =2;
    myFrameConfig.WriteDelayTimerCount = 10;
    Status = XAxiVdma_SetFrameCounter(&videoDMAController, &myFrameConfig);
    if (Status != XST_SUCCESS) {
	   xil_printf("Set frame counter failed %d\r\n", Status);
	   if(Status == XST_VDMA_MISMATCH_ERROR)
		   xil_printf("DMA Mismatch Error\r\n");
    }
    // Now we tell the driver about the geometry of our frame buffer and a few other things.
    // Our image is 480 x 640.
    XAxiVdma_DmaSetup myFrameBuffer;
    myFrameBuffer.VertSizeInput = 480;    // 480 vertical pixels.
    myFrameBuffer.HoriSizeInput = 640*4;  // 640 horizontal (32-bit pixels).
    myFrameBuffer.Stride = 640*4;         // Dont' worry about the rest of the values.
    myFrameBuffer.FrameDelay = 0;
    myFrameBuffer.EnableCircularBuf=1;
    myFrameBuffer.EnableSync = 0;
    myFrameBuffer.PointNum = 0;
    myFrameBuffer.EnableFrameCounter = 0;
    myFrameBuffer.FixedFrameStoreAddr = 0;
    if(XST_FAILURE == XAxiVdma_DmaConfig(&videoDMAController, XAXIVDMA_READ, &myFrameBuffer)) {
    	xil_printf("DMA Config Failed\r\n");
     }
    // We need to give the frame buffer pointers to the memory that it will use. This memory
    // is where you will write your video data. The vdma IP/driver then streams it to the HDMI
    // IP.
     myFrameBuffer.FrameStoreStartAddr[0] = FRAME_BUFFER_0_ADDR;
     myFrameBuffer.FrameStoreStartAddr[1] = FRAME_BUFFER_0_ADDR + 4*640*480;
 
     if(XST_FAILURE == XAxiVdma_DmaSetBufferAddr(&videoDMAController, XAXIVDMA_READ,
    		               myFrameBuffer.FrameStoreStartAddr)) {
    	 xil_printf("DMA Set Address Failed Failed\r\n");
     }
     // Print a sanity message if you get this far.
     xil_printf("Woohoo! I made it through initialization.\n\r");
     // Now, let's get ready to start displaying some stuff on the screen.
     // The variables framePointer and framePointer1 are just pointers to the base address
     // of frame 0 and frame 1.
     unsigned int * framePointer0 = (unsigned int *) FRAME_BUFFER_0_ADDR;
     unsigned int * framePointer1 = ((unsigned int *) FRAME_BUFFER_0_ADDR) + 640*480;
     // Just paint some large red, green, blue, and white squares in different
     // positions of the image for each frame in the buffer (framePointer0 and framePointer1).
     int row=0, col=0;
     for( row=0; row<480; row++) {
    	 for(col=0; col<640; col++) {
    	 if(row < 240) {
    		 if(col<320) {
    			 // upper left corner.
    			 framePointer0[row*640 + col] = 0x00FF0000;  // frame 0 is red here.
    			 framePointer1[row*640 + col] = 0x0000FF00;  // frame 1 is green here.
    		 } else {
    			 // upper right corner.
    			 framePointer0[row*640 + col] = 0x000000FF;  // frame 0 is blue here.
    			 framePointer1[row*640 + col] = 0x00FF0000;  // frame 1 is red here.
    		 }
    	 } else {
    		 if(col<320) {
    			 // lower left corner.
    			 framePointer0[row*640 + col] = 0x0000FF00;  // frame 0 is green here.
    			 framePointer1[row*640 + col] = 0x00FFFFFF;  // frame 1 is white here.
    		 } else {
    			 // lower right corner.
    			 framePointer0[row*640 + col] = 0x00FFFFFF;  // frame 0 is white here.
    			 framePointer1[row*640 + col] = 0x000000FF;  // frame 1 is blue here.
    		 }
    	 }
       }
     }
     // This tells the HDMI controller the resolution of your display (there must be a better way to do this).
     XIo_Out32(XPAR_AXI_HDMI_0_BASEADDR, 640*480);
 
     // Start the DMA for the read channel only.
     if(XST_FAILURE == XAxiVdma_DmaStart(&videoDMAController, XAXIVDMA_READ)){
    	 xil_printf("DMA START FAILED\r\n");
     }
     int frameIndex = 0;
     // We have two frames, let's park on frame 0. Use frameIndex to index them.
     // Note that you have to start the DMA process before parking on a frame.
     if (XST_FAILURE == XAxiVdma_StartParking(&videoDMAController, frameIndex,  XAXIVDMA_READ)) {
    	 xil_printf("vdma parking failed\n\r");
     }
     // Oscillate between frame 0 and frame 1.
     int sillyTimer = MAX_SILLY_TIMER;  // Just a cheap delay between frames.
     while (1) {
    	 while (sillyTimer) sillyTimer--;    // Decrement the timer.
    	 sillyTimer = MAX_SILLY_TIMER;       // Reset the timer.
         frameIndex = (frameIndex + 1) % 2;  // Alternate between frame 0 and frame 1.
         if (XST_FAILURE == XAxiVdma_StartParking(&videoDMAController, frameIndex,  XAXIVDMA_READ)) {
        	 xil_printf("vdma parking failed\n\r");
         }
     }
     cleanup_platform();
 
    return 0;
}

How to Generate Bit-Map Data for your Aliens and Other Things

There are several ways to generate the bit-maps for your aliens. In the past, students have used editors to create bit-maps. Another approach is to create the bit-maps using a text editor. The code below will not compile but should give you a good idea of how to do this. If you squint your eyes a bit, you should be able to see the shape of one of the aliens in the text below (the 1s are the bright pixels that form the alien). Carefully study what packWord32 does. Also, note that the 1's and 0's that form the alien are part of a 'C' array initialization. After thinking about this for a bit, and carefully studying the vdmaTest.c code, you should be able to figure out how use this data structure to write the bit-map to the display.

#include <stdio.h>
#include <stdint.h>
#define ALIEN_HEIGHT 16
 
// Packs each horizontal line of the figures into a single 32 bit word. 
#define packWord32(b31,b30,b29,b28,b27,b26,b25,b24,b23,b22,b21,b20,b19,b18,b17,b16,b15,b14,b13,b12,b11,b10,b9,b8,b7,b6,b5,b4,b3,b2,b1,b0) \
((b31 << 31) | (b30 << 30) | (b29 << 29) | (b28 << 28) | (b27 << 27) | (b26 << 26) | (b25 << 25) | (b24 << 24) |						  \
 (b23 << 23) | (b22 << 22) | (b21 << 21) | (b20 << 20) | (b19 << 19) | (b18 << 18) | (b17 << 17) | (b16 << 16) |						  \
 (b15 << 15) | (b14 << 14) | (b13 << 13) | (b12 << 12) | (b11 << 11) | (b10 << 10) | (b9  << 9 ) | (b8  << 8 ) |						  \
 (b7  << 7 ) | (b6  << 6 ) | (b5  << 5 ) | (b4  << 4 ) | (b3  << 3 ) | (b2  << 2 ) | (b1  << 1 ) | (b0  << 0 ) )
 
uint32_t topOutAlienSymbol[ALIEN_HEIGHT] =
{ 
	packWord32(0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
	packWord32(0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
	packWord32(0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
	packWord32(0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
	packWord32(0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
	packWord32(0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
	packWord32(0,0,0,0,1,1,1,1,0,0,1,1,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0),
	packWord32(0,0,0,0,1,1,1,1,0,0,1,1,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0),
	packWord32(0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0),
	packWord32(0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0),
	packWord32(0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
	packWord32(0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
	packWord32(0,0,0,0,0,0,1,1,0,0,1,1,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
	packWord32(0,0,0,0,0,0,1,1,0,0,1,1,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
	packWord32(0,0,0,0,1,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0),
	packWord32(0,0,0,0,1,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0), 
};                                                                                                                                                                       
 
#define WORD_WIDTH 32
 
int main() {		
  // Let's print out the alien as ASCII characters on the screen.
  // Each line of the alien is a 32-bit integer. We just need to strip the bits out and send
  // them to stdout.
  // MSB is the left-most pixel for the alien, so start from the MSB as we print from left to right.
  uint32_t row, column;
  for (row=0; row<ALIEN_HEIGHT; row++) {
    for (column=0; column<WORD_WIDTH; column++) {
      if ((topOutAlienSymbol[row] & (1<<(WORD_WIDTH-1-column)))) {
	printf("#");
      } else {
	printf(" ");
      }
    }
    printf("\n");
  }
}

Use the code below to receive characters typed on the keyboard.

#include <stdio.h>
 
char input;
 
input = getchar();

Handling Globals

Functional interfaces provide a structured way to handle globals using set and get functions. You can see examples of how to do this below.

#ifndef globals_h
#define globals_h

#define <stdint.h>

typedef struct {uint16_t x; uint16_t y;} point_t;

void setTankPositionGlobal(uint16_t val);
uint16_t getTankPositionGlobal();

void setTankBulletPosition(point_t val);
point_t getTankBulletPosition();

//////////// Another way to do it without structs. //////////////
void setTankBulletPositionX(uint16_t val);
void setTankBulletPositionY(uint16_t val);

uint16_t getTankBulletPositionX();
uint16_t getTankBulletPositionY();

#endif
// Demonstrates one way to handle globals safely in C.
#include "globals.h"

// Here are the globals.
static uint16_t tankPosition;
static point_t tankBulletPosition;
static point_t alienBlockPosition;

// Here are the accessors.
void setTankPositionGlobal(uint16_t val) {
  tankPosition = val;
}

uint16_t getTankPositionGlobal() {
  return tankPosition;
}

void setTankBulletPosition(point_t val) {
  tankBulletPosition.x = val.x; 
  tankBulletPosition.y = val.y;
}

point_t getTankBulletPosition() {
  return tankBulletPosition;
}

//////////////// Another way to do this without structs ////////////////
uint16_t tankBulletPositionX;
uint16_t tankBulletPositionY;

void setTankBulletPositionX(uint16_t val) {tankBulletPositionX = val;}
void setTankBulletPositionY(uint16_t val) {tankBulletPositionY = val;}

uint16_t getTankBulletPositionX(){return tankBulletPositionX;}
uint16_t getTankBulletPositionY(){return tankBulletPositionY;}
#include <stdio.h>

#include "globals.h"
int main() {
  point_t currentPosition;
  currentPosition.x = 99;
  currentPosition.y = 11;
  
  setTankPositionGlobal(14);
  printf("tank position: %d\n\r", getTankPositionGlobal());

  setTankBulletPosition(currentPosition);
  printf("tank x:%d tank y:%d\n\r", getTankBulletPosition().x, getTankBulletPosition().y);

  // What does this do?
  point_t anotherTankBulletPosition = getTankBulletPosition();
  anotherTankBulletPosition.x = 55;
  anotherTankBulletPosition.y = 44;
  // Does this affect the value of the global?
  printf("tank x:%d tank y:%d\n\r", getTankBulletPosition().x, getTankBulletPosition().y);
  
}

Helps

Here are the bunker erosion patterns. I double-checked them and they are correct.

Here is some C-code that defines the alien shapes and other things. You can use these if you like.

#include <stdint.h>
// Must define packword for each of the different bit-widths.
static const uint32_t saucer_16x7[] =
{
packword16(0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0),
packword16(0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0),
packword16(0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0),
packword16(0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0),
packword16(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1),
packword16(0,0,1,1,1,0,0,1,1,0,0,1,1,1,0,0),
packword16(0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0)
};
 
static const uint32_t alien_explosion_12x10[] =
{
packword12(0,0,0,0,0,0,1,0,0,0,0,0),
packword12(0,0,0,1,0,0,1,0,0,0,1,0),
packword12(1,0,0,1,0,0,0,0,0,1,0,0),
packword12(0,1,0,0,1,0,0,0,1,0,0,0),
packword12(0,0,0,0,0,0,0,0,0,0,1,1),
packword12(1,1,0,0,0,0,0,0,0,0,0,0),
packword12(0,0,0,1,0,0,0,1,0,0,1,0),
packword12(0,0,1,0,0,0,0,0,1,0,0,1),
packword12(0,1,0,0,0,1,0,0,1,0,0,0),
packword12(0,0,0,0,0,1,0,0,0,0,0,0)
};
 
static const uint32_t alien_top_in_12x8[] =
{
packword12(0,0,0,0,0,1,1,0,0,0,0,0),
packword12(0,0,0,0,1,1,1,1,0,0,0,0),
packword12(0,0,0,1,1,1,1,1,1,0,0,0),
packword12(0,0,1,1,0,1,1,0,1,1,0,0),
packword12(0,0,1,1,1,1,1,1,1,1,0,0),
packword12(0,0,0,1,0,1,1,0,1,0,0,0),
packword12(0,0,1,0,0,0,0,0,0,1,0,0),
packword12(0,0,0,1,0,0,0,0,1,0,0,0)
};
 
static const uint32_t alien_top_out_12x8[] =
{
packword12(0,0,0,0,0,1,1,0,0,0,0,0),
packword12(0,0,0,0,1,1,1,1,0,0,0,0),
packword12(0,0,0,1,1,1,1,1,1,0,0,0),
packword12(0,0,1,1,0,1,1,0,1,1,0,0),
packword12(0,0,1,1,1,1,1,1,1,1,0,0),
packword12(0,0,0,0,1,0,0,1,0,0,0,0),
packword12(0,0,0,1,0,1,1,0,1,0,0,0),
packword12(0,0,1,0,1,0,0,1,0,1,0,0)
};
 
static const uint32_t alien_middle_in_12x8[] =
{
packword12(0,0,0,1,0,0,0,0,0,1,0,0),
packword12(0,0,0,0,1,0,0,0,1,0,0,0),
packword12(0,0,0,1,1,1,1,1,1,1,0,0),
packword12(0,0,1,1,0,1,1,1,0,1,1,0),
packword12(0,1,1,1,1,1,1,1,1,1,1,1),
packword12(0,1,1,1,1,1,1,1,1,1,1,1),
packword12(0,1,0,1,0,0,0,0,0,1,0,1),
packword12(0,0,0,0,1,1,0,1,1,0,0,0)
};
 
static const uint32_t alien_middle_out_12x8[] =
{
packword12(0,0,0,1,0,0,0,0,0,1,0,0),
packword12(0,1,0,0,1,0,0,0,1,0,0,1),
packword12(0,1,0,1,1,1,1,1,1,1,0,1),
packword12(0,1,1,1,0,1,1,1,0,1,1,1),
packword12(0,1,1,1,1,1,1,1,1,1,1,1),
packword12(0,0,1,1,1,1,1,1,1,1,1,0),
packword12(0,0,0,1,0,0,0,0,0,1,0,0),
packword12(0,0,1,0,0,0,0,0,0,0,1,0)
};
 
static const uint32_t alien_bottom_in_12x8[] =
{
packword12(0,0,0,0,1,1,1,1,0,0,0,0),
packword12(0,1,1,1,1,1,1,1,1,1,1,0),
packword12(1,1,1,1,1,1,1,1,1,1,1,1),
packword12(1,1,1,0,0,1,1,0,0,1,1,1),
packword12(1,1,1,1,1,1,1,1,1,1,1,1),
packword12(0,0,1,1,1,0,0,1,1,1,0,0),
packword12(0,1,1,0,0,1,1,0,0,1,1,0),
packword12(0,0,1,1,0,0,0,0,1,1,0,0)
};
 
static const uint32_t alien_bottom_out_12x8[] =
{
packword12(0,0,0,0,1,1,1,1,0,0,0,0),
packword12(0,1,1,1,1,1,1,1,1,1,1,0),
packword12(1,1,1,1,1,1,1,1,1,1,1,1),
packword12(1,1,1,0,0,1,1,0,0,1,1,1),
packword12(1,1,1,1,1,1,1,1,1,1,1,1),
packword12(0,0,0,1,1,0,0,1,1,0,0,0),
packword12(0,0,1,1,0,1,1,0,1,1,0,0),
packword12(1,1,0,0,0,0,0,0,0,0,1,1)
};
 
static const uint32_t tank_15x8[] =
{
packword15(0,0,0,0,0,0,0,1,0,0,0,0,0,0,0),
packword15(0,0,0,0,0,0,1,1,1,0,0,0,0,0,0),
packword15(0,0,0,0,0,0,1,1,1,0,0,0,0,0,0),
packword15(0,1,1,1,1,1,1,1,1,1,1,1,1,1,0),
packword15(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1),
packword15(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1),
packword15(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1),
packword15(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)
};
 
 
// Shape of the entire bunker.
static const uint32_t bunker_24x18[] =
{
packword24(0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0),
packword24(0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0),
packword24(0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0),
packword24(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1),
packword24(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1),
packword24(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1),
packword24(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1),
packword24(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1),
packword24(1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1),
packword24(1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1),
packword24(1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1),
packword24(1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1),
packword24(1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1),
packword24(1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1),
packword24(1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1),
packword24(1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1),
packword24(1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1),
packword24(1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1)
};
 
// These are the blocks that comprise the bunker and each time a bullet
// strikes one of these blocks, you erode the block as you sequence through 
// these patterns.
static const uint32_t bunkerDamage0_6x6[] = {
packword6(0,1,1,0,0,0),
packword6(0,0,0,0,0,1),
packword6(1,1,0,1,0,0),
packword6(1,0,0,0,0,0),
packword6(0,0,1,1,0,0),
packword6(0,0,0,0,1,0)
};
 
static const uint32_t bunkerDamage1_6x6[] = {
packword6(1,1,1,0,1,0),
packword6(1,0,1,0,0,1),
packword6(1,1,0,1,1,1),
packword6(1,0,0,0,0,0),
packword6(0,1,1,1,0,1),
packword6(0,1,1,0,1,0)
};
 
static const uint32_t bunkerDamage2_6x6[] = {
packword6(1,1,1,1,1,1),
packword6(1,0,1,1,0,1),
packword6(1,1,0,1,1,1),
packword6(1,1,0,1,1,0),
packword6(0,1,1,1,0,1),
packword6(1,1,1,1,1,1)
};
 
static const uint32_t bunkerDamage3_6x6[] = {
packword6(1,1,1,1,1,1),
packword6(1,1,1,1,1,1),
packword6(1,1,1,1,1,1),
packword6(1,1,1,1,1,1),
packword6(1,1,1,1,1,1),
packword6(1,1,1,1,1,1)
};

Pass Off

The TAs will test your solution using the UART interface that was described above.

Scoring

  1. 40 points possible for a full pass-off.
  2. 10 points possible if the appearance of your game is identical with the original (using the example I provided in the syllabus). Remember to render everything at a size similar to the actual game, relative to the screen.
  3. 30 points possible for an excellent write-up.
  4. 20 points possible for well organized, well-commented code that follows the specifications above.

Hand In

Hand in a copy of your sources with your write-up. Link to Write-Up Instructions

lab_3.txt · Last modified: 2017/09/20 12:25 by brad_hutchings