/*
 * (c) Ivan Popov 1998
 *
 * Init a soundcard with Yamaha Y701 chip (OPL3-SA)
 *
 * A quick hack, but it works.
 * Reasonably self-documented :)
 *
 * Please mention my name, otherwise do what you want with this program.
 *
 * For those who do not know - the language below is Esperanto.
 *
 * Tiu cxi programo inicas sonkarton kun Yamaha-701 mikrocirkvito.
 *
 * La parametroj estas sekvaj:
 *
 * wss-adreso:     unu el 0x530,0xe80,0xf40,0x604,-
 * mpu-adreso:     unu el 0x330,0x332,0x334,0x300,-
 * mpu-interrompo: unu el 5,7,9,10
 * sb-adreso:      unu el 0x220,0x240,-
 * sb-interrompo:  unu el 5,7,9,10,11
 * sb-dma:         unu el 0,1,3
 *
 *
 * Ivan Popov, oktobro 1998
 */

#define IOFILE "/dev/port"

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <unistd.h>

#include <stdio.h>

static int ports;

static unsigned int
i8( int port )
{
  char v8 = 0x17;

  if( lseek(ports, (off_t) port, SEEK_SET) == -1 ){
    perror("8-bit port lseek error");
    exit( 2 );
  }
  if( read(ports,&v8,1) != 1 ){
    perror("8-bit port read error");
    exit( 2 );
  }
  return (unsigned int) (unsigned char) v8;
}

static void
o8( int port, int value )
{
  char v8 = value;

  if( lseek(ports, (off_t) port, SEEK_SET) == -1 ){
    perror("8-bit port lseek error");
    exit( 2 );
  }
  if( write(ports,&value,1) != 1 ){
    perror("8-bit port write error");
    exit( 2 );
  }
}

static void
o16( int port, int value )
{
  char v16[2];

  v16[0] = (value & 0xff);
  v16[1] = ((value >> 8) & 0xff);

  if( lseek(ports, (off_t) port, SEEK_SET) == -1 ){
    perror("16-bit port lseek error");
    exit( 2 );
  }
  if( write(ports,&value,2) != 2 ){
    perror("16-bit port write error");
    exit( 2 );
  }
}

static void
mperror( char *s )
{
  fprintf( stderr, "%s\n", s );
  exit( 1 );
}

/**************************************/

int
opl3sa_read(int addr)
{
  o8( 0xf86, 0x1d ); /* password */
  o8( 0xf86, addr ); /* address */
  return i8( 0xf87 ); /* data */
}

void
opl3sa_write(int addr, int value)
{
  o8( 0xf86, 0x1d ); /* password */
  o8( 0xf86, addr ); /* address */
  o8( 0xf87, value ); /* data */
}

/**************************************/

main(int argc, char **argv)
{
  char *p;
  int wss_value, mpu_value, mpu_irq_value, sb_value, sb_irq_value, dma_value;
  int wss_flag, mpu_flag, sb_flag;

  if( argc != 7 ){
    mperror("exactly 6 arguments required:\n\
 wss_io=xxx mpu_io=xxx mpu_irq=xxx sb_io=xxx sb_irq=xxx sb_dma=xxx");
  }

  p = argv[1];
  wss_flag = 1;
  if(        !strcmp(p,"wss_io=0x530") ){
    wss_value = 0x24;
  } else if( !strcmp(p,"wss_io=0xe80") ){
    wss_value = (0x24|0x08);
  } else if( !strcmp(p,"wss_io=0xf40") ){
    wss_value = (0x24|0x10);
  } else if( !strcmp(p,"wss_io=0x604") ){
    wss_value = (0x24|0x18);
  } else if( !strcmp(p,"wss_io=-") ){
    wss_flag = 0;
  } else {
    mperror("wss_io value must be one of 0x530,0xe80,0xf40,0x604,-");
  }

  p = argv[2];
  mpu_flag = 1;
  if(        !strcmp(p,"mpu_io=0x330") ){
    mpu_value = 0x00;
  } else if( !strcmp(p,"mpu_io=0x332") ){
    mpu_value = 0x20;
  } else if( !strcmp(p,"mpu_io=0x334") ){
    mpu_value = 0x40;
  } else if( !strcmp(p,"mpu_io=0x300") ){
    mpu_value = 0x60;
  } else if( !strcmp(p,"mpu_io=-") ){
    mpu_flag = 0;
  } else {
    mperror("mpu_io value must be one of 0x330,0x332,0x334,0x300,-");
  }

  if( mpu_flag ){
    p = argv[3];
    if(        !strcmp(p,"mpu_irq=5") ){
      mpu_irq_value = 1;
    } else if( !strcmp(p,"mpu_irq=7") ){
      mpu_irq_value = 2;
    } else if( !strcmp(p,"mpu_irq=9") ){
      mpu_irq_value = 3;
    } else if( !strcmp(p,"mpu_irq=10") ){
      mpu_irq_value = 4;
    } else {
      mperror("mpu_irq value must be one of 5,7,9,10");
    }
  }

  p = argv[4];
  sb_flag = 1;
  if(        !strcmp(p,"sb_io=0x220") ){
    sb_value = 0x00;
  } else if( !strcmp(p,"sb_io=0x240") ){
    sb_value = 0x20;
  } else if( !strcmp(p,"sb_io=-") ){
    sb_flag = 0;
  } else {
    mperror("sb_io value must be one of 0x220,0x240,-");
  }

  if( sb_flag ){
    p = argv[5];
    if(        !strcmp(p,"sb_irq=5") ){
      sb_irq_value = 1;
    } else if( !strcmp(p,"sb_irq=7") ){
      sb_irq_value = 2;
    } else if( !strcmp(p,"sb_irq=9") ){
      sb_irq_value = 3;
    } else if( !strcmp(p,"sb_irq=10") ){
      sb_irq_value = 4;
    } else if( !strcmp(p,"sb_irq=11") ){
      sb_irq_value = 5;
    } else {
      mperror("sb_irq value must be one of 5,7,9,10,11");
    }

    p = argv[6];
    if(        !strcmp(p,"sb_dma=0") ){
      dma_value = 1;
    } else if( !strcmp(p,"sb_dma=1") ){
      dma_value = 2;
    } else if( !strcmp(p,"sb_dma=3") ){
      dma_value = 3;
    } else {
      mperror("sb_dma value must be one of 0,1,3");
    }
  }

  if( (ports=open(IOFILE,O_RDWR)) == -1 ){
    perror(IOFILE);
    exit( 1 );
  }

  opl3sa_write(0x01, 0x00);       /* Disable MSS */
  opl3sa_write(0x02, 0x00);       /* Disable SB */
  opl3sa_write(0x03, 0x00);       /* Disable MPU */

  if ( wss_flag ){                        /* Setup MSS */
    opl3sa_write(0x01, wss_value);
  }

  if( sb_flag ){                          /* Setup SB */
    opl3sa_write(0x02, 0x40|sb_value|(sb_irq_value<<2)|dma_value);
  }

  if( mpu_flag ){                         /* Setup MPU */
    mpu_value |= 0x83; /* MPU & OPL3 (synth) & game port enable */
    mpu_value |= mpu_irq_value << 2;
    opl3sa_write(0x03, mpu_value);
  }

  return 0;
}
