home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Virtual Reality Zone
/
VRZONE.ISO
/
mac
/
PC
/
PCGLOVE
/
POWER386
/
POWER386.SH
/
nes.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-12-20
|
9KB
|
426 lines
/*
* NES streams driver.
*
* Derived, through several generations, from code typed in from
* the AT&T Streams Programmer's Guide.
*
* Supports Nintendeo Entertainment System joysticks via a parallel port.
* No interrupts. No zap-gun support.
*
* Each minor number corresponds to one joystick plug. It should be possible
* to hook up a few NES gizmos via parallel port.
*
* Copyright 1991, Lance C. Norskog
*
* version 0.1 - base, running with X
* version 0.2 - switch to double-bucket input management
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/sysmacros.h>
#ifndef __GNUC__
#include <sys/inline.h>
#endif
#include <sys/errno.h>
static struct module_info rnes = {
3, "nes", 0, INFPSZ, 500, 100
};
static struct module_info wnes = {
3, "nes", 0, INFPSZ, 500, 100
};
static int nesopen(), nesclose(), neswput(), nesrsrv();
static struct qinit urqinit = { /* Upper read */
0, nesrsrv, nesopen, nesclose, NULL, &rnes, 0
};
static struct qinit uwqinit = { /* Upper write */
neswput, 0, nesopen, nesclose, NULL, &wnes, 0
};
struct streamtab nesinfo = {
&urqinit, &uwqinit, NULL, NULL
};
int nes_debug = 0, nes_notimer = 0, nes_nostrobe = 0;
#define debug(mask, x) if (nes_debug & mask) printf x;
/* 1 for upper-level, 2 for lower-level, 4 for input data */
int nes_in, /* number of input packets */
nes_inb; /* number of input bytes */
int nes_nomblk, /* number of allocb failures */
nes_nodupb, /* number of dupb failures */
nes_full, /* number of buffer-full failures */
nes_cant, /* number of canput failures */
nes_busy; /* number of bucket-busy */
int nes_bsize = 1024; /* several seconds worth */
#define NNES 1
#define NES_right 0x01
#define NES_left 0x02
#define NES_down 0x04
#define NES_up 0x08
#define NES_start 0x10
#define NES_select 0x20
#define NES_B 0x40
#define NES_A 0x80
#define NES_SWITCHES (NES_start | NES_select | NES_A | NES_B)
#define NES_MOVES (NES_right | NES_left | NES_up | NES_up)
struct nes_nes {
unsigned short ctrl_port, ctrl_clock, ctrl_reset,
data_port, data_mask,
ticks;
unsigned char canopen;
queue_t *rdq;
int timer;
int nes; /* last buttons */
int switches;
mblk_t *mp, *other; /* pointer to input blocks */
} nes_nes[NNES] = {
{
0x378,
1,
2,
0x379,
0x10,
HZ,
}
};
typedef struct nes_nes nes_t;
int nnes = NNES;
int nes_c1 = 20;
int nes_c2 = 20;
int nes_c3 = 20;
int nes_c4 = 20;
int nes_c5 = 20;
/* move to space.c */
nesinit() {
int i, hz;
for(i=0; i<nnes; i++) {
nes_nes[i].rdq = (queue_t *) 0;
nes_nes[i].canopen = 1;
nes_strobe_init(&nes_nes[i]);
hz = HZ / nes_nes[i].ticks;
nes_nes[i].ticks = ((hz == 0) ? 1 : hz);
nes_nes[i].switches = 0;
}
}
nesopen(q, dev, flag, sflag)
queue_t *q;
{
nes_t *nes;
int nes_timer();
if (sflag == CLONEOPEN) {
return OPENFAIL;
/* for (dev = 0; dev < nnes; dev++)
if (nes_nes[dev].rdq == (queue_t *) 0)
break; */
} else dev = minor(dev);
if (dev > nnes)
return OPENFAIL;
nes = &nes_nes[dev];
if (nes->rdq)
return dev;
/* q_ptr is the q's repository for our per-chan structure */
q->q_ptr = (caddr_t) nes;
WR(q)->q_ptr = (caddr_t) nes;
nes->rdq = q;
debug(1, ("Line %d\n", __LINE__));
/* Start timer */
if (! nes_notimer)
nes->timer = timeout(nes_timer, q, nes->ticks);
nes->mp = allocb(nes_bsize, BPRI_LO);
nes->other = allocb(nes_bsize, BPRI_LO);
if (! nes->mp) nes_nomblk++;
debug(1, ("Line %d\n", __LINE__));
return dev;
}
static
nesclose(q)
queue_t *q;
{
nes_t *nes = (nes_t *) q->q_ptr;
mblk_t *mp;
debug(1, ("Line %d\n", __LINE__));
untimeout(nes->timer);
nes->rdq = (queue_t *) 0;
if (nes->mp)
freemsg(nes->mp);
if (nes->other)
freemsg(nes->other);
nes->mp = (mblk_t *) 0;
nes->other = (mblk_t *) 0;
nes_strobe_init(nes);
debug(1, ("Line %d\n", __LINE__));
}
/* This is called when, and only when, the above reader can read. */
/*
* Double-bucket method:
* Only send up when other bucket is finished processing.
* Always duplicate buffers before sending them up.
*/
nesrsrv(q)
queue_t *q;
{
nes_t *nes = (nes_t *) q->q_ptr;
mblk_t *mp;
int can, bytes = 0;
debug(1, ("Line %d\n", __LINE__));
if (!nes->mp)
return;
if (nes->other && (nes->other->b_datap->db_ref > 1)) {
nes_busy++;
return;
}
debug(1, ("Line %d\n", __LINE__));
if (! (bytes = (nes->mp->b_wptr > nes->mp->b_rptr)))
return;
can = canput(q->q_next);
debug(1, ("Line %d\n", __LINE__));
if (! can) {
nes_cant++;
return;
}
if (!(mp = dupb(nes->mp))) {
nes_nodupb++;
return;
}
debug(1, ("Line %d\n", __LINE__));
/* send up copy of current bucket */
nes_in++;
nes_inb += mp->b_wptr - mp->b_rptr;
putnext(q, mp);
/* Swap buckets */
mp = nes->other;
nes->other = nes->mp;
nes->mp = mp;
debug(1, ("Line %d\n", __LINE__));
if (!nes->mp)
nes->mp = allocb(nes_bsize, BPRI_LO);
if (! nes->mp)
nes_nomblk++;
else /* reset read/write pointers of recycled bucket */
nes->mp->b_rptr = nes->mp->b_wptr = nes->mp->b_datap->db_base;
}
/* This is called strioctl(), whether or not it's there! */
neswput(q, mp)
queue_t *q;
mblk_t *mp;
{
nes_t *nes = (nes_t *) q->q_ptr;
switch(mp->b_datap->db_type) {
case M_IOCTL:
/* process */
mp->b_datap->db_type = M_IOCNAK;
qreply(q, mp);
break;
/* flush handling must be here */
case M_FLUSH:
if (*mp->b_rptr & FLUSHW)
flushq(q, FLUSHDATA);
if (*mp->b_rptr & FLUSHR) {
flushq(RD(q), FLUSHDATA);
*mp->b_rptr &= ~FLUSHW;
qreply(q, mp);
} else
freemsg(mp);
break;
default:
freemsg(q, mp);
}
}
nes_timer(q)
queue_t *q;
{
nes_t *nes = (nes_t *) q->q_ptr;
unsigned char bits, nes_strobe();
mblk_t *tmp;
if (nes_notimer)
goto resched;
debug(2, ("Line %d\n", __LINE__));
if (!nes->mp && !(nes->mp = allocb(nes_bsize, BPRI_LO))) {
nes_nomblk++;
goto resched;
}
debug(2, ("Line %d\n", __LINE__));
if (nes->mp->b_wptr == nes->mp->b_datap->db_lim) {
nes_full++;
goto resched;
}
debug(2, ("Line %d\n", __LINE__));
bits = nes_strobe(nes);
if (((bits & NES_right) && (bits & NES_left)) ||
((bits & NES_up) && (bits & NES_down))) {
/* skip */
} else {
debug(2, ("Line %d\n", __LINE__));
/* report on movement or change in switches */
if (bits || ((bits & (NES_SWITCHES)) != nes->switches)) {
*(nes->mp->b_wptr++) = bits;
debug(2, ("Line %d\n", __LINE__));
qenable(nes->rdq);
nes->switches = (bits & NES_SWITCHES);
}
debug(2, ("Line %d\n", __LINE__));
}
resched:
nes->timer = timeout(nes_timer, q, nes->ticks);
}
/*
* Put port in quiescent state
*/
nes_strobe_init(nes)
nes_t *nes;
{
int i, j;
if (nes_nostrobe)
return;
for(i = 0; i < 8; i++)
if ((1<<i) & nes->data_mask)
break;
if (i == 8) {
nes->canopen = 0;
printf("NES%d: data_mask not set!\n", nes - nes_nes);
}
outb(nes->ctrl_port, 0);
outb(nes->data_port, 0);
}
#define hold(clk) for(slow = clk; slow; slow--);
/* Actually strobe the NES and add events to the record */
unsigned char
nes_strobe(nes)
nes_t *nes;
{
int i, slow;
unsigned char data;
unsigned short bits = 0; /* not char! */
debug(2, ("Line %d\n", __LINE__));
if (nes_nostrobe)
return 0;
/*
#ifndef __GNUC__
intr_disable();
#endif
*/
debug(2, ("Line %d\n", __LINE__));
outb(nes->ctrl_port, nes->ctrl_reset | nes->ctrl_clock);
hold(nes_c1);
outb(nes->ctrl_port, nes->ctrl_clock);
hold(nes_c2);
for(i = 0; i < 8; i++) {
data = inb(nes->data_port);
bits |= (data & nes->data_mask);
hold(nes_c3);
outb(nes->ctrl_port, 0);
hold(nes_c4);
outb(nes->ctrl_port, nes->ctrl_clock);
hold(nes_c5);
bits <<= 1;
}
debug(2, ("Line %d\n", __LINE__));
/* bits now straddles word, is upwards by one */
/* downshift into place */
for(i = 0; i < 8; i++) {
bits >>= 1;
if (nes->data_mask & (1 << i))
break;
}
debug(2, ("Line %d\n", __LINE__));
/*
#ifndef __GNUC__
intr_restore();
#endif
*/
debug(2, ("Line %d\n", __LINE__));
bits = ~bits;
if (bits && (nes_debug & 4))
printf("Input: 0x%x\n", bits & 0x1ff);
return (unsigned char) bits;
}
static char *
M_string(type)
{
switch(type) {
case M_DATA: return "M_DATA";
case M_PROTO: return "M_PROTO";
case M_BREAK: return "M_BREAK";
case M_PASSFP: return "M_PASSFP";
case M_SIG: return "M_SIG";
case M_DELAY: return "M_DELAY";
case M_CTL: return "M_CTL";
case M_IOCTL: return "M_IOCTL";
case M_SETOPTS: return "M_SETOPTS";
case M_IOCACK: return "M_IOCACK";
case M_IOCNAK: return "M_IOCNAK";
case M_PCPROTO: return "M_PCPROTO";
case M_PCSIG: return "M_PCSIG";
case M_FLUSH: return "M_FLUSH";
case M_STOP: return "M_STOP";
case M_START: return "M_START";
case M_HANGUP: return "M_HANGUP";
case M_ERROR: return "M_ERROR";
default: return "M_UNKNOWN";
}
}
nesintr() {;}
/* Don't digest it here.
if (bits & NES_right)
nes->right++;
if (bits & NES_left)
nes->left++;
if (bits & NES_up)
nes->up++;
if (bits & NES_down)
nes->down++;
if (bits & NES_A)
nes->A = 1;
if (bits & NES_B)
nes->B = 1;
*/