]> 4ch.mooo.com Git - 16.git/blob - src/lib/16_sd.c
imfplay works now~
[16.git] / src / lib / 16_sd.c
1 /* Project 16 Source Code~\r
2  * Copyright (C) 2012-2017 sparky4 & pngwen & andrius4669 & joncampbell123 & yakui-lover\r
3  *\r
4  * This file is part of Project 16.\r
5  *\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
10  *\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
15  *\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
20  *\r
21  */\r
22 \r
23 #include "src/lib/16_sd.h"\r
24 \r
25 //#define SD_USESCAMMPM\r
26 \r
27 static void (interrupt *SD_old_irq0)();\r
28 //void interrupt        (*old_irq0)(void);\r
29 \r
30 void opl2out(word reg, word data)\r
31 {\r
32         __asm\r
33         {\r
34                 mov     ax,reg\r
35                 mov     dx,word ptr [ADLIB_FM_ADDRESS]\r
36                 or      ah,ah\r
37                 jz      @@1\r
38                 add     dx,2\r
39 @@1:    out     dx,al\r
40                 mov     cx,6\r
41 @@2:    in      al,dx\r
42                 loop    @@2\r
43                 inc     dl\r
44                 mov     ax,data\r
45                 out     dx,al\r
46                 dec     dl\r
47                 mov     cx,36\r
48 @@3:    in      al,dx\r
49                 loop    @@3\r
50         }\r
51 }\r
52 \r
53 void opl3out(word reg, word data)\r
54 {\r
55         __asm\r
56         {\r
57                 mov     ax,reg\r
58                 mov     dx,word ptr [ADLIB_FM_ADDRESS]\r
59                 or      ah,ah\r
60                 jz      @@1\r
61                 add     dx,2\r
62 @@1:    out     dx,al\r
63                 inc     dl\r
64                 mov     ax,data\r
65                 out     dx,al\r
66                 dec     dl\r
67                 mov     cx,26\r
68 @@2:    in      al,dx\r
69                 loop    @@2\r
70         }\r
71 }\r
72 \r
73 void opl3exp(word data)\r
74 {\r
75         __asm\r
76         {\r
77                 mov     ax,data\r
78                 mov     dx,word ptr [ADLIB_FM_ADDRESS]\r
79                 add     dx,2\r
80                 out     dx,al\r
81                 mov     cx,6\r
82 @@1:    in      al,dx\r
83                 loop    @@1\r
84                 inc     dl\r
85                 mov     al,ah\r
86                 out     dx,al\r
87                 mov     cx,36\r
88 @@2:    in      al,dx\r
89                 loop    @@2\r
90         }\r
91 }\r
92 \r
93 /* Function: FMResest *******************************************************\r
94 *\r
95 *     Description:        quick and dirty sound card reset (zeros all\r
96 *                         registers).\r
97 *\r
98 */\r
99 void FMReset(void/*int percusiveMode*/)\r
100 {\r
101         int i;\r
102 \r
103         /* zero all registers */\r
104         for(i = MIN_REGISTER; i < MAX_REGISTER+1; i++) opl2out(i, 0);\r
105 \r
106         /* allow FM chips to control the waveform of each operator */\r
107         opl2out(0x01, 0x20);\r
108 \r
109         /* set rhythm enabled (6 melodic voices, 5 percussive) */\r
110         opl2out(0xBD, 0x20);\r
111 \r
112         //FMSetPercusiveMode(percusiveMode);\r
113 } /* End of FMReset */\r
114 \r
115 /* Function: FMKeyOff *******************************************************\r
116 *\r
117 *     Parameters:        voice - which voice to turn off.\r
118 *\r
119 *     Description:        turns off the specified voice.\r
120 *\r
121 */\r
122 void FMKeyOff(int voice)\r
123 {\r
124         int regNum;\r
125 \r
126         /* turn voice off */\r
127         regNum = 0xB0 + voice % 11;//NUMVOICE;\r
128         opl2out(regNum, 0x0E);\r
129 } /* End of FMKeyOff */\r
130 \r
131 /* Function: FMKeyOn *******************************************************\r
132 *\r
133 *     Parameters:        voice - which voice to turn on.\r
134 *                         freq - its frequency (note).\r
135 *                         octave - its octave.\r
136 *\r
137 *     Description:        turns on a voice of specfied frequency and\r
138 *                         octave.\r
139 *\r
140 */\r
141 void FMKeyOn(int voice, int freq, int octave)\r
142 {\r
143         int regNum, tmp;\r
144 \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
151 \r
152 /* Function: FMSetVoice *****************************************************\r
153 *\r
154 *     Parameters:        voiceNum - which voice to set.\r
155 *                         ins - instrument to set voice.\r
156 *\r
157 *     Description:        sets the instrument of a voice.\r
158 *\r
159 */\r
160 void FMSetVoice(int voiceNum, FMInstrument *ins){\r
161         int opCellNum, cellOffset;\r
162 \r
163         voiceNum %= 11;//NUMVOICE;\r
164         cellOffset = voiceNum % 3 + ((voiceNum / 3) << 3);\r
165 \r
166         /* set sound characteristic */\r
167         opCellNum = 0x20 + (char)cellOffset;\r
168         opl2out(opCellNum, ins->SoundCharacteristic[0]);\r
169         opCellNum += 3;\r
170         opl2out(opCellNum, ins->SoundCharacteristic[1]);\r
171 \r
172         /* set level/output */\r
173         opCellNum = 0x40 + (char)cellOffset;\r
174         opl2out(opCellNum, ins->Level[0]);\r
175         opCellNum += 3;\r
176         opl2out(opCellNum, ins->Level[1]);\r
177 \r
178         /* set Attack/Decay */\r
179         opCellNum = 0x60 + (char)cellOffset;\r
180         opl2out(opCellNum, ins->AttackDecay[0]);\r
181         opCellNum += 3;\r
182         opl2out(opCellNum, ins->AttackDecay[1]);\r
183 \r
184         /* set Sustain/Release */\r
185         opCellNum = 0x80 + (char)cellOffset;\r
186         opl2out(opCellNum, ins->SustainRelease[0]);\r
187         opCellNum += 3;\r
188         opl2out(opCellNum, ins->SustainRelease[1]);\r
189 \r
190         /* set Wave Select */\r
191         opCellNum = 0xE0 + (char)cellOffset;\r
192         opl2out(opCellNum, ins->WaveSelect[0]);\r
193         opCellNum += 3;\r
194         opl2out(opCellNum, ins->WaveSelect[1]);\r
195 \r
196         /* set Feedback/Selectivity */\r
197         opCellNum = (byte)0xC0 + (byte)voiceNum;\r
198         opl2out(opCellNum, ins->Feedback);\r
199 } /* End of FMSetVoice */\r
200 \r
201 void SD_Initimf(global_game_variables_t *gvar)\r
202 {\r
203         if (!init_adlib()) {\r
204                 printf("Cannot init library\n");\r
205                 return;\r
206         }\r
207         if (!probe_8254()) { /* we need the timer to keep time with the music */\r
208                 printf("8254 timer not found\n");\r
209                 return;\r
210         }\r
211 \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
223 \r
224         SD_adlib_shut_up();\r
225         shutdown_adlib_opl3(); // NTS: Apparently the music won't play otherwise\r
226 }\r
227 \r
228 void SD_imf_free_music(global_game_variables_t *gvar)\r
229 {\r
230 #ifndef SD_USESCAMMPM\r
231         if (gvar->ca.sd.imf_music) free(gvar->ca.sd.imf_music);\r
232 #else\r
233         MM_FreePtr(MEMPTRCONV gvar->ca.audiosegs[0], gvar);     //TODO make behave like id engine\r
234 #endif\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
237 }\r
238 \r
239 int SD_imf_load_music(const char *path, global_game_variables_t *gvar)\r
240 {\r
241         unsigned long len;\r
242         unsigned char buf[8];\r
243         int fd;\r
244 \r
245         SD_imf_free_music(gvar);\r
246 \r
247         fd = open(path,O_RDONLY|O_BINARY);\r
248         if (fd < 0) return 0;\r
249 \r
250         len = lseek(fd,0,SEEK_END);\r
251         lseek(fd,0,SEEK_SET);\r
252         read(fd,buf,2);\r
253         if (buf[0] != 0 || buf[1] != 0) // type 1 IMF\r
254                 len = *((uint16_t*)buf);\r
255         else\r
256                 lseek(fd,0,SEEK_SET);\r
257 \r
258         if (len == 0 || len > 65535UL) {\r
259                 close(fd);\r
260                 return 0;\r
261         }\r
262         len -= len & 3;\r
263 \r
264 #ifndef SD_USESCAMMPM\r
265         gvar->ca.sd.imf_music = malloc(len);\r
266 #else\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
269 #endif\r
270         if (gvar->ca.sd.imf_music == NULL) {\r
271                 close(fd);\r
272                 return 0;\r
273         }\r
274         read(fd,gvar->ca.sd.imf_music,len);\r
275         close(fd);\r
276 \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
279         return 1;\r
280 }\r
281 \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
285 {\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
289                 SD_old_irq0();\r
290         }\r
291         else {\r
292                 p8259_OCW2(0,P8259_OCW2_NON_SPECIFIC_EOI);\r
293         }\r
294 }\r
295 \r
296 void SD_imf_tick(global_game_variables_t *gvar)\r
297 {\r
298         if (gvar->ca.sd.imf_delay_countdown == 0) {\r
299                 do {\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
304                         {\r
305 //                              printf("replay\n");\r
306                                 gvar->ca.sd.imf_play_ptr = gvar->ca.sd.imf_music;\r
307                         }\r
308                 } while (gvar->ca.sd.imf_delay_countdown == 0);\r
309         }\r
310         else {\r
311                 gvar->ca.sd.imf_delay_countdown--;\r
312         }\r
313 }\r
314 \r
315 void SD_adlib_shut_up() {\r
316         int i;\r
317 \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
326         }\r
327 \r
328         for (i=0;i < adlib_fm_voices;i++) {\r
329                 struct adlib_fm_operator *f;\r
330 \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
335                 f->decay_rate = 4;\r
336                 f->sustain_level = 0;\r
337                 f->release_rate = 8;\r
338                 f->f_number = 400;\r
339                 f->sustain = 1;\r
340                 f->octave = 4;\r
341                 f->key_on = 0;\r
342 \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
347                 f->decay_rate = 4;\r
348                 f->sustain_level = 0;\r
349                 f->release_rate = 8;\r
350                 f->f_number = 0;\r
351                 f->sustain = 1;\r
352                 f->octave = 0;\r
353                 f->key_on = 0;\r
354         }\r
355 \r
356         adlib_apply_all();\r
357 }\r