1 /*
2  *  Copyright (C) 2002-2010  The DOSBox Team
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 
19 /* $Id: joystick.cpp,v 1.21 2009-05-27 09:15:41 qbix79 Exp $ */
20 
21 #include <string.h>
22 #include "dosbox.h"
23 #include "inout.h"
24 #include "setup.h"
25 #include "joystick.h"
26 #include "pic.h"
27 #include "support.h"
28 
29 #define RANGE 64
30 #define TIMEOUT 10
31 
32 #define OHMS 120000/2
33 #define JOY_S_CONSTANT 0.0000242
34 #define S_PER_OHM 0.000000011
35 
36 struct JoyStick {
37 	bool enabled;
38 	float xpos,ypos;
39 	double xtick,ytick;
40 	Bitu xcount,ycount;
41 	bool button[2];
42 };
43 
44 JoystickType joytype;
45 static JoyStick stick[2];
46 
47 static Bit32u last_write = 0;
48 static bool write_active = false;
49 static bool swap34 = false;
50 bool button_wrapping_enabled = true;
51 
52 extern bool autofire; //sdl_mapper.cpp
53 
read_p201(Bitu port,Bitu iolen)54 static Bitu read_p201(Bitu port,Bitu iolen) {
55 	/* Reset Joystick to 0 after TIMEOUT ms */
56 	if(write_active && ((PIC_Ticks - last_write) > TIMEOUT)) {
57 		write_active = false;
58 		stick[0].xcount = 0;
59 		stick[1].xcount = 0;
60 		stick[0].ycount = 0;
61 		stick[1].ycount = 0;
62 //		LOG_MSG("reset by time %d %d",PIC_Ticks,last_write);
63 	}
64 
65 	/**  Format of the byte to be returned:
66 	**                        | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
67 	**                        +-------------------------------+
68 	**                          |   |   |   |   |   |   |   |
69 	**  Joystick B, Button 2 ---+   |   |   |   |   |   |   +--- Joystick A, X Axis
70 	**  Joystick B, Button 1 -------+   |   |   |   |   +------- Joystick A, Y Axis
71 	**  Joystick A, Button 2 -----------+   |   |   +----------- Joystick B, X Axis
72 	**  Joystick A, Button 1 ---------------+   +--------------- Joystick B, Y Axis
73 	**/
74 	Bit8u ret=0xff;
75 	if (stick[0].enabled) {
76 		if (stick[0].xcount) stick[0].xcount--; else ret&=~1;
77 		if (stick[0].ycount) stick[0].ycount--; else ret&=~2;
78 		if (stick[0].button[0]) ret&=~16;
79 		if (stick[0].button[1]) ret&=~32;
80 	}
81 	if (stick[1].enabled) {
82 		if (stick[1].xcount) stick[1].xcount--; else ret&=~4;
83 		if (stick[1].ycount) stick[1].ycount--; else ret&=~8;
84 		if (stick[1].button[0]) ret&=~64;
85 		if (stick[1].button[1]) ret&=~128;
86 	}
87 	return ret;
88 }
89 
read_p201_timed(Bitu port,Bitu iolen)90 static Bitu read_p201_timed(Bitu port,Bitu iolen) {
91 	Bit8u ret=0xff;
92 	double currentTick = PIC_FullIndex();
93 	if( stick[0].enabled ){
94 		if( stick[0].xtick < currentTick ) ret &=~1;
95 		if( stick[0].ytick < currentTick ) ret &=~2;
96 	}
97 	if( stick[1].enabled ){
98 		if( stick[1].xtick < currentTick ) ret &=~4;
99 		if( stick[1].ytick < currentTick ) ret &=~8;
100 	}
101 
102 	if (stick[0].enabled) {
103 		if (stick[0].button[0]) ret&=~16;
104 		if (stick[0].button[1]) ret&=~32;
105 	}
106 	if (stick[1].enabled) {
107 		if (stick[1].button[0]) ret&=~64;
108 		if (stick[1].button[1]) ret&=~128;
109 	}
110 	return ret;
111 }
112 
write_p201(Bitu port,Bitu val,Bitu iolen)113 static void write_p201(Bitu port,Bitu val,Bitu iolen) {
114 	/* Store writetime index */
115 	write_active = true;
116 	last_write = PIC_Ticks;
117 	if (stick[0].enabled) {
118 		stick[0].xcount=(Bitu)((stick[0].xpos*RANGE)+RANGE);
119 		stick[0].ycount=(Bitu)((stick[0].ypos*RANGE)+RANGE);
120 	}
121 	if (stick[1].enabled) {
122 		stick[1].xcount=(Bitu)(((swap34? stick[1].ypos : stick[1].xpos)*RANGE)+RANGE);
123 		stick[1].ycount=(Bitu)(((swap34? stick[1].xpos : stick[1].ypos)*RANGE)+RANGE);
124 	}
125 
126 }
write_p201_timed(Bitu port,Bitu val,Bitu iolen)127 static void write_p201_timed(Bitu port,Bitu val,Bitu iolen) {
128 	// Store writetime index
129 	// Axes take time = 24.2 microseconds + ( 0.011 microsecons/ohm * resistance )
130 	// to reset to 0
131 	// Precalculate the time at which each axis hits 0 here
132 	double currentTick = PIC_FullIndex();
133 	if (stick[0].enabled) {
134 		stick[0].xtick = currentTick + 1000.0*( JOY_S_CONSTANT + S_PER_OHM *
135 	                         (double)(((stick[0].xpos+1.0)* OHMS)) );
136 		stick[0].ytick = currentTick + 1000.0*( JOY_S_CONSTANT + S_PER_OHM *
137 		                 (double)(((stick[0].ypos+1.0)* OHMS)) );
138 	}
139 	if (stick[1].enabled) {
140 		stick[1].xtick = currentTick + 1000.0*( JOY_S_CONSTANT + S_PER_OHM *
141 		                 (double)((swap34? stick[1].ypos : stick[1].xpos)+1.0) * OHMS);
142 		stick[1].ytick = currentTick + 1000.0*( JOY_S_CONSTANT + S_PER_OHM *
143 		                 (double)((swap34? stick[1].xpos : stick[1].ypos)+1.0) * OHMS);
144 	}
145 }
146 
JOYSTICK_Enable(Bitu which,bool enabled)147 void JOYSTICK_Enable(Bitu which,bool enabled) {
148 	if (which<2) stick[which].enabled=enabled;
149 }
150 
JOYSTICK_Button(Bitu which,Bitu num,bool pressed)151 void JOYSTICK_Button(Bitu which,Bitu num,bool pressed) {
152 	if ((which<2) && (num<2)) stick[which].button[num]=pressed;
153 }
154 
JOYSTICK_Move_X(Bitu which,float x)155 void JOYSTICK_Move_X(Bitu which,float x) {
156 	if (which<2) {
157 		stick[which].xpos=x;
158 	}
159 }
160 
JOYSTICK_Move_Y(Bitu which,float y)161 void JOYSTICK_Move_Y(Bitu which,float y) {
162 	if (which<2) {
163 		stick[which].ypos=y;
164 	}
165 }
166 
JOYSTICK_IsEnabled(Bitu which)167 bool JOYSTICK_IsEnabled(Bitu which) {
168 	if (which<2) return stick[which].enabled;
169 	return false;
170 }
171 
JOYSTICK_GetButton(Bitu which,Bitu num)172 bool JOYSTICK_GetButton(Bitu which, Bitu num) {
173 	if ((which<2) && (num<2)) return stick[which].button[num];
174 	return false;
175 }
176 
JOYSTICK_GetMove_X(Bitu which)177 float JOYSTICK_GetMove_X(Bitu which) {
178 	if (which<2) return stick[which].xpos;
179 	return 0.0f;
180 }
181 
JOYSTICK_GetMove_Y(Bitu which)182 float JOYSTICK_GetMove_Y(Bitu which) {
183 	if (which<2) return stick[which].ypos;
184 	return 0.0f;
185 }
186 
187 class JOYSTICK:public Module_base{
188 private:
189 	IO_ReadHandleObject ReadHandler;
190 	IO_WriteHandleObject WriteHandler;
191 public:
JOYSTICK(Section * configuration)192 	JOYSTICK(Section* configuration):Module_base(configuration){
193 		Section_prop * section=static_cast<Section_prop *>(configuration);
194 		const char * type=section->Get_string("joysticktype");
195 		if (!strcasecmp(type,"none"))       joytype = JOY_NONE;
196 		else if (!strcasecmp(type,"false")) joytype = JOY_NONE;
197 		else if (!strcasecmp(type,"auto"))  joytype = JOY_AUTO;
198 		else if (!strcasecmp(type,"2axis")) joytype = JOY_2AXIS;
199 		else if (!strcasecmp(type,"4axis")) joytype = JOY_4AXIS;
200 		else if (!strcasecmp(type,"4axis_2")) joytype = JOY_4AXIS_2;
201 		else if (!strcasecmp(type,"fcs"))   joytype = JOY_FCS;
202 		else if (!strcasecmp(type,"ch"))    joytype = JOY_CH;
203 		else joytype = JOY_AUTO;
204 
205 		bool timed = section->Get_bool("timed");
206 		if(timed) {
207 			ReadHandler.Install(0x201,read_p201_timed,IO_MB);
208 			WriteHandler.Install(0x201,write_p201_timed,IO_MB);
209 		} else {
210 			ReadHandler.Install(0x201,read_p201,IO_MB);
211 			WriteHandler.Install(0x201,write_p201,IO_MB);
212 		}
213 		autofire = section->Get_bool("autofire");
214 		swap34 = section->Get_bool("swap34");
215 		button_wrapping_enabled = section->Get_bool("buttonwrap");
216 		stick[0].enabled = false;
217 		stick[1].enabled = false;
218 		stick[0].xtick = stick[0].ytick = stick[1].xtick =
219 		                 stick[1].ytick = PIC_FullIndex();
220 	}
221 };
222 static JOYSTICK* test;
223 
JOYSTICK_Destroy(Section * sec)224 void JOYSTICK_Destroy(Section* sec) {
225 	delete test;
226 }
227 
JOYSTICK_Init(Section * sec)228 void JOYSTICK_Init(Section* sec) {
229 	test = new JOYSTICK(sec);
230 	sec->AddDestroyFunction(&JOYSTICK_Destroy,true);
231 }
232