/**
*** Includes ************************************
**/

#pragma inline

#include <stdlib.h>
#include <conio.h>
#include <alloc.h>
#include <dos.h>
#include <io.h>
#include <fcntl.h>

#define SOUNDPLAYER 0
#define SOUNDBLASTER 1

#include "fades.c"
#include "ems.c"
#include "clock.c"
#include "screen.c"
#include "misc.c"
#include "compress.c"

/**
*** Global variables ****************************
**/

char       *ConvMemoryPointer       ;
char       *ConvSeg[9]              ;
char       *EMSMemory               ;
char       *EMSMemoryPointer        ;
int         EMSPage                 ;
int         EMSHandle               ;
long        total_samples           ;
long        filesize_hbytes         ;
int         Segment                 ;
int         port       = 0x378      ;
int         looping                 ;
int         Done                    ;
int         Compression             ;
char        cb                      ;
char        cc                      ;
char        skip                    ;
char        Device = 66             ;
int         accum = 0x80            ;
int         base_write              ;
int         base_dsp                ;
unsigned    samplerate   = 11025    ;

char       *Dos4Z = 
  "DOS/4SW Professional Real Mode Run-time  Version .99\r\n"
  "Copyright (c) Nullsoft, Inc. 1995\r\n";
char       *Help = 
  "Syntax: \n\r"                                             
  "   DACE -f FileName -r SampleRate -d Device [-p Port] [-noems] [-c] [-l]\r\n"
  "-p Port is device baseport in hex\n\r"
  "-d Device can be 0, 1, or 2:\n\r"
  "   0: Soundplayer, port defaults to 378h\n\r"
  "   1: Soundblaster, port defaults to 220h\n\r"
  "-noems disables EMS usage\n\r"
  "-l enables looping\n\r" 
  "-c for 2:1 realtime decompression\r\n";

char      *DacPlayer = "DAC Player 2.0 by Nullsoft";

/**
*** Interrupt handlers **************************
**/

void interrupt (*new_int)();
void interrupt conv_int() {
  static unsigned int_count;
  if (!Done) {
    if (Compression) {
      if (!skip) {
        int_count++;
        cc = *(ConvMemoryPointer++);
        accum -= shift_table[(cc >> 4) & 0x0f];
        cb = (((accum) < 0) ? 0 : ((accum) > 255) 
          ? 255 : accum);
        skip++;
      }
      else {
        accum -= shift_table[cc & 0x0f];
        cb = (((accum) < 0) ? 0 : ((accum) > 255) 
          ? 255 : accum);
        skip--;
      }
    } else {
      cb = *(ConvMemoryPointer++);
      int_count++;
    }
    if (Device == SOUNDBLASTER) {
      asm mov dx, base_write
      sbl1:
      asm {
        in  al, dx
        test al, 80h
        jnz sbl1 
        mov al, 10h
        out dx, al 
      }
      sbl2:
      asm {
        in  al, dx
        test al, 80h
        jnz sbl2
        mov al, cb
        out dx, al 
      }
    } else
      asm {
        mov al, cb
        mov dx, port
        out dx, al
      }
    if ( !skip && int_count == 65000) 
      ConvMemoryPointer = ConvSeg[Segment++];
    total_samples++;
    if (total_samples == filesize_hbytes) {
      if (!looping) Done++;
      total_samples = 0;
      accum = 0x80;
      Segment = 1;
      ConvMemoryPointer = ConvSeg[0];
    }
  }
  clock_counter++;
  if (clock_counter >= clock_top) {
    clock_counter = 0;
    old_clock_int();
  }
  else {
    asm mov al, 20h
    asm out 20h, al
  }
}

void interrupt EMS_int() {
  static int int_samples;
  if (!Done) {
    if (Compression) {
      if (!skip) {
        cc = *(EMSMemoryPointer++);
	int_samples++;
        accum -= shift_table[(cc >> 4) & 0x0f];
        cb = (((accum) < 0) ? 0 : ((accum) > 255) 
          ? 255 : accum);
        skip ++;
      }
      else {
        accum -= shift_table[cc & 0x0f];
        cb = (((accum) < 0) ? 0 : ((accum) > 255) 
          ? 255 : accum);
        skip --;
      }
    } else { 
      cb = *(EMSMemoryPointer++);
      int_samples++;
    }
    if (Device == SOUNDBLASTER) {
      asm mov dx, base_write
      sbl1:
      asm {
        in  al, dx
        test al, 80h
        jnz sbl1
        mov al, 10h
        out dx, al 
      }
      sbl2:
      asm {
        in  al, dx
        test al, 80h
        jnz sbl2
        mov al, cb
        out dx, al 
      }
    } else 
      asm {
        mov dx, port
        mov al, cb
        out dx, al
      }
    if ( !skip && int_samples == EMSPAGESIZE) {
      asm {
        mov ax, word ptr EMSMemory
        mov word ptr EMSMemoryPointer, ax
        mov int_samples, 0
      }
      EMSPage++;
      ems2conv(EMSPage, EMSHandle,EMSMemory);
    }
    total_samples++;
    if (total_samples == filesize_hbytes) {
      if (!looping) Done++;
      accum = 0x80;
      total_samples = 0;
      EMSPage = 0;
      EMSMemoryPointer = EMSMemory; 
      ems2conv(EMSPage, EMSHandle,EMSMemory);
    }
  }
  clock_counter++;
  if (clock_counter >= clock_top) {
    clock_counter = 0;
    old_clock_int();
  }
  else {
    asm mov al, 20h
    asm out 20h, al
  }
}


/**
*** Main () *************************************
**/


#include "osc.c"

void main(int argc, char *argv[]) {

/*
** Variables
*/

  static char   isTrunc               ;
  long          filesize_bytes        ;
  char          numsegs               ;
  int           tempint               ;
  char         *filename = (char *) 
                           malloc(128);
  int           filenumber            ;
  static char   usage                 ;
  int           i                     ;
  char          portset = 0           ;
  char          isEMS = 1             ;
  long          temptick              ;
  long 		templong              ;

/*
** Code begin  (code_begin)
*/

  cprintf("%s",Dos4Z);
  temptick = *((long far *)0x46CL) + 7;
  while (*((long far *)0x46CL) < temptick);
  
  cprintf("\r\n%s\r\n",DacPlayer);
  ctrlbrk(Break_Handler);
  directvideo = 1;
  *filename = 0;

/*
** Command line parsing
*/

  for (i = 1; i < argc; i++)
    if (argv[i][0] != '-' && argv[i][0] != '/') usage++;
    else switch (*(++argv[i])) {
      case 'C': case 'c':
        Compression++;
      break;
      case 'F': case 'f':
        fullFilename(argv[++i], filename);
      break;
      case 'R': case 'r':
        samplerate = (long) atol(argv[++i]);
      break;
      case 'P': case 'p':
        portset ++;
        port = strtol(argv[++i], &argv[i+1] - 1,16);
      break;
      case 'L': case 'l':
        looping++;
      break;
      case 'D': case 'd':
        Device = atoi(argv[++i]);
      break;
      case 'N': case 'n':
        if (toupper(*(++argv[i])) == 'O' &&
            toupper(*(++argv[i])) == 'E' &&
            toupper(*(++argv[i])) == 'M' &&
            toupper(*(++argv[i])) == 'S') isEMS = 0;
        else usage ++;
      break;
      default:
        usage++;
      break;
    }
  if (isEMS) 
    if (checkEMS()) isEMS = 0;
  if (usage || !filename[0]) {
    cprintf(Help);
    exit(1);
  }
  if (Device == SOUNDBLASTER) {
    if (!portset) port = 0x220;
    base_write = port + 0xc;
    outportb(port + 6,1);
    temptick = *((long far *)0x46CL) + 2;
    while (*((long far *)0x46CL) < temptick);
    outportb(port + 6,0);
    temptick = *((long far *)0x46CL) + 2;
    while (*((long far *)0x46CL) < temptick);
    if (inportb(port + 0xE) & 0x80 &&
        inportb(port+0xa) == 0xaa);
    else 
      exitError(1,"SoundBlaster NOT detected."); 
    cprintf("SoundBlaster Selected\n\r");
  }
  else if (Device == SOUNDPLAYER)
    cprintf("SoundPlayer Selected\n\r");
  else 
    exitError(1,"sound device not selected or invalid device.");

  if (samplerate > 48000 || samplerate < 4000) 
    exitError(1,"samplerate must be between 4000 and 48000 hz.");

/*
** End parse command line
** ---------------------
** Initialize file I/O
*/

  if ((filenumber = open(filename, O_BINARY | O_RDONLY)) < 0) 
    exitError(2, "file not found");
  filesize_bytes = filelength(filenumber);
  cprintf("File Size   : %ld\r\n", filesize_bytes);
/*
** EMS loading begin (EMS_load)
*/
  if (isEMS && filesize_bytes > 16384) {
    EMSMemory = (char *) malloc(EMSPAGESIZE);
    tempint = filesize_bytes / EMSPAGESIZE;
    tempint++;
    cprintf("EMS Free    : %ld\n\r", (long) getavailEMS() * EMSPAGESIZE);
    if (getavailEMS() < 10)
      exitError(5,"insufficient EMS memory");
    if (getavailEMS() < tempint) {
      tempint = getavailEMS() - 1;
      filesize_bytes = tempint;
      filesize_bytes *= EMSPAGESIZE; 
      isTrunc++;
    }
    filesize_hbytes = filesize_bytes;
    if (Compression) filesize_hbytes <<= 1;
    if ((EMSHandle = allocEMS(tempint)) == EMS_ERROR)
      exitError(5,"EMS allocation failed");
    cprintf(
      "Loading     : %ld bytes -> EMS, 0  %% done\b\b\b\b\b\b\b\b\b",
      filesize_bytes);
    for ( EMSPage = 0 ; EMSPage < tempint; EMSPage++) {
      _read(filenumber, EMSMemory, EMSPAGESIZE); 
      conv2ems(EMSPage, EMSHandle,EMSMemory);
      templong = EMSPage;
      templong *= 100;
      templong /= tempint;
      cprintf("%-3d\b\b\b", templong);
    }
    close(filenumber);
    EMSMemoryPointer = EMSMemory;
    EMSPage = 0;
    ems2conv(0,EMSHandle,EMSMemory);
    new_int = EMS_int;
/*
** Conventional loading begin (conv_load)
*/
  } else {
    templong  = farcoreleft();
    templong >>= 16;
    templong--;
    templong <<= 16;
    cprintf("Memory Free : %ld\r\n", templong);
    if (templong < filesize_bytes) {
      filesize_bytes = templong;
      isTrunc++;
    }
    filesize_hbytes = filesize_bytes;
    if (Compression) filesize_hbytes <<= 1;
    tempint = filesize_bytes / 65000;
    if (filesize_bytes % 65000) tempint++;
    numsegs = tempint;
    cprintf(
      "Loading     : %ld bytes -> memory, 0  %% done\b\b\b\b\b\b\b\b\b",
      filesize_bytes);
    for ( i = 0 ; i < tempint ; i++) {
      ConvSeg[i] = (char *) malloc(65000);
      ConvMemoryPointer = ConvSeg[i];
      _read(filenumber,ConvMemoryPointer, 65000);
      cprintf("%-3d\b\b\b",(i * 6500000L) / (filesize_bytes));
    }
    close(filenumber);
    Segment           = 1;
    ConvMemoryPointer = ConvSeg[0];
    new_int = conv_int;
  }
  if (Device == SOUNDBLASTER) 
    outportb(base_write,0xd1);
/*
** Begin SoundFile Playing
*/
  do {
   static char first_time;
   if (!first_time) fadeout();
   textmode(0x12);
   if (!first_time) set_black();
   setup_screen(port, filesize_bytes, filename,
               samplerate, isTrunc, Compression, DacPlayer, Device);
   if (!first_time) {
     fadein();
     rev_clock(new_int, samplerate);
   }
   first_time = 1;
  } while (osc());
  fix_clock();
  fadeout();

/*
** Restore System
*/

  if (Device == SOUNDBLASTER) 
    outportb(base_write,0xd3);
  if (isEMS) {
    freeEMS(EMSHandle); 
    farfree(EMSMemory);
  }
  else 
    for ( i = 0; i < numsegs; i ++)
      farfree(ConvSeg[i]);
  free(filename);

/*
** We're outa here
*/

  textmode(3);
  set_black();
  cprintf("Thank you for using DACE\n\r");
  fadein();
}

/**
*** End of file *********************************
**/

