This is qp.c in view mode; [Download] [Up]
/* QP (QuintProcessor) board support stuff
*
* for system 3.0 NeXTs
* on system 2.0, look for "system 2.0" and follow the directions
*
* Basic library of code supplied by Ariel is in /dist/QPdist/lib/source.
* The following is a translation of their code to our way of doing things.
* (All we want is low-level access to each host interface, and a way to reset each dsp).
*
*/
#include "stdio.h"
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
/* #include <sys/errno.h> */ /* this line not needed in system 1.0? */
/* on system 2.0, un-comment-out the previous line */
extern int errno;
/* on system 2.0, comment out the previous line */
#include "/NextDeveloper/Headers/bsd/sys/errno.h"
/* on system 2.0, comment out the previous line -- last of 2.0/3.0 changes */
/* #include "/dist/QPdist/lib/source/ndslot.h" */ /* also exists on clm distribution area */
#include "ndslot.h"
#include <signal.h>
#include <setjmp.h>
#include <libc.h>
#ifndef AKCL
#include "clmdsp.h" /* <dsp/dsp.h> and hostInterface declaration */
#endif AKCL
/* 1.0 -- #define QP_ID 0x8001 */
#define QP_ID 0x80018000
/* QP.h */
#define RST_NMI 0x803 /* 1.0 -- 0x200 */
/* RESERVED/SIMULT was 0x100, now 0x403 etc -- data in byte 3 in system 2.0 */
static int got_interrupt = 0;
int QP_get_interrupt(void) {return got_interrupt;}
QP_clear_interrupt(void) {got_interrupt = 0;}
static jmp_buf err_buf;
static void sys_err(int sig)
{
got_interrupt = sig;
longjmp(err_buf,sig); /* I believe this is C's non-local GOTO */
}
static int dspFd = -1;
static int current_slot = -1;
static int current_dsp = -1;
static char *slotbase;
static QPRegs *hi[16]; /* array of host interface structures */
static void QP_close_driver(void)
{
if (dspFd != -1)
{
ioctl(dspFd,SLOTIOCDISABLE,(void *) 0);
close(dspFd);
dspFd = -1;
}
}
static int QP_reset_dsp(int dsp)
{
void (*old_SIGBUS)();
/* signals in unix are handled with int (*signal (int sig void (*func)(int))) int, if that makes sense */
/* (i.e. signal is a function that returns a pointer to a function that returns an integer) */
/* SIGBUS is bus error. SIGSEGV is segmentation violation */
void (*old_SIGSEGV)();
char val;
int i;
/* extern void usleep(unsigned int useconds); */
/* It is my belief that usleep is incompatible with Lisp's Trace function */
/* -- with usleep we often hang for no apparent reason during qp-boot */
/* here we are apparently preparing to catch a bus error or a segmentation error. We set the new handler */
/* to be sys_err, which does a jump to the last destination set by setjmp (this is pure conjecture) */
/* signal returns the previous handler so that we can restore it later */
old_SIGBUS = signal(SIGBUS,sys_err);
old_SIGSEGV = signal(SIGSEGV,sys_err);
if (setjmp(err_buf) != 0) /* this (I believe) sets the destination of a subsequent longjmp */
{ /* if passed a signal at the longjmp, this returns non-zero (apparently) */
/* so we get here if we got an interrupt. We then restore the old handlers */
/* and return 1. */
(void) signal(SIGBUS,old_SIGBUS);
(void) signal(SIGSEGV,old_SIGSEGV);
return(1);
}
val = ~(1 << (dsp+3)); /* RESET is active low */
/* val = ~(1 << (dsp+5)); */ /* dsp is 0..4 here (Ariel doc p59) */
*((unsigned char volatile *)(slotbase+RST_NMI)) = val;
/* usleep(10000); */ /* this is a VERY long wait -- surely it's not necessary ?!? */
/* at power on, the on-chip oscillator can take 3.75 ms to stabilize */
/* but NeXT takes 1000's of times longer than that to get going. */
/* Thereafter, this stabilization happens only when we restart (via reset) after */
/* executing a STOP command, which will never happen in clm -- I guarantee it! */
/* I also never use a WAIT or SWI command, in case it matters */
/* usleep(100); */
for (i=0;i<1000;i++);
*((unsigned char volatile *)(slotbase+RST_NMI)) = 0xFF;
(void) signal(SIGBUS,old_SIGBUS);
(void) signal(SIGSEGV,old_SIGSEGV);
return(0);
}
static int QP_open_driver(char *name) /* name == "/dev/slot2" for example */
{
struct stat stbuf;
if (stat(name,&stbuf) == -1) return(1);
if ((stbuf.st_mode & S_IFCHR) == 0) return(2); /* is it a "character special" device */
if ((dspFd=open(name,O_RDWR)) == -1) return (3); /* no driver? */
/* slotbase = 0; */ /* this line causes the QP to be flakey on 3.2! */
if (ioctl(dspFd,SLOTIOCGADDR_CI,(void *) &slotbase) == -1)
{
QP_close_driver();
return(4); /* can't map address */
}
if (ioctl(dspFd,SLOTIOCNOSTOFWD,(void *) 0) == -1)
{
QP_close_driver();
if (errno == ENXIO) /* sys/errno.h => no such device */
return(5); /* new driver, but no NBIC */
else return(6); /* old QP driver somehow */
}
return(0);
}
static int QP_open_normal_driver_for_slot(int slot)
{
int i;
switch (slot)
{
case 2: /* slot2 (normal address space) */
i = QP_open_driver("/dev/slot2");
break;
case 4:
i = QP_open_driver("/dev/slot4");
break;
case 6:
i = QP_open_driver("/dev/slot6");
break;
}
return(i);
}
static int QP_open_s_driver_for_slot(int slot)
{
int i;
switch (slot)
{
case 2:
i = QP_open_driver("/dev/slots2"); /* try to open slots 2 (i.e. slot space where MFG code is and so on) */
break; /* see Ariel doc p56 or maybe NextBus Specification */
case 4:
i = QP_open_driver("/dev/slots4");
break;
case 6:
i = QP_open_driver("/dev/slots6");
break;
}
return(i);
}
static int QP_get_board_id(unsigned *bdId)
{
void (*old_SIGBUS)();
void (*old_SIGSEGV)();
old_SIGBUS = signal(SIGBUS,sys_err);
old_SIGSEGV = signal(SIGSEGV,sys_err);
if (setjmp(err_buf) != 0)
{
(void) signal(SIGBUS,old_SIGBUS);
(void) signal(SIGSEGV,old_SIGSEGV);
return(1);
}
*bdId = *((unsigned char volatile *)(slotbase + 0xFFFFF0)); /* msbyte of Mfg Code */
*bdId <<= 8;
*bdId |= *((unsigned char volatile *)(slotbase + 0xFFFFF4)); /* lsbyte of Mfg Code */
/* 1.0 -- stopped here. Next 4 lines for 2.0 */
*bdId <<= 8;
*bdId |= *((unsigned char volatile *)(slotbase + 0xFFFFF8)); //msbyte of Board ID
*bdId <<= 8;
*bdId |= *((unsigned char volatile *)(slotbase + 0xFFFFFc)); //lsbyte of Board ID
/* 2.0 -- end of addition (board ID) */
(void) signal(SIGBUS,old_SIGBUS);
(void) signal(SIGSEGV,old_SIGSEGV);
return(0);
}
static void QP_align_slot_offsets(int slot)
{
int i;
for (i=0;i<5;i++) /* set up dsp host interface pointers */
{ /* slotbase set as side effect of QP_open_driver */
/* 1.0 -- hi[i+((slot/2-1)*5)+1]=(DSPRegs *)(slotbase+(8<<i)); */
hi[i+((slot/2-1)*5)+1]=(QPRegs *)(slotbase+(32<<i));
}
}
static int QP_check_slot(int slot) /* check a slot to see if it has a QP board */
{
int i;
unsigned boardID;
signal(SIGBUS,SIG_DFL); /* turn on bus error signal */
/* Is this really what we want? What is the default (SIG_DFL) handler for a bus error? */
i=QP_open_s_driver_for_slot(slot);
if (i != 0)
{
signal(SIGBUS,SIG_IGN); /* SIG_IGN = ignore (bus) error */
return(i+(slot-1)*100); /* no slots2 driver here (or whatever) */
}
i = QP_get_board_id(&boardID); /* is it QP board? */
QP_close_driver(); /* don't need slots2 anymore */
signal(SIGBUS,SIG_IGN); /* turn off bus error */
if (i == 0) /* found board ID */
{
/* boardID = (boardID & 0xFFFF); */ /* just MFG ID */
if (boardID == QP_ID) /* hooray -- it's a QP board in this slot */
{
i = QP_open_normal_driver_for_slot(slot);
if (i != 0) return(i+slot*100); /* no slot2 driver (or whatever) */
QP_close_driver(); /* got what we needed... */
return(0); /* 0 returned only if successfully mapped dsps */
}
}
return(-1); /* no QP here, or some other problem */
}
int QP_check_all_slots(int *slots) /* check all three slots for QP boards */
{ /* returns 0=some QP board found; 1=no QP boards */
int i;
for (i=1;i<=16;i++)
{
hi[i]=NULL; /* the usual paranoia ... */
}
slots[0] = QP_check_slot(2);
slots[1] = QP_check_slot(4);
slots[2] = QP_check_slot(6);
if ((slots[0] == 0) || (slots[1] == 0) || (slots[2] == 0))
return(0);
return(1);
}
static int QP_prepare_dsp(int slot, int dsp) /* slot: 2, 4, or 6; dsp: 0..4 */
{
int i;
if (current_slot != slot) /* gotta close old driver (if any), open new */
{
if (current_slot != -1) /* old driver still active? */
QP_close_driver();
current_slot = slot;
i = QP_open_normal_driver_for_slot(slot);
if (i != 0)
{
current_slot = -1;
return(i+slot*100);
}
QP_align_slot_offsets(slot); /* get memory map to host interface of each dsp */
}
current_dsp = dsp;
return(dsp+((slot/2-1)*5)+1); /* returns hi relative number for this dsp */
}
QP_all_done(void)
{
if (current_slot != -1) QP_close_driver();
current_slot = -1;
current_dsp = -1;
}
/* Y:FFFF has DRAM size bits (4 and 5)
* 0 0 = unused
* 0 1 = 1M
* 1 0 = 4M
* 1 1 = 256K
* see qp.lisp qp-dram-size
*/
/* to tell whether it's 16K or 64K, write above 32K, read back, if same = 64K, if 70000F = 16K */
/* to set auto-refresh for DRAM, set bit 7 of Y:FFFA in master (BSET 7 Y FFFA or whatever) */
int QP_boot_dsp(int slot, int dsp, int end, int *program, int monend, int memorymap)
{
int hi_dsp;
int i;
int *j,*pend;
hi_dsp = QP_prepare_dsp(slot,dsp);
if ((hi_dsp > 16) || (hi_dsp < 0)) return(hi_dsp);
qp_HI = hi[hi_dsp];
put_qp_icr(0);
i = QP_reset_dsp(dsp);
if (i != 0) return(i+1000);
/* now edit the monitor for QP use (the incoming monitor sets up the built-in dsp) */
if (dsp == 4) /* 0..3 are the slaves */
{
/* set IO wait states to 1 */
/* STORE 1 X-IO #xFFFE */
program[monend] = 0x8F4BE;
program[monend+1] = 1;
/* enable master's DSPRAM */
/* BSET 1 Y #xFFFA */
program[monend+2] = 0xA7061;
program[monend+3] = 0xFFFA;
/* BSET 1 Y-IO #xFFFA is #xABA61 and 1 */
}
else
{
/* turn on external RAM */
/* #xFFC2 <- #xe00000 -- i.e. set AMODE bits, set handshake reset flag */
/* LOAD A #xe00000 */
program[monend] = 0x56f400;
program[monend+1] = memorymap;
/* STORE A Y #xFFC2 */
program[monend+2] = 0x5e7000;
program[monend+3] = 0xFFC2;
/* memory map (bits 21 and 22 of FFC2)
* 0 x = no ext p mem, vector mem
* 1 0 = ext p, scalar mem
* 1 1 = ext p, vector mem
* bit 23 = enable handshake flags
*/
}
pend = program + end; /* load monitor */
for (j=program;j<=pend;j++)
put_qp_tx(*j);
put_qp_icr(8);
for (i=0;i<1000;i++); /* Ariel software waits here for some reason */
for (i=0;i<4;i++) program[monend+i]=0;
return(0);
}
/* we use next56.c to deal with these guys -- just set hostInterface to whichever dsp we want to talk to */
/* hi_dsp[0] = main 56000 */
int QP_set_current_dsp (int slot, int dsp)
{
if (slot != 0)
{
int hi_dsp;
hi_dsp = QP_prepare_dsp(slot,dsp); /* just in case we are changing slots */
if ((hi_dsp > 16) || (hi_dsp < 0)) return hi_dsp;
qp_HI = hi[hi_dsp];
}
else return 0;
return -1;
}
int QP_is_open (void)
{
if (dspFd == -1) return(0);
return (-1);
}
QP_report_hi (int *his)
{
int i;
for (i=1;i<16;i++)
{
his[i]=(int)hi[i];
}
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.