-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathsnes.c
217 lines (178 loc) · 5.04 KB
/
snes.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
/* Nes/Snes/N64/Gamecube to Wiimote
* Copyright (C) 2012 Raphaël Assénat
*
* Based on earlier work:
*
* Nes/Snes/Genesis/SMS/Atari to USB
* Copyright (C) 2006-2007 Raphaël Assénat
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* The author may be contacted at [email protected]
*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/pgmspace.h>
#include <string.h>
#include "gamepads.h"
#include "snes.h"
#define GAMEPAD_BYTES 2
/******** IO port definitions **************/
#define SNES_LATCH_DDR DDRC
#define SNES_LATCH_PORT PORTC
#define SNES_LATCH_BIT (1<<1)
#define SNES_CLOCK_DDR DDRC
#define SNES_CLOCK_PORT PORTC
#define SNES_CLOCK_BIT (1<<0)
#define SNES_DATA_PORT PORTC
#define SNES_DATA_DDR DDRC
#define SNES_DATA_PIN PINC
#define SNES_DATA_BIT (1<<2)
/********* IO port manipulation macros **********/
#define SNES_LATCH_LOW() do { SNES_LATCH_PORT &= ~(SNES_LATCH_BIT); } while(0)
#define SNES_LATCH_HIGH() do { SNES_LATCH_PORT |= SNES_LATCH_BIT; } while(0)
#define SNES_CLOCK_LOW() do { SNES_CLOCK_PORT &= ~(SNES_CLOCK_BIT); } while(0)
#define SNES_CLOCK_HIGH() do { SNES_CLOCK_PORT |= SNES_CLOCK_BIT; } while(0)
#define SNES_GET_DATA() (SNES_DATA_PIN & SNES_DATA_BIT)
/*********** prototypes *************/
static char snesInit(void);
static char snesUpdate(void);
// the most recent bytes we fetched from the controller
static unsigned char last_read_controller_bytes[GAMEPAD_BYTES];
// the most recently reported bytes
static unsigned char last_reported_controller_bytes[GAMEPAD_BYTES];
static char nes_mode = 0;
static char snesInit(void)
{
unsigned char sreg;
sreg = SREG;
cli();
// clock and latch as output
SNES_LATCH_DDR |= SNES_LATCH_BIT;
SNES_CLOCK_DDR |= SNES_CLOCK_BIT;
// data as input
SNES_DATA_DDR &= ~(SNES_DATA_BIT);
// enable pullup. This should prevent random toggling of pins
// when no controller is connected.
SNES_DATA_PORT |= SNES_DATA_BIT;
// clock is normally high
SNES_CLOCK_PORT |= SNES_CLOCK_BIT;
// LATCH is Active HIGH
SNES_LATCH_PORT &= ~(SNES_LATCH_BIT);
snesUpdate();
SREG = sreg;
return 0;
}
/*
*
Clock Cycle Button Reported
=========== ===============
1 B
2 Y
3 Select
4 Start
5 Up on joypad
6 Down on joypad
7 Left on joypad
8 Right on joypad
9 A
10 X
11 L
12 R
13 none (always high)
14 none (always high)
15 none (always high)
16 none (always high)
*
*/
static char snesUpdate(void)
{
int i;
unsigned char tmp=0;
SNES_LATCH_HIGH();
_delay_us(12);
SNES_LATCH_LOW();
for (i=0; i<8; i++)
{
_delay_us(6);
SNES_CLOCK_LOW();
tmp <<= 1;
if (!SNES_GET_DATA()) { tmp |= 0x01; }
_delay_us(6);
SNES_CLOCK_HIGH();
}
last_read_controller_bytes[0] = tmp;
for (i=0; i<8; i++)
{
_delay_us(6);
SNES_CLOCK_LOW();
tmp <<= 1;
if (!SNES_GET_DATA()) { tmp |= 0x01; }
_delay_us(6);
SNES_CLOCK_HIGH();
}
last_read_controller_bytes[1] = tmp;
return 0;
}
static char snesChanged(void)
{
return memcmp(last_read_controller_bytes,
last_reported_controller_bytes, GAMEPAD_BYTES);
}
static void snesGetReport(gamepad_data *dst)
{
unsigned char h, l;
if (dst != NULL)
{
l = last_read_controller_bytes[0];
h = last_read_controller_bytes[1];
// The 4 last bits are always high if an SNES controller
// is connected. With a NES controller, they are low.
// (High on the wire is a 0 here due to the snesUpdate() implementation)
//
if ((h & 0x0f) == 0x0f) {
nes_mode = 1;
} else {
nes_mode = 0;
}
if (nes_mode) {
// Nes controllers send the data in this order:
// A B Sel St U D L R
dst->nes.pad_type = PAD_TYPE_NES;
dst->nes.buttons = l;
dst->nes.raw_data[0] = l;
} else {
dst->nes.pad_type = PAD_TYPE_SNES;
dst->snes.buttons = l;
dst->snes.buttons |= h<<8;
dst->snes.raw_data[0] = l;
dst->snes.raw_data[1] = h;
}
}
memcpy(last_reported_controller_bytes,
last_read_controller_bytes,
GAMEPAD_BYTES);
}
static Gamepad SnesGamepad = {
.init = snesInit,
.update = snesUpdate,
.changed = snesChanged,
.getReport = snesGetReport
};
Gamepad *snesGetGamepad(void)
{
return &SnesGamepad;
}