From menudo.uh.edu!zaphod.mps.ohio-state.edu!swrinde!cs.utexas.edu!uunet!stanford.edu!apple!netcomsv!thinman Thu Jun 20 20:36:45 CDT 1991 Hello folks, Well, there seems to an appreciable interest in my Nintendo (tm) joystick driver for UNIX/386, so I'm posting it. It's a Streams driver, and includes appropriate support files for Thomas Roell's 386 UNIX X Window releases. Enjoy! Lance Norskog thinman@netcom.com c!/bin/sh c This is a shell archive (produced by shar 3.49) c To extract the files from this archive, save it to a file, remove c everything above the "!/bin/sh" line above, and type "sh file_name". c c made 06/20/1991 01:56 UTC by thinman@netcom.com c Source directory /usr/lance/vr/nes c c existing files will NOT be overwritten unless -c is specified c c This shar contains: c length mode name c ------ ---------- ------------------------------------------ c 4412 -rw-r--r-- README c 577 -rw-r--r-- Makefile c 9050 -rw-r--r-- nes.c c 1546 -rw-r--r-- strobe.c c 21649 -rw-r--r-- mouseX386.c c 301 -rw-r--r-- nintendo.h c 13 -rw-r--r-- Node c 26 -rw-r--r-- System c 67 -rw-r--r-- Master c c ============= README ============== if test -f 'README' -a X"$1" != X"-c"; then echo 'x - skipping README (File already exists)' else echo 'x - extracting README (Text)' sed 's/^X//' << 'SHAR_EOF' > 'README' && XHello- X XThis shar contains my Nintendo Entertainment System joystick driver. XIt's a Streams driver for UNIX V.3/386. It includes the support driver Xfiles for the original release of Thomas Roell's 386/X11R4 software. X XThis has only been tested on an Orchid 386/20 motherboard running XAT&T UNIX V.3.2. You shouldn't have much trouble with normal Xvariants like ISC or Everex. With terminally weird junk like SCO, Xgood luck. Edit the Master file to have the driver's major Xdevice number in the indicated place. Add Master as one line Xin the file /etc/conf/cf.d/mdevice. If you need to use a different Xparallel port number, change the port in System and nes.c. X XThis project requires a little soldering, I'm afraid. It was inspired Xby an article in Byte magazine (July 1990, pp. 288-289) on attaching Xa Mattel Power Glove to an IBM PC. In fact, the article was not about Xthe PG, but instead about attaching a Nintendo Joystick to a PC. X XIf you want to make the PG send raw data so you can do Virtual Reality Xwith it, this won't do it at all. You need schematics which I can send Xyou, and real electronic skills, which I can't. X XThe driver includes constants for the parallel port c and the pins Xattached. The pinouts for the cable are: X XNintendo Printer X1 - GND X2 - 2 (Clock) X3 - 3 (Reset) X4 - 13 (Data) X5 - NC X6 - NC X7 - 5V X XYou need to have a spare disk power connector in your PC. XYou need to get the 5V and GND lines from this connector. XI got empty shells for the big 4-pin Molexes and built a plug. X XDon't even think about doing this without an ohmmeter. X XThe algorithm is simple: wiggle the Reset line, wait a bit. XRead the status from the Data line. This is the A button. XFor 7 times wiggle the clock line, wait a bit, and read a bit. XThis gives the other 3 buttons and the four direction buttons. X XThe strobe program demonstrates the use of the cable, and Xallows you to test your cable without messing around with the kernel. XYou will need to calibrate the waiting constant for your computer. XYou will then need to transfer that constant to the variables Xnes_c[1-5] in nes.c. They seem to want to be the same. The Xconstant 20 came from a 20 mhz 386. Mr. Joystick is fairly picky Xabout the range of times he's happy with, so you may have to mess Xaround a bit with strobe.c. When you're done, the program output Xshould reliably reflect the buttons. X XTo add it you Thomas Roell's X386 system, cd to server/ddx/at386 Xin your X source tree. The enclosed mouse.c dates from his original X256-color release, Nov 8, 1990. You may have to do something for Xhis later versions. The enclosed nintendo.h is included by mouse.c X XYou also need to add the following three lines to config.c: X< cdefine NINTENDO 11 X< { NINTENDO, "nintendo" }, X< case NINTENDO: X386LinkDevice(NesMouseConfig()); break; XJust hunt for MICROSOFT, and you'll see where they go. X XNow, comment out the mouse config line in X/usr/lib/X11/X386/config/Xconfig and add this line: X X Nintendo "/dev/nes0" "A" "Select" "B" 10 X XThis is to use /dev/nes0 (you might add more later, you never know) Xand to configure NES "A" as the left button, NES "Select" as the Xmiddle button, NES "B" as the right button, and an accelerator Xvalue (see below) of 10. The accelerator value is optional, Xand defaults to 0, but you must give all three button descriptors! XThe button descriptors available are: "A", "B", "Select", "Start, Xand "AB". The latter refers to simultaneously pressing A and B. XThe Power Glove does this in some modes; there is no timer-based Xheuristic for deciding that "A-50 milliseconds-B" counts as an AB. X XThe accelerator value makes the cursor speed up when you hold down Xthe joystick button in one direction. I find 3 to 5 pleasantly useable, Xand nicer than a mouse. X XYou jaded Streams hackers may be in for a surprise. Hint: search for X"bucket" in nes.c. Especially serial port Streams driver hackers. X XHomework: 1) add some dead time before starting acceleration. 2) add Xtime smarts for the AB combination. 3) Bring up ZapGun (light pen) Xsupport on a VGA card based on the Cirrus chip set, the only chipset Xthat support light pens. Pins 5 & 6 on the Nintendo plug are lightpen Xinput and trigger input. It's handy for games, and you can get a Xhelmet with the ZapGun lightpen lens built in as a monocle. Head Xtracking doesn't have to cost $50,000. X XEnjoy! X XLance Norskog Xthinman@netcom.com SHAR_EOF chmod 0644 README || echo 'restore of README failed' Wc_c="`wc -c < 'README'`" test 4412 -eq "$Wc_c" || echo 'README: original size 4412, current size' "$Wc_c" fi c ============= Makefile ============== if test -f 'Makefile' -a X"$1" != X"-c"; then echo 'x - skipping Makefile (File already exists)' else echo 'x - extracting Makefile (Text)' sed 's/^X//' << 'SHAR_EOF' > 'Makefile' && Xc Xc Makefile for Nintendo joystick driver Xc Xc Copyright 1991 Lance Norskog Xc X XFILES= README Makefile nes.c strobe.c mouseX386.c nintendo.h Node System Master X Xall: nes.o strobe X Xstrobe: strobe.c X cc strobe.c -o strobe X Xinstall: X mkdir /etc/conf/pack.d/nes X rm -f /etc/conf/pack.d/nes/Driver.o X cp nes.o /etc/conf/pack.d/nes/Driver.o X rm -f /etc/conf/sdevice.d/nes /etc/conf/node.d/nes X cp Node /etc/conf/node.d/nes X cp System /etc/conf/system.d/nes X echo "You did install the Master file, didn't you?" X Xshar: X /usr2/tools/shar/shar -F -s thinman@netcom.com $(FILES) X XFRC: SHAR_EOF chmod 0644 Makefile || echo 'restore of Makefile failed' Wc_c="`wc -c < 'Makefile'`" test 577 -eq "$Wc_c" || echo 'Makefile: original size 577, current size' "$Wc_c" fi c ============= nes.c ============== if test -f 'nes.c' -a X"$1" != X"-c"; then echo 'x - skipping nes.c (File already exists)' else echo 'x - extracting nes.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'nes.c' && X/* X * NES streams driver. X * X * Derived, through several generations, from code typed in from X * the AT&T Streams Programmer's Guide. X * X * Supports Nintendeo Entertainment System joysticks via a parallel port. X * No interrupts. No zap-gun support. X * X * Each minor number corresponds to one joystick plug. It should be possible X * to hook up a few NES gizmos via parallel port. X * X * Copyright 1991, Lance C. Norskog X * X * version 0.1 - base, running with X X * version 0.2 - switch to double-bucket input management X */ X Xcinclude Xcinclude Xcinclude Xcinclude Xcinclude Xcifndef __GNUC__ Xcinclude Xcendif Xcinclude X Xstatic struct module_info rnes = { X 3, "nes", 0, INFPSZ, 500, 100 X}; X Xstatic struct module_info wnes = { X 3, "nes", 0, INFPSZ, 500, 100 X}; X Xstatic int nesopen(), nesclose(), neswput(), nesrsrv(); X Xstatic struct qinit urqinit = { /* Upper read */ X 0, nesrsrv, nesopen, nesclose, NULL, &rnes, 0 X}; X Xstatic struct qinit uwqinit = { /* Upper write */ X neswput, 0, nesopen, nesclose, NULL, &wnes, 0 X}; X Xstruct streamtab nesinfo = { X &urqinit, &uwqinit, NULL, NULL X}; X Xint nes_debug = 0, nes_notimer = 0, nes_nostrobe = 0; Xcdefine debug(mask, x) if (nes_debug & mask) printf x; X/* 1 for upper-level, 2 for lower-level, 4 for input data */ X Xint nes_in, /* number of input packets */ X nes_inb; /* number of input bytes */ Xint nes_nomblk, /* number of allocb failures */ X nes_nodupb, /* number of dupb failures */ X nes_full, /* number of buffer-full failures */ X nes_cant, /* number of canput failures */ X nes_busy; /* number of bucket-busy */ X Xint nes_bsize = 1024; /* several seconds worth */ X Xcdefine NNES 1 X Xcdefine NES_right 0x01 Xcdefine NES_left 0x02 Xcdefine NES_down 0x04 Xcdefine NES_up 0x08 Xcdefine NES_start 0x10 Xcdefine NES_select 0x20 Xcdefine NES_B 0x40 Xcdefine NES_A 0x80 X Xcdefine NES_SWITCHES (NES_start | NES_select | NES_A | NES_B) Xcdefine NES_MOVES (NES_right | NES_left | NES_up | NES_up) X Xstruct nes_nes { X unsigned short ctrl_port, ctrl_clock, ctrl_reset, X data_port, data_mask, X ticks; X unsigned char canopen; X queue_t *rdq; X int timer; X int nes; /* last buttons */ X int switches; X mblk_t *mp, *other; /* pointer to input blocks */ X} nes_nes[NNES] = { X{ X 0x378, X 1, X 2, X 0x379, X 0x10, X HZ, X} X}; X Xtypedef struct nes_nes nes_t; X Xint nnes = NNES; X Xint nes_c1 = 20; Xint nes_c2 = 20; Xint nes_c3 = 20; Xint nes_c4 = 20; Xint nes_c5 = 20; X X/* move to space.c */ X Xnesinit() { X int i, hz; X X for(i=0; i nnes) X return OPENFAIL; X nes = &nes_nes[dev]; X if (nes->rdq) X return dev; X X /* q_ptr is the q's repository for our per-chan structure */ X q->q_ptr = (caddr_t) nes; X WR(q)->q_ptr = (caddr_t) nes; X nes->rdq = q; X Xdebug(1, ("Line %d\n", __LINE__)); X /* Start timer */ X if (! nes_notimer) X nes->timer = timeout(nes_timer, q, nes->ticks); X nes->mp = allocb(nes_bsize, BPRI_LO); X nes->other = allocb(nes_bsize, BPRI_LO); X if (! nes->mp) nes_nomblk++; X Xdebug(1, ("Line %d\n", __LINE__)); X return dev; X} X Xstatic Xnesclose(q) Xqueue_t *q; X{ X nes_t *nes = (nes_t *) q->q_ptr; X mblk_t *mp; X Xdebug(1, ("Line %d\n", __LINE__)); X untimeout(nes->timer); X nes->rdq = (queue_t *) 0; X if (nes->mp) X freemsg(nes->mp); X if (nes->other) X freemsg(nes->other); X nes->mp = (mblk_t *) 0; X nes->other = (mblk_t *) 0; X nes_strobe_init(nes); Xdebug(1, ("Line %d\n", __LINE__)); X} X X/* This is called when, and only when, the above reader can read. */ X/* X * Double-bucket method: X * Only send up when other bucket is finished processing. X * Always duplicate buffers before sending them up. X */ Xnesrsrv(q) Xqueue_t *q; X{ X nes_t *nes = (nes_t *) q->q_ptr; X mblk_t *mp; X int can, bytes = 0; X Xdebug(1, ("Line %d\n", __LINE__)); X if (!nes->mp) X return; X if (nes->other && (nes->other->b_datap->db_ref > 1)) { X nes_busy++; X return; X } Xdebug(1, ("Line %d\n", __LINE__)); X if (! (bytes = (nes->mp->b_wptr > nes->mp->b_rptr))) X return; X can = canput(q->q_next); Xdebug(1, ("Line %d\n", __LINE__)); X if (! can) { X nes_cant++; X return; X } X if (!(mp = dupb(nes->mp))) { X nes_nodupb++; X return; X } X Xdebug(1, ("Line %d\n", __LINE__)); X /* send up copy of current bucket */ X nes_in++; X nes_inb += mp->b_wptr - mp->b_rptr; X putnext(q, mp); X X /* Swap buckets */ X mp = nes->other; X nes->other = nes->mp; X nes->mp = mp; X Xdebug(1, ("Line %d\n", __LINE__)); X if (!nes->mp) X nes->mp = allocb(nes_bsize, BPRI_LO); X if (! nes->mp) X nes_nomblk++; X else /* reset read/write pointers of recycled bucket */ X nes->mp->b_rptr = nes->mp->b_wptr = nes->mp->b_datap->db_base; X} X X/* This is called strioctl(), whether or not it's there! */ Xneswput(q, mp) Xqueue_t *q; Xmblk_t *mp; X{ X nes_t *nes = (nes_t *) q->q_ptr; X X switch(mp->b_datap->db_type) { X case M_IOCTL: X /* process */ X X mp->b_datap->db_type = M_IOCNAK; X qreply(q, mp); X break; X X /* flush handling must be here */ X case M_FLUSH: X if (*mp->b_rptr & FLUSHW) X flushq(q, FLUSHDATA); X if (*mp->b_rptr & FLUSHR) { X flushq(RD(q), FLUSHDATA); X *mp->b_rptr &= >FLUSHW; X qreply(q, mp); X } else X freemsg(mp); X break; X default: X freemsg(q, mp); X } X} X Xnes_timer(q) Xqueue_t *q; X{ X nes_t *nes = (nes_t *) q->q_ptr; X unsigned char bits, nes_strobe(); X mblk_t *tmp; X X if (nes_notimer) X goto resched; Xdebug(2, ("Line %d\n", __LINE__)); X if (!nes->mp && !(nes->mp = allocb(nes_bsize, BPRI_LO))) { X nes_nomblk++; X goto resched; X } X Xdebug(2, ("Line %d\n", __LINE__)); X if (nes->mp->b_wptr == nes->mp->b_datap->db_lim) { X nes_full++; X goto resched; X } X Xdebug(2, ("Line %d\n", __LINE__)); X bits = nes_strobe(nes); X if (((bits & NES_right) && (bits & NES_left)) || X ((bits & NES_up) && (bits & NES_down))) { X /* skip */ X } else { Xdebug(2, ("Line %d\n", __LINE__)); X /* report on movement or change in switches */ X if (bits || ((bits & (NES_SWITCHES)) != nes->switches)) { X *(nes->mp->b_wptr++) = bits; Xdebug(2, ("Line %d\n", __LINE__)); X qenable(nes->rdq); X nes->switches = (bits & NES_SWITCHES); X } Xdebug(2, ("Line %d\n", __LINE__)); X } X resched: X nes->timer = timeout(nes_timer, q, nes->ticks); X} X X/* X * Put port in quiescent state X */ X Xnes_strobe_init(nes) Xnes_t *nes; X{ X int i, j; X X if (nes_nostrobe) X return; X for(i = 0; i < 8; i++) X if ((1<data_mask) X break; X if (i == 8) { X nes->canopen = 0; X printf("NES%d: data_mask not set!\n", nes - nes_nes); X } X outb(nes->ctrl_port, 0); X outb(nes->data_port, 0); X} X Xcdefine hold(clk) for(slow = clk; slow; slow--); X X/* Actually strobe the NES and add events to the record */ Xunsigned char Xnes_strobe(nes) Xnes_t *nes; X{ X int i, slow; X unsigned char data; X unsigned short bits = 0; /* not char! */ X Xdebug(2, ("Line %d\n", __LINE__)); X if (nes_nostrobe) X return 0; X/* Xcifndef __GNUC__ X intr_disable(); Xcendif X*/ Xdebug(2, ("Line %d\n", __LINE__)); X outb(nes->ctrl_port, nes->ctrl_reset | nes->ctrl_clock); X hold(nes_c1); X outb(nes->ctrl_port, nes->ctrl_clock); X hold(nes_c2); X for(i = 0; i < 8; i++) { X data = inb(nes->data_port); X bits |= (data & nes->data_mask); X hold(nes_c3); X outb(nes->ctrl_port, 0); X hold(nes_c4); X outb(nes->ctrl_port, nes->ctrl_clock); X hold(nes_c5); X bits <<= 1; X } Xdebug(2, ("Line %d\n", __LINE__)); X /* bits now straddles word, is upwards by one */ X /* downshift into place */ X for(i = 0; i < 8; i++) { X bits >>= 1; X if (nes->data_mask & (1 << i)) X break; X } Xdebug(2, ("Line %d\n", __LINE__)); X/* Xcifndef __GNUC__ X intr_restore(); Xcendif X*/ Xdebug(2, ("Line %d\n", __LINE__)); X bits = >bits; Xif (bits && (nes_debug & 4)) X printf("Input: 0x%x\n", bits & 0x1ff); X return (unsigned char) bits; X} X Xstatic char * XM_string(type) X{ X switch(type) { X case M_DATA: return "M_DATA"; X case M_PROTO: return "M_PROTO"; X case M_BREAK: return "M_BREAK"; X case M_PASSFP: return "M_PASSFP"; X case M_SIG: return "M_SIG"; X case M_DELAY: return "M_DELAY"; X case M_CTL: return "M_CTL"; X case M_IOCTL: return "M_IOCTL"; X case M_SETOPTS: return "M_SETOPTS"; X case M_IOCACK: return "M_IOCACK"; X case M_IOCNAK: return "M_IOCNAK"; X case M_PCPROTO: return "M_PCPROTO"; X case M_PCSIG: return "M_PCSIG"; X case M_FLUSH: return "M_FLUSH"; X case M_STOP: return "M_STOP"; X case M_START: return "M_START"; X case M_HANGUP: return "M_HANGUP"; X case M_ERROR: return "M_ERROR"; X default: return "M_UNKNOWN"; X } X} X Xnesintr() {;} X/* Don't digest it here. X if (bits & NES_right) X nes->right++; X if (bits & NES_left) X nes->left++; X if (bits & NES_up) X nes->up++; X if (bits & NES_down) X nes->down++; X X if (bits & NES_A) X nes->A = 1; X if (bits & NES_B) X nes->B = 1; X*/ X SHAR_EOF chmod 0644 nes.c || echo 'restore of nes.c failed' Wc_c="`wc -c < 'nes.c'`" test 9050 -eq "$Wc_c" || echo 'nes.c: original size 9050, current size' "$Wc_c" fi c ============= strobe.c ============== if test -f 'strobe.c' -a X"$1" != X"-c"; then echo 'x - skipping strobe.c (File already exists)' else echo 'x - extracting strobe.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'strobe.c' && X/* X * Subroutine and test program to strobe NES controller. X * Jan 5, 1990 X */ X Xcinclude Xcinclude Xcinclude Xcinclude Xcinclude Xcinclude X Xunsigned char strobe( /* unsigned char port */ ); X Xmain() { X unsigned char old, new; X extern errno; X X old = new = 0; X setbuf(stdout, (char *) 0); X errno = 0; X ioctl(0, KDADDIO, 0x378); X if (errno) { X perror("ADDIO"); X } X ioctl(0, KDADDIO, 0x379); X if (errno) { X perror("ADDIO"); X } X ioctl(0, KDADDIO, 0x37a); X if (errno) { X perror("ADDIO"); X } X ioctl(0, KDENABIO); X if (errno) { X perror("ENABIO"); X } X strobe_init(0x378); X while(1) { X new = strobe(0x378); X if (new != old) X printf("%02x ", new); X old = new; X } X} X Xcdefine NES_CLOCK 0x1 /* to port */ Xcdefine NES_RESET 0x2 /* to port */ Xcdefine NES_DATA 0x10 /* from port + 1 */ X Xunsigned char nes_shift[16] = { 4, 5, 6, 7, 8, 9, 10, 11 }; X Xint c1 = 20; Xint c2 = 20; Xint c3 = 20; Xint c4 = 20; Xint c5 = 20; X Xstrobe_init(port) X{ X outb(port, 0); X outb(port+1, 0); X} X Xcdefine hold(clk) for(slow = clk; slow; slow--); X Xunsigned char Xstrobe(port) Xint port; X{ X int i, slow; X unsigned char data; X unsigned short bits = 0; X unsigned char xx[8]; X X /* stop interrupts */ X outb(port, NES_RESET | NES_CLOCK); X hold(c1); X outb(port, NES_CLOCK); X hold(c2); X for(i = 0; i < 8; i++) { X data = inb(port + 1); X bits |= ((data & NES_DATA) << nes_shift[i]); X hold(c5); X outb(port, 0); X hold(c3); X outb(port, NES_CLOCK); X hold(c4); X } X bits >>= 8; X return (unsigned char) >bits; X} SHAR_EOF chmod 0644 strobe.c || echo 'restore of strobe.c failed' Wc_c="`wc -c < 'strobe.c'`" test 1546 -eq "$Wc_c" || echo 'strobe.c: original size 1546, current size' "$Wc_c" fi c ============= mouseX386.c ============== if test -f 'mouseX386.c' -a X"$1" != X"-c"; then echo 'x - skipping mouseX386.c (File already exists)' else echo 'x - extracting mouseX386.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'mouseX386.c' && X/* X * Copyright 1990 by Thomas Roell, Dinkelscherben, Germany. X * X * Permission to use, copy, modify, distribute, and sell this software and its X * documentation for any purpose is hereby granted without fee, provided that X * the above copyright notice appear in all copies and that both that X * copyright notice and this permission notice appear in supporting X * documentation, and that the name of Thomas Roell not be used in X * advertising or publicity pertaining to distribution of the software without X * specific, written prior permission. Thomas Roell makes no representations X * about the suitability of this software for any purpose. It is provided X * "as is" without express or implied warranty. X * X * THOMAS ROELL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, X * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO X * EVENT SHALL THOMAS ROELL BE LIABLE FOR ANY SPECIAL, INDIRECT OR X * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, X * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER X * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR X * PERFORMANCE OF THIS SOFTWARE. X * X * Author: Thomas Roell, roell@lan.informatik.tu-muenchen.de X * X * $Header: /proj/X11/mit/server/ddx/at386/RCS/mouse.c,v 1.6 90/11/08 17:49:14 root Exp $ X */ X X Xcinclude "X386.h" Xcinclude "inputstr.h" Xcinclude "scrnintstr.h" Xcinclude "nintendo.h" X X/* X * Private structure for a MouseSystems Mouse X */ Xtypedef struct { X X386MouseRec info; X int state; X int buttons; X int dx,dy; X} MouseSysRec, * MouseSysPtr; X X/* X * Private structure for a Microsoft Mouse X */ Xtypedef struct { X X386MouseRec info; X int state; X int emulate; X int buttons; X int dx,dy; X} MicrosoftRec, *MicrosoftPtr; X X/* X * Private structure for a Bus Mouse X */ Xtypedef struct { X X386MouseRec info; X int state; X int buttons; X int dx,dy; X} BusMouseRec, *BusMousePtr; X X/* X * Private structure for a Nintendo Joystick fake mouse X * Supports configured mappings NES switches to mouse buttons, X * and non-linear sped up joystick inputs. X */ Xtypedef struct { X X386MouseRec info; X int switches; /* last NES switches */ X int moves; /* last NES move bits */ X int buttons; /* X buttons */ X int dx,dy; /* accumulated movement */ X int add, dda, shift; /* nonlinear motion inputs */ X int A, B, Start, Select, AB; /* what makes left-middle-right ? */ X} NesMouseRec, *NesMousePtr; X X/* X * lets create a simple finite-state machine X */ X Xstatic char state[24][3] = { X {-1,-1,0}, {-1,-1,4}, {-1,-1,8}, {2, -1,12}, /* nothing pressed */ X { 1, 0,0}, { 1,-1,16}, { 1, 0,8}, {2, -1,12}, /* delayed right */ X { 4, 0,0}, { 4, 0,4}, { 4,-1,20}, {2, -1,12}, /* delayed left */ X { 0,-1,0}, { 0,-1,0}, { 0,-1,0}, {-1,-1,12}, /* pressed middle */ X { 0,-1,0}, {-1,-1,16}, { 0,-1,8}, { 0, 2,12}, /* pressed right */ X { 0,-1,0}, { 0,-1,4}, {-1,-1,20}, { 0, 2,12}, /* pressed left */ X}; X X X/* X *----------------------------------------------------------------------- X * MouseCtrl -- X * Alter the control parameters for the mouse. X * X * Results: X * None. X * X * Side Effects: X * None. X * X *----------------------------------------------------------------------- X */ Xstatic void XMouseCtrl(pMouse, pCtrl) X DevicePtr pMouse; X PtrCtrl *pCtrl; X{ X X386MousePtr pPriv = (X386MousePtr)pMouse->devicePrivate; X X TRACE( ("MouseCtrl(pMouse=0x%x, pCtrl=0x%x)\n", pMouse, pCtrl) ); X X /* X * check for reasonable values X */ X if ((pCtrl->num > 0) && (pCtrl->den > 0)) { X pPriv->num = pCtrl->num; X pPriv->den = pCtrl->den; X } else { X pPriv->num = 1; X pPriv->den = 1; X } X X if (pCtrl->threshold > 0) X pPriv->threshold = pCtrl->threshold; X else X pPriv->threshold = 1; X} X X/* X *----------------------------------------------------------------------- X * GetMotionEvents -- X * Return the (number of) motion events in the "motion history X * buffer" (snicker) between the given times. X * X * Results: X * The number of events stuffed. X * X * Side Effects: X * None. X * X *----------------------------------------------------------------------- X */ Xstatic int XGetMotionEvents (buff, start, stop, pScreen) X CARD32 start, stop; X xTimecoord *buff; X ScreenPtr pScreen; X{ X TRACE( ("GetMotionEvents(buff=0x%x, start=%d, stop=%d, pScreen=0x%x)\n", X buff, start, stop, pScreen) ); X X return 0; X} X X/* X *----------------------------------------------------------------------- X * MouseSysProc -- X * Handle the initialization, etc. of a mouse X * X * Results: X * none. X * X * Side Effects: X * none. X * X *----------------------------------------------------------------------- X */ Xstatic int XMouseSysProc(pMouse, what) X DevicePtr pMouse; X int what; X{ X unchar map[4]; X struct termio tty; X Atom type; X X386DevicePtr pPriv = (X386DevicePtr)pMouse->devicePrivate; X X TRACE( ("MouseSysProc(pMouse=0x%x, what=%d)\n", pMouse, what) ); X X switch (what) X { X case DEVICE_INIT: X if ((pPriv->Fd = open(pPriv->Name,O_RDONLY | O_NDELAY)) < 0) { X ErrorF ("Cannot open MouseSystemsMouse\n"); X return (!Success); } X ioctl(pPriv->Fd,TCGETA,&tty); X tty.c_iflag = IGNBRK | IGNPAR ; X tty.c_oflag = 0; X tty.c_cflag = B1200 | CS8 | CSTOPB | CREAD | CLOCAL; X tty.c_lflag = 0; X tty.c_cc[VTIME]=0; X tty.c_cc[VMIN]=1; X ioctl(pPriv->Fd,TCSETA,&tty); X X ((MouseSysPtr)pPriv)->state = 0; X ((X386MousePtr)pPriv)->buttons = 0; X ((X386MousePtr)pPriv)->x = 0; X ((X386MousePtr)pPriv)->y = 0; X pMouse->on = FALSE; X X map[1] = 3; X map[2] = 2; X map[3] = 1; X InitPointerDeviceStruct(pMouse, map, 3, GetMotionEvents, MouseCtrl, 0); X Xcifdef XINPUT X type = MakeAtom(pPriv->type, strlen(pPriv->type), FALSE); X AssignTypeAndName(pMouse, type, "Mouse Systems Mouse"); Xcendif X X break; X X case DEVICE_ON: X AddEnabledDevice(pPriv->Fd); X pMouse->on = TRUE; X break; X X case DEVICE_CLOSE: X case DEVICE_OFF: X RemoveEnabledDevice(pPriv->Fd); X pMouse->on = FALSE; X break; X } X return Success; X} X X/* X *----------------------------------------------------------------------- X * MouseSysProcessEvents -- X * Return the number of processed events. X * X * Results: X * Number of events processed. X * X * Side Effects: X * Process Events. X *----------------------------------------------------------------------- X */ Xstatic void XMouseSysProcessEvents(pMouse) X DevicePtr pMouse; X{ X unchar rBuf[64]; X int i,nBytes; X MouseSysPtr pPriv = (MouseSysPtr)pMouse->devicePrivate; X X nBytes = read(((X386DevicePtr)pPriv)->Fd, rBuf, sizeof(rBuf)); X X for ( i=0; i < nBytes; i++) { X switch(pPriv->state) { X case 0: X if ((rBuf[i] & 0xF8) == 0x80) { X pPriv->buttons = (>rBuf[i]) & 0x07; X (pPriv->state)++; } X break; X X case 1: X pPriv->dx = (signed char)rBuf[i]; X (pPriv->state)++; X break; X X case 2: X pPriv->dy = (signed char)rBuf[i]; X (pPriv->state)++; X break; X X case 3: X (pPriv->dx) += (signed char)rBuf[i]; X (pPriv->state)++; X break; X X case 4: X (pPriv->dy) += (signed char)rBuf[i]; X pPriv->state = 0; X X386MouseEvent(pMouse, pPriv->buttons, pPriv->dx, -pPriv->dy); X break; X } X } X} X X X/* X *----------------------------------------------------------------------- X * MouseSysConfig -- X * Read the configuration of a MouseSystems Mouse. X * X * Results: X * A pointer to the allocated device. X * X * Side Effects: X * X *----------------------------------------------------------------------- X */ XX386DevicePtr XMouseSysConfig() X{ X X386DevicePtr dev; X X TRACE( ("MouseSysConfig()\n") ); X X if (X386Lex(NULL) != STRING) X FatalError("Devicename expected (%d)\n",X386LineNo); X X dev = (X386DevicePtr)xalloc(sizeof(MouseSysRec)); X dev->deviceProc = MouseSysProc; X dev->deviceEvents = MouseSysProcessEvents; X dev->type = "MOUSE"; X dev->Name = X386Val.str; X return dev; X} X X/* X *----------------------------------------------------------------------- X * MicrosoftProc -- X * Handle the initialization, etc. of a mouse X * X * Results: X * none. X * X * Side Effects: X * none. X * X *----------------------------------------------------------------------- X */ Xstatic int XMicrosoftProc(pMouse, what) X DevicePtr pMouse; X int what; X{ X unchar map[4]; X struct termio tty; X Atom type; X X386DevicePtr pPriv = (X386DevicePtr)pMouse->devicePrivate; X X TRACE( ("MicrosoftProc(pMouse=0x%x, what=%d)\n", pMouse, what) ); X X switch (what) X { X case DEVICE_INIT: X if ((pPriv->Fd= open(pPriv->Name,O_RDONLY | O_NDELAY)) < 0) { X Error ("Cannot open MicrosoftMouse\n"); X return (!Success); } X ioctl(pPriv->Fd,TCGETA,&tty); X tty.c_iflag = IGNBRK | IGNPAR ; X tty.c_oflag = 0; X tty.c_cflag = B1200 | CS7 | CREAD | CLOCAL; X tty.c_lflag = 0; X tty.c_cc[VTIME]=0; X tty.c_cc[VMIN]=1; X ioctl(pPriv->Fd,TCSETA,&tty); X X ((MicrosoftPtr)pPriv)->state = 0; X ((MicrosoftPtr)pPriv)->emulate = 0; X ((X386MousePtr)pPriv)->buttons = 0; X ((X386MousePtr)pPriv)->x = 0; X ((X386MousePtr)pPriv)->y = 0; X pMouse->on = FALSE; X X map[1] = 3; X map[2] = 2; X map[3] = 1; X InitPointerDeviceStruct(pMouse, map, 3, GetMotionEvents, MouseCtrl, 0); X Xcifdef XINPUT X type = MakeAtom(pPriv->type, strlen(pPriv->type), FALSE); X AssignTypeAndName(pMouse, type, "Microsoft Mouse"); Xcendif X X break; X X case DEVICE_ON: X AddEnabledDevice(pPriv->Fd); X pMouse->on = TRUE; X break; X X case DEVICE_CLOSE: X case DEVICE_OFF: X RemoveEnabledDevice(pPriv->Fd); X pMouse->on = FALSE; X break; X X } X return Success; X} X X/* X *----------------------------------------------------------------------- X * MicrosoftProcessEvents -- X * Return the number of processed events. X * X * Results: X * Number of events processed. X * X * Side Effects: X * Process Events. X *----------------------------------------------------------------------- X */ Xstatic void XMicrosoftProcessEvents(pMouse) X DevicePtr pMouse; X{ X unchar rBuf[64]; X int i,nBytes; X MicrosoftPtr pPriv = (MicrosoftPtr)pMouse->devicePrivate; X X nBytes = read(((X386DevicePtr)pPriv)->Fd, rBuf, sizeof(rBuf)); X X for ( i=0; i < nBytes; i++) { X switch(pPriv->state) { X case 0: X if ((rBuf[i] & 0x40) == 0x40) { X pPriv->buttons= (rBuf[i]>>4) &0x3; X pPriv->dy = (signed char)((rBuf[i] &0xC) <<4); X pPriv->dx = (signed char)((rBuf[i] &0x3) <<6); X (pPriv->state)++; } X break; X X case 1: X (pPriv->dx) |= (signed char)(rBuf[i]&0x3F); X (pPriv->state)++; X break; X X case 2: X (pPriv->dy) |= (signed char)(rBuf[i]&0x3F); X pPriv->state = 0; X X386MouseEvent(pMouse, X ((X386MousePtr)pPriv)->buttons, pPriv->dx, pPriv->dy); X /* X * emulate the third button by the other two X */ X if (state[pPriv->buttons + pPriv->emulate][0] != -1) X X386MouseEvent(pMouse, state[pPriv->buttons+pPriv->emulate][0], 0, 0); X if (state[pPriv->buttons + pPriv->emulate][1] != -1) X X386MouseEvent(pMouse, state[pPriv->buttons+pPriv->emulate][1], 0, 0); X X pPriv->emulate = state[pPriv->buttons + pPriv->emulate][2]; X break; X } X } X} X X X/* X *----------------------------------------------------------------------- X * MicrosoftConfig -- X * Read the configuration of a MicroTrash (tm) Mouse. X * X * Results: X * A pointer to the allocated device. X * X * Side Effects: X * X *----------------------------------------------------------------------- X */ XX386DevicePtr XMicrosoftConfig() X{ X X386DevicePtr dev; X X TRACE( ("MircosoftConfig()\n") ); X X if (X386Lex(NULL) != STRING) X FatalError("Devicename expected (%d)\n",X386LineNo); X X dev = (X386DevicePtr)xalloc(sizeof(MicrosoftRec)); X dev->deviceProc = MicrosoftProc; X dev->deviceEvents = MicrosoftProcessEvents; X dev->type = "MOUSE"; X dev->Name = X386Val.str; X return dev; X} X X X/* X *----------------------------------------------------------------------- X * BusMouseProc -- X * Handle the initialization, etc. of a mouse X * X * Results: X * none. X * X * Side Effects: X * none. X * X *----------------------------------------------------------------------- X */ Xstatic int XBusMouseProc(pMouse, what) X DevicePtr pMouse; X int what; X{ X unchar map[4]; X Atom type; X X386DevicePtr pPriv = (X386DevicePtr)pMouse->devicePrivate; X X TRACE( ("BusMouseProc(pMouse=0x%x, what=%d)\n", pMouse, what) ); X X switch (what) X { X case DEVICE_INIT: X if ((pPriv->Fd= open(pPriv->Name,O_RDONLY | O_NDELAY)) < 0) { X Error ("Cannot open BusMouse\n"); X return (!Success); } X X ((BusMousePtr)pPriv)->state = 0; X ((X386MousePtr)pPriv)->buttons = 0; X ((X386MousePtr)pPriv)->x = 0; X ((X386MousePtr)pPriv)->y = 0; X pMouse->on = FALSE; X X map[1] = 3; X map[2] = 2; X map[3] = 1; X InitPointerDeviceStruct(pMouse, map, 3, GetMotionEvents, MouseCtrl, 0); X Xcifdef XINPUT X type = MakeAtom(pPriv->type, strlen(pPriv->type), FALSE); X AssignTypeAndName(pMouse, type, "Bus Mouse"); Xcendif X X break; X X case DEVICE_ON: X AddEnabledDevice(pPriv->Fd); X pMouse->on = TRUE; X break; X X case DEVICE_CLOSE: X case DEVICE_OFF: X RemoveEnabledDevice(pPriv->Fd); X pMouse->on = FALSE; X break; X X } X return Success; X} X X/* X *----------------------------------------------------------------------- X * BusMouseProcessEvents -- X * Process the good old BusMouse events. X * X * Results: X * Number of events processed. X * X * Side Effects: X * Process Events. X *----------------------------------------------------------------------- X */ Xstatic void XBusMouseProcessEvents(pMouse) X DevicePtr pMouse; X{ X unchar rBuf[64]; X int i,nBytes; X BusMousePtr pPriv = (BusMousePtr)pMouse->devicePrivate; X X nBytes = read(((X386DevicePtr)pPriv)->Fd, rBuf, sizeof(rBuf)); X X for ( i=0; i < nBytes; i++) { X switch(pPriv->state) { X case 0: X if ((rBuf[i] & 0xF8) == 0x80) { X pPriv->buttons= (>rBuf[i]) &0x7; X (pPriv->state)++; } X break; X X case 1: X (pPriv->dx) = (signed char)(rBuf[i]); X (pPriv->state)++; X break; X X case 2: X (pPriv->dy) = (signed char)(rBuf[i]); X (pPriv->state)++; X break; X X case 3: X (pPriv->state)++; X break; X X case 4: X pPriv->state = 0; X X386MouseEvent(pMouse, pPriv->buttons, pPriv->dx, -pPriv->dy); X break; X } X } X} X X X/* X *----------------------------------------------------------------------- X * BusMouseConfig -- X * Read the configuration of a BusMouse. X * X * Results: X * A pointer to the allocated device. X * X * Side Effects: X * X *----------------------------------------------------------------------- X */ XX386DevicePtr XBusMouseConfig() X{ X X386DevicePtr dev; X X TRACE( ("BusMouseConfig()\n") ); X X if (X386Lex(NULL) != STRING) X FatalError("Devicename expected (%d)\n",X386LineNo); X X dev = (X386DevicePtr)xalloc(sizeof(BusMouseRec)); X dev->deviceProc = BusMouseProc; X dev->deviceEvents = BusMouseProcessEvents; X dev->type = "MOUSE"; X dev->Name = X386Val.str; X return dev; X} X X/* X *----------------------------------------------------------------------- X * NesMouseProc -- X * Handle the initialization, etc. of a Nintendo fake mouse X * X * Results: X * none. X * X * Side Effects: X * none. X * X *----------------------------------------------------------------------- X */ Xstatic int XNesMouseProc(pMouse, what) X DevicePtr pMouse; X int what; X{ X unchar map[4]; X Atom type; X X386DevicePtr pPriv = (X386DevicePtr)pMouse->devicePrivate; X X TRACE( ("NesMouseProc(pMouse=0x%x, what=%d)\n", pMouse, what) ); X X switch (what) X { X case DEVICE_INIT: X if ((pPriv->Fd= open(pPriv->Name,O_RDONLY | O_NDELAY)) < 0) { X Error ("Cannot open NesMouse\n"); X return (!Success); } X X ((NesMousePtr)pPriv)->switches = 0; X ((NesMousePtr)pPriv)->moves = 0; X /* dda is set earlier, in NesMouseConfig */ X ((NesMousePtr)pPriv)->add = 128; X ((NesMousePtr)pPriv)->shift = 7; X ((X386MousePtr)pPriv)->buttons = 0; X ((X386MousePtr)pPriv)->x = 0; X ((X386MousePtr)pPriv)->y = 0; X pMouse->on = FALSE; X X map[1] = 3; X map[2] = 2; X map[3] = 1; X InitPointerDeviceStruct(pMouse, map, 3, GetMotionEvents, MouseCtrl, 0); X Xcifdef XINPUT X type = MakeAtom(pPriv->type, strlen(pPriv->type), FALSE); X AssignTypeAndName(pMouse, type, "NES Mouse"); Xcendif X X break; X X case DEVICE_ON: X AddEnabledDevice(pPriv->Fd); X pMouse->on = TRUE; X break; X X case DEVICE_CLOSE: X case DEVICE_OFF: X RemoveEnabledDevice(pPriv->Fd); X pMouse->on = FALSE; X break; X X } X return Success; X} X X/* X *----------------------------------------------------------------------- X * NesMouseProcessEvents -- X * Process the good old NesMouse events. X * X * Results: X * Number of events processed. X * X * Side Effects: X * Process Events. X *----------------------------------------------------------------------- X */ Xstatic void XNesMouseProcessEvents(pMouse) X DevicePtr pMouse; X{ X unchar rBuf[1024]; /* we can get lots of NES bytes */ X int i,nBytes; X NesMousePtr pPriv = (NesMousePtr)pMouse->devicePrivate; X int doinput; X X nBytes = read(((X386DevicePtr)pPriv)->Fd, rBuf, sizeof(rBuf)); X X /* Each byte is encoded as per nintendo.h. Many samples a second. */ X /* X * Decode motion linearly. With 1K read channel, we can be assured X * of pulling all current input. X */ X pPriv->dx = pPriv->dy = 0; X doinput = 0; X for ( i=0; i < nBytes; i++) { X if (rBuf[i] & NES_up) X pPriv->dy -= pPriv->add; X else if (rBuf[i] & NES_down) X pPriv->dy += pPriv->add; X if (rBuf[i] & NES_right) X pPriv->dx += pPriv->add; X else if (rBuf[i] & NES_left) X pPriv->dx -= pPriv->add; X if (rBuf[i] & NES_MOVES) X if ((rBuf[i] & NES_MOVES) == pPriv->moves) X pPriv->add += pPriv->dda; /* accelerate */ X else X pPriv->add = (1 << pPriv->shift); /* downshift */ X pPriv->moves = (rBuf[i] & NES_MOVES); X if ((rBuf[i] & (NES_SWITCHES)) != pPriv->switches) { X pPriv->buttons = 0; X if (rBuf[i] & NES_A) X pPriv->buttons |= pPriv->A; X if (rBuf[i] & NES_B) X pPriv->buttons |= pPriv->B; X if (rBuf[i] & NES_select) X pPriv->buttons |= pPriv->Select; X if (rBuf[i] & NES_start) X pPriv->buttons |= pPriv->Start; X /* X * No AB support yet. I don't like state machine system above. X * It really needs to be sub-second timer-based. X */ X pPriv->switches = rBuf[i] & (NES_SWITCHES); X doinput++; X } X X386MouseEvent(pMouse, pPriv->buttons, X pPriv->dx >> pPriv->shift, pPriv->dy >> pPriv->shift); X pPriv->dx = pPriv->dy = 0; X } X} X X X/* X *----------------------------------------------------------------------- X * NesMouseConfig -- X * Read the configuration of a NesMouse. X * X * Results: X * A pointer to the allocated device. X * X * Side Effects: X * X *----------------------------------------------------------------------- X */ X XX386DevicePtr XNesMouseConfig() X{ X NesMousePtr dev; X char *cp, *cp2, *strchr(); X int i; X X TRACE( ("NesMouseConfig()\n") ); X X if (X386Lex(NULL) != STRING) X FatalError("Devicename expected (%d)\n",X386LineNo); X X /* Reverse convention from above */ X dev = (NesMousePtr)xalloc(sizeof(NesMouseRec)); X ((X386DevicePtr)dev)->deviceProc = NesMouseProc; X ((X386DevicePtr)dev)->deviceEvents = NesMouseProcessEvents; X ((X386DevicePtr)dev)->type = "MOUSE"; X ((X386DevicePtr)dev)->Name = X386Val.str; X X /* Read in Nintendo sub-configuration */ X /* Syntax: button[,button].. " " */ X /* The keyword parser doesn't handle ,'s and I'm too lazy */ X dev->A = dev->B = dev->Start = dev->Select = dev->AB = 0; X for(i = 2; i >= 0; i--) { X if (X386Lex(NULL) != STRING) X FatalError("Nintendo button name expected (%d)\n",X386LineNo); X cp = X386Val.str; X if (cp2 = strchr(cp, ',')) X *cp2 = '\0'; X while(cp && *cp) { X if (streq(cp, "A")) { X if (dev->A) X FatalError("Nintendo A button already assigned\n"); X else dev->A = (1<B) X FatalError("Nintendo B button already assigned\n"); X else dev->B = (1<Start) X FatalError("Nintendo Start button already assigned\n"); X else dev->Start = (1<Select) X FatalError("Nintendo Select button already assigned\n"); X else dev->Select = (1<AB) X FatalError("Nintendo A+B button combination already assigned\n"); X else dev->AB = (1<dda = 0; X if ((X386Lex(NULL) == NUMBER) && (X386Val.num >= 0) && (X386Val.num <= 20)) X dev->dda = X386Val.num; X return (X386DevicePtr)dev; X} X SHAR_EOF chmod 0644 mouseX386.c || echo 'restore of mouseX386.c failed' Wc_c="`wc -c < 'mouseX386.c'`" test 21649 -eq "$Wc_c" || echo 'mouseX386.c: original size 21649, current size' "$Wc_c" fi c ============= nintendo.h ============== if test -f 'nintendo.h' -a X"$1" != X"-c"; then echo 'x - skipping nintendo.h (File already exists)' else echo 'x - extracting nintendo.h (Text)' sed 's/^X//' << 'SHAR_EOF' > 'nintendo.h' && X Xcdefine NES_right 0x01 Xcdefine NES_left 0x02 Xcdefine NES_down 0x04 Xcdefine NES_up 0x08 Xcdefine NES_start 0x10 Xcdefine NES_select 0x20 Xcdefine NES_B 0x40 Xcdefine NES_A 0x80 X Xcdefine NES_SWITCHES (NES_start | NES_select | NES_A | NES_B) Xcdefine NES_MOVES (NES_right | NES_left | NES_up | NES_down) X SHAR_EOF chmod 0644 nintendo.h || echo 'restore of nintendo.h failed' Wc_c="`wc -c < 'nintendo.h'`" test 301 -eq "$Wc_c" || echo 'nintendo.h: original size 301, current size' "$Wc_c" fi c ============= Node ============== if test -f 'Node' -a X"$1" != X"-c"; then echo 'x - skipping Node (File already exists)' else echo 'x - extracting Node (Text)' sed 's/^X//' << 'SHAR_EOF' > 'Node' && Xnes nes0 c 0 SHAR_EOF chmod 0644 Node || echo 'restore of Node failed' Wc_c="`wc -c < 'Node'`" test 13 -eq "$Wc_c" || echo 'Node: original size 13, current size' "$Wc_c" fi c ============= System ============== if test -f 'System' -a X"$1" != X"-c"; then echo 'x - skipping System (File already exists)' else echo 'x - extracting System (Text)' sed 's/^X//' << 'SHAR_EOF' > 'System' && Xnes Y 1 0 0 0 378 37f 0 0 SHAR_EOF chmod 0644 System || echo 'restore of System failed' Wc_c="`wc -c < 'System'`" test 26 -eq "$Wc_c" || echo 'System: original size 26, current size' "$Wc_c" fi c ============= Master ============== if test -f 'Master' -a X"$1" != X"-c"; then echo 'x - skipping Master (File already exists)' else echo 'x - extracting Master (Text)' sed 's/^X//' << 'SHAR_EOF' > 'Master' && Xnes I icS nes 0 XX 1 255 -1 X ^ major device number goes here SHAR_EOF chmod 0644 Master || echo 'restore of Master failed' Wc_c="`wc -c < 'Master'`" test 67 -eq "$Wc_c" || echo 'Master: original size 67, current size' "$Wc_c" fi exit 0 -- Lance THES IZ BENITO AGEN. WEE AR STUK SUMWERE IN TEKSUS. WEE AR LOST. I OFURRED THE MAP TOO CAESAR THRE TIMES, END THRICE HEE REFUZED ITT. THERFORE, I REMANED NAVIGATER. - BENITO, alt.culture.electric-midget.classics