DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator
src/hardware/disney.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 <string.h>
00021 #include <algorithm>
00022 #include "dosbox.h"
00023 #include "inout.h"
00024 #include "mixer.h"
00025 #include "pic.h"
00026 #include "setup.h"
00027 #include "bios.h"
00028 #include "mem.h"
00029 #include "control.h"
00030 
00031 using namespace std;
00032 
00033 unsigned int DISNEY_BASE = 0x0378;
00034 
00035 #define DISNEY_SIZE 128
00036 
00037 typedef struct _dac_channel {
00038         Bit8u buffer[DISNEY_SIZE];      // data buffer
00039         Bitu used;                                      // current data buffer level
00040         double speedcheck_sum;
00041         double speedcheck_last;
00042         bool speedcheck_failed;
00043         bool speedcheck_init;
00044 } dac_channel;
00045 
00046 static struct {
00047         // parallel port stuff
00048         Bit8u data;
00049         Bit8u status;
00050         Bit8u control;
00051         // the D/A channels
00052         dac_channel da[2];
00053 
00054         Bitu last_used;
00055         MixerObject * mo;
00056         MixerChannel * chan;
00057         bool stereo;
00058         // which channel do we use for mono output?
00059         // and the channel used for stereo
00060         dac_channel* leader;
00061         
00062         Bitu state;
00063         Bitu interface_det;
00064         Bitu interface_det_ext;
00065 } disney;
00066 
00067 #define DS_IDLE 0
00068 #define DS_RUNNING 1
00069 #define DS_FINISH 2
00070 #define DS_ANALYZING 3
00071 
00072 static void DISNEY_CallBack(Bitu len);
00073 
00074 static void DISNEY_disable(Bitu) {
00075         if(disney.mo) {
00076                 disney.chan->AddSilence();
00077                 disney.chan->Enable(false);
00078         }
00079         disney.leader = 0;
00080         disney.last_used = 0;
00081         disney.state = DS_IDLE;
00082         disney.interface_det = 0;
00083         disney.interface_det_ext = 0;
00084         disney.stereo = false;
00085 }
00086 
00087 static void DISNEY_enable(Bitu freq) {
00088         if(freq < 500 || freq > 100000) {
00089                 // try again..
00090                 disney.state = DS_IDLE;
00091 #if 0
00092                 LOG(LOG_MISC,LOG_NORMAL)("disney enable rejected, %ld Hz, stereo=%u",(long)freq,(int)disney.stereo);
00093 #endif
00094                 return; 
00095         } else {
00096 #if 0
00097                 if(disney.stereo) LOG(LOG_MISC,LOG_NORMAL)("disney enable %d Hz, stereo",freq);
00098                 else LOG(LOG_MISC,LOG_NORMAL)("disney enable %d Hz, mono",freq);
00099 #endif
00100                 disney.chan->SetFreq(freq);
00101                 disney.chan->Enable(true);
00102                 disney.state = DS_RUNNING;
00103         }
00104 }
00105 
00106 static void DISNEY_analyze(Bitu channel){
00107         switch(disney.state) {
00108                 case DS_RUNNING: // should not get here
00109                         break;
00110                 case DS_IDLE:
00111                         // initialize channel data
00112                         for(int i = 0; i < 2; i++) {
00113                                 disney.da[i].used = 0;
00114                                 disney.da[i].speedcheck_sum = 0;
00115                                 disney.da[i].speedcheck_failed = false;
00116                                 disney.da[i].speedcheck_init = false;
00117                         }
00118                         disney.da[channel].speedcheck_last = PIC_FullIndex();
00119                         disney.da[channel].speedcheck_init = true;
00120                         
00121                         disney.state = DS_ANALYZING;
00122                         break;
00123 
00124                 case DS_FINISH: 
00125                 {
00126                         // detect stereo: if we have about the same data amount in both channels
00127                         Bits st_diff = (Bits)disney.da[0].used - (Bits)disney.da[1].used;
00128                         
00129                         // find leader channel (the one with higher rate) [this good for the stereo case?]
00130                         if(disney.da[0].used > disney.da[1].used) {
00131                                 //disney.monochannel=0;
00132                                 disney.leader = &disney.da[0];
00133                         } else {
00134                                 //disney.monochannel=1;
00135                                 disney.leader = &disney.da[1];
00136                         }
00137                         
00138                         if((st_diff < 5) && (st_diff > -5)) disney.stereo = true;
00139                         else disney.stereo = false;
00140 
00141                         // calculate rate for both channels
00142                         Bitu ch_speed[2];
00143 
00144                         for(Bitu i = 0; i < 2; i++) {
00145                                 if (disney.da[i].used > 1/*avoid divide-by-zero!*/) {
00146                                         ch_speed[i] = (Bitu)(1.0/((disney.da[i].speedcheck_sum/1000.0) /
00147                                                 (float)(((float)disney.da[i].used)-1.0))); // -1.75
00148                                 }
00149                                 else {
00150                                         ch_speed[i] = 0;
00151                                 }
00152                         }
00153 
00154                         // choose the larger value
00155                         DISNEY_enable(max(ch_speed[0],ch_speed[1]));
00156                         break;
00157                 }
00158                 case DS_ANALYZING:
00159                 {
00160                         double current = PIC_FullIndex();
00161                         dac_channel* cch = &disney.da[channel];
00162 
00163                         if(!cch->speedcheck_init) {
00164                                 cch->speedcheck_init = true;
00165                                 cch->speedcheck_last = current;
00166                                 break;
00167                         }
00168                         cch->speedcheck_sum += current - cch->speedcheck_last;
00169                         //LOG_MSG("t=%f",current - cch->speedcheck_last);
00170                         
00171                         // sanity checks (printer...)
00172                         if((current - cch-> speedcheck_last) < 0.01 ||
00173                                 (current - cch-> speedcheck_last) > 2)
00174                                 cch->speedcheck_failed = true;
00175                         
00176                         // if both are failed we are back at start
00177                         if(disney.da[0].speedcheck_failed && disney.da[1].speedcheck_failed) {
00178                                 disney.state=DS_IDLE;
00179                                 break;
00180                         }
00181 
00182                         cch->speedcheck_last = current;
00183                         
00184                         // analyze finish condition
00185                         if(disney.da[0].used > 30 || disney.da[1].used > 30)
00186                                 disney.state = DS_FINISH;
00187                         break;
00188                 }
00189         }
00190 }
00191 
00192 static void disney_write(Bitu port,Bitu val,Bitu iolen) {
00193     (void)iolen;//UNUSED
00194         //LOG_MSG("write disney time %f addr%x val %x",PIC_FullIndex(),port,val);
00195         disney.last_used=PIC_Ticks;
00196         switch (port-DISNEY_BASE) {
00197         case 0:         /* Data Port */
00198         {
00199                 disney.data=val;
00200                 // if data is written here too often without using the stereo
00201                 // mechanism we use the simple DAC machanism. 
00202         if(disney.state != DS_RUNNING) {
00203                         disney.interface_det++;
00204                         if(disney.interface_det > 5)
00205                                 DISNEY_analyze(0);
00206                 }
00207                 if(disney.interface_det > 5) {
00208                         if(disney.da[0].used < DISNEY_SIZE) {
00209                                 disney.da[0].buffer[disney.da[0].used] = disney.data;
00210                                 disney.da[0].used++;
00211                         } //else LOG_MSG("disney overflow 0");
00212                 }
00213                 break;
00214         }
00215         case 1:         /* Status Port */               
00216                 LOG(LOG_MISC,LOG_NORMAL)("DISNEY:Status write %x",(int)val);
00217                 break;
00218         case 2:         /* Control Port */
00219                 if((disney.control & 0x2) && !(val & 0x2)) {
00220                         if(disney.state != DS_RUNNING) {
00221                                 disney.interface_det = 0;
00222                                 disney.interface_det_ext = 0;
00223                                 DISNEY_analyze(1);
00224                         }
00225 
00226                         // stereo channel latch
00227                         if(disney.da[1].used < DISNEY_SIZE) {
00228                                 disney.da[1].buffer[disney.da[1].used] = disney.data;
00229                                 disney.da[1].used++;
00230                         } //else LOG_MSG("disney overflow 1");
00231                 }
00232 
00233                 if((disney.control & 0x1) && !(val & 0x1)) {
00234                         if(disney.state != DS_RUNNING) {
00235                                 disney.interface_det = 0;
00236                                 disney.interface_det_ext = 0;
00237                                 DISNEY_analyze(0);
00238                         }
00239                         // stereo channel latch
00240                         if(disney.da[0].used < DISNEY_SIZE) {
00241                                 disney.da[0].buffer[disney.da[0].used] = disney.data;
00242                                 disney.da[0].used++;
00243                         } //else LOG_MSG("disney overflow 0");
00244                 }
00245 
00246                 if((disney.control & 0x8) && !(val & 0x8)) {
00247                         // emulate a device with 16-byte sound FIFO
00248                         if(disney.state != DS_RUNNING) {
00249                                 disney.interface_det_ext++;
00250                                 disney.interface_det = 0;
00251                                 if(disney.interface_det_ext > 5) {
00252                                         disney.leader = &disney.da[0];
00253                                         DISNEY_enable(7000);
00254                                 }
00255                         }
00256                         if(disney.interface_det_ext > 5) {
00257                                 if(disney.da[0].used < DISNEY_SIZE) {
00258                                         disney.da[0].buffer[disney.da[0].used] = disney.data;
00259                                         disney.da[0].used++;
00260                                 }
00261                         }
00262                 }
00263 
00264 //              LOG_WARN("DISNEY:Control write %x",val);
00265                 if (val&0x10) LOG(LOG_MISC,LOG_ERROR)("DISNEY:Parallel IRQ Enabled");
00266                 disney.control=val;
00267                 break;
00268         }
00269 }
00270 
00271 static Bitu disney_read(Bitu port,Bitu iolen) {
00272     (void)iolen;//UNUSED
00273         Bitu retval;
00274         switch (port-DISNEY_BASE) {
00275         case 0:         /* Data Port */
00276 //              LOG(LOG_MISC,LOG_NORMAL)("DISNEY:Read from data port");
00277                 return disney.data;
00278                 break;
00279         case 1:         /* Status Port */       
00280 //              LOG(LOG_MISC,"DISNEY:Read from status port %X",disney.status);
00281                 retval = 0x07;//0x40; // Stereo-on-1 and (or) New-Stereo DACs present
00282                 if(disney.interface_det_ext > 5) {
00283                         if (disney.leader && disney.leader->used >= 16){
00284                                 retval |= 0x40u; // ack
00285                                 retval &= ~0x4u; // interrupt
00286                         }
00287                 }
00288                 if(!(disney.data&0x80)) retval |= 0x80; // pin 9 is wired to pin 11
00289                 return retval;
00290                 break;
00291         case 2:         /* Control Port */
00292                 LOG(LOG_MISC,LOG_NORMAL)("DISNEY:Read from control port");
00293                 return disney.control;
00294                 break;
00295         }
00296         return 0xff;
00297 }
00298 
00299 static void DISNEY_PlayStereo(Bitu len, Bit8u* l, Bit8u* r) {
00300         static Bit8u stereodata[DISNEY_SIZE*2];
00301         for(Bitu i = 0; i < len; i++) {
00302                 stereodata[i*2] = l[i];
00303                 stereodata[i*2+1] = r[i];
00304         }
00305         disney.chan->AddSamples_s8(len,stereodata);
00306 }
00307 
00308 static void DISNEY_CallBack(Bitu len) {
00309         if (!len) return;
00310         if (disney.leader == NULL) return;
00311 
00312         // get the smaller used
00313         Bitu real_used;
00314         if(disney.stereo) {
00315                 real_used = disney.da[0].used;
00316                 if(disney.da[1].used < real_used) real_used = disney.da[1].used;
00317         } else
00318                 real_used = disney.leader->used;
00319 
00320         if (real_used >= len) { // enough data for now
00321                 if(disney.stereo) DISNEY_PlayStereo(len, disney.da[0].buffer, disney.da[1].buffer);
00322                 else disney.chan->AddSamples_m8(len,disney.leader->buffer);
00323 
00324                 // put the rest back to start
00325                 for(int i = 0; i < 2; i++) {
00326                         // TODO for mono only one 
00327                         memmove(disney.da[i].buffer,&disney.da[i].buffer[len],DISNEY_SIZE/*real_used*/-len);
00328                         disney.da[i].used -= len;
00329                 }
00330         // TODO: len > DISNEY
00331         } else { // not enough data
00332                 if(disney.stereo) {
00333                         Bit8u gapfiller0 = 128;
00334                         Bit8u gapfiller1 = 128;
00335                         if(real_used) {
00336                                 gapfiller0 = disney.da[0].buffer[real_used-1];
00337                                 gapfiller1 = disney.da[1].buffer[real_used-1];
00338                         };
00339 
00340                         memset(disney.da[0].buffer+real_used,
00341                                 gapfiller0,len-real_used);
00342                         memset(disney.da[1].buffer+real_used,
00343                                 gapfiller1,len-real_used);
00344 
00345                         DISNEY_PlayStereo(len, disney.da[0].buffer, disney.da[1].buffer);
00346                         len -= real_used;
00347 
00348                 } else { // mono
00349                         Bit8u gapfiller = 128; //Keep the middle
00350                         if(real_used) {
00351                                 // fix for some stupid game; it outputs 0 at the end of the stream
00352                                 // causing a click. So if we have at least two bytes availible in the
00353                                 // buffer and the last one is a 0 then ignore that.
00354                                 if(disney.leader->buffer[real_used-1]==0)
00355                                         real_used--;
00356                         }
00357                         // do it this way because AddSilence sounds like a gnawing mouse
00358                         if(real_used)
00359                                 gapfiller = disney.leader->buffer[real_used-1];
00360                         //LOG_MSG("gapfiller %x, fill len %d, realused %d",gapfiller,len-real_used,real_used);
00361                         memset(disney.leader->buffer+real_used, gapfiller, len-real_used);
00362                         disney.chan->AddSamples_m8(len, disney.leader->buffer);
00363                 }
00364                 disney.da[0].used =0;
00365                 disney.da[1].used =0;
00366 
00367                 //LOG_MSG("disney underflow %d",len - real_used);
00368         }
00369         if (disney.last_used+100<PIC_Ticks) {
00370                 // disable sound output
00371                 PIC_AddEvent(DISNEY_disable,0.0001f);   // I think we shouldn't delete the 
00372                                                                                                 // mixer while we are inside it
00373         }
00374 }
00375 
00376 void CPU_Snap_Back_To_Real_Mode();
00377 void CPU_Snap_Back_Restore();
00378 
00379 class DISNEY: public Module_base {
00380 private:
00381         IO_ReadHandleObject ReadHandler;
00382         IO_WriteHandleObject WriteHandler;
00383         //FIXME: this conflicts with MPU-401, is this really needed? //IO_WriteHandleObject WriteHandler_cvm;
00384         //MixerObject MixerChan;
00385 public:
00386         DISNEY(Section* configuration):Module_base(configuration) {
00387                 Section_prop * section=static_cast<Section_prop *>(configuration);
00388                 if(!section->Get_bool("disney")) return;
00389 
00390                 for(int i = 0; i < 2; i++) {
00391                         disney.da[i].used = 0;
00392                         disney.da[i].speedcheck_sum = 0;
00393                         disney.da[i].speedcheck_failed = false;
00394                         disney.da[i].speedcheck_init = false;
00395                 }
00396 
00397                 WriteHandler.Install(DISNEY_BASE,disney_write,IO_MB,3);
00398                 ReadHandler.Install(DISNEY_BASE,disney_read,IO_MB,3);
00399                 // see above //WriteHandler_cvm.Install(0x330,disney_write,IO_MB,1);
00400         
00401                 disney.status=0x84;
00402                 disney.control=0;
00403                 disney.last_used=0;
00404 
00405                 disney.mo = new MixerObject();
00406                 disney.chan=disney.mo->Install(&DISNEY_CallBack,10000,"DISNEY");
00407                 DISNEY_disable(0);
00408         }
00409         ~DISNEY(){
00410                 CPU_Snap_Back_To_Real_Mode();
00411 
00412                 BIOS_SetLPTPort(0,0);
00413                 DISNEY_disable(0);
00414                 if (disney.mo)
00415                         delete disney.mo;
00416 
00417                 CPU_Snap_Back_Restore();
00418         }
00419 };
00420 
00421 static DISNEY* test = NULL;
00422 
00423 static void DISNEY_ShutDown(Section* sec){
00424     (void)sec;//UNUSED
00425     if (test) {
00426         delete test;
00427         test = NULL;
00428     }
00429 }
00430 
00431 Bitu DISNEY_BasePort() {
00432         return DISNEY_BASE;
00433 }
00434 
00435 bool DISNEY_ShouldInit() {
00436         Section_prop *sec = (Section_prop*)control->GetSection("speaker");
00437         return sec->Get_bool("disney");
00438 }
00439 
00440 bool DISNEY_HasInit() {
00441         return (test != NULL);
00442 }
00443 
00444 // parallel port init code will call this depending on port allocation and config.
00445 void DISNEY_Init(unsigned int base_port) {
00446         if (test == NULL) {
00447                 DISNEY_BASE = base_port;
00448                 LOG(LOG_MISC,LOG_DEBUG)("Allocating Disney Sound emulation on port %xh",DISNEY_BASE);
00449                 test = new DISNEY(control->GetSection("speaker"));
00450         }
00451 }
00452 
00453 void DISNEY_Init() {
00454         LOG(LOG_MISC,LOG_DEBUG)("Initializing Disney Sound Source emulation");
00455 
00456         AddExitFunction(AddExitFunctionFuncPair(DISNEY_ShutDown),true);
00457 }
00458