/*
  Rutinas de PWM para el altavoz interno del PC, con soporte de doble buffer
  virtual.
  Por Luis Crespo, FidoNet 2:343/108.21, Internet d8089110@est.fib.upc.es
*/

#include <dos.h>
#include "hardware.h"

void interrupt (*OldTimerISR)();  /* Puntero a la antigua ISR del timer */
unsigned char *pBufSpk;           /* Puntero al falso buffer de DMA */
static unsigned BufSize,          /* Tama¤o del buffer */
                BufPos;           /* Offset de la muestra en el buffer */
static void (*NextBuf)();         /* Rutina de usuario */


unsigned char TablaSpeaker[256] =
  {0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,
   0x40,0x40,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,
   0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3E,0x3E,
   0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,
   0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,
   0x3D,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,
   0x3C,0x3C,0x3C,0x3B,0x3B,0x3B,0x3B,0x3B,
   0x3B,0x3B,0x3B,0x3B,0x3B,0x3A,0x3A,0x3A,
   0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x39,
   0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39,
   0x39,0x38,0x38,0x38,0x38,0x38,0x38,0x38,
   0x38,0x37,0x37,0x37,0x37,0x37,0x36,0x36,
   0x36,0x36,0x35,0x35,0x35,0x35,0x34,0x34,
   0x34,0x33,0x33,0x32,0x32,0x31,0x31,0x30,
   0x30,0x2F,0x2E,0x2D,0x2C,0x2B,0x2A,0x29,
   0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,
   0x20,0x1F,0x1E,0x1D,0x1C,0x1B,0x1A,0x19,
   0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,
   0x11,0x10,0x10,0x0F,0x0F,0x0E,0x0E,0x0D,
   0x0D,0x0D,0x0C,0x0C,0x0C,0x0C,0x0B,0x0B,
   0x0B,0x0B,0x0A,0x0A,0x0A,0x0A,0x0A,0x09,
   0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,
   0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,
   0x08,0x08,0x08,0x08,0x07,0x07,0x07,0x07,
   0x07,0x07,0x07,0x06,0x06,0x06,0x06,0x06,
   0x06,0x06,0x06,0x06,0x06,0x06,0x05,0x05,
   0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,
   0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
   0x04,0x04,0x03,0x03,0x03,0x03,0x03,0x03,
   0x03,0x03,0x03,0x03,0x02,0x02,0x02,0x02,
   0x02,0x02,0x02,0x02,0x02,0x01,0x01,0x01,
   0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01};


void InitSpeaker()
{
  asm CLI;
  outportb(0x43,0x90); /* Prepara el Timer 2: Acceso MSB y modo terminal */
  outportb(0x61,inportb(0x61) | 3); /* Hace que el speaker funcione con el timer 2 */
  asm STI;
}

void CloseSpeaker()
{
  asm CLI;
  outportb(0x61,inportb(0x61) & 0xFC);  /* Para el altavoz */
  outportb(0x43,0xB6); /* Restaura el valor del registro de control del Timer 2 */
  asm STI;
}

void SpkSendByte(unsigned char b)
{
  outportb(0x42,TablaSpeaker[b]);
}

void interrupt TimerISR()
{
  EOI();
  asm STI;
  SpkSendByte(pBufSpk[BufPos]);
  BufPos++;
  if (BufPos==BufSize) NextBuf();
  if (BufPos==BufSize*2)
  {
    BufPos = 0;
    NextBuf();
  }
}

void SpkPlayBuf(void *pBuffer, unsigned BufferSize, unsigned *Frec, void (*BufProc)())
{
  pBufSpk = pBuffer;
  BufSize = BufferSize;
  NextBuf = BufProc;
  BufPos = 0;
  OldTimerISR = getvect(8);
  setvect(8,TimerISR);
  SetTimerFrec(Frec);
  InitSpeaker();
}

void SpkStopBuf()
{
  CloseSpeaker();
  ProgTimer0(0);
  setvect(8,OldTimerISR);
}

