home *** CD-ROM | disk | FTP | other *** search
/ Virtual Reality Homebrewer's Handbook / vr.iso / vr386 / pcdevice / sgsppt.c < prev    next >
C/C++ Source or Header  |  1996-03-19  |  18KB  |  662 lines

  1.  
  2. /* Sega and glove support routines; written by Dave Stampe, July 1992
  3.    Large portions rewritten by Dave Stampe, 24/12/93, to work with new
  4.    timer code and to be easier to use externally  */
  5.  
  6. /*
  7.    These routines have been extensively copied by others, notably
  8.    OGLOVE.  The glove code here is the original, and works as well
  9.    as any glove code out there. It was among the first glove drivers
  10.    for the PC, and the first to use noise reduction.  OGLOVE copies
  11.    the NR, read, and init code almost verbatim, and also copies
  12.    other's work in the C++ wrapper. (See the notice below!)    */
  13.  
  14. /*
  15.  
  16.  This code is part of the VR-386 project, created by Dave Stampe.
  17.  VR-386 is a desendent of REND386, created by Dave Stampe and
  18.  Bernie Roehl.  Almost all the code has been rewritten by Dave
  19.  Stampre for VR-386.
  20.  
  21.  Copyright (c) 1994 by Dave Stampe:
  22.  May be freely used to write software for release into the public domain
  23.  or for educational use; all commercial endeavours MUST contact Dave Stampe
  24.  (dstampe@psych.toronto.edu) for permission to incorporate any part of
  25.  this software or source code into their products!  Usually there is no
  26.  charge for under 50-100 items for low-cost or shareware products, and terms
  27.  are reasonable.  Any royalties are used for development, so equipment is
  28.  often acceptable payment.
  29.  
  30.  ATTRIBUTION:  If you use any part of this source code or the libraries
  31.  in your projects, you must give attribution to VR-386 and Dave Stampe,
  32.  and any other authors in your documentation, source code, and at startup
  33.  of your program.  Let's keep the freeware ball rolling!
  34.  
  35.  DEVELOPMENT: VR-386 is a effort to develop the process started by
  36.  REND386, improving programmer access by rewriting the code and supplying
  37.  a standard API.  If you write improvements, add new functions rather
  38.  than rewriting current functions.  This will make it possible to
  39.  include you improved code in the next API release.  YOU can help advance
  40.  VR-386.  Comments on the API are welcome.
  41.  
  42.  CONTACT: dstampe@psych.toronto.edu
  43. */
  44.  
  45. #include <stdio.h>
  46. #include <stdlib.h>
  47. #include <dos.h>
  48. #include <bios.h>
  49. #include <signal.h>
  50. #include <string.h>     /* stricmp() */
  51.  
  52. #include "devpriv.h"
  53. #include "pcdevice.h"
  54. #include "f3dkitd.h"    /* set_vpage() */
  55.  
  56. /************ INTERFACE SPECIFIC GLOVE STUFF ****************/
  57.  
  58. int port_image = 0;
  59. int sega_port_image = 0;
  60.  
  61. #define INPORT   0x379           /* i/o port addresses */
  62. #define OUTPORT  0x378
  63.  
  64. /* bits from parallel port: */
  65. #define GDATA    0x10    /* PG data in */
  66. #define GLATCH   0x02    /* PG latch out */
  67. #define GCLOCK   0x01    /* PG clock out */
  68. #define GCLOLAT  0x03    /* clock + latch */
  69.  
  70. #define GMASK    0x03     /* bits changable for glove access */
  71.  
  72. /************ GLOVE SPEED AND INTERFACE *****************/
  73.  
  74. #define FASTCOUNT 8       /* # of fast packet bytes */
  75.  
  76. #define D2BITS      12      /* BIT DELAY: microseconds */
  77. #define D2BYTES      140      /* BYTE DELAY: microseconds */
  78.  
  79. #define MAXRESET  200      /* failed reads before resetting glove */
  80.  
  81.  
  82. /***** declarations for assembly code in glovedel.asm *****/
  83.  
  84. int glove_none_mask = 0;
  85. int glove_latch_mask = GLATCH;
  86. int glove_clock_mask = GCLOCK;
  87. int glove_clock_latch = GCLOLAT;
  88.  
  89. int glove_in_port = INPORT;
  90. int glove_out_port = OUTPORT;
  91.  
  92. int glove_data_mask = GDATA;
  93. int glove_write_mask = GMASK;
  94.  
  95. int glove_bit_delay = D2BITS;
  96.  
  97. int glove_byte_delay = D2BYTES;
  98.  
  99. static int glove_892_delay = 892;
  100. static int glove_2260_delay = 2260;
  101. static int glove_7212_delay = 7212;
  102. static int glove_10000_delay = 10000;
  103.  
  104.  
  105. /********* defines for output line pair control *******/
  106.  
  107. #define C0L0() set_glove_bits(glove_none_mask)    /* clock 0 latch 0 */
  108. #define C0L1() set_glove_bits(glove_latch_mask)   /* clock 0 latch 1 */
  109. #define C1L0() set_glove_bits(glove_clock_mask)   /* clock 1 latch 0 */
  110. #define C1L1() set_glove_bits(glove_clock_latch)  /* clock 1 latch 1 */
  111.  
  112. /************ status and read control **********/
  113.  
  114. static int glove_rx_try = 10; /* number of read tries: first will be rxflags */
  115. static int glove_rx_flags = 0; /* holds flags (read 10 mS after packet */
  116.  
  117.  
  118. /********************** HIRES MODE SETUP *****************/
  119.  
  120. /*  HIRES ENTRY CODES
  121.     byte:
  122.     1- any value between $05 and $31   (x,y res. divisor???)
  123.     2- only $C1 and $81 work OK (C1)(81 does garbage)
  124.     3- no effect    (08)
  125.     4- no effect    (00)
  126.     5- no effect    (02)
  127.     6- only $FF works   (FF)
  128.     7- seems to affect read rate slightly, 1 fastest (01)
  129.  */
  130.  
  131. static int glove_ignore = 0; /* # of interrupts to ignore glove so it */
  132.                              /* will recover from mode setup          */
  133.  
  134. static int hires_code[7] = { 0x05, 0xC1, 0x08, 0x00, 0x02, 0xFF, 0x01 };
  135.  
  136. static void Hires(void) /* enter HIRES mode */
  137. {
  138.     int i,j,k;
  139.     /* dummy read 4 bits from glove:  */
  140.     C1L0(); 
  141.     C1L1(); /* generate a reset (latch) pulse */
  142.     glove_delay(glove_bit_delay); /* delay for 6 us */
  143.     C1L0();
  144.     glove_delay(glove_bit_delay); /* delay for 6 us */
  145.  
  146.     C0L0(); 
  147.     C1L0(); /* pulse clock */
  148.     C0L0();
  149.     C1L0(); /* pulse clock */
  150.     C0L0(); 
  151.     C1L0(); /* pulse clock */
  152.     C0L0(); 
  153.     C1L0(); /* pulse clock */
  154.  
  155.     /* force glove to listen for setup */
  156.     C1L0();
  157.     glove_delay(glove_7212_delay); /* 7212 us delay */
  158.     C1L1();
  159.     glove_delay(glove_2260_delay); /* 2260 us delay */
  160.  
  161.     for (i = 0; i < 7; i++) /* send 7 bytes of mode data */
  162.     {
  163.         k = hires_code[i];
  164.         for (j = 0; j < 8; j++) /* 8 bits per byte, MSB first */
  165.         {
  166.             if (k & 0x80)
  167.             {
  168.                 C1L1();
  169.                 C0L1();
  170.                 C1L1();
  171.             }
  172.             else
  173.             {
  174.                 C1L0();
  175.                 C0L0();
  176.                 C1L0();
  177.             }
  178.             k <<= 1;
  179.         }
  180.         glove_delay(glove_byte_delay);
  181.     }
  182.  
  183.     glove_delay(glove_892_delay); /* 892 us delay (end of setup data) */
  184.  
  185.     C1L0(); /* drop the reset line */
  186.     glove_ignore = 10; /* some time for the glove controller to relax */
  187. }
  188.  
  189.  
  190. /**************** HYSTERISIS NOISE REMOVAL: x,y,z,rot *************/
  191.  
  192. #define XHYST 2            /* hysterisis for X, Y low noise reduction */
  193. #define YHYST 2            /* 2 eliminates +/-3 quanta of noise */
  194. #define ZHYST 1
  195. #define RHYST 0
  196.  
  197. static int ox = -1000; /* last x,y for hysterisis */
  198. static int oy = -1000;
  199. static int oz = -1000;
  200. static int or = -1000;
  201.  
  202. static void dehyst(glove_data *g) /* hysterisis deglitch (low noise removal) */
  203. {
  204.     int x = g->x;
  205.     int y = g->y;
  206.     int z = g->z;
  207.     int r = g->rot;
  208.     /* handle recentering ("0"key or "Center") */
  209.     if (g->keys == 0) ox = oy = oz = 0;
  210.  
  211.     if (x-ox > XHYST) ox = x-XHYST; /* X hysterisis */
  212.     else
  213.         if (ox-x > XHYST) ox = x+XHYST;
  214.  
  215.     if (y-oy > YHYST) oy = y-YHYST; /* Y hysterisis */
  216.     else
  217.         if (oy-y > YHYST) oy = y+YHYST;
  218.  
  219.     if (z-oz > ZHYST) oz = z-ZHYST; /* Z hysterisis */
  220.     else
  221.         if (oz-z > ZHYST) oz = z+ZHYST;
  222.  
  223.     if (RHYST)
  224.     {
  225.         if (or < 3 && r > 8) /* CCW around-end */
  226.             if (or > 0 || r < 11) or = r+RHYST; 
  227.         else;
  228.         else
  229.             if (or > 8 && r < 3) /* CW around-end */
  230.                 if (or < 11 || r > 0) or = r-RHYST; 
  231.         else;
  232.             else
  233.                 if (r-or > RHYST) or = r-RHYST; /* R hysterisis */
  234.                 else
  235.                     if (or-r > RHYST) or = r+RHYST;
  236.  
  237.         if (or > 11) or -= 11;
  238.         if (or < 0) or += 11;
  239.     }
  240.     else or = r;
  241.     g->x = ox; /* replace present X,Y data */
  242.     g->y = oy;
  243.     g->z = oz;
  244.     g->rot = or;
  245. }
  246.  
  247. /****************** DEGLITCHING ***************/
  248.  
  249. #define XACC 8          /* X, Y maximum accel/decel level. Should */
  250. #define YACC 8        /* be 6-10, but too high limits gesturing */
  251.  
  252. #define XXTEND 1    /* stretches deglitching time */
  253. #define YXTEND 1
  254. #define ROTTIME 1
  255.  
  256. static int x1 = 0; /* delayed 1 sample (for smoothed velocity test) */
  257. static int y1 = 0;
  258. static int x2 = 0; /* delayed 2 samples */
  259. static int y2 = 0;
  260. static int lx = 0; /* last good X,Y speed */
  261. static int ly = 0;
  262. static int lax = 0; /* bad data "stretch" counter */
  263. static int lay = 0;
  264. static int lsx = 0; /* X,Y "hold" values to replace bad data */
  265. static int lsy = 0;
  266. static int lcx = 0; /* last X,Y speed for accel. calc. */
  267. static int lcy = 0;
  268.  
  269. static int saverot = 0;
  270. static int lastrot = 0; /* last stable rotate */
  271. static int chrot = 0; /* time its been stable */
  272.  
  273. static void deglitch(glove_data *g)
  274. {
  275.     int vx, vy;
  276.  
  277.     int x = g->x;
  278.     int y = g->y;
  279.  
  280.     if (g->keys == 0) /* reset on recentering ("0" or "Center" key) */
  281.     {
  282.         x1 = x2 = y1 = y2 = 0;
  283.         lx = ly = lax = lay = 0;
  284.         lsx = lsy = lcx = lcy = 0;
  285.         lastrot = chrot = saverot = 0;
  286.     }
  287.  
  288.     if (lastrot != g->rot)
  289.     {
  290.         chrot = 0;
  291.         lastrot = g->rot;
  292.         g->rot = saverot;
  293.     }
  294.     else
  295.         {
  296.         if (chrot < ROTTIME)
  297.         {
  298.             chrot++;
  299.             g->rot = saverot;
  300.         }
  301.         else saverot = lastrot = g->rot;
  302.     }
  303.  
  304.     vx = x-((x1+x2) >> 1); /* smoothed velocity */
  305.     vy = y-((y1+y2) >> 1);
  306.  
  307.     x2 = x1; /* update last values */
  308.     x1 = g->x;
  309.  
  310.     y2 = y1;
  311.     y1 = g->y;
  312.  
  313.     if (abs(lcx-vx) > XACC) lax = XXTEND; /* check for extreme acceleration */
  314.     if (lax == 0) lx = vx; /* save only good velocity        */
  315.     lcx = vx; /* save velocity for next accel.  */
  316.  
  317.     if (abs(lcy-vy) > YACC) lay = YXTEND; /* same deal for Y accel. */
  318.     if (lay == 0) ly = vy;
  319.     lcy = vy;
  320.  
  321.     if (lax != 0) /* hold X pos'n if glitch */
  322.     {
  323.         g->x = lsx;
  324.         lax--;
  325.     }
  326.  
  327.     if (lay != 0) /* hold Y pos'n if glitch */
  328.     {
  329.         g->y = lsy;
  330.         lay--;
  331.     }
  332.  
  333.     lsx = g->x; /* save position for X,Y hold */
  334.     lsy = g->y;
  335. }
  336.  
  337. /**************** COMPUTE COUNTS FOR TIME CALIBRATION ***********/
  338.  
  339. static float factor;
  340.  
  341. static int uconvert(int t)
  342. {
  343.   float f;
  344.  
  345.   f = factor*((float)t);
  346.   if (f < 1.0) return 1;
  347.   if (f > 60000.0) return 60000;
  348.   return f;
  349. }
  350.  
  351.  
  352. static void calibrate(void)
  353. {
  354.   unsigned c = timed_glove_delay(1400);    // counts for delay of 2000
  355.  
  356.   factor = 1190.0/((float)c);           // microseconds per delay
  357.                     // factor of 2 fixup due to
  358.                     // timing difference in new code
  359.  
  360.   glove_bit_delay = uconvert(glove_bit_delay);
  361.   glove_byte_delay = uconvert(glove_byte_delay);
  362.   glove_892_delay = uconvert(glove_892_delay);
  363.   glove_2260_delay = uconvert(glove_2260_delay);
  364.   glove_7212_delay = uconvert(glove_7212_delay);
  365.   glove_10000_delay = uconvert(glove_10000_delay);
  366. }
  367.  
  368. /******************* gesture recognition **************/
  369.  
  370. #define G_FLAT      0
  371. #define G_THUMB_IN  1
  372. #define G_INDEX_IN  2
  373. #define G_MIDDLE_IN 3
  374. #define G_RING_IN   4
  375. #define G_PINCH     5
  376. #define G_FIST      6
  377. #define G_THUMB_OUT 7
  378. #define G_POINT     8
  379. #define G_BADFINGER 9
  380. #define G_RING_OUT 10
  381. #define G_UNKNOWN  11      /* gesture classification */
  382.  
  383. long gesture_time = 0;
  384. unsigned char gesture_type = G_UNKNOWN;
  385. static unsigned char last_gesture = G_UNKNOWN;
  386.  
  387. /* more reliable mapped gestures */
  388.  
  389. static unsigned char gesture_map[12] = { 
  390.     G_FLAT, G_FLAT, G_PINCH, G_MIDDLE_IN,
  391.     G_RING_IN, G_PINCH, G_FIST, G_FIST, G_POINT,
  392.     G_BADFINGER, G_FIST, G_UNKNOWN };
  393.  
  394. /* unmapped gestures */
  395. /*
  396. char gesture_map[12] = { G_FLAT, G_THUMB_IN, G_INDEX_IN, G_MIDDLE_IN,
  397.              G_RING_IN, G_PINCH, G_FIST, G_THUMB_OUT,
  398.              G_POINT, G_BADFINGER, G_RING_OUT, G_UNKNOWN };
  399. */
  400.  
  401. static int g_lookup[256]; /* compiled lookup table for gestures */
  402.  
  403. static char gs[11][8] = { 
  404.     0,1, 0,0, 0,0, 0,0,
  405.     2,3, 0,0, 0,0, 0,1,
  406.     /* data is: for each gesture   */ 0,1, 2,3, 0,1, 0,1,
  407.     /* and for each finger, a max. */ 0,3, 0,1, 2,3, 0,1,
  408.     /* and min. bend value         */ 0,3, 0,1, 0,1, 2,3,
  409.     1,3, 2,3, 0,1, 0,1,
  410.     2,3, 2,3, 2,3, 1,3,
  411.     0,1, 2,3, 2,3, 1,3,
  412.     0,3, 0,1, 2,3, 1,3,
  413.     0,3, 2,3, 0,1, 1,3,
  414.     0,3, 2,3, 2,3, 0,1 };
  415.  
  416. static void init_gestures(void)
  417. {
  418.  int g,t,i,m,r,x;
  419.  
  420.  for (g = 0; g < 256; g++) g_lookup[g] = G_UNKNOWN;
  421.  
  422.  for (g = 11; g; )
  423.    {
  424.      g--;
  425.      for (t = 0; t < 4; t++)
  426.        for (i = 0; i < 4; i++)
  427.      for (m = 0; m < 4; m++)
  428.        for (r = 0; r < 4; r++)
  429.          {
  430.            x = (t<<6)+(i<<4)+(m<<2)+r;
  431.            if (gs[g][0] <= t && gs[g][1] >= t)
  432.          if (gs[g][2] <= i && gs[g][3] >= i)
  433.            if (gs[g][4] <= m && gs[g][5] >= m)
  434.              if (gs[g][6] <= r && gs[g][7] >= r)
  435.             g_lookup[x] = g;
  436.          }
  437.    }
  438. }
  439.  
  440. static void gesture_process(glove_data *g)
  441. {
  442.     gesture_time++;
  443.     gesture_type = g_lookup[((int)g->fingers) & 255];
  444.     gesture_type = gesture_map[gesture_type];
  445.     if (gesture_type != last_gesture) gesture_time = 0;
  446.     last_gesture = gesture_type;
  447. }
  448.  
  449.  
  450. /******************* READ GLOVE DATA PACKET *************/
  451.  
  452. void getglove(glove_data *buf) /* read 6 byte data packet */
  453. {
  454.     unsigned char *bp = (char *) buf;
  455.     int i;
  456.  
  457.     for (i = 0; i < FASTCOUNT; ++i)
  458.     {
  459.         *bp++ = get_glove_byte(); /* read data */
  460.         glove_delay(glove_byte_delay);
  461.     }
  462.     glove_rx_try = 0;
  463.     buf->rxflags = 0x3f; /* default all rx fired, but will be read in */
  464. }   /* 8-10 mS when glove_ready returns 2        */
  465.     /* also stored in glove_rx_flags          */
  466.  
  467. /*************** GLOVE INTERRUPT HANDLER **************/
  468.  
  469. static glove_data glove_int_data; /* our copy of the most recent data */
  470.  
  471. static unsigned unready = 0; /* number of times glove has been not ready */
  472. static int glove_deglitch; /* switches deglitching on and off */
  473.  
  474. void glove_int_handler()
  475. {
  476.   int n;
  477.  
  478.   if (glove_ignore) /* wait out setup time if required */
  479.     {
  480.       glove_ignore--;
  481.       return;
  482.     }
  483.  
  484.   glove_rx_try++;
  485.  
  486.   if ((n = get_glove_byte()) != 0xA0)
  487.     {
  488.       if (glove_rx_try == 1) glove_rx_flags = glove_int_data.rxflags = n;
  489.       if (++unready > MAXRESET)
  490.     { /* glove not responding... reset it */
  491.       unready = 0;
  492.       Hires();
  493.     }
  494.     }
  495.   else
  496.     { /* data ready! */
  497.       glove_delay(glove_byte_delay);
  498.       unready = 0;
  499.       getglove(&glove_int_data);
  500.       if (glove_deglitch)
  501.         deglitch(&glove_int_data); /* remove spikes and jumps */
  502.       dehyst(&glove_int_data); /* add hysteresis to remove LL noise */
  503.       gesture_process(&glove_int_data);
  504.       ++glove_int_data.nmissed; /* flag data as new */
  505.     }
  506. }
  507.  
  508. /******************* GLOVE INITIALIZATION ***************/
  509.  
  510. #define TIMER_CONTROL 0x43    /* timer control register */
  511. #define TIMER_0       0x40    /* timer zero data register */
  512. #define TIMER_MODE    0x36    /* byte to write to control register */
  513.  
  514. void glove_init(int gdeg) /* gdeg switches deglitching */
  515. {
  516.     outportb(TIMER_CONTROL, TIMER_MODE); /* reprogram timer to mod 65536 */
  517.     outportb(TIMER_0, 0); /* so calibration is correct    */
  518.     outportb(TIMER_0, 0);
  519.  
  520.     init_timer(6500,glove_int_handler);
  521.     glove_deglitch = gdeg;
  522.     calibrate(); /* compute timing counts */
  523.     Hires(); /* enter hires mode */
  524.     init_gestures();
  525.     glove_int_data.nmissed = 0; /* mark no new data yet */
  526. }
  527.  
  528. /************** EXTERNAL DATA INTERFACE **************/
  529.  
  530. int glove_read(glove_data *glov) /* return copy of data packet */
  531. {
  532.     disable();
  533.     *glov = glove_int_data;
  534.     glove_int_data.nmissed = 0;
  535.     enable();
  536.     return glov->nmissed;
  537. }
  538.  
  539.                    /* data status:                   */
  540. int glove_ready()  /* returns 0 if no new data,      */
  541. {                  /*         1 if new data ready,   */
  542.     int lgin, lgrt;  /*         2 if rxflags valid too */
  543.  
  544.     disable();
  545.     lgin = glove_int_data.nmissed;
  546.     lgrt = glove_rx_try;
  547.     enable();
  548.  
  549.     if (lgin == 0) return 0;
  550.     if (lgrt > 0) return 2;
  551.     return 1;
  552. }
  553.  
  554.  
  555.  
  556.  
  557. /********************* SEGA GLASSES CONTROL *****************/
  558.  
  559. int left_page = 0; /* left image */
  560. int right_page = 1; /* right image */
  561. volatile int has_switched; /* = 3 once both switched in */
  562.  
  563. static int adv_has_switched = 0;
  564.  
  565. #define SEGA_LEFT  0x30
  566. #define SEGA_RIGHT 0x20
  567. #define SEGA_OFF   0x00
  568.  
  569. #define SEGA_MASK  0x30       /* bits writeable for Sega */
  570.  
  571. #define COMPORT 0x3fc       /* com1 = 0x3fc, com2 = 0x2fc */
  572.  
  573. int sega_left = SEGA_LEFT;
  574. int sega_right = SEGA_RIGHT;
  575. int sega_address = COMPORT;
  576. int sega_mask = SEGA_MASK;
  577. int sega_doff = SEGA_OFF;
  578.  
  579. static int phase = 1; /* current image */
  580.  
  581. void select_sega_port(int port)  /* sets up default Sega stuff */
  582. {
  583.  sega_address = port;
  584.  sega_left = SEGA_LEFT;
  585.  sega_right = SEGA_RIGHT;
  586.  sega_mask = SEGA_MASK;
  587.  sega_doff = SEGA_OFF;
  588. }
  589.  
  590. static void sega_write(int data)  /* write Sega data w/o disturbance */
  591. {                          /* can also handle glove, Sega on same port */
  592.   disable();
  593.   if (sega_address == glove_out_port)
  594.     sega_port_image = port_image =
  595.             (port_image & (~sega_mask)) | (data & sega_mask);
  596.   else
  597.     sega_port_image = (sega_port_image & (~sega_mask)) | (data & sega_mask);
  598.   outportb(sega_address, sega_port_image);
  599.   enable();
  600. }
  601.  
  602.  
  603. void switch_LCD_driver(int to_go)  /* now gets arg, sync at 0, 1 = advanced signal */
  604. {                            /* 2-step switching ensures adr. load of VGA    */
  605.   if (to_go == 1)
  606.     {
  607.       if (phase == sega_left)
  608.     {
  609.       phase = sega_right;
  610.       if (switch_driver(SW_ADV_SWITCH, 0) )
  611.             set_vpage(right_page);
  612.       adv_has_switched = 1;
  613.     }
  614.       else
  615.     {
  616.       phase = sega_left;
  617.       if (switch_driver(SW_ADV_SWITCH, 1) )
  618.             set_vpage(left_page);
  619.       adv_has_switched = 2;
  620.     }
  621.     }
  622.   else if (to_go == 0)
  623.     {
  624.       switch_driver(SW_SYNC_SWITCH, (phase == sega_left) ? 1 : 0);
  625.       has_switched |= adv_has_switched;
  626.       adv_has_switched = 0;
  627.     }
  628. }
  629.  
  630.  
  631. void switch_sega(int to_go)  /* now gets arg, sync at 0, 1 = advanced signal */
  632. {
  633.   if (to_go == 1)
  634.     {
  635.       if (phase == sega_left)
  636.     {
  637.       phase = sega_right;
  638.       set_vpage(right_page);
  639.       adv_has_switched = 1;
  640.     }
  641.       else
  642.     {
  643.       phase = sega_left;
  644.       set_vpage(left_page);
  645.       adv_has_switched = 2;
  646.     }
  647.     }
  648.   else if (to_go == 0)
  649.    {
  650.      sega_write(phase);
  651.      has_switched |= adv_has_switched;
  652.      adv_has_switched = 0;
  653.    }
  654. }
  655.  
  656.  
  657. void sega_off()
  658. {
  659.   sega_write(sega_doff); /* turn glasses driver off to save lcds! */
  660. }
  661.  
  662.