DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator
src/hardware/adlib.cpp
00001 /*
00002  *  Copyright (C) 2002-2015  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017  */
00018 
00019 
00020 #include <stdlib.h>
00021 #include <string.h>
00022 #include <math.h>
00023 #include <sys/types.h>
00024 #include "adlib.h"
00025 
00026 #include "setup.h"
00027 #include "mapper.h"
00028 #include "mem.h"
00029 #include "dbopl.h"
00030 #include "nukedopl.h"
00031 
00032 bool adlib_force_timer_overflow_on_polling = false;
00033 
00034 namespace OPL2 {
00035         #include "opl.cpp"
00036 
00037         struct Handler : public Adlib::Handler {
00038                 virtual void WriteReg( Bit32u reg, Bit8u val ) {
00039                         adlib_write(reg,val);
00040                 }
00041                 virtual Bit32u WriteAddr( Bit32u port, Bit8u val ) {
00042             (void)port;//UNUSED
00043                         return val;
00044                 }
00045 
00046                 virtual void Generate( MixerChannel* chan, Bitu samples ) {
00047                         Bit16s buf[1024];
00048                         while( samples > 0 ) {
00049                                 Bitu todo = samples > 1024 ? 1024 : samples;
00050                                 samples -= todo;
00051                                 adlib_getsample(buf, (Bits)todo);
00052                                 chan->AddSamples_m16( todo, buf );
00053                         }
00054                 }
00055 
00056                 virtual void Init( Bitu rate ) {
00057                         adlib_init(rate);
00058                 }
00059 
00060                 ~Handler() {
00061                 }
00062         };
00063 }
00064 
00065 namespace OPL3 {
00066         #define OPLTYPE_IS_OPL3
00067         #include "opl.cpp"
00068 
00069         struct Handler : public Adlib::Handler {
00070                 virtual void WriteReg( Bit32u reg, Bit8u val ) {
00071                         adlib_write(reg,val);
00072                 }
00073                 virtual Bit32u WriteAddr( Bit32u port, Bit8u val ) {
00074                         adlib_write_index(port, val);
00075                         return opl_index;
00076                 }
00077                 virtual void Generate( MixerChannel* chan, Bitu samples ) {
00078                         Bit16s buf[1024*2];
00079                         while( samples > 0 ) {
00080                                 Bitu todo = samples > 1024 ? 1024 : samples;
00081                                 samples -= todo;
00082                                 adlib_getsample(buf, (Bits)todo);
00083                                 chan->AddSamples_s16( todo, buf );
00084                         }
00085                 }
00086 
00087                 virtual void Init( Bitu rate ) {
00088                         adlib_init(rate);
00089                 }
00090 
00091                 ~Handler() {
00092                 }
00093         };
00094 }
00095 
00096 namespace NukedOPL {
00097         struct Handler : public Adlib::Handler {
00098                 opl3_chip chip;
00099                 virtual void WriteReg( Bit32u reg, Bit8u val ) {
00100                         OPL3_WriteReg(&chip, reg, val);
00101                 }
00102                 virtual Bit32u WriteAddr( Bit32u port, Bit8u val ) {
00103                         Bit16u addr;
00104                         addr = val;
00105                         if ((port & 2) && (addr == 0x05 || chip.newm)) {
00106                                 addr |= 0x100;
00107                         }
00108                         return addr;
00109                 }
00110                 virtual void Generate( MixerChannel* chan, Bitu samples ) {
00111                         Bit16s buf[1024*2];
00112                         while( samples > 0 ) {
00113                                 Bitu todo = samples > 1024 ? 1024 : samples;
00114                                 samples -= todo;
00115                                 OPL3_GenerateStream(&chip, buf, todo);
00116                                 chan->AddSamples_s16( todo, buf );
00117                         }
00118                 }
00119                 virtual void Init( Bitu rate ) {
00120                         OPL3_Reset(&chip, rate);
00121                 }
00122                 ~Handler() {
00123                 }
00124         };
00125 }
00126 
00127 
00128 #define RAW_SIZE 1024
00129 
00130 
00131 /*
00132         Main Adlib implementation
00133 
00134 */
00135 
00136 namespace Adlib {
00137 
00138 
00139 /* Raw DRO capture stuff */
00140 
00141 #ifdef _MSC_VER
00142 #pragma pack (1)
00143 #endif
00144 
00145 #define HW_OPL2 0
00146 #define HW_DUALOPL2 1
00147 #define HW_OPL3 2
00148 
00149 struct RawHeader {
00150         Bit8u id[8];                            /* 0x00, "DBRAWOPL" */
00151         Bit16u versionHigh;                     /* 0x08, size of the data following the m */
00152         Bit16u versionLow;                      /* 0x0a, size of the data following the m */
00153         Bit32u commands;                        /* 0x0c, Bit32u amount of command/data pairs */
00154         Bit32u milliseconds;            /* 0x10, Bit32u Total milliseconds of data in this chunk */
00155         Bit8u hardware;                         /* 0x14, Bit8u Hardware Type 0=opl2,1=dual-opl2,2=opl3 */
00156         Bit8u format;                           /* 0x15, Bit8u Format 0=cmd/data interleaved, 1 maybe all cdms, followed by all data */
00157         Bit8u compression;                      /* 0x16, Bit8u Compression Type, 0 = No Compression */
00158         Bit8u delay256;                         /* 0x17, Bit8u Delay 1-256 msec command */
00159         Bit8u delayShift8;                      /* 0x18, Bit8u (delay + 1)*256 */                       
00160         Bit8u conversionTableSize;      /* 0x191, Bit8u Raw Conversion Table size */
00161 } GCC_ATTRIBUTE(packed);
00162 #ifdef _MSC_VER
00163 #pragma pack()
00164 #endif
00165 /*
00166         The Raw Tables is < 128 and is used to convert raw commands into a full register index 
00167         When the high bit of a raw command is set it indicates the cmd/data pair is to be sent to the 2nd port
00168         After the conversion table the raw data follows immediatly till the end of the chunk
00169 */
00170 
00171 //Table to map the opl register to one <127 for dro saving
00172 class Capture {
00173         //127 entries to go from raw data to registers
00174         Bit8u ToReg[127];
00175         //How many entries in the ToPort are used
00176         Bit8u RawUsed;
00177         //256 entries to go from port index to raw data
00178         Bit8u ToRaw[256];
00179         Bit8u delay256;
00180         Bit8u delayShift8;
00181         RawHeader header;
00182 
00183         FILE*   handle;                         //File used for writing
00184         Bit32u  startTicks;                     //Start used to check total raw length on end
00185         Bit32u  lastTicks;                      //Last ticks when last last cmd was added
00186         Bit8u   buf[1024];      //16 added for delay commands and what not
00187         Bit32u  bufUsed;
00188 #if 0//unused
00189     Bit8u       cmd[2];                         //Last cmd's sent to either ports
00190         bool    doneOpl3;
00191         bool    doneDualOpl2;
00192 #endif
00193         RegisterCache* cache;
00194 
00195         void MakeEntry( Bit8u reg, Bit8u& raw ) {
00196                 ToReg[ raw ] = reg;
00197                 ToRaw[ reg ] = raw;
00198                 raw++;
00199         }
00200         void MakeTables( void ) {
00201                 Bit8u index = 0;
00202                 memset( ToReg, 0xff, sizeof ( ToReg ) );
00203                 memset( ToRaw, 0xff, sizeof ( ToRaw ) );
00204                 //Select the entries that are valid and the index is the mapping to the index entry
00205                 MakeEntry( 0x01, index );                                       //0x01: Waveform select
00206                 MakeEntry( 0x04, index );                                       //104: Four-Operator Enable
00207                 MakeEntry( 0x05, index );                                       //105: OPL3 Mode Enable
00208                 MakeEntry( 0x08, index );                                       //08: CSW / NOTE-SEL
00209                 MakeEntry( 0xbd, index );                                       //BD: Tremolo Depth / Vibrato Depth / Percussion Mode / BD/SD/TT/CY/HH On
00210                 //Add the 32 byte range that hold the 18 operators
00211                 for ( int i = 0 ; i < 24; i++ ) {
00212                         if ( (i & 7) < 6 ) {
00213                                 MakeEntry(0x20 + i, index );            //20-35: Tremolo / Vibrato / Sustain / KSR / Frequency Multiplication Facto
00214                                 MakeEntry(0x40 + i, index );            //40-55: Key Scale Level / Output Level 
00215                                 MakeEntry(0x60 + i, index );            //60-75: Attack Rate / Decay Rate 
00216                                 MakeEntry(0x80 + i, index );            //80-95: Sustain Level / Release Rate
00217                                 MakeEntry(0xe0 + i, index );            //E0-F5: Waveform Select
00218                         }
00219                 }
00220                 //Add the 9 byte range that hold the 9 channels
00221                 for ( int i = 0 ; i < 9; i++ ) {
00222                         MakeEntry(0xa0 + i, index );                    //A0-A8: Frequency Number
00223                         MakeEntry(0xb0 + i, index );                    //B0-B8: Key On / Block Number / F-Number(hi bits) 
00224                         MakeEntry(0xc0 + i, index );                    //C0-C8: FeedBack Modulation Factor / Synthesis Type
00225                 }
00226                 //Store the amount of bytes the table contains
00227                 RawUsed = index;
00228 //              assert( RawUsed <= 127 );
00229                 delay256 = RawUsed;
00230                 delayShift8 = RawUsed+1; 
00231         }
00232 
00233         void ClearBuf( void ) {
00234                 fwrite( buf, 1, bufUsed, handle );
00235                 header.commands += bufUsed / 2;
00236                 bufUsed = 0;
00237         }
00238         void AddBuf( Bit8u raw, Bit8u val ) {
00239                 buf[bufUsed++] = raw;
00240                 buf[bufUsed++] = val;
00241                 if ( bufUsed >= sizeof( buf ) ) {
00242                         ClearBuf();
00243                 }
00244         }
00245         void AddWrite( Bit32u regFull, Bit8u val ) {
00246                 Bit8u regMask = regFull & 0xff;
00247                 /*
00248                         Do some special checks if we're doing opl3 or dualopl2 commands
00249                         Although you could pretty much just stick to always doing opl3 on the player side
00250                 */
00251                 //Enabling opl3 4op modes will make us go into opl3 mode
00252                 if ( header.hardware != HW_OPL3 && regFull == 0x104 && val && (*cache)[0x105] ) {
00253                         header.hardware = HW_OPL3;
00254                 } 
00255                 //Writing a keyon to a 2nd address enables dual opl2 otherwise
00256                 //Maybe also check for rhythm
00257                 if ( header.hardware == HW_OPL2 && regFull >= 0x1b0 && regFull <=0x1b8 && val ) {
00258                         header.hardware = HW_DUALOPL2;
00259                 }
00260                 Bit8u raw = ToRaw[ regMask ];
00261                 if ( raw == 0xff )
00262                         return;
00263                 if ( regFull & 0x100 )
00264                         raw |= 128;
00265                 AddBuf( raw, val );
00266         }
00267         void WriteCache( void  ) {
00268                 Bitu i, val;
00269                 /* Check the registers to add */
00270                 for (i=0;i<256;i++) {
00271                         //Skip the note on entries
00272                         if (i>=0xb0 && i<=0xb8) 
00273                                 continue;
00274                         val = (*cache)[ i ];
00275                         if (val) {
00276                                 AddWrite( i, val );
00277                         }
00278                         val = (*cache)[ 0x100 + i ];
00279                         if (val) {
00280                                 AddWrite( 0x100 + i, val );
00281                         }
00282                 }
00283         }
00284         void InitHeader( void ) {
00285                 memset( &header, 0, sizeof( header ) );
00286                 memcpy( header.id, "DBRAWOPL", 8 );
00287                 header.versionLow = 0;
00288                 header.versionHigh = 2;
00289                 header.delay256 = delay256;
00290                 header.delayShift8 = delayShift8;
00291                 header.conversionTableSize = RawUsed;
00292         }
00293         void CloseFile( void ) {
00294                 if ( handle ) {
00295                         ClearBuf();
00296                         /* Endianize the header and write it to beginning of the file */
00297                         var_write( &header.versionHigh, header.versionHigh );
00298                         var_write( &header.versionLow, header.versionLow );
00299                         var_write( &header.commands, header.commands );
00300                         var_write( &header.milliseconds, header.milliseconds );
00301                         fseek( handle, 0, SEEK_SET );
00302                         fwrite( &header, 1, sizeof( header ), handle );
00303                         fclose( handle );
00304                         handle = 0;
00305                 }
00306         }
00307 public:
00308         bool DoWrite( Bit32u regFull, Bit8u val ) {
00309                 Bit8u regMask = regFull & 0xff;
00310                 //Check the raw index for this register if we actually have to save it
00311                 if ( handle ) {
00312                         /*
00313                                 Check if we actually care for this to be logged, else just ignore it
00314                         */
00315                         Bit8u raw = ToRaw[ regMask ];
00316                         if ( raw == 0xff ) {
00317                                 return true;
00318                         }
00319                         /* Check if this command will not just replace the same value 
00320                            in a reg that doesn't do anything with it
00321                         */
00322                         if ( (*cache)[ regFull ] == val )
00323                                 return true;
00324                         /* Check how much time has passed */
00325                         Bitu passed = PIC_Ticks - lastTicks;
00326                         lastTicks = PIC_Ticks;
00327                         header.milliseconds += passed;
00328 
00329                         //if ( passed > 0 ) LOG_MSG( "Delay %d", passed ) ;
00330                         
00331                         // If we passed more than 30 seconds since the last command, we'll restart the the capture
00332                         if ( passed > 30000 ) {
00333                                 CloseFile();
00334                                 goto skipWrite; 
00335                         }
00336                         while (passed > 0) {
00337                                 if (passed < 257) {                     //1-256 millisecond delay
00338                                         AddBuf( delay256, passed - 1 );
00339                                         passed = 0;
00340                                 } else {
00341                                         Bitu shift = (passed >> 8);
00342                                         passed -= shift << 8;
00343                                         AddBuf( delayShift8, shift - 1 );
00344                                 }
00345                         }
00346                         AddWrite( regFull, val );
00347                         return true;
00348                 }
00349 skipWrite:
00350                 //Not yet capturing to a file here
00351                 //Check for commands that would start capturing, if it's not one of them return
00352                 if ( !(
00353                         //note on in any channel 
00354                         ( regMask>=0xb0 && regMask<=0xb8 && (val&0x020) ) ||
00355                         //Percussion mode enabled and a note on in any percussion instrument
00356                         ( regMask == 0xbd && ( (val&0x3f) > 0x20 ) )
00357                 )) {
00358                         return true;
00359                 }
00360                 handle = OpenCaptureFile("Raw Opl",".dro");
00361                 if (!handle)
00362                         return false;
00363                 InitHeader();
00364                 //Prepare space at start of the file for the header
00365                 fwrite( &header, 1, sizeof(header), handle );
00366                 /* write the Raw To Reg table */
00367                 fwrite( &ToReg, 1, RawUsed, handle );
00368                 /* Write the cache of last commands */
00369                 WriteCache( );
00370                 /* Write the command that triggered this */
00371                 AddWrite( regFull, val );
00372                 //Init the timing information for the next commands
00373                 lastTicks = PIC_Ticks;  
00374                 startTicks = PIC_Ticks;
00375                 return true;
00376         }
00377         Capture( RegisterCache* _cache ) {
00378                 cache = _cache;
00379                 handle = 0;
00380                 bufUsed = 0;
00381                 MakeTables();
00382         }
00383         ~Capture() {
00384                 CloseFile();
00385         }
00386 
00387 };
00388 
00389 /*
00390 Chip
00391 */
00392 
00393 bool Chip::Write( Bit32u reg, Bit8u val ) {
00394         if (adlib_force_timer_overflow_on_polling) {
00395                 /* detect end of polling loop by whether it writes */
00396                 last_poll = PIC_FullIndex();
00397                 poll_counter = 0;
00398         }
00399 
00400         switch ( reg ) {
00401         case 0x02:
00402                 timer[0].counter = val;
00403                 return true;
00404         case 0x03:
00405                 timer[1].counter = val;
00406                 return true;
00407         case 0x04:
00408                 double time;
00409                 time = PIC_FullIndex();
00410                 if ( val & 0x80 ) {
00411                         timer[0].Reset( time );
00412                         timer[1].Reset( time );
00413                 } else {
00414                         timer[0].Update( time );
00415                         timer[1].Update( time );
00416                         if ( val & 0x1 ) {
00417                                 timer[0].Start( time, 80 );
00418                         } else {
00419                                 timer[0].Stop( );
00420                         }
00421                         timer[0].masked = (val & 0x40) > 0;
00422                         if ( timer[0].masked )
00423                                 timer[0].overflow = false;
00424                         if ( val & 0x2 ) {
00425                                 timer[1].Start( time, 320 );
00426                         } else {
00427                                 timer[1].Stop( );
00428                         }
00429                         timer[1].masked = (val & 0x20) > 0;
00430                         if ( timer[1].masked )
00431                                 timer[1].overflow = false;
00432 
00433                 }
00434                 return true;
00435         }
00436         return false;
00437 }
00438 
00439 
00440 Bit8u Chip::Read( ) {
00441         double time( PIC_FullIndex() );
00442         timer[0].Update( time );
00443         timer[1].Update( time );
00444 
00445         if (adlib_force_timer_overflow_on_polling) {
00446                 static const double poll_timeout = 0.1; /* if polling more than 100us per second, do timeout */
00447 
00448                 if ((time-last_poll) > poll_timeout) {
00449                         poll_counter = 0;
00450                 }
00451                 else if (++poll_counter >= 50) {
00452 //                      LOG_MSG("Adlib polling hack triggered. Forcing timers to reset. Hope this helps your DOS game to detect Adlib.");
00453 
00454                         poll_counter = 0;
00455                         if (!timer[0].overflow && timer[0].enabled) {
00456                                 timer[0].Stop();
00457                                 timer[0].overflow = true;
00458                         }
00459                         if (!timer[1].overflow && timer[1].enabled) {
00460                                 timer[1].Stop();
00461                                 timer[1].overflow = true;
00462                         }
00463                 }
00464 
00465                 last_poll = time;
00466         }
00467 
00468         Bit8u ret = 0;
00469         //Overflow won't be set if a channel is masked
00470         if ( timer[0].overflow ) {
00471                 ret |= 0x40;
00472                 ret |= 0x80;
00473         }
00474         if ( timer[1].overflow ) {
00475                 ret |= 0x20;
00476                 ret |= 0x80;
00477         }
00478         return ret;
00479 
00480 }
00481 
00482 void Module::CacheWrite( Bit32u reg, Bit8u val ) {
00483         //capturing?
00484         if ( capture ) {
00485                 capture->DoWrite( reg, val );
00486         }
00487         //Store it into the cache
00488         cache[ reg ] = val;
00489 }
00490 
00491 void Module::DualWrite( Bit8u index, Bit8u reg, Bit8u val ) {
00492         //Make sure you don't use opl3 features
00493         //Don't allow write to disable opl3             
00494         if ( reg == 5 ) {
00495                 return;
00496         }
00497         //Only allow 4 waveforms
00498         if ( reg >= 0xE0 ) {
00499                 val &= 3;
00500         } 
00501         //Write to the timer?
00502         if ( chip[index].Write( reg, val ) ) 
00503                 return;
00504         //Enabling panning
00505         if ( reg >= 0xc0 && reg <=0xc8 ) {
00506                 val &= 0x0f;
00507                 val |= index ? 0xA0 : 0x50;
00508         }
00509         Bit32u fullReg = reg + (index ? 0x100u : 0u);
00510         handler->WriteReg( fullReg, val );
00511         CacheWrite( fullReg, val );
00512 }
00513 
00514 
00515 void Module::PortWrite( Bitu port, Bitu val, Bitu iolen ) {
00516     (void)iolen;//UNUSED
00517         //Keep track of last write time
00518         lastUsed = PIC_Ticks;
00519         //Maybe only enable with a keyon?
00520         if ( !mixerChan->enabled ) {
00521                 mixerChan->Enable(true);
00522         }
00523         if ( port&1 ) {
00524                 switch ( mode ) {
00525                 case MODE_OPL2:
00526                 case MODE_OPL3:
00527                         if ( !chip[0].Write( reg.normal, val ) ) {
00528                                 handler->WriteReg( reg.normal, val );
00529                                 CacheWrite( reg.normal, val );
00530                         }
00531                         break;
00532                 case MODE_DUALOPL2:
00533                         //Not a 0x??8 port, then write to a specific port
00534                         if ( !(port & 0x8) ) {
00535                                 Bit8u index = ( port & 2 ) >> 1;
00536                                 DualWrite( index, reg.dual[index], val );
00537                         } else {
00538                                 //Write to both ports
00539                                 DualWrite( 0, reg.dual[0], val );
00540                                 DualWrite( 1, reg.dual[1], val );
00541                         }
00542                         break;
00543                 }
00544         } else {
00545                 //Ask the handler to write the address
00546                 //Make sure to clip them in the right range
00547                 switch ( mode ) {
00548                 case MODE_OPL2:
00549                         reg.normal = handler->WriteAddr( port, val ) & 0xff;
00550                         break;
00551                 case MODE_OPL3:
00552                         reg.normal = handler->WriteAddr( port, val ) & 0x1ff;
00553                         break;
00554                 case MODE_DUALOPL2:
00555                         //Not a 0x?88 port, when write to a specific side
00556                         if ( !(port & 0x8) ) {
00557                                 Bit8u index = ( port & 2 ) >> 1;
00558                                 reg.dual[index] = val & 0xff;
00559                         } else {
00560                                 reg.dual[0] = val & 0xff;
00561                                 reg.dual[1] = val & 0xff;
00562                         }
00563                         break;
00564                 }
00565         }
00566 }
00567 
00568 
00569 Bitu Module::PortRead( Bitu port, Bitu iolen ) {
00570     (void)iolen;//UNUSED
00571         switch ( mode ) {
00572         case MODE_OPL2:
00573                 //We allocated 4 ports, so just return -1 for the higher ones
00574                 if ( !(port & 3 ) ) {
00575                         //Make sure the low bits are 6 on opl2
00576                         return chip[0].Read() | 0x6;
00577                 } else {
00578                         return 0xff;
00579                 }
00580         case MODE_OPL3:
00581                 //We allocated 4 ports, so just return -1 for the higher ones
00582                 if ( !(port & 3 ) ) {
00583                         return chip[0].Read();
00584                 } else {
00585                         return 0xff;
00586                 }
00587         case MODE_DUALOPL2:
00588                 //Only return for the lower ports
00589                 if ( port & 1 ) {
00590                         return 0xff;
00591                 }
00592                 //Make sure the low bits are 6 on opl2
00593                 return chip[ (port >> 1) & 1].Read() | 0x6;
00594         }
00595         return 0;
00596 }
00597 
00598 
00599 void Module::Init( Mode m ) {
00600         mode = m;
00601         switch ( mode ) {
00602         case MODE_OPL3:
00603         case MODE_OPL2:
00604                 break;
00605         case MODE_DUALOPL2:
00606                 //Setup opl3 mode in the hander
00607                 handler->WriteReg( 0x105, 1 );
00608                 //Also set it up in the cache so the capturing will start opl3
00609                 CacheWrite( 0x105, 1 );
00610                 break;
00611         }
00612 }
00613 
00614 } //namespace
00615 
00616 
00617 
00618 static Adlib::Module* module = 0;
00619 
00620 static void OPL_CallBack(Bitu len) {
00621         module->handler->Generate( module->mixerChan, len );
00622         //Disable the sound generation after 30 seconds of silence
00623         if ((PIC_Ticks - module->lastUsed) > 30000) {
00624                 Bitu i;
00625                 for (i=0xb0;i<0xb9;i++) if (module->cache[i]&0x20||module->cache[i+0x100]&0x20) break;
00626                 if (i==0xb9) module->mixerChan->Enable(false);
00627                 else module->lastUsed = PIC_Ticks;
00628         }
00629 }
00630 
00631 static Bitu OPL_Read(Bitu port,Bitu iolen) {
00632         return module->PortRead( port, iolen );
00633 }
00634 
00635 void OPL_Write(Bitu port,Bitu val,Bitu iolen) {
00636         // if writing the data port, assume a change in OPL state that should be reflected immediately.
00637         // this is a way to render "sample accurate" without needing "sample accurate" mode in the mixer.
00638         // CHGOLF's Adlib digital audio hack works fine with this hack.
00639         if (port&1) module->mixerChan->FillUp();
00640 
00641         module->PortWrite( port, val, iolen );
00642 }
00643 
00644 /*
00645         Save the current state of the operators as instruments in an reality adlib tracker file
00646 */
00647 void SaveRad() {
00648         unsigned char b[16 * 1024];
00649         unsigned int w = 0;
00650 
00651         FILE* handle = OpenCaptureFile("RAD Capture",".rad");
00652         if ( !handle )
00653                 return;
00654         //Header
00655         fwrite( "RAD by REALiTY!!", 1, 16, handle );
00656         b[w++] = 0x10;          //version
00657         b[w++] = 0x06;          //default speed and no description
00658         //Write 18 instuments for all operators in the cache
00659         for ( unsigned int i = 0; i < 18; i++ ) {
00660                 Bit8u* set = module->cache + ( i / 9 ) * 256;
00661                 Bitu offset = ((i % 9) / 3) * 8 + (i % 3);
00662                 Bit8u* base = set + offset;
00663                 b[w++] = 1 + i;         //instrument number
00664                 b[w++] = base[0x23];
00665                 b[w++] = base[0x20];
00666                 b[w++] = base[0x43];
00667                 b[w++] = base[0x40];
00668                 b[w++] = base[0x63];
00669                 b[w++] = base[0x60];
00670                 b[w++] = base[0x83];
00671                 b[w++] = base[0x80];
00672                 b[w++] = set[0xc0 + (i % 9)];
00673                 b[w++] = base[0xe3];
00674                 b[w++] = base[0xe0];
00675         }
00676         b[w++] = 0;             //instrument 0, no more instruments following
00677         b[w++] = 1;             //1 pattern following
00678         //Zero out the remaing part of the file a bit to make rad happy
00679         for ( unsigned int i = 0; i < 64; i++ ) {
00680                 b[w++] = 0;
00681         }
00682         fwrite( b, 1, w, handle );
00683         fclose( handle );
00684 }
00685 
00686 
00687 void OPL_SaveRawEvent(bool pressed) {
00688         if (!pressed)
00689                 return;
00690     if (module == NULL)
00691         return;
00692 
00693 //      SaveRad();return;
00694         /* Check for previously opened wave file */
00695         if ( module->capture ) {
00696                 delete module->capture;
00697                 module->capture = 0;
00698                 LOG_MSG("Stopped Raw OPL capturing.");
00699         } else {
00700                 LOG_MSG("Preparing to capture Raw OPL, will start with first note played.");
00701                 module->capture = new Adlib::Capture( &module->cache );
00702         }
00703 
00704         mainMenu.get_item("mapper_caprawopl").check(module->capture != NULL).refresh_item(mainMenu);
00705 }
00706 
00707 namespace Adlib {
00708 
00709 Module::Module( Section* configuration ) : Module_base(configuration) {
00710         DOSBoxMenu::item *item;
00711 
00712         reg.dual[0] = 0;
00713         reg.dual[1] = 0;
00714         reg.normal = 0;
00715         handler = 0;
00716         capture = 0;
00717 
00718         Section_prop * section=static_cast<Section_prop *>(configuration);
00719         Bitu base = (Bitu)section->Get_hex("sbbase");
00720         Bitu rate = (Bitu)section->Get_int("oplrate");
00721         //Make sure we can't select lower than 8000 to prevent fixed point issues
00722         if ( rate < 8000 )
00723                 rate = 8000;
00724         std::string oplemu( section->Get_string( "oplemu" ) );
00725 
00726         adlib_force_timer_overflow_on_polling = section->Get_bool("adlib force timer overflow on detect");
00727 
00728         mixerChan = mixerObject.Install(OPL_CallBack,rate,"FM");
00729         mixerChan->SetScale( 2.0 );
00730         if (oplemu == "fast") {
00731                 handler = new DBOPL::Handler();
00732         }
00733         else if (oplemu == "compat") {
00734                 if (oplmode == OPL_opl2) {
00735                         handler = new OPL2::Handler();
00736                 }
00737                 else {
00738                         handler = new OPL3::Handler();
00739                 }
00740         } else if (oplemu == "nuked") {
00741                 handler = new NukedOPL::Handler();
00742         } else {
00743                 handler = new DBOPL::Handler();
00744         }
00745         handler->Init( rate );
00746         bool single = false;
00747         switch ( oplmode ) {
00748         case OPL_opl2:
00749                 single = true;
00750                 Init( Adlib::MODE_OPL2 );
00751                 break;
00752         case OPL_dualopl2:
00753                 Init( Adlib::MODE_DUALOPL2 );
00754                 break;
00755         case OPL_opl3:
00756                 Init( Adlib::MODE_OPL3 );
00757                 break;
00758         default:
00759                 break;
00760         }
00761         //0x388 range
00762         WriteHandler[0].Install(0x388,OPL_Write,IO_MB, 4 );
00763         ReadHandler[0].Install(0x388,OPL_Read,IO_MB, 4 );
00764         //0x220 range
00765         if ( !single ) {
00766                 WriteHandler[1].Install(base,OPL_Write,IO_MB, 4 );
00767                 ReadHandler[1].Install(base,OPL_Read,IO_MB, 4 );
00768         }
00769         //0x228 range
00770         WriteHandler[2].Install(base+8,OPL_Write,IO_MB, 2);
00771         ReadHandler[2].Install(base+8,OPL_Read,IO_MB, 1);
00772 
00773         MAPPER_AddHandler(OPL_SaveRawEvent,MK_nothing,0,"caprawopl","Cap OPL",&item);
00774         item->set_text("Record FM (OPL) output");
00775 }
00776 
00777 Module::~Module() {
00778         if ( capture ) {
00779                 delete capture;
00780         }
00781         if ( handler ) {
00782                 delete handler;
00783         }
00784 }
00785 
00786 //Initialize static members
00787 OPL_Mode Module::oplmode=OPL_none;
00788 
00789 }       //Adlib Namespace
00790 
00791 
00792 void OPL_Init(Section* sec,OPL_Mode oplmode) {
00793         Adlib::Module::oplmode = oplmode;
00794         module = new Adlib::Module( sec );
00795 }
00796 
00797 void OPL_ShutDown(Section* sec){
00798     (void)sec;//UNUSED
00799         delete module;
00800         module = 0;
00801 }
00802