#include <stdio.h>
#include <dos.h>
#include <conio.h>

#define ADLIB_ADDR_PORT 0x388
#define ADLIB_DATA_PORT 0x389
#define BYTE char unsigned

typedef struct
{
BYTE	flags1;
BYTE	flags2;
BYTE	ksl_attn1;
BYTE	ksl_attn2;
BYTE	att_dec1;
BYTE	att_dec2;
BYTE	sus_rel1;
BYTE	sus_rel2;

BYTE	wave_sel1;
BYTE	wave_sel2;
BYTE	fdbk_alg;
} PatchData;



PatchData sample_patch = {
(1<<5)|(15),
(1<<5)|(7),
0,
0,
0xF0,
0xFF,
0xF8,
0xF8,
0,
3,
(3<<1)|(1)
};


PatchData drum_patch_6={0x00,0x00,0x0B,0x00,0xA8,0xD6,0x4C,0x4F,0,0,0};
PatchData drum_patch_7={0x01,0x0C,0x06,0x00,0xF9,0xF8,0x68,0xB5,0,0,1};
PatchData drum_patch_8={0x02,0x03,0x00,0x05,0xF7,0xF5,0xB5,0xA5,0,0,1};

static int op_offset[]={0,1,2,8,9,10,16,17,18};

static int fnumber[]={
0x158,
0x16B,
0x181,
0x198,
0x1B0,
0x1CA,
0x1E5,
0x202,
0x220,
0x241,
0x263,
0x287
};



static char shadow_0xBD;
static char reg_shadow[9];

//void write_to_reg(char reg,char value)
void write_to_reg(unsigned reg,int value)
{
int	i;

	for (i=0;i<35;i++)
		inp(ADLIB_ADDR_PORT);

	_outp(ADLIB_ADDR_PORT,reg);

	for (i=0;i<7;i++)
		inp(ADLIB_ADDR_PORT);

	_outp(ADLIB_DATA_PORT,value);

}


void write_patch(PatchData *pd,int voice_no)
{
int	addr;
int	volume;

	addr=op_offset[voice_no];

	write_to_reg(0x20+addr,pd->flags1);
	write_to_reg(0x20+3+addr,pd->flags2);

	write_to_reg(0x40+addr,pd->ksl_attn1);
	write_to_reg(0x40+3+addr,pd->ksl_attn2);

	write_to_reg(0x60+addr,pd->att_dec1);
	write_to_reg(0x60+3+addr,pd->att_dec2);

	write_to_reg(0x80+addr,pd->sus_rel1);
	write_to_reg(0x80+3+addr,pd->sus_rel2);

	write_to_reg(0xE0+addr,pd->wave_sel1);
	write_to_reg(0xE0+3+addr,pd->wave_sel2);

	write_to_reg(0xC0+voice_no,pd->fdbk_alg);
}

void fm_reset(void)
{
int	i;

	write_to_reg(0x01,0x20);
	write_to_reg(0x08,0x20);
	write_to_reg(0xBD,0xC0);

	for(i=0;i<9;i++)
		{
		write_to_reg(0xA0,0x00);
		write_to_reg(0xB0,0x00);
		}
}

void set_frequency(int voice_no,int octave,int note)
{
unsigned char key_oct_fn;

int	fnum=fnumber[note];

	write_to_reg(0xA0+voice_no,fnum&0xFF);
	key_oct_fn=((octave&0x03)<<2)|((fnum>>8)&0x03);
	reg_shadow[voice_no]=key_oct_fn;
	write_to_reg(0xB0+voice_no,key_oct_fn);
}

void play_voice(int voice_no,int octave,int note)
{
unsigned char key_oct_fn;

int	fnum=fnumber[note];

	write_to_reg(0xA0+voice_no,fnum&0xFF);
	key_oct_fn=0x20|((octave&0x03)<<2)|((fnum>>8)&0x03);
	reg_shadow[voice_no]=key_oct_fn;
	write_to_reg(0xB0+voice_no,key_oct_fn);
}


void stop_voice(int voice_no)
{
	write_to_reg(0xB0+voice_no,reg_shadow[voice_no]&0x1F);
}


void init_drums(void)
{
int	i;

	for (i=6;i<9;i++)
		stop_voice(i);

	write_to_reg(0xBD,0xE0);
	shadow_0xBD=0xE0;

	write_patch(&drum_patch_6,6);
	write_patch(&drum_patch_7,7);
	write_patch(&drum_patch_8,8);

	set_frequency(6,2,1);
	set_frequency(7,2,2);
	set_frequency(8,2,3);
}


void play_drum(int drumno)
{
char	outbyte;

	if (drumno<5)
		{
		outbyte=1<<drumno;
		outbyte|=shadow_0xBD;
		write_to_reg(0xBD,outbyte);
		}
}

void stop_drum(int drumno)
{
char	outbyte;

	if (drumno<5)
		{
		outbyte=1<<drumno;
		outbyte^=0xFF;
		outbyte&=shadow_0xBD;
		write_to_reg(0xBD,outbyte);
		}
}



void main(void)
{
char	key;
int	block,note;

	puts("Hit letters to play notes");
	puts("Hit numbers to play drums");
	puts("\nPress ENTER to quit");

	fm_reset();
	init_drums();
	write_patch(&sample_patch,0);

	for(;;)
		{
		key=getch();
		if (key==13) break;

		if ((key>='1')&&(key<='5'))
			{
			key-='1';
			stop_drum(key);

			play_drum(key);
			}
		else
			{
			key-='a';
			block=2+key/12;
			note=key%12;
			play_voice(0,block,note);
			stop_voice(0);
			}
		}


	fm_reset();
}



