UNIT Speaker;
{
  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
}

INTERFACE

USES
  Hardware;

  PROCEDURE InitSpeaker;
  PROCEDURE CloseSpeaker;
  PROCEDURE SpkSendByte(b:Byte);
  PROCEDURE SpkPlayBuf(pBuffer:Pointer; BufferSize:Word; VAR Frec:Word; BufProc:TBufProc);
  PROCEDURE SpkStopBuf;

IMPLEMENTATION

USES
  DOS;

VAR
  OldTimerISR : Pointer;  {Puntero a la antigua ISR del timer}
  pBufSpk     : PByte;    {Puntero al buffer de DMA falso}
  BufSize,                {Tama¤o del buffer}
  BufPos      : Word;     {Offset de la muestra en el buffer}
  NextBuffer  : TBufProc; {Rutina de usuario}

CONST
  TablaSpeaker : ARRAY [0..255] OF Byte =
    ($40,$40,$40,$40,$40,$40,$40,$40,$40,$40,$3F,$3F,$3F,$3F,$3F,$3F,
     $3F,$3F,$3F,$3F,$3F,$3F,$3E,$3E,$3E,$3E,$3E,$3E,$3E,$3E,$3E,$3E,
     $3D,$3D,$3D,$3D,$3D,$3D,$3D,$3D,$3D,$3C,$3C,$3C,$3C,$3C,$3C,$3C,
     $3C,$3C,$3C,$3B,$3B,$3B,$3B,$3B,$3B,$3B,$3B,$3B,$3B,$3A,$3A,$3A,
     $3A,$3A,$3A,$3A,$3A,$3A,$3A,$39,$39,$39,$39,$39,$39,$39,$39,$39,
     $39,$38,$38,$38,$38,$38,$38,$38,$38,$37,$37,$37,$37,$37,$36,$36,
     $36,$36,$35,$35,$35,$35,$34,$34,$34,$33,$33,$32,$32,$31,$31,$30,
     $30,$2F,$2E,$2D,$2C,$2B,$2A,$29,$28,$27,$26,$25,$24,$23,$22,$21,
     $20,$1F,$1E,$1D,$1C,$1B,$1A,$19,$18,$17,$16,$15,$14,$13,$12,$11,
     $11,$10,$10,$0F,$0F,$0E,$0E,$0D,$0D,$0D,$0C,$0C,$0C,$0C,$0B,$0B,
     $0B,$0B,$0A,$0A,$0A,$0A,$0A,$09,$09,$09,$09,$09,$09,$09,$09,$09,
     $08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$07,$07,$07,$07,
     $07,$07,$07,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$05,$05,
     $05,$05,$05,$05,$05,$05,$05,$05,$04,$04,$04,$04,$04,$04,$04,$04,
     $04,$04,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$02,$02,$02,$02,
     $02,$02,$02,$02,$02,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01);


  PROCEDURE InitSpeaker;
  BEGIN
    ASM CLI END;
    Port[$43]:=$90;         {Prepara el Timer 2: Acceso MSB y modo terminal}
    Port[$61]:=Port[$61] OR 3; {Hace que el speaker funcione con el timer 2}
    ASM STI END;
  END;

  PROCEDURE CloseSpeaker;
  BEGIN
    ASM CLI END;
    Port[$61]:=Port[$61] AND $FC;   {Para el altavoz}
    Port[$43]:=$B6;  {Restaura el valor del registro de control del Timer 2}
    ASM STI END;
  END;

  PROCEDURE SpkSendByte(b:Byte);
  BEGIN
    Port[$42]:=TablaSpeaker[b];
  END;



  PROCEDURE TimerISR; INTERRUPT;
  TYPE
    TBarr=ARRAY [0..0] OF Byte;
  BEGIN
    EOI;
    ASM STI END;
    SpkSendByte(TBarr(pBufSpk^)[BufPos]);
    Inc(BufPos);
    IF BufPos=BufSize THEN NextBuffer;
    IF BufPos=BufSize*2 THEN
    BEGIN
      BufPos:=0;
      NextBuffer;
    END;
  END;

  PROCEDURE SpkPlayBuf(pBuffer:Pointer; BufferSize:Word; VAR Frec:Word; BufProc:TBufProc);
  BEGIN
    pBufSpk:=pBuffer;
    BufSize:=BufferSize;
    NextBuffer:=BufProc;
    BufPos:=0;
    GetIntVec(8,OldTimerISR);
    SetIntVec(8,@TimerISR);
    SetTimerFrec(Frec);
    InitSpeaker;
  END;

  PROCEDURE SpkStopBuf;
  BEGIN
    CloseSpeaker;
    ProgTimer0(0);
    SetIntVec(8,OldTimerISR);
  END;


END.