comparison Plugins/Input/adplug/core/realopl.cpp @ 428:15ca2ea93a30 trunk

[svn] Sync with upstream CVS. This implements RIX playback.
author chainsaw
date Sat, 14 Jan 2006 07:27:13 -0800
parents 8df427a314a8
children 0a73d1faeb4e
comparison
equal deleted inserted replaced
427:f61e69a1a376 428:15ca2ea93a30
1 /* 1 /*
2 * Adplug - Replayer for many OPL2/OPL3 audio file formats. 2 * Adplug - Replayer for many OPL2/OPL3 audio file formats.
3 * Copyright (C) 1999, 2000, 2001 Simon Peter, <dn.tlp@gmx.net>, et al. 3 * Copyright (C) 1999 - 2005 Simon Peter, <dn.tlp@gmx.net>, et al.
4 * 4 *
5 * This library is free software; you can redistribute it and/or 5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public 6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either 7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version. 8 * version 2.1 of the License, or (at your option) any later version.
14 * 14 *
15 * You should have received a copy of the GNU Lesser General Public 15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software 16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 * 18 *
19 * 19 * realopl.cpp - Real hardware OPL, by Simon Peter <dn.tlp@gmx.net>
20 * realopl.cpp - Real hardware OPL, by Simon Peter (dn.tlp@gmx.net)
21 */ 20 */
22 21
23 #include <conio.h> 22 #ifdef _MSC_VER // Microsoft Visual C++
23 # include <conio.h>
24 # define INP _inp
25 # define OUTP _outp
26 #elif defined(__WATCOMC__) // Watcom C/C++ and OpenWatcom
27 # include <conio.h>
28 # define INP inp
29 # define OUTP outp
30 #elif defined(WIN32) && defined(__MSVCRT__) && defined(__MINGW32__)
31 /*
32 int __cdecl _inp(unsigned short);
33 int __cdecl _outp(unsigned short, int);
34 # define INP _inp
35 # define OUTP _outp
36 */
37 # define INP inb
38 # define OUTP(reg, val) outb(val, reg)
39 #else // no support on other platforms
40 # define INP(reg) 0
41 # define OUTP(reg, val)
42 #endif
43
24 #include "realopl.h" 44 #include "realopl.h"
25 45
26 #ifdef _MSC_VER 46 #define SHORTDELAY 6 // short delay in I/O port-reads after OPL hardware output
27 #define INP _inp 47 #define LONGDELAY 35 // long delay in I/O port-reads after OPL hardware output
28 #define OUTP _outp 48
29 #elif defined(__WATCOMC__) 49 const unsigned char CRealopl::op_table[9] =
30 #define INP inp 50 {0x00, 0x01, 0x02, 0x08, 0x09, 0x0a, 0x10, 0x11, 0x12};
31 #define OUTP outp 51
52 #if defined(WIN32) && defined(__MINGW32__)
53 static __inline unsigned char
54 inb (unsigned short int port)
55 {
56 unsigned char _v;
57
58 __asm__ __volatile__ ("inb %w1,%0":"=a" (_v):"Nd" (port));
59 return _v;
60 }
61
62 static __inline void
63 outb (unsigned char value, unsigned short int port)
64 {
65 __asm__ __volatile__ ("outb %b0,%w1": :"a" (value), "Nd" (port));
66 }
32 #endif 67 #endif
33 68
34 /* 69 CRealopl::CRealopl(unsigned short initport)
35 * chris: TODO: This isn't quite right. According to Jeff Lee's doc: 70 : adlport(initport), hardvol(0), bequiet(false), nowrite(false)
36 * 71 {
37 * "After writing to the register port, you must wait twelve cycles before 72 for(int i=0;i<22;i++) {
38 * sending the data; after writing the data, eighty-four cycles must elapse 73 hardvols[0][i][0] = 0;
39 * before any other sound card operation may be performed. 74 hardvols[0][i][1] = 0;
40 * 75 hardvols[1][i][0] = 0;
41 * | The AdLib manual gives the wait times in microseconds: three point three 76 hardvols[1][i][1] = 0;
42 * | (3.3) microseconds for the address, and twenty-three (23) microseconds 77 }
43 * | for the data. 78
44 * | 79 currType = TYPE_OPL3;
45 * | The most accurate method of producing the delay is to read the register 80 }
46 * | port six times after writing to the register port, and read the register 81
47 * | port thirty-five times after writing to the data port." 82 bool CRealopl::harddetect()
48 * 83 {
49 * 84 unsigned char stat1, stat2, i;
50 * In other words, the delay constants represented by {SHORT|LONG}DELAY below 85 unsigned short adp = (currChip == 0 ? adlport : adlport + 2);
51 * aren't given in microseconds, but rather direct reads (INB) from the Adlib 86
52 * I/O ports. 87 hardwrite(4,0x60); hardwrite(4,0x80);
53 * 88 stat1 = INP(adp);
54 * Translation: SHORTDELAY should be 6, and LONGDELAY is just fine. :-) 89 hardwrite(2,0xff); hardwrite(4,0x21);
55 */ 90 for(i=0;i<80;i++) // wait for adlib
56 91 INP(adp);
57 #define SHORTDELAY 6 // short delay in I/O port-reads after OPL hardware output 92 stat2 = INP(adp);
58 #define LONGDELAY 35 // long delay in I/O port-reads after OPL hardware output 93 hardwrite(4,0x60); hardwrite(4,0x80);
59 94
60 // the 9 operators as expected by the OPL2 95 if(((stat1 & 0xe0) == 0) && ((stat2 & 0xe0) == 0xc0))
61 static const unsigned char op_table[9] = {0x00, 0x01, 0x02, 0x08, 0x09, 0x0a, 0x10, 0x11, 0x12}; 96 return true;
62 97 else
63 CRealopl::CRealopl(unsigned short initport): adlport(initport), hardvol(0), bequiet(false), nowrite(false) 98 return false;
64 {
65 for(int i=0;i<22;i++) {
66 hardvols[i][0] = 0;
67 hardvols[i][1] = 0;
68 }
69 } 99 }
70 100
71 bool CRealopl::detect() 101 bool CRealopl::detect()
72 { 102 {
73 unsigned char stat1,stat2,i; 103 unsigned char stat;
74 104
75 hardwrite(4,0x60); hardwrite(4,0x80); 105 setchip(0);
76 stat1 = INP(adlport); 106 if(harddetect()) {
77 hardwrite(2,0xff); hardwrite(4,0x21); 107 // is at least OPL2, check for OPL3
78 for(i=0;i<80;i++) // wait for adlib 108 currType = TYPE_OPL2;
79 INP(adlport); 109
80 stat2 = INP(adlport); 110 stat = INP(adlport);
81 hardwrite(4,0x60); hardwrite(4,0x80); 111 if(stat & 6) {
82 112 // not OPL3, try dual-OPL2
83 if(((stat1 & 0xe0) == 0) && ((stat2 & 0xe0) == 0xc0)) 113 setchip(1);
84 return true; 114 if(harddetect())
85 else 115 currType = TYPE_DUAL_OPL2;
86 return false; 116 } else
117 currType = TYPE_OPL3;
118
119 setchip(0);
120 return true;
121 } else
122 return false;
87 } 123 }
88 124
89 void CRealopl::setvolume(int volume) 125 void CRealopl::setvolume(int volume)
90 { 126 {
91 int i; 127 int i, j;
92 128
93 hardvol = volume; 129 hardvol = volume;
94 for(i=0;i<9;i++) { 130 for(j = 0; j < 2; j++)
95 hardwrite(0x43+op_table[i],((hardvols[op_table[i]+3][0] & 63) + volume) > 63 ? 63 : hardvols[op_table[i]+3][0] + volume); 131 for(i = 0; i < 9; i++) {
96 if(hardvols[i][1] & 1) // modulator too? 132 hardwrite(0x43+op_table[i],((hardvols[j][op_table[i]+3][0] & 63) + volume) > 63 ? 63 : hardvols[j][op_table[i]+3][0] + volume);
97 hardwrite(0x40+op_table[i],((hardvols[op_table[i]][0] & 63) + volume) > 63 ? 63 : hardvols[op_table[i]][0] + volume); 133 if(hardvols[j][i][1] & 1) // modulator too?
98 } 134 hardwrite(0x40+op_table[i],((hardvols[j][op_table[i]][0] & 63) + volume) > 63 ? 63 : hardvols[j][op_table[i]][0] + volume);
135 }
99 } 136 }
100 137
101 void CRealopl::setquiet(bool quiet) 138 void CRealopl::setquiet(bool quiet)
102 { 139 {
103 bequiet = quiet; 140 bequiet = quiet;
104 141
105 if(quiet) { 142 if(quiet) {
106 oldvol = hardvol; 143 oldvol = hardvol;
107 setvolume(63); 144 setvolume(63);
108 } else 145 } else
109 setvolume(oldvol); 146 setvolume(oldvol);
110 } 147 }
111 148
112 void CRealopl::hardwrite(int reg, int val) 149 void CRealopl::hardwrite(int reg, int val)
113 { 150 {
114 int i; 151 int i;
115 152 unsigned short adp = (currChip == 0 ? adlport : adlport + 2);
116 OUTP(adlport,reg); // set register 153
117 for(i=0;i<SHORTDELAY;i++) // wait for adlib 154 OUTP(adp,reg); // set register
118 INP(adlport); 155 for(i=0;i<SHORTDELAY;i++) // wait for adlib
119 OUTP(adlport+1,val); // set value 156 INP(adp);
120 for(i=0;i<LONGDELAY;i++) // wait for adlib 157 OUTP(adp+1,val); // set value
121 INP(adlport); 158 for(i=0;i<LONGDELAY;i++) // wait for adlib
159 INP(adp);
122 } 160 }
123 161
124 void CRealopl::write(int reg, int val) 162 void CRealopl::write(int reg, int val)
125 { 163 {
126 int i; 164 int i;
127 165
128 if(nowrite) 166 if(nowrite)
129 return; 167 return;
130 168
131 if(bequiet && (reg >= 0xb0 && reg <= 0xb8)) // filter all key-on commands 169 if(currType == TYPE_OPL2 && currChip > 0)
132 val &= ~32; 170 return;
133 if(reg >= 0x40 && reg <= 0x55) // cache volumes 171
134 hardvols[reg-0x40][0] = val; 172 if(bequiet && (reg >= 0xb0 && reg <= 0xb8)) // filter all key-on commands
135 if(reg >= 0xc0 && reg <= 0xc8) 173 val &= ~32;
136 hardvols[reg-0xc0][1] = val; 174 if(reg >= 0x40 && reg <= 0x55) // cache volumes
137 if(hardvol) // reduce volume 175 hardvols[currChip][reg-0x40][0] = val;
138 for(i=0;i<9;i++) { 176 if(reg >= 0xc0 && reg <= 0xc8)
139 if(reg == 0x43 + op_table[i]) 177 hardvols[currChip][reg-0xc0][1] = val;
140 val = ((val & 63) + hardvol) > 63 ? 63 : val + hardvol; 178 if(hardvol) // reduce volume
141 else 179 for(i=0;i<9;i++) {
142 if((reg == 0x40 + op_table[i]) && (hardvols[i][1] & 1)) 180 if(reg == 0x43 + op_table[i])
143 val = ((val & 63) + hardvol) > 63 ? 63 : val + hardvol; 181 val = ((val & 63) + hardvol) > 63 ? 63 : val + hardvol;
144 } 182 else
145 183 if((reg == 0x40 + op_table[i]) && (hardvols[currChip][i][1] & 1))
146 hardwrite(reg,val); 184 val = ((val & 63) + hardvol) > 63 ? 63 : val + hardvol;
185 }
186
187 hardwrite(reg,val);
147 } 188 }
148 189
149 void CRealopl::init() 190 void CRealopl::init()
150 { 191 {
151 int i; 192 int i, j;
152 193
153 for (i=0;i<9;i++) { // stop instruments 194 for(j = 0; j < 2; j++) {
154 hardwrite(0xb0 + i,0); // key off 195 setchip(j);
155 hardwrite(0x80 + op_table[i],0xff); // fastest release 196
156 } 197 for(i=0;i<9;i++) { // stop instruments
157 hardwrite(0xbd,0); // clear misc. register 198 hardwrite(0xb0 + i,0); // key off
158 } 199 hardwrite(0x80 + op_table[i],0xff); // fastest release
200 }
201
202 hardwrite(0xbd,0); // clear misc. register
203 }
204 }