1 /* Project 16 Source Code~
\r
2 * Copyright (C) 2012-2017 sparky4 & pngwen & andrius4669 & joncampbell123 & yakui-lover
\r
4 * This file is part of Project 16.
\r
6 * Project 16 is free software; you can redistribute it and/or modify
\r
7 * it under the terms of the GNU General Public License as published by
\r
8 * the Free Software Foundation; either version 3 of the License, or
\r
9 * (at your option) any later version.
\r
11 * Project 16 is distributed in the hope that it will be useful,
\r
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
14 * GNU General Public License for more details.
\r
16 * You should have received a copy of the GNU General Public License
\r
17 * along with this program. If not, see <http://www.gnu.org/licenses/>, or
\r
18 * write to the Free Software Foundation, Inc., 51 Franklin Street,
\r
19 * Fifth Floor, Boston, MA 02110-1301 USA.
\r
23 #include "src/lib/16_sd.h"
\r
25 //static void (interrupt *old_irq0)();
\r
26 extern struct glob_game_vars *ggvv;
\r
28 void opl2out(word reg, word data)
\r
33 mov dx,word ptr [ADLIB_FM_ADDRESS]
\r
51 void opl3out(word reg, word data)
\r
56 mov dx,word ptr [ADLIB_FM_ADDRESS]
\r
71 void opl3exp(word data)
\r
76 mov dx,word ptr [ADLIB_FM_ADDRESS]
\r
91 /* Function: FMResest *******************************************************
\r
93 * Description: quick and dirty sound card reset (zeros all
\r
97 void FMReset(void/*int percusiveMode*/)
\r
101 /* zero all registers */
\r
102 for(i = MIN_REGISTER; i < MAX_REGISTER+1; i++) opl2out(i, 0);
\r
104 /* allow FM chips to control the waveform of each operator */
\r
105 opl2out(0x01, 0x20);
\r
107 /* set rhythm enabled (6 melodic voices, 5 percussive) */
\r
108 opl2out(0xBD, 0x20);
\r
110 //FMSetPercusiveMode(percusiveMode);
\r
111 } /* End of FMReset */
\r
113 /* Function: FMKeyOff *******************************************************
\r
115 * Parameters: voice - which voice to turn off.
\r
117 * Description: turns off the specified voice.
\r
120 void FMKeyOff(int voice)
\r
124 /* turn voice off */
\r
125 regNum = 0xB0 + voice % 11;//NUMVOICE;
\r
126 opl2out(regNum, 0x0E);
\r
127 } /* End of FMKeyOff */
\r
129 /* Function: FMKeyOn *******************************************************
\r
131 * Parameters: voice - which voice to turn on.
\r
132 * freq - its frequency (note).
\r
133 * octave - its octave.
\r
135 * Description: turns on a voice of specfied frequency and
\r
139 void FMKeyOn(int voice, int freq, int octave)
\r
143 regNum = 0xA0 + voice % 11;//NUMVOICE;
\r
144 opl2out(regNum, freq & 0xff);
\r
145 regNum = 0xB0 + voice % 11;//NUMVOICE;
\r
146 tmp = (freq >> 8) | (octave << 2) | 0x20;
\r
147 opl2out(regNum, tmp);
\r
148 } /* End of FMKeyOn */
\r
150 /* Function: FMSetVoice *****************************************************
\r
152 * Parameters: voiceNum - which voice to set.
\r
153 * ins - instrument to set voice.
\r
155 * Description: sets the instrument of a voice.
\r
158 void FMSetVoice(int voiceNum, FMInstrument *ins){
\r
159 int opCellNum, cellOffset;
\r
161 voiceNum %= 11;//NUMVOICE;
\r
162 cellOffset = voiceNum % 3 + ((voiceNum / 3) << 3);
\r
164 /* set sound characteristic */
\r
165 opCellNum = 0x20 + (char)cellOffset;
\r
166 opl2out(opCellNum, ins->SoundCharacteristic[0]);
\r
168 opl2out(opCellNum, ins->SoundCharacteristic[1]);
\r
170 /* set level/output */
\r
171 opCellNum = 0x40 + (char)cellOffset;
\r
172 opl2out(opCellNum, ins->Level[0]);
\r
174 opl2out(opCellNum, ins->Level[1]);
\r
176 /* set Attack/Decay */
\r
177 opCellNum = 0x60 + (char)cellOffset;
\r
178 opl2out(opCellNum, ins->AttackDecay[0]);
\r
180 opl2out(opCellNum, ins->AttackDecay[1]);
\r
182 /* set Sustain/Release */
\r
183 opCellNum = 0x80 + (char)cellOffset;
\r
184 opl2out(opCellNum, ins->SustainRelease[0]);
\r
186 opl2out(opCellNum, ins->SustainRelease[1]);
\r
188 /* set Wave Select */
\r
189 opCellNum = 0xE0 + (char)cellOffset;
\r
190 opl2out(opCellNum, ins->WaveSelect[0]);
\r
192 opl2out(opCellNum, ins->WaveSelect[1]);
\r
194 /* set Feedback/Selectivity */
\r
195 opCellNum = (byte)0xC0 + (byte)voiceNum;
\r
196 opl2out(opCellNum, ins->Feedback);
\r
197 } /* End of FMSetVoice */
\r
200 void SD_Initimf(global_game_variables_t *gvar)
\r
202 if (!init_adlib()) {
\r
203 printf("Cannot init library\n");
\r
206 if (!probe_8254()) { /* we need the timer to keep time with the music */
\r
207 printf("8254 timer not found\n");
\r
211 gvar->ca.sd.irq0_ticks=
\r
212 //gvar->ca.sd.irq0_cnt=
\r
213 //gvar->ca.sd.irq0_add=
\r
214 gvar->ca.sd.imf_delay_countdown=
\r
215 gvar->ca.sd.irq0_max=0;
\r
216 gvar->ca.sd.imf_music=
\r
217 gvar->ca.sd.imf_play_ptr=
\r
218 gvar->ca.sd.imf_music_end=NULL;
\r
219 gvar->ca.sd.irq0_cnt = 0;
\r
220 gvar->ca.sd.irq0_add = 182;
\r
221 gvar->ca.sd.irq0_max = 1000; /* about 18.2Hz */
\r
223 SD_adlib_shut_up();
\r
224 shutdown_adlib_opl3(); // NTS: Apparently the music won't play otherwise
\r
227 void SD_imf_free_music(global_game_variables_t *gvar)
\r
229 // if (gvar->ca.sd.imf_music) free(gvar->ca.sd.imf_music);
\r
230 MM_FreePtr(MEMPTR gvar->ca.audiosegs[0], gvar); //TODO make behave like id engine
\r
231 gvar->ca.sd.imf_music = gvar->ca.sd.imf_play_ptr = gvar->ca.sd.imf_music_end = NULL;
\r
232 gvar->ca.sd.imf_delay_countdown = 0;
\r
235 int SD_imf_load_music(const char *path, global_game_variables_t *gvar)
\r
238 unsigned char buf[8];
\r
241 SD_imf_free_music(gvar);
\r
243 fd = open(path,O_RDONLY|O_BINARY);
\r
244 if (fd < 0) return 0;
\r
246 len = lseek(fd,0,SEEK_END);
\r
247 lseek(fd,0,SEEK_SET);
\r
249 if (buf[0] != 0 || buf[1] != 0) // type 1 IMF
\r
250 len = *((uint16_t*)buf);
\r
252 lseek(fd,0,SEEK_SET);
\r
254 if (len == 0 || len > 65535UL) {
\r
260 MM_GetPtr(MEMPTR gvar->ca.audiosegs[0],len, gvar);
\r
261 gvar->ca.sd.imf_music = (struct imf_entry *)gvar->ca.audiosegs[0];
\r
262 if (gvar->ca.sd.imf_music == NULL) {
\r
266 read(fd,gvar->ca.sd.imf_music,len);
\r
269 gvar->ca.sd.imf_play_ptr = gvar->ca.sd.imf_music;
\r
270 gvar->ca.sd.imf_music_end = gvar->ca.sd.imf_music + (len >> 2UL);
\r
274 // WARNING: subroutine call in interrupt handler. make sure you compile with -zu flag for large/compact memory models
\r
275 /*void interrupt irq0()
\r
277 ggvv->ca.sd.irq0_ticks++;
\r
278 if ((ggvv->ca.sd.irq0_cnt += ggvv->ca.sd.irq0_add) >= ggvv->ca.sd.irq0_max) {
\r
279 ggvv->ca.sd.irq0_cnt -= ggvv->ca.sd.irq0_max;
\r
283 p8259_OCW2(0,P8259_OCW2_NON_SPECIFIC_EOI);
\r
287 void SD_imf_tick(global_game_variables_t *gvar)
\r
289 if (gvar->ca.sd.imf_delay_countdown == 0) {
\r
291 adlib_write(gvar->ca.sd.imf_play_ptr->reg,gvar->ca.sd.imf_play_ptr->data);
\r
292 gvar->ca.sd.imf_delay_countdown = gvar->ca.sd.imf_play_ptr->delay;
\r
293 gvar->ca.sd.imf_play_ptr++;
\r
294 if (gvar->ca.sd.imf_play_ptr == gvar->ca.sd.imf_music_end)
\r
296 // printf("replay\n");
\r
297 gvar->ca.sd.imf_play_ptr = gvar->ca.sd.imf_music;
\r
299 } while (gvar->ca.sd.imf_delay_countdown == 0);
\r
302 gvar->ca.sd.imf_delay_countdown--;
\r
306 void SD_adlib_shut_up() {
\r
309 memset(adlib_fm,0,sizeof(adlib_fm));
\r
310 memset(&adlib_reg_bd,0,sizeof(adlib_reg_bd));
\r
311 for (i=0;i < adlib_fm_voices;i++) {
\r
312 struct adlib_fm_operator *f;
\r
313 f = &adlib_fm[i].mod;
\r
314 f->ch_a = f->ch_b = f->ch_c = f->ch_d = 1;
\r
315 f = &adlib_fm[i].car;
\r
316 f->ch_a = f->ch_b = f->ch_c = f->ch_d = 1;
\r
319 for (i=0;i < adlib_fm_voices;i++) {
\r
320 struct adlib_fm_operator *f;
\r
322 f = &adlib_fm[i].mod;
\r
323 f->mod_multiple = 1;
\r
324 f->total_level = 63 - 16;
\r
325 f->attack_rate = 15;
\r
327 f->sustain_level = 0;
\r
328 f->release_rate = 8;
\r
334 f = &adlib_fm[i].car;
\r
335 f->mod_multiple = 1;
\r
336 f->total_level = 63 - 16;
\r
337 f->attack_rate = 15;
\r
339 f->sustain_level = 0;
\r
340 f->release_rate = 8;
\r