interrupts

Instead of using GD.waitvblank to wait for the end of the frame, it can be more efficient to use interrupts to signal to the Arduino CPU that the frame has ended. The Gameduino can do this using pin 2, an extra pin not involved in the regular SPI traffic.

The scheme shown here is rather more general: it can trigger an interrupt on any specified raster line. The special case of a vertical blanking interrupt is a raster interrupt at line 300.

In this configuration, the Arduino sets pin 2 as an input, and uses attachInterrupt to trigger an interrupt every time pin 2 rises.

The Gameduino is configured to output on pin 2, and gives control of pin 2 to the coprocessor.

On the coprocessor, the rasterinterrupt microprogram generates a high value on pin 2 whenever the raster passes a specified line.

start-microcode rasterinterrupt

: 1+ d# 1 + ;
: @     dup c@ swap 1+ c@ swab or ;

\ COMM+0 holds the 16 bit raster line number:
\   0 is first line of screen
\ 299 is last visible line of screen
\ 300 is beginning of vertical blanking
\
\ This microprogram loop raises P2 when the raster is below line COMM+0,
\ so the Arduino can trigger an interrupt

: main
    d# 0 P2_DIR c!          \ Make P2 an output
                            \ Drive P2 high when raster is past line COMM+0
    begin
        COMM+0 @            \ user value
        YLINE c@            \ hardware line
        <                   \ true when hardware line is below user value
        P2_V c!             \ write bool to P2
    again
;

end-microcode

The Arduino sketch uses attachInterrupt to monitor pin 2 and call function service when pin 2 rises. service here just changes the background color, loads the next value for the interrupt line, and returns.

The result is a red bar between lines 150 and 170.

#include <SPI.h>
#include <GD.h>

#include "rasterinterrupt.h"

#define LINETIME_US 41.6    // time for one raster line in microseconds
#define delayLines(n) delayMicroseconds(int(n * LINETIME_US))

static int line;

#define BLACK 
#define RED   RGB(255, 0, 0)

void service()
{
  delayLines(0.5);   // wait half a line: puts us in middle of screen
  if (line == 150) {
    GD.wr16(BG_COLOR, RGB(255, 0, 0));  // turn red at line 150
    line = 170;
  } else {
    GD.wr16(BG_COLOR, RGB(0, 0, 0));    // turn black at line 170
    line = 150;
  }
  GD.wr16(COMM+0, line);    // Set next split line
}

void setup()
{
  int i;

  GD.begin();
  GD.ascii();
  GD.putstr(0, 0, "Raster interrupts");

  pinMode(2, INPUT);        // Arduino reads on pin 2
  GD.wr(IOMODE, 'J');       // pin 2 is under microprogram control
  line = 150;
  GD.wr16(COMM+0, line);    // Set first split line
                            // The raster interrupt microprogram
  GD.microcode(rasterinterrupt_code, sizeof(rasterinterrupt_code));
                            // call 'rising' every time pin 2 rises
  attachInterrupt(0, service, RISING);
}

void loop()
{
}