A Library for the Gameduino shield

Dependencies:   Arduino

Dependents:   Gameduino_Asteroids_game Gameduino_Ball_demo Gameduino_Bitmap_demo Gameduino_chessboard_demo ... more

This is a library for the Gameduino, a shield for the Arduino. It uses a secondary library to recreate the arduino functionality. This is to keep this library as close to the original as possible. The original was released by James Bowman, who designed the Gameduino. His webpage (http://excamera.com/sphinx/gameduino/) has tutorials and plenty of other information on the Gameduino. I have published a lot of the demos on his page and they can be found here:

Asteroids:

Import programGameduino_Asteroids_game

Asteroids game using a Gameduino

Ball demo:

Import programGameduino_Ball_demo

Bouncing ball demo for the Gameduino

Bitmap demo:

Import programGameduino_Bitmap_demo

Bitmap demo for the Gameduino

Chessboard demo:

Import programGameduino_chessboard_demo

Chessboard demo for the Gameduino

Collision demo:

Import programGameduino_collision_demo

Collision demo for the Gameduino

Frogger game:

Import programGameduino_Frogger_game

Frogger game for the Gameduino

jkcollision demo:

Import programGameduino_jkcollision_demo

jkcollision demo for the Gameduino

Joystick test:

Import programGameduino_Joystick_test

Joystick test program for the Gameduino

Manic miner game:

Import programGameduino_Manic_Miner_game

Manic miner game for the Gameduino

Palettes demo:

Import programGameduino_Palettes_demo

Palettes demo for the Gameduino

Rotate demo:

Import programGameduino_Rotate_demo

Sprite rotation demo for the Gameduino

Screenshot demo:

Import programGameduino_screenshot_test

Screen shot demo and test program for the Gameduino

This needs to run a python script, more information and the script can be found on the gameduino site.

Scroll demo:

Import programGameduino_Scroll_demo

Scroll demo for the Gameduino

snow demo:

Import programGameduino_Snow_demo

White noise demo for the Gameduino

spinning dna demo:

Import programGameduino_Spinning_DNA_demo

Spinning dna graphics demo for the Gameduino

split screen demo:

Import programGameduino_Split_Screen_demo

Split screen graphics demo for the Gameduino

256 sprites demo:

Import programGameduino_Sprites256_demo

Sprite control demo for the Gameduino

I've also ported Invaders, written for the arduino by Colin (couldn't find his surname) and can be found at http://www.artlum.com/gameduino/gameduino.html:

Import programGameduino_Invaders_game

Invaders game for the Gameduino

and made a template for gameduino based projects. I have used an mbeduino board and have included a a file shield.h which contains the pinouts for this board. This can easily be modified to your needs. As a default I have chosen to use pull ups on the joystick inputs and switch down to 0V. This can be quickly altered and this is at the top of most main.c files, with the exception of invaders. This has more configuration options in the joystick files.

GD.cpp

Committer:
TheChrisyd
Date:
2012-12-20
Revision:
4:84c1ca3e1be0
Parent:
0:c9523256df08

File content as of revision 4:84c1ca3e1be0:

/**
 * @section LICENSE
 *  Copyright (c) 2012 James Bowman, Chris Dick
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @section DESCRIPTION
 * Copyright (c) 2011 by James Bowman <jamesb@excamera.com>
 * Gameduino library for mbed. Ported from Arduino by Chris Dick
 *
 * 
 *
 *
 */

#include "GD.h"

GDClass::GDClass(PinName mosi, PinName miso, PinName sclk, PinName cs, PinName utx, PinName urx)
    : gameduino_spi(mosi, miso, sclk)
    , gameduino_cs(cs) 
    , pc(utx, urx) {
    
    }

void GDClass::begin()
{
  delay(250); // give Gameduino time to boot
  gameduino_spi.format(8,0);
  gameduino_spi.frequency(8000000);

  
  gameduino_cs = 1;

  wr(J1_RESET, 1);           // HALT coprocessor
  __wstart(RAM_SPR);            // Hide all sprites
  for (int i = 0; i < 512; i++)
  {
    xhide();
  }
  __end();
  fill(RAM_PIC, 0, 1024 * 10);  // Zero all character RAM
  fill(RAM_SPRPAL, 0, 2048);    // Sprite palletes black
  fill(RAM_SPRIMG, 0, 64 * 256);   // Clear all sprite data
  fill(VOICES, 0, 256);         // Silence
  fill(PALETTE16A, 0, 128);     // Black 16-, 4-palletes and COMM

  wr16(SCROLL_X, 0);
  wr16(SCROLL_Y, 0);
  wr(JK_MODE, 0);
  wr(SPR_DISABLE, 0);
  wr(SPR_PAGE, 0);
  wr(IOMODE, 0);
  wr16(BG_COLOR, 0);
  wr16(SAMPLE_L, 0);
  wr16(SAMPLE_R, 0);
  wr16(SCREENSHOT_Y, 0);
  wr(MODULATOR, 64);
}

void GDClass::end() {
}

void GDClass::__start(unsigned int addr) // start an spi transaction to addr
{
  gameduino_cs = 0;
  gameduino_spi.write(highByte(addr));
  gameduino_spi.write(lowByte(addr));  
}

void GDClass::__wstart(unsigned int addr) // start an spi write transaction to addr
{
  __start(0x8000|addr);
}

void GDClass::__wstartspr(unsigned int sprnum)
{
  __start((0x8000 | RAM_SPR) + (sprnum << 2));
  spr = 0;
}

void GDClass::__end() // end the spi transaction
{
  gameduino_cs = 1;
}

byte GDClass::rd(unsigned int addr)
{
  __start(addr);
  byte r = gameduino_spi.write(0);
  __end();
  return r;
}

void GDClass::wr(unsigned int addr, byte v)
{
  __wstart(addr);
  gameduino_spi.write(v);
  __end();
}

unsigned int GDClass::rd16(unsigned int addr)
{
  unsigned int r;

  __start(addr);
  r = gameduino_spi.write(0);
  r |= (gameduino_spi.write(0) << 8);
  __end();
  return r;
}

void GDClass::wr16(unsigned int addr, unsigned int v)
{
  __wstart(addr);
  gameduino_spi.write(lowByte(v));
  gameduino_spi.write(highByte(v));
  __end();
}

void GDClass::fill(int addr, byte v, unsigned int count)
{
  __wstart(addr);
  while (count--)
    gameduino_spi.write(v);
  __end();
}

void GDClass::copy(unsigned int addr, PROGMEM prog_uchar *src, int count)
{
  __wstart(addr);
  while (count--) {
    gameduino_spi.write(pgm_read_byte_near(src));
    src++;
  }
  __end();
}



void GDClass::microcode(PROGMEM prog_uchar *src, int count)
{
  wr(J1_RESET, 1);
  copy(J1_CODE, src, count);
  wr(J1_RESET, 0);
}



void GDClass::setpal(int pal, unsigned int rgb)
{
  wr16(RAM_PAL + (pal << 1), rgb);
}

void GDClass::sprite(int spr, int x, int y, byte image, byte palette, byte rot, byte jk)
{
  __wstart(RAM_SPR + (spr << 2));
  gameduino_spi.write(lowByte(x));
  gameduino_spi.write((palette << 4) | (rot << 1) | (highByte(x) & 1));
  gameduino_spi.write(lowByte(y));
  gameduino_spi.write((jk << 7) | (image << 1) | (highByte(y) & 1));
  __end();
}

void GDClass::xsprite(int ox, int oy, signed char x, signed char y, byte image, byte palette, byte rot, byte jk)
{
  if (rot & 2)
    x = -16-x;
  if (rot & 4)
    y = -16-y;
  if (rot & 1) {
      int s;
      s = x; x = y; y = s;
  }
  ox += x;
  oy += y;
  gameduino_spi.write(lowByte(ox));
  gameduino_spi.write((palette << 4) | (rot << 1) | (highByte(ox) & 1));
  gameduino_spi.write(lowByte(oy));
  gameduino_spi.write((jk << 7) | (image << 1) | (highByte(oy) & 1));
  spr++;
}

void GDClass::xhide()
{
  gameduino_spi.write(lowByte(400));
  gameduino_spi.write(highByte(400));
  gameduino_spi.write(lowByte(400));
  gameduino_spi.write(highByte(400));
  spr++;
}

void GDClass::plots(int ox, int oy, PROGMEM sprplot *psp, byte count, byte rot, byte jk)
{
  while (count--) {
    struct sprplot sp;
    sp = *psp++;
    xsprite(ox, oy, sp.x, sp.y, sp.image, sp.palette, rot, jk);
  }
}

void GDClass::sprite2x2(int spr, int x, int y, byte image, byte palette, byte rot, byte jk)
{
  __wstart(0x3000 + (spr << 2));
  xsprite(x, y, -16, -16, image + 0, palette, rot, jk);
  xsprite(x, y,   0, -16, image + 1, palette, rot, jk);
  xsprite(x, y, -16,   0, image + 2, palette, rot, jk);
  xsprite(x, y,   0,   0, image + 3, palette, rot, jk);
  __end();
}

void GDClass::waitvblank()
{
  // Wait for the VLANK to go from 0 to 1: this is the start
  // of the vertical blanking interval.

  while (rd(VBLANK) == 1)
    ;
  while (rd(VBLANK) == 0)
    ;
}

/* Fixed ascii font, useful for debug */

#include "font8x8.h"
static byte stretch[16] = {
  0x00, 0x03, 0x0c, 0x0f,
  0x30, 0x33, 0x3c, 0x3f,
  0xc0, 0xc3, 0xcc, 0xcf,
  0xf0, 0xf3, 0xfc, 0xff
};


void GDClass::ascii()
{
  long i;
  for (i = 0; i < 768; i++) {
    byte b = font8x8[i];
    byte h = stretch[b >> 4];
    byte l = stretch[b & 0xf];
    wr(0x1000 + (16 * ' ') + (2 * i), h);
    wr(0x1000 + (16 * ' ') + (2 * i) + 1, l);
  }
  for (i = 0x20; i < 0x80; i++) {
    setpal(4 * i + 0, TRANSPARENT);
    setpal(4 * i + 3, RGB(255,255,255));
  }
  fill(RAM_PIC, ' ', 4096);
}

void GDClass::putstr(int x, int y, const char *s)
{
  __wstart((y << 6) + x);
  while (*s)
    gameduino_spi.write(*s++);
  __end();
}

void GDClass::voice(int v, byte wave, unsigned int freq, byte lamp, byte ramp)
{
  __wstart(VOICES + (v << 2));
  gameduino_spi.write(lowByte(freq));
  gameduino_spi.write(highByte(freq) | (wave << 7));
  gameduino_spi.write(lamp);
  gameduino_spi.write(ramp);
  __end();
}

void GDClass::screenshot(unsigned int frame)
{
  int yy, xx;
  byte undone[38];  // 300-long bitmap of lines pending

  // initialize to 300 ones
  memset(undone, 0xff, 37);
  undone[37] = 0xf;
  int nundone = 300;

  pc.putc(0xa5);   // sync byte
  pc.putc(lowByte(frame));
  pc.putc(highByte(frame));

  while (nundone) {
    // find a pending line a short distance ahead of the raster
    int hwline = rd16(SCREENSHOT_Y) & 0x1ff;
    for (yy = (hwline + 7) % 300; ((undone[yy>>3] >> (yy&7)) & 1) == 0; yy = (yy + 1) % 300)
      ;
    wr16(SCREENSHOT_Y, 0x8000 | yy);   // ask for it

    // housekeeping while waiting: mark line done and send yy
    undone[yy>>3] ^= (1 << (yy&7));
    nundone--;
    pc.putc(lowByte(yy));
    pc.putc(highByte(yy));
    while ((rd(SCREENSHOT_Y + 1) & 0x80) == 0)
      ;

    // Now send the line, compressing zero pixels
    uint16_t zeroes = 0;
    for (xx = 0; xx < 800; xx += 2) {
      uint16_t v = rd16(SCREENSHOT + xx);
      if (v == 0) {
        zeroes++;
      } else {
        if (zeroes) {
          pc.putc(lowByte(zeroes));
          pc.putc(0x80 | highByte(zeroes));
          zeroes = 0;
        }
        pc.putc(lowByte(v));
        pc.putc(highByte(v));
      }
    }
    if (zeroes) {
      pc.putc(lowByte(zeroes));
      pc.putc(0x80 | highByte(zeroes));
    }
  }
  wr16(SCREENSHOT_Y, 0);   // restore screen to normal
}

class GDflashbits {
public:
  void begin(PROGMEM prog_uchar *s) {
    src = s;
    mask = 0x01;
  }
  byte get1(void) {
    byte r = (pgm_read_byte_near(src) & mask) != 0;
    mask <<= 1;
    if (!mask) {
      mask = 1;
      src++;
    }
    return r;
  }
  unsigned short getn(byte n) {
    unsigned short r = 0;
    while (n--) {
      r <<= 1;
      r |= get1();
    }
    return r;
  }
private:
  PROGMEM prog_uchar *src;
  byte mask;
};

static GDflashbits GDFB;

void GDClass::uncompress(unsigned int addr, PROGMEM prog_uchar *src)
{
  GDFB.begin(src);
  byte b_off = GDFB.getn(4);
  byte b_len = GDFB.getn(4);
  byte minlen = GDFB.getn(2);
  unsigned short items = GDFB.getn(16);
  while (items--) {
    if (GDFB.get1() == 0) {
      wr(addr++, GDFB.getn(8));
    } else {
      int offset = -GDFB.getn(b_off) - 1;
      int l = GDFB.getn(b_len) + minlen;
      while (l--) {
        wr(addr, rd(addr + offset));
        addr++;
      }
    }
  }
}