/*
 * Copyright 1990 by Thomas Roell, Dinkelscherben, Germany.
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Thomas Roell not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  Thomas Roell makes no representations
 * about the suitability of this software for any purpose.  It is provided
 * "as is" without express or implied warranty.
 *
 * THOMAS ROELL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL THOMAS ROELL BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 *
 * Author:  Thomas Roell, roell@lan.informatik.tu-muenchen.de
 *
 * $Header: /proj/X11/mit/server/ddx/at386/RCS/mouse.c,v 1.6 90/11/08 17:49:14 root Exp $
 */


#include "X386.h"
#include "inputstr.h"
#include "scrnintstr.h"
#include "nintendo.h"

/*
 * Private structure for a MouseSystems Mouse
 */
typedef struct {
  X386MouseRec  info;
  int           state;
  int           buttons;
  int           dx,dy;
} MouseSysRec, * MouseSysPtr;
 
/*
 * Private structure for a Microsoft Mouse
 */
typedef struct {
  X386MouseRec  info;
  int           state;
  int           emulate;
  int           buttons;
  int           dx,dy;
} MicrosoftRec, *MicrosoftPtr;
 
/*
 * Private structure for a Bus Mouse
 */
typedef struct {
  X386MouseRec  info;
  int           state;
  int           buttons;
  int           dx,dy;
} BusMouseRec, *BusMousePtr;

/*
 * Private structure for a Nintendo Joystick fake mouse
 * Supports configured mappings NES switches to mouse buttons,
 * and non-linear sped up joystick inputs.
 */
typedef struct {
  X386MouseRec  info;
  int		switches;		    /* last NES switches */
  int		moves;			    /* last NES move bits */
  int           buttons;		    /* X buttons */
  int           dx,dy;			    /* accumulated movement */
  int		add, dda, shift;	    /* nonlinear motion inputs */
  int		A, B, Start, Select, AB;    /* what makes left-middle-right ? */
} NesMouseRec, *NesMousePtr;

/*
 * lets create a simple finite-state machine
 */

static char state[24][3] = {
  {-1,-1,0},  {-1,-1,4},  {-1,-1,8},  {2, -1,12}, /* nothing pressed */
  { 1, 0,0},  { 1,-1,16}, { 1, 0,8},  {2, -1,12}, /* delayed right */
  { 4, 0,0},  { 4, 0,4},  { 4,-1,20}, {2, -1,12}, /* delayed left */
  { 0,-1,0},  { 0,-1,0},  { 0,-1,0},  {-1,-1,12}, /* pressed middle */
  { 0,-1,0},  {-1,-1,16}, { 0,-1,8},  { 0, 2,12}, /* pressed right */
  { 0,-1,0},  { 0,-1,4},  {-1,-1,20}, { 0, 2,12}, /* pressed left */
};

  
/*
 *-----------------------------------------------------------------------
 * MouseCtrl --
 *      Alter the control parameters for the mouse.
 *
 * Results:
 *      None.
 *
 * Side Effects:
 *      None.
 *
 *-----------------------------------------------------------------------
 */
static void
MouseCtrl(pMouse, pCtrl)
     DevicePtr pMouse;
     PtrCtrl   *pCtrl;
{
  X386MousePtr pPriv = (X386MousePtr)pMouse->devicePrivate;

  TRACE( ("MouseCtrl(pMouse=0x%x, pCtrl=0x%x)\n", pMouse, pCtrl) );

  /*
   * check for reasonable values 
   */
  if ((pCtrl->num > 0) && (pCtrl->den > 0)) {
    pPriv->num = pCtrl->num;
    pPriv->den = pCtrl->den;
  } else {
    pPriv->num = 1;
    pPriv->den = 1;
  }

  if (pCtrl->threshold > 0)
    pPriv->threshold = pCtrl->threshold;
  else
    pPriv->threshold = 1;
}

/*
 *-----------------------------------------------------------------------
 * GetMotionEvents --
 *      Return the (number of) motion events in the "motion history
 *      buffer" (snicker) between the given times.
 *
 * Results:
 *      The number of events stuffed.
 *
 * Side Effects:
 *      None.
 *
 *-----------------------------------------------------------------------
 */
static int
GetMotionEvents (buff, start, stop, pScreen)
     CARD32 start, stop;
     xTimecoord *buff;
     ScreenPtr pScreen;
{
  TRACE( ("GetMotionEvents(buff=0x%x, start=%d, stop=%d, pScreen=0x%x)\n",
	  buff, start, stop, pScreen) );

  return 0;
}

/*
 *-----------------------------------------------------------------------
 * MouseSysProc --
 *      Handle the initialization, etc. of a mouse
 *
 * Results:
 *      none.
 *
 * Side Effects:
 *      none.
 *
 *-----------------------------------------------------------------------
 */
static int
MouseSysProc(pMouse, what)
     DevicePtr	pMouse;
     int        what;
{
  unchar        map[4];
  struct        termio tty;
  Atom          type;
  X386DevicePtr pPriv = (X386DevicePtr)pMouse->devicePrivate;
  
  TRACE( ("MouseSysProc(pMouse=0x%x, what=%d)\n", pMouse, what) );

  switch (what)
    {
    case DEVICE_INIT: 
      if ((pPriv->Fd = open(pPriv->Name,O_RDONLY | O_NDELAY)) < 0) {
	ErrorF ("Cannot open MouseSystemsMouse\n");
	return (!Success); }
      ioctl(pPriv->Fd,TCGETA,&tty);  
      tty.c_iflag = IGNBRK | IGNPAR ;     
      tty.c_oflag = 0;           
      tty.c_cflag = B1200 | CS8 | CSTOPB | CREAD | CLOCAL;
      tty.c_lflag = 0;
      tty.c_cc[VTIME]=0; 
      tty.c_cc[VMIN]=1;
      ioctl(pPriv->Fd,TCSETA,&tty);

      ((MouseSysPtr)pPriv)->state = 0;
      ((X386MousePtr)pPriv)->buttons = 0;
      ((X386MousePtr)pPriv)->x = 0;
      ((X386MousePtr)pPriv)->y = 0;
      pMouse->on = FALSE;

      map[1] = 3;
      map[2] = 2;
      map[3] = 1;
      InitPointerDeviceStruct(pMouse, map, 3, GetMotionEvents, MouseCtrl, 0);

#ifdef XINPUT
      type = MakeAtom(pPriv->type, strlen(pPriv->type), FALSE);
      AssignTypeAndName(pMouse, type, "Mouse Systems Mouse");
#endif      

      break;
	
      case DEVICE_ON:
	AddEnabledDevice(pPriv->Fd); 
	pMouse->on = TRUE;
	break;
	
      case DEVICE_CLOSE:
      case DEVICE_OFF:
	RemoveEnabledDevice(pPriv->Fd); 
	pMouse->on = FALSE;
	break;
    }
  return Success;
}

/*
 *-----------------------------------------------------------------------
 * MouseSysProcessEvents --
 *      Return the number of processed events.
 *
 * Results:
 *      Number of events processed.
 *
 * Side Effects:
 *      Process Events.
 *-----------------------------------------------------------------------
 */
static void
MouseSysProcessEvents(pMouse)
     DevicePtr    pMouse;
{
  unchar          rBuf[64];
  int             i,nBytes;
  MouseSysPtr pPriv = (MouseSysPtr)pMouse->devicePrivate;

  nBytes = read(((X386DevicePtr)pPriv)->Fd, rBuf, sizeof(rBuf));

  for ( i=0; i < nBytes; i++) {
    switch(pPriv->state) {
    case 0:
      if ((rBuf[i] & 0xF8) == 0x80) {
	pPriv->buttons = (~rBuf[i]) & 0x07;
	(pPriv->state)++; }
      break;
      
    case 1:
      pPriv->dx = (signed char)rBuf[i];
      (pPriv->state)++;
      break;
      
    case 2:
      pPriv->dy = (signed char)rBuf[i];
      (pPriv->state)++;
      break;
      
    case 3:
      (pPriv->dx) += (signed char)rBuf[i];
      (pPriv->state)++;
      break;
      
    case 4:
      (pPriv->dy) += (signed char)rBuf[i];
      pPriv->state = 0;
      X386MouseEvent(pMouse, pPriv->buttons, pPriv->dx, -pPriv->dy);
      break;
    }
  }
}


/*
 *-----------------------------------------------------------------------
 * MouseSysConfig --
 *	Read the configuration of a MouseSystems Mouse.
 *
 * Results:
 *	A pointer to the allocated device.
 *
 * Side Effects:
 *
 *-----------------------------------------------------------------------
 */
X386DevicePtr
MouseSysConfig()
{
  X386DevicePtr dev;

  TRACE( ("MouseSysConfig()\n") );

  if (X386Lex(NULL) != STRING)
    FatalError("Devicename expected (%d)\n",X386LineNo);

  dev = (X386DevicePtr)xalloc(sizeof(MouseSysRec));
  dev->deviceProc   = MouseSysProc;
  dev->deviceEvents = MouseSysProcessEvents;
  dev->type = "MOUSE";
  dev->Name = X386Val.str;
  return dev;
}

/*
 *-----------------------------------------------------------------------
 * MicrosoftProc --
 *      Handle the initialization, etc. of a mouse
 *
 * Results:
 *      none.
 *
 * Side Effects:
 *      none.
 *
 *-----------------------------------------------------------------------
 */
static int
MicrosoftProc(pMouse, what)
     DevicePtr	pMouse;
     int        what;
{
  unchar        map[4];
  struct        termio tty;
  Atom          type;
  X386DevicePtr pPriv = (X386DevicePtr)pMouse->devicePrivate;

  TRACE( ("MicrosoftProc(pMouse=0x%x, what=%d)\n", pMouse, what) );
  
  switch (what)
    {
    case DEVICE_INIT: 
      if ((pPriv->Fd= open(pPriv->Name,O_RDONLY | O_NDELAY)) < 0) {
	Error ("Cannot open MicrosoftMouse\n");
	return (!Success); }
      ioctl(pPriv->Fd,TCGETA,&tty);  
      tty.c_iflag = IGNBRK | IGNPAR ;     
      tty.c_oflag = 0;           
      tty.c_cflag = B1200 | CS7 | CREAD | CLOCAL;
      tty.c_lflag = 0;
      tty.c_cc[VTIME]=0; 
      tty.c_cc[VMIN]=1;
      ioctl(pPriv->Fd,TCSETA,&tty);
      
      ((MicrosoftPtr)pPriv)->state = 0;
      ((MicrosoftPtr)pPriv)->emulate = 0;
      ((X386MousePtr)pPriv)->buttons = 0;
      ((X386MousePtr)pPriv)->x = 0;
      ((X386MousePtr)pPriv)->y = 0;
      pMouse->on = FALSE;
 
      map[1] = 3;
      map[2] = 2;
      map[3] = 1;
      InitPointerDeviceStruct(pMouse, map, 3, GetMotionEvents, MouseCtrl, 0);

#ifdef XINPUT
      type = MakeAtom(pPriv->type, strlen(pPriv->type), FALSE);
      AssignTypeAndName(pMouse, type, "Microsoft Mouse");
#endif

      break;
      
    case DEVICE_ON:
      AddEnabledDevice(pPriv->Fd);
      pMouse->on = TRUE;
      break;
      
    case DEVICE_CLOSE:
    case DEVICE_OFF:
      RemoveEnabledDevice(pPriv->Fd);
      pMouse->on = FALSE;
      break;
      
    }
  return Success;
}

/*
 *-----------------------------------------------------------------------
 * MicrosoftProcessEvents --
 *      Return the number of processed events.
 *
 * Results:
 *      Number of events processed.
 *
 * Side Effects:
 *      Process Events.
 *-----------------------------------------------------------------------
 */
static void
MicrosoftProcessEvents(pMouse)
     DevicePtr    pMouse;
{
  unchar           rBuf[64];
  int              i,nBytes;
  MicrosoftPtr pPriv = (MicrosoftPtr)pMouse->devicePrivate;

  nBytes = read(((X386DevicePtr)pPriv)->Fd, rBuf, sizeof(rBuf));

  for ( i=0; i < nBytes; i++) {
    switch(pPriv->state) {
    case 0:
      if ((rBuf[i] & 0x40) == 0x40) {
	pPriv->buttons= (rBuf[i]>>4) &0x3;
	pPriv->dy = (signed char)((rBuf[i] &0xC) <<4);
	pPriv->dx = (signed char)((rBuf[i] &0x3) <<6);
	(pPriv->state)++; }
      break;
      
    case 1:
      (pPriv->dx) |= (signed char)(rBuf[i]&0x3F);
      (pPriv->state)++;
      break;
      
    case 2:
      (pPriv->dy) |= (signed char)(rBuf[i]&0x3F);
      pPriv->state = 0;
      X386MouseEvent(pMouse, 
		 ((X386MousePtr)pPriv)->buttons, pPriv->dx, pPriv->dy);
      /*
       * emulate the third button by the other two
       */
      if (state[pPriv->buttons + pPriv->emulate][0] != -1)
	X386MouseEvent(pMouse, state[pPriv->buttons+pPriv->emulate][0], 0, 0);
      if (state[pPriv->buttons + pPriv->emulate][1] != -1)
	X386MouseEvent(pMouse, state[pPriv->buttons+pPriv->emulate][1], 0, 0);

      pPriv->emulate = state[pPriv->buttons + pPriv->emulate][2];
      break;
    }
  }
}


/*
 *-----------------------------------------------------------------------
 * MicrosoftConfig --
 *	Read the configuration of a MicroTrash (tm) Mouse.
 *
 * Results:
 *	A pointer to the allocated device.
 *
 * Side Effects:
 *
 *-----------------------------------------------------------------------
 */
X386DevicePtr
MicrosoftConfig()
{
  X386DevicePtr dev;

  TRACE( ("MircosoftConfig()\n") );

  if (X386Lex(NULL) != STRING)
    FatalError("Devicename expected (%d)\n",X386LineNo);

  dev = (X386DevicePtr)xalloc(sizeof(MicrosoftRec));
  dev->deviceProc   = MicrosoftProc;
  dev->deviceEvents = MicrosoftProcessEvents;
  dev->type = "MOUSE";
  dev->Name = X386Val.str;
  return dev;
}


/*
 *-----------------------------------------------------------------------
 * BusMouseProc --
 *      Handle the initialization, etc. of a mouse
 *
 * Results:
 *      none.
 *
 * Side Effects:
 *      none.
 *
 *-----------------------------------------------------------------------
 */
static int
BusMouseProc(pMouse, what)
     DevicePtr	pMouse;
     int        what;
{
  unchar        map[4];
  Atom          type;
  X386DevicePtr pPriv = (X386DevicePtr)pMouse->devicePrivate;

  TRACE( ("BusMouseProc(pMouse=0x%x, what=%d)\n", pMouse, what) );
  
  switch (what)
    {
    case DEVICE_INIT: 
      if ((pPriv->Fd= open(pPriv->Name,O_RDONLY | O_NDELAY)) < 0) {
	Error ("Cannot open BusMouse\n");
	return (!Success); }
      
      ((BusMousePtr)pPriv)->state = 0;
      ((X386MousePtr)pPriv)->buttons = 0;
      ((X386MousePtr)pPriv)->x = 0;
      ((X386MousePtr)pPriv)->y = 0;
      pMouse->on = FALSE;
 
      map[1] = 3;
      map[2] = 2;
      map[3] = 1;
      InitPointerDeviceStruct(pMouse, map, 3, GetMotionEvents, MouseCtrl, 0);

#ifdef XINPUT
      type = MakeAtom(pPriv->type, strlen(pPriv->type), FALSE);
      AssignTypeAndName(pMouse, type, "Bus Mouse");
#endif

      break;
      
    case DEVICE_ON:
      AddEnabledDevice(pPriv->Fd);
      pMouse->on = TRUE;
      break;
      
    case DEVICE_CLOSE:
    case DEVICE_OFF:
      RemoveEnabledDevice(pPriv->Fd);
      pMouse->on = FALSE;
      break;
      
    }
  return Success;
}

/*
 *-----------------------------------------------------------------------
 * BusMouseProcessEvents --
 *      Process the good old BusMouse events.
 *
 * Results:
 *      Number of events processed.
 *
 * Side Effects:
 *      Process Events.
 *-----------------------------------------------------------------------
 */
static void
BusMouseProcessEvents(pMouse)
     DevicePtr    pMouse;
{
  unchar           rBuf[64];
  int              i,nBytes;
  BusMousePtr  pPriv = (BusMousePtr)pMouse->devicePrivate;

  nBytes = read(((X386DevicePtr)pPriv)->Fd, rBuf, sizeof(rBuf));

  for ( i=0; i < nBytes; i++) {
    switch(pPriv->state) {
    case 0:
      if ((rBuf[i] & 0xF8) == 0x80) {
	pPriv->buttons= (~rBuf[i]) &0x7;
	(pPriv->state)++; }
      break;
      
    case 1:
      (pPriv->dx) = (signed char)(rBuf[i]);
      (pPriv->state)++;
      break;
      
    case 2:
      (pPriv->dy) = (signed char)(rBuf[i]);
      (pPriv->state)++;
      break;

    case 3:
      (pPriv->state)++;
      break;

    case 4:
      pPriv->state = 0;
      X386MouseEvent(pMouse, pPriv->buttons, pPriv->dx, -pPriv->dy);
      break;
    }
  }
}


/*
 *-----------------------------------------------------------------------
 * BusMouseConfig --
 *	Read the configuration of a BusMouse.
 *
 * Results:
 *	A pointer to the allocated device.
 *
 * Side Effects:
 *
 *-----------------------------------------------------------------------
 */
X386DevicePtr
BusMouseConfig()
{
  X386DevicePtr dev;

  TRACE( ("BusMouseConfig()\n") );

  if (X386Lex(NULL) != STRING)
    FatalError("Devicename expected (%d)\n",X386LineNo);

  dev = (X386DevicePtr)xalloc(sizeof(BusMouseRec));
  dev->deviceProc   = BusMouseProc;
  dev->deviceEvents = BusMouseProcessEvents;
  dev->type = "MOUSE";
  dev->Name = X386Val.str;
  return dev;
}

/*
 *-----------------------------------------------------------------------
 * NesMouseProc --
 *      Handle the initialization, etc. of a Nintendo fake mouse
 *
 * Results:
 *      none.
 *
 * Side Effects:
 *      none.
 *
 *-----------------------------------------------------------------------
 */
static int
NesMouseProc(pMouse, what)
     DevicePtr	pMouse;
     int        what;
{
  unchar        map[4];
  Atom          type;
  X386DevicePtr pPriv = (X386DevicePtr)pMouse->devicePrivate;

  TRACE( ("NesMouseProc(pMouse=0x%x, what=%d)\n", pMouse, what) );
  
  switch (what)
    {
    case DEVICE_INIT: 
      if ((pPriv->Fd= open(pPriv->Name,O_RDONLY | O_NDELAY)) < 0) {
	Error ("Cannot open NesMouse\n");
	return (!Success); }
      
      ((NesMousePtr)pPriv)->switches = 0;
      ((NesMousePtr)pPriv)->moves = 0;
      /* dda is set earlier, in NesMouseConfig */
      ((NesMousePtr)pPriv)->add = 128;
      ((NesMousePtr)pPriv)->shift = 7;
      ((X386MousePtr)pPriv)->buttons = 0;
      ((X386MousePtr)pPriv)->x = 0;
      ((X386MousePtr)pPriv)->y = 0;
      pMouse->on = FALSE;
 
      map[1] = 3;
      map[2] = 2;
      map[3] = 1;
      InitPointerDeviceStruct(pMouse, map, 3, GetMotionEvents, MouseCtrl, 0);

#ifdef XINPUT
      type = MakeAtom(pPriv->type, strlen(pPriv->type), FALSE);
      AssignTypeAndName(pMouse, type, "NES Mouse");
#endif

      break;
      
    case DEVICE_ON:
      AddEnabledDevice(pPriv->Fd);
      pMouse->on = TRUE;
      break;
      
    case DEVICE_CLOSE:
    case DEVICE_OFF:
      RemoveEnabledDevice(pPriv->Fd);
      pMouse->on = FALSE;
      break;
      
    }
  return Success;
}

/*
 *-----------------------------------------------------------------------
 * NesMouseProcessEvents --
 *      Process the good old NesMouse events.
 *
 * Results:
 *      Number of events processed.
 *
 * Side Effects:
 *      Process Events.
 *-----------------------------------------------------------------------
 */
static void
NesMouseProcessEvents(pMouse)
     DevicePtr    pMouse;
{
  unchar           rBuf[1024];	/* we can get lots of NES bytes */
  int              i,nBytes;
  NesMousePtr  pPriv = (NesMousePtr)pMouse->devicePrivate;
  int		   doinput;

  nBytes = read(((X386DevicePtr)pPriv)->Fd, rBuf, sizeof(rBuf));

  /* Each byte is encoded as per nintendo.h.  Many samples a second. */
  /* 
   * Decode motion linearly.  With 1K read channel, we can be assured 
   * of pulling all current input.  
   */
  pPriv->dx = pPriv->dy = 0;
  doinput = 0;
  for ( i=0; i < nBytes; i++) {
    if (rBuf[i] & NES_up)
	pPriv->dy -= pPriv->add;
    else if (rBuf[i] & NES_down)
	pPriv->dy += pPriv->add;
    if (rBuf[i] & NES_right)
	pPriv->dx += pPriv->add;
    else if (rBuf[i] & NES_left)
	pPriv->dx -= pPriv->add;
    if (rBuf[i] & NES_MOVES)
    if ((rBuf[i] & NES_MOVES) == pPriv->moves)
        pPriv->add += pPriv->dda;			/* accelerate */
    else
	pPriv->add = (1 << pPriv->shift);		/* downshift */
    pPriv->moves = (rBuf[i] & NES_MOVES);
    if ((rBuf[i] & (NES_SWITCHES)) != pPriv->switches) {
      pPriv->buttons = 0;
      if (rBuf[i] & NES_A)
	pPriv->buttons |= pPriv->A;
      if (rBuf[i] & NES_B)
	pPriv->buttons |= pPriv->B;
      if (rBuf[i] & NES_select)
	pPriv->buttons |= pPriv->Select;
      if (rBuf[i] & NES_start)
	pPriv->buttons |= pPriv->Start;
      /*
       * No AB support yet.  I don't like state machine system above.
       * It really needs to be sub-second timer-based.
       */
      pPriv->switches = rBuf[i] & (NES_SWITCHES);
      doinput++;
    }
    X386MouseEvent(pMouse, pPriv->buttons, 
	pPriv->dx >> pPriv->shift, pPriv->dy >> pPriv->shift);
    pPriv->dx = pPriv->dy = 0;
  }
}


/*
 *-----------------------------------------------------------------------
 * NesMouseConfig --
 *	Read the configuration of a NesMouse.
 *
 * Results:
 *	A pointer to the allocated device.
 *
 * Side Effects:
 *
 *-----------------------------------------------------------------------
 */

X386DevicePtr
NesMouseConfig()
{
  NesMousePtr dev;
  char *cp, *cp2, *strchr();
  int i;

  TRACE( ("NesMouseConfig()\n") );

  if (X386Lex(NULL) != STRING)
    FatalError("Devicename expected (%d)\n",X386LineNo);

  /* Reverse convention from above */
  dev = (NesMousePtr)xalloc(sizeof(NesMouseRec));
  ((X386DevicePtr)dev)->deviceProc   = NesMouseProc;
  ((X386DevicePtr)dev)->deviceEvents = NesMouseProcessEvents;
  ((X386DevicePtr)dev)->type = "MOUSE";
  ((X386DevicePtr)dev)->Name = X386Val.str;

  /* Read in Nintendo sub-configuration */
  /* Syntax: button[,button].. " " */
  /* The keyword parser doesn't handle ,'s and I'm too lazy */
  dev->A = dev->B = dev->Start = dev->Select = dev->AB = 0;
  for(i = 2; i >= 0; i--) {
    if (X386Lex(NULL) != STRING)
      FatalError("Nintendo button name expected (%d)\n",X386LineNo);
    cp = X386Val.str;
    if (cp2 = strchr(cp, ','))
	*cp2 = '\0';
    while(cp && *cp) {
      if (streq(cp, "A")) {
	  if (dev->A)
	      FatalError("Nintendo A button already assigned\n");
	  else dev->A = (1<<i);
      } else if (streq(cp, "B")) {
	  if (dev->B)
	      FatalError("Nintendo B button already assigned\n");
	  else dev->B = (1<<i);
      } else if (streq(cp, "Start")) {
	  if (dev->Start)
	      FatalError("Nintendo Start button already assigned\n");
	  else dev->Start = (1<<i);
      } else if (streq(cp, "Select")) {
	  if (dev->Select)
	      FatalError("Nintendo Select button already assigned\n");
	  else dev->Select = (1<<i);
      } else if (streq(cp, "AB")) {
	  if (dev->AB)
	      FatalError("Nintendo A+B button combination already assigned\n");
	  else dev->AB = (1<<i);
      } else 
          FatalError("Nintendo button '%s' not recognized!\n", cp);
      /* Advance to next Nintendo button for this mouse button */
      if (cp2) {
	*cp2++ = ',';	/* restore char to avoid memory leak */
        cp = cp2;
      } else
        cp = 0;
      }
  }
  /* optional DDA argument:  */
  dev->dda = 0;
  if ((X386Lex(NULL) == NUMBER) && (X386Val.num >= 0) && (X386Val.num <= 20))
	dev->dda = X386Val.num;
  return (X386DevicePtr)dev;
}

