DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/hardware/joystick.cpp
00001 /*
00002  *  Copyright (C) 2002-2020  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 along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 
00020 #include <string.h>
00021 #include "dosbox.h"
00022 #include "inout.h"
00023 #include "setup.h"
00024 #include "joystick.h"
00025 #include "pic.h"
00026 #include "support.h"
00027 #include "control.h"
00028 
00029 #define RANGE 64
00030 #define TIMEOUT 10
00031 
00032 #define OHMS 120000/2
00033 #define JOY_S_CONSTANT 0.0000242
00034 #define S_PER_OHM 0.000000011
00035 
00036 struct JoyStick {
00037         bool enabled;
00038         float xpos,ypos;
00039         double xtick,ytick;
00040         Bitu xcount,ycount;
00041         bool button[2];
00042 };
00043 
00044 JoystickType joytype;
00045 static JoyStick stick[2];
00046 
00047 static Bit32u last_write = 0;
00048 static bool write_active = false;
00049 static bool swap34 = false;
00050 bool button_wrapping_enabled = true;
00051 
00052 extern bool autofire; //sdl_mapper.cpp
00053 extern int joy1axes[]; //sdl_mapper.cpp
00054 extern int joy2axes[]; //sdl_mapper.cpp
00055 
00056 static Bitu read_p201(Bitu port,Bitu iolen) {
00057     (void)iolen;//UNUSED
00058     (void)port;//UNUSED
00059         /* Reset Joystick to 0 after TIMEOUT ms */
00060         if(write_active && ((PIC_Ticks - last_write) > TIMEOUT)) {
00061                 write_active = false;
00062                 stick[0].xcount = 0;
00063                 stick[1].xcount = 0;
00064                 stick[0].ycount = 0;
00065                 stick[1].ycount = 0;
00066 //              LOG_MSG("reset by time %d %d",PIC_Ticks,last_write);
00067         }
00068 
00078         Bit8u ret=0xff;
00079         if (stick[0].enabled) {
00080                 if (stick[0].xcount) stick[0].xcount--; else ret&=~1;
00081                 if (stick[0].ycount) stick[0].ycount--; else ret&=~2;
00082                 if (stick[0].button[0]) ret&=~16;
00083                 if (stick[0].button[1]) ret&=~32;
00084         }
00085         if (stick[1].enabled) {
00086                 if (stick[1].xcount) stick[1].xcount--; else ret&=~4;
00087                 if (stick[1].ycount) stick[1].ycount--; else ret&=~8;
00088                 if (stick[1].button[0]) ret&=~64;
00089                 if (stick[1].button[1]) ret&=~128;
00090         }
00091         return ret;
00092 }
00093 
00094 static Bitu read_p201_timed(Bitu port,Bitu iolen) {
00095     (void)port;//UNUSED
00096     (void)iolen;//UNUSED
00097         Bit8u ret=0xff;
00098         double currentTick = PIC_FullIndex();
00099         if( stick[0].enabled ){
00100                 if( stick[0].xtick < currentTick ) ret &=~1;
00101                 if( stick[0].ytick < currentTick ) ret &=~2;
00102         }
00103         if( stick[1].enabled ){
00104                 if( stick[1].xtick < currentTick ) ret &=~4;
00105                 if( stick[1].ytick < currentTick ) ret &=~8;
00106         }
00107 
00108         if (stick[0].enabled) {
00109                 if (stick[0].button[0]) ret&=~16;
00110                 if (stick[0].button[1]) ret&=~32;
00111         }
00112         if (stick[1].enabled) {
00113                 if (stick[1].button[0]) ret&=~64;
00114                 if (stick[1].button[1]) ret&=~128;
00115         }
00116         return ret;
00117 }
00118 
00119 static void write_p201(Bitu port,Bitu val,Bitu iolen) {
00120     (void)val;//UNUSED
00121     (void)port;//UNUSED
00122     (void)iolen;//UNUSED
00123         /* Store writetime index */
00124         write_active = true;
00125         last_write = (Bit32u)PIC_Ticks;
00126         if (stick[0].enabled) {
00127                 stick[0].xcount=(Bitu)((stick[0].xpos*RANGE)+RANGE);
00128                 stick[0].ycount=(Bitu)((stick[0].ypos*RANGE)+RANGE);
00129         }
00130         if (stick[1].enabled) {
00131                 stick[1].xcount=(Bitu)(((swap34? stick[1].ypos : stick[1].xpos)*RANGE)+RANGE);
00132                 stick[1].ycount=(Bitu)(((swap34? stick[1].xpos : stick[1].ypos)*RANGE)+RANGE);
00133         }
00134 
00135 }
00136 static void write_p201_timed(Bitu port,Bitu val,Bitu iolen) {
00137     (void)val;//UNUSED
00138     (void)port;//UNUSED
00139     (void)iolen;//UNUSED
00140         // Store writetime index
00141         // Axes take time = 24.2 microseconds + ( 0.011 microsecons/ohm * resistance )
00142         // to reset to 0
00143         // Precalculate the time at which each axis hits 0 here
00144         double currentTick = PIC_FullIndex();
00145         if (stick[0].enabled) {
00146                 stick[0].xtick = currentTick + 1000.0*( JOY_S_CONSTANT + S_PER_OHM *
00147                                  (double)((stick[0].xpos+1.0)* OHMS) );
00148                 stick[0].ytick = currentTick + 1000.0*( JOY_S_CONSTANT + S_PER_OHM *
00149                                  (double)((stick[0].ypos+1.0)* OHMS) );
00150         }
00151         if (stick[1].enabled) {
00152                 stick[1].xtick = currentTick + 1000.0*( JOY_S_CONSTANT + S_PER_OHM *
00153                                  (double)((swap34? stick[1].ypos : stick[1].xpos)+1.0) * OHMS);
00154                 stick[1].ytick = currentTick + 1000.0*( JOY_S_CONSTANT + S_PER_OHM *
00155                                  (double)((swap34? stick[1].xpos : stick[1].ypos)+1.0) * OHMS);
00156         }
00157 }
00158 
00159 void JOYSTICK_Enable(Bitu which,bool enabled) {
00160         LOG(LOG_MISC,LOG_DEBUG)("JOYSTICK: Stick %u enable=%u",(int)which,enabled?1:0);
00161         if (which<2) stick[which].enabled=enabled;
00162 }
00163 
00164 void JOYSTICK_Button(Bitu which,Bitu num,bool pressed) {
00165         if ((which<2) && (num<2)) stick[which].button[num]=pressed;
00166 }
00167 
00168 void JOYSTICK_Move_X(Bitu which,float x) {
00169         if (which<2) {
00170                 stick[which].xpos=x;
00171         }
00172 }
00173 
00174 void JOYSTICK_Move_Y(Bitu which,float y) {
00175         if (which<2) {
00176                 stick[which].ypos=y;
00177         }
00178 }
00179 
00180 bool JOYSTICK_IsEnabled(Bitu which) {
00181         if (which<2) return stick[which].enabled;
00182         return false;
00183 }
00184 
00185 bool JOYSTICK_GetButton(Bitu which, Bitu num) {
00186         if ((which<2) && (num<2)) return stick[which].button[num];
00187         return false;
00188 }
00189 
00190 float JOYSTICK_GetMove_X(Bitu which) {
00191         if (which<2) return stick[which].xpos;
00192         return 0.0f;
00193 }
00194 
00195 float JOYSTICK_GetMove_Y(Bitu which) {
00196         if (which<2) return stick[which].ypos;
00197         return 0.0f;
00198 }
00199 
00200 class JOYSTICK:public Module_base{
00201 private:
00202         IO_ReadHandleObject ReadHandler;
00203         IO_WriteHandleObject WriteHandler;
00204 public:
00205         JOYSTICK(Section* configuration):Module_base(configuration){
00206                 Section_prop * section=static_cast<Section_prop *>(configuration);
00207 
00208                 bool timed = section->Get_bool("timed");
00209                 if(timed) {
00210                         ReadHandler.Install(0x201,read_p201_timed,IO_MB);
00211                         WriteHandler.Install(0x201,write_p201_timed,IO_MB);
00212                 } else {
00213                         ReadHandler.Install(0x201,read_p201,IO_MB);
00214                         WriteHandler.Install(0x201,write_p201,IO_MB);
00215                 }
00216         }
00217 };
00218 
00219 static JOYSTICK* test = NULL;
00220 
00221 void JOYSTICK_Destroy(Section* sec) {
00222     (void)sec;//UNUSED
00223     if (test != NULL) {
00224         delete test;
00225         test = NULL;
00226     }
00227 }
00228 
00229 void JOYSTICK_OnPowerOn(Section* sec) {
00230     (void)sec;//UNUSED
00231     if (test == NULL) {
00232         LOG(LOG_MISC,LOG_DEBUG)("Allocating joystick emulation");
00233         test = new JOYSTICK(control->GetSection("joystick"));
00234     }
00235 }
00236 
00237 void JOYSTICK_Init() {
00238         LOG(LOG_MISC,LOG_DEBUG)("Initializing joystick emulation");
00239 
00240         /* NTS: Joystick emulation does not work if we init joystick type AFTER mapper init.
00241          *      We cannot wait for poweron/reset signal for determination of joystick type.
00242          *      But, I/O port setup can happen later. */
00243         {
00244                 Section_prop * section=static_cast<Section_prop *>(control->GetSection("joystick"));
00245 
00246                 const char * type=section->Get_string("joysticktype");
00247                 if (!strcasecmp(type,"none"))       joytype = JOY_NONE;
00248                 else if (!strcasecmp(type,"false")) joytype = JOY_NONE;
00249                 else if (!strcasecmp(type,"auto"))  joytype = JOY_AUTO;
00250                 else if (!strcasecmp(type,"2axis")) joytype = JOY_2AXIS;
00251                 else if (!strcasecmp(type,"4axis")) joytype = JOY_4AXIS;
00252                 else if (!strcasecmp(type,"4axis_2")) joytype = JOY_4AXIS_2;
00253                 else if (!strcasecmp(type,"fcs"))   joytype = JOY_FCS;
00254                 else if (!strcasecmp(type,"ch"))    joytype = JOY_CH;
00255                 else joytype = JOY_AUTO;
00256 
00257                 autofire = section->Get_bool("autofire");
00258                 swap34 = section->Get_bool("swap34");
00259                 button_wrapping_enabled = section->Get_bool("buttonwrap");
00260                 stick[0].enabled = false;
00261                 stick[1].enabled = false;
00262                 stick[0].xtick = stick[0].ytick = stick[1].xtick =
00263                                  stick[1].ytick = PIC_FullIndex();
00264                 
00265                 // retrieves axes mapping
00266                 auto joysticks = 2;
00267                 auto axes = 8;
00268                 for (auto i = 0; i < joysticks; i++)
00269                 {
00270                         for (auto j = 0; j < axes; j++)
00271                         {
00272                                 auto propname = "joy" + std::to_string(i + 1) + "axis" + std::to_string(j);
00273                                 auto axis = section->Get_int(propname);
00274                                 if (i == 0)
00275                                 {
00276                                         joy1axes[j] = axis;
00277                                 }
00278                                 else
00279                                 {
00280                                         joy2axes[j] = axis;
00281                                 }
00282                         }
00283                 }
00284         }
00285 
00286         AddExitFunction(AddExitFunctionFuncPair(JOYSTICK_Destroy),true);
00287 
00288     if (!IS_PC98_ARCH)
00289         AddVMEventFunction(VM_EVENT_POWERON,AddVMEventFunctionFuncPair(JOYSTICK_OnPowerOn));
00290 }
00291 
00292 //save state support
00293 namespace
00294 {
00295 class SerializeStick : public SerializeGlobalPOD
00296 {
00297 public:
00298     SerializeStick() : SerializeGlobalPOD("Joystick")
00299     {
00300         registerPOD(joytype);
00301         registerPOD(stick);
00302         registerPOD(last_write);
00303         registerPOD(write_active);
00304         registerPOD(swap34);
00305         registerPOD(button_wrapping_enabled);
00306         registerPOD(autofire);
00307     }
00308 } dummy;
00309 }