#include <fcntl.h>

int ieee; /* holds file descriptor (handle) for IEEE I/O routines */

/***************************************************************************
 *
 *  These routines hide the incompatibility of memory references    
 *  between small and large data sizes.  When using small data,
 *  pointers are 16 bits, and contain the offset from DS.  When using
 *  large data, pointers are 32 bits, and contain both the segment and
 *  offset values.  The following routines are defined:
 *
 *  int segment(ptr) returns the segment address of the pointer, ptr.
 *  int offset(ptr) returns the offset address of ptr.
 *
 ****************************************************************************
 *
 *  Normally, the small data model versions of these routines are compiled.
 *  To use a large data model, #define largedata, or used -Dlargedata in the
 *  cc command line.
 *
 ***************************************************************************/

#ifndef largedata
/******************** SMALL DATA MODEL *************************************/
int segment(ptr)
  void *ptr;
{ static struct { int CS, SS, DS, ES; } segs = { 0, 0, 0, 0 };
  if (segs.DS==0) segread(&segs);
  return segs.DS;
}

#define offset(ptr) (int)ptr

#else
/****************** LARGE DATA MODEL ***************************************/
/* int segment(ptr)
 *    void *ptr;
 */
int segment(offs,seg)
  int offs, seg;
{ return seg; }

/* int offset(ptr)
 *   void *ptr;
 */
int offset(offs,seg)
{ return offs; }

#endif















/****************************************************************************
 *
 * ioctl_rd(fd,buf,bufsize) and ioctl_wt(fd,buf,bufsize) are just like
 * the unbuffered 'read' and 'write' routines, except that they read
 * and write to the I/O control path (the "back door") of the device.
 * See the Aztec C library manual for descriptions of 'read' and 'write'.
 *
 * These routines are used to control special features of devices by
 * providing an additional method of communicating with their driver
 * that does not transfer data to or from the device.
 *
 * ioctl_wt is used with Driver488 to send the "BREAK" command, and
 * ioctl_rd is used to read the internal state of Driver488.  See
 * Chapter 6 of the Personal488 manual for more details.
 *
 ***************************************************************************/

extern int errno;	/* Holds error code, if any */ 

int ioctl_io(handle,chars,size,iocall)
  int handle,
      size,
      iocall;
  char chars[];
{ struct { int AX, BX, CX, DX, SI, DI, DS, ES; } regs;
  int len;

  regs.AX=iocall;
  regs.BX=handle;
  regs.CX=size;
  regs.DX=offset(chars);
  regs.DS=segment(chars);
  if (sysint(0x21,&regs,&regs) & 1) {
    errno=regs.AX;
    return -1;
  } else {
    return regs.AX;
  }
}

#define ioctl_rd(handle,chars,size) ioctl_io(handle,chars,size,0x4402)
#define ioctl_wt(handle,chars,size) ioctl_io(handle,chars,size,0x4403)
 























/****************************************************************************
 *
 * cklpint() is a function that is true if light pen interrupt is pending.
 *
 * _false_() is a function that does returns zero (false).
 *
 * (*ieee_cki)() is a pointer to a function that is used during IEEE I/O to
 * check for the light pen interrupt.  It is usually set to cklpint or 
 * _false_.
 *
 * no_op() is a function that does nothing.
 *
 * (*ieee_isr)() is a pointer to a function that is executed when the
 * IEEE I/O routines detect an interrupt by checking (*ieee_cki)().
 * ieee_isr is usually set to no_op, but it may be set by the programmer
 * to point to the desired interrupt service routine.
 *
 ****************************************************************************/

int cklpint()
{ struct { int AX, BX, CX, DX, SI, DI, DS, ES; } regs;
  char buf;
  int firstresponse;

  if (ioctl_rd(ieee,&buf,1) != 1) return 0;
  if (buf != '0') return 0;
  regs.AX=0x0400; /* Function 4: check light pen status */
  sysint(0x10,&regs,&regs);
  firstresponse=regs.AX & 0xFF00;
  regs.AX=0x0400; /* Function 4: check light pen status */
  sysint(0x10,&regs,&regs);
  return ((regs.AX & 0xFF00) | firstresponse);
}

int _false_()
{ return 0; }

int (*ieee_cki)() = _false_;

void no_op()
{ }

void (*ieee_isr)() = no_op;























/****************************************************************************
 *
 * ieeewt(chars) writes the zero-terminated string to the file 'ieee' and
 * checks for errors.
 *
 * ieeerd(chars) reads chars from the file 'ieee'. 'chars' must be an array
 * so that sizeof(chars) will give the number of bytes in chars.
 *
 ***************************************************************************/ 

int _ieeewt(handle,chars)
  int handle;
  char chars[];
{ int written,
      errcode;

  if ( (*ieee_cki)() ) (*ieee_isr)();
  written=write(handle,chars,strlen(chars));
  if (written==-1) {
    errcode=errno;
    printf("\n\n_ieeewt error: 0x%x, writing \"%s\"\n\n",errcode,chars);
  }

  return written;
}


int _ieeerd(handle,chars,size)
  int handle,
      size;
  char chars[];
{ int red,
      errcode;

  red=read(handle,chars,size);
  if (red==-1) {
    errcode=errno;
    printf("\n\n_ieeerd error: 0x%x\n\n",errcode);
  }
  return red;
}

#define ieeewt(chars) _ieeewt(ieee,chars)
#define ieeerd(chars) _ieeerd(ieee,chars,sizeof(chars))






















/****************************************************************************
 *
 * ieeeprtf(format,vars) is equivalent to printf(format,vars) except that
 *   it writes to Personal488 and is limited to 10 integer (or 5 long or 
 *   2 float) vars.
 *
 * ieeescnf(format,&vars) is equivalent to scanf(format,&vars) except that
 *   it reads from Personal488 and is limited to 5 &vars.
 *
 ***************************************************************************/

int ieeeprtf(format,a,b,c,d,e,f,g,h,i,j)
char *format;
int a,b,c,d,e,f,g,h,i,j;
{ char buffer[256];
  sprintf(buffer,format,a,b,c,d,e,f,g,h,i,j);
  return ieeewt(buffer);
}

int ieeescnf(format,a,b,c,d,e)
char *format,*a,*b,*c,*d,*e;
{ char buffer[257];
  int  readst;
  if ( (readst=_ieeerd(ieee,buffer,256)) < 0 ) { return readst; }
  buffer[256]=(char)0;
  return sscanf(buffer,format,a,b,c,d,e);
}







































/*****************************************************************************
 *
 * int rawmode(handle) -- sets file described by 'handle' into raw mode.
 *
 * In raw mode control characters returned from the file are not checked.
 * In particular, control-Z does not mark the end-of-file.  This is very
 * useful when trying to read binary data from files (including the IEEE
 * driver). Returns -1 if an error occurred.
 *
 ****************************************************************************/

int rawmode(handle)
  int handle;
{ struct { int AX, BX, CX, DX, SI, DI, DS, ES; } regs;

  regs.AX=0x4400;	/* Get device data */
  regs.BX=handle;
  if (sysint(0x21,&regs,&regs) & 1) {
    errno=regs.AX;
    printf("0x4400 rawmode error: %d",regs.AX);
    return -1;
  }

  regs.DX |= 0x20;	/* Set raw mode in device attributes */
  regs.DX &= 0xFF;	/* Set DH=0 */

  regs.AX=0x4401;	/* Set device data */
  regs.BX=handle;
  if (sysint(0x21,&regs,&regs) & 1) {
    errno=regs.AX;
    printf("0x4401 rawmode error: %d",regs.AX);
    return -1;
  }
 return 0;
}































/****************************************************************************
 *
 * int ieeeinit() -- initializes Personal488 for I/O.
 *
 * Opens the external file 'ieee' for communication with Personal488,
 * sets the file into raw mode, sends IOCTL "BREAK" to get the attention
 * of Personal488, sends the "RESET" command to reset the system.  Sets
 * EOL OUT to line feed so that command strings need only be follwed by
 * \n, and sets EOL IN to NULL ($000) so that returned data is automatically
 * made into a valid string.
 *
 * Returns -1 if an error occurred.
 *
 ****************************************************************************/

int ieeeinit()
{ if ( ( (ieee=open("ieee",O_RDWR))	== -1) ||
         (rawmode(ieee)			== -1) ||
         (ioctl_wt(ieee,"break",5)	== -1) ||
         (ieeewt("reset\r\n")		== -1) ||
         (ieeewt("eol out lf\r\n")	== -1) ||
         (ieeewt("eol in $0\n")		== -1) ||
         (ieeewt("fill error\n")	== -1)) return -1;
  return 0;
}

