I finally got my 8253 timer project finished. One minor hurdle that I didn't anticipate is that MESS's abstraction of the host computer's soundcard and its emulated CPU aren't synchronized. The soundcard is supposed to request samples from the emulated CPU at 44.1KHz, but according to the emulated CPU it seems to be doing it slightly faster than that (or the emulated CPU is running slightly slow by the soundcard's standard) so my buffer keeps underrunning.
The way to fix this is to tell the CPU to generate samples at a slightly higher rate (according to its clock) so that the soundcard and the CPU stay in sync. The trouble is, I don't know exactly how much faster the soundcard is than the CPU. Besides which, it seems to fluctuate and the ratio may even be completely different on a different machine.
The next thing I tried was dynamically adjusting the sample rate 60 times a second to the value which was calculated for the previous time-period. The problem with this was that the variations in the sample rate could sometimes be quite large, and the resulting "flutter" of the emulated computer's soundtrack sounded absolutely awful. Also, the buffer might now be slightly less full than optimal (leaving little room for further error) or slightly more full than optimal (causing the soundtrack to "lag" behind the graphics a bit). Clearly the most elegant way to solve this would be to smooth out these variations in such a way that the average sample rate over long time would be correct.
Next, I set up a running total of the "drift" time, and adjusted the sample rate in proportion to this. Of course, this caused the sample rate to oscillate around the correct value - I needed some damping to remove "energy" from the system. A couple of pages of calculations later I had modelled the system with a second order differential equation and solved for the critical damping value. After some tweaking of the buffer length and time-constant values it now works wonderfully - no buffer underruns, no noticable flutter and the graphics and sound are fairly well synchronized.
The only problem now is the aliasing - the 8253 timer in the PC can generate frequencies of up to almost 600KHz, and because of the rather unscientific way I'm downsampling that high-frequency waveform down to 44,100Hz (roughly) all the harmonics too high for the soundcard to play are aliased down to frequencies between 0 and 22,050KHz (roughly) so when you attempt to play a square wave, you get a whole pile of harmonics you weren't expecting and it sounds rather less than perfect. Since the sampling rate drifts up and down, playing a constant high-pitched tone makes all sorts of swooping noises. I wonder if it would be feasable to use a proper finite impulse response filter to downsample while removing all the frequencies that are too high-pitched to play...
For those rare few of you for who are not interested in all this geek stuff, here are some rather spectacular mazes.