Простейшее цифровое эхо

program echo;
uses dsp_dma,getsbinf;
{Ввод звука - 16 бит со знаком, вывод - 8 бит со знаком.}

const
   BufSize   = 2*1024;              { размер буфера DMA }
   TimeConst = 156;             { 156 - примерно 10 кГц }
   HalfBufToFill  : integer = 0; 
                 { которая половина буфера DMA свободна }
   BothBuf        : byte    = 0;
                   { индикатор заполнения обоих буферов }
type
   RecBufType  = array[0..BufSize-1]of integer;
                                { для буфера DMA записи }
   PlayBufType = array[0..BufSize-1]of shortint;
                       { для буфера DMA воспроизведения }
var
   RecBuf  : ^RecBufType;         { буфер DMA для записи}
   PlayBuf : ^PlayBufType;{буфер DMA для воспроизведения}
   inpage,   outpage   : word; {страницы для буферов DMA}
   inoffset, outoffset : word; {смещения для буферов DMA}

{$F+}
procedure SBint;interrupt;
                {обработчик прерывания от звуковой платы}
var
   intstat : integer;
   i : integer;
begin
   Port[base + $04] := $82;
          {проверяем, по какому каналу пришло прерывание}
   intstat := Port[base + $05] and 3;
   BothBuf := BothBuf or intstat;
   if (intstat and 2 <> 0) then begin  {16-битовый канал}
      i := Port[base + $0F];
   end;
   if (intstat and 1 <> 0) then begin {8-битовый канал}
      i := Port[base + $0E];
   end;
   if BothBuf = 3 then begin
                {если прошли прерывания от обоих каналов}
      for i := 0 to BufSize div 2 - 1 do
         PlayBuf^[HalfBufToFill*BufSize div 2 + i] :=
            hi(RecBuf^[HalfBufToFill*BufSize div 2 + i]);
      write(HalfBufToFill,#8);
                {выводим на экран номер половинки буфера}
      HalfBufToFill := HalfBufToFill xor 1;
      BothBuf := 0;
   end;
   if (irq > 8) then  
  {для  IRQ 10, посылаем сигнал EOI во второй контроллер}
      Port[$A0] := $20;
   Port[$20] := $20;  { посылаем EOI в первый контроллер}
end;
{$F-}

var
   SkipLength : longint;
           {размер памяти до границы 64-Кбайт страницы}
   SkipBlock  : pointer;
begin
   writeln(?               Эхо - Sound Blaster 16 в ?,
                                   ?режиме full duplex?);
   writeln(?                   для завершения работы ?,
                                        ?нажмите Enter?);

   GetBlasterInfo;     {определяем характеристики карты}
   if (cardtype <> 6) then begin
            {Проверка, что на плате возможен full duplex}
      writeln(cardtype);
      writeln(
     ?Для работы программы необходим Sound Blaster 16.?);
      halt;
   end;
   if (dma8 = dma16) then begin
      writeln(?Ошибка: совпадение 8-битового и ?,
                              ?16-битового каналов DMA.?);
      halt;
   end;

   SetMixer; {сброс DMAC и установки микшера}

   getmem(SkipBlock,16);
       {проверка, чтобы буферы не пересекали границу 64К}
   SkipLength := $10000 - (seg(SkipBlock^) shl 4) 
                                       - ofs(SkipBlock^);
   freemem(SkipBlock,16);
   if SkipLength > 3*BufSize then 
      getmem(SkipBlock,SkipLength);

   getmem(RecBuf,2*BufSize);
                     {выделение памяти для буфера записи}
   inpage   := ((longint(seg(RecBuf^)) * 16) 
                              + ofs(RecBuf^)) div $10000;
   inoffset := ((longint(seg(RecBuf^)) * 16) 
                               + ofs(RecBuf^)) and $FFFF;

   getmem(PlayBuf,BufSize);
            {выделение памяти для буфера воспроизведения}
   outpage   := ((longint(seg(PlayBuf^)) * 16) 
                             + ofs(PlayBuf^)) div $10000;
   outoffset := ((longint(seg(PlayBuf^)) * 16) 
                              + ofs(PlayBuf^)) and $FFFF;

   fillchar(PlayBuf^,BufSize,0);
                         {очистка буфера воспроизведения}

   EnableInterrupt( @SBint);
   SetupDMA(dma16,inpage,inoffset,BufSize, $54);
                                            {DMA на ввод}
   SetupDSP($BE,$10,BufSize div 2,TimeConst);
                             {16 бит со знаком FIFO моно}
   SetupDMA(dma8,outpage,outoffset,BufSize, $58);
                                           {DMA на вывод}
   SetupDSP($C6,$10,BufSize div 2,TimeConst);
                              {8 бит со знаком FIFO моно}

   readln;

   dspout($D5); {приостанавливаем 16-битовый ввод-вывод}
   dspout($D0); {приостанавливаем 8-битовый ввод-вывод}
   DisableInterrupt;

   freemem(PlayBuf,BufSize);
   freemem(RecBuf,2*BufSize);
   if SkipLength < 3*BufSize then 
      freemem(SkipBlock,SkipLength);
end.