]> 4ch.mooo.com Git - 16.git/blob - src/lib/16_sd.c
16_ca needs huge amounts of work and I should remember what needs to be done soon...
[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 //static void (interrupt *old_irq0)();\r
26 void interrupt  (*old_irq0)(void);\r
27 \r
28 void opl2out(word reg, word data)\r
29 {\r
30         __asm\r
31         {\r
32                 mov     ax,reg\r
33                 mov     dx,word ptr [ADLIB_FM_ADDRESS]\r
34                 or      ah,ah\r
35                 jz      @@1\r
36                 add     dx,2\r
37 @@1:    out     dx,al\r
38                 mov     cx,6\r
39 @@2:    in      al,dx\r
40                 loop    @@2\r
41                 inc     dl\r
42                 mov     ax,data\r
43                 out     dx,al\r
44                 dec     dl\r
45                 mov     cx,36\r
46 @@3:    in      al,dx\r
47                 loop    @@3\r
48         }\r
49 }\r
50 \r
51 void opl3out(word reg, word data)\r
52 {\r
53         __asm\r
54         {\r
55                 mov     ax,reg\r
56                 mov     dx,word ptr [ADLIB_FM_ADDRESS]\r
57                 or      ah,ah\r
58                 jz      @@1\r
59                 add     dx,2\r
60 @@1:    out     dx,al\r
61                 inc     dl\r
62                 mov     ax,data\r
63                 out     dx,al\r
64                 dec     dl\r
65                 mov     cx,26\r
66 @@2:    in      al,dx\r
67                 loop    @@2\r
68         }\r
69 }\r
70 \r
71 void opl3exp(word data)\r
72 {\r
73         __asm\r
74         {\r
75                 mov     ax,data\r
76                 mov     dx,word ptr [ADLIB_FM_ADDRESS]\r
77                 add     dx,2\r
78                 out     dx,al\r
79                 mov     cx,6\r
80 @@1:    in      al,dx\r
81                 loop    @@1\r
82                 inc     dl\r
83                 mov     al,ah\r
84                 out     dx,al\r
85                 mov     cx,36\r
86 @@2:    in      al,dx\r
87                 loop    @@2\r
88         }\r
89 }\r
90 \r
91 /* Function: FMResest *******************************************************\r
92 *\r
93 *     Description:        quick and dirty sound card reset (zeros all\r
94 *                         registers).\r
95 *\r
96 */\r
97 void FMReset(void/*int percusiveMode*/)\r
98 {\r
99         int i;\r
100 \r
101         /* zero all registers */\r
102         for(i = MIN_REGISTER; i < MAX_REGISTER+1; i++) opl2out(i, 0);\r
103 \r
104         /* allow FM chips to control the waveform of each operator */\r
105         opl2out(0x01, 0x20);\r
106 \r
107         /* set rhythm enabled (6 melodic voices, 5 percussive) */\r
108         opl2out(0xBD, 0x20);\r
109 \r
110         //FMSetPercusiveMode(percusiveMode);\r
111 } /* End of FMReset */\r
112 \r
113 /* Function: FMKeyOff *******************************************************\r
114 *\r
115 *     Parameters:        voice - which voice to turn off.\r
116 *\r
117 *     Description:        turns off the specified voice.\r
118 *\r
119 */\r
120 void FMKeyOff(int voice)\r
121 {\r
122         int regNum;\r
123 \r
124         /* turn voice off */\r
125         regNum = 0xB0 + voice % 11;//NUMVOICE;\r
126         opl2out(regNum, 0x0E);\r
127 } /* End of FMKeyOff */\r
128 \r
129 /* Function: FMKeyOn *******************************************************\r
130 *\r
131 *     Parameters:        voice - which voice to turn on.\r
132 *                         freq - its frequency (note).\r
133 *                         octave - its octave.\r
134 *\r
135 *     Description:        turns on a voice of specfied frequency and\r
136 *                         octave.\r
137 *\r
138 */\r
139 void FMKeyOn(int voice, int freq, int octave)\r
140 {\r
141         int regNum, tmp;\r
142 \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
149 \r
150 /* Function: FMSetVoice *****************************************************\r
151 *\r
152 *     Parameters:        voiceNum - which voice to set.\r
153 *                         ins - instrument to set voice.\r
154 *\r
155 *     Description:        sets the instrument of a voice.\r
156 *\r
157 */\r
158 void FMSetVoice(int voiceNum, FMInstrument *ins){\r
159         int opCellNum, cellOffset;\r
160 \r
161         voiceNum %= 11;//NUMVOICE;\r
162         cellOffset = voiceNum % 3 + ((voiceNum / 3) << 3);\r
163 \r
164         /* set sound characteristic */\r
165         opCellNum = 0x20 + (char)cellOffset;\r
166         opl2out(opCellNum, ins->SoundCharacteristic[0]);\r
167         opCellNum += 3;\r
168         opl2out(opCellNum, ins->SoundCharacteristic[1]);\r
169 \r
170         /* set level/output */\r
171         opCellNum = 0x40 + (char)cellOffset;\r
172         opl2out(opCellNum, ins->Level[0]);\r
173         opCellNum += 3;\r
174         opl2out(opCellNum, ins->Level[1]);\r
175 \r
176         /* set Attack/Decay */\r
177         opCellNum = 0x60 + (char)cellOffset;\r
178         opl2out(opCellNum, ins->AttackDecay[0]);\r
179         opCellNum += 3;\r
180         opl2out(opCellNum, ins->AttackDecay[1]);\r
181 \r
182         /* set Sustain/Release */\r
183         opCellNum = 0x80 + (char)cellOffset;\r
184         opl2out(opCellNum, ins->SustainRelease[0]);\r
185         opCellNum += 3;\r
186         opl2out(opCellNum, ins->SustainRelease[1]);\r
187 \r
188         /* set Wave Select */\r
189         opCellNum = 0xE0 + (char)cellOffset;\r
190         opl2out(opCellNum, ins->WaveSelect[0]);\r
191         opCellNum += 3;\r
192         opl2out(opCellNum, ins->WaveSelect[1]);\r
193 \r
194         /* set Feedback/Selectivity */\r
195         opCellNum = (byte)0xC0 + (byte)voiceNum;\r
196         opl2out(opCellNum, ins->Feedback);\r
197 } /* End of FMSetVoice */\r
198 \r
199 void SD_Initimf(global_game_variables_t *gvar)\r
200 {\r
201         if (!init_adlib()) {\r
202                 printf("Cannot init library\n");\r
203                 return;\r
204         }\r
205         if (!probe_8254()) { /* we need the timer to keep time with the music */\r
206                 printf("8254 timer not found\n");\r
207                 return;\r
208         }\r
209 \r
210         gvar->ca.sd.irq0_ticks=\r
211         //gvar->ca.sd.irq0_cnt=\r
212         //gvar->ca.sd.irq0_add=\r
213         gvar->ca.sd.imf_delay_countdown=\r
214         gvar->ca.sd.irq0_max=0;\r
215         gvar->ca.sd.imf_music=\r
216         gvar->ca.sd.imf_play_ptr=\r
217         gvar->ca.sd.imf_music_end=NULL;\r
218         gvar->ca.sd.irq0_cnt = 0;\r
219         gvar->ca.sd.irq0_add = 182;\r
220         gvar->ca.sd.irq0_max = 1000; /* about 18.2Hz */\r
221 \r
222         SD_adlib_shut_up();\r
223         shutdown_adlib_opl3(); // NTS: Apparently the music won't play otherwise\r
224 }\r
225 \r
226 void SD_imf_free_music(global_game_variables_t *gvar)\r
227 {\r
228 //      if (gvar->ca.sd.imf_music) free(gvar->ca.sd.imf_music);\r
229         MM_FreePtr(MEMPTRCONV gvar->ca.audiosegs[0], gvar);     //TODO make behave like id engine\r
230         gvar->ca.sd.imf_music = gvar->ca.sd.imf_play_ptr = gvar->ca.sd.imf_music_end = NULL;\r
231         gvar->ca.sd.imf_delay_countdown = 0;\r
232 }\r
233 \r
234 int SD_imf_load_music(const char *path, global_game_variables_t *gvar)\r
235 {\r
236         unsigned long len;\r
237         unsigned char buf[8];\r
238         int fd;\r
239 \r
240         SD_imf_free_music(gvar);\r
241 \r
242         fd = open(path,O_RDONLY|O_BINARY);\r
243         if (fd < 0) return 0;\r
244 \r
245         len = lseek(fd,0,SEEK_END);\r
246         lseek(fd,0,SEEK_SET);\r
247         read(fd,buf,2);\r
248         if (buf[0] != 0 || buf[1] != 0) // type 1 IMF\r
249                 len = *((uint16_t*)buf);\r
250         else\r
251                 lseek(fd,0,SEEK_SET);\r
252 \r
253         if (len == 0 || len > 65535UL) {\r
254                 close(fd);\r
255                 return 0;\r
256         }\r
257         len -= len & 3;\r
258 \r
259         MM_GetPtr(MEMPTRCONV gvar->ca.audiosegs[0],len, gvar);\r
260         gvar->ca.sd.imf_music = (struct imf_entry *)gvar->ca.audiosegs[0];\r
261         if (gvar->ca.sd.imf_music == NULL) {\r
262                 close(fd);\r
263                 return 0;\r
264         }\r
265         read(fd,gvar->ca.sd.imf_music,len);\r
266         close(fd);\r
267 \r
268         gvar->ca.sd.imf_play_ptr = gvar->ca.sd.imf_music;\r
269         gvar->ca.sd.imf_music_end = gvar->ca.sd.imf_music + (len >> 2UL);\r
270         return 1;\r
271 }\r
272 \r
273 struct glob_game_vars   *ggvv;\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
276 {\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
280                 old_irq0();\r
281         }\r
282         else {\r
283                 p8259_OCW2(0,P8259_OCW2_NON_SPECIFIC_EOI);\r
284         }\r
285 }\r
286 \r
287 void SD_imf_tick(global_game_variables_t *gvar)\r
288 {\r
289         if (gvar->ca.sd.imf_delay_countdown == 0) {\r
290                 do {\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
295                         {\r
296 //                              printf("replay\n");\r
297                                 gvar->ca.sd.imf_play_ptr = gvar->ca.sd.imf_music;\r
298                         }\r
299                 } while (gvar->ca.sd.imf_delay_countdown == 0);\r
300         }\r
301         else {\r
302                 gvar->ca.sd.imf_delay_countdown--;\r
303         }\r
304 }\r
305 \r
306 void SD_adlib_shut_up() {\r
307         int i;\r
308 \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
317         }\r
318 \r
319         for (i=0;i < adlib_fm_voices;i++) {\r
320                 struct adlib_fm_operator *f;\r
321 \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
326                 f->decay_rate = 4;\r
327                 f->sustain_level = 0;\r
328                 f->release_rate = 8;\r
329                 f->f_number = 400;\r
330                 f->sustain = 1;\r
331                 f->octave = 4;\r
332                 f->key_on = 0;\r
333 \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
338                 f->decay_rate = 4;\r
339                 f->sustain_level = 0;\r
340                 f->release_rate = 8;\r
341                 f->f_number = 0;\r
342                 f->sustain = 1;\r
343                 f->octave = 0;\r
344                 f->key_on = 0;\r
345         }\r
346 \r
347         adlib_apply_all();\r
348 }\r