
{
If anyone can fill me in on how to output in stereo, I'd be very
appreciative... I've heard that port $220/$221 is for left channel,
$222/$223 is for the right, but that doesn't make any sense...does it?

This code makes R2D2 noises on a SoundBlaster until you press ESC.

{adapted from SS4CH.PAS by Frank Hirsch}
USES Crt;

const sampleSize=4096;
var sampleData:array[0..sampleSize-1]of byte;
const samplePos:longint=0;
var sampleSpeed:longint;
var sampleDelta:longint;

const resetPort =$226;
const readPort  =$22A;
const writePort =$22C;
const dataAvailPort=$22E;

function readByte:byte;begin
 repeat until shortInt(port[dataAvailPort])<0;
 readByte:=port[readPort];
 end;

procedure initDSP;begin
 port[resetPort]:=1;
 delay(1);
 port[resetPort]:=0;
 repeat until readByte=$AA;
 end;

var counter:longint;

procedure timerInt;assembler;asm
  push ax
  push bx
  push dx
  push di
  push ds
  push es
  mov ax,seg @DATA
  mov ds,ax

  mov es,[segB800]
  xor byte ptr es:[0],$21

  mov bx,[word ptr samplePos+2]
  mov ah,byte ptr sampleData[bx]
  mov dx,[word ptr sampleSpeed]    {next sample byte}
  add [word ptr samplePos],dx
  adc bx,[word ptr sampleSpeed+2]
  and bx,[sampleSize-1]
  mov [word ptr samplePos+2],bx
  mov bx,[word ptr sampleDelta]
  add [word ptr sampleSpeed],bx
  mov bx,[word ptr sampleDelta+2]
  adc [word ptr sampleSpeed+2],bx
  mov dx,writePort
 @P2:              {ready for output byte?}
  in al,dx
  test al,$80
  jnz @P2
  mov al,ah
  out dx,al

  mov al,$20       {process interrupt}
  out $20,al
{  sti}
                   {prep NEXT output}
 @P1:              {ready for command?}
  in al,dx
  test al,$80
  jnz @P1
  mov al,$10       {set up a DAC output}
  out dx,al

  db $66; inc word ptr [counter]

  pop es
  pop ds
  pop di
  pop dx
  pop bx
  pop ax
  iret
  end;

var
  vec08:pointer absolute 0:8*4;
  old08:pointer;

procedure setTimerTics(tics:word);begin
  asm cli; end;
  port[$43]:=$36;
  port[$40]:=lo(tics);
  port[$40]:=hi(tics);
  asm sti end;
  end;

procedure setTimerFreq(freq:word);begin
  setTimerTics(succ(word($1234DC div freq)));
  end;

procedure stopTimer;begin setTimerTics(0); end;

procedure writeByte(b:byte);begin
  repeat until shortInt(port[writePort])>=0;
  port[writePort]:=b;
  end;

procedure speaker(onOff:boolean);begin
  if onOff then writeByte($D1)
  else writeByte($D3);
  end;

var i,j,n:word;

const rate=16384;

procedure note(freq,dur,slide:longint);
begin
  counter:=0;
  sampleSpeed:=freq*sampleSize*(65536 div rate);
  sampleDelta:=slide;
  dur:=(dur*rate)div 1000;
  repeat
    if port[$60]=$81 then break;
    until counter>=dur;
  end;


begin
 initDSP;
 for i:=0 to sampleSize-1 do
   sampleData[i]:=round(sin(i*pi/(sampleSize shr 1))*127.5+127.5);
 old08:=vec08;
 speaker(true);
 writeByte($10);  {prep sb for data}
 asm cli end;
 vec08:=@timerInt;
 asm sti end;
 setTimerFreq(rate);
 repeat
   case random(4) of
     0:note(random(1900)+60,(random(2)*80)+40,integer(random(3))-1);
     1:note(random(800)+450,(random(2)*80)+140,integer(random(2049))
                                                              -1024);
     2:note(0,(random(2)+1)*40,0);
     3:note(random(30)+15,(random(2)*80)+40,random(2));
     end;
   until port[$60]=$81;
 stopTimer;
 asm cli end;
 vec08:=old08;
 asm sti end;
 speaker(false);  {it's probably gonna eat this as data}
 speaker(false);
 end.
