By: Emil Gilliam
Re: adlib digitized sound
------------------------------------------------------------------------

Okay, here we go with some specifics on how to play digitized sound  out 
of the AdLib!  Note:  If you write a program that uses sound boards, you 
should make it recognize the Sound Blaster and use the Sound Blaster DAC 
if  there is one, because it sounds better, so I suggest that  you  only 
use  this method for playing digitized sound out of the AdLib  if  there 
actually  is  a real AdLib.  (Also, I don't know if  this  method  works 
right  on the Sound Blaster, so if there's a Sound Blaster, use the  DAC 
instead!!)

Here's how to play a digitized sample out of the AdLib:

1.) Disable interrupts.  (While we change vectors and things)

2.)  Get  the old interrupt 8 vector and save it.  Set the  interrupt  8 
vector to point to a new interrupt 8 handler (which is described below).  
I  suggest  that you get and set interrupt vectors yourself  instead  of 
calling DOS to do it, because maybe DOS tries to hook itself into  every 
vector  somehow (?) and that can be a problem when our new  interrupt  8 
vector will be called thousands of times a second.

3.)  Okay,  now we're going to set up the AdLib  for  playing  digitized 
sound. As many of you know, the AdLib uses I/O ports 388h and 389h (388h 
is the index register, 389h is the data, and 389h is write-only).

Note:  After you write a register number to port 388h, you need to  wait 
3.3  microseconds for the card to respond.  After you write a  value  to 
one  of  the registers at port 389h, you need to  wait  23  microseconds 
before you can write to another register.  You can wait 3.3 microseconds 
by doing 6 "IN AL,DX" instructions from port 388h, (I think that each IN 
AL,DX  from port 388h will take the same amount of time no  matter  what 
the  speed of the computer is [?] because it takes the card a  while  to 
get the value of the status register of the AdLib, which is what you get 
when  you  read  from port 388h.  But we don't need the  value  of  that 
status  register, we just need to IN AL,DX from it 6 times for  the  3.3 
microsecond  wait.)   You can wait the 23 microseconds by doing  35  "IN 
AL,DX"  instructions  from port 388h.  By the way, you  should  put  all 
these  IN AL,DX's in a row rather that in a loop, because the loop  will 
take time and you'll wait more time than necessary.

Here's how to set up the AdLib card for playing digitized sound.   Write 
21h to register 20h (Sets MULTI=1,AM=0,VIB=0,KSR=0,EG=1 for operator 1).  
Then  write 0F0h to register 60h (Sets attack rate to 15 and decay  rate 
to 0 for operator 1, makes sense because we want the sine wave to  start 
immediately and we never want it to stop), and write 0F0h to register 80h 
(sets the sustain level to 15 and the release rate to 0 for operator 1).

Then, write 01h to register 0C0h.  (Feedback=0 and Additive Synthesis is 
on  for voice 1 [which is operator 1 and operator 4]).  Then write 0  to 
register 0E0h.  (Waveform=regular sine wave for operator 1.)  Then write 
3Fh to register 43h (sets total level=63 and attenuation for operator 4, 
don't  ask me why this is done, but I do know that operator  4  combines 
with operator 1 to make one of the voices).

Then write 1 to register 0B0h.
Then write 8Fh to register 0A0h.
Then write 2Eh to register 0B0h.  (Set KEY ON)
(I don't know why we have the multiple writes to register 0B0h.

This sets the frequency of operator 1, the speed of the sine wave.

4.)  Immediately  (right  after  writing  2Eh  to  register  0B0h,   and 
interrupts  are still disabled), wait until 952h 8253 timer  ticks  have 
passed.   I  think that what this does is wait for the sine  wave  being 
generated by operator 1 to get all the way to the top.

5.)  Now that the sine wave of operator 1 is at the top, we're  immedia
tely going to change the frequency of operator 1 to 0, so that the  sine 
wave  is  stuck at the top.  Write 20h to register 0B0h and write  0  to 
register  0A0h.  The  20h in register 0B0h keeps operator  1  in  KEY-ON 
state.

6.)  We're  almost  done setting up.  Divide 1193180 by  the  number  of 
samples  per  second and write that value into timer channel 0  (of  the 
8253 chip). This will change the number of times per second interrupt  8 
is called to the number of samples per second.  Interrupt 8 is going  to 
be  called for each sample, and it will play one sample each  time  it's 
called.

7.) Enable interrupts, and wait until the sample is done playing.  (Your 
interrupt 8 handler should set some sort of flag when it's done.)

8.)  Disable interrupts, set the interrupt 8 vector to what it  used  to 
be, write 0 to 8253 timer channel 0 (to make interrupt 8 go 18.2 times a 
second), and we're done!

-----

Okay.  The new interrupt 8 handler should do this.

1.)  Save any used registers.  (I know that the CPU will probably be  in 
the waiting loop when interrupt 8 is called, and the waiting loop  might 
use no registers, but save registers just to be on the safe side because 
the  CPU  might be in the middle of processing a keyboard  interrupt  or 
something like that.)

2.) Get a 6-bit sample from the digitized sound sample.  (If you have an 
8-bit  sample, just shift each sample right two bits.  I assume  that'll 
be the case because you can write a program to play digitized sound that 
writes  values to the Sound Blaster DAC if there is a Sound Blaster  and 
use  this method if there is an AdLib.)  Write that number  to  register 
40h.  (The top 2 bits should be 0).

Note:  An  alternative is to have the initialization code write  40h  to 
port  388h  right  before enabling interrupts, and  then  all  that  the 
interrupt  8 handler has to do is write values to port 389h  instead  of 
having to write 40h to port 388h every time.

3.)  Write  20h to port 20h (8259 non-specific EOI),  restore  any  used 
registers, and get the heck outta there with IRET.

-----

The  only  problem with this is that the computer's clock will  be  held 
while  the  sample  is playing, because the regular  interrupt  8  isn't 
executing  while the sample is playing, but if that's a problem you  can 
circumvent that yourself by getting the length of the sample and  adding 
that to the computer's time when you're done, or by possibly getting the 
CMOS  time.  That's  something  I still don't know,  on  AT's  does  the 
computer  get the time from the CMOS whenever you have to get the  time, 
or  does  the computer just get the time on bootup and write  that  into 
some  low-memory  address for interrupt 8 to increase  every  time  it's 
called?   On AT's, does interrupt 8 even increase any time variables  at 
all?  Does it need to?  If anyone knows anything about that, please  let 
me know.

-----

   The  playing of digitized sound out of the AdLib doesn't have to  use 
operator  1,  it  can use any operator.  It just has  to  use  the  same 
operator  all  the  way  through.  (Except for when  you  write  3Fh  to 
register  43h, don't ask me why that was in what I disassembled...   but 
if you change operator numbers, you'll have to change this reference  to 
operator  4 to the operator that the other one is "paired  up  with"...)  
That means that you can play digitized sound and music at the same  time 
out of the AdLib!  Just play digitized sound as usual and while  waiting 
for  the  sample to play, use the other voices to play music!   You  can 
even play two samples at the same time by using two different  operators 
and  having  your interrupt 8 handler write values to  both!   Theoreti
cally,  you can play as many samples as you want and music at  the  same 
time...   I'm  sure  that in practice that would be VERY  hard  to  pull 
off...  almost more trouble than it's worth... but maybe someone  should 
give it a try!


And  there  you have it.  I hope that THAT is pretty complete!   But  if 
anyone still needs any more information, like on how to read or write to 
the 8253 timer chip, let me know.                                                   
                                                                           
                                                Emil Gilliam               
