[Back to SOUND SWAG index]  [Back to Main SWAG index]  [Original]

{
  From: Gerhard Dalenoort                            Read: Yes    Replied: No
}
Program DigiTal; { Real-Mode Only }

{ Timer routines tnx to Unit digital

  InterruptVector   : Array [0..255] of Pointer Absolute $0000 : $0000;

  My Question is this: In DPMI mode were do I have to look
                       for the Interrupt Table ???
                       and How to program DMA ???

   GreetZ,
     Gerhard.
}

Uses Crt;
Const
  fname = 'c:\modplay\sounds\hcaprince.911';  { Can be any raw data File }
  SAMPLERATE = 16000; { about: 3Khz - 44Khz }

  HexTable : Array[0..15] of Char =
        ('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');

  Counter : Word = 1;
{ SB-DSP Constants }
   BASE       = $220;  { Change this to your card base adres }
   resetport  = Base + $6;
   readport   = Base + $A;
   Writeport  = Base + $C;
   statusport = Base + $E;
   dac_Write  = $10;
   adc_read   = $20;
   speakeron  = $D1;
   speakeroff = $D3;

{ Timer Constants}
  C8253ModeControl   = $43;
  C8253Channel       : Array [0..2] of Byte = ($40, $41, $42);
  C8253OperatingFreq = 1193180;
  C8259Command       = $20;
  TimerInterrupt     = $08;


Var
  OldTimerInterrupt : Pointer;
  InterruptVector   : Array [0..255] of Pointer Absolute $0000 : $0000;
  OK,DonePlaying    : Boolean;
  Sound             : Pointer;
  Size,
  SegSound, OfsSound: Word;
  Mbyte             : byte;

Procedure spk_on;
begin
   Repeat Until port[Writeport] < $80;
   port[Writeport] := $D1;
end;

Procedure spk_off;
begin
   Repeat Until port[Writeport] < $80;
   port[Writeport] := $D3;
end;

Function resetDSP : Boolean;
(*----------------------------------------------------------
 Not Mine...
 The Book says:
 - Write a 1 to ResetPort $2x6,
 - Wait 3 MicroSec. (æs)
 - Write a 0 to ResetPort $2x6
 - Wait until ready byte $0AA at DataPort $2xA
   (it is advisable to check first for a data avaible: Port $22E bit 7 set)
   Typically the DSP takes about 100 microsecs. to reset.

   Mine looks like this in pseudo code:
     Port[$226]:=1;
     Asm nop; nop; nop; End; { Consume time }
     Port[$226]:=0;
     I:=0; { Longint }
     Repeat Inc(I) until (I>123000) or (Port[$22A]=$0AA);
     ResetDSP:=(I<32000);
-------------------------------------------------------------- *)

Var
   count, bdum : Byte;
begin
   resetDSP := False;
   port[resetport] := 1;
   For count := 1 to 6 do bdum := port[statusport];
   port[resetport] := 0;
   For count := 1 to 6 do bdum := port[statusport];
   Repeat Until port[statusport] > $80;
   if port[readport] = $AA then resetDSP := True;
end;


Procedure generic(reg,cmd:Integer; data:Byte);
      { ?? not used here, what does it do ?? }
begin
   Repeat Until port[Writeport] < $80;
   port[reg] := cmd;
   Repeat Until port[Writeport] < $80;
   port[reg] := data;
end;


fUNCtION hEX( tHEvAR:wORD):sTRING; { See what the it looks like in HEX }
vAR
 bUF    : sTRING;
 w      : wORD;
bEGIN
 w := tHEvAR;
 bUF:=      hEXtABLE[ hI(w) sHR  4];
 bUF:=bUF + hEXtABLE[ hI(w) mOD 16];
 bUF:=bUF + hEXtABLE[ lO(w) sHR  4];
 bUF:=bUF + hEXtABLE[ lO(w) mOD 16];;
 hEX:=bUF;
eND;



{=[ 8253 Timer Programming Routines ]=====================================}
Procedure Set8253Channel(ChannelNumber : Byte; ProgramValue : Word);
begin
  Port[C8253ModeControl] := 54 or (ChannelNumber SHL 6); { XX110110 }
  Port[C8253Channel[ChannelNumber]] := Lo(ProgramValue);
  Port[C8253Channel[ChannelNumber]] := Hi(ProgramValue);
end;



{-[ Set Timer Interupt Vector To Default Handler ]------------------------}
Procedure SetTimerInterruptVectorDefault;
begin
  aSM cli END;
  InterruptVector[TimerInterrupt] := OldTimerInterrupt;
  aSM sti END;
end;


{-[ Set Clock Channel 0 Back To 18.2 Default Value ]----------------------}
Procedure SetDefaultTimerSpeed;
begin
  Set8253Channel (0, 0);
end;




{=[ MUST CALL BEFORE ExitING Program!!! ]=================================}
Procedure CleanUp;
begin
  SetDefaultTimerSpeed;
  SetTimerInterruptVectorDefault;
end;




{-[ Set Clock Channel 0 (INT 8, IRQ 0) To Input Speed ]-------------------}
Procedure SetPlaySpeed(Speed : LongInt);
Var ProgramValue : Word;
begin
  if Speed > 33000 then Speed:=33000;
  ProgramValue := C8253OperatingFreq div Speed;
  Set8253Channel(0, ProgramValue);
end;


Procedure PlayData; Interrupt;
var DATA : Byte;
begin
  if Not(DonePlaying) Then
  begin
    if Counter <= Size Then
    begin
      Data:=(Mem[SegSound:OfsSound+Counter] {SHR 2} ); { SHR = Volume down }
      Inc(Counter);

{     Port[$22C]:=$10;      NextByte is realtime sample Data
      Port[$22C]:=Data;     Sample Data
                 The next ASM is the same as these
                 two port Writes to the DSP, Maybe just faster.
}       Asm
          mov  dx,$22C
          in   al,dx
          mov  al,10h
          out  dx,al
          in   al,dx
          mov  al,data
          out  dx,al
        end;
    end else { Counter > Size }
    begin
      DonePlaying := True;
      Counter     := 1;
    end;
  end;

  Port[C8259Command] := $20; { Enable Interupts }
end;




{---------------------- LoadRawSampleData --------------------}
Function loadFile(Var buffer:Pointer; Filename:String) : Word;
Var
   fromf : File;
   size : LongInt;
   errcode : Integer;
begin
   assign(fromf,Filename);
   reset(fromf,1);
   errcode := ioresult;
   if errcode = 0 then
   begin
      size := Filesize(fromf);
      {Writeln(size);}
      getmem(buffer,size);
      blockread(fromf,buffer^,size);
   end
   else size := 0;
   loadFile := size;
   close(fromf);
end;



Procedure unload(buffer:Pointer; size:Word);
begin freemem(buffer,size); end;



Var I    : Integer;
    Ch   : Char;
Begin
 Clrscr;
 DonePlaying:=True;
 Size := LoadFile(Sound,Fname);
 SegSound:=Seg(Sound^);          { Get SegMent and Ofset of sample }
 OfsSound:=Ofs(Sound^);

 OK:=ResetDSP;                   { Reset DSP (soundCard) }
 if Not OK then begin Write(#7,'No card ?');Halt(1);end; { NO CARD DETECTED }
 Spk_on;

 SetPlaySpeed(SAMPLERATE);       { Set Herz Timer for Interrupt $8 }
 ASM cli END;                    { Point Interrupt $8 to My procedure }
  OldTimerInterrupt := InterruptVector[TimerInterrupt];
  InterruptVector[TimerInterrupt] := @PlayData;
 ASM sti END;

 GotoXY(10,10);
 Writeln('Testin: ',Counter:5);
 WriteLn(#13#10,' We Still got: ',MaxAvail,' Memory, load more samples,');
 WriteLn(' That would be ',MaxAvail div 64000,' samples of 64000 bytes
(MaxSampleSize) '); WriteLn(' and ',MaxAvail mod 64000,' bytes of memory,
enough ? ...nah.'); WriteLn(' Sample loaded is ',Size,' Bytes already');
 WriteLn(#13#10' O jeah, press space to play the sample, <Esc> to Quit ');

 repeat
   IF Port[$60]=57 then { SpaceBar is pressed ? }
     begin
       Delay(1);                    { Won't slow the playing }
       GotoXY(10,10);               { Won't slow the playing }
       Write('Testin: ',Counter:5); { Won't slow the playing }
       if DonEplaying then  { it means the sample is played }
            Begin              { Loop Mode }
              Counter:=1;      { First set the counter to 1, the interrupt }
              DonePlaying:=False; { plays as soon as DonePlaying = false }
            end;
     end else                  { if not spacebar is down }
     begin                     { Stop playing and reset the counter }
       DonePlaying:=True;
       counter:=1;
     end;
     if Keypressed Then Ch:=ReaDkEY; { The port doesn't read the key }
 Until Ch=#27;

 CleanUp; { Set the interrupt to original etc. }
 Unload(Sound,Size);  { Free Sample Memory }
 Spk_off; { DAC to output off }
 ClrScr;
end.

[Back to SOUND SWAG index]  [Back to Main SWAG index]  [Original]