www.pudn.com > virtual_gameboy-0.8.8.zip > IBMADLIB.C
/* IbmAdlib.c This file is part of the VGB-DOS project Copyright (C) Marcel de Kogel (m.dekogel@student.utwente.nl), 1996 You may not use this file for commercial purposes Please notify me if you make any changes to this file */ #include "GB.h" #include "IbmMsdos.h" #define NUM_CHANNELS 18 #include#include #include int stereomode=1; static int got_sb=0; static byte gotadlib=0; static byte Sound_Reg[0x27]; static byte kbf[NUM_CHANNELS]; /* KEY_BLOCK_F-NUMBER */ static int ChannelFreq[NUM_CHANNELS]; static int ChannelEnv[NUM_CHANNELS]; static int freqsweep[NUM_CHANNELS]; static int envsweep[NUM_CHANNELS]; static int envsweepparam[NUM_CHANNELS]; static byte channel_on[NUM_CHANNELS]; static byte tone_off[NUM_CHANNELS]; /* bit 1: sound on/off (reg 0x16) bit 2: pan (reg 0x15) bit 3: mode 3 on/off (reg 0x1A) bit 4: speaker on/off (reg 0x14) */ static byte key_on[NUM_CHANNELS]; static word FMPoort=0x388; static byte sound_on=1; static byte sound_is_on=1; static byte vol_is_nul[NUM_CHANNELS]; static byte *channel_voice[NUM_CHANNELS]; static byte OPL3=1; static byte WriteSoundBusy=0; static word OpAd[]= { 0x000,0x001,0x002,0x008,0x009,0x00A,0x010,0x011,0x012, 0x100,0x101,0x102,0x108,0x109,0x10A,0x110,0x111,0x112 }; static word ChanAd[]= { 0x000,0x001,0x002,0x003,0x004,0x005,0x006,0x007,0x008, 0x100,0x101,0x102,0x103,0x104,0x105,0x106,0x107,0x108 }; static byte Sound_Melodic[11]= { 0x23,0x21, /* AM_VIB_EG-TYP_KSR_MULTI */ 0x1E,0x00, /* KSL_TL */ 0xFF,0xFF, /* AR_DR */ 0x08,0x08, /* SL_RR */ 0x00,0x00, /* WS */ 0x0E /* FB_FM */ }; static byte Sound_Wave[11]= { 0x23,0x21, /* AM_VIB_EG-TYP_KSR_MULTI */ 0x1E,0x00, /* KSL_TL */ 0xFF,0xFF, /* AR_DR */ 0x08,0x08, /* SL_RR */ 0x00,0x00, /* WS */ 0x0A /* FB_FM */ }; static byte Sound_Noise[11]= { 0x2E,0x2E, /* AM_VIB_EG-TYP_KSR_MULTI */ 0x40,0x03, /* KSL_TL */ 0xFF,0xFF, /* AR_DR */ 0x0F,0x0F, /* SL_RR */ 0x00,0x03, /* WS */ 0x0E /* FB_FM */ }; static byte Sound_Melodic_OPL3[11]= { 0x21,0x21, /* AM_VIB_EG-TYP_KSR_MULTI */ 0x00,0x16, /* KSL_TL */ 0xFF,0xFF, /* AR_DR */ 0x0A,0x0A, /* SL_RR */ 0x06,0x00, /* WS */ 0x01 /* FB_FM */ }; static byte Sound_Wave_OPL3[11]= { 0x21,0x21, /* AM_VIB_EG-TYP_KSR_MULTI */ 0x06,0x10, /* KSL_TL */ 0xFF,0xFF, /* AR_DR */ 0x0A,0x0A, /* SL_RR */ 0x06,0x00, /* WS */ 0x01 /* FB_FM */ }; static word FMFreqs[] = { #include "FMFreqs.h" }; static void WriteFM_OPL2 (byte value,byte index) { int i; outportb (FMPoort,index); for (i=0;i<6;++i) inportb (FMPoort); outportb (FMPoort+1,value); for (i=0;i<35;++i) inportb (FMPoort); } static void WriteFM_OPL3_1 (byte value,byte index) { outportb (FMPoort,index); inportb (FMPoort); outportb (FMPoort+1,value); inportb (FMPoort); inportb (FMPoort); } static void WriteFM_OPL3_2 (byte value,byte index) { outportb (FMPoort+2,index); inportb (FMPoort); outportb (FMPoort+3,value); inportb (FMPoort); inportb (FMPoort); } static void WriteFM (byte value,word index) { if (OPL3) { if (index&0x100) WriteFM_OPL3_2 (value,(byte)index); else WriteFM_OPL3_1 (value,(byte)index); } else { if ((index&0x100)==0) WriteFM_OPL2 (value,(byte)index); } } static byte ReadFM (void) { return inportb (FMPoort); } static void Adlib_ChannelOff (byte channel) { WriteFM (kbf[channel],0xB0+ChanAd[channel]); key_on[channel]=0; } static void Adlib_ChannelOn (byte channel) { if (!sound_on) return; if (vol_is_nul[channel]) return; if (!channel_on[channel]) return; WriteFM (kbf[channel]|32,0xB0+ChanAd[channel]); key_on[channel]=1; } void Adlib_SoundOff (void) { int i; if (!gotadlib) return; sound_is_on=0; for (i=0;i<4;++i) Adlib_ChannelOff (i); } void Adlib_SoundOn (void) { int i; if (!gotadlib) return; if (!sound_on) return; sound_is_on=1; for (i=0;i<4;++i) if (!tone_off[i]) Adlib_ChannelOn (i); } void Adlib_ToggleSound (void) { if (!gotadlib) return; sound_on^=1; if (sound_on) Adlib_SoundOn (); else Adlib_SoundOff (); } void Adlib_ToggleChannel (int channel) { if (!gotadlib) return; if (!sound_on) return; if (!sound_is_on) return; channel_on[channel]^=1; if (channel_on[channel]) Adlib_ChannelOn (channel); else Adlib_ChannelOff (channel); } static void WriteVolume (byte vol,byte reg,byte index) { unsigned tl; /* tl=63-(reg&63); tl*=(vol&63); tl/=64; if (tl>=64) tl=63; tl=63-tl; */ if (vol==0) tl=63; else { if (vol>63) vol=63; tl=(reg&63)+(63-vol); if (tl>63) tl=63; } WriteFM ((reg&0xC0)|tl,index); } static void Adlib_SetVolume (byte channel,byte vol) { ChannelEnv[channel]=vol; if (vol==0) { vol_is_nul[channel]=1; if (!tone_off[channel]) Adlib_ChannelOff (channel); return; } if (vol_is_nul[channel]) { vol_is_nul[channel]=0; if (!tone_off[channel]) Adlib_ChannelOn (channel); } if (channel_voice[channel][10]&1) /* additive synthesis */ WriteVolume (vol,channel_voice[channel][2],0x40+OpAd[channel]); WriteVolume (vol,channel_voice[channel][3],0x43+OpAd[channel]); } static void Adlib_SetFreq (byte channel, word freq) { int block,f; ChannelFreq[channel]=freq; f=FMFreqs[freq*2+1]; block=FMFreqs[freq*2]; WriteFM (f&0xFF,0xA0+ChanAd[channel]); kbf[channel]=(f>>8)+(block<<2); if (!tone_off[channel]) { if (f) Adlib_ChannelOn (channel); else Adlib_ChannelOff (channel); } } static void Adlib_SetVoice (char *voice,int channel) { WriteFM (voice[0],0x20+OpAd[channel]); WriteFM (voice[1],0x23+OpAd[channel]); WriteFM (voice[2],0x40+OpAd[channel]); WriteFM (voice[3],0x43+OpAd[channel]); WriteFM (voice[4],0x60+OpAd[channel]); WriteFM (voice[5],0x63+OpAd[channel]); WriteFM (voice[6],0x80+OpAd[channel]); WriteFM (voice[7],0x83+OpAd[channel]); WriteFM (voice[8],0xE0+OpAd[channel]); WriteFM (voice[9],0xE3+OpAd[channel]); if (OPL3) WriteFM (voice[10]|0x30,0xC0+ChanAd[channel]); else WriteFM (voice[10],0xC0+ChanAd[channel]); channel_voice[channel]=voice; } static void Adlib_SetVoices (void) { if (OPL3) { memcpy (Sound_Melodic,Sound_Melodic_OPL3,sizeof(Sound_Melodic)); memcpy (Sound_Wave,Sound_Wave_OPL3,sizeof(Sound_Wave)); } Adlib_SetVoice (Sound_Melodic,0); Adlib_SetVolume (0,15); Adlib_SetVoice (Sound_Melodic,1); Adlib_SetVolume (1,15); Adlib_SetVoice (Sound_Wave,2); Adlib_SetVolume (2,15); Adlib_SetVoice (Sound_Noise,3); Adlib_SetVolume (3,15); } static int Reset_Adlib (void) { byte tmp1,tmp2; int i; WriteFM (0,1); WriteFM (0x60,4); WriteFM (0x80,4); tmp1=ReadFM (); WriteFM (0xFF,2); WriteFM (0x21,4); for (i=0;i<300;++i) ReadFM (); tmp2=ReadFM (); WriteFM (0x60,4); WriteFM (0x80,4); if ((tmp1&0xE0)!=0) return 0; if ((tmp2&0xE0)!=0xC0) return 0; WriteFM (0,1); return 1; } int Adlib_Init (void) { if (SB_Info.baseport) { if (SB_Info.type>=4) FMPoort=SB_Info.baseport; else { FMPoort=SB_Info.baseport+8; OPL3=0; } } if (!Reset_Adlib()) return 0; if (OPL3) { if ((ReadFM()&6)==0) { WriteFM (1,0x105); WriteFM (0,0x104); } else OPL3=0; } memset (vol_is_nul,0,sizeof(vol_is_nul)); memset (tone_off,0,sizeof(tone_off)); memset (ChannelFreq,0,sizeof(ChannelFreq)); memset (ChannelEnv,0,sizeof(ChannelEnv)); memset (freqsweep,0,sizeof(freqsweep)); memset (envsweep,0,sizeof(envsweep)); memset (envsweepparam,0,sizeof(envsweepparam)); memset (channel_on,1,sizeof(channel_on)); WriteFM (0x00,0xBD); WriteFM (0,8); Adlib_SetVoices (); gotadlib=1; got_sb=sb_init (); return FMPoort+(OPL3<<16)+(got_sb<<17); } void Adlib_Reset (void) { int i; if (!gotadlib) return; for (i=0;i<4;++i) { Adlib_ChannelOff (i); Adlib_SetVolume (i,0); } if (OPL3) WriteFM (0,0x105); OPL3=0; Reset_Adlib (); WriteFM (0x00,0xBD); WriteFM (0,8); if (got_sb) sb_exit (); } /* High nibble: AND mask */ /* Low nibble: OR mask */ static void Adlib_ToneOn (byte channel,byte state,int keyon) { int oldstate,newstate; oldstate=(tone_off[channel])? 1:0; tone_off[channel]|=(state&0x0F); tone_off[channel]&=(state>>4); newstate=(tone_off[channel])? 1:0; /* if (oldstate!=newstate) */ { if (keyon) { if (!tone_off[channel]) Adlib_ChannelOn (channel); else Adlib_ChannelOff (channel); } else Adlib_ChannelOff (channel); } } static void Adlib_SetPan (int channel,int left,int right) { byte regval; if (OPL3) { regval=channel_voice[channel][10]; if (left) regval|=(stereomode==1)? 0x10:0x20; if (right) regval|=(stereomode==1)? 0x20:0x10; if (!stereomode && (left || right)) regval|=0x30; WriteFM (regval,0xC0+ChanAd[channel]); } else Adlib_ToneOn (channel,(left | right)? 0xD0:0xF2,1); } static byte GetSoundVolume (byte v) { const byte voltable[16] = { 0,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60 }; return voltable[(v>>4)&15]; } static int GetEnvSweep (byte channel,byte v) { const int envsweeptabel[8]={ 0,1,3,5,7,9,11,13 }; /* printf ("EnvSweep %u,%02x\n",channel,v); */ return (envsweeptabel[v&7])*((v&8)? 1:-1); } static int GetFreqSweep (byte v) { const int freqsweeptabel[8]= { 0,7,4,3,2,1,1,1 }; return (v&7)*freqsweeptabel[(v&30)>>4]*((v&0x08)? -1:1); } void Adlib_WriteSoundReg (byte r,word v) { static byte Mode3_VolTable[4]= { 0,54,52,50 }; static int fmvol[8]= { 0,9,10,11,12,13,14,15 }; if (!gotadlib) return; WriteSoundBusy=1; switch (r) { case 0x00: /* mode 1 frequency sweep */ freqsweep[0]=GetFreqSweep(v); break; case 0x01: /* mode 1 len / duty */ break; case 0x02: /* mode 1 envelope sweep */ Adlib_SetVolume (0,GetSoundVolume(v)); envsweep[0]=GetEnvSweep(0,v); break; case 0x03: /* mode 1 freq lo */ Adlib_SetFreq (0,v | ((Sound_Reg[4]&7)<<8)); break; case 0x04: /* mode 1 freq hi */ if (v&0x80) { /* Adlib_ChannelOff (0); */ if (envsweep[0]) Adlib_SetVolume (0,GetSoundVolume(Sound_Reg[2])); } Adlib_SetFreq (0,((v&7)<<8) | Sound_Reg[3]); break; case 0x05: /* mode 2 frequency sweep (?) */ freqsweep[1]=GetFreqSweep(v); break; case 0x06: /* mode 2 len / duty */ break; case 0x07: /* mode 2 envelope sweep */ Adlib_SetVolume (1,GetSoundVolume(v)); envsweep[1]=GetEnvSweep(1,v); break; case 0x08: /* mode 2 freq lo */ Adlib_SetFreq (1,v | ((Sound_Reg[9]&7)<<8)); break; case 0x09: /* mode 2 freq hi */ if (v&0x80) { /* Adlib_ChannelOff (1); */ if (envsweep[1]) Adlib_SetVolume (1,GetSoundVolume(Sound_Reg[7])); } Adlib_SetFreq (1,((v&7)<<8) | Sound_Reg[8]); break; case 0x0A: /* mode 3 on/off */ Adlib_ToneOn (2,(v&0x80)? 0xB0:0xF4,1); break; case 0x0B: /* mode 3 len */ break; case 0x0C: /* mode 3 output level */ Adlib_SetVolume (2,Mode3_VolTable[(v&0x60)>>5]); break; case 0x0D: /* mode 3 freq lo */ Adlib_SetFreq (2,v | ((Sound_Reg[0x0E]&7)<<8)); break; case 0x0E: /* mode 3 freq hi */ if (v&0x80) { Adlib_ChannelOff (2); } Adlib_SetFreq (2,((v&7)<<8) | Sound_Reg[0x0D]); break; case 0x10: /* mode 4 len */ break; case 0x11: /* mode 4 envelope */ Adlib_SetVolume (3,GetSoundVolume(v)); envsweep[3]=GetEnvSweep(3,v); break; case 0x12: /* mode 4 polynomial counter */ Adlib_SetFreq (3,1100); break; case 0x13: /* mode 4 counter initial */ break; case 0x14: /* channel control */ if (got_sb) sb_setfmvolume (fmvol[(v&0x70)>>4],fmvol[v&0x07]); break; case 0x15: /* selection of sound output terminal */ Adlib_SetPan (0,v&0x10,v&0x01); Adlib_SetPan (1,v&0x20,v&0x02); Adlib_SetPan (2,v&0x40,v&0x04); Adlib_SetPan (3,v&0x80,v&0x08); break; case 0x16: /* sound on/off */ if ((v&0x80)==0) { Adlib_ToneOn (0,0xF1,1); Adlib_ToneOn (1,0xF1,1); Adlib_ToneOn (2,0xF1,1); Adlib_ToneOn (3,0xF1,1); } else { Adlib_ToneOn (0,0xE0,key_on[0]); Adlib_ToneOn (1,0xE0,key_on[1]); Adlib_ToneOn (2,0xE0,key_on[2]); Adlib_ToneOn (3,0xE0,key_on[3]); } break; } Sound_Reg[r]=v; WriteSoundBusy=0; } #define envperiod 1 #define freqperiod 2 void Adlib_InterruptRoutine (void) { int i,tmp; static int envcount=1; static int freqcount=1; if (WriteSoundBusy) return; WriteSoundBusy=1; __enable (); if (!--envcount) { envcount=envperiod; for (i=0;i 63) tmp=63; if (tmp!=ChannelEnv[i]) Adlib_SetVolume (i,tmp); } } } } if (!--freqcount) { freqcount=freqperiod; for (i=0;i 2047) tmp=2047; if (tmp!=ChannelFreq[i]) Adlib_SetFreq (i,tmp); } } } WriteSoundBusy=0; } void Adlib_DecreaseVolume (void) { if (got_sb) sb_decreasemastervolume (); } void Adlib_IncreaseVolume (void) { if (got_sb) sb_increasemastervolume (); }