VOGONS


CMS/GameBlaster emulation thread

Topic actions

First post, by Jepael

User metadata
Rank Oldbie
Rank
Oldbie

Thanks to NewRisingSun's DosBox patch to log CMS/GameBlaster audio into VGM files, there is now a way to get some source material for experimenting.

So I wrote a simple program that can read the DosBox-dumped VGM file and understand barely enough of it to render some GameBlaster audio output to a file.

At first try it was just outputting garbage, but after fixing one stupid bug, try to guess the look on my face when I first heard this!

The flac file is post-processed in Audacity, it is resampled from 3.58MHz to 44.1kHz and then high-pass filtered to remove DC offset.
So the renderer does not (yet) output audio file that could be played normally.

Let me know what you think.
I was kind of hoping it would sound somewhat better though, but what can you expect from square waves, no matter how great they are resampled/processed.

Also, this made me realize that VGM file can't properly handle external triggering of envelope generators, but fortunately this music does not use envelope generators at all.

Reply 1 of 61, by PhilsComputerLab

User metadata
Rank l33t++
Rank
l33t++

I just listened to it and compared it with a recording of mine, it sounds great!

A bit quiet, maybe normalise / amplify before exporting. This is really easy with Audacity.

But yea, if DOSBox can sounds like this, that would be very awesome indeed 😀

Thank you for helping.

YouTube, Facebook, Website

Reply 2 of 61, by OPLx

User metadata
Rank Member
Rank
Member

@Jepael: Here's the tool I wrote to play around with the SAA1099's registers (channels 0 through 2 only). You can use mouse and keyboard (space bar sets the values). DOSBox 0.74 has a bug with the mouse position in the 40x25 text mode so I would recommend using a different build. Hope this helps out with the emulation work. 😀

Attachments

  • Filename
    CMSVIEW.ZIP
    File size
    19.35 KiB
    Downloads
    154 downloads
    File license
    Fair use/fair dealing exception

Reply 4 of 61, by Jepael

User metadata
Rank Oldbie
Rank
Oldbie

Any specific questions about how the real Philips SAA1099P chip works?
I've got a CT-1320 and some measuring equipment borrowed.

Here's how the PWM amplitude is applied to output tone for instance:

setup.jpg
Filename
setup.jpg
File size
99.89 KiB
Views
2942 views
File comment
Reverse engineering
File license
Fair use/fair dealing exception

Reply 5 of 61, by Jepael

User metadata
Rank Oldbie
Rank
Oldbie

I haven't found a believable documentation for it, so took a whack at the SAA1099 noise generator and decided to document it.
It is possible that the exact noise generator algorithm was already known, but it may be written in different form (Galois vs Fibonacci, left shift vs right shift, etc).

I controlled the chip to generate noise at some sane speed (about 3500 Hz update rate) and recorded it on another PC.

Visual inspection revealed that the noise has a period of approximately 262000 bits. That's about 2^18 bits, not a coincidence. Also, the longest low period encountered was 17 bits, and longest high period encountered was 18 bits.

This revealed it most likely is a 18-bit maximal length LFSR with period of 262143 bits. Not all sound chips use a maximal length LFSR, so never assume it is. Given the longest periods seen, the 18-bit period must be ones out from the LFSR, and the 17-bit period must be zeroes, as an LFSR can't have all bits zero or it's stuck with zeroes.

And since there are many tap configurations that can do it, most likely it's one with least amount of feedback taps, because why waste precious silicon area. Turns out the minimum tap count is two and there are only two different tap configurations (well, just one and it's reverse). After finding a good starting point in the stream, the next bits easily reveal the feedback taps, so it was easy to determine which one it is with pen and paper.

int saa1099_prng(void)
{
static uint32_t lfsr=1; // seed unknown, must be non-zero

/*
SAA1099P noise generator as documented by Jepael
18-bit Galois LFSR
Feedback polynomial = x^18 + x^11 + x^1
Period = 2^18-1 = 262143 bits
Verified to match recorded noise from my SAA1099P
*/

if (lfsr&1)
{
lfsr=(lfsr>>1)^0x20400;
return 1;
}
else
{
lfsr=(lfsr>>1);
return 0;
}
}

Then a comparison of recorded noise versus noise generated with above algorithm:

SAA1099_noise.png
Filename
SAA1099_noise.png
File size
102.14 KiB
Views
2886 views
File comment
SAA1099 noise comparison
File license
Fair use/fair dealing exception

Looks like a match, and it continues to match throughout the waveform, until the 262143-bit noise period ends. I can't believe I actually got it to match at first try.

What's next? I don't know, but I ran into some undocumented feature that enables higher noise frequencies than the ones documented.. Oh and current emulators tend to understand "mixing" noise and tone channels rather differently how the chip actually does it.

Reply 6 of 61, by OPLx

User metadata
Rank Member
Rank
Member

Pretty interesting findings! If you have a chance, would you mind elaborating how the undocumented noise at higher frequencies is triggered? I'm only familiar with setting the noise generator control to 0x03 (for generator 0) at address 0x16 to drive the frequencies higher.

Reply 7 of 61, by Jepael

User metadata
Rank Oldbie
Rank
Oldbie
OPLx wrote:

Pretty interesting findings! If you have a chance, would you mind elaborating how the undocumented noise at higher frequencies is triggered? I'm only familiar with setting the noise generator control to 0x03 (for generator 0) at address 0x16 to drive the frequencies higher.

Thanks, sure thing.

Hmm, I think you can only get lower frequencies than the three presets when the noise is clocked by tone generator.
Fixed rates for clocking the noisegen are X=27965 Hz, X/2, and X/4, while with a tone generator clocking it you can get up to about X/2 maximum as well.

The trick is to set the tone generator reset bit while the tone generator is used to clock the noise.
Apparently that makes the noisegen to be clocked by the undivided octave clock, so lowest octave 0 is same as rate X. And each octave higher doubles the noise frequency. You can't hear much at the highest octaves though, as they are beyond human hearing range 😀
But the downside is, you can't generate any tones when the tonegens are in reset, so it's not a very useful trick.

Reply 8 of 61, by OPLx

User metadata
Rank Member
Rank
Member
Jepael wrote:
Thanks, sure thing. […]
Show full quote

Thanks, sure thing.

Hmm, I think you can only get lower frequencies than the three presets when the noise is clocked by tone generator.
Fixed rates for clocking the noisegen are X=27965 Hz, X/2, and X/4, while with a tone generator clocking it you can get up to about X/2 maximum as well.

The trick is to set the tone generator reset bit while the tone generator is used to clock the noise.
Apparently that makes the noisegen to be clocked by the undivided octave clock, so lowest octave 0 is same as rate X. And each octave higher doubles the noise frequency. You can't hear much at the highest octaves though, as they are beyond human hearing range 😀
But the downside is, you can't generate any tones when the tonegens are in reset, so it's not a very useful trick.

That's really interesting to know. I'll have to try it out to hear what it sounds like. The more I understand about this chip, the more I come to appreciate what it can actually do. 😀

Reply 9 of 61, by Lord Nightmare

User metadata
Rank Newbie
Rank
Newbie

Jepael: I updated the saa1099 core in MAME to use the correct lfsr polynomial: https://github.com/mamedev/mame/commit/c11c22 … e3be48ba31786f8
I also added a note about the fact that if the noise channels are set to be clocked by a tone channel, and the 0x1c bit 1 reset bit is set, they get clocked by the undivided octave clocks, but I did not implement this yet.
Are there any other obvious issues I should try to fix from elsewhere in this thread?

LN

"When life gives you zombies... *CHA-CHIK* ...you make zombie-ade!"

Reply 10 of 61, by Jepael

User metadata
Rank Oldbie
Rank
Oldbie
Lord Nightmare wrote:
Jepael: I updated the saa1099 core in MAME to use the correct lfsr polynomial: https://github.com/mamedev/mame/commit/c11c22 … e […]
Show full quote

Jepael: I updated the saa1099 core in MAME to use the correct lfsr polynomial: https://github.com/mamedev/mame/commit/c11c22 … e3be48ba31786f8
I also added a note about the fact that if the noise channels are set to be clocked by a tone channel, and the 0x1c bit 1 reset bit is set, they get clocked by the undivided octave clocks, but I did not implement this yet.
Are there any other obvious issues I should try to fix from elsewhere in this thread?

LN

The tone+noise mixed together on same channel is not very easy to explain. PWM of amplitude modulator is emulated as pure amplitude level.

Tone off, noise off : output value idles high. Easy, it's zero.

Tone on, noise off : output value toggles at tone frequency, between idle high, and active low. Low period is modulated by 16-step PWM amplitude (max vol 15 means it's 15 PWM times low and 1 PWM time high. min vol 0 means silence, output value idles high). Easy, it's tone at max volume.

Tone off, noise on: output value comes from noise bit, between idle high, and active low. Low period is modulated by 16-step PWM amplitude. Easy, it's noise at max volume.
As described in my previous post, I am positive than noise generator output bit 1 is the idle high, and bit 0 is the active low.

Tone on, noise on:
-when tone value is at one state (high?), output idles high. The noise has no effect here. (So it is not a sum of tone+noise).
-when tone value is at the other state (low?), and noise is low, the signal is low at full amplitude set by PWM.
-when tone value is at the other state (low?), and noise is high, the signal is high every other PWM period, and low every other period at amplitude set by PWM. So this averages the signal to be low at only half the amplitude.

Maybe these two recordings will help:
http://www.mediafire.com/file/mk0ccd3e1oyqa44 … oth_manual.flac
http://www.mediafire.com/file/ck45apx2faimzuw … _both_auto.flac

Both recordings have the following sequence: tone burst, noise burst, tone/both/noise, the other one has also noise/both/tone sequence at the end.

Reply 11 of 61, by superfury

User metadata
Rank l33t++
Rank
l33t++

I've tried to implement all but that latest post(about tone/noise off/on states, which will be using PWM). I remember it was originally based on Dosbox, but looking at Lord Nightmare's commit and saa1099.cpp code, it seems that either Dosbox uses the same codebase or I've based mine off mame's. Can't remember though(many years ago, before moving to bitbucket and starting to use git).

I have this strange problem: when I try to play the Monkey Island music(besides being slow due to 14MHz/2 sampling rate being downsampled and rendered at ~44.1kHz by ticking at the hardware base 14MHz rate that's used), I get way too high tones for some reason? Anyone can see what's going wrong?

https://bitbucket.org/superfury/unipcemu/src/ … ter.c?at=master

It uses a base 7MHz(14MHz divided by 2) to render it's samples(using PCM), then high-pass filters it by 18.2Hz, then low-pass filters it at the nyquist rate before sending the samples at ~44.1kHz to the renderer's buffer.

Anyone can see what's going wrong? Why are the tones higher than it's supposed to be?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 12 of 61, by Jepael

User metadata
Rank Oldbie
Rank
Oldbie
superfury wrote:

Anyone can see what's going wrong? Why are the tones higher than it's supposed to be?

2*GAMEBLASTER.baseclock/512

That's exactly two times too big, result should be 13982.xxx Hz. Try if it fixes it.
Sorry I don't have a personal bitbucket account to post a pull request.

Reply 13 of 61, by Stiletto

User metadata
Rank l33t++
Rank
l33t++
superfury wrote:

I've tried to implement all but that latest post(about tone/noise off/on states, which will be using PWM). I remember it was originally based on Dosbox, but looking at Lord Nightmare's commit and saa1099.cpp code, it seems that either Dosbox uses the same codebase or I've based mine off mame's. Can't remember though(many years ago, before moving to bitbucket and starting to use git).

Yes, DOSBox's code for SAA1099 was based off MAME's.

Don't worry, that code was relicensed in MAME as being licensed under the 3-clause BSD license as of March 30, 2016. Prior to that, DOSBox wasn't following our license. 😜

"I see a little silhouette-o of a man, Scaramouche, Scaramouche, will you
do the Fandango!" - Queen

Stiletto

Reply 14 of 61, by superfury

User metadata
Rank l33t++
Rank
l33t++

I've improved the Game Blaster sampling(removing the multiplication of 2 on the frequencies(both square wave output and RNG) and resampling to support seperate, independant Game Blaster and Output rendering(the same way as the Sound Blaster rendering):
https://bitbucket.org/superfury/unipcemu/src/ … ter.c?at=master

One strange thing: with a divider of 2 the output sound fine(full quality rendering), but when I enable the commented out 512 divider(to get a low samplerate for testing), I get ticking without sound outputted(it should be rendering at ~29kHz on the Game Blaster and recording(rendering to output, using the MHZ14_RENDERTICK define) at ~44.1kHz). Anyone can see what's going wrong there? I simply wanted the two decoupled to do testing on it(Rendering at ~7MHz is pretty heavy, even on a 4.0GHz i7 CPU, slowing down emulation greatly).

Edit: Managed to fix the bug: it was loading the frequency modifier with the base rate(which never changes, always being ~7MHz) instead of the required sample rate, thus producing samples at a 7MHz rate instead of a ~29kHz rate when the divider is set to 14MHz/512.

The only problem still left is that rendering the Game Blaster is still pretty heavy, even on even a good CPU(4.0GHz i7), causing it to slow down and having a heavy latency problem. Is there any way to improve that? This currently amounts to ~7M fmodf executions each second, taking up almost all execution time on the single CPU it's ran on.

https://bitbucket.org/superfury/unipcemu/src/ … ter.c?at=master

It currently low-pass filters it at 22kHz(nyquist frequency of 44.1kHz), then high-pass at 18.2Hz to remove DC offset.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 15 of 61, by Stiletto

User metadata
Rank l33t++
Rank
l33t++
Stiletto wrote:

Yes, DOSBox's code for SAA1099 was based off MAME's.

Don't worry, that code was relicensed in MAME as being licensed under the 3-clause BSD license as of March 30, 2016. Prior to that, DOSBox wasn't following our license. 😜

One thing I'll add is, only the SAA1099 code present in MAME from March 30, 2016 and newer is relicensed 3-clause BSD, so I guess to be technical, to use the code you must pull from March 30, 2016 revision or newer, then apply your changes, to use the relicensed code. Or verify that there's no changes between your code and the code in MAME.

"I see a little silhouette-o of a man, Scaramouche, Scaramouche, will you
do the Fandango!" - Queen

Stiletto

Reply 16 of 61, by superfury

User metadata
Rank l33t++
Rank
l33t++

I simply used a semi-clean room design: since I'm quite forgetful(forgetting most things within a minute), I read the original code, remember the things that happen(generic formulas etc.), then write my own code based on the formulas remembered(can be clearly seen in my implementation of the volume envelope(the repeating numbers) in UniPCemu. The only cause for even reading the code is because I can't find any documentation on the hardware(strange though, since it's such a generic Phillips chip).

Edit: A bit optimized version:
https://bitbucket.org/superfury/unipcemu/src/ … ter.c?at=master

Optimized signal generation(square waves precalculations) mostly. Also disabled the high-pass filter of 18.2Hz(It removes lots of sounds generated during the The Secret of Monkey Island opening, because of an unknown reason). The full 7MHz rate is still too heavy to use effeciently, unfortunately.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 17 of 61, by superfury

User metadata
Rank l33t++
Rank
l33t++

This is my latest revision of UniPCemu's (originally loosely based on Dosbox/MAME's Game Blaster core) sound generation. I've optimized it a lot to be able to run decently at high frequencies(~3.57MHz(14318180Hz divided by 4) samplerate).

I've implemented the post of Jepael of 26 Jan 2017, 22:55 about "Tone off/on, Noise off/on" combinations. I hope I understood it correctly?

https://bitbucket.org/superfury/unipcemu/src/ … ter.c?at=master

Also the filters have been disabled(they're too heavy on CPU load). I've recorded some playback(resampled to 44.1kHz with lowest-neighbor resampling, no filtering(except for 18.2Hz low-pass after resampling to 44.1kHz)).

Some audio captures from UniPCemu(no filtering, except for 18.2Hz high-pass filtering), lowest-neighbor resampled to 44.1kHz:
New mixing method(based on Jepael's latest post on 26 Jan): https://www.dropbox.com/s/5ty5anv28ogpjrx/rec … 0_1902.wav?dl=0
Original Dosbox/MAME method, commented out in the code: https://www.dropbox.com/s/t7sn6ibupxp6rgg/rec … 0_1902.wav?dl=0

Edit: For some reason, the recordings sound a little bit off. Anyone can see what's the cause for it? I know that the sample rate is divided by the frequency(floating point) that's set and rounded down(uint_32 storage) to create the overflow number(after how many samples to flip the PCM output from 0 to 1 or vice versa, using a simple XOR 1 operation), but this shouldn't be much of a problem, as it uses about 3.5MHz samplerate(over 350 times the nyquist frequency of 22050Hz to represent the signal), so it should only be off a very tiny bit? Anyone can see what's going wrong? This happens to both the Dosbox/MAME formula-generated output and the current one(based on Jepael's post).

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 18 of 61, by superfury

User metadata
Rank l33t++
Rank
l33t++

This is what I get when I generate a 500Hz square wave using the current algorithm(pure output, unmodified):

Filename
testgameblaster500hz.zip
File size
9.96 KiB
Downloads
105 downloads
File comment
500Hz square wave generated.
File license
Fair use/fair dealing exception

Using this code:

	//Load test wave information for generating samples!
GAMEBLASTER.chips[0].squarewave[7].timeout = (uint_32)(__GAMEBLASTER_BASERATE/(double)(500.0f*2.0f)); //New timeout!
GAMEBLASTER.chips[0].squarewave[7].timepoint = 0; //Reset!
GAMEBLASTER.chips[0].squarewave[7].freq = 500.0f; //We're updated!

WAVEFILE *testoutput=NULL;

uint_32 i;
byte signal;

testoutput = createWAV("captures/testgameblaster500hz.wav",1,(uint_32)__GAMEBLASTER_BASERATE); //Start the log!

for (i=0;i<__GAMEBLASTER_BASERATE;++i) //Generate one second of data!
{
signal = getSAA1099SquareWave(&GAMEBLASTER.chips[0],7);
writeWAVMonoSample(testoutput,signed2unsigned16(signal?(sword)32767:(sword)-32768)); //Write a sample!
}

closeWAV(&testoutput); //Close the wave file!

Just before initializing all channel frequencies(right after the initSoundFilter calls).

Is the generated signal correct? According to my hex editor(WavePad refuses to open it, while Windows 10's Groove Music opens and plays it without problems), it switches state every 3579 samples, with a full wave taking 7158 samples(At a 14MHz/4 rate, thus a 3579545Hz samplerate), thus amounting to a 500,xxx...Hz signal?

Edit: So the input signal seems correct? Then why is the output signal in the Game Blaster emulation so strange(off-tune)?

Edit: I've recorded the Monkey Island opening from the Secret of Monkey Island and the first song(using UniPCemu's normal 44.1kHz recording, which is only high-pass filtered after resampling to 44.1kHz).

Filename
MonkeyIsland_3.5MHzResampledTo44.1kHz_UniPCemu_20170131_1641.zip
File size
2.24 MiB
Downloads
96 downloads
File comment
Monkey Island opening and first songs, ~3.5MHz resampled to 44.1kHz, only high-pass filtered at 18.2Hz after resampling to remove DC.
File license
Fair use/fair dealing exception

Anyone can see why the sound isn't as sharp as Dosbox's and MAME's? Is this simply because the sample period(in samples for each wave half) is rounded down, instead of floating point? (Compared to https://www.youtube.com/watch?v=SEyIcNDDAB8 )

Original code was based on https://sourceforge.net/p/dosbox/code-0/HEAD/ … gameblaster.cpp

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 19 of 61, by superfury

User metadata
Rank l33t++
Rank
l33t++

I've lowered the Game Blaster 3.5MHz signal by a factor of 16(since the samples already have 16-level PCM applied, Dosbox-style), making low-pass 22050Hz filtering possible again with reasonable speed.

Sound Recording of UniPCemu commit 2017/02/01 17:48 playing the sound at 3.5MHz further divided by 16(as the samples already have 16-level PCM applied when rendering the Game Blaster), low-pass filtered at 22050Hz(nyquist frequency of 44.1kHz), high-pass filtered at the emulator's 18.2Hz(minimum PC-speaker frequency):

Filename
MonkeyIsland_3.5MHzDIV16_lowpass22kHz_ResampleTo44.1kHz_UniPCemu_20170201_1748.zip
File size
3.51 MiB
Downloads
97 downloads
File comment
The Secret of Monkey Island opening, low-pass filter at 22050Hz, high-pass filtered at 18.2Hz at 3.5MHz/16 base frequency.
File license
Fair use/fair dealing exception

Although emulation should be more accurate now, for some reason the sound is still a bit strange, compared to the recording at the start of this thread. Anyone can see why my emulation is off? What's going wrong?

https://bitbucket.org/superfury/unipcemu/src/ … ter.c?at=master

Anyone? Jepael?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io