blob: 8d50bee956224f805737dc3d6d56125edf8dd69f [file] [log] [blame] [raw]
#ifdef CMS
#ifdef CMS_DEBUG
#include <stdio.h>
#include <stdarg.h>
#endif
#include "cms.h"
#ifdef CMS_DEBUG
void debug_log(const char *fmt, ...)
{
FILE *f_log;
va_list ap;
f_log = fopen("cmslog.log", "a");
va_start(ap, fmt);
vfprintf(f_log, fmt, ap);
va_end(ap);
fclose(f_log);
}
#endif
mid_channel cms_synth[MAX_CMS_CHANNELS]; // CMS synth
// CMS IO port address
unsigned short cmsPort;
// The 12 note-within-an-octave values for the SAA1099, starting at B
static unsigned char noteAdr[] = {5, 32, 60, 85, 110, 132, 153, 173, 192, 210, 227, 243};
// Volume
static unsigned char atten[128] = {
0,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,
3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,
5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,
7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,
9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,
11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,
13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,
15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
};
// Logic channel - first chip/second chip
static unsigned char ChanReg[12] = {000,001,002,003,004,005,000,001,002,003,004,005};
// Set octave command
static unsigned char OctavReg[12] = {0x10,0x10,0x11,0x11,0x12,0x12,0x10,0x10,0x11,0x11,0x12,0x12};
unsigned char CmsOctaveStore[12];
unsigned char ChanEnableReg[2] = {0,0};
void __declspec( naked ) cmsWrite(void)
{
/*
parameters
dx = port base+offset
ah = register
al = data
*/
_asm
{
inc dx
xchg al,ah
out dx,al
dec dx
xchg al,ah
out dx,al
ret
}
}
void __declspec( naked ) cmsNull(unsigned short port)
{
/*
parameters
dx = port offset to null
*/
_asm
{
add dx,cmsPort // FIXME! CMSPortAddr
mov cx,20h
xor ax,ax
loop_nul: // null all 20 registers
call cmsWrite
inc ah
loop loop_nul
mov ax,1C02h // reset chip
call cmsWrite
mov ax,1C01h // enable this chip
call cmsWrite
ret
}
}
void cmsReset(unsigned short port)
{
unsigned char i;
cmsPort = port;
_asm
{
mov dx,0
call cmsNull
mov dx,2
call cmsNull
}
for (i=0;i<11;i++)CmsOctaveStore[i]=0;
ChanEnableReg[0]=0;
ChanEnableReg[1]=0;
for (i=0;i<MAX_CMS_CHANNELS;i++)
{
cms_synth[i].note=0;
cms_synth[i].volume=0;
}
}
void cmsDisableVoice(unsigned char voice)
{
_asm
{
xor bh,bh
mov bl,voice
mov dx,cmsPort // FIXME !!!
mov bl,ChanReg[bx] ; bl = true channel (0 - 5)
xor di,di
mov cl,voice
cmp cl,06h
jl skip_inc
inc di
add dx,2
skip_inc:
mov al,14h
inc dx
out dx,al
dec dx
mov al,ChanEnableReg[di]
mov ah,01h
mov cl,bl
shl ah,cl
not ah
and al,ah ; al = voice enable reg
out dx,al
mov ChanEnableReg[di],al
}
}
void cmsSetVolume(unsigned char voice,unsigned char amplitudeLeft,unsigned char amplitudeRight)
{
_asm
{
xor bh,bh
mov bl,voice
mov dx,cmsPort // FIXME !!!
cmp bl,06h ; check channel num > 5?
jl setVol ; yes - set port = port + 2
add dx,2
setVol:
mov bl,ChanReg[bx] ; bx = true channel (0 - 5)
mov al,byte ptr amplitudeLeft
mov ah,byte ptr amplitudeRight
mov cl,4
shl ah,cl
or al,ah
mov ah,bl
call cmsWrite
}
}
void cmsSound(unsigned char voice,unsigned char freq,unsigned char octave,unsigned char amplitudeLeft,unsigned char amplitudeRight)
{
_asm
{
xor bh,bh
mov bl,voice
mov dx,cmsPort // FIXME !!!
cmp bl,06h ; check channel num > 5?
jl setOctave ; yes - set port = port + 2
add dx,2
setOctave:
mov bl,ChanReg[bx] ; bx = true channel (0 - 5)
mov ah,OctavReg[bx] ; ah = Set octave command
;
; ah now = register
; 0,1,6,7=$10
; 2,3,8,9=$11
; 4,5,10,11=$12
;
; CMS octave regs are write only, so we have to track
; the values in adjoining voices manually
mov al,ah
xor ah,ah ; ax = set octave cmd (10h - 12h)
mov di,ax ; di = ax
sub di,010h ; di = octave cmd - 10h (0..2 index)
mov cl,voice
cmp cl,06h
jl skip_inc
add di,3
skip_inc:
mov ah,al ; set ah back to octave cmd
mov al,byte ptr CmsOctaveStore[di]
mov bh,octave
test bl,01h
jnz shiftOctave
and al,0F0h
jmp outOctave
shiftOctave:
and al,0Fh
mov cl,4
shl bh,cl
outOctave:
or al,bh
mov byte ptr CmsOctaveStore[di],al
call cmsWrite ; set octave to CMS
setAmp:
mov al,byte ptr amplitudeLeft
mov ah,byte ptr amplitudeRight
;and al,0Fh
mov cl,4
shl ah,cl
or al,ah
mov ah,bl
call cmsWrite
setFreq:
mov al,byte ptr freq
or ah,08h
call cmsWrite
voiceEnable:
mov al,14h
inc dx
out dx,al
dec dx
xor di,di
mov cl,voice
cmp cl,06h
jl skip_inc2
inc di
skip_inc2:
mov al,ChanEnableReg[di]
mov ah,01h
mov cl,bl
shl ah,cl
or al,ah
out dx,al
mov ChanEnableReg[di],al
}
}
// ****
// High-level CMS synth procedures
// ****
void cmsNoteOff(unsigned char channel, unsigned char note)
{
unsigned char i;
unsigned char voice;
voice = MAX_CMS_CHANNELS;
for(i=0; i<MAX_CMS_CHANNELS; i++)
{
if(cms_synth[i].note==note)
{
voice = i;
break;
}
}
// Note not found, ignore note off command
if(voice==MAX_CMS_CHANNELS)
{
#ifdef CMS_DEBUG
debug_log("not found Ch=%u,note=%u\n",channel & 0xFF,note & 0xFF);
#endif
return;
}
cmsDisableVoice(voice);
cms_synth[voice].note = 0;
}
void cmsNoteOn(unsigned char channel, unsigned char note, unsigned char velocity)
{
unsigned char octave;
unsigned char noteVal;
unsigned char i;
unsigned char voice;
unsigned char note_cms;
if (velocity != 0)
{
note_cms = note+1;
octave = (note_cms / 12) - 1; //Some fancy math to get the correct octave
noteVal = note_cms - ((octave + 1) * 12); //More fancy math to get the correct note
voice = MAX_CMS_CHANNELS;
for(i=0; i<MAX_CMS_CHANNELS; i++)
{
if(cms_synth[i].note==0)
{
voice = i;
break;
}
}
// We run out of voices, ignore note on command
if(voice==MAX_CMS_CHANNELS)
{
#ifdef CMS_DEBUG
debug_log("no space for note Ch=%u,note=%u,vel=%u\n",channel & 0xFF,note & 0xFF,velocity & 0xFF);
#endif
return;
}
cmsSound(voice,noteAdr[noteVal],octave,atten[velocity],atten[velocity]);
cms_synth[voice].note = note;
cms_synth[voice].volume = velocity;
}
else
cmsNoteOff(channel,note);
}
void cmsTick(void)
{
#if 0
unsigned char i;
unsigned char noteVal;
for (i=0;i<MAX_CMS_CHANNELS;i++)
if (cms_synth[i].note != 0)
{
noteVal = cms_synth[i].volume-1;
if (noteVal != 0)
{
cmsSetVolume(i,atten[noteVal],atten[noteVal]);
cms_synth[i].volume = noteVal;
}
else
{
cmsDisableVoice(i);
cms_synth[i].volume = 0;
cms_synth[i].note = 0;
}
}
#endif
}
void cmsController(unsigned char channel, unsigned char id, unsigned char val)
{
unsigned char i;
if (id == 0x07) // set main volume
{
for (i=0;i<MAX_CMS_CHANNELS;i++)
if (cms_synth[i].note != 0)
{
cmsSetVolume(i,atten[val],atten[val]);
cms_synth[i].volume = val;
}
}
}
#endif