ftp.nice.ch/pub/next/unix/music/clm.d.tar.gz#/qp.c

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.