Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Nintendo.cpp
- Committer:
- christopherjwang
- Date:
- 2015-12-06
- Revision:
- 0:7434770d9fc9
File content as of revision 0:7434770d9fc9:
///* //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 ////================================================================================ // //// it has this fucking global variable... ////Gamecube_ Gamecube; //// ////Gamecube_::Gamecube_(void) { //// ////} // //Gamecube_::Gamecube_(PinName _data_line) // :data_line(_data_line) //{ // //} // //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 */ // //void Gamecube_::gc_send(uint8_t* buff, uint8_t len) { // __disable_irq(); // uint8_t i; // data_line.output(); // for (i=0; i < len; i++) { // data_line.write(buff[i]); // wait_us(1); // } // __enable_irq(); //} /////** //// * 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; ////}