{****************************************************************************}
{                                                                            }
{ MODULE:         DevSB                                                      }
{                                                                            }
{ DESCRIPTION:    Device driver for the Sound Blaster sound card and         }
{                 compatibles, including the Sound Blaster Pro, Sound        }
{                 Booster, etc...                                            }
{                 Uses both: DMA and timer polling.                          }
{                                                                            }
{ AUTHOR:         Juan Carlos Ar‚valo                                        }
{                                                                            }
{ MODIFICATIONS:  Nobody (yet ;-)                                            }
{                                                                            }
{ HISTORY:        18-Oct-1992 Documentation. It doesn't allow the stereo     }
{                             of the SB Pro yet.                             }
{                 12-Nov-1992 SB Pro driver included. Speed-ups and fixes.   }
{                                                                            }
{ (C) 1992 VangeliSTeam                                                      }
{____________________________________________________________________________}

UNIT DevSB;

INTERFACE

CONST
  SBDevID        = 'SBlaster-Mono';
  DMASBDevID     = 'DMA-SB-Mono';
  DMASBSterDevID = 'DMA-SB-Stereo';
  DMASBMixDevID  = 'Mix-DMA-SB-Stereo';
  DMASBMix2DevID = 'Mix2-DMA-SB-Stereo';

CONST
  SbCmdTimeout : WORD    = 100;   { $10 DSP Command timeout.   }
  SbSplTimeout : WORD    = 10;    { $10 DSP Parameter timeout. }

  SbStereoMix  : BYTE    = 2;     { Stereo mixing algorithm.          }

  SbProMixMasterVol : BYTE    = 255;   { Master volume of the SB Pro mixer.    }
  SbProMixDACVol    : BYTE    = 255;   { DAC volume.                           }
  SbProMixFMVol     : BYTE    = 255;   { FM music volume.                      }
  SbProMixFilter    : BOOLEAN = FALSE; { TRUE = Activate SB Pro output filter. }


PROCEDURE SBInit  (Hz: WORD);
PROCEDURE SBEnd;




IMPLEMENTATION

USES Dos,
     SoundDevices, StrConst,
     Kbd, Debugging, SoundBlaster, Hardware;





FUNCTION SBName : TDevName; FAR;
  BEGIN
    SBName := GetString(StrDevSBName);
  END;

FUNCTION DMASBName : TDevName; FAR;
  BEGIN
    DMASBName := GetString(StrDevDMASBName);
  END;

FUNCTION DMASBSterName : TDevName; FAR;
  BEGIN
    DMASBSterName := GetString(StrDevDMASBSterName);
  END;

FUNCTION DMASBMixName : TDevName; FAR;
  BEGIN
    DMASBMixName := GetString(StrDevDMASBMixName);
  END;

FUNCTION DMASBMix2Name : TDevName; FAR;
  BEGIN
    DMASBMix2Name := GetString(StrDevDMASBMix2Name);
  END;




{$L DEVSB}




(******)

PROCEDURE SBInit(Hz: WORD);
  BEGIN
    SbRegInit;
    CalcTimerData(Hz);
    InitTimer;
  END;


PROCEDURE SBIntHandler; FAR; EXTERNAL;


PROCEDURE SBChgHz(Hz: WORD); FAR;
  BEGIN
    CalcTimerData(Hz);
    InitTimer;
  END;

PROCEDURE DevPoll; FAR;
  BEGIN
  END;




PROCEDURE SBEnd; 
  BEGIN
    SbRegDone;
  END;


CONST
  SBData : TSoundDevice = (
    DevID      : SBDevID;
    DMA        : FALSE
  );

(******)




FUNCTION GetDMACount : WORD;
  VAR
    c : BYTE;
  BEGIN
    PORT[$0C] := 0;           {clear BYTE POINTER flip-flop TO lower BYTE}

    c := PORT[$03];
    GetDMACount := c + 256*WORD(PORT[$03]);
  END;



CONST
  OldDMAIrq : POINTER = NIL;

  DMAPlacedInBuf : WORD = 0;

PROCEDURE DMAIrq; FAR; FORWARD;
PROCEDURE DoDMA;       FORWARD;

{

FUNCTION LimitHz(Hz: WORD; Output: BOOLEAN);
  BEGIN

    IF Hz < 4000 THEN Hz := 4000;
    IF (NOT DoHiSpeed) OR (SbStereo AND NOT Sb16Detected) THEN
      IF Hz > 23000 THEN Hz := 23000;

    LimitHz := Hz;
  END;



FUNCTION CalcTimeConst(Hz: WORD) : BYTE;
  VAR
    TimeConst : BYTE;
  BEGIN
    Hz := LimitHz(Hz, TRUE);

    IF SbStereo AND NOT Sb16Detected THEN
      BEGIN
        TimeConst := Hi(65536 - 128000000 DIV Hz);
        SoundHz   :=  500000 DIV (256 - WORD(TimeConst));
      END
    ELSE IF (NOT Sb16Detect) AND (NOT DoHiSpeed) THEN
      BEGIN
        TimeConst := 256 - 1000000 DIV Hz;
        SoundHz   := 1000000 DIV (256 - WORD(TimeConst));
      END
    ELSE
      BEGIN
        TimeConst := Hi(65536 - 256000000 DIV Hz);
        SoundHz   := 1000000 DIV (256 - WORD(TimeConst));
      END;

    CalcTimeConst := TimeConst;

  END;


FUNCTION CalcHz(tc: BYTE) : WORD;
  VAR
    TimeConst : BYTE;
  BEGIN

    IF (NOT Sb16Detect) AND (NOT DoHiSpeed) THEN
      IF SbStereo THEN
        Hz :=  500000 DIV (256 - WORD(TimeConst))
      ELSE
        Hz := 1000000 DIV (256 - WORD(TimeConst))
    ELSE
      Hz := 1000000 DIV (256 - WORD(TimeConst));

    CalcHz := Hz;

  END;
}


FUNCTION DMASBGetRealFreq(Hz: WORD) : WORD; FAR;
  VAR
    i    : WORD;
    NHz1 : WORD;
    NHz2 : WORD;
  BEGIN
    IF Hz < 4000 THEN Hz := 4000;
    IF (NOT DoHiSpeed) OR (SbStereo AND NOT Sb16Detected) THEN
      IF Hz > 21800 THEN Hz := 21800;

    i := Hi(65536 - (256000000 DIV Hz));
    NHz1 := 1000000 DIV (256 - i);
    NHz2 := 1000000 DIV (256 - i - 1);

    IF ABS(INTEGER(NHz1 - Hz)) > ABS(INTEGER(NHz2 - Hz)) THEN NHz1 := NHz2;

    DMASBGetRealFreq := NHz1;
  END;


FUNCTION DMASBProGetRealFreq(Hz: WORD) : WORD; FAR;
  VAR
    i    : WORD;
    NHz1 : WORD;
    NHz2 : WORD;
  BEGIN
    IF Sb16Detected THEN
      BEGIN
        DMASBProGetRealFreq := DMASBGetRealFreq(Hz);
        EXIT;
      END;

    IF Hz < 4000 THEN Hz := 4000;
    IF (NOT DoHiSpeed) OR SbStereo THEN
      IF Hz > 21800 THEN Hz := 21800;

    i := Hi(65536 - (128000000 DIV Hz));
    NHz1 :=  500000 DIV (256 - i);
    NHz2 :=  500000 DIV (256 - i - 1);

    IF ABS(INTEGER(NHz1 - Hz)) > ABS(INTEGER(NHz2 - Hz)) THEN NHz1 := NHz2;

    DMASBProGetRealFreq := NHz1;
  END;


PROCEDURE DMASBCalcTimerData(Hz: WORD);
  BEGIN
    CalcTimerData(PeriodicHz);

    Hz := ActiveDevice^.GetRealFreqProc(Hz);

    IF SbStereo AND NOT Sb16Detected THEN
      BEGIN
        TimeConst := Hi(65536 - 128000000 DIV Hz);
        SoundHz   :=  500000 DIV (256 - WORD(TimeConst));
      END
    ELSE
      BEGIN
        TimeConst := Hi(65536 - 256000000 DIV Hz);
        SoundHz   := 1000000 DIV (256 - WORD(TimeConst));
      END;
  END;



FUNCTION  SbMonoDetect : BOOLEAN; FAR;
  BEGIN
    SbRegInit;
    SbProInit;
    Sb16Init;
    SbMonoDetect := SbRegDetect;
  END;


FUNCTION  SbStereoDetect : BOOLEAN; FAR;
  BEGIN
    SbRegInit;
    SbProInit;
    Sb16Init;
    SbStereoDetect := SbProDetect OR Sb16Detect;
  END;



PROCEDURE DMASBInit(Hz: WORD);
  BEGIN
    SbRegInit;
    SbProInit;
    Sb16Init;

    IF OldDMAIrq = NIL THEN BEGIN
      OldDMAIrq := SetIRQVector(SbIrq, @DMAIrq);
      EnableIRQ(SbIrq);
    END;

    DMASet(SbDMAChan, $58, DMABuffer, DMABufferSize);

    SbProSetStereo(SbStereo);
    DMASBCalcTimerData(Hz);
    InitTimer;

    SbUpdateTimeConst;

    DMAStopped := FALSE;
    DMAStop    := FALSE;
  END;


PROCEDURE DMASBMonoInit(Hz: WORD); FAR;
  BEGIN
    SbStereo := FALSE;
    Sb16Bit  := FALSE;
    DMASbInit(Hz);
  END;


PROCEDURE DMASBSterInit(Hz: WORD); FAR;
  BEGIN
    SbStereo    := TRUE;
    Sb16Bit     := FALSE;
    SbStereoMix := 0;
    DMASbInit(Hz);
  END;


PROCEDURE DMASBMixInit(Hz: WORD); FAR;
  BEGIN
    SbStereo    := TRUE;
    Sb16Bit     := FALSE;
    SbStereoMix := 1;
    DMASbInit(Hz);
  END;


PROCEDURE DMASBMix2Init(Hz: WORD); FAR;
  BEGIN
    SbStereo    := TRUE;
    Sb16Bit     := FALSE;
    SbStereoMix := 2;
    DMASbInit(Hz);
  END;


PROCEDURE DMASBChgHz(Hz: WORD); FAR;
  BEGIN
    DMASBCalcTimerData(Hz);
  END;


PROCEDURE DMASBEnd; FAR;
  BEGIN

    IF OldDMAIrq <> NIL THEN BEGIN
      DMAStopped := FALSE;
      DMAStop    := TRUE;

      ASM PUSHF; STI END;

      WHILE (NOT DMAStopped) AND (NOT DeviceIdling) DO;

      ASM POPF END;

      SetIRQVector(SbIrq, OldDMAIrq);
      OldDMAIrq := NIL;
    END;
    DisableIRQ(SbIrq);
  END;




(*
PROCEDURE DMAXchgBuffs; ASSEMBLER;
  ASM
                MOV     DX,WORD PTR [DMABuffer1+2]
                MOV     SI,WORD PTR [DMABuffer1]
                MOV     BX,WORD PTR [DMABuffer1Full]
                LES     DI,         [DMABuffer2]
                MOV     AX,WORD PTR [DMABuffer2Full]
                MOV     WORD PTR [DMABuffer1],DI
                MOV     WORD PTR [DMABuffer1+2],ES
                MOV     WORD PTR [DMABuffer1Full],AX
                MOV     WORD PTR [DMABuffer2],SI
                MOV     WORD PTR [DMABuffer2+2],DX
                MOV     WORD PTR [DMABuffer2Full],BX
  END;
*)


(*
PROCEDURE AdjustBufferPointers;
  VAR
    o : WORD;
  BEGIN
    o := DMAPlacedInBuf;
    IF SbStereo THEN INC(o, o);
    IF Sb16Bit  THEN INC(o, o);

    IF DMABuffer1 = DMABuffer THEN
      DMABuffer2 := Ptr(SEG(DMABuffer^),OFS(DMABuffer^) + o)
    ELSE
      DMABuffer1 := Ptr(SEG(DMABuffer^),OFS(DMABuffer^) + o);
  END;
*)


PROCEDURE Dump8Channels; FORWARD;


PROCEDURE DMADoGetBuff; ASSEMBLER;
  ASM
{
                PUSH    0
                PUSH    0
                PUSH    $FF
                CALL    SetBorder
}

                CALL    DoGetBuffer

                ADD     [DMAPlacedInBuf],AX

{
                PUSH    0
                PUSH    $FF
                PUSH    0
                CALL    SetBorder
                MOV     AX,[DMAPlacedInBuf]
}

                CMP     AX,10
                JNC     @@nofin

{
                XOR     AX,AX
                MOV     [DMAPlacedInBuf],AX
}
                JMP     @@Fin1

@@nofin:

                MOV     BL,[SbStereo]
                AND     BL,BL
                JZ      @@1
                 ADD    AX,AX
@@1:

                PUSH    AX
                CALL    GetDMACount
                MOV     BX,DMABufferSize - 1
                SUB     BX,AX
                ADD     BX,WORD PTR [DMABuffer]
                MOV     CX,BX
                SUB     BX,WORD PTR [DMABufferPtr]
                JNC     @@5
                 ADD    BX,DMABufferSize
@@5:            POP     AX
                PUSH    AX
                SUB     BX,AX
                JNC     @@6
{                 INC    [PleaseFallBack]}
                 ADD    AX,BX
                 JNZ    @@6

{                 DEC    [PleaseFallBack]}
                 POP    AX
                 PUSH   AX
@@6:
                POP     BX
{
                PUSH    AX
                MOV     BX,WORD PTR [DMABufferPtr]
                SUB     BX,CX
                JNC     @@7
                 ADD    BX,DMABufferSize
@@7:            POP     AX
                SUB     BX,AX
                JNC     @@8
                 ADD    AX,BX
@@8:
}
                MOV     BL,[SbStereo]
                AND     BL,BL
                JZ      @@2
                 SHR    AX,1
@@2:
                 JMP    Dump8Channels
@@Fin1:
  END;



PROCEDURE Dump8Channels; ASSEMBLER;
  CONST
    First : BOOLEAN = TRUE;
  ASM
                MOV     DL,[First]
                AND     DL,DL
                JZ      @@nofirst

                MOV     WORD PTR [CS:@@Data11-2],0
                MOV     WORD PTR [CS:@@Data12-2],0
                MOV     WORD PTR [CS:@@Data21-2],0
                MOV     WORD PTR [CS:@@Data22-2],0
                MOV     [First],0

@@nofirst:
                MOV     DL,[DoEqualice]
                MOV     BYTE PTR [CS:@@Equalice],DL

                MOV     BL,[SbStereo]
                MOV     BH,[SbStereoMix]
                LES     DI,[DMABufferPtr]
                MOV     CX,[DMABufferEnd]
                MOV     WORD PTR [CS:@@moData1-2],CX
                MOV     WORD PTR [CS:@@stData1-2],CX
                MOV     WORD PTR [CS:@@m1Data1-2],CX
                MOV     WORD PTR [CS:@@m2Data1-2],CX
                LDS     SI,[Sounding]
                CLD
                MOV     CX,AX
                AND     BL,BL
                JNZ     @@stereo
@@lpfillmono:
                 MOV    AX,[DS:SI]
                 ADD    AX,[DS:SI+6]
                 ADD    AX,[DS:SI+8]
                 ADD    AX,[DS:SI+14]
                 ADD    AX,[DS:SI+16]
                 ADD    AX,[DS:SI+22]
                 ADD    AX,[DS:SI+24]
                 ADD    AX,[DS:SI+30]

                 MOV    BX,[DS:SI+2]
                 ADD    BX,[DS:SI+4]
                 ADD    BX,[DS:SI+10]
                 ADD    BX,[DS:SI+12]
                 ADD    BX,[DS:SI+18]
                 ADD    BX,[DS:SI+20]
                 ADD    BX,[DS:SI+26]
                 ADD    BX,[DS:SI+28]

                 ADD    AX,BX
                 JNO    @@nooverf
                 JS     @@posit
                  MOV   AX,-32768
                 JMP    @@nooverf
@@posit:          MOV   AX,32767
@@nooverf:




        MOV     DL,BYTE PTR [CS:@@Equalice]
        AND     DL,DL
        JZ      @@noequal

        PUSH    CX

        PUSH    AX

        CWD

        MOV     BX,1234
@@Data11:
        MOV     CX,1234
@@Data12:

        SAR     CX,1
        RCR     BX,1
        SAR     CX,1
        RCR     BX,1
        SUB     AX,BX
        SBB     DX,CX

        SAR     CX,1
        RCR     BX,1
        SUB     AX,BX
        SBB     DX,CX

        MOV     WORD PTR [CS:@@Data11-2],AX
        MOV     WORD PTR [CS:@@Data12-2],DX

        MOV     BX,AX
        MOV     CX,DX

        POP     AX

        PUSH    BX
        PUSH    CX

        CWD

        MOV     BX,1234
@@Data21:
        MOV     CX,1234
@@Data22:

        SAR     CX,1
        RCR     BX,1
        ADD     AX,BX
        ADC     DX,CX

        SAR     CX,1
        RCR     BX,1
        ADD     AX,BX
        ADC     DX,CX

        SAR     CX,1
        RCR     BX,1
        SAR     CX,1
        RCR     BX,1
        ADD     AX,BX
        ADC     DX,CX

        MOV     WORD PTR [CS:@@Data21-2],AX
        MOV     WORD PTR [CS:@@Data22-2],DX

        POP     CX
        POP     BX

        SUB     AX,BX
        SBB     DX,CX
        SUB     AX,BX
        SBB     DX,CX

        SAR     DX,1
        RCR     AX,1

        CMP     DX,0
        JG      @@up
        JNE     @@neg
        CMP     AX,32768
        JC      @@nada
@@up:    MOV    AX,32767
         JMP    @@nada
@@neg:  CMP     DX,-1
        JNZ     @@dw
        CMP     AX,32768
        JNC     @@nada
@@dw:    MOV    AX,-32768
@@nada:

        POP     CX

@@noequal:





(*

        MOV     DX,AX
        MOV     BX,1234
        NEG     BX
@@Data1:
        ADD     AX,BX
                 JNO    @@1nooverf
                 JS     @@1posit
                  MOV   AX,-32767
                 JMP    @@1nooverf
@@1posit:         MOV   AX,32767
@@1nooverf:
{        MOV     WORD PTR [CS:@@Data1-2],AX}
        XCHG    AX,DX

        MOV     BX,1234
@@Data2:
        ADD     AX,BX
                 JNO    @@2nooverf
                 JS     @@2posit
                  MOV   AX,-32767
                 JMP    @@2nooverf
@@2posit:         MOV   AX,32767
@@2nooverf:
{        MOV     WORD PTR [CS:@@Data2-2],AX}

        SUB     AX,DX
                 JNO    @@3nooverf
                 JS     @@3posit
                  MOV   AX,-32767
                 JMP    @@3nooverf
@@3posit:         MOV   AX,32767
@@3nooverf:

        NEG     DX
        ADD     AX,DX
                 JNO    @@4nooverf
                 JS     @@4posit
                  MOV   AX,-32767
                 JMP    @@4nooverf
@@4posit:         MOV   AX,32767
@@4nooverf:
        ADD     AX,DX
                 JNO    @@5nooverf
                 JS     @@5posit
                  MOV   AX,-32767
                 JMP    @@5nooverf
@@5posit:         MOV   AX,32767
@@5nooverf:

*)







                 XOR    AH,80h
                 MOV    AL,AH
                 STOSB
                 CMP    DI,$1234
@@moData1:
                 JB     @@monocont
                  SUB   DI,DMABufferSize
@@monocont:      ADD    SI,MaxChannels*2
                 DEC CX
                 JNZ @@lpfillmono
{                 LOOP   @@lpfillmono}
                JMP     @@Fin



@@Equalice:     DB 0






@@stereo:       AND     BH,BH
                JNZ     @@domix
@@lpfillster:
                 MOV    AX,[DS:SI]
                 ADD    AX,[DS:SI+6]
                 ADD    AX,[DS:SI+8]
                 ADD    AX,[DS:SI+14]
                 ADD    AX,[DS:SI+16]
                 ADD    AX,[DS:SI+22]
                 ADD    AX,[DS:SI+24]
                 ADD    AX,[DS:SI+30]

                 MOV    BX,[DS:SI+2]
                 ADD    BX,[DS:SI+4]
                 ADD    BX,[DS:SI+10]
                 ADD    BX,[DS:SI+12]
                 ADD    BX,[DS:SI+18]
                 ADD    BX,[DS:SI+20]
                 ADD    BX,[DS:SI+26]
                 ADD    BX,[DS:SI+28]

                 ADD     BX,BX
                 JNO     @@stnooverf1
                 JS      @@stposit1
                  MOV    BX,-32768
                 JMP     @@stnooverf1
@@stposit1:       MOV    BX,32767
@@stnooverf1:
                 ADD     AX,AX
                 JNO     @@stnooverf2
                 JS      @@stposit2
                  MOV    AX,-32768
                 JMP     @@stnooverf2
@@stposit2:       MOV    AX,32767
@@stnooverf2:
                 MOV    AL,BH
                 XOR    AX,8080h
                 STOSW
                 CMP    DI,$1234
@@stData1:
                 JB     @@stercont
                  SUB   DI,DMABufferSize
@@stercont:      ADD    SI,MaxChannels*2
                 LOOP   @@lpfillster
                JMP     @@Fin

@@domix:        DEC     BH
                JNZ     @@domix2
@@lpfillmix1:
                 MOV    AX,[DS:SI]
                 ADD    AX,[DS:SI+6]
                 ADD    AX,[DS:SI+8]
                 ADD    AX,[DS:SI+14]
                 ADD    AX,[DS:SI+16]
                 ADD    AX,[DS:SI+22]
                 ADD    AX,[DS:SI+24]
                 ADD    AX,[DS:SI+30]

                 MOV    BX,[DS:SI+2]
                 ADD    BX,[DS:SI+4]
                 ADD    BX,[DS:SI+10]
                 ADD    BX,[DS:SI+12]
                 ADD    BX,[DS:SI+18]
                 ADD    BX,[DS:SI+20]
                 ADD    BX,[DS:SI+26]
                 ADD    BX,[DS:SI+28]

                 MOV     DX,AX
                 ADD     AX,BX
                 JNO     @@m1nooverf0
                  RCR    AX,1
                 JMP     @@m1ovc0
@@m1nooverf0:     SAR    AX,1
@@m1ovc0:
                 ADD     BX,AX
                 JNO     @@m1nooverf1
                 JS      @@m1posit1
                  MOV    BX,-32768
                 JMP     @@m1nooverf1
@@m1posit1:       MOV    BX,32767
@@m1nooverf1:
                 ADD     AX,DX
                 JNO     @@m1nooverf2
                 JS      @@m1posit2
                  MOV    AX,-32768
                 JMP     @@m1nooverf2
@@m1posit2:       MOV    AX,32767
@@m1nooverf2:
                 MOV    AL,BH
                 XOR    AX,8080h
                 STOSW
                 CMP    DI,$1234
@@m1Data1:
                 JB     @@mix1cont
                  SUB   DI,DMABufferSize
@@mix1cont:      ADD    SI,MaxChannels*2
                 LOOP   @@lpfillmix1
                JMP     @@Fin



@@domix2:
@@lpfillmix2:
                 MOV    AX,[DS:SI]
                 ADD    AX,[DS:SI+6]
                 ADD    AX,[DS:SI+8]
                 ADD    AX,[DS:SI+14]
                 ADD    AX,[DS:SI+16]
                 ADD    AX,[DS:SI+22]
                 ADD    AX,[DS:SI+24]
                 ADD    AX,[DS:SI+30]

                 MOV    BX,[DS:SI+2]
                 ADD    BX,[DS:SI+4]
                 ADD    BX,[DS:SI+10]
                 ADD    BX,[DS:SI+12]
                 ADD    BX,[DS:SI+18]
                 ADD    BX,[DS:SI+20]
                 ADD    BX,[DS:SI+26]
                 ADD    BX,[DS:SI+28]


                 SAR     BX,1
                 SAR     AX,1
                 MOV     DX,AX
                 ADD     AX,BX

                 MOV     [WORD PTR CS:@@datastm2-2],AX
                 SAR     AX,1
                 ADD     AX,1234
@@datastm2:
                 JNO     @@m2nooverf1
                 JS      @@m2posit1
                  MOV    AX,-32768
                 JMP     @@m2nooverf1
@@m2posit1:       MOV    AX,32767
@@m2nooverf1:
                 ADD     DX,AX
                 JNO     @@m2nooverf2
                 JS      @@m2posit2
                  MOV    DX,-32768
                 JMP     @@m2nooverf2
@@m2posit2:       MOV    DX,32767
@@m2nooverf2:
                 ADD     AX,BX
                 JNO     @@m2nooverf3
                 JS      @@m2posit3
                  MOV    AX,-32768
                 JMP     @@m2nooverf3
@@m2posit3:       MOV    AX,32767
@@m2nooverf3:
                 MOV    AL,DH
                 XOR    AX,8080h
                 STOSW
                 CMP    DI,$1234
@@m2Data1:
                 JB     @@mix2cont
                  SUB   DI,DMABufferSize
@@mix2cont:      ADD    SI,MaxChannels*2
                 LOOP   @@lpfillmix2

@@Fin:          MOV     AX,SEG(@Data)
                MOV     DS,AX
                MOV     WORD PTR [DMABufferPtr],DI

{
                PUSH    0
                PUSH    0
                PUSH    0
                CALL    SetBorder
}
  END;




PROCEDURE DoDMA; ASSEMBLER;
  CONST
    OLDDMABP     : WORD = 0;
    OLDDMACT     : WORD = 0;
  ASM
                CLI

                PUSH    BX
                PUSH    CX
                PUSH    DX
                PUSH    DI
                PUSH    SI
                PUSH    ES
{
                CALL    GetDMACount
                INC     AX
                MOV     BX,[OLDDMACT]
                MOV     [OLDDMACT],AX
                SUB     BX,AX
                JNC     @@8
                 ADD    BX,DMABufferSize
@@8:
                PUSH    0
                PUSH    BX
                PUSH    $70
                CALL    WriteNum

                MOV     BX,WORD PTR [DMABufferPtr]
                MOV     AX,[OLDDMABP]
                MOV     [OLDDMABP],BX
                SUB     BX,AX
                JNC     @@9
                 ADD    BX,DMABufferSize
@@9:
                PUSH    20
                PUSH    BX
                PUSH    $70
                CALL    WriteNum
}

                PUSHA
                PUSH    'l'
                PUSH    $02
                CALL    WriteChar
                POPA

                MOV     AX,[DMAPlacedInBuf]
                AND     AX,AX
                JNZ     @@haybuf

                INC     [PleaseFallBack]

                PUSHA
                PUSH    'd'
                PUSH    $70
                CALL    WriteChar
                POPA

                CALL    DMADoGetBuff

                CALL    DMADoGetBuff

                MOV     AX,[DMAPlacedInBuf]
                AND     AX,AX
                JNZ     @@haybuf
                 INC    AH
                 MOV    [DeviceIdling],AH
                 JMP    @@Fin

@@haybuf:       XOR     BH,BH
                MOV     [DeviceIdling],BH

                CALL    SbUpdateTimeConst

                MOV     CX,[DMAPlacedInBuf]
                PUSH    CX
                PUSH    1
                CALL    SbPlaySample

                XOR     AX,AX
                MOV     [DMAPlacedInBuf],AX

@@7:
                CALL    DMADoGetBuff
                CALL    DMADoGetBuff

@@Fin:          POP     ES
                POP     SI
                POP     DI
                POP     DX
                POP     CX
                POP     BX
  END;




PROCEDURE DMATim; FAR; ASSEMBLER;
  ASM
                CLI

                PUSH    AX
                PUSH    DS
                PUSH    ES

                MOV     AX,SEG(@Data)
                MOV     DS,AX

                PUSHA
                PUSH    't'
                PUSH    $20
                CALL    WriteChar
                POPA

                MOV     AL,[DMAIrqWatch]
                CMP     AL,20
                JNC     @@dowatch

                MOV     AL,[DeviceIdling]
                AND     AL,AL
                JZ      @@notneeded

                PUSHA
                CALL    InitTimer
                POPA

                JMP     @@noack
@@dowatch:
                PUSH    DX
                MOV     DX,[DSP8AckPort]
                IN      AL,DX
                MOV     DX,[DSPLifePort]
                IN      AL,DX
                POP     DX

@@noack:        MOV     AL,[DMAStop]
                AND     AL,AL
                JZ      @@notstop

                MOV     [DMAStopped],AL
                MOV     [DeviceIdling],AL
                JMP     @@notneeded

@@notstop:      XOR     AL,AL
                MOV     [DeviceIdling],AL

                CALL    DoDMA

                XOR     AL,AL
                MOV     [DMAIrqWatch],AL
                
@@notneeded:    MOV     AL,[DMAIrqWatch]
                CMP     AL,100
                JNC     @@noinc

                INC     [DMAIrqWatch]

@@noinc:        MOV     AX,[SystemClockIncr]
                ADD     [SystemClockCount],AX
                JNC     @@nosys

                PUSHF
                CALL    CSOldInt8
                JMP     @@sisys

@@nosys:        MOV     AL,20h
                OUT     20h,AL

@@sisys:        STI

                MOV     AL,[DMAStop]
                AND     AL,AL
                JZ      @@notstop2

                MOV     [DMAStopped],AL
                MOV     [DeviceIdling],AL
                JMP     @@donothing

@@notstop2:     PUSHA
                CALL    PeriodicProc
                MOV     AX,[DMAPlacedInBuf]
                AND     AX,AX
                JNZ     @@nogetbuff

                CLI
                CALL    DMADoGetBuff
                STI

@@nogetbuff:
                POPA

@@donothing:
                POP     ES
                POP     DS
                POP     AX
                IRET
  END;

PROCEDURE DMAIrq; ASSEMBLER;
  CONST
    Old83 : BYTE = 0;
  ASM
                CLI

                PUSH    DX
                PUSH    AX
                PUSH    DS
                PUSH    ES

                MOV     AX,SEG(@Data)
                MOV     DS,AX
{
                MOV     DX,[MixAddrPort]
                MOV     AL,$83
                OUT     DX,AL
                INC     DL
                IN      AL,DX
                MOV     [Old83],AL
                DEC     DL

                MOV     AL,$83
                OUT     DX,AL
                INC     DL
                XOR     AL,AL
                OUT     DX,AL
}
                MOV     DX,[DSP8AckPort]
                IN      AL,DX

                MOV     DX,[DSPLifePort]
                IN      AL,DX


                PUSHA
                PUSH    'i'
                PUSH    $07
                CALL    WriteChar
                POPA

                XOR     AL,AL
                MOV     [DMAIrqWatch],AL

                MOV     AL,[DMAStop]
                AND     AL,AL
                JZ      @@nostop

                MOV     [DMAStopped],AL
                MOV     [DeviceIdling],AL
                JMP     @@Fin
@@nostop:
                CALL    DoDMA
@@Fin:
{
                PUSHA
                PUSH    100
                PUSH    $DA
                CALL    SbWriteByte
                POPA

                PUSHA
                PUSH    100
                PUSH    $45
                CALL    SbWriteByte
                POPA
}
{
                MOV     DX,[MixAddrPort]
                MOV     AL,$83
                OUT     DX,AL
                INC     DL
                MOV     AL,[Old83]
                OUT     DX,AL
}
                MOV     AX,[SbIrq]
                CMP     AL,10
                JNZ     @@not10

                MOV     AL,$20
                OUT     $A0,AL

@@not10:        MOV     AL,$20
                OUT     $20,AL

                POP     ES
                POP     DS
                POP     AX
                POP     DX
                IRET
  END;


CONST
  DMASBData : TSoundDevice = (
    DevID      : DMASBDevID;
    DMA        : TRUE
  );


CONST
  DMASBSterData : TSoundDevice = (
    DevID      : DMASBSterDevID;
    DMA        : TRUE
  );


CONST
  DMASBMixData : TSoundDevice = (
    DevID      : DMASBMixDevID;
    DMA        : TRUE
  );


CONST
  DMASBMix2Data : TSoundDevice = (
    DevID      : DMASBMix2DevID;
    DMA        : TRUE
  );




(******)

BEGIN

  WITH SBData DO BEGIN
    Name            := SBName;
    AutoDetect      := SbRegDetect;
    InitRut         := SBInit;
    ChgHzProc       := SBChgHz;
    GetRealFreqProc := GetRealFreq;
    TimerHandler    := SBIntHandler;
    PollRut         := DevPoll;
    EndRut          := SBEnd;
  END;

  WITH DMASBData DO BEGIN
    Name            := DMASBName;
    AutoDetect      := SbMonoDetect;
    InitRut         := DMASBMonoInit;
    ChgHzProc       := DMASBChgHz;
    GetRealFreqProc := DMASBGetRealFreq;
    TimerHandler    := DMATim;
    PollRut         := DevPoll;
    EndRut          := DMASBEnd;
  END;

  WITH DMASBSterData DO BEGIN
    Name            := DMASBSterName;
    AutoDetect      := SbStereoDetect;
    InitRut         := DMASBSterInit;
    ChgHzProc       := DMASBChgHz;
    GetRealFreqProc := DMASBProGetRealFreq;
    TimerHandler    := DMATim;
    PollRut         := DevPoll;
    EndRut          := DMASBEnd;
  END;

  WITH DMASBMixData DO BEGIN
    Name            := DMASBMixName;
    AutoDetect      := SbStereoDetect;
    InitRut         := DMASBMixInit;
    ChgHzProc       := DMASBChgHz;
    GetRealFreqProc := DMASBProGetRealFreq;
    TimerHandler    := DMATim;
    PollRut         := DevPoll;
    EndRut          := DMASBEnd;
  END;

  WITH DMASBMix2Data DO BEGIN
    Name            := DMASBMix2Name;
    AutoDetect      := SbStereoDetect;
    InitRut         := DMASBMix2Init;
    ChgHzProc       := DMASBChgHz;
    GetRealFreqProc := DMASBProGetRealFreq;
    TimerHandler    := DMATim;
    PollRut         := DevPoll;
    EndRut          := DMASBEnd;
  END;

  InitDevice(@DMASBMix2Data);
  InitDevice(@DMASBMixData);
  InitDevice(@DMASBSterData);
  InitDevice(@DMASBData);
  InitDevice(@SBData);

END.
