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