
{===========================================================================}
{ Konzept        : DATA BECKERs Sound Blaster Superbuch                     }
{ Unit SBCMS     : Stellt elementare Routinen zur Programmierung der C/MS-  }
{                  Chips des Sound Blaster 1.5/2.0 zur Verfgung. Es ist zu }
{                  beachten, daá der Sound Blaster standardmáig nicht mit  }
{                  dem C/MS-Chipsatz ausgerstet ist (dieser Chipsatz wird  }
{                  optional angeboten).                                     }
{===========================================================================}
{ Autor          : Arthur Burda                                             }
{ Dateiname      : SBCMS.PAS                                                }
{ entwickelt am  : 19.07.1993                                               }
{ letztes Update : 01.09.1993                                               }
{ Version        : 1.04                                                     }
{ Compiler       : Turbo Pascal 6.0 und hher                               }
{===========================================================================}

UNIT SBCMS;

{---------------------------------------------------------------------------}
{ Compiler-Schalter                                                         }
{---------------------------------------------------------------------------}

{$B-}                         { Kurzschluáverfahren fr boolesche Ausdrcke }
{$D-}                                        { keine Debugger-Informationen }
{$F+}                                                { FAR-Aufrufe erlauben }
{$G+}                                                   { 286-Code erzeugen }
{$I-}                                                   { keine I/O-Prfung }
{$O+}                                            { Unit overlayfhig machen }
{$R-}                                               { keine Bereichsprfung }
{$S-}                                                  { keine Stackprfung }
{$X+}                    { Behandlung von Funktionen wie Prozeduren mglich }

INTERFACE

USES CRT, SBDrv;                            { CRT- und SBDrv-Unit einbinden }

TYPE

  {=========================================================================}
  { TSides: linker und rechter C/MS-Kanal                                   }
  {=========================================================================}

  TSides = (CMSLeft, CMSRight);

{===========================================================================}
{ Prozedur InitSBCMS: Initialisiert die beiden C/MS-Chips. Bei SB Pro und   }
{                     hher sind anstelle der C/MS-Chips die FM-Stereo-     }
{                     Chips vorhanden. Bei diesen Karten macht die Verwen-  }
{                     dung der Routinen aus dieser Unit wenig Sinn, da ein  }
{                     Stereo-Chipsatz standardmáig verfgbar ist.          }
{===========================================================================}
{ Eingabe: keine                                                            }
{ Ausgabe: keine                                                            }
{---------------------------------------------------------------------------}

PROCEDURE InitSBCMS;

{===========================================================================}
{ Funktion SBCMSIn: Liest den Wert eines C/MS-Registers aus dem Feldspei-   }
{                   cher fr C/MS-Registerinhalte aus. Es kann nicht direkt }
{                   auf die C/MS-Portadressen zugegriffen werden, da alle   }
{                   Register den Status "Write Only" haben.                 }
{===========================================================================}
{ Eingabe: Reg  = Nummer des Registers                                      }
{          Bank = Nummer der Bank (0 oder 1)                                }
{ Ausgabe: ein Datenbyte                                                    }
{---------------------------------------------------------------------------}

FUNCTION SBCMSIn(Reg, Bank : Byte) : Byte;

{===========================================================================}
{ Prozedur SBCMSOut: Verndert den Inhalt eines Registers des C/MS-Chip-    }
{                    satzes. Gleichzeitig wird der neue Wert auch im Feld-  }
{                    speicher fr C/MS-Registerinhalte vermerkt.            }
{===========================================================================}
{ Eingabe: Reg  = Nummer des Registers                                      }
{          Bank = Nummer der Bank (0 oder 1)                                }
{          Data = Datenbyte                                                 }
{ Ausgabe: keine                                                            }
{---------------------------------------------------------------------------}

PROCEDURE SBCMSOut(Reg, Bank, Data : Byte);

{===========================================================================}
{ Prozedur SetAmplitude: Setzt die Amplitude einer Stimme auf den angegebe- }
{                        nen Wert auf einem C/MS-Kanal. Der Wertebereich    }
{                        fr die Stimme liegt zwischen 0 und 11, fr die    }
{                        Amplitudenstrke zwischen 0 und 15.                }
{===========================================================================}
{ Eingabe: Voice = Nummer der Stimme (0-11)                                 }
{          Side  = linker oder rechter C/MS-Kanal (CMSLeft, CMSRight)       }
{          Param = Strke der Amplitude (0-15)                              }
{ Ausgabe: keine                                                            }
{---------------------------------------------------------------------------}

PROCEDURE SetAmplitude(Voice : Byte; Side : TSides; Param : Byte);

{===========================================================================}
{ Funktion GetAmplitude: Liefert die Amplitudenstrke einer Stimme auf dem  }
{                        angegebenen C/MS-Kanal zurck. Die Amplitude ist   }
{                        eine physikalische Bezeichnung fr Lautstrke.     }
{===========================================================================}
{ Eingabe: Voice = Nummer der Stimme (0-11)                                 }
{          Side  = linker oder rechter C/MS-Kanal (CMSLeft, CMSRight)       }
{ Ausgabe: Amplitudenstrke                                                 }
{---------------------------------------------------------------------------}

FUNCTION GetAmplitude(Voice : Byte; Side : TSides) : Byte;

{===========================================================================}
{ Prozedur SetFreq: Setzt die Frequenz einer Stimme.                        }
{===========================================================================}
{ Eingabe: Voice = Nummer der Stimme (0-11)                                 }
{          Param = Frequenz-Parameter                                       }
{ Ausgabe: keine                                                            }
{---------------------------------------------------------------------------}

PROCEDURE SetFreq(Voice, Param : Byte);

{===========================================================================}
{ Funktion GetFreq: Liefert die Frequenz einer Stimme zurck.               }
{===========================================================================}
{ Eingabe: Voice = Nummer der Stimme (0-11)                                 }
{ Ausgabe: Frequenz-Parameter                                               }
{---------------------------------------------------------------------------}

FUNCTION GetFreq(Voice : Byte) : Byte;

{===========================================================================}
{ Prozedur SetCMSOctave: Setzt die Oktave einer Stimme. Zur Unterscheidung  }
{                        von der Routine SetOctave aus der Unit SBFM wurde  }
{                        diese Prozedur SetCMSOctave genannt.               }
{===========================================================================}
{ Eingabe: Voice  = Nummer der Stimme (0-11)                                }
{          Octave = Oktavnummer (0-7)                                       }
{ Ausgabe: keine                                                            }
{---------------------------------------------------------------------------}

PROCEDURE SetCMSOctave(Voice, Octave : Byte);

{===========================================================================}
{ Funktion GetCMSOctave: Liefert die Nummer der Oktave der angegebenen      }
{                        Stimme zurck.                                     }
{===========================================================================}
{ Eingabe: Voice = Nummer der Stimme (0-11)                                 }
{ Ausgabe: Nummer der Oktave                                                }
{---------------------------------------------------------------------------}

FUNCTION GetCMSOctave(Voice : Byte) : Byte;

{===========================================================================}
{ Prozedur SetCMSTone: Schaltet eine Stimme an oder aus.                    }
{===========================================================================}
{ Eingabe: Voice = Nummer der Stimme (0-11)                                 }
{          On    = TRUE, wenn die Stimme angeschaltet werden soll, FALSE    }
{                  fr Ausschalten                                          }
{ Ausgabe: keine                                                            }
{---------------------------------------------------------------------------}

PROCEDURE SetCMSTone(Voice : Byte; On : Boolean);

{===========================================================================}
{ Funktion CMSToneOn: Liefert TRUE zurck, wenn die angegebene Stimme ein-  }
{                     geschaltet ist, sonst FALSE.                          }
{===========================================================================}
{ Eingabe: Voice = Nummer der Stimme (0-11)                                 }
{ Ausgabe: TRUE oder FALSE                                                  }
{---------------------------------------------------------------------------}

FUNCTION CMSToneOn(Voice : Byte) : Boolean;

{===========================================================================}
{ Prozedur SetNoiseParam: Setzt den Frequenz-Parameter fr einen Gerusch-  }
{                         generator. Der Wertebereich fr den Generator     }
{                         liegt zwischen 0 und 11, fr den Parameter zwi-   }
{                         schen 0 und 3.                                    }
{===========================================================================}
{ Eingabe: Gen   = Nummer des Generators (0-11)                             }
{          Param = Frequenz-Parameter (0-3)                                 }
{ Ausgabe: keine                                                            }
{---------------------------------------------------------------------------}

PROCEDURE SetNoiseParam(Gen, Param : Byte);

{===========================================================================}
{ Funktion GetNoiseParam: Gibt den Frequenz-Parameter eines Geruschgenera- }
{                         tors zurck.                                      }
{===========================================================================}
{ Eingabe: Gen = Nummer des Geruschgenerators (0-11)                       }
{ Ausgabe: Frequenz-Parameter                                               }
{---------------------------------------------------------------------------}

FUNCTION GetNoiseParam(Gen : Byte) : Byte;

{===========================================================================}
{ Prozedur SetNoise: Schaltet ein Gerusch ein bzw. aus.                    }
{===========================================================================}
{ Eingabe: Gen = Nummer des Geruschgenerators (0-11)                       }
{          On  = TRUE bedeutet Einschalten, FALSE Ausschalten               }
{ Ausgabe: keine                                                            }
{---------------------------------------------------------------------------}

PROCEDURE SetNoise(Gen : Byte; On : Boolean);

{===========================================================================}
{ Funktion NoiseOn: Liefert TRUE zurck, wenn ein Gerusch angeschaltet ist }
{                   und FALSE, wenn es ausgeschaltet ist.                   }
{===========================================================================}
{ Eingabe: Gen = Nummer des Generators (0-11)                               }
{ Ausgabe: TRUE oder FALSE                                                  }
{---------------------------------------------------------------------------}

FUNCTION NoiseOn(Gen : Byte) : Boolean;

IMPLEMENTATION

VAR

  {-------------------------------------------------------------------------}
  { Portadressen der beiden C/MS-Chips                                      }
  {-------------------------------------------------------------------------}

  CMS_DataBank0 : Word;                   { C/MS-Datenport des ersten Chips }
  CMS_AddrBank0 : Word;                { C/MS-Registerport des ersten Chips }
  CMS_DataBank1 : Word;                  { C/MS-Datenport des zweiten Chips }
  CMS_AddrBank1 : Word;               { C/MS-Registerport des zweiten Chips }

  {-------------------------------------------------------------------------}
  { Feldspeicher fr die C/MS-Register                                      }
  {-------------------------------------------------------------------------}

  CMSRegs : ARRAY[0..1, 0..255] OF Byte;

PROCEDURE InitSBCMS;

VAR
  Count : Word;                                                    { Zhler }

BEGIN
  InitSBDrv;                   { Umgebungsvariablen auslesen und berprfen }

  { C/MS-Portadressen setzen }

  CMS_DataBank0 := SB_BaseAddress;
  CMS_AddrBank0 := SB_BaseAddress+1;
  CMS_DataBank1 := SB_BaseAddress+2;
  CMS_AddrBank1 := SB_BaseAddress+3;

  { C/MS-Register initialisieren }

  FOR Count := 0 TO 255 DO
    BEGIN
      SBCMSOut(Count, 0, 0);
      SBCMSOut(Count, 1, 0);
    END;
  SBCMSOut($1C, 0, 1);
  SBCMSOut($1C, 1, 1);
END;

FUNCTION SBCMSIn(Reg, Bank : Byte) : Byte;

BEGIN
  SBCMSIn := CMSRegs[Bank, Reg];
END;

PROCEDURE SBCMSOut(Reg, Bank, Data : Byte);

BEGIN
  IF Bank = 0 THEN       { Register des ersten Chips (Bank 0) modifizieren? }
    BEGIN                                                              { ja }
      Port[CMS_AddrBank0] := Reg; { Register-Nummer zum Registerport senden }
      Port[CMS_DataBank0] := Data;             { Datenbyte an den Datenport }
      CMSRegs[Bank, Reg] := Data;        { Datenbyte im Feldspeicher merken }
    END;
  IF Bank = 1 THEN         { Register des zweiten Chips (Bank 1) verndern? }
    BEGIN                                                              { ja }
      Port[CMS_AddrBank1] := Reg;
      Port[CMS_DataBank1] := Data;
      CMSRegs[Bank, Reg] := Data;
    END;
END;

PROCEDURE SetAmplitude(Voice : Byte; Side : TSides; Param : Byte);

VAR
  Reg  : Byte;                                            { Register-Nummer }
  Old  : Byte;                                       { alter Registerinhalt }
  Bank : Byte;                            { Bank (erster oder zweiter Chip) }

BEGIN
  IF Voice < 6 THEN                   { Stimme im Bereich zwischen 0 und 5? }
    BEGIN
      Reg := Voice;         { Register-Nummer gleich der Nummer desr Stimme }
      Bank := 0;                  { ja, erster Chip muá angesprochen werden }
    END
  ELSE                                           { nein, Stimme hher als 5 }
    BEGIN
      Reg := Voice-6;                           { Register-Nummer bestimmen }
      Bank := 1;                     { zweiter Chip muá angesprochen werden }
    END;
  Old := SBCMSIn(Reg, Bank);                  { alten Registerwert abfragen }
  IF Side = CMSLeft THEN                               { linker C/MS-Kanal? }
    BEGIN                                                              { ja }
      Param := (Param AND 15) SHL 4;       { Korrektur des Amplitudenwertes }
      Old := Old AND 15;        { 4 unteren Bits des alten Registerinhaltes }
    END
  ELSE                                                 { rechter C/MS-Kanal }
    BEGIN
      Param := Param AND 15;          { 4 unteren Bits der Amplitudenstrke }
      Old := Old AND 240;               { Werte zwischen 0 und 240 zulassen }
    END;
  SBCMSOut(Reg, Bank, Param OR Old);      { Daten in die Register schreiben }
END;

FUNCTION GetAmplitude(Voice : Byte; Side : TSides) : Byte;

VAR
  Reg  : Byte;                                            { Register-Nummer }
  Old  : Byte;                                       { alter Registerinhalt }
  Bank : Byte;                            { Bank (erster oder zweiter Chip) }

BEGIN
  IF Voice < 6 THEN                   { Stimme im Bereich zwischen 0 und 5? }
    BEGIN
      Reg := Voice;          { Register-Nummer gleich der Nummer der Stimme }
      Bank := 0;                  { ja, erster Chip muá angesprochen werden }
    END
  ELSE                                           { nein, Stimme hher als 5 }
    BEGIN
      Reg := Voice-6;                           { Register-Nummer bestimmen }
      Bank := 1;                     { zweiter Chip muá angesprochen werden }
    END;
  IF Side = CMSLeft THEN                               { linker C/MS-Kanal? }
    GetAmplitude := SBCMSIn(Reg, Bank) SHR 4                           { ja }
  ELSE                                          { nein, rechter C/MS-Kanal? }
    GetAmplitude := SBCMSIn(Reg, Bank) AND 15;
END;

PROCEDURE SetFreq(Voice, Param : Byte);

VAR
  Reg  : Byte;                                            { Register-Nummer }
  Bank : Byte;                            { Bank (erster oder zweiter Chip) }

BEGIN
  Reg := Voice+8;                               { Register-Nummer ermitteln }
  IF Voice < 6 THEN                   { Stimme im Bereich zwischen 0 und 5? }
    Bank := 0                     { ja, erster Chip muá angesprochen werden }
  ELSE                                           { nein, Stimme hher als 5 }
    Bank := 1;                       { zweiter Chip muá angesprochen werden }
  SBCMSOut(Reg, Bank, Param);             { Daten in die Register eintragen }
END;

FUNCTION GetFreq(Voice : Byte) : Byte;

VAR
  Reg  : Byte;                                            { Register-Nummer }
  Bank : Byte;                            { Bank (erster oder zweiter Chip) }

BEGIN
  Reg := Voice+8;                               { Register-Nummer ermitteln }
  IF Voice < 6 THEN                   { Stimme im Bereich zwischen 0 und 5? }
    Bank := 0                     { ja, erster Chip muá angesprochen werden }
  ELSE                                           { nein, Stimme hher als 5 }
    Bank := 1;                       { zweiter Chip muá angesprochen werden }
  GetFreq := SBCMSIn(Reg, Bank);
END;

PROCEDURE SetCMSOctave(Voice, Octave : Byte);

VAR
  Reg  : Byte;                                            { Register-Nummer }
  Old  : Byte;                                       { alter Registerinhalt }
  Bank : Byte;                            { Bank (erster oder zweiter Chip) }

BEGIN
  IF Voice < 6 THEN                   { Stimme im Bereich zwischen 0 und 5? }
    BEGIN
      Reg := 16+(Voice SHR 1);                  { Register-Nummer bestimmen }
      Bank := 0;                  { ja, erster Chip muá angesprochen werden }
    END
  ELSE                                           { nein, Stimme hher als 5 }
    BEGIN
      Reg := 16+((Voice-6) SHR 1);                        { Register-Nummer }
      Bank := 1;                     { zweiter Chip muá angesprochen werden }
    END;
  Old := SBCMSIn(Reg, Bank);                  { alten Registerwert abfragen }

  { Bitoperationen }

  IF (Voice AND 1) = 1 THEN
    BEGIN
      Octave := (Octave AND 7) SHL 4;
      Old := Old AND 15;
    END
  ELSE
    BEGIN
      Octave := Octave AND 7;
      Old := Old AND 240;
    END;

  SBCMSOut(Reg, Bank, Octave OR Old);     { Daten in die Register schreiben }
END;

FUNCTION GetCMSOctave(Voice : Byte) : Byte;

VAR
  Reg  : Byte;                                            { Register-Nummer }
  Bank : Byte;                            { Bank (erster oder zweiter Chip) }

BEGIN
  IF Voice < 6 THEN                   { Stimme im Bereich zwischen 0 und 5? }
    BEGIN
      Reg := 16+(Voice SHR 1);                  { Register-Nummer bestimmen }
      Bank := 0;                  { ja, erster Chip muá angesprochen werden }
    END
  ELSE                                           { nein, Stimme hher als 5 }
    BEGIN
      Reg := 16+((Voice-6) SHR 1);                        { Register-Nummer }
      Bank := 1;                     { zweiter Chip muá angesprochen werden }
    END;
  IF (Voice AND 1) = 1 THEN
    GetCMSOctave := SBCMSIn(Reg, Bank) SHR 4
  ELSE
    GetCMSOctave := SBCMSIn(Reg, Bank) AND 7;
END;

PROCEDURE SetCMSTone(Voice : Byte; On : Boolean);

CONST
  Reg : Byte = $14;

VAR
  Bank, Help : Byte;

BEGIN
  IF Voice < 6 THEN                                 { Stimme kleiner als 6? }
    BEGIN                                                              { ja }
      Bank := 0;                 { erster C/MS-Chip muá programmiert werden }
      Help := 1 SHL Voice;
    END
  ELSE                                           { Stimme zwischen 6 und 11 }
    BEGIN
      Bank := 1;                { zweiter C/MS-Chip muá programmiert werden }
      Help := 1 SHL (Voice-6);
    END;
  IF On THEN                                              { Ton anschalten? }
    SBCMSOut(Reg, Bank, SBCMSIn(Reg, Bank) OR Help)                    { ja }
  ELSE                                                               { nein }
    SBCMSOut(Reg, Bank, SBCMSIn(Reg, Bank) AND (255-Help));
END;

FUNCTION CMSToneOn(Voice : Byte) : Boolean;

CONST
  Reg : Byte = $14;

VAR
  Bank, Help : Byte;

BEGIN
  IF Voice < 6 THEN                                 { Stimme kleiner als 6? }
    BEGIN                                                              { ja }
      Bank := 0;                 { erster C/MS-Chip muá programmiert werden }
      Help := 1 SHL Voice;
    END
  ELSE                                           { Stimme zwischen 6 und 11 }
    BEGIN
      Bank := 1;                { zweiter C/MS-Chip muá programmiert werden }
      Help := 1 SHL (Voice-6);
    END;
  CMSToneOn := (SBCMSIn(Reg, Bank) AND Help) = Help;
END;

PROCEDURE SetNoiseParam(Gen, Param : Byte);

CONST
  Reg : Byte = $16;

VAR
  Bank : Byte;                            { Bank (erster oder zweiter Chip) }
  Old  : Byte;                                       { alter Registerinhalt }

BEGIN
  IF Gen > 3 THEN
    Gen := 3;
  IF Gen < 2 THEN                    { Nummer des Generators kleiner als 2? }
    Bank := 0                       { erster C/MS-Chip ist zu programmieren }
  ELSE                                     { Nummer des Generators 2 oder 3 }
    Bank := 1;                  { zweiter C/MS-Chip muá programmiert werden }
  Old := SBCMSIn(Reg, Bank);                     { alten Registerwert lesen }

  { Bitoperationen }

  IF (Gen AND 1) = 1 THEN
    BEGIN
      Param := (Param AND 7) SHL 4;
      Old := Old AND 15;
    END
  ELSE
    BEGIN
      Param := Param AND 7;
      Old := Old AND 240;
    END;

  SBCMSOut(Reg, Bank, Param OR Old);      { Daten in die Register schreiben }
END;

FUNCTION GetNoiseParam(Gen : Byte) : Byte;

CONST
  Reg : Byte = $16;

VAR
  Bank : Byte;                            { Bank (erster oder zweiter Chip) }

BEGIN
  IF Gen > 3 THEN
    Gen := 3;
  IF Gen < 2 THEN                    { Nummer des Generators kleiner als 2? }
    Bank := 0                       { erster C/MS-Chip ist zu programmieren }
  ELSE                                     { Nummer des Generators 2 oder 3 }
    Bank := 1;                  { zweiter C/MS-Chip muá programmiert werden }
  IF (Gen AND 1) = 1 THEN
    GetNoiseParam := SBCMSIn(Reg, Bank) SHR 4
  ELSE
    GetNoiseParam := SBCMSIn(Reg, Bank) AND 7;
END;

PROCEDURE SetNoise(Gen : Byte; On : Boolean);

CONST
  Reg : Byte = $15;

VAR
  Bank, Help : Byte;

BEGIN
  IF Gen < 6 THEN                    { Nummer des Generators kleiner als 6? }
    BEGIN                                                              { ja }
      Bank := 0;                 { erster C/MS-Chip muá programmiert werden }
      Help := 1 SHL Gen;
    END
  ELSE                                        { Generator zwischen 6 und 11 }
    BEGIN
      Bank := 1;                { zweiter C/MS-Chip muá programmiert werden }
      Help := 1 SHL (Gen-6);
    END;
  IF On THEN                                              { Ton anschalten? }
    SBCMSOut(Reg, Bank, SBCMSIn(Reg, Bank) OR Help)                    { ja }
  ELSE                                                               { nein }
    SBCMSOut(Reg, Bank, SBCMSIn(Reg, Bank) AND (255-Help));
END;

FUNCTION NoiseOn(Gen : Byte) : Boolean;

CONST
  Reg : Byte = $15;

VAR
  Bank, Help : Byte;

BEGIN
  IF Gen < 6 THEN                                { Generator kleiner als 6? }
    BEGIN                                                              { ja }
      Bank := 0;                 { erster C/MS-Chip muá programmiert werden }
      Help := 1 SHL Gen;
    END
  ELSE                                        { Generator zwischen 6 und 11 }
    BEGIN
      Bank := 1;                { zweiter C/MS-Chip muá programmiert werden }
      Help := 1 SHL (Gen-6);
    END;
  NoiseOn := (SBCMSIn(Reg, Bank) AND Help) = Help;
END;

{---------------------------------------------------------------------------}
{ Startcode der Unit                                                        }
{---------------------------------------------------------------------------}

BEGIN
  InitSBCMS;
END.
