• Please review our updated Terms and Rules here

Creative Music System (C/MS)???

deathshadow

Veteran Member
Joined
Jan 4, 2011
Messages
1,378
Does anyone have ANY information on actually programming these? I'm working on trying to have a fairly complete 'early PC' sound library and this would round out my current list...

Right now I have PC speaker and Tandy/Jr. Sound complete, and am working on Adlib and planning a softsynth for speaker/covox/DAC -- but I'd like to get the pre-adlib creative side of the equation up and running too.

Fun part for me right now is going to be determining if a system is fast enough to emulate tandy sound on Speaker/Covox -- I know how to do the emulation, but not entirely sure what the minimum system spec is going to be using my code...

Though that's nowhere NEAR as much fun as trying to 'reclaim' some extra RAM on 64k systems -- anyone know how to figure out where in memory command.com is located without doing a manual scan? :D
 
Does anyone have ANY information on actually programming these? I'm working on trying to have a fairly complete 'early PC' sound library and this would round out my current list...

Right now I have PC speaker and Tandy/Jr. Sound complete, and am working on Adlib and planning a softsynth for speaker/covox/DAC -- but I'd like to get the pre-adlib creative side of the equation up and running too.

Fun part for me right now is going to be determining if a system is fast enough to emulate tandy sound on Speaker/Covox -- I know how to do the emulation, but not entirely sure what the minimum system spec is going to be using my code...

Though that's nowhere NEAR as much fun as trying to 'reclaim' some extra RAM on 64k systems -- anyone know how to figure out where in memory command.com is located without doing a manual scan? :D

I have a booklet that came with my Soundblaster 1.5 with CMS chips entitled "C/MS Programming Information" with everything you need including sample code. See attached.
 

Attachments

  • CMS Programming Information.zip
    82.3 KB · Views: 5
Last edited:
Thanks, that's just what I needed, though I thought the 1.5 didn't come with C/MS.... or was that the one where the sockets were present and you had to buy it separately?

In fact the sockets were present on both the SB 1.5 and 2.0, but the 2.0 requires an additional PAL chip for I/O addres decoding controll for the CMS chips. Because of this, adding CMS chips to a SB 2.0 without the PAL installed will not work, and therefore SB 2.0 cards with these chips installed are quite rare.
 
Hmm. Trying to translate their 'notes' / frequency values into actual frequencies, but their numbers don't make any sense...

A..G = 3,31,58,83,107,130,151,172,191,209,226,242

The gap between values DROPS as the notes go up -- that's inverse from how frequencies actually work... Does anyone know how I'd translate that into actual frequencies, or more importantly how to take a frequency and turn it into those numbers WITHOUT some fat-ass lookup table? I can handle octave conversions (simple mul/div by two) but I'll be damned if I can find a relationship between those numbers and the note frequencies.

-- edit --

AND those numbers for a through G do NOT match the ones in the chip documentation?!?
 
Hmm. Trying to translate their 'notes' / frequency values into actual frequencies, but their numbers don't make any sense...

A..G = 3,31,58,83,107,130,151,172,191,209,226,242

The gap between values DROPS as the notes go up -- that's inverse from how frequencies actually work... Does anyone know how I'd translate that into actual frequencies, or more importantly how to take a frequency and turn it into those numbers WITHOUT some fat-ass lookup table? I can handle octave conversions (simple mul/div by two) but I'll be damned if I can find a relationship between those numbers and the note frequencies.

These early squarewave synthesizers are actually counters, and the number is how many ticks per wave, or how many ticks between each change of state. The octave bits are used to divide the main input clock by factors of two, which will effectively change the octave.

Because of this, the pitch of the output solely relies on the input clock. It may well be that the CM/S and SB cards clocks them at something else than the chip documentation suggests.
 
I can see that on the output frequency skew -- but not on the gap BETWEEN frequencies. Even as a overflow counter the curve would not be backwards... If you plot A1 to A2 in actual frequencies (110 to 220) you'll find it's a concave curve A# to B being more than A to A#.... you plot thier input numbers for frequencies, it's convex -- the gap for higher notes is smaller, not larger.

I'm trying to see if I can get a trig function to give me that curve or to find some correlation so I can convert an actual frequency to their "frequency number" on the fly... The relationship makes no sense compared to every other audio device I've dealt with. (including adlib -- which is sad when adlib makes more sense). This is gonna suck if I end up having to use a lookup table.
 
i learned how these types of synth chips worked when writing my NES emulator. yeah, they go by counters so it's usually basically a division of the clock input it's receiving. although it can get more complex than that. those big skews are strange though. i'll have a look at the doc and see if i can figure out wtf is going on there.
 
These old counter-based sound generators had certain limitation when it came to the tune of the lower-pitched tones. Perhaps the SAA-1099 tries to compensate for this with some well designed atrimetric between the input data and the counters?
 
Last edited:
I charted it out, and the deviation is wierd in that it is LITERALLY the exact opposite of the octave skew...

Which is to say if you draw a straight line of twelve notes from A to A, then scale the frequency of the two A's to match and the range of values on the SAA to match, the curves mirror each-other almost exactly.

I have NO idea how to implement that as code though. I'm using a lookup chart for now range 0..127 (there's literally NO reason to even try more resolution than that given the inefficiencies in the input curve)... which isn't too bad given it's only 128 bytes. I can live with that. Just wish I didn't have to.

Had another odd issue where I'm unable to get the odd-numbered channels working right (high byte on octave) -- well, they work fine on their own, but if you play the even numbered sound at the same time the odd numbered ones squeak like the octave is messed up. I'll post my code for that up later but since what I'm working on (pac man game) only needs three voices I'm just using the even numbered channels for now which works great.

Now back to implementing my adlib support. :D
 
Went back to it when I realized that while the note progressions were right, it was about 400 cents out of tune -- the numbers in that CMS manual are WAY off compared to the data sheet for the chip. Not only are the frequencies reported for each octave WAY off, they don't match the datasheet for the chip -- and it turns out the clock is ON the chip. At first I thought it was just DosBox's implementation, but checking it against a real SB 1.0 confirms it.

I always thought CMS music sounded out of tune compared to other devices in things like Sierra games -- Looks like I was right and the games that don't "sound right" are wrong because they followed that booklet.

I also figured out why using the odd numbered voices was screwy -- despite them storing octaves as two voices per register (0x10..0x12), those registers are WRITE ONLY... So you can't read to mask and preserve (that's really stupid IMHO)... Strange since the enable/disable bits in register 0x14 are read/write.

For those curious on what I've got coded:
Code:
const
	CMSFreqMap:packed array[0..127] of byte=(
		000,003,007,011,015,019,023,027,
		031,034,038,041,045,048,051,055,
		058,061,064,066,069,072,075,077,
		080,083,086,088,091,094,096,099,
		102,104,107,109,112,114,116,119,
		121,123,125,128,130,132,134,136,
		138,141,143,145,147,149,151,153,
		155,157,159,161,162,164,166,168,
		170,172,174,175,177,179,181,182,
		184,186,188,189,191,193,194,196,
		197,199,200,202,203,205,206,208,
		209,210,212,213,214,216,217,218,
		219,221,222,223,225,226,227,228,
		229,231,232,233,234,235,236,237,
		239,240,241,242,243,244,245,246,
		247,249,250,251,252,253,254,255
	);

type
	tCmsOctaveStore:packed array[0..11] of byte;
	
var 
	soundPort:word;
	cmsOctaveStore:^tCmsOctaveStore;
	
procedure cmsReset; assembler;
asm
{ reset all 32 registers }
	mov  dx,soundPort
	mov  cx,1
@loopReset:
	mov  bx,cx
	mov  cx,$20
	xor  ax,ax
@loopRegisters:
	inc  dx
	out  dx,al
	inc  al
	xchg al,ah
	dec  dx
	out  dx,al
	xchg al,ah
	loop @loopRegisters

{ reset freq registers and enable sound }
	inc  dx
	mov  al,$1C
	out  dx,al
	dec  dx
	mov  al,3
	out  dx,al

	mov  cx,bx
	loop @loopReset

	les  di,cmsOctaveStore;
	mov  cx,3
	xor  ax,ax
	rep  stosw
end; { cmsReset }

procedure cmsSound(voice,freq,octave,amplitudeLeft,amplitudeRight:byte); assembler;
asm
	xor  dx,dx
	xor  ah,ah
	mov  al,voice

{
	octave registers are WRITE ONLY (stupid) so to preserve
	other voices octave settings we have to track this ourselves
	so let's get ES:DI pointed at the correct offset in our table
	ahead of time.
}
	mov  bx,ax
	shr  bx,1
	les  di,cmsOctaveStore
	add  di,bx

{
	BL = voice mod 6 = chip voice
	DX = sound card port + (voice div 6)*2
	typically $2x0 for chip 1, $2x2 for chip 2
}
	mov  bx,6
	div  bx
	mov  bx,dx  { bl now equals chip voice }
	mov  dx,soundPort
	shl  ax,1   { deterimine which chip }
	add  dx,ax

{	set amplitude }
	inc  dx     { set address register }
	mov  al,bl  { amplitudes $00..$05 }
	out  dx,al

	dec  dx     { set data register }
	mov  al,amplitudeLeft
	mov  ah,amplitudeRight
	and  al,$0F
	mov  cl,4
	shl  ah,cl
	or   al,ah
	out  dx,al

{ set frequency }
	inc  dx     { set address register }
	mov  al,bl
	or   al,$08 { frequencies $08..$0D }
	out  dx,al

	dec  dx { set data register }
	mov  al,freq
	out  dx,al

{ set octave }
	inc  dx         { set address register }
	mov  al,bl
	shr  al,1
	or   al,$10     { 2 octaves per register $10..$12 }
	out  dx,al

	dec  dx         { set data register }
	mov  al,es:[di] { read from our buffer }
	mov  ah,octave
	and  ah,$07     { just in case, mask it off }
	mov  bh,bl
	and  bh,$01
	jnz  @voiceOdd
	and  al,$F8     { voice even, mask out bottom 3 bits }
	jmp  @outOctave
@voiceOdd:
	and  al,$8F     { voice odd, mask out bits 4..6 }
	mov  cl,4
	shl  ah,cl      { and slide our octave value over }
@outOctave:
	or   al,ah      { put the two together }
	out  dx,al      { and store on card}
	mov  es:[di],al { and in buffer }

{ freq enable }
	inc  dx         { set address register }
	mov  al,$14     { channel on/off $14 bits 0..5 }
	out  dx,al

	dec  dx         { set data register }
	in   al,dx
	mov  ah,$01
	mov  cl,bl
	shl  ah,cl
	or   al,ah
	out  dx,al
end; { cmsSound }

procedure cmsOutFreq(channel,freq,level:word;);
var
	outOctave,
	outFreq:word;
begin
	if (freq<32) or (freq>7823) or (level=0) then begin
		cmsSound(channel,0,0,0,0);
	end else begin
		outOctave:=4;
		outFreq:=freq;
		while (outFreq<489) do begin
			outFreq:=outFreq*2;
			dec(outOctave);
		end;
		while (outFreq>977) do begin
			outFreq:=outFreq div 2;
			inc(outOctave);
		end;
		cmsSound(
			channel,
			CMSFreqMap[((outFreq-489)*128) div 489],
			outOctave,
			level,level
		);
	end;
end;

A little rough around the edges, but it works. ( assumes you do new(cmsOctaveStore) and soundPort is set to $220 or appropriate value )
 
Last edited:
I have a booklet that came with my Soundblaster 1.5 with CMS chips entitled "C/MS Programming Information" with everything you need including sample code. See attached.

Hi everyone;

Back in 1991 I have bought a rare Sound Blaster 1.6 that was basically a SB 1.5 with the CMS chips included in it.

It came with that booklet with the programming information but it's a kind of incomplete because some time after i found out that we can use the 18 and 19 (24 and 25 in decimal) resgisters to produce envelopes.

Here is the complete registers sheet:
registers.jpg

And the registers 18 and 19 explanation:
envelope.jpg

Use register 18 to produce envelopes for channels 1-3 and register 19 to produce envelopes for channels 4-6.

I really don't know why this information is not present in the booklet that came with the card.

Cheers.
 
Back
Top