forked from NicoHood/Nintendo
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathNintendo.cpp
More file actions
488 lines (418 loc) · 16.6 KB
/
Nintendo.cpp
File metadata and controls
488 lines (418 loc) · 16.6 KB
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
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
/*
Copyright (c) 2014-2015 NicoHood
See the readme for credit to other people.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "Nintendo.h"
//================================================================================
// Gamecube
//================================================================================
Gamecube_ Gamecube;
Gamecube_::Gamecube_(void){
// empty
}
bool Gamecube_::begin(const uint8_t pin)
{
// discard the information
Gamecube_Status_t status;
return begin(pin, status);
}
bool Gamecube_::begin(const uint8_t pin, Gamecube_Status_t &status)
{
// get the port mask and the pointers to the in/out/mode registers
uint8_t bitMask = digitalPinToBitMask(pin);
uint8_t port = digitalPinToPort(pin);
volatile uint8_t* modePort = portModeRegister(port);
volatile uint8_t* outPort = portOutputRegister(port);
volatile uint8_t* inPort = portInputRegister(port);
// Initialize the gamecube controller by sending it a null byte.
// This is unnecessary for a standard controller, but is required for the
// Wavebird.
uint8_t command[] = { 0x00 };
// don't want interrupts getting in the way
uint8_t oldSREG = SREG;
cli();
// send the command
gc_send(command, sizeof(command), modePort, outPort, bitMask);
// read in data
uint8_t receivedBytes = gc_get((uint8_t*)&status, sizeof(status), modePort, outPort, inPort, bitMask);
// end of time sensitive code
SREG = oldSREG;
// return status information for optional use
bool newinput;
if (receivedBytes == sizeof(status)){
// switch the first two bytes to compare it easy with the documentation
uint8_t temp = status.whole8[0];
status.whole8[0] = status.whole8[1];
status.whole8[1] = temp;
newinput = true;
}
else
newinput = false;
return newinput;
}
bool Gamecube_::end(const uint8_t pin){
// Turns off rumble by sending a normal reading request
// and discards the information
Gamecube_Data_t report;
return read(pin, report, false);
}
bool Gamecube_::read(const uint8_t pin, Gamecube_Data_t &report, const bool rumble)
{
// get the port mask and the pointers to the in/out/mode registers
uint8_t bitMask = digitalPinToBitMask(pin);
uint8_t port = digitalPinToPort(pin);
volatile uint8_t* modePort = portModeRegister(port);
volatile uint8_t* outPort = portOutputRegister(port);
volatile uint8_t* inPort = portInputRegister(port);
// command to send to the gamecube, LSB is rumble
uint8_t command[] = { 0x40, 0x03, rumble & 0x01 };
// don't want interrupts getting in the way
uint8_t oldSREG = SREG;
cli();
// send the command
gc_send(command, sizeof(command), modePort, outPort, bitMask);
// read in new data
uint8_t receivedBytes = gc_get((uint8_t*)&report, sizeof(report), modePort, outPort, inPort, bitMask);
// end of time sensitive code
SREG = oldSREG;
// return status information for optional use
bool newinput;
if (receivedBytes == sizeof(report))
newinput = true;
else
newinput = false;
return newinput;
}
//================================================================================
// Gamecube/N64 i/o functions
//================================================================================
// nop definitions, placed here so the header/user
// doesnt see/use this because it is %[nop] specific
/*
Serial.begin(115200);
for (int n = 0; n < 100; n++) {
Serial.print("#define nopn");
Serial.print(n);
Serial.print(" nopn");
Serial.println(n % 3);
}
*/
#define nopManual(n) nopn ## n
#define nopn0 // (0 % 3)
#define nopn1 "nop\n" // (1 % 3)
#define nopn2 "nop\nnop\n" // (2 % 3)
#define nopn3 nopn0 // (3 % 3)
#define nopn4 nopn1 //..
#define nopn5 nopn2
#define nopn6 nopn0
#define nopn7 nopn1
#define nopn8 nopn2
#define nopn9 nopn0
#define nopn10 nopn1
#define nopn11 nopn2
#define nopn12 nopn0
#define nopn13 nopn1
#define nopn14 nopn2
#define nopn15 nopn0
#define nopn16 nopn1
#define nopn17 nopn2
#define nopn18 nopn0
#define nopn19 nopn1
#define nopn20 nopn2
#define nopn21 nopn0
#define nopn22 nopn1
#define nopn23 nopn2
#define nopn24 nopn0
#define nopn25 nopn1
#define nopn26 nopn2
#define nopn27 nopn0
#define nopn28 nopn1
#define nopn29 nopn2
#define nopn30 nopn0
#define nopn31 nopn1
#define nopn32 nopn2
#define nopn33 nopn0
#define nopn34 nopn1
#define nopn35 nopn2
#define nopn36 nopn0
#define nopn37 nopn1
#define nopn38 nopn2
#define nopn39 nopn0
#define nopn40 nopn1
#define nopn41 nopn2
#define nopn42 nopn0
#define nopn43 nopn1
#define nopn44 nopn2
#define nopn45 nopn0
#define nopn46 nopn1
#define nopn47 nopn2
#define nopn48 nopn0
#define nopn49 nopn1
#define nopn50 nopn2
#define nopn51 nopn0
#define nopn52 nopn1
#define nopn53 nopn2
#define nopn54 nopn0
#define nopn55 nopn1
#define nopn56 nopn2
#define nopn57 nopn0
#define nopn58 nopn1
#define nopn59 nopn2
#define nopn60 nopn0
#define nopn61 nopn1
#define nopn62 nopn2
#define nopn63 nopn0
#define nopn64 nopn1
#define nopn65 nopn2
#define nopn66 nopn0
#define nopn67 nopn1
#define nopn68 nopn2
#define nopn69 nopn0
#define nopn70 nopn1
#define nopn71 nopn2
#define nopn72 nopn0
#define nopn73 nopn1
#define nopn74 nopn2
#define nopn75 nopn0
#define nopn76 nopn1
#define nopn77 nopn2
#define nopn78 nopn0
#define nopn79 nopn1
#define nopn80 nopn2
#define nopn81 nopn0
#define nopn82 nopn1
#define nopn83 nopn2
#define nopn84 nopn0
#define nopn85 nopn1
#define nopn86 nopn2
#define nopn87 nopn0
#define nopn88 nopn1
#define nopn89 nopn2
#define nopn90 nopn0
#define nopn91 nopn1
#define nopn92 nopn2
#define nopn93 nopn0
#define nopn94 nopn1
#define nopn95 nopn2
#define nopn96 nopn0
#define nopn97 nopn1
#define nopn98 nopn2
#define nopn99 nopn0
#define nop_reg "%[nop]" // in this sketch we named the register like this
#define nop_block(id, N) /* nops have to be >=3 in order to work*/ \
"ldi " nop_reg ", (" #N "/3)\n" /* (1) ldi, start */ \
".L%=_nop_loop" #id ":\n" /* + ((N-1) * (1) dec + (2) brne), (N-1) loops */ \
"dec " nop_reg "\n" /* + (1) dec + (1) brne, last loop */ \
"brne .L%=_nop_loop" #id "\n" /* --> (N * 3) nops */ \
nopManual(N) /* N % 3 manual nops */
/**
* This sends the given byte sequence to the controller
* length must be at least 1
*/
void gc_send(uint8_t* buff, uint8_t len,
volatile uint8_t* modePort, volatile uint8_t* outPort, uint8_t bitMask)
{
// set pin to output, default high
*outPort |= bitMask;
*modePort |= bitMask;
// temporary register values used as "clobbers"
register uint8_t bitCount;
register uint8_t data;
register uint8_t nop;
asm volatile (
"; Start of gc_send assembly\n"
// passed in to this block are:
// the %a[buff] register is the buffer pointer
// %[len] is the register holding the length of the buffer in bytes
// Instruction cycles are noted in parentheses
// branch instructions have two values, one if the branch isn't
// taken and one if it is
// %[data] will be the current buffer byte loaded from memory
// %[bitCount] will be the bit counter for the current byte. when this
// reaches 0, we need to decrement the length counter, load
// the next buffer byte, and loop. (if the length counter becomes
// 0, that's our exit condition)
// This label starts the outer loop, which sends a single byte
".L%=_byte_loop:\n"
"ld %[data], %a[buff]+\n" // (2) load the next byte and increment byte pointer
"ldi %[bitCount],0x08\n" // (1) set bitcount to 8 bits
// This label starts the inner loop, which sends a single bit
".L%=_bit_loop:\n"
"st %a[outPort],%[low]\n" // (2) pull the line low
// line needs to stay low for 1µs for a 1 bit, 3µs for a 0 bit
// this block figures out if the next bit is a 0 or a 1
// the strategy here is to shift the register left, then test and
// branch on the carry flag
"lsl %[data]\n" // (1) shift left. MSB goes into carry bit of status reg
"brcc .L%=_zero_bit\n" // (1/2) branch if carry is cleared
// this block is the timing for a 1 bit (1µs low, 3µs high)
// Stay low for 2uS: 16 - 2 (above lsl,brcc) - 2 (below st) = 12 cycles
nop_block(1, 12) // nop block 1, 12 cycles
"st %a[outPort],%[high]\n" // (2) set the line high again
// Now stay high for 2µs of the 3µs to sync up with the branch below
// 2*16 - 2 (for the rjmp) = 30 cycles
nop_block(2, 30) // nop block 2, 30 cycles
"rjmp .L%=_finish_bit\n" // (2)
// this block is the timing for a 0 bit (3µs low, 1µs high)
// Need to go high in 3*16 - 3 (above lsl,brcc) - 2 (below st) = 43 cycles
".L%=_zero_bit:\n"
nop_block(3, 43) // nop block 3, 43 cycles
"st %a[outPort],%[high]\n" // (2) set the line high again
// The two branches meet up here.
// We are now *exactly* 3µs into the sending of a bit, and the line
// is high again. We have 1µs to do the looping and iteration
// logic.
".L%=_finish_bit:\n"
"dec %[bitCount]\n" // (1) subtract 1 from our bit counter
"breq .L%=_load_next_byte\n" // (1/2) branch if we've sent all the bits of this byte
// At this point, we have more bits to send in this byte, but the
// line must remain high for another 1µs (minus the above
// instructions and the jump below and the st instruction at the
// top of the loop)
// 16 - 2(above) - 2 (rjmp below) - 2 (st after jump) = 10
nop_block(4, 10) // nop block 4, 10 cycles
"rjmp .L%=_bit_loop\n" // (2)
// This block starts 3 cycles into the last 1µs of the line being high
// We need to decrement the byte counter. If it's 0, that's our exit condition.
// If not we need to load the next byte and go to the top of the byte loop
".L%=_load_next_byte:\n"
"dec %[len]\n" // (1) len--
"breq .L%=_loop_exit\n" // (1/2) if the byte counter is 0, exit
// delay block:
// needs to go high after 1µs or 16 cycles
// 16 - 5 (above) - 2 (the jump itself) - 5 (after jump) = 4
nop_block(5, 4) // nop block 5, 4 cycles
"rjmp .L%=_byte_loop\n" // (2)
// Loop exit
".L%=_loop_exit:\n"
// final task: send the stop bit, which is a 1 (1µs low 3µs high)
// the line goes low in:
// 16 - 6 (above since line went high) - 2 (st instruction below) = 8 cycles
nop_block(6, 8) // nop block 6, 8 cycles
"st %a[outPort],%[low]\n" // (2) pull the line low
// stay low for 1µs
// 16 - 2 (below st) = 14
nop_block(7, 14) // nop block 7, 14 cycles
"st %a[outPort],%[high]\n" // (2) set the line high again
// just stay high. no need to wait 3µs before returning
// ----------
// outputs:
: [buff] "+e" (buff), // (read and write)
[outPort] "+e" (outPort), // (read and write)
[bitCount] "=&d" (bitCount), // (output only, ldi needs the upper registers)
[data] "=&r" (data), // (output only)
[nop] "=&d" (nop) // (output only, ldi needs the upper registers)
// inputs:
: [len] "r" (len),
[high] "r" (*outPort | bitMask), // precalculate new pin states
[low] "r" (*outPort & ~bitMask) // this works because we turn interrupts off
// no clobbers
); // end of asm volatile
}
/**
* Read bytes from the gamecube controller
* listen for the expected bytes of data back from the controller and
* and pack it into the buff
*/
uint8_t gc_get(uint8_t* buff, uint8_t len,
volatile uint8_t* modePort, volatile uint8_t* outPort, volatile uint8_t * inPort, uint8_t bitMask)
{
// prepare pin for input with pullup
*modePort &= ~bitMask;
*outPort |= bitMask;
// temporary register values used as "clobbers"
register uint8_t timeoutCount; // counts down the timeout
register uint8_t bitCount; // counts down 8 bits for each byte
register uint8_t inputVal; // temporary variable to save the pin states
register uint8_t data; // keeps the temporary received data byte
register uint8_t receivedBytes; // the return value of the function
asm volatile (
"; Start of gc_get assembly\n"
// [bitCount] is our bit counter. We read %[len] bytes
// and increment the byte pointer and receivedBytes every 8 bits
"ldi %[bitCount],0x08\n" // (1) set bitcount to 8 bits
"ldi %[receivedBytes],0x00\n" // (1) default exit value is 0 bytes received
// This first spinloop waits for the line to go low.
// It loops 64 times before it gives up and returns
".L%=_wait_for_low:\n"
"ldi %[timeoutCount],%[timeout]\n" // (1) set the timeout
".L%=_wait_for_low_loop:\n" // 7 cycles if loop fails
"ld %[inputVal], %a[inPort]\n" // (2) read the pin (happens before the 2 cycles)
"and %[inputVal], %[bitMask]\n" // (1) compare pinstate with bitmask
"breq .L%=_wait_for_measure\n" // (1/2) jump to the measure part if pin is low
// the following happens if the line is still high
"dec %[timeoutCount]\n" // (1) decrease timeout by 1
"brne .L%=_wait_for_low_loop\n" // (1/2) loop if the counter isn't 0
"rjmp .L%=_exit\n" // (2) timeout, jump to the end
// Next block. The line has just gone low. Wait approx 2µs
// each cycle is 1/16 µs on a 16Mhz processor
// best case: 32 - 5 (above) - 1 (below) = 26 nops
// worst case: 32 - 5 (above) - 6 (above, worst case) - 1 (below) = 20 nops
// --> 23 nops
".L%=_wait_for_measure:\n"
// nop block, 23 cycles, use inputVal as temporary reg since we dont need it right now
"ldi %[inputVal], (23/3)\n" /* (1) ldi, start */
".L%=_nop_loop1:\n" /* + ((N-1) * (1) dec + (2) brne), (N-1) loops */
"dec %[inputVal]\n" /* + (1) dec + (1) brne, last loop */
"brne .L%=_nop_loop1\n" /* --> (N * 3) nops */
nopManual(2) /* 23 % 3 manual nops */
// save the data
"lsl %[data]\n" // (1) left shift the current byte in %[data]
"ld %[inputVal], %a[inPort]\n" // (2) read the pin (happens before the 2 cycles)
"and %[inputVal], %[bitMask]\n" // (1) compare pinstate with bitmask
"breq .L%=_check_bit_count\n" // (1/2) skip setting data to 1 if pin is low
"sbr %[data],0x01\n" // set bit 1 in %[data] if pin is high
".L%=_check_bit_count:\n"
"dec %[bitCount]\n" // (1) decrement 1 from our bit counter
"brne .L%=_wait_for_high\n" // (1/2) branch if we've not received the whole byte
// we received a full byte
"st %a[buff]+,%[data]\n" // (2) save %[data] back to memory and increment byte pointer
"inc %[receivedBytes]\n" // (1) increase byte count
"ldi %[bitCount],0x08\n" // (1) set bitcount to 8 bits again
"cp %[len],%[receivedBytes]\n" // (1) %[len] == %[receivedBytes] ?
"breq .L%=_exit\n" // (1/2) jump to exit if we received all bytes
// dont wait for line to go high again
// This next block waits for the line to go high again.
// again, it sets a timeout counter of 64 iterations
".L%=_wait_for_high:\n"
"ldi %[timeoutCount],%[timeout]\n" // (1) set the timeout
".L%=_wait_for_high_loop:\n" // 7 cycles if loop fails
"ld %[inputVal], %a[inPort]\n" // (2) read the pin (happens before the 2 cycles)
"and %[inputVal], %[bitMask]\n" // (1) compare pinstate with bitmask
"brne .L%=_wait_for_low\n" // (1/2) line is high. ready for next loop
// the following happens if the line is still low
"dec %[timeoutCount]\n" // (1) decrease timeout by 1
"brne .L%=_wait_for_high_loop\n" // (1/2) loop if the counter isn't 0
// timeout, exit now
".L%=_exit:\n"
// ----------
// outputs:
: [receivedBytes] "=&d" (receivedBytes), // (ldi needs the upper registers)
[buff] "+e" (buff), // (read and write)
[bitCount] "=&d" (bitCount), // (output only, ldi needs the upper registers)
[timeoutCount] "=&r" (timeoutCount), // (output only)
[inputVal] "=&r" (inputVal), // (output only)
[data] "=&r" (data) // (output only)
// inputs
: [len] "r" (len),
[inPort] "e" (inPort),
[bitMask] "r" (bitMask),
[timeout] "M" (NINTENDO_GAMECUBE_N64_TIMEOUT) // constant
); // end of asm volatile
return receivedBytes;
}