Retro Invaders a space invaders clone by Chris Favreau. Written for the RetroMbuino development board from outrageouscircuits.com for the game programming contest.

Dependencies:   mbed

This is a space invaders clone written for the Retro Mbuino from outrageous circuits.

Development board: http://www.outrageouscircuits.com/shop/product/15 ).

The game itself is basic space invaders. Shoot them before they get to the bottom of the screen. It has a UFO saucer which you can shoot for extra points. You get 4 shields and each shield can be hit up to 4 times before it is gone. Hmm... as each level increases the speed of the invaders shots goes up. The invaders only speed up when there is less of them. You complete the level when you shoot all the invaders. The game ends when a) you run out of lives (you start with 3) or the invaders get to the bottom.

The LEDs turned out to be a pretty cool addition to the game. I wrote a class that blinks them and turns them on for a specified amount of time. They add a nice extra to the game. I use them on the intro screen and when the UFO is present.

The sound turned out to be really difficult for a few reasons. The biggest was that I had never written a sound engine before. The interrupt service routine working off the timer was the easier part. I also had a lot of trouble because there is no filter to filter out the PWM frequency to the speaker... so I had to run the PWM frequency way up there 30 kHz.

The graphics turned out to be a bit of a bear too. Thanks to Chris Taylor for his really great LCD API. I picked up a couple of frames per second from that. I had modified the DisplayN18 class for blitting a single line buffer to the LCD panel however his is a little faster for some reason? I used a different approach to doing the graphics (as I have very little experience with anything other than double buffered displays). I have a tile map and a list of sprites. Each tile/sprite is 1 bit 8x8. They could be bigger. I ran out of time. That much is not special. What is different from what I can tell is that I use a 1 line buffer that is 160 shorts long. The render function first adds the tile map data into the line buffer first. Then the sprites are added over the existing data. You can have a great deal of different sprites and maps going to the screen and just have to rewrite the LCD memory once per frame. After each line is composited, the line is then drawn to the LCD. Kind of like an Atari 2600. Each sprite/tile has a foreground and background color and can be different from the other tiles/sprites. There is one color reserved for Transparency.

There are 16 colors to choose from. I chose a palette based on the Macintosh OS 4.1 palette I found on WikiPedia. It is a very nice mix of colors.

I found a sprite editor called SpriteX ( https://code.google.com/p/spritesx-ed/ )... it works nicely except that the 16x16 sprites are in a weird format. Time limited me to 8x8 sprites. Oh well.

I used nokring to make the music. It makes RTTTL formatted ring tones which my sound api can play. Here is a useful site that has lots of arcade/video game ring tones with a link to nokring in the utilities page. http://arcadetones.emuunlim.com/files.htm

Other than all that stuff I used state machines to do most of the game logic. Please excuse the horrible coding as I tried to comment a lot of it however it is not very nice to look at. Lots of long functions...

Revision:
0:c79e1f29f029
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Display/display.cpp	Tue Mar 03 04:26:01 2015 +0000
@@ -0,0 +1,723 @@
+#include <stdarg.h>     // va_list
+#include "display.h"
+#include "font8x8.h"    // default 8x8 bitmap font
+#include "colors.h"     // predefined colors
+
+#define COLOR(fore, back) ((fore << 4) | (back & 0x0F))
+
+display::display() : LCD_ST7735(P0_19, P0_20, P0_7, P0_21, P0_22, P1_15, P0_2, LCD_ST7735::RGB)
+{
+    // Fill out the default color palette - use the MSX/Coleco default 16 color palette
+    // Found them here:  http://theadamresource.com/articles/misc/wk970202.html
+    memset(m_color_pal, 0, COLOR_PAL_LEN * sizeof(uint16_t));
+    /*
+    m_color_pal[0] =    RGB2SHORT(0,    0,      0);         // BLACK - Transparent (support in display code)
+    m_color_pal[1] =    RGB2SHORT(0,    0,      0);         // BLACK - Opaque
+    m_color_pal[2] =    RGB2SHORT(71,   183,    59);        // Medium Green
+    m_color_pal[3] =    RGB2SHORT(124,  207,    111);       // Light Green
+    m_color_pal[4] =    RGB2SHORT(93,   18,     255);       // Dark Blue
+    m_color_pal[5] =    RGB2SHORT(128,  114,    255);       // Light Blue
+    m_color_pal[6] =    RGB2SHORT(182,  91,     71);        // Dark Red (maybe brown)
+    m_color_pal[7] =    RGB2SHORT(93,   200,    237);       // Cyan
+    m_color_pal[8] =    RGB2SHORT(215,  107,    72);        // Medium Red
+    m_color_pal[9] =    RGB2SHORT(251,  143,    108);       // Light Red
+    m_color_pal[10] =   RGB2SHORT(195,  205,    65);        // Dark Yellow
+    m_color_pal[11] =   RGB2SHORT(211,  218,    118);       // Light Yellow
+    m_color_pal[12] =   RGB2SHORT(62,   159,    47);        // Dark Green
+    m_color_pal[13] =   RGB2SHORT(182,  100,    199);       // Magenta
+    m_color_pal[14] =   RGB2SHORT(204,  204,    204);       // Grey
+    m_color_pal[15] =   RGB2SHORT(255,  255,    255);       // White
+    */
+    /*
+    m_color_pal[0] =    RGB2SHORT(0,    0,      0);         // BLACK - Transparent (support in display code)
+    m_color_pal[1] =    RGB2SHORT(0,    0,      0);         // BLACK - Opaque
+    m_color_pal[2] =    RGB2SHORT(32,   192,    32);        // Medium Green
+    m_color_pal[3] =    RGB2SHORT(96,   224,    96);        // Light Green
+    m_color_pal[4] =    RGB2SHORT(32,   32,     224);       // Dark Blue
+    m_color_pal[5] =    RGB2SHORT(64,   96,     224);       // Light Blue
+    m_color_pal[6] =    RGB2SHORT(160,  32,     32);        // Dark Red (maybe brown)
+    m_color_pal[7] =    RGB2SHORT(64,   192,    224);       // Cyan
+    m_color_pal[8] =    RGB2SHORT(224,  32,     32);        // Medium Red
+    m_color_pal[9] =    RGB2SHORT(224,  96,     96);        // Light Red
+    m_color_pal[10] =   RGB2SHORT(192,  192,    32);        // Dark Yellow
+    m_color_pal[11] =   RGB2SHORT(192,  192,    128);       // Light Yellow
+    m_color_pal[12] =   RGB2SHORT(32,   128,    32);        // Dark Green
+    m_color_pal[13] =   RGB2SHORT(192,  64,     160);       // Magenta
+    m_color_pal[14] =   RGB2SHORT(160,  160,    160);       // Grey
+    m_color_pal[15] =   RGB2SHORT(255,  255,    255);       // White
+    */
+    
+    // Mac OS4.1 16 Color Palette (modified for transparent)
+    // Found here: http://upload.wikimedia.org/wikipedia/commons/5/57/Mac_16colors_palette.png
+    m_color_pal[0] =    RGB2SHORT(255,  255,    255);       // White
+    m_color_pal[1] =    RGB2SHORT(255,  255,    0);         // Yellow
+    m_color_pal[2] =    RGB2SHORT(255,  102,    0);         // Orange
+    m_color_pal[3] =    RGB2SHORT(255,  0,      0);         // Red
+    m_color_pal[4] =    RGB2SHORT(255,  0,      103);       // Magenta
+    m_color_pal[5] =    RGB2SHORT(51,   0,      153);       // Dark Blue
+    m_color_pal[6] =    RGB2SHORT(0,    0,      204);       // Blue
+    m_color_pal[7] =    RGB2SHORT(0,    153,    255);       // Light Blue
+    m_color_pal[8] =    RGB2SHORT(0,    255,    0);         // Green
+    m_color_pal[9] =    RGB2SHORT(0,    102,    0);         // Dark Green
+    m_color_pal[10] =   RGB2SHORT(102,  51,     0);         // Brown
+    m_color_pal[11] =   RGB2SHORT(153,  102,    51);        // Light Brown
+    m_color_pal[12] =   RGB2SHORT(187,  187,    187);       // Light Grey
+    m_color_pal[13] =   RGB2SHORT(136,  136,    136);       // Grey
+    m_color_pal[14] =   RGB2SHORT(0,    0,      0);       // White
+    m_color_pal[15] =   RGB2SHORT(0,    0,      0);         // Transparent (black)
+    
+    // Allocate our line buffer
+    m_line_buffer = new uint8_t[LINE_BUFFER_LEN];
+    
+    // Set our default colors
+    m_fore_color = PAL_WHITE;
+    m_back_color = PAL_TRANSPARENT;
+    
+    clear();
+    
+    // Initialize our sprite list
+    for (int i = 0; i < MAX_SPRITES; i++)
+        memset(&sprite_list[i], 0, sizeof(SPRITE));
+        
+    // Clear our user character buffer
+    memset(user_char, 0, MAX_USER_CHAR_BUF_LEN);
+}
+
+display::~display()
+{
+}
+
+void display::clear()
+{
+    // Clear our character map buffer
+    memset(m_char_map, 0, CHAR_MAP_LEN);
+    // Clear our color map buffer
+    memset(m_color_map, COLOR(m_fore_color, m_back_color), CHAR_MAP_LEN);
+    
+    // Set the cursor position back to 0
+    m_cursor_x = 0;
+    m_cursor_y = 0;
+}
+
+void display::bounds_check_and_scroll()
+{
+    // Check to see if our Y cursor location is beyond our map height
+    if (m_cursor_y >= CHAR_MAP_HEIGHT)
+    {
+        // Check to see if we are 
+        // Scroll everything up 1 line
+        memcpy(m_char_map, &m_char_map[CHAR_MAP_WIDTH], CHAR_MAP_LAST_LINE);
+        memcpy(m_color_map, &m_color_map[CHAR_MAP_WIDTH], CHAR_MAP_LAST_LINE);
+        // Clear the bottom line
+        memset(&m_char_map[CHAR_MAP_LAST_LINE], 0, CHAR_MAP_WIDTH);
+        memset(&m_color_map[CHAR_MAP_LAST_LINE], COLOR(m_fore_color, m_back_color), CHAR_MAP_WIDTH);
+        // Put the position of the cursor on the last line
+        m_cursor_y = CHAR_MAP_HEIGHT - 1;
+        m_cursor_x = 0;
+    }
+}
+
+void display::crlf()
+{
+    // Advance our cursor to the next line
+    m_cursor_x = 0;
+    m_cursor_y++;
+}
+
+void display::print(char *pString)
+{   
+    // Bounds check the cursor position (and do scrolling if we need to)
+    // before we put any characters in the map
+    bounds_check_and_scroll();
+    
+    // Calculate the current linear cursor position
+    int pos = get_linear_cursor_pos();
+    
+    // Loop through the string and put it into the character map
+    while (*pString)
+    {
+        char character = *pString;
+        
+        /*
+        // Handle a carriage return
+        if (character == 10)
+        {
+            m_cursor_x = 0;
+            pos = get_linear_cursor_pos();
+        }
+        else
+        // Handle a line feed
+        if (character == 13)
+        {
+            m_cursor_y++;
+            bounds_check_and_scroll();
+            pos = get_linear_cursor_pos();
+        }
+        else
+        */
+        {
+            if ((character < 32) || (character > 126))
+            {
+                // Some other special character
+                character = 95; // substitue a solid block if there is a special character
+            }
+            else
+            {
+                // Just subtract out the special character offset
+                character -= 32;
+            }
+            
+            // Place the character into the map
+            m_char_map[pos] = character;
+            // Copy the forecolor into the map
+            m_color_map[pos] = COLOR(m_fore_color, m_back_color); 
+            // Next linear position
+            pos++;
+            // Advance the cursor position
+            m_cursor_x++;
+            if (m_cursor_x >= CHAR_MAP_WIDTH)
+            {
+                if (m_word_wrap)
+                {
+                    // Next line
+                    crlf();
+                    pos = get_linear_cursor_pos();
+                }
+                else
+                {
+                    // Stop putting the characters in the map
+                    // we have hit the end of the line
+                    break;
+                }
+                    
+            }
+        }
+        // Next character
+        pString++;
+    }
+}
+
+void display::print(int iNumber)
+{
+    char sTemp[CHAR_MAP_WIDTH];
+    snprintf(sTemp, CHAR_MAP_WIDTH, "%d", iNumber);
+    print(sTemp);
+}
+
+void display::print(float fNumber)
+{
+    char sTemp[CHAR_MAP_WIDTH];
+    snprintf(sTemp, CHAR_MAP_WIDTH, "%0.2f", fNumber);
+    print(sTemp);
+}
+
+void display::print(const char *format, ...)
+{
+    char sTemp[CHAR_MAP_WIDTH];
+    
+    va_list list;
+    va_start(list, format);
+
+    // Print something in the file
+    vsnprintf(sTemp, CHAR_MAP_WIDTH, format, list);
+    
+    print(sTemp);
+}
+
+void display::println(char *pString)
+{
+    print(pString);
+    crlf();
+    
+}
+
+void display::println(int iNumber)
+{
+    print(iNumber);
+    crlf();
+}
+
+void display::println(float fNumber)
+{
+    print(fNumber);
+    crlf();
+}
+
+void display::println(const char *format, ...)
+{
+    char sTemp[CHAR_MAP_WIDTH];
+    
+    va_list list;
+    va_start(list, format);
+
+    // Print something in the file
+    vsnprintf(sTemp, CHAR_MAP_WIDTH, format, list);
+    
+    println(sTemp);
+}
+
+void display::printat(uint8_t x, uint8_t y, char *pString)
+{
+    // Bounds check the coordinates
+    if (x >= CHAR_MAP_WIDTH) return;
+    if (y >= CHAR_MAP_HEIGHT) return;
+    
+    // Get the starting linear position
+    int pos = get_linear_pos(x, y);
+    
+    // Loop through the string and put it into the character map
+    while (*pString)
+    {
+        char character = *pString;
+        
+        // Ignore CR and LF
+        if ((character == 10) || (character == 13))
+        {
+            pString++;
+            continue;
+        }
+        
+        if ((character < 32) || (character > 126))
+        {
+            // Some other special character
+            character = 95; // substitue a solid block if there is a special character
+        }
+        else
+        {
+            // Just subtract out the special character offset
+            character -= 32;
+        }
+        
+        // Place the character into the map
+        m_char_map[pos] = character;
+        // Copy the forecolor into the map
+        m_color_map[pos] = COLOR(m_fore_color, m_back_color);
+        // Next linear position
+        pos++;
+        // Advance the cursor position
+        x++;
+        // Check for out of bounds in which case we just exit
+        if (x >= CHAR_MAP_WIDTH) return;
+        if (pos >= CHAR_MAP_LEN) return;
+        // Next character
+        pString++;
+    }
+}
+
+void display::printat(uint8_t x, uint8_t y, int iNumber)
+{
+    char sTemp[CHAR_MAP_WIDTH];
+    snprintf(sTemp, CHAR_MAP_WIDTH, "%d", iNumber);
+    printat(x, y, sTemp);
+}
+
+void display::printat(uint8_t x, uint8_t y, float fNumber)
+{
+    char sTemp[CHAR_MAP_WIDTH];
+    snprintf(sTemp, CHAR_MAP_WIDTH, "%0.2f", fNumber);
+    printat(x, y, sTemp);
+}
+
+void display::printat(uint8_t x, uint8_t y, const char *format, ...)
+{
+    char sTemp[CHAR_MAP_WIDTH];
+    
+    va_list list;
+    va_start(list, format);
+
+    // Print something in the file
+    vsnprintf(sTemp, CHAR_MAP_WIDTH, format, list);
+    
+    printat(x, y, sTemp);
+}
+
+void display::setcharat(uint8_t x, uint8_t y, uint8_t character, uint8_t fore_color, uint8_t back_color)
+{
+    // Bounds check the coordinates
+    if (x >= CHAR_MAP_WIDTH) return;
+    if (y >= CHAR_MAP_HEIGHT) return;
+    
+    // Get the starting linear position
+    int pos = get_linear_pos(x, y);
+    
+    // Place the character into the map
+    m_char_map[pos] = character;
+    
+    // Check the colors
+    if (fore_color >= 16) fore_color = m_fore_color;
+    if (back_color >= 16) back_color = m_back_color;
+    
+    // Copy the forecolor into the map
+    m_color_map[pos] = COLOR(fore_color, back_color);
+    
+}
+
+void display::blit_line(uint8_t line)
+{
+    if (line >= LCD_HEIGHT) return;
+    
+    // LCD_ST7735
+    clip(0, line, 159, 0);
+    write(0x2C, m_line_buffer, LINE_BUFFER_LEN);
+    
+    // DisplayN18
+    //setClippingArea(0, line, 159, 0);
+    //writeCommand(0x2C);
+    //writeData(m_line_buffer, LINE_BUFFER_LEN);
+}
+
+void display::render()
+{
+    uint16_t *line_buf = (uint16_t *)m_line_buffer;
+    uint8_t *char_buf = font8x8_basic;
+    register uint8_t data = 0;
+    
+    int cindex = 0;
+    int char_line = 0;
+    int line_x = 0;
+    
+    for (int line = 0; line < LCD_HEIGHT; line++)
+    {
+        line_x = 0;
+        cindex = (line >> 3) * CHAR_MAP_WIDTH;
+        for (int map_x = 0; map_x < CHAR_MAP_WIDTH; map_x++)
+        {
+            register uint8_t data_index = m_char_map[cindex];
+            if (data_index < 128)
+                data = char_buf[(data_index << 3) + char_line];
+            else
+                data = user_char[((data_index - 128) << 3) + char_line];
+            register uint16_t fore_color = m_color_pal[m_color_map[cindex] >> 4];
+            register uint16_t back_color = m_color_pal[(m_color_map[cindex] & 0x0F)];
+            line_buf[line_x++] = (data & 0x01) ? fore_color : back_color;
+            line_buf[line_x++] = (data & 0x02) ? fore_color : back_color;
+            line_buf[line_x++] = (data & 0x04) ? fore_color : back_color;
+            line_buf[line_x++] = (data & 0x08) ? fore_color : back_color;
+            line_buf[line_x++] = (data & 0x10) ? fore_color : back_color;
+            line_buf[line_x++] = (data & 0x20) ? fore_color : back_color;
+            line_buf[line_x++] = (data & 0x40) ? fore_color : back_color;
+            line_buf[line_x++] = (data & 0x80) ? fore_color : back_color;
+            // Next character in our map
+            cindex++;
+        }
+        
+        char_line++;
+        if (char_line >= CHAR_HEIGHT) char_line = 0;
+        
+        // Add in the sprites ... they are the TOP layer
+        for (int iSprite = 0; iSprite < MAX_SPRITES; iSprite++)
+        {
+            // Check to see if the sprite is enabled
+            if (!sprite_list[iSprite].enabled) continue;
+            
+            // Check to see if the sprite is within our current line
+            if (line < sprite_list[iSprite].y) continue;
+            if (line >= (sprite_list[iSprite].y + 8)) continue;
+            
+            // Get the row of the sprite we are blitting
+            register uint8_t row = line - sprite_list[iSprite].y;
+            // Get the x position
+            register uint8_t x = sprite_list[iSprite].x;
+            // Get the foreground color
+            register uint16_t fc = m_color_pal[sprite_list[iSprite].fore_color];
+            register uint16_t bc = m_color_pal[sprite_list[iSprite].back_color];
+            // Get the starting index for the sprite into the user character buffer
+            register uint8_t data = user_char[(sprite_list[iSprite].char_index << 3) + row];
+            
+            if (sprite_list[iSprite].back_color != PAL_TRANSPARENT)
+            {
+                line_buf[x] = (data & 0x01) ? fc : bc; x++;
+                line_buf[x] = (data & 0x02) ? fc : bc; x++;
+                line_buf[x] = (data & 0x04) ? fc : bc; x++;
+                line_buf[x] = (data & 0x08) ? fc : bc; x++;
+                line_buf[x] = (data & 0x10) ? fc : bc; x++;
+                line_buf[x] = (data & 0x20) ? fc : bc; x++;
+                line_buf[x] = (data & 0x40) ? fc : bc; x++;
+                line_buf[x] = (data & 0x80) ? fc : bc; x++;
+            }
+            else
+            {
+                if (data & 0x01) line_buf[x] = fc; x++;
+                if (data & 0x02) line_buf[x] = fc; x++;
+                if (data & 0x04) line_buf[x] = fc; x++;
+                if (data & 0x08) line_buf[x] = fc; x++;
+                if (data & 0x10) line_buf[x] = fc; x++;
+                if (data & 0x20) line_buf[x] = fc; x++;
+                if (data & 0x40) line_buf[x] = fc; x++;
+                if (data & 0x80) line_buf[x] = fc; x++;
+            }
+        }
+                
+        // Draw the line
+        blit_line(line);
+    }
+}
+
+void display::AddSprite(int iSpriteNum, int iChar, int x, int y, int fore_color, int back_color, bool midhandle)
+{
+    if (iSpriteNum < 0) return;
+    if (iSpriteNum >= MAX_SPRITES) return;
+    if (iChar >= MAX_USER_CHAR) return;
+    
+    sprite_list[iSpriteNum].char_index = iChar;
+    sprite_list[iSpriteNum].x = x;
+    sprite_list[iSpriteNum].y = y;
+    sprite_list[iSpriteNum].fore_color = fore_color;
+    sprite_list[iSpriteNum].back_color = back_color;
+    sprite_list[iSpriteNum].midhandle = midhandle;
+    
+    // CHeck to see if we treat the X and Y coordinates as the middle of the sprite...
+    if (midhandle)
+    {
+        // Redo the XY coordinates
+        SetSpritePos(iSpriteNum, x, y);
+    }
+    
+    // Set the enabled flag last just in case we get called from an interrupt or we are interrupted
+    sprite_list[iSpriteNum].enabled = true;
+}
+
+void display::EnableSprite(int iSpriteNum, bool bEnable)
+{
+    if (iSpriteNum < 0) return;
+    if (iSpriteNum >= MAX_SPRITES) return;
+    
+    sprite_list[iSpriteNum].enabled = bEnable;
+}
+
+void display::RemoveSprite(int iSpriteNum)
+{
+    EnableSprite(iSpriteNum, false);
+}
+
+void display::SetSpriteChar(int iSpriteNum, int iChar)
+{
+    if (iSpriteNum < 0) return;
+    if (iSpriteNum >= MAX_SPRITES) return;
+    if (iChar >= MAX_USER_CHAR) return;
+    
+    sprite_list[iSpriteNum].char_index = iChar;
+}
+
+void display::SetSpritePos(int iSpriteNum, int x, int y)
+{
+    if (iSpriteNum < 0) return;
+    if (iSpriteNum >= MAX_SPRITES) return;
+    
+    if (sprite_list[iSpriteNum].midhandle)
+    {
+        x -= 4;
+        y -= 4;
+    }
+    
+    sprite_list[iSpriteNum].x = x;
+    sprite_list[iSpriteNum].y = y;
+}
+
+void display::SetSpriteColor(int iSpriteNum, int fore_color, int back_color)
+{
+    if (iSpriteNum < 0) return;
+    if (iSpriteNum >= MAX_SPRITES) return;
+    
+    sprite_list[iSpriteNum].fore_color = fore_color;
+    sprite_list[iSpriteNum].back_color = back_color;
+}
+
+void display::SetMidHandle(int iSpriteNum, bool bEnable)
+{
+    if (iSpriteNum < 0) return;
+    if (iSpriteNum >= MAX_SPRITES) return;
+    
+    sprite_list[iSpriteNum].midhandle = bEnable;
+}
+
+void display::SetCustomChar(int iCharNum, uint8_t *pBuf, int iNum, bool isMSX)
+{
+    if (iCharNum >= MAX_USER_CHAR) return;
+    if ((iNum + iCharNum) > MAX_USER_CHAR) return;
+    
+    for (int i = 0; i < iNum; i++)
+    {
+        if (isMSX)
+            SetCustomChar8x8MSX(iCharNum + i, pBuf);
+        else
+            SetCustomChar8x8Norm(iCharNum + i, pBuf);
+        pBuf += 8;
+    }
+}
+
+void display::SetCustomChar8x8Norm(int iCharNum, uint8_t *pBuf)
+{
+    if (iCharNum >= MAX_USER_CHAR) return;
+    
+    memcpy(&user_char[iCharNum * 8], pBuf, 8);
+}
+
+void display::SetCustomChar8x8MSX(int iCharNum, uint8_t *pBuf)
+{
+    if (iCharNum >= MAX_USER_CHAR) return;
+    
+    // 8x8 MSX tiles have the bit order reversed
+    for (int i = 0; i < 8; i++)
+    {
+        uint8_t temp2 = 0;
+        uint8_t temp1 = pBuf[i];
+        for (int bit = 0; bit < 8; bit++)
+        {
+            temp2 = temp2 << 1;
+            temp2 |= (temp1 & 0x01);
+            temp1 = temp1 >> 1;
+        }
+        user_char[iCharNum * 8 + i] = temp2;
+    }
+}
+
+void display::set_map(uint8_t char_index, uint8_t x, uint8_t y, uint8_t fore_color, uint8_t back_color)
+{
+    if (x >= CHAR_MAP_WIDTH) return;
+    if (y >= CHAR_MAP_HEIGHT) return;
+    
+    int pos = get_linear_pos(x, y);
+    
+    m_char_map[pos] = char_index;
+    m_color_map[pos] = COLOR(fore_color, back_color);
+    
+}
+
+void display::set_map_info(uint16_t tile_info, uint8_t x, uint8_t y)
+{
+    if (x >= CHAR_MAP_WIDTH) return;
+    if (y >= CHAR_MAP_HEIGHT) return;
+    
+    int pos = get_linear_pos(x, y);
+    
+    m_char_map[pos] = (tile_info >> 8);
+    m_color_map[pos] = (tile_info & 0x00FF);
+}
+
+uint8_t display::get_map(uint8_t x, uint8_t y)
+{
+    return (get_map_info(x, y) >> 8);
+}
+
+uint8_t display::get_map_color(uint8_t x, uint8_t y)
+{
+    return (get_map_info(x, y) & 0x00FF);
+}
+
+uint16_t display::get_map_info(uint8_t x, uint8_t y)
+{
+    if (x >= CHAR_MAP_WIDTH) return 0;
+    if (y >= CHAR_MAP_HEIGHT) return 0;
+    
+    int pos = get_linear_pos(x, y);
+    
+    return ((m_char_map[pos] << 8) | m_color_map[pos]);
+}
+
+void display::shift_map(SHIFT_DIRECTION direction, int inc, bool clear_after_shift)
+{
+    switch (direction)
+    {
+    case (eShiftUp):
+        for (int i = 1; i < CHAR_MAP_HEIGHT; i++)
+        {
+            memcpy(&m_char_map[(i - 1) * CHAR_MAP_WIDTH], &m_char_map[i * CHAR_MAP_WIDTH], CHAR_MAP_WIDTH);
+            memcpy(&m_color_map[(i - 1) * CHAR_MAP_WIDTH], &m_color_map[i * CHAR_MAP_WIDTH], CHAR_MAP_WIDTH);
+        }
+        if (clear_after_shift)
+        {
+            // Clear the bottom row
+            memset(&m_char_map[CHAR_MAP_HEIGHT - 1], 0, CHAR_MAP_WIDTH); 
+             memset(&m_color_map[CHAR_MAP_HEIGHT - 1], COLOR(m_fore_color, m_back_color), CHAR_MAP_WIDTH);
+        }
+        break;
+    case (eShiftDown):
+        for (int i = (CHAR_MAP_HEIGHT - 1); i > 0; i--)
+        {
+            memcpy(&m_char_map[i * CHAR_MAP_WIDTH], &m_char_map[(i - 1) * CHAR_MAP_WIDTH], CHAR_MAP_WIDTH);
+            memcpy(&m_color_map[i * CHAR_MAP_WIDTH], &m_color_map[(i - 1) * CHAR_MAP_WIDTH], CHAR_MAP_WIDTH);
+        }
+        if (clear_after_shift)
+        {
+            // Clear the top row
+            memset(&m_char_map[0], 0, CHAR_MAP_WIDTH); 
+            memset(&m_color_map[0], COLOR(m_fore_color, m_back_color), CHAR_MAP_WIDTH);
+        }
+        break;
+    case (eShiftLeft):
+        for (int y = 0; y < CHAR_MAP_HEIGHT; y++)
+        {
+            for (int x = 1; x < CHAR_MAP_WIDTH; x++)
+            {
+                int pos = get_linear_pos(x, y);
+                m_char_map[pos - 1] = m_char_map[pos];
+                m_color_map[pos -1] = m_color_map[pos];
+            }
+        }
+        if (clear_after_shift)
+        {
+            // Clear the right column
+            for (int y = 0; y < CHAR_MAP_HEIGHT; y++)
+            {
+                m_char_map[get_linear_pos((CHAR_MAP_WIDTH - 1), y)] = 0;
+                m_color_map[get_linear_pos((CHAR_MAP_WIDTH - 1), y)] = COLOR(m_fore_color, m_back_color);
+            }
+        }
+        break;
+    case (eShiftRight):
+        for (int y = 0; y < CHAR_MAP_HEIGHT; y++)
+        {
+            for (int x = (CHAR_MAP_WIDTH - 1); x > 0; x--)
+            {
+                int pos = get_linear_pos(x, y);
+                m_char_map[pos] = m_char_map[pos - 1];
+                m_color_map[pos] = m_color_map[pos - 1];
+            }
+        }
+        if (clear_after_shift)
+        {
+            // Clear the left column
+            for (int y = 0; y < CHAR_MAP_HEIGHT; y++)
+            {
+                m_char_map[get_linear_pos(0, y)] = 0;
+                m_color_map[get_linear_pos(0, y)] = COLOR(m_fore_color, m_back_color);
+            }
+        }
+        break;
+    }
+}
+
+bool display::SpriteCollision(int iSpriteNum1, int iSpriteNum2)
+{
+    if (iSpriteNum1 < 0) return false;
+    if (iSpriteNum1 >= MAX_SPRITES) return false;
+    if (iSpriteNum2 < 0) return false;
+    if (iSpriteNum2 >= MAX_SPRITES) return false;
+    
+    SPRITE *pSprite1 = &sprite_list[iSpriteNum1];
+    SPRITE *pSprite2 = &sprite_list[iSpriteNum2];
+    
+    if (!pSprite1->enabled) return false;
+    if (!pSprite2->enabled) return false;
+    
+    // Bounding box collision - might be broken
+    /*
+    int dx = pSprite1->x - pSprite2->x;
+    if (dx < 0) dx *= -1;
+    int dy = pSprite1->y - pSprite2->y;
+    if (dy < 0) dy *= -1;
+
+    return ((dx < 16) && (dy < 16));
+    */
+    
+    // Circle based collision detection
+    int dx = pSprite1->x - pSprite2->x;
+    int dy = pSprite1->y - pSprite2->y;
+    int dist = (dx * dx) + (dy * dy);
+    int r = 3 + 3;  // make the radius 4 - 1
+    return (dist < (r * r));
+    
+    // TODO - perhaps after the circle detection maybe we can do pixel perfect detection...
+    // OR - do detection when we are rendering???
+}