1 /******************************************************************************/
2 /* Mednafen - Multi-system Emulator                                           */
3 /******************************************************************************/
4 /* megamouse.cpp:
5 **  Copyright (C) 2009-2016 Mednafen Team
6 **
7 ** This program is free software; you can redistribute it and/or
8 ** modify it under the terms of the GNU General Public License
9 ** as published by the Free Software Foundation; either version 2
10 ** of the License, or (at your option) any later version.
11 **
12 ** This program is distributed in the hope that it will be useful,
13 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 ** GNU General Public License for more details.
16 **
17 ** You should have received a copy of the GNU General Public License
18 ** along with this program; if not, write to the Free Software Foundation, Inc.,
19 ** 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 */
21 
22 #include "../shared.h"
23 #include "megamouse.h"
24 #include <trio/trio.h>
25 
26 namespace MDFN_IEN_MD
27 {
28 
29 enum
30 {
31  MASK_TH = 0x40,
32  MASK_TR = 0x20,
33  MASK_TL = 0x10,
34  MASK_DATA = 0x0F
35 };
36 
37 class MegaMouse final : public MD_Input_Device
38 {
39         public:
40 	MegaMouse();
41 	virtual ~MegaMouse() override;
42         virtual void UpdateBus(const int32 master_timestamp, uint8 &bus, const uint8 genesis_asserted) override;
43         virtual void UpdatePhysicalState(const void *data) override;
44         virtual void BeginTimePeriod(const int32 timestamp_base) override;
45         virtual void EndTimePeriod(const int32 master_timestamp) override;
46 	virtual void Power(void) override;
47         virtual void StateAction(StateMem *sm, const unsigned load, const bool data_only, const char *section_prefix) override;
48 
49 	private:
50 	int32 mouse_x;
51 	int32 mouse_y;
52 	uint8 buttons;
53 
54 	int32 busy_until;
55 
56 	uint8 phase;
57 	uint8 bus_av;
58 	uint8 data_buffer[0x8];
59 };
60 
61 const IDIISG MegaMouseIDII =
62 {
63  IDIIS_AxisRel("motion", "Motion",/**/ "left", "Left",/**/ "right", "Right", 0),
64  IDIIS_AxisRel("motion", "Motion",/**/ "up", "Up",/**/ "down", "Down", 1),
65  IDIIS_Button("left", "Left Button", 2),
66  IDIIS_Button("right", "Right Button", 3),
67  IDIIS_Button("middle", "Middle Button", 4),
68  IDIIS_Button("start", "Start Button", 5),
69 };
70 
71 enum
72 {
73  PHASE_RESETTING = 0,
74  PHASE_RESET_EXEC,
75  PHASE_INITIAL,
76  PHASE_BEGIN,
77  PHASE_DATA0,
78  PHASE_DATA1,
79  PHASE_DATA2,
80  PHASE_DATA3,
81  PHASE_DATA4,
82  PHASE_DATA5,
83  PHASE_DATA6,
84  PHASE_DATA7,
85  PHASE_END
86 };
87 
MegaMouse()88 MegaMouse::MegaMouse()
89 {
90         mouse_x = 0;
91         mouse_y = 0;
92         buttons = 0;
93 
94 	phase = PHASE_INITIAL;
95 	busy_until = -1;
96 }
97 
~MegaMouse()98 MegaMouse::~MegaMouse()
99 {
100 
101 }
102 
Power(void)103 void MegaMouse::Power(void)
104 {
105 	bus_av = 0x10;
106 	phase = PHASE_INITIAL;
107 	busy_until = -1;
108 }
109 
110 
BeginTimePeriod(const int32 timestamp_base)111 void MegaMouse::BeginTimePeriod(const int32 timestamp_base)
112 {
113  //puts("Begin");
114  if(busy_until >= 0)
115   busy_until += timestamp_base;
116 }
117 
EndTimePeriod(const int32 master_timestamp)118 void MegaMouse::EndTimePeriod(const int32 master_timestamp)
119 {
120  if(busy_until >= 0 && master_timestamp >= busy_until)
121  {
122   //puts("Advance phase");
123   if(phase < PHASE_END)
124    phase++;
125   busy_until = -1;
126  }
127 
128  if(busy_until >= 0)
129   busy_until -= master_timestamp;
130 }
131 
UpdateBus(const int32 master_timestamp,uint8 & bus,const uint8 genesis_asserted)132 void MegaMouse::UpdateBus(const int32 master_timestamp, uint8 &bus, const uint8 genesis_asserted)
133 {
134  const bool th = bus & MASK_TH;
135  const bool tr = bus & MASK_TR;
136 
137  if(th && tr && phase != PHASE_RESET_EXEC)
138  {
139   busy_until = -1;
140   phase = PHASE_INITIAL;
141  }
142 
143  if(busy_until >= 0)
144  {
145   if(master_timestamp >= busy_until)
146   {
147    //puts("Advance phase");
148    if(phase < PHASE_END)
149     phase++;
150    busy_until = -1;
151   }
152  }
153 
154  if(busy_until < 0)
155  {
156   switch(phase)
157   {
158    case PHASE_RESET_EXEC:
159 	bus_av = 0x00;
160 	if(tr)
161 	 busy_until = master_timestamp + 400;
162 	break;
163 
164    case PHASE_INITIAL:
165 	if(th)
166 	{
167 	 bus_av = 0x10;
168 
169 	 if(!tr)
170 	 {
171 	  phase = PHASE_RESETTING;
172 	  busy_until = master_timestamp + 400;
173 	 }
174 	}
175 	else
176 	{
177 	 phase++;
178 	}
179 	break;
180 
181    case PHASE_BEGIN:
182 	bus_av = 0x1B;
183 	if(!tr)
184 	{
185 	 int32 rel_x = mouse_x;
186      	 int32 rel_y = mouse_y;
187      	 bool x_neg = 0;
188 	 bool y_neg = 0;
189 
190      	 if(rel_x < -255)
191 	  rel_x = -255;
192 
193 	 if(rel_x > 255)
194 	  rel_x = 255;
195 
196 	 if(rel_y < -255)
197 	  rel_y = -255;
198 
199 	 if(rel_y > 255)
200 	  rel_y = 255;
201 
202 	 mouse_x -= rel_x;
203 	 mouse_y -= rel_y;
204 
205 	 rel_y = -rel_y;
206 
207 	 x_neg = (rel_x < 0);
208 	 y_neg = (rel_y < 0);
209 
210 	 data_buffer[0] = 0xF;
211 	 data_buffer[1] = 0xF;
212 	 data_buffer[2] = (x_neg ? 0x1 : 0x0) | (y_neg ? 0x2 : 0x0); // Axis sign and overflow
213 	 data_buffer[3] = buttons; // Button state
214 	 data_buffer[4] = (rel_x >> 4) & 0xF; // X axis MSN
215 	 data_buffer[5] = (rel_x >> 0) & 0xF; // X axis LSN
216 	 data_buffer[6] = (rel_y >> 4) & 0xF; // Y axis MSN
217      	 data_buffer[7] = (rel_y >> 0) & 0xF; // Y axis LSN
218 
219 	 //printf("DB: %02x %02x %02x %02x %02x %02x %02x %02x\n", data_buffer[0], data_buffer[1], data_buffer[2], data_buffer[3], data_buffer[4], data_buffer[5], data_buffer[6], data_buffer[7]);
220 
221 	 busy_until = master_timestamp + 400;
222 	}
223 	break;
224 
225    case PHASE_DATA0: case PHASE_DATA1: case PHASE_DATA2: case PHASE_DATA3:
226    case PHASE_DATA4: case PHASE_DATA5: case PHASE_DATA6: case PHASE_DATA7:
227 	bus_av = data_buffer[phase - PHASE_DATA0] | (((phase - PHASE_DATA0) & 1) << 4);
228 	if(tr != ((phase - PHASE_DATA0) & 1))
229 	 busy_until = master_timestamp + 400;
230 	break;
231 
232    case PHASE_END:
233 	bus_av ^= 0x10;
234 
235 	if(tr != (bool)(bus_av & 0x10))
236 	{
237 	 busy_until = master_timestamp + 400;
238 	}
239 	break;
240   }
241  }
242 
243  bus = (bus &~ 0x1F) | bus_av;
244  //printf("%02x, %02x --- %d -- %d\n", bus, bus_av, phase, master_timestamp);
245 }
246 
UpdatePhysicalState(const void * data)247 void MegaMouse::UpdatePhysicalState(const void *data)
248 {
249  mouse_x += (int16)MDFN_de16lsb((uint8 *)data + 0);
250  mouse_y += (int16)MDFN_de16lsb((uint8 *)data + 2);
251  buttons = ((uint8 *)data)[4];
252 }
253 
StateAction(StateMem * sm,const unsigned load,const bool data_only,const char * section_prefix)254 void MegaMouse::StateAction(StateMem *sm, const unsigned load, const bool data_only, const char *section_prefix)
255 {
256  SFORMAT StateRegs[] =
257  {
258   SFVAR(mouse_x),
259   SFVAR(mouse_y),
260   SFVAR(buttons),
261 
262   SFVAR(busy_until),
263 
264   SFVAR(phase),
265   SFVAR(bus_av),
266   SFVAR(data_buffer),
267 
268   SFEND
269  };
270  char sname[64];
271 
272  trio_snprintf(sname, sizeof(sname), "%s-mmouse", section_prefix);
273 
274  if(!MDFNSS_StateAction(sm, load, data_only, StateRegs, sname, true) && load)
275   Power();
276  else if(load)
277  {
278 
279  }
280 }
281 
MDInput_MakeMegaMouse(void)282 MD_Input_Device *MDInput_MakeMegaMouse(void)
283 {
284  MD_Input_Device *ret = new MegaMouse();
285 
286  return(ret);
287 }
288 
289 }
290