From: johnk73@ix.netcom.com (John Kalstrom )
Subject: TECH: PowerGlove test program
Date: 31 Dec 1995 11:33:21 GMT
Message-ID: <4c5se1$a2@ixnews6.ix.netcom.com>
Organization: Netcom


From: johnk73@ix.netcom.com (John Kalstrom )

OK, maybe this time my news server won't hang!

Recently I bought a Mattel PowerGlove modified for the PC parallel
port.  After studying O2GLOVE.ZIP source, I wanted to test that I
understood how to talk to the thing.  (Ultimately, I want to write a
TSR.)

Here's a short & simple program I wrote.  You should be able to
understand the hardware/protocol in just a few minutes of study.

----------------------------------------------------------------------
/*
Mattel Power Glove test, John Kalstrom, Copyright Dec 95
Use it any way you want, except don't upload to CompuServe
Compiled successfully with Turbo C++ 1.0

Algorithm extracted from O2GLOVE.ZIP, Mark Pflaging 9/92
(Which says it owes: Mitch Allen, Dave Stampe, Greg Alt, Manfredo,
 Dream Park Research)  It's great to share!

Pins:                  NES     LPT
  ground (to glove)     1       18
  clock (to glove)      2       2
  latch (to glove)      3       3
  data  (from glove)    4       13
  +5 (to glove)         7       (from keyboard connector)
  NES pin 1 is small end of connector
    1234
     567
  LPT pin connections as per Byte Jul 90, p288

Receive byte from glove (MSB first):
  Pulse latch to 1 (normally 0) for >85 us
  Read bit
  Pulse clock to 0 (normally 1) for >15 us
  ...

Send byte to glove (MSB first):
  Set latch to bit value, pulse clock
  ...

Reset glove to hires mode (timing is loose):
  Pulse latch
  Pulse clock 4 times (dummy read)
  Wait 7.2 ms
  Raise latch
  Wait 2.3 ms
  Send: 06 C1 08 00 02 FF 00
  Wait 30 ms
  Read bytes until A0

Read glove status (timing is tighter):
  A0, X, Y, Z, rotation, fingers, keys, junk byte, junk byte

  X/Y/Z: -125 to 125
  rotation: 0 to 11 (0 up, increments clockwise)
  thumb & 3 fingers:
    0 = unflexed, 3 = full flex
  keys:
    0-B = 0 - 9, A, B buttons
    C-F = left, up, down, right keypad
    80, 82, 83, 84 = enter, start, select, prog buttons
*/

#include <stdio.h>    // printf
#include <dos.h>      // inportb
#include <conio.h>    // getch
#include <time.h>     // time

/* Utility routines ripped from O2GLOVE */
#define GDATA      0x10    // PG data in
#define GLATCH     0x02    // PG latch out
#define GCLOCK     0x01    // PG clock out
#define GCLOLAT    0x03    // clock + latch
#define OutPort    0x378   // ---------------  for LPT1; use 278 & 279 for LPT2
#define InPort     0x379
unsigned char BITREAD() { return ( inportb( InPort ) & GDATA ) >> 4 ; }
void C0L0() { outportb( OutPort, 0 ); }        // clock 0 latch 0.
void C0L1() { outportb( OutPort, GLATCH ); }   // clock 0 latch 1.
void C1L0() { outportb( OutPort, GCLOCK ); }   // clock 1 latch 0.
void C1L1() { outportb( OutPort, GCLOLAT ); }  // clock 1 latch 1.

/* Timing stuff */
static float u15, u100, u1000;
unsigned long tmr;
#define TIMER(count) { for (tmr = count; tmr; --tmr) ; }

void send(unsigned char byte)
{
  int idx;

  for (idx = 0; idx < 8; ++idx)
  {
    if (byte & 0x80)
    {
      C1L1(); C0L1(); C1L1();
    } else
    {
      C1L0(); C0L0(); C1L0();
    }
    byte <<= 1;
    TIMER(u15);
  }
  TIMER(u100);
}

unsigned char rx()
{
  int idx;
  unsigned char value = 0;

  C1L0();
  C1L1();
  TIMER(u15);
  C1L0();
  /* I guess this is shifted out in hardware (nanoseconds) -- note that
   * no delay seems to be needed for the clock pulse
   */
  for (idx = 0; idx < 8; ++idx)
  {
    value <<= 1;
    value |= BITREAD();
    C0L0();
    C1L0();
  }
  TIMER(u100);
  return value;
}

int main()
{
  int idx;
  time_t then;
  float count_per_usec;

  /* Adjust for speed of computer */
/* #define PER_USEC 5.714 */
#ifdef PER_USEC
  count_per_usec = PER_USEC;
#else
  /* get number of seconds for 1e9 counts */
  printf("Adjusting for computer speed; takes 20 sec on 486-100\n");
  /* Wait until second rolls over, else error could be up to 2 sec */
  time(&then);
  while (time(0) == then) ;
  TIMER(1e8);
  count_per_usec = 1e8 / (difftime(time(0), then) - 1) / 1e6;
  printf("Recompile with -DPER_USEC=%.3f for faster run\n", count_per_usec);
  puts("Press RETURN"); getchar();
#endif
  u15 = count_per_usec * 15;
  u100 = count_per_usec * 100;
  u1000 = count_per_usec * 1000;

  /* Wake glove up / resync */
  for (idx = 0; idx < 30; ++idx)
  {
    if (rx() == 0xA0)
      break;
    TIMER(u1000 * 3);
  }

  /* Set to hires mode */
  C1L0();
  C1L1();
  C1L0();

  for (idx = 0; idx < 4; ++idx)
  {
    C0L0();
    C1L0();
  }
  C1L0();
  TIMER(u1000 * 7.2);
  C1L1();
  TIMER(u1000 * 2.3);

  send(0x06); send(0xC1); send(0x08); send(0x00); send(0x02);
  send(0xFF); send(0x00);

  TIMER(u1000);
  C1L0();

  TIMER(u1000 * 30);

  /* Read data */
  while (!kbhit())
  {
    unsigned char x, y, z, rot, finger, key,
                  pt1, pt2;  /* packet type */

    /* Wait for beginning of packet */
    while (rx() != 0xA0 && !kbhit())
      TIMER(u1000 * 3);

    x = rx(); y = rx(); z = rx(); rot = rx(); finger = rx(); key = rx();
    pt1 = rx(); pt2 = rx();

    /* There are some kind of packets besides data... (which is 0 0) */
    if (pt1 || pt2)
    {
      printf("Packet type %d %d\n", pt1, pt2);
      continue;
    }

    printf(
"x = %4d  y = %4d  z = %4d  rot = %2d  th=%d 1=%d 2=%d 3=%d  key = %02x\n",
           (char) x, (char) y, (char) z, rot,
           finger >> 6, (finger >> 4) & 0x03, (finger >> 2) & 0x03,
           finger & 0x03,
           key);
  }
  return 0;
}

