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 //#define SD_USESCAMMPM
\r
27 static void (interrupt *SD_old_irq0)();
\r
28 //void interrupt (*old_irq0)(void);
\r
30 void opl2out(word reg, word data)
\r
35 mov dx,word ptr [ADLIB_FM_ADDRESS]
\r
53 void opl3out(word reg, word data)
\r
58 mov dx,word ptr [ADLIB_FM_ADDRESS]
\r
73 void opl3exp(word data)
\r
78 mov dx,word ptr [ADLIB_FM_ADDRESS]
\r
93 /* Function: FMResest *******************************************************
\r
95 * Description: quick and dirty sound card reset (zeros all
\r
99 void FMReset(void/*int percusiveMode*/)
\r
103 /* zero all registers */
\r
104 for(i = MIN_REGISTER; i < MAX_REGISTER+1; i++) opl2out(i, 0);
\r
106 /* allow FM chips to control the waveform of each operator */
\r
107 opl2out(0x01, 0x20);
\r
109 /* set rhythm enabled (6 melodic voices, 5 percussive) */
\r
110 opl2out(0xBD, 0x20);
\r
112 //FMSetPercusiveMode(percusiveMode);
\r
113 } /* End of FMReset */
\r
115 /* Function: FMKeyOff *******************************************************
\r
117 * Parameters: voice - which voice to turn off.
\r
119 * Description: turns off the specified voice.
\r
122 void FMKeyOff(int voice)
\r
126 /* turn voice off */
\r
127 regNum = 0xB0 + voice % 11;//NUMVOICE;
\r
128 opl2out(regNum, 0x0E);
\r
129 } /* End of FMKeyOff */
\r
131 /* Function: FMKeyOn *******************************************************
\r
133 * Parameters: voice - which voice to turn on.
\r
134 * freq - its frequency (note).
\r
135 * octave - its octave.
\r
137 * Description: turns on a voice of specfied frequency and
\r
141 void FMKeyOn(int voice, int freq, int octave)
\r
145 regNum = 0xA0 + voice % 11;//NUMVOICE;
\r
146 opl2out(regNum, freq & 0xff);
\r
147 regNum = 0xB0 + voice % 11;//NUMVOICE;
\r
148 tmp = (freq >> 8) | (octave << 2) | 0x20;
\r
149 opl2out(regNum, tmp);
\r
150 } /* End of FMKeyOn */
\r
152 /* Function: FMSetVoice *****************************************************
\r
154 * Parameters: voiceNum - which voice to set.
\r
155 * ins - instrument to set voice.
\r
157 * Description: sets the instrument of a voice.
\r
160 void FMSetVoice(int voiceNum, FMInstrument *ins){
\r
161 int opCellNum, cellOffset;
\r
163 voiceNum %= 11;//NUMVOICE;
\r
164 cellOffset = voiceNum % 3 + ((voiceNum / 3) << 3);
\r
166 /* set sound characteristic */
\r
167 opCellNum = 0x20 + (char)cellOffset;
\r
168 opl2out(opCellNum, ins->SoundCharacteristic[0]);
\r
170 opl2out(opCellNum, ins->SoundCharacteristic[1]);
\r
172 /* set level/output */
\r
173 opCellNum = 0x40 + (char)cellOffset;
\r
174 opl2out(opCellNum, ins->Level[0]);
\r
176 opl2out(opCellNum, ins->Level[1]);
\r
178 /* set Attack/Decay */
\r
179 opCellNum = 0x60 + (char)cellOffset;
\r
180 opl2out(opCellNum, ins->AttackDecay[0]);
\r
182 opl2out(opCellNum, ins->AttackDecay[1]);
\r
184 /* set Sustain/Release */
\r
185 opCellNum = 0x80 + (char)cellOffset;
\r
186 opl2out(opCellNum, ins->SustainRelease[0]);
\r
188 opl2out(opCellNum, ins->SustainRelease[1]);
\r
190 /* set Wave Select */
\r
191 opCellNum = 0xE0 + (char)cellOffset;
\r
192 opl2out(opCellNum, ins->WaveSelect[0]);
\r
194 opl2out(opCellNum, ins->WaveSelect[1]);
\r
196 /* set Feedback/Selectivity */
\r
197 opCellNum = (byte)0xC0 + (byte)voiceNum;
\r
198 opl2out(opCellNum, ins->Feedback);
\r
199 } /* End of FMSetVoice */
\r
201 void SD_Initimf(global_game_variables_t *gvar)
\r
203 if (!init_adlib()) {
\r
204 printf("Cannot init library\n");
\r
207 if (!probe_8254()) { /* we need the timer to keep time with the music */
\r
208 printf("8254 timer not found\n");
\r
212 gvar->ca.sd.irq0_ticks=
\r
213 //gvar->ca.sd.irq0_cnt=
\r
214 //gvar->ca.sd.irq0_add=
\r
215 gvar->ca.sd.imf_delay_countdown=
\r
216 gvar->ca.sd.irq0_max=0;
\r
217 gvar->ca.sd.imf_music=
\r
218 gvar->ca.sd.imf_play_ptr=
\r
219 gvar->ca.sd.imf_music_end=NULL;
\r
220 gvar->ca.sd.irq0_cnt = 0;
\r
221 gvar->ca.sd.irq0_add = 182;
\r
222 gvar->ca.sd.irq0_max = 1000; /* about 18.2Hz */
\r
224 SD_adlib_shut_up();
\r
225 shutdown_adlib_opl3(); // NTS: Apparently the music won't play otherwise
\r
228 void SD_imf_free_music(global_game_variables_t *gvar)
\r
230 #ifndef SD_USESCAMMPM
\r
231 if (gvar->ca.sd.imf_music) free(gvar->ca.sd.imf_music);
\r
233 MM_FreePtr(MEMPTRCONV gvar->ca.audiosegs[0], gvar); //TODO make behave like id engine
\r
235 gvar->ca.sd.imf_music = gvar->ca.sd.imf_play_ptr = gvar->ca.sd.imf_music_end = NULL;
\r
236 gvar->ca.sd.imf_delay_countdown = 0;
\r
239 int SD_imf_load_music(const char *path, global_game_variables_t *gvar)
\r
242 unsigned char buf[8];
\r
245 SD_imf_free_music(gvar);
\r
247 fd = open(path,O_RDONLY|O_BINARY);
\r
248 if (fd < 0) return 0;
\r
250 len = lseek(fd,0,SEEK_END);
\r
251 lseek(fd,0,SEEK_SET);
\r
253 if (buf[0] != 0 || buf[1] != 0) // type 1 IMF
\r
254 len = *((uint16_t*)buf);
\r
256 lseek(fd,0,SEEK_SET);
\r
258 if (len == 0 || len > 65535UL) {
\r
264 #ifndef SD_USESCAMMPM
\r
265 gvar->ca.sd.imf_music = malloc(len);
\r
267 MM_GetPtr(MEMPTRCONV gvar->ca.audiosegs[0],len, gvar);
\r
268 gvar->ca.sd.imf_music = (struct imf_entry *)gvar->ca.audiosegs[0];
\r
270 if (gvar->ca.sd.imf_music == NULL) {
\r
274 read(fd,gvar->ca.sd.imf_music,len);
\r
277 gvar->ca.sd.imf_play_ptr = gvar->ca.sd.imf_music;
\r
278 gvar->ca.sd.imf_music_end = gvar->ca.sd.imf_music + (len >> 2UL);
\r
282 struct glob_game_vars *ggvv;
\r
283 // WARNING: subroutine call in interrupt handler. make sure you compile with -zu flag for large/compact memory models
\r
284 void interrupt SD_irq0()
\r
286 ggvv->ca.sd.irq0_ticks++;
\r
287 if ((ggvv->ca.sd.irq0_cnt += ggvv->ca.sd.irq0_add) >= ggvv->ca.sd.irq0_max) {
\r
288 ggvv->ca.sd.irq0_cnt -= ggvv->ca.sd.irq0_max;
\r
292 p8259_OCW2(0,P8259_OCW2_NON_SPECIFIC_EOI);
\r
296 void SD_imf_tick(global_game_variables_t *gvar)
\r
298 if (gvar->ca.sd.imf_delay_countdown == 0) {
\r
300 adlib_write(gvar->ca.sd.imf_play_ptr->reg,gvar->ca.sd.imf_play_ptr->data);
\r
301 gvar->ca.sd.imf_delay_countdown = gvar->ca.sd.imf_play_ptr->delay;
\r
302 gvar->ca.sd.imf_play_ptr++;
\r
303 if (gvar->ca.sd.imf_play_ptr == gvar->ca.sd.imf_music_end)
\r
305 // printf("replay\n");
\r
306 gvar->ca.sd.imf_play_ptr = gvar->ca.sd.imf_music;
\r
308 } while (gvar->ca.sd.imf_delay_countdown == 0);
\r
311 gvar->ca.sd.imf_delay_countdown--;
\r
315 void SD_adlib_shut_up() {
\r
318 memset(adlib_fm,0,sizeof(adlib_fm));
\r
319 memset(&adlib_reg_bd,0,sizeof(adlib_reg_bd));
\r
320 for (i=0;i < adlib_fm_voices;i++) {
\r
321 struct adlib_fm_operator *f;
\r
322 f = &adlib_fm[i].mod;
\r
323 f->ch_a = f->ch_b = f->ch_c = f->ch_d = 1;
\r
324 f = &adlib_fm[i].car;
\r
325 f->ch_a = f->ch_b = f->ch_c = f->ch_d = 1;
\r
328 for (i=0;i < adlib_fm_voices;i++) {
\r
329 struct adlib_fm_operator *f;
\r
331 f = &adlib_fm[i].mod;
\r
332 f->mod_multiple = 1;
\r
333 f->total_level = 63 - 16;
\r
334 f->attack_rate = 15;
\r
336 f->sustain_level = 0;
\r
337 f->release_rate = 8;
\r
343 f = &adlib_fm[i].car;
\r
344 f->mod_multiple = 1;
\r
345 f->total_level = 63 - 16;
\r
346 f->attack_rate = 15;
\r
348 f->sustain_level = 0;
\r
349 f->release_rate = 8;
\r