/ MCUME_teensy41 / teensysnes / controls.cpp
controls.cpp
  1  /*****************************************************************************\
  2       Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
  3                  This file is licensed under the Snes9x License.
  4     For further information, consult the LICENSE file in the root directory.
  5  \*****************************************************************************/
  6  
  7  #include "snes9x.h"
  8  #include "memory.h"
  9  #include "apu.h"
 10  //#include "snapshot.h"
 11  #include "controls.h"
 12  
 13  static s9xcommand_t keymap[MaxControlID + 1];
 14  static int			FLAG_LATCH = FALSE;
 15  static struct {
 16  	uint32	read_idx;
 17  	uint32	buttons;
 18  } joypads[2];
 19  
 20  // Note: these should be in asciibetical order!
 21  #define THE_COMMANDS \
 22  	S(Debugger), \
 23  	S(DecFrameRate), \
 24  	S(EmuTurbo), \
 25  	S(ExitEmu), \
 26  	S(IncFrameRate), \
 27  	S(LoadFreezeFile), \
 28  	S(Pause), \
 29  	S(Reset), \
 30  	S(SaveFreezeFile), \
 31  	S(SoftReset), \
 32  	S(SoundChannel0), \
 33  	S(SoundChannel1), \
 34  	S(SoundChannel2), \
 35  	S(SoundChannel3), \
 36  	S(SoundChannel4), \
 37  	S(SoundChannel5), \
 38  	S(SoundChannel6), \
 39  	S(SoundChannel7), \
 40  	S(SoundChannelsOn), \
 41  	S(ToggleBG0), \
 42  	S(ToggleBG1), \
 43  	S(ToggleBG2), \
 44  	S(ToggleBG3), \
 45  	S(ToggleEmuTurbo), \
 46  	S(ToggleSprites), \
 47  	S(ToggleTransparency) \
 48  
 49  #define S(x)	x
 50  
 51  enum command_numbers
 52  {
 53  	THE_COMMANDS,
 54  	LAST_COMMAND
 55  };
 56  
 57  #undef S
 58  #define S(x)	#x
 59  
 60  static const char	*command_names[LAST_COMMAND + 1] =
 61  {
 62  	THE_COMMANDS,
 63  	NULL
 64  };
 65  
 66  #undef S
 67  #undef THE_COMMANDS
 68  
 69  static void DisplayStateChange (const char *str, bool8 on)
 70  {
 71  	snprintf(String, sizeof(String), "%s: %s", str, on ? "on":"off");
 72  	S9xMessage(S9X_INFO, 0, String);
 73  }
 74  
 75  void S9xControlsReset (void)
 76  {
 77  	memset(&joypads, 0, sizeof(joypads));
 78  	FLAG_LATCH = FALSE;
 79  }
 80  
 81  void S9xUnmapAllControls (void)
 82  {
 83  	memset(&keymap, 0, sizeof(keymap));
 84  	S9xControlsReset();
 85  }
 86  
 87  s9xcommand_t S9xGetCommandT (const char *name)
 88  {
 89  	s9xcommand_t	cmd ;
 90  	cmd.type = S9xBadMapping;
 91  	cmd.button_norpt = 0;
 92  	cmd.command = 0;
 93  	//s9xcommand_t	cmd = {
 94  	//	.type = S9xBadMapping,
 95  	//	.button_norpt = 0,
 96  	//	.command = 0,
 97  	//};
 98  
 99  	if (!name)
100  		cmd.type = S9xBadMapping;
101  	else
102  	if (!strcmp(name, "None"))
103  		cmd.type = S9xNoMapping;
104  	else
105  	if (!strncmp(name, "Joypad", 6))
106  	{
107  		if (((int)name[6] - '1') <= 0)
108  			cmd.type = S9xButtonJoypad0;
109  		else
110  			cmd.type = S9xButtonJoypad1;
111  
112  		const char	*s = name + 7;
113  		int i = 0;
114  
115  		while (s[0] == ' ')
116  			s++;
117  
118  		if (!strncmp(s, "Up",     2))	{ i |= SNES_UP_MASK;     s += 2; if (*s == '+') s++; }
119  		if (!strncmp(s, "Down",   4))	{ i |= SNES_DOWN_MASK;   s += 4; if (*s == '+') s++; }
120  		if (!strncmp(s, "Left",   4))	{ i |= SNES_LEFT_MASK;   s += 4; if (*s == '+') s++; }
121  		if (!strncmp(s, "Right",  5))	{ i |= SNES_RIGHT_MASK;  s += 5; if (*s == '+') s++; }
122  
123  		if (*s == 'A')	{ i |= SNES_A_MASK;  s++; if (*s == '+') s++; }
124  		if (*s == 'B')	{ i |= SNES_B_MASK;  s++; if (*s == '+') s++; }
125  		if (*s == 'X')	{ i |= SNES_X_MASK;  s++; if (*s == '+') s++; }
126  		if (*s == 'Y')	{ i |= SNES_Y_MASK;  s++; if (*s == '+') s++; }
127  		if (*s == 'L')	{ i |= SNES_TL_MASK; s++; if (*s == '+') s++; }
128  		if (*s == 'R')	{ i |= SNES_TR_MASK; s++; if (*s == '+') s++; }
129  
130  		if (!strncmp(s, "Start",  5))	{ i |= SNES_START_MASK;  s += 5; if (*s == '+') s++; }
131  		if (!strncmp(s, "Select", 6))	{ i |= SNES_SELECT_MASK; s += 6; }
132  
133  		if (i == 0 || *s != 0 || *(s - 1) == '+')
134  			return (cmd);
135  
136  		cmd.buttons = i;
137  	}
138  	else
139  	{
140  		for (int i = 0; i < LAST_COMMAND; i++)
141  		{
142  			if (strcasecmp(command_names[i], name) == 0)
143  			{
144  				cmd.type = S9xButtonCommand;
145  				cmd.command = i;
146  				break;
147  			}
148  		}
149  	}
150  
151  	return (cmd);
152  }
153  
154  s9xcommand_t S9xGetMapping (uint32 id)
155  {
156  	if (id > MaxControlID)
157  	{
158  		s9xcommand_t dummy = {0};
159  		return dummy;
160  	}
161  
162  	return (keymap[id]);
163  }
164  
165  void S9xUnmapButton (uint32 id)
166  {
167  	if (id > MaxControlID)
168  	{
169  		printf("WARNING: Invalid Control ID %d, should be between 0 and %d\n", id, MaxControlID);
170  		return;
171  	}
172  
173  	memset(&keymap[id], 0, sizeof(s9xcommand_t));
174  }
175  
176  bool S9xMapButtonT (uint32 id, const char *command)
177  {
178  	s9xcommand_t cmd = S9xGetCommandT(command);
179  
180  	if (cmd.type == S9xBadMapping)
181  	{
182  		printf("WARNING: Invalid command '%s'\n", command);
183  		return (false);
184  	}
185  
186  	return S9xMapButton(id, cmd);
187  }
188  
189  bool S9xMapButton (uint32 id, s9xcommand_t mapping)
190  {
191  	if (id > MaxControlID)
192  	{
193  		printf("WARNING: Invalid Control ID %d, should be between 0 and %d\n", id, MaxControlID);
194  		return (false);
195  	}
196  
197  	if (mapping.type == S9xNoMapping)
198  	{
199  		S9xUnmapButton(id);
200  		return (true);
201  	}
202  
203  	if (keymap[id].type != S9xNoMapping)
204  		fprintf(stderr, "WARNING: Remapping ID %d\n", id);
205  
206  	keymap[id] = mapping;
207  
208  	return (true);
209  }
210  
211  void S9xReportButton (uint32 id, bool pressed)
212  {
213  	if (id > MaxControlID)
214  		return;
215  
216  	// skips the "already-pressed check" unless it's a command, as a hack to work around the following problem:
217  	// FIXME: this makes the controls "stick" after loading a savestate while recording a movie and holding any button
218  	if (keymap[id].type == S9xButtonCommand && keymap[id].button_norpt)
219  		return;
220  
221  	keymap[id].button_norpt = pressed;
222  
223  	S9xApplyCommand(keymap[id], pressed);
224  }
225  
226  void S9xApplyCommand (s9xcommand_t cmd, int data1)
227  {
228  	switch (cmd.type)
229  	{
230  		case S9xNoMapping:
231  			return;
232  
233  		case S9xButtonJoypad0:
234  			if (data1)
235  				joypads[0].buttons |= cmd.buttons;
236  			else
237  				joypads[0].buttons &= ~cmd.buttons;
238  			return;
239  
240  		case S9xButtonJoypad1:
241  			if (data1)
242  				joypads[1].buttons |= cmd.buttons;
243  			else
244  				joypads[1].buttons &= ~cmd.buttons;
245  
246  			return;
247  
248  		case S9xButtonCommand:
249  			if (!data1)
250  			{
251  				switch (cmd.command)
252  				{
253  					case EmuTurbo:
254  						Settings.TurboMode = FALSE;
255  						break;
256  					default:
257  						fprintf(stderr, "Unknown command %04x\n", cmd.command);
258  				}
259  			}
260  			else
261  			{
262  				switch (cmd.command)
263  				{
264  					case ExitEmu:
265  						S9xExit();
266  						break;
267  
268  					case Reset:
269  						S9xReset();
270  						break;
271  
272  					case SoftReset:
273  						S9xSoftReset();
274  						break;
275  
276  					case EmuTurbo:
277  						Settings.TurboMode = TRUE;
278  						break;
279  
280  					case ToggleEmuTurbo:
281  						Settings.TurboMode = !Settings.TurboMode;
282  						DisplayStateChange("Turbo mode", Settings.TurboMode);
283  						break;
284  
285  					case Debugger:
286  					#ifdef DEBUGGER
287  						CPU.Flags |= DEBUG_MODE_FLAG;
288  					#endif
289  						break;
290  
291  					case IncFrameRate:
292  						if (Settings.SkipFrames == AUTO_FRAMERATE)
293  							Settings.SkipFrames = 1;
294  						else
295  						if (Settings.SkipFrames < 10)
296  							Settings.SkipFrames++;
297  
298  						if (Settings.SkipFrames == AUTO_FRAMERATE)
299  							S9xMessage(S9X_INFO, 0, "Auto frame skip");
300  						else
301  						{
302  							sprintf(String, "Frame skip: %d", Settings.SkipFrames - 1);
303  							S9xMessage(S9X_INFO, 0, String);
304  						}
305  
306  						break;
307  
308  					case DecFrameRate:
309  						if (Settings.SkipFrames <= 1)
310  							Settings.SkipFrames = AUTO_FRAMERATE;
311  						else
312  						if (Settings.SkipFrames != AUTO_FRAMERATE)
313  							Settings.SkipFrames--;
314  
315  						if (Settings.SkipFrames == AUTO_FRAMERATE)
316  							S9xMessage(S9X_INFO, 0, "Auto frame skip");
317  						else
318  						{
319  							sprintf(String, "Frame skip: %d", Settings.SkipFrames - 1);
320  							S9xMessage(S9X_INFO, 0, String);
321  						}
322  
323  						break;
324  
325  					case LoadFreezeFile:
326  						// S9xUnfreezeGame(S9xChooseFilename(TRUE));
327  						break;
328  
329  					case SaveFreezeFile:
330  						// S9xFreezeGame(S9xChooseFilename(FALSE));
331  						break;
332  
333  					case Pause:
334  						Settings.Paused = !Settings.Paused;
335  						DisplayStateChange("Pause", Settings.Paused);
336  						break;
337  
338  					case SoundChannel0:
339  					case SoundChannel1:
340  					case SoundChannel2:
341  					case SoundChannel3:
342  					case SoundChannel4:
343  					case SoundChannel5:
344  					case SoundChannel6:
345  					case SoundChannel7:
346  						S9xToggleSoundChannel((int)cmd.command - SoundChannel0);
347  						sprintf(String, "Sound channel %d toggled", (int)cmd.command - SoundChannel0);
348  						S9xMessage(S9X_INFO, 0, String);
349  						break;
350  
351  					case SoundChannelsOn:
352  						S9xToggleSoundChannel(8);
353  						S9xMessage(S9X_INFO, 0, "All sound channels on");
354  						break;
355  
356  					case ToggleBG0:
357  						Settings.BG_Forced ^= 1;
358  						DisplayStateChange("BG#0", !(Settings.BG_Forced & 1));
359  						break;
360  
361  					case ToggleBG1:
362  						Settings.BG_Forced ^= 2;
363  						DisplayStateChange("BG#1", !(Settings.BG_Forced & 2));
364  						break;
365  
366  					case ToggleBG2:
367  						Settings.BG_Forced ^= 4;
368  						DisplayStateChange("BG#2", !(Settings.BG_Forced & 4));
369  						break;
370  
371  					case ToggleBG3:
372  						Settings.BG_Forced ^= 8;
373  						DisplayStateChange("BG#3", !(Settings.BG_Forced & 8));
374  						break;
375  
376  					case ToggleSprites:
377  						Settings.BG_Forced ^= 16;
378  						DisplayStateChange("Sprites", !(Settings.BG_Forced & 16));
379  						break;
380  
381  					case ToggleTransparency:
382  						Settings.Transparency = !Settings.Transparency;
383  						DisplayStateChange("Transparency effects", Settings.Transparency);
384  						break;
385  
386  					case LAST_COMMAND:
387  						break;
388  
389  					default:
390  						fprintf(stderr, "Unknown command %04x\n", cmd.command);
391  				}
392  			}
393  
394  			return;
395  
396  		default:
397  			fprintf(stderr, "WARNING: Unknown command type %d\n", cmd.type);
398  			return;
399  	}
400  }
401  
402  void S9xSetJoypadLatch (bool latch)
403  {
404  	if (latch && !FLAG_LATCH)
405  	{
406  		joypads[0].read_idx = 0;
407  		joypads[1].read_idx = 0;
408  	}
409  
410  	FLAG_LATCH = latch;
411  }
412  
413  uint8 S9xReadJOYSERn (int n)
414  {
415  	n &= 1;
416  
417  	int bits = (OpenBus & ~3) | ((n == 1) ? 0x1c : 0);
418  
419  	if (FLAG_LATCH)
420  	{
421  		return (bits | ((joypads[n].buttons >> 15) & 1));
422  	}
423  	else
424  	{
425  		if (joypads[n].read_idx < 16)
426  		{
427  			return (bits | ((joypads[n].buttons >> (15 - joypads[n].read_idx++)) & 1));
428  		}
429  		else
430  		{
431  			return (bits | 1);
432  		}
433  	}
434  }
435  
436  void S9xDoAutoJoypad (void)
437  {
438  	S9xSetJoypadLatch(1);
439  	S9xSetJoypadLatch(0);
440  
441  	for (int n = 0; n < 2; n++)
442  	{
443  		joypads[n].read_idx = 16;
444  		WRITE_WORD(Memory.CPU_IO + 0x218 + n * 2, joypads[n].buttons);
445  		WRITE_WORD(Memory.CPU_IO + 0x21c + n * 2, 0);
446  	}
447  }