]> 4ch.mooo.com Git - 16.git/blob - src/lib/dl/adlib.c
cleaned up the repo from debugging watcom2 ^^
[16.git] / src / lib / dl / adlib.c
1 /* adlib.c
2  *
3  * Adlib OPL2/OPL3 FM synthesizer chipset controller library.
4  * (C) 2010-2012 Jonathan Campbell.
5  * Hackipedia DOS library.
6  *
7  * This code is licensed under the LGPL.
8  * <insert LGPL legal text here>
9  *
10  * Compiles for intended target environments:
11  *   - MS-DOS [pure DOS mode, or Windows or OS/2 DOS Box]
12  *
13  * On most Sound Blaster compatible cards all the way up to the late 1990s, a
14  * Yamaha OPL2 or OPL3 chipset exists (or may be emulated on PCI cards) that
15  * responds to ports 388h-389h. Through these I/O ports you control the FM
16  * synthesizer engine. On some cards, a second OPL2 may exist at 38A-38Bh,
17  * and on ISA PnP cards, the OPL3 may be located at 38C-38Dh if software
18  * configured. */
19 /* TODO: ISA PnP complementary library */
20 /* TODO: Modifications to the library to support OPL2/OPL3 chipsets at I/O ports
21  *       other than 388h */
22  
23 #include <stdio.h>
24 #include <conio.h> /* this is where Open Watcom hides the outp() etc. functions */
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <malloc.h>
29 #include <fcntl.h>
30 #include <dos.h>
31
32 #include "src/lib/doslib/adlib.h"
33
34 unsigned short                  adlib_voice_to_op_opl2[9] = {0x00,0x01,0x02, 0x08,0x09,0x0A, 0x10,0x11,0x12};
35 /* NTS: There is a HOWTO out there stating that the registers line up 0,1,2,6,7,8,... == WRONG! */
36 unsigned short                  adlib_voice_to_op_opl3[18] = {0x00,0x01,0x02, 0x08,0x09,0x0A, 0x10,0x11,0x12, 0x100,0x101,0x102, 0x108,0x109,0x10A, 0x110,0x111,0x112};
37 unsigned short*                 adlib_voice_to_op = adlib_voice_to_op_opl2;
38
39 struct adlib_reg_bd             adlib_reg_bd;
40 struct adlib_fm_channel         adlib_fm[ADLIB_FM_VOICES];
41 int                             adlib_fm_voices = 0;
42 unsigned char                   adlib_flags = 0;
43
44 struct adlib_fm_channel adlib_fm_preset_violin_opl3 = {
45         .mod = {0,      1,      1,      1,      1,      1,      42,     6,      1,      1,      4,      0,
46                 3,      456,    1,      1,      1,      1,      4,      0,      5},
47         .car = {0,      1,      1,      1,      1,      1,      63,     4,      1,      1,      4,      0,
48                 3,      456,    1,      1,      1,      1,      0,      0,      2}
49 };
50
51 struct adlib_fm_channel adlib_fm_preset_violin_opl2 = {
52         .mod = {0,      1,      1,      1,      1,      1,      42,     6,      1,      1,      4,      0,
53                 3,      456,    1,      1,      1,      1,      2,      0,      1},
54         .car = {0,      1,      1,      1,      1,      1,      63,     4,      1,      1,      4,      0,
55                 3,      456,    1,      1,      1,      1,      0,      0,      2}
56 };
57
58 struct adlib_fm_channel adlib_fm_preset_piano = {
59         .mod = {0,      0,      1,      1,      1,      1,      42,     10,     4,      2,      3,      0,
60                 4,      456,    1,      1,      1,      1,      4,      0,      0},
61         .car = {0,      0,      1,      1,      1,      1,      63,     10,     1,      8,      3,      0,
62                 4,      456,    1,      1,      1,      1,      0,      0,      0}
63 };
64
65 struct adlib_fm_channel adlib_fm_preset_harpsichord = {
66         .mod = {0,      0,      1,      1,      1,      1,      42,     10,     3,      2,      3,      0,
67                 4,      456,    1,      1,      1,      1,      2,      0,      3},
68         .car = {0,      0,      1,      1,      1,      1,      63,     10,     5,      3,      3,      0,
69                 4,      456,    1,      1,      1,      1,      0,      0,      3}
70 };
71
72 /* NTS: adjust the modulator total level value to vary between muted (27) and open (47) with
73  *      further adjustment if you want to mimick the change in sound when you blow harder */
74 struct adlib_fm_channel adlib_fm_preset_horn = {
75         .mod = {0,      0,      1,      0,      1,      0,      47,     6,      1,      1,      7,      0,
76                 4,      514,    1,      1,      1,      1,      0,      0,      0},
77         .car = {0,      0,      1,      0,      1,      0,      47,     8,      2,      2,      7,      0,
78                 4,      456,    1,      1,      1,      1,      0,      0,      0}
79 };
80
81 struct adlib_fm_channel adlib_fm_preset_deep_bass_drum = {
82         .mod = {0,      0,      0,      0,      1,      0,      13,     7,      1,      0,      1,      0,
83                 2,      456,    1,      1,      1,      1,      7,      0,      1},
84         .car = {0,      0,      1,      1,      1,      1,      63,     15,     2,      6,      1,      0,
85                 2,      456,    1,      1,      1,      1,      0,      0,      0}
86 };
87
88 /* NTS: You can simulate hitting software or harder by adjusting the modulator total volume
89  *      as well as raising or lowering the frequency */
90 struct adlib_fm_channel adlib_fm_preset_small_drum = {
91         .mod = {0,      0,      0,      1,      1,      1,      54,     15,     10,     15,     15,     0,
92                 3,      456,    1,      1,      1,      1,      1,      0,      0},
93         .car = {0,      0,      1,      1,      1,      1,      63,     15,     7,      15,     15,     0,
94                 3,      456,    1,      1,      1,      1,      1,      0,      0}
95 };
96
97 unsigned char adlib_read(unsigned short i) {
98         unsigned char c;
99         outp(ADLIB_IO_INDEX+((i>>8)*2),(unsigned char)i);
100         adlib_wait();
101         c = inp(ADLIB_IO_DATA+((i>>8)*2));
102         adlib_wait();
103         return c;
104 }
105
106 void adlib_write(unsigned short i,unsigned char d) {
107         outp(ADLIB_IO_INDEX+((i>>8)*2),(unsigned char)i);
108         adlib_wait();
109         outp(ADLIB_IO_DATA+((i>>8)*2),d);
110         adlib_wait();
111 }
112
113 /* TODO: adlib_write_imm_1() and adlib_write_imm_2()
114  *       this would allow DOS programs to use this ADLIB library from within
115  *       an interrupt routine */
116
117 int probe_adlib(unsigned char sec) {
118         unsigned char a,b,retry=3;
119         unsigned short bas = sec ? 0x100 : 0;
120
121         /* this code uses the 8254 for timing */
122         if (!probe_8254())
123                 return 1;
124
125         do {
126                 adlib_write(0x04+bas,0x60);                     /* reset both timers */
127                 adlib_write(0x04+bas,0x80);                     /* enable interrupts */
128                 a = adlib_status(sec);
129                 adlib_write(0x02+bas,0xFF);                     /* timer 1 */
130                 adlib_write(0x04+bas,0x21);                     /* start timer 1 */
131                 t8254_wait(t8254_us2ticks(100));
132                 b = adlib_status(sec);
133                 adlib_write(0x04+bas,0x60);                     /* reset both timers */
134                 adlib_write(0x04+bas,0x00);                     /* disable interrupts */
135
136                 if ((a&0xE0) == 0x00 && (b&0xE0) == 0xC0)
137                         return 1;
138
139         } while (--retry != 0);
140
141         return 0;
142 }
143
144 int init_adlib() {
145         adlib_flags = 0;
146         if (!probe_adlib(0))
147                 return 0;
148
149         adlib_write(0x01,0x20); /* enable waveform select */
150         adlib_voice_to_op = adlib_voice_to_op_opl2;
151         adlib_fm_voices = 9;
152
153         if (probe_adlib(1)) {
154                 adlib_fm_voices = 18;
155                 adlib_flags = ADLIB_FM_DUAL_OPL2;
156         }
157         else {
158                 /* NTS: "unofficial" method of detecting OPL3 */
159                 if ((adlib_status(0) & 0x06) == 0) {
160                         adlib_fm_voices = 18;
161                         adlib_flags = ADLIB_FM_OPL3;
162                         adlib_voice_to_op = adlib_voice_to_op_opl3;
163
164                         /* init like an OPL3 */
165                         adlib_write(0x105,0x01);                /* set OPL3 bit */
166                         probe_adlib(0);
167                         adlib_write(0x104,0x00);                /* disable any 4op connections */
168                 }
169         }
170
171         return 1;
172 }
173
174 void shutdown_adlib_opl3() {
175         if (adlib_flags & ADLIB_FM_OPL3) {
176                 adlib_write(0x105,0x00);                /* clear OPL3 bit */
177                 probe_adlib(0);
178                 adlib_fm_voices = 9;
179                 adlib_voice_to_op = adlib_voice_to_op_opl2;
180                 adlib_flags &= ~ADLIB_FM_OPL3;
181         }
182 }
183
184 void shutdown_adlib() {
185         shutdown_adlib_opl3();
186 }
187
188 void adlib_update_group20(unsigned int op,struct adlib_fm_operator *f) {
189         adlib_write(0x20+op,    (f->am << 7) |
190                                 (f->vibrato << 6) |
191                                 (f->sustain << 5) |
192                                 (f->key_scaling_rate << 4) |
193                                 (f->mod_multiple << 0));
194 }
195
196 void adlib_update_group40(unsigned int op,struct adlib_fm_operator *f) {
197         adlib_write(0x40+op,    (f->level_key_scale << 6) |
198                                 ((f->total_level^63) << 0));
199 }
200
201 void adlib_update_group60(unsigned int op,struct adlib_fm_operator *f) {
202         adlib_write(0x60+op,    (f->attack_rate << 4) |
203                                 (f->decay_rate << 0));
204 }
205
206 void adlib_update_group80(unsigned int op,struct adlib_fm_operator *f) {
207         adlib_write(0x80+op,    (f->sustain_level << 4) |
208                                 (f->release_rate << 0));
209 }
210
211 void adlib_update_groupA0(unsigned int channel,struct adlib_fm_channel *ch) {
212         struct adlib_fm_operator *f = &ch->mod;
213         unsigned int x = (channel >= 9) ? 0x100 : 0;
214         adlib_write(0xA0+(channel%9)+x,  f->f_number);
215         adlib_write(0xB0+(channel%9)+x, (f->key_on << 5) |
216                                         (f->octave << 2) |
217                                         (f->f_number >> 8));
218 }
219
220 void adlib_update_groupC0(unsigned int channel,struct adlib_fm_channel *ch) {
221         struct adlib_fm_operator *f = &ch->mod;
222         unsigned int x = (channel >= 9) ? 0x100 : 0;
223         adlib_write(0xC0+(channel%9)+x, (f->feedback << 1) |
224                                         (f->connection << 0) |
225                                         (f->ch_d << 7) |
226                                         (f->ch_c << 6) |
227                                         (f->ch_b << 5) |
228                                         (f->ch_a << 4));
229 }
230
231 void adlib_update_groupE0(unsigned int op,struct adlib_fm_operator *f) {
232         adlib_write(0xE0+op,    (f->waveform << 0));
233 }
234
235 void adlib_update_operator(unsigned int op,struct adlib_fm_operator *f) {
236         adlib_update_group20(op,f);
237         adlib_update_group40(op,f);
238         adlib_update_group60(op,f);
239         adlib_update_group80(op,f);
240         adlib_update_groupE0(op,f);
241 }
242
243 void adlib_update_bd(struct adlib_reg_bd *b) {
244         adlib_write(0xBD,       (b->am_depth << 7) |
245                                 (b->vibrato_depth << 6) |
246                                 (b->rythm_enable << 5) |
247                                 (b->bass_drum_on << 4) |
248                                 (b->snare_drum_on << 3) |
249                                 (b->tom_tom_on << 2) |
250                                 (b->cymbal_on << 1) |
251                                 (b->hi_hat_on << 0));
252 }
253
254 void adlib_apply_all() {
255         struct adlib_fm_operator *f;
256         unsigned short op;
257         int ch;
258
259         for (ch=0;ch < adlib_fm_voices;ch++) {
260                 f = &adlib_fm[ch].mod; op = adlib_voice_to_op[ch];   adlib_update_operator(op,f);
261                 f = &adlib_fm[ch].car; op = adlib_voice_to_op[ch]+3; adlib_update_operator(op,f);
262                 adlib_update_groupA0(ch,&adlib_fm[ch]);
263                 adlib_update_groupC0(ch,&adlib_fm[ch]);
264         }
265         adlib_update_bd(&adlib_reg_bd);
266 }
267
268 double adlib_fm_op_to_freq(struct adlib_fm_operator *f) {
269         unsigned long t = (unsigned long)f->f_number * 49716UL;
270         return (double)t / (1UL << (20UL - (unsigned long)f->octave));
271 }
272
273 void adlib_freq_to_fm_op(struct adlib_fm_operator *f,double freq) {
274         unsigned long l;
275
276         freq *= (1UL << (20UL - (unsigned long)f->octave));
277         l = (unsigned long)freq / 49716UL;
278         f->octave = 4;
279         while (l > 1023UL) {
280                 f->octave++;
281                 l >>= 1UL;
282         }
283         while (l != 0UL && l < 256UL) {
284                 f->octave--;
285                 l <<= 1UL;
286         }
287         f->f_number = l;
288 }
289