├── JumperJ1.jpg ├── GC-Schematic.png ├── ESS-map-graphs.png ├── retrospy-dont-filter.png ├── serial-monitor-example.PNG ├── n64-controller-button-map.png ├── ESS-Adapter ├── src │ └── Nintendo │ │ ├── header.jpg │ │ ├── extras │ │ ├── plug.png │ │ ├── Gamecube.fzz │ │ ├── gamecube_connector_socket_labels.jpg │ │ ├── Nintendo 64 Controller Information_files │ │ │ ├── n64-n.gif │ │ │ ├── N64Cmd0x00.gif │ │ │ ├── N64Cmd0x01.gif │ │ │ ├── litechip.jpg │ │ │ ├── n64wireframe.gif │ │ │ ├── n64controller.gif │ │ │ └── N64ControllerBits.gif │ │ ├── Nintendo Gamecube Controller Pinout-Dateien │ │ │ ├── gcbits.jpg │ │ │ ├── gcprobe.jpg │ │ │ ├── timing.gif │ │ │ ├── gc-socket1.gif │ │ │ └── gcdata-small.jpg │ │ ├── objdump.bat │ │ ├── Yet Another Gamecube Documentation-Dateien │ │ │ └── techdoc.css │ │ ├── Nintendo 64 Controller Information.htm │ │ └── Nintendo Gamecube Controller Pinout.htm │ │ ├── .gitmodules │ │ ├── library.properties │ │ ├── LICENSE │ │ ├── examples │ │ ├── Gamecube │ │ │ ├── GamecubeConsole │ │ │ │ └── GamecubeConsole.ino │ │ │ ├── Gamecube_USB_HID │ │ │ │ └── Gamecube_USB_HID.ino │ │ │ ├── GamecubeMultiSingleplayer │ │ │ │ └── GamecubeMultiSingleplayer.ino │ │ │ └── GamecubeController │ │ │ │ └── GamecubeController.ino │ │ └── N64 │ │ │ └── N64Controller │ │ │ └── N64Controller.ino │ │ ├── src │ │ ├── Nintendo.h │ │ ├── N64API.h │ │ ├── GamecubeAPI.h │ │ ├── Gamecube_N64.h │ │ ├── N64.h │ │ ├── N64.c │ │ ├── Gamecube.h │ │ ├── N64API.hpp │ │ ├── Gamecube.c │ │ ├── GamecubeAPI.hpp │ │ └── Gamecube_N64.c │ │ ├── CHANGELOG.md │ │ ├── keywords.txt │ │ └── Readme.md ├── input-display.hpp ├── ESS.hpp ├── extra.hpp ├── input-display.cpp ├── ESS-Adapter.ino ├── extra.cpp └── ESS.cpp ├── README.md └── generate-map.py /JumperJ1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skuzee/ESS-Adapter/HEAD/JumperJ1.jpg -------------------------------------------------------------------------------- /GC-Schematic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skuzee/ESS-Adapter/HEAD/GC-Schematic.png -------------------------------------------------------------------------------- /ESS-map-graphs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skuzee/ESS-Adapter/HEAD/ESS-map-graphs.png -------------------------------------------------------------------------------- /retrospy-dont-filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skuzee/ESS-Adapter/HEAD/retrospy-dont-filter.png -------------------------------------------------------------------------------- /serial-monitor-example.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skuzee/ESS-Adapter/HEAD/serial-monitor-example.PNG -------------------------------------------------------------------------------- /n64-controller-button-map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skuzee/ESS-Adapter/HEAD/n64-controller-button-map.png -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/header.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skuzee/ESS-Adapter/HEAD/ESS-Adapter/src/Nintendo/header.jpg -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/extras/plug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skuzee/ESS-Adapter/HEAD/ESS-Adapter/src/Nintendo/extras/plug.png -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/extras/Gamecube.fzz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skuzee/ESS-Adapter/HEAD/ESS-Adapter/src/Nintendo/extras/Gamecube.fzz -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Nintendo.wiki"] 2 | path = Nintendo.wiki 3 | url = https://github.com/NicoHood/Nintendo.wiki.git 4 | -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/extras/gamecube_connector_socket_labels.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skuzee/ESS-Adapter/HEAD/ESS-Adapter/src/Nintendo/extras/gamecube_connector_socket_labels.jpg -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/extras/Nintendo 64 Controller Information_files/n64-n.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skuzee/ESS-Adapter/HEAD/ESS-Adapter/src/Nintendo/extras/Nintendo 64 Controller Information_files/n64-n.gif -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/extras/Nintendo 64 Controller Information_files/N64Cmd0x00.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skuzee/ESS-Adapter/HEAD/ESS-Adapter/src/Nintendo/extras/Nintendo 64 Controller Information_files/N64Cmd0x00.gif -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/extras/Nintendo 64 Controller Information_files/N64Cmd0x01.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skuzee/ESS-Adapter/HEAD/ESS-Adapter/src/Nintendo/extras/Nintendo 64 Controller Information_files/N64Cmd0x01.gif -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/extras/Nintendo 64 Controller Information_files/litechip.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skuzee/ESS-Adapter/HEAD/ESS-Adapter/src/Nintendo/extras/Nintendo 64 Controller Information_files/litechip.jpg -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/extras/Nintendo Gamecube Controller Pinout-Dateien/gcbits.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skuzee/ESS-Adapter/HEAD/ESS-Adapter/src/Nintendo/extras/Nintendo Gamecube Controller Pinout-Dateien/gcbits.jpg -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/extras/Nintendo Gamecube Controller Pinout-Dateien/gcprobe.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skuzee/ESS-Adapter/HEAD/ESS-Adapter/src/Nintendo/extras/Nintendo Gamecube Controller Pinout-Dateien/gcprobe.jpg -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/extras/Nintendo Gamecube Controller Pinout-Dateien/timing.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skuzee/ESS-Adapter/HEAD/ESS-Adapter/src/Nintendo/extras/Nintendo Gamecube Controller Pinout-Dateien/timing.gif -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/extras/Nintendo 64 Controller Information_files/n64wireframe.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skuzee/ESS-Adapter/HEAD/ESS-Adapter/src/Nintendo/extras/Nintendo 64 Controller Information_files/n64wireframe.gif -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/extras/Nintendo 64 Controller Information_files/n64controller.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skuzee/ESS-Adapter/HEAD/ESS-Adapter/src/Nintendo/extras/Nintendo 64 Controller Information_files/n64controller.gif -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/extras/Nintendo Gamecube Controller Pinout-Dateien/gc-socket1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skuzee/ESS-Adapter/HEAD/ESS-Adapter/src/Nintendo/extras/Nintendo Gamecube Controller Pinout-Dateien/gc-socket1.gif -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/extras/Nintendo Gamecube Controller Pinout-Dateien/gcdata-small.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skuzee/ESS-Adapter/HEAD/ESS-Adapter/src/Nintendo/extras/Nintendo Gamecube Controller Pinout-Dateien/gcdata-small.jpg -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/extras/Nintendo 64 Controller Information_files/N64ControllerBits.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skuzee/ESS-Adapter/HEAD/ESS-Adapter/src/Nintendo/extras/Nintendo 64 Controller Information_files/N64ControllerBits.gif -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/library.properties: -------------------------------------------------------------------------------- 1 | name=Nintendo 2 | version=1.3.0 3 | author=NicoHood 4 | maintainer=NicoHood 5 | sentence=Connect all your favourite Nintendo Controllers to your Arduino. 6 | paragraph=Currently supported: Gamecube, N64. Carefully read the wiki pages to know how to setup the controller connection. 7 | category=Signal Input/Output 8 | url=https://github.com/NicoHood/Nintendo 9 | architectures=avr 10 | -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/extras/objdump.bat: -------------------------------------------------------------------------------- 1 | @ echo off 2 | REM Program to output the elf file from an Arduino sketch 3 | title Get objdump from elf 4 | 5 | echo Please enter the path of the temp .cpp.elf file 6 | set /p elfpath= 7 | echo. 8 | 9 | set idepath=C:\Users\Nico\Documents\arduino-1.6.3 10 | set destpath=C:\Arduino.txt 11 | set command=%idepath%\hardware\tools\avr\bin\avr-objdump -S %elfpath% ^> %destpath% 12 | 13 | cd %idepath% 14 | 15 | :disassemble 16 | echo %command% 17 | echo. 18 | %command% 19 | echo. 20 | 21 | pause 22 | 23 | goto disassemble -------------------------------------------------------------------------------- /ESS-Adapter/input-display.hpp: -------------------------------------------------------------------------------- 1 | //input-display.hpp 2 | 3 | //The Following code is released under GNU GENERAL PUBLIC LICENSE Version 3 and uses a nicohood's nintendo library released under MIT license. 4 | 5 | #pragma once 6 | #include 7 | #include "src/Nintendo/src/Nintendo.h" 8 | 9 | #define ASCII_0 '\0' 10 | #define ASCII_1 '1' 11 | #define NEWLINE '\n' 12 | 13 | void writeToUSB_BYTE(Gamecube_Data_t& data); 14 | 15 | void writeToUSB_BYTE(N64_Report_t& report); 16 | 17 | void writeToUSB_BIT(Gamecube_Report_t& GC_report); 18 | 19 | void tryPrint(String input); 20 | 21 | void tryPrintln(String input); 22 | 23 | void tryPrint(uint8_t input); 24 | 25 | void tryPrintln(uint8_t input); 26 | 27 | void checkSerialBufferFull(); 28 | -------------------------------------------------------------------------------- /ESS-Adapter/ESS.hpp: -------------------------------------------------------------------------------- 1 | //ESS.hpp 2 | 3 | //The Following code is released under GNU GENERAL PUBLIC LICENSE Version 3 and uses a nicohood's nintendo library released under MIT license. 4 | 5 | #pragma once 6 | #include 7 | #include "src/Nintendo/src/Nintendo.h" 8 | 9 | # define OOT_MAX 80 10 | # define BOUNDARY 39 11 | 12 | extern const PROGMEM char one_dimensional_map[]; 13 | 14 | extern const PROGMEM char triangular_map[]; 15 | 16 | void gc_to_n64(uint8_t coords[2]); 17 | 18 | uint16_t triangular_to_linear_index(uint8_t row, uint8_t col, uint8_t size); 19 | 20 | void invert_vc(uint8_t coords[2]); 21 | 22 | void invert_vc_gc(uint8_t coords[2]); 23 | 24 | void invert_vc_n64(int8_t coords[2], uint8_t ucoords[2]); 25 | 26 | void normalize_origin(uint8_t coords[2], uint8_t origin[2]); 27 | 28 | void N64toGC_buttonMap_Generic(const N64_Report_t& N64report, Gamecube_Report_t& GCreport); 29 | 30 | void N64toGC_buttonMap_OOT(const N64_Report_t& N64report, Gamecube_Report_t& GC_report); 31 | 32 | void N64toGC_buttonMap_Yoshi(const N64_Report_t& N64report, Gamecube_Report_t& GC_report); 33 | -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2021 NicoHood 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 14 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 15 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 16 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 19 | OR OTHER DEALINGS IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/examples/Gamecube/GamecubeConsole/GamecubeConsole.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2016 NicoHood 3 | See the readme for credit to other people. 4 | 5 | GamecubeConsole example 6 | Read controller and mirror data to the gamecube 7 | */ 8 | 9 | #include "Nintendo.h" 10 | 11 | // Define a Gamecube Controller and a Console 12 | CGamecubeController GamecubeController1(7); 13 | CGamecubeConsole GamecubeConsole1(8); 14 | 15 | // Pin definitions 16 | #define pinLed LED_BUILTIN 17 | 18 | void setup() 19 | { 20 | // Set up debug led 21 | pinMode(pinLed, OUTPUT); 22 | 23 | // Start debug serial 24 | Serial.begin(115200); 25 | } 26 | 27 | 28 | void loop() 29 | { 30 | // Try to read the controller data 31 | if (GamecubeController1.read()) 32 | { 33 | // Mirror the controller data to the console 34 | if (!GamecubeConsole1.write(GamecubeController1)) 35 | { 36 | Serial.println(F("Error writing Gamecube controller.")); 37 | digitalWrite(pinLed, HIGH); 38 | delay(1000); 39 | } 40 | } 41 | else 42 | { 43 | Serial.println(F("Error reading Gamecube controller.")); 44 | digitalWrite(pinLed, HIGH); 45 | delay(1000); 46 | } 47 | digitalWrite(pinLed, LOW); 48 | } 49 | -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/src/Nintendo.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2016 NicoHood 3 | See the readme for credit to other people. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | */ 23 | 24 | // Include guard 25 | #pragma once 26 | 27 | // Software version 28 | #define NINTENDO_VERSION 1337 29 | 30 | #include 31 | 32 | #if (F_CPU != 16000000) 33 | #error This library only supports 16MHz AVRs 34 | #endif 35 | 36 | //================================================================================ 37 | // Nintendo 38 | //================================================================================ 39 | 40 | // include all library parts 41 | #include "GamecubeAPI.h" 42 | #include "N64API.h" 43 | -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | This changlog uses the [ISO 8601 date format](https://www.iso.org/iso-8601-date-and-time-format.html) of (YYYY-MM-DD). 7 | 8 | ## [Unreleased] 9 | 10 | ## [1.3.0] - 2021-01-22 11 | 12 | ### Added 13 | 14 | * Added N64 Console support #29 (Thanks @JonnyHaystack) 15 | 16 | ## [1.2.1] - 2017-01-27 17 | 18 | ### Fixed 19 | 20 | * Fix compiling of assembler functions 21 | 22 | ## 1.2 - 2016-07-19 23 | 24 | ### Added 25 | 26 | * Added N64Controller support 27 | * Added GamecubeConsole support 28 | * Added library.properties 29 | * Added version identifier 30 | 31 | ### Changed 32 | 33 | * Reworked library structure 34 | * Separated consoles 35 | 36 | ### Fixed 37 | 38 | * Untabified all source files 39 | 40 | ## [1.1] - 2015-02-14 41 | 42 | ### Added 43 | 44 | * Multiple controllers possible 45 | * Added new begin() function overload 46 | 47 | ### Changed 48 | 49 | * Hardcoded all get/send functions with assembler 50 | * Improved pin management/ram usage 51 | * Improved end() function 52 | * Improved example 53 | * Updated HID Project example to version 2.1 54 | * Updated structs 55 | * Moved documentation to the Wiki 56 | 57 | ### Fixed 58 | 59 | * Now with all IDE versions compatible 60 | 61 | ## [1.0] - 2014-07-01 62 | 63 | ### Added 64 | * Added Gamecube Device support 65 | 66 | 67 | [Unreleased]: https://github.com/NicoHood/Nintendo/compare/1.3.0...HEAD 68 | [1.3.0]: https://github.com/NicoHood/Nintendo/compare/1.2.1...1.3.0 69 | [1.2.1]: https://github.com/NicoHood/Nintendo/releases/tag/1.2.1 70 | [1.1]: https://github.com/NicoHood/Nintendo/compare/1.0...1.1 71 | [1.0]: https://github.com/NicoHood/Nintendo/releases/tag/1.0 72 | -------------------------------------------------------------------------------- /ESS-Adapter/extra.hpp: -------------------------------------------------------------------------------- 1 | //extra.hpp 2 | 3 | //The Following code is released under GNU GENERAL PUBLIC LICENSE Version 3 and uses a nicohood's nintendo library released under MIT license. 4 | 5 | #pragma once 6 | #include 7 | #include "src/Nintendo/src/Nintendo.h" 8 | 9 | #define MENU_BUTTON_TIMEOUT 300 10 | #define EEPROM_VERSION 2 11 | 12 | // Define output pins for debug timing with Logic Analyzer. 13 | #define DEBUG_READ 15 14 | #define DEBUG_ESS 14 15 | #define DEBUG_INPUT 16 16 | #define DEBUG_WRITE 10 17 | 18 | //LED Indicator pins 19 | //#define COMMON_ANODE 20 | #define COMMON_CATHODE 21 | #define LED1_PIN_R 10 22 | #define LED1_PIN_G 16 23 | #define LED1_PIN_B 14 24 | #define LED2_PIN_R 15 25 | #define LED2_PIN_G 18 // A0 26 | #define LED2_PIN_B 19 // A1 27 | 28 | #ifdef COMMON_ANODE 29 | #define LED_ON LOW 30 | #define LED_OFF HIGH 31 | #endif 32 | 33 | #ifdef COMMON_CATHODE 34 | #define LED_ON HIGH 35 | #define LED_OFF LOW 36 | #endif 37 | 38 | #define GAME_OOT 0 39 | #define GAME_YOSHI 1 40 | #define GAME_GENERIC 2 41 | 42 | #define ESS_OFF 0 43 | #define ESS_ON 1 44 | 45 | #define INPUT_DISPLAY_OFF 0 46 | #define INPUT_DISPLAY_ON 1 47 | 48 | #define TRIGGER_THRESHOLD_OFF 0 49 | #define TRIGGER_THRESHOLD_ON 1 50 | #define DEF_TRIGGER_THRESHOLD 100 51 | 52 | typedef struct { 53 | uint8_t input_display_enabled : 1; 54 | uint8_t game_selection : 3; 55 | uint8_t ess_map : 3; 56 | uint8_t trigger_threshold_enabled : 1; 57 | uint8_t trigger_threshold; 58 | } EEPROM_settings; 59 | 60 | extern EEPROM_settings settings; 61 | extern CGamecubeController GCcontroller; 62 | 63 | void softReset(); 64 | 65 | void analogTriggerToDigitalPress(Gamecube_Report_t& GCreport); 66 | 67 | //void blinkLED(uint8_t blinks, uint8_t blinkTime); 68 | 69 | uint8_t enterSettingsMenuN64Controller(const N64_Report_t& N64report); 70 | 71 | uint8_t changeSettings(Gamecube_Report_t& GCreport); 72 | 73 | void loadSettings(); 74 | 75 | void printSetting(); 76 | 77 | void initializeDebug(); 78 | 79 | void initilizeStatusLights(); 80 | 81 | void IndicatorLights(uint8_t LEDNumber, uint8_t LEDcolor); 82 | 83 | void rumbleMotor(uint16_t duration, uint16_t pause, uint8_t pulses); 84 | -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For Nintendo 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | ####################################### 10 | # Methods and Functions (KEYWORD2) 11 | ####################################### 12 | 13 | GamecubeController1 KEYWORD1 14 | GamecubeController2 KEYWORD1 15 | GamecubeController3 KEYWORD1 16 | GamecubeController4 KEYWORD1 17 | GamecubeConsole1 KEYWORD1 18 | GamecubeConsole2 KEYWORD1 19 | GamecubeConsole3 KEYWORD1 20 | GamecubeConsole4 KEYWORD1 21 | N64 KEYWORD1 22 | 23 | gc_n64_send KEYWORD2 24 | gc_n64_get KEYWORD2 25 | gc_n64_send_get KEYWORD2 26 | 27 | gc_init KEYWORD2 28 | gc_origin KEYWORD2 29 | gc_read KEYWORD2 30 | gc_write KEYWORD2 31 | 32 | begin KEYWORD2 33 | end KEYWORD2 34 | read KEYWORD2 35 | write KEYWORD2 36 | reset KEYWORD2 37 | getDevice KEYWORD2 38 | connected KEYWORD2 39 | getRumble KEYWORD2 40 | setRumble KEYWORD2 41 | getStatus KEYWORD2 42 | getOrigin KEYWORD2 43 | getReport KEYWORD2 44 | getData KEYWORD2 45 | 46 | buttons0 KEYWORD2 47 | buttons1 KEYWORD2 48 | xAxis KEYWORD2 49 | yAxis KEYWORD2 50 | cxAxis KEYWORD2 51 | cyAxis KEYWORD2 52 | 53 | ####################################### 54 | # Instances (KEYWORD2) 55 | ####################################### 56 | 57 | CGamecubeController KEYWORD2 58 | CGamecubeConsole KEYWORD2 59 | 60 | Gamecube_Status_t KEYWORD2 61 | Gamecube_Origin_t KEYWORD2 62 | Gamecube_Report_t KEYWORD2 63 | Gamecube_Data_t KEYWORD2 64 | 65 | 66 | N64_Status_t KEYWORD2 67 | N64_Data_t KEYWORD2 68 | 69 | ####################################### 70 | # Constants (LITERAL1) 71 | ####################################### 72 | 73 | NINTENDO_DEVICE_GC_WIRED LITERAL1 74 | NINTENDO_DEVICE_GC_NONE LITERAL1 75 | 76 | 77 | NINTENDO_DEVICE_N64_WIRED LITERAL1 78 | 79 | NINTENDO_N64_DPAD_CENTERED LITERAL1 80 | NINTENDO_N64_DPAD_UP LITERAL1 81 | NINTENDO_N64_DPAD_UP_RIGHT LITERAL1 82 | NINTENDO_N64_DPAD_RIGHT LITERAL1 83 | NINTENDO_N64_DPAD_DOWN_RIGHT LITERAL1 84 | NINTENDO_N64_DPAD_DOWN LITERAL1 85 | NINTENDO_N64_DPAD_DOWN_LEFT LITERAL1 86 | NINTENDO_N64_DPAD_LEFT LITERAL1 87 | NINTENDO_N64_DPAD_UP_LEFT LITERAL1 88 | 89 | NINTENDO_N64_CPAD_CENTERED LITERAL1 90 | NINTENDO_N64_CPAD_UP LITERAL1 91 | NINTENDO_N64_CPAD_UP_RIGHT LITERAL1 92 | NINTENDO_N64_CPAD_RIGHT LITERAL1 93 | NINTENDO_N64_CPAD_DOWN_RIGHT LITERAL1 94 | NINTENDO_N64_CPAD_DOWN LITERAL1 95 | NINTENDO_N64_CPAD_DOWN_LEFT LITERAL1 96 | NINTENDO_N64_CPAD_LEFT LITERAL1 97 | NINTENDO_N64_CPAD_UP_LEFT LITERAL1 98 | -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/src/N64API.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2016 NicoHood 3 | See the readme for credit to other people. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | */ 23 | 24 | // Include guard 25 | #pragma once 26 | 27 | #include "N64.h" 28 | 29 | // Default data for an original, initialized, centered N64 controller 30 | static constexpr N64_Data_t defaultN64Data = { 31 | .status = { 0x05, 0x00, 0x02 }, 32 | .report = { 0x00, 0x00, 0x00, 0x00 } 33 | }; 34 | 35 | //================================================================================ 36 | // N64 Controller API 37 | //================================================================================ 38 | 39 | class CN64Controller : protected N64_Data_t { 40 | public: 41 | inline CN64Controller(const uint8_t p); 42 | inline void reset(void); 43 | inline bool begin(void); 44 | inline uint16_t getDevice(void); 45 | inline bool connected(void); 46 | inline bool read(void); 47 | inline bool getRumble(void); 48 | //inline bool setRumble(bool rumble); 49 | inline bool end(void); 50 | inline N64_Status_t getStatus(void); 51 | inline N64_Report_t getReport(void); 52 | inline N64_Data_t getData(void); 53 | 54 | protected: 55 | const uint8_t pin; 56 | friend class CN64Console; 57 | }; 58 | 59 | 60 | //================================================================================ 61 | // N64 Host API 62 | //================================================================================ 63 | 64 | class CN64Console{ 65 | public: 66 | inline CN64Console(const uint8_t p); 67 | inline bool write(N64_Data_t &data); 68 | inline bool write(CN64Controller &controller); 69 | inline bool write(N64_Report_t &report); 70 | 71 | protected: 72 | const uint8_t pin; 73 | }; 74 | 75 | #include "N64API.hpp" 76 | -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/Readme.md: -------------------------------------------------------------------------------- 1 | Modified! Arduino Nintendo Library 1337 2 | ============================== 3 | This version of Nicohood's Library has been modified to work on 5v logic level 16MHz ARM processors without the use of a Logic Level Converter. 4 | This version requires a 740ohm pullup resistor (500 to 1000ohm) between the controller data pin and 3.3v 5 | By switching the data pin between OUTPUT LOW and INPUT (NO PUULUP) We can pull the data line low to communicate without outputting 5v to the 3.3 data line. 6 | Checkout https://github.com/Skuzee/ESS-Adapter for my main hardware project where I use this library to make a n64/gamecube controller adapter and input display. 7 | 8 | 9 | ![header](header.jpg) 10 | 11 | This library is made to connect Nintendo Controllers to your Arduino very easy. 12 | Make sure you grab the right hardware, tear off some cables to use your controllers 13 | on your PC for example. The requirement are written in each library readme part. 14 | 15 | Buy Me A Coffee 16 | 17 | ##### Supported devices 18 | Host mode means that you can hook up an controller to your Arduino and read its buttons. 19 | Controller mode means that your Arduino can act as controller. 20 | 21 | * GamecubeConsole (Device Mode) 22 | * GamecubeController (Host Mode) 23 | * N64Console (Device Mode) 24 | * N64Controller (Host Mode) 25 | 26 | ##### Planned features 27 | * Wii-Mote (USB Host shield) 28 | * Wii Nunchuk (I2C) 29 | * Wii Classic Controller (I2C) 30 | * Wii-Mote plus(USB Host shield) 31 | * Wiiu Pro Controller (USB Host shield) 32 | * SNES Controller (I don't have any) 33 | 34 | ##### Todo: 35 | * N64 rumble -> example 36 | * N64 -> USB example 37 | * GC to N64 example 38 | 39 | ##### Possible projects: 40 | * Gamecube HID Controller 41 | * Gamecube to X Adapter 42 | * X to Gamecube Adapter 43 | * Selfmade Gamecube Controller 44 | * Wireless Gamecube Controller 45 | * 2 Player merged Controller 46 | * Manipulated (shortcut) Gamecube Controller 47 | * Gamecube Controller as Arduino input 48 | 49 | Download 50 | ======== 51 | 52 | Download the zip, extract and remove the "-master" of the folder. 53 | Install the library [as described here](http://arduino.cc/en/pmwiki.php?n=Guide/Libraries). 54 | You can also use the Arduino Library Manager to get the latest version. 55 | 56 | Checkout the ['dev' branch](https://github.com/NicoHood/Nintendo/tree/dev) to test the bleeding edge of this software. It might now work at all or has a lot of debugging stuff in it. 57 | 58 | Wiki 59 | ==== 60 | 61 | All documentation moved to the [wiki page](https://github.com/NicoHood/Nintendo/wiki). 62 | 63 | Contact 64 | ======= 65 | 66 | Contact information can be found here: 67 | 68 | www.nicohood.de 69 | -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/src/GamecubeAPI.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2016 NicoHood 3 | See the readme for credit to other people. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | */ 23 | 24 | // Include guard 25 | #pragma once 26 | 27 | #include "Gamecube.h" 28 | 29 | // Default data for an original, initialized, centered Gamecube controller 30 | static constexpr Gamecube_Data_t defaultGamecubeData = { 31 | .status = { 0x09, 0x00, 0x03 }, 32 | .origin = { 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x1F, 0x1F, 0x00, 0x00 }, 33 | .report = { 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x1F, 0x1F } 34 | }; 35 | 36 | //================================================================================ 37 | // Gamecube Controller API 38 | //================================================================================ 39 | 40 | class CGamecubeController : protected Gamecube_Data_t { 41 | public: 42 | inline CGamecubeController(const uint8_t p); 43 | inline void reset(void); 44 | inline bool begin(void); 45 | inline uint16_t getDevice(void); 46 | inline bool connected(void); 47 | inline bool read(void); 48 | inline bool getRumble(void); 49 | inline bool setRumble(bool rumble); 50 | inline bool end(void); 51 | inline Gamecube_Status_t getStatus(void); 52 | inline Gamecube_Origin_t getOrigin(void); 53 | inline Gamecube_Report_t getReport(void); 54 | inline Gamecube_Data_t getData(void); 55 | // TODO add a calibrate with origin function 56 | 57 | protected: 58 | const uint8_t pin; 59 | friend class CGamecubeConsole; 60 | }; 61 | 62 | 63 | //================================================================================ 64 | // Gamecube Host API 65 | //================================================================================ 66 | 67 | class CGamecubeConsole{ 68 | public: 69 | inline CGamecubeConsole(const uint8_t p); 70 | inline bool write(Gamecube_Data_t &data); 71 | inline bool write(CGamecubeController &controller); 72 | inline bool write(Gamecube_Report_t &report); 73 | 74 | protected: 75 | const uint8_t pin; 76 | }; 77 | 78 | #include "GamecubeAPI.hpp" 79 | -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/examples/Gamecube/Gamecube_USB_HID/Gamecube_USB_HID.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2016 NicoHood 3 | See the readme for credit to other people. 4 | 5 | Gamecube_USB_HID example 6 | Use your Gamecube controller with PC as generic HID interface. 7 | The led indicated errors. 8 | 9 | This needs the HID Project. 10 | https://github.com/NicoHood/HID 11 | */ 12 | 13 | #include "HID-Project.h" 14 | #include "Nintendo.h" 15 | 16 | // Define a Gamecube Controller 17 | CGamecubeController GamecubeController1(7); 18 | 19 | // Pin definitions 20 | #define pinLed LED_BUILTIN 21 | 22 | void setup() 23 | { 24 | // Set up debug led 25 | pinMode(pinLed, OUTPUT); 26 | 27 | // Sends a clean report to the host. This is important on any Arduino type. 28 | Gamepad.begin(); 29 | } 30 | 31 | 32 | void loop() 33 | { 34 | // Try to read the controller data 35 | if (GamecubeController1.read()) 36 | { 37 | // Send controller data to the USB interface 38 | auto report = GamecubeController1.getReport(); 39 | sendGamecubeReport(report); 40 | 41 | // delay a bit to not overload PC 42 | delay(10); 43 | } 44 | else 45 | { 46 | // Center gamepad on disconnect 47 | Gamepad.end(); 48 | 49 | // Add debounce if reading failed 50 | digitalWrite(pinLed, HIGH); 51 | delay(1000); 52 | } 53 | digitalWrite(pinLed, LOW); 54 | } 55 | 56 | 57 | void sendGamecubeReport(Gamecube_Report_t &gc_report) 58 | { 59 | // The two control sticks 60 | Gamepad.xAxis((gc_report.xAxis - 0x80) << 8); 61 | Gamepad.yAxis(~(gc_report.yAxis - 0x80) << 8); // y stick needs to be inverted 62 | Gamepad.rxAxis((gc_report.cxAxis - 0x80) << 8); 63 | Gamepad.ryAxis((gc_report.cyAxis - 0x80) << 8); 64 | 65 | // you can merge L/R (PS3 controller uses this methode too) 66 | // or you can also seperate the triggers 67 | // for Windows calibration comment out the l/r buttons below 68 | // (because l/r triggers the wizard) 69 | //Gamepad.zAxis(abs(gc_report.left - gc_report.right) - 0x80); 70 | Gamepad.zAxis(gc_report.left - 0x80); 71 | Gamepad.rzAxis(gc_report.right - 0x80); 72 | 73 | // D-Pad: 74 | if (gc_report.dup && gc_report.dright) { 75 | Gamepad.dPad1(GAMEPAD_DPAD_UP_RIGHT); 76 | } 77 | else if (gc_report.dup && gc_report.dleft) { 78 | Gamepad.dPad1(GAMEPAD_DPAD_UP_LEFT); 79 | } 80 | else if (gc_report.ddown && gc_report.dright) { 81 | Gamepad.dPad1(GAMEPAD_DPAD_DOWN_RIGHT); 82 | } 83 | else if (gc_report.ddown && gc_report.dleft) { 84 | Gamepad.dPad1(GAMEPAD_DPAD_DOWN_LEFT); 85 | } 86 | else if (gc_report.dup) { 87 | Gamepad.dPad1(GAMEPAD_DPAD_UP); 88 | } 89 | else if (gc_report.ddown) { 90 | Gamepad.dPad1(GAMEPAD_DPAD_DOWN); 91 | } 92 | else if (gc_report.dright) { 93 | Gamepad.dPad1(GAMEPAD_DPAD_RIGHT); 94 | } 95 | else if (gc_report.dleft) { 96 | Gamepad.dPad1(GAMEPAD_DPAD_LEFT); 97 | } 98 | 99 | // 8 Gamecube buttons 100 | Gamepad.buttons(0x00UL | (gc_report.buttons0 & 0x1F) | ((gc_report.buttons1 & 0x70) << 1)); 101 | 102 | // You could add a zero value correction here or a minimum 103 | // for the left/right shoulder triggers but the os can 104 | // (and should) calibrate controllers very well. 105 | 106 | // Write the information to the PC 107 | Gamepad.write(); 108 | } 109 | -------------------------------------------------------------------------------- /ESS-Adapter/input-display.cpp: -------------------------------------------------------------------------------- 1 | //input-display.cpp 2 | 3 | //The Following code is released under GNU GENERAL PUBLIC LICENSE Version 3 and uses a nicohood's nintendo library released under MIT license. 4 | 5 | #include "input-display.hpp" 6 | #include "extra.hpp" 7 | 8 | void writeToUSB_BYTE(Gamecube_Data_t& data) { 9 | if (Serial.availableForWrite() >= 10 && settings.input_display_enabled) { // Only write to serial data buffer if it's not full. Disconnecting Arduino Serial Monitor makes serial data buffer fill and halt program; this prevents that. 10 | noInterrupts(); // Important! Due to how time sensitive our code is, and how the arduino parallel processes serial data (with interrupts), we must allow the data to be written to the serial buffer or it will back up and hault the program. This was causing a hiccup in the timing roughly once every second, resulting in missing controller reads/updates. 11 | Serial.write(data.report.raw8, sizeof(data.report.raw8)); 12 | Serial.write(NEWLINE); 13 | interrupts(); 14 | } 15 | } 16 | 17 | void writeToUSB_BYTE(N64_Report_t& report) { 18 | if (Serial.availableForWrite() >= 6 && settings.input_display_enabled) { // Only write to serial data buffer if it's not full. Disconnecting Arduino Serial Monitor makes serial data buffer fill and halt program; this prevents that. 19 | noInterrupts(); // Important! Due to how time sensitive our code is, and how the arduino parallel processes serial data (with interrupts), we must allow the data to be written to the serial buffer or it will back up and hault the program. This was causing a hiccup in the timing roughly once every second, resulting in missing controller reads/updates. 20 | Serial.write(report.raw8, sizeof(report.raw8)); 21 | Serial.write(NEWLINE); 22 | interrupts(); 23 | } 24 | } 25 | 26 | void writeToUSB_BIT(Gamecube_Report_t &GCreport) { // Sending the data as ASCII would allow for compatibility with old versions of nintendospy. This isn't tested and honestly this is probably too slow to work. 8 times slower than writeToUSB_BYTE. The data sent is 65 bytes long per poll, and the serial buffer is only 64 bytes. If the buffer fills up the program will block until there is room to finish sending the data. ¯\_(ツ)_/¯ 27 | if (Serial.availableForWrite() >= 10 && settings.input_display_enabled) { 28 | noInterrupts(); 29 | for (uint8_t byteCounter = 0; byteCounter < 8; byteCounter++) { 30 | for (uint8_t bitCounter = 0; bitCounter < 8; bitCounter++) { 31 | //Masks off one bit at a time and sends an ascii 1 or 0 to serial console. 32 | Serial.write(GCreport.raw8[byteCounter] & (0x80 >> bitCounter) ? ASCII_1 : ASCII_0); 33 | } 34 | } 35 | Serial.write(NEWLINE); 36 | interrupts(); 37 | } 38 | } 39 | 40 | void tryPrint(String input) { 41 | if ( Serial.availableForWrite() > input.length()) 42 | Serial.print(input); 43 | } 44 | 45 | void tryPrintln(String input) { 46 | if ( Serial.availableForWrite() > input.length()) 47 | Serial.println(input); 48 | } 49 | 50 | void tryPrint(uint8_t input) { 51 | if ( Serial.availableForWrite() > 1) 52 | Serial.print(input); 53 | } 54 | 55 | void tryPrintln(uint8_t input) { 56 | if ( Serial.availableForWrite() > 1) 57 | Serial.println(input); 58 | } 59 | 60 | void checkSerialBufferFull() { 61 | static uint8_t lastBuffer; 62 | 63 | if (Serial.availableForWrite() == lastBuffer) 64 | Serial.flush(); 65 | else 66 | lastBuffer = Serial.availableForWrite(); 67 | } 68 | -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/examples/N64/N64Controller/N64Controller.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2016 NicoHood 3 | See the readme for credit to other people. 4 | 5 | N64Controller example 6 | Enter any key into the serial monitor to start reading. 7 | */ 8 | 9 | #include "Nintendo.h" 10 | 11 | // Define a N64 Controller 12 | CN64Controller N64Controller(7); 13 | 14 | // Pin definitions 15 | #define pinLed LED_BUILTIN 16 | 17 | void setup() 18 | { 19 | // Set up debug led 20 | pinMode(pinLed, OUTPUT); 21 | 22 | // Start debug serial 23 | while (!Serial); 24 | Serial.begin(115200); 25 | 26 | // Wait for some Serial input 27 | Serial.println(F("Enter any key to start reading the controller.")); 28 | Serial.println(F("Press A on the controller to enable rumble.")); 29 | while (Serial.read() == -1); 30 | 31 | Serial.println(F("Starting controller emulation")); 32 | Serial.println(); 33 | } 34 | 35 | void loop() 36 | { 37 | // Try to read the controller data 38 | if (N64Controller.read()) 39 | { 40 | // Print Controller information 41 | auto status = N64Controller.getStatus(); 42 | auto report = N64Controller.getReport(); 43 | print_n64_report(report, status); 44 | delay(100); 45 | } 46 | else 47 | { 48 | // Add debounce if reading failed 49 | Serial.println(F("Error reading N64 controller.")); 50 | digitalWrite(pinLed, HIGH); 51 | delay(1000); 52 | } 53 | digitalWrite(pinLed, LOW); 54 | } 55 | 56 | void print_n64_report(N64_Report_t &n64_report, N64_Status_t &n64_status) 57 | { 58 | // Print device information 59 | Serial.print(F("Device: ")); 60 | switch (n64_status.device) { 61 | case NINTENDO_DEVICE_N64_NONE: 62 | Serial.println(F("No N64 Controller found!")); 63 | break; 64 | case NINTENDO_DEVICE_N64_WIRED: 65 | Serial.println(F("Original Nintendo N64 Controller")); 66 | break; 67 | 68 | default: 69 | Serial.print(F("Unknown ")); 70 | Serial.println(n64_status.device, HEX); 71 | break; 72 | } 73 | 74 | // Prints the raw data from the controller 75 | Serial.println(); 76 | Serial.println(F("Printing N64 controller report:")); 77 | 78 | Serial.print(F("A:\t")); 79 | Serial.println(n64_report.a); 80 | 81 | Serial.print(F("B:\t")); 82 | Serial.println(n64_report.b); 83 | 84 | Serial.print(F("Z:\t")); 85 | Serial.println(n64_report.z); 86 | 87 | Serial.print(F("Start:\t")); 88 | Serial.println(n64_report.start); 89 | 90 | Serial.print(F("Dup:\t")); 91 | Serial.println(n64_report.dup); 92 | Serial.print(F("Ddown:\t")); 93 | Serial.println(n64_report.ddown); 94 | Serial.print(F("Dleft:\t")); 95 | Serial.println(n64_report.dleft); 96 | Serial.print(F("Dright:\t")); 97 | Serial.println(n64_report.dright); 98 | 99 | Serial.print(F("L:\t")); 100 | Serial.println(n64_report.l); 101 | Serial.print(F("R:\t")); 102 | Serial.println(n64_report.r); 103 | 104 | Serial.print(F("Cup:\t")); 105 | Serial.println(n64_report.cup); 106 | Serial.print(F("Cdown:\t")); 107 | Serial.println(n64_report.cdown); 108 | Serial.print(F("Cleft:\t")); 109 | Serial.println(n64_report.cleft); 110 | Serial.print(F("Cright:\t")); 111 | Serial.println(n64_report.cright); 112 | 113 | Serial.print(F("xAxis:\t")); 114 | Serial.println(n64_report.xAxis, DEC); 115 | Serial.print(F("yAxis:\t")); 116 | Serial.println(n64_report.yAxis, DEC); 117 | 118 | Serial.println(); 119 | } 120 | -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/examples/Gamecube/GamecubeMultiSingleplayer/GamecubeMultiSingleplayer.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2016 NicoHood 3 | See the readme for credit to other people. 4 | 5 | GamecubeMultiSingleplayer example 6 | Read 2 controllers and send merged data to the gamecube. 7 | This way you can play a single player game in semi coop. 8 | */ 9 | 10 | #include "Nintendo.h" 11 | 12 | // Define a Gamecube Controller and a Console 13 | CGamecubeController GamecubeController1(7); 14 | CGamecubeConsole GamecubeConsole1(8); 15 | CGamecubeController GamecubeController2(9); 16 | 17 | // Pin definitions 18 | #define pinLed LED_BUILTIN 19 | 20 | void setup() 21 | { 22 | // Set up debug led 23 | pinMode(pinLed, OUTPUT); 24 | 25 | // Start debug serial 26 | Serial.begin(115200); 27 | Serial.println(F("Start")); 28 | } 29 | 30 | 31 | void loop() 32 | { 33 | for (uint8_t option = 0; option < 2; option++) 34 | { 35 | // Only read a single controller between each gamecube poll. 36 | // The gamecube polls every 1ms - 15ms. 37 | // In Mario football it polls 2 times fast (1ms between) and then again after 15ms. 38 | // It may also help to write 2 times to the gamecube instead of only a single time. 39 | switch (option) { 40 | case 0: 41 | { 42 | if (!GamecubeController1.read()) { 43 | Serial.println(F("Error reading Gamecube controller 1.")); 44 | digitalWrite(pinLed, HIGH); 45 | delay(1000); 46 | return; 47 | } 48 | } 49 | break; 50 | case 1: 51 | { 52 | if (!GamecubeController2.read()) { 53 | Serial.println(F("Error reading Gamecube controller 2.")); 54 | digitalWrite(pinLed, HIGH); 55 | delay(1000); 56 | return; 57 | } 58 | } 59 | break; 60 | } 61 | 62 | // Get the data of each controller 63 | auto r1 = GamecubeController1.getReport(); 64 | auto r2 = GamecubeController2.getReport(); 65 | 66 | // Merge both controller data into one 67 | Gamecube_Data_t d = defaultGamecubeData; 68 | d.report.buttons0 |= r1.buttons0 | r2.buttons0; 69 | d.report.buttons1 |= r1.buttons1 | r2.buttons1; 70 | for (uint8_t i = 0; i < 4; i++) 71 | { 72 | // Add axis value together 73 | int newAxis = r1.raw8[2 + i] + r2.raw8[2 + i] - 128; 74 | if (newAxis < 0) { 75 | newAxis = 0; 76 | } 77 | else if (newAxis > 0xFF) { 78 | newAxis = 255; 79 | } 80 | d.report.raw8[2 + i] = newAxis; 81 | } 82 | 83 | // Use the maximum of each left/right trigger 84 | if (r1.left > r2.left) { 85 | d.report.left = r1.left; 86 | } 87 | else { 88 | d.report.left = r2.left; 89 | } 90 | if (r1.right > r2.right) { 91 | d.report.right = r1.right; 92 | } 93 | else { 94 | d.report.right = r2.right; 95 | } 96 | 97 | // Mirror the controller data to the console 98 | if (!GamecubeConsole1.write(d)) 99 | { 100 | Serial.println(F("Error writing Gamecube controller.")); 101 | digitalWrite(pinLed, HIGH); 102 | delay(1000); 103 | } 104 | 105 | // Enable rumble 106 | if (d.status.rumble) { 107 | GamecubeController1.setRumble(true); 108 | GamecubeController2.setRumble(true); 109 | } 110 | else { 111 | GamecubeController1.setRumble(false); 112 | GamecubeController2.setRumble(false); 113 | } 114 | } 115 | 116 | digitalWrite(pinLed, LOW); 117 | } 118 | -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/src/Gamecube_N64.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2016 NicoHood 3 | See the readme for credit to other people. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | */ 23 | 24 | // Include guard 25 | #pragma once 26 | 27 | #include 28 | 29 | //================================================================================ 30 | // Typedefs 31 | //================================================================================ 32 | 33 | typedef union{ 34 | // 3 bytes of statusreport that we get from the controller 35 | uint8_t raw8[3]; 36 | uint16_t raw16[0]; 37 | struct { 38 | // Device information, needs to be swapped to fit the documentation below: 39 | // 15 wireless (1: wireless Controller) 40 | // 14 wireless receive (0: not wireless 1: wireless) 41 | // 13 Rumble Motor (0: available 1: not available) 42 | // 12 Type of Controller (always 0 ?) 43 | // 11 Type of Controller (0: N64 1: Dolphin) 44 | // 10 wireless type (0:IF 1:RF) 45 | // 9 wireless state (0: variable 1: fixed) 46 | // 8 0: non standard Controller 1: Dolphin Standard Controller 47 | // 7 48 | // 6 49 | // 5 wireless origin (0:invalid 1:valid) 50 | // 4 wireless fix id (0:not fixed 1:fixed) 51 | // 3 wireless type - 0:normal 1: non-controller (?) 52 | // 2 wireless type - 0:normal 1: lite controller 53 | // 1 wireless type - 54 | // 0 wireless type - 55 | uint16_t device; 56 | 57 | // controller status (only rumble is known) 58 | union{ 59 | uint8_t status; 60 | struct{ 61 | uint8_t status0 : 3; 62 | uint8_t rumble : 1; 63 | uint8_t status1 : 4; 64 | }; 65 | }; 66 | }; 67 | } Gamecube_N64_Status_t; 68 | 69 | //================================================================================ 70 | // Function prototypes 71 | //================================================================================ 72 | 73 | #ifdef __cplusplus 74 | extern "C" { 75 | #endif 76 | 77 | // functions to communicate with the gc/n64 controller 78 | uint8_t gc_n64_send_get(const uint8_t pin, uint8_t* command, const uint8_t commandLen, 79 | uint8_t* report, const uint8_t reportLen) __attribute__((noinline)); 80 | 81 | void gc_n64_send(const uint8_t* buff, uint8_t len, 82 | volatile uint8_t* modePort, volatile uint8_t* outPort, uint8_t bitMask) 83 | __attribute__((noinline)); 84 | 85 | uint8_t gc_n64_get(uint8_t* buff, uint8_t len, 86 | volatile uint8_t* modePort, volatile uint8_t* outPort, volatile uint8_t * inPort, uint8_t bitMask) 87 | __attribute__((noinline)); 88 | 89 | #ifdef __cplusplus 90 | } 91 | #endif 92 | -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/src/N64.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2016 NicoHood 3 | See the readme for credit to other people. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | */ 23 | 24 | // Include guard 25 | #pragma once 26 | 27 | #include 28 | 29 | //================================================================================ 30 | // N64 Definitions 31 | //================================================================================ 32 | 33 | #include "Gamecube_N64.h" 34 | 35 | // N64 controller device status ids 36 | // You need to switch the bytes for the docs which are widely available online. 37 | // A default N64 controller would look like 0x0500. 38 | #define NINTENDO_DEVICE_N64_WIRED 0x0005 39 | #define NINTENDO_DEVICE_N64_NONE 0x0000 40 | 41 | 42 | //================================================================================ 43 | // N64 Typedefs 44 | //================================================================================ 45 | 46 | typedef union{ 47 | // 4 bytes of datareport that we get from the controller 48 | uint8_t raw8[4]; 49 | uint16_t raw16[0]; 50 | uint32_t raw32[0]; 51 | 52 | struct{ 53 | uint8_t dpad : 4; 54 | uint8_t buttons0 : 4; 55 | uint8_t cpad : 4; 56 | uint8_t buttons1 : 4; 57 | }; 58 | 59 | struct { 60 | // first data byte (bitfields are sorted in LSB order) 61 | uint8_t dright : 1; 62 | uint8_t dleft : 1; 63 | uint8_t ddown : 1; 64 | uint8_t dup : 1; 65 | uint8_t start : 1; 66 | uint8_t z : 1; 67 | uint8_t b : 1; 68 | uint8_t a : 1; 69 | 70 | // second data byte 71 | uint8_t cright : 1; 72 | uint8_t cleft : 1; 73 | uint8_t cdown : 1; 74 | uint8_t cup : 1; 75 | uint8_t r : 1; 76 | uint8_t l : 1; 77 | uint8_t low1 : 1; 78 | uint8_t low0 : 1; 79 | 80 | // 3rd-4th data byte 81 | int8_t xAxis; 82 | int8_t yAxis; 83 | }; 84 | } N64_Report_t; 85 | 86 | // Gamecube an N64 use the same status schema 87 | typedef Gamecube_N64_Status_t N64_Status_t; 88 | 89 | struct N64_Data_t{ 90 | // All information required for reading/writing a N64 Controller 91 | N64_Status_t status; 92 | N64_Report_t report; 93 | }; 94 | 95 | 96 | //================================================================================ 97 | // N64 Function Prototypes 98 | //================================================================================ 99 | 100 | #ifdef __cplusplus 101 | extern "C" { 102 | #endif 103 | 104 | // Functions to communicate with the gc controller 105 | bool n64_init(const uint8_t pin, N64_Status_t* status); 106 | bool n64_read(const uint8_t pin, N64_Report_t* report); 107 | uint8_t n64_write(const uint8_t pin, N64_Status_t* status, N64_Report_t* report); 108 | 109 | #ifdef __cplusplus 110 | } 111 | #endif 112 | -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/examples/Gamecube/GamecubeController/GamecubeController.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2016 NicoHood 3 | See the readme for credit to other people. 4 | 5 | GamecubeController example 6 | Enter any key into the serial monitor to start reading. 7 | Press A on the controller to enable rumble. 8 | */ 9 | 10 | #include "Nintendo.h" 11 | 12 | // Define a Gamecube Controller 13 | CGamecubeController GamecubeController(7); 14 | 15 | // Pin definitions 16 | #define pinLed LED_BUILTIN 17 | 18 | void setup() 19 | { 20 | // Set up debug led 21 | pinMode(pinLed, OUTPUT); 22 | 23 | // Start debug serial 24 | while (!Serial); 25 | Serial.begin(115200); 26 | 27 | // Wait for some Serial input 28 | Serial.println(F("Enter any key to start reading the controller.")); 29 | Serial.println(F("Press A on the controller to enable rumble.")); 30 | while (Serial.read() == -1); 31 | 32 | Serial.println(F("Starting controller emulation")); 33 | Serial.println(); 34 | } 35 | 36 | void loop() 37 | { 38 | // Try to read the controller data 39 | if (GamecubeController.read()) 40 | { 41 | // Print Controller information 42 | auto status = GamecubeController.getStatus(); 43 | auto report = GamecubeController.getReport(); 44 | print_gc_report(report, status); 45 | delay(100); 46 | 47 | // Rumble if button "A" was pressed 48 | if (report.a) { 49 | GamecubeController.setRumble(true); 50 | } 51 | else { 52 | GamecubeController.setRumble(false); 53 | } 54 | } 55 | else 56 | { 57 | // Add debounce if reading failed 58 | Serial.println(F("Error reading Gamecube controller.")); 59 | digitalWrite(pinLed, HIGH); 60 | delay(1000); 61 | } 62 | digitalWrite(pinLed, LOW); 63 | } 64 | 65 | void print_gc_report(Gamecube_Report_t &gc_report, Gamecube_Status_t &gc_status) 66 | { 67 | // Print device information 68 | Serial.print(F("Device: ")); 69 | switch (gc_status.device) { 70 | case NINTENDO_DEVICE_GC_NONE: 71 | Serial.println(F("No Gamecube Controller found!")); 72 | break; 73 | case NINTENDO_DEVICE_GC_WIRED: 74 | Serial.println(F("Original Nintendo Gamecube Controller")); 75 | break; 76 | 77 | default: 78 | Serial.print(F("Unknown ")); 79 | Serial.println(gc_status.device, HEX); 80 | break; 81 | } 82 | 83 | // Print rumble state 84 | Serial.print(F("Rumble ")); 85 | if (gc_status.rumble) 86 | Serial.println(F("on")); 87 | else 88 | Serial.println(F("off")); 89 | 90 | // Prints the raw data from the controller 91 | Serial.println(); 92 | Serial.println(F("Printing Gamecube controller report:")); 93 | Serial.print(F("Start:\t")); 94 | Serial.println(gc_report.start); 95 | 96 | Serial.print(F("Y:\t")); 97 | Serial.println(gc_report.y); 98 | 99 | Serial.print(F("X:\t")); 100 | Serial.println(gc_report.x); 101 | 102 | Serial.print(F("B:\t")); 103 | Serial.println(gc_report.b); 104 | 105 | Serial.print(F("A:\t")); 106 | Serial.println(gc_report.a); 107 | 108 | Serial.print(F("L:\t")); 109 | Serial.println(gc_report.l); 110 | Serial.print(F("R:\t")); 111 | Serial.println(gc_report.r); 112 | Serial.print(F("Z:\t")); 113 | Serial.println(gc_report.z); 114 | 115 | Serial.print(F("Dup:\t")); 116 | Serial.println(gc_report.dup); 117 | Serial.print(F("Ddown:\t")); 118 | Serial.println(gc_report.ddown); 119 | Serial.print(F("Dright:\t")); 120 | Serial.println(gc_report.dright); 121 | Serial.print(F("Dleft:\t")); 122 | Serial.println(gc_report.dleft); 123 | 124 | Serial.print(F("xAxis:\t")); 125 | Serial.println(gc_report.xAxis, DEC); 126 | Serial.print(F("yAxis:\t")); 127 | Serial.println(gc_report.yAxis, DEC); 128 | 129 | Serial.print(F("cxAxis:\t")); 130 | Serial.println(gc_report.cxAxis, DEC); 131 | Serial.print(F("cyAxis:\t")); 132 | Serial.println(gc_report.cyAxis, DEC); 133 | 134 | Serial.print(F("L:\t")); 135 | Serial.println(gc_report.left, DEC); 136 | Serial.print(F("R:\t")); 137 | Serial.println(gc_report.right, DEC); 138 | Serial.println(); 139 | } 140 | -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/src/N64.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2016 NicoHood 3 | See the readme for credit to other people. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | */ 23 | 24 | #include "N64.h" 25 | 26 | //================================================================================ 27 | // N64 Controller 28 | //================================================================================ 29 | 30 | bool n64_init(const uint8_t pin, N64_Status_t* status) 31 | { 32 | // Initialize the N64 controller by sending it a null byte. 33 | // This is unnecessary for a standard controller, but the console does this. 34 | uint8_t command[] = { 0x00 }; 35 | 36 | // Send the command and read in data 37 | uint8_t receivedBytes = gc_n64_send_get(pin, command, sizeof(command), (uint8_t*)status, sizeof(N64_Status_t)); 38 | 39 | // Return status information for optional use. 40 | // On error the report may have been modified! 41 | return (receivedBytes == sizeof(N64_Status_t)); 42 | } 43 | 44 | 45 | bool n64_read(const uint8_t pin, N64_Report_t* report) 46 | { 47 | // Command to send to the N64 48 | uint8_t command[] = { 0x01 }; 49 | 50 | // Send the command and read in data 51 | uint8_t receivedBytes = gc_n64_send_get(pin, command, sizeof(command), (uint8_t*)report, sizeof(N64_Report_t)); 52 | 53 | // Return status information for optional use. 54 | // On error the report may have been modified! 55 | return (receivedBytes == sizeof(N64_Report_t)); 56 | } 57 | 58 | 59 | //================================================================================ 60 | // N64 Console 61 | //================================================================================ 62 | 63 | uint8_t n64_write(const uint8_t pin, N64_Status_t* status, N64_Report_t* report) 64 | { 65 | // 0 = no input/error, 1 = identify, 2 = status (poll), 66 | // 3 = read from expansion bus, 4 = write to expansion bus 67 | uint8_t ret = 0; 68 | 69 | // Get the port mask and the pointers to the in/out/mode registers 70 | uint8_t bitMask = digitalPinToBitMask(pin); 71 | uint8_t port = digitalPinToPort(pin); 72 | volatile uint8_t* modePort = portModeRegister(port); 73 | volatile uint8_t* outPort = portOutputRegister(port); 74 | volatile uint8_t* inPort = portInputRegister(port); 75 | 76 | // Don't want interrupts getting in the way 77 | uint8_t oldSREG = SREG; 78 | cli(); 79 | 80 | // Read in data from the console 81 | // After receiving the init command you have max 20ms to respond (for the data command)! 82 | uint8_t command[3]; 83 | uint8_t receivedBytes = gc_n64_get(command, sizeof(command), modePort, outPort, inPort, bitMask); 84 | 85 | // Identify 86 | if (receivedBytes == 1 && command[0] == 0x00) 87 | { 88 | gc_n64_send(status->raw8, sizeof(N64_Status_t), modePort, outPort, bitMask); 89 | ret = 1; 90 | } 91 | // Poll 92 | else if (receivedBytes == 1 && command[0] == 0x01) 93 | { 94 | gc_n64_send(report->raw8, sizeof(N64_Report_t), modePort, outPort, bitMask); 95 | ret = 2; 96 | } 97 | 98 | // End of time sensitive code 99 | SREG = oldSREG; 100 | 101 | return ret; 102 | } 103 | -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/src/Gamecube.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2016 NicoHood 3 | See the readme for credit to other people. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | */ 23 | 24 | // Include guard 25 | #pragma once 26 | 27 | #include 28 | 29 | //================================================================================ 30 | // Gamecube Definitions 31 | //================================================================================ 32 | 33 | #include "Gamecube_N64.h" 34 | 35 | // Gamecube controller device status ids 36 | // You need to switch the bytes for the docs which are widely available online. 37 | // A default Gamecube controller would look like 0x0900. 38 | #define NINTENDO_DEVICE_GC_WIRED 0x0009 39 | #define NINTENDO_DEVICE_GC_NONE 0x0000 40 | 41 | 42 | //================================================================================ 43 | // Gamecube Typedefs 44 | //================================================================================ 45 | 46 | typedef union{ 47 | // 8 bytes of datareport that we get from the controller 48 | uint8_t raw8[8]; 49 | uint16_t raw16[0]; 50 | uint32_t raw32[0]; 51 | 52 | struct{ 53 | uint8_t buttons0; 54 | union{ 55 | uint8_t buttons1; 56 | uint8_t dpad : 4; 57 | }; 58 | }; 59 | 60 | struct { 61 | // first data byte (bitfields are sorted in LSB order) 62 | uint8_t a : 1; 63 | uint8_t b : 1; 64 | uint8_t x : 1; 65 | uint8_t y : 1; 66 | uint8_t start : 1; 67 | uint8_t origin : 1; // Indicates if GetOrigin(0x41) was called (LOW) 68 | uint8_t errlatch : 1; 69 | uint8_t errstat : 1; 70 | 71 | // second data byte 72 | uint8_t dleft : 1; 73 | uint8_t dright : 1; 74 | uint8_t ddown : 1; 75 | uint8_t dup : 1; 76 | uint8_t z : 1; 77 | uint8_t r : 1; 78 | uint8_t l : 1; 79 | uint8_t high1 : 1; 80 | 81 | // 3rd-8th data byte 82 | uint8_t xAxis; 83 | uint8_t yAxis; 84 | uint8_t cxAxis; 85 | uint8_t cyAxis; 86 | uint8_t left; 87 | uint8_t right; 88 | }; 89 | } Gamecube_Report_t; 90 | 91 | 92 | // Gamecube an N64 use the same status schema 93 | typedef Gamecube_N64_Status_t Gamecube_Status_t; 94 | 95 | 96 | typedef union{ 97 | // GetOrigin requests the initial values of the controller when it was plugged in (powered up) 98 | // This request consists of a normal data package and 2 unknown bytes. 99 | // It is possible that those mark the deadzone/toleranz, but are zero in my tests. 100 | // If the GetOrigin request was not sent to the controller, it will respond it in the data report. 101 | // The gamecube will NOT accept controller data when no GetOrigin request was made (origin bit in data report set). 102 | uint8_t raw8[10]; 103 | uint16_t raw16[0]; 104 | uint32_t raw32[0]; 105 | 106 | struct{ 107 | Gamecube_Report_t inititalData; 108 | uint8_t deadzone0; 109 | uint8_t deadzone1; 110 | }; 111 | } Gamecube_Origin_t; 112 | 113 | 114 | struct Gamecube_Data_t{ 115 | // All information required for reading/writing a Gamecube Controller 116 | Gamecube_Status_t status; 117 | Gamecube_Origin_t origin; 118 | Gamecube_Report_t report; 119 | }; 120 | 121 | 122 | //================================================================================ 123 | // Gamecube Function Prototypes 124 | //================================================================================ 125 | 126 | #ifdef __cplusplus 127 | extern "C" { 128 | #endif 129 | 130 | // Functions to communicate with the gc controller 131 | bool gc_init(const uint8_t pin, Gamecube_Status_t* status); 132 | bool gc_origin(const uint8_t pin, Gamecube_Origin_t* origin); 133 | bool gc_read(const uint8_t pin, Gamecube_Report_t* report, const bool rumble); 134 | uint8_t gc_write(const uint8_t pin, Gamecube_Status_t* status, Gamecube_Origin_t* origin, Gamecube_Report_t* report); 135 | 136 | #ifdef __cplusplus 137 | } 138 | #endif 139 | -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/extras/Yet Another Gamecube Documentation-Dateien/techdoc.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | font-family:arial, times, lucida, geneva, sans-serif; 4 | } 5 | 6 | A { color:222288 } 7 | 8 | /* heading styles */ 9 | H1 { 10 | border:1px solid black; 11 | background-color: d0d0f0; 12 | background: d0d0f0; 13 | color: 000000; 14 | padding: 10px; 15 | font-size:180%; 16 | } 17 | H2 { 18 | border:1px solid black; 19 | background-color: d0d0f0; 20 | background: d0d0f0; 21 | color: 000000; 22 | padding: 5px; 23 | font-size:160%; 24 | } 25 | H3 { 26 | border:1px solid black; 27 | background-color: d0d0f0; 28 | background: d0d0f0; 29 | color: 000000; 30 | padding: 5px; 31 | font-size:140%; 32 | } 33 | H4 { 34 | border:1px solid black; 35 | background-color: d0d0f0; 36 | background: d0d0f0; 37 | color: 000000; 38 | padding: 5px; 39 | font-size:120%; 40 | } 41 | H5 { 42 | border:1px solid black; 43 | background-color: d0d0f0; 44 | background: d0d0f0; 45 | padding: 5px; 46 | color: 000000; 47 | } 48 | H6 { 49 | border:1px solid black; 50 | background-color: d0d0f0; 51 | background: d0d0f0; 52 | padding: 5px; 53 | color: 000000; 54 | } 55 | 56 | /* lists */ 57 | UL { 58 | background-color: FFFFFF; 59 | background: FFFFFF; 60 | color: 882288; 61 | } 62 | LI { 63 | background-color: FFFFFF; 64 | background: FFFFFF; 65 | color: 000000; 66 | } 67 | 68 | /* table on top-level is only used to group things 69 | together _without_ visually indicating it */ 70 | 71 | 72 | /* table on top-level is only used to group things 73 | together _without_ visually indicating it */ 74 | 75 | TABLE { 76 | background-color: ffffff; 77 | /* color: 000000; */ 78 | border: 0px none #000; 79 | padding: 0px; 80 | margin: 5px; 81 | position: relative, left; 82 | } 83 | 84 | 85 | DIV.P TABLE B { 86 | background-color: e8e8ff; 87 | } 88 | 89 | TABLE TABLE TD TABLE { 90 | border: 1px solid #d8d8ef; 91 | padding: 0px; 92 | margin: 0px; 93 | width: 100%; 94 | } 95 | 96 | TD { 97 | background-color: ffffff; 98 | /* color: 000000; */ 99 | border: 0px none #000; 100 | padding: 0px; 101 | margin: 0px; 102 | } 103 | 104 | TR { 105 | background-color: ffffff; 106 | /* color: 000000; */ 107 | border: 0px none #000; 108 | padding: 0px; 109 | margin: 0px; 110 | } 111 | 112 | 113 | 114 | TABLE TABLE { 115 | background-color: 000000; 116 | /* color: 000000; */ 117 | border: 0px none #000; 118 | padding: 0px; 119 | margin: 0px; 120 | width: auto; 121 | } 122 | 123 | TABLE TABLE TD { 124 | background-color: e8e8ff; 125 | /* color: 000000; */ 126 | border: 0px none #000; 127 | padding: 2px 5px 2px 5px; 128 | margin: 0px; 129 | } 130 | 131 | TABLE TABLE TR { 132 | background-color: ffffff; 133 | /* color: 000000; */ 134 | border: 0px none #000; 135 | padding: 0px; 136 | margin: 0px; 137 | } 138 | 139 | 140 | /* links to the index */ 141 | div.idx { 142 | background-color: ffffff; 143 | color: 000000; 144 | text-align:right; 145 | font-size:80%; 146 | font-family:fixed, lucida, arial, geneva, sans-serif; 147 | } 148 | 149 | div.idx A { 150 | color:222255; 151 | } 152 | 153 | div.head { 154 | border:0px none black; 155 | background-color: ffffff; 156 | background: ffffff; 157 | color: 000000; 158 | padding: 0px; 159 | font-size: 15px; 160 | margin: 0px; 161 | text-align:right; 162 | vertical-align:middle; 163 | } 164 | 165 | div.head h1 { 166 | border:1px solid black; 167 | background-color: d0d0f0; 168 | background: d0d0f0; 169 | color: 000000; 170 | padding: 2px; 171 | font-size: 15px; 172 | margin: 0px; 173 | text-align:right; 174 | vertical-align:middle; 175 | } 176 | 177 | div.idxfrm { 178 | border:0px none black; 179 | background-color: ffffff; 180 | background: ffffff; 181 | color: 000000; 182 | padding: 0px; 183 | font-size:100%; 184 | margin: 0px; 185 | } 186 | 187 | .idxfrm ul { 188 | margin-left:0em; 189 | padding-left:0em; 190 | } 191 | .idxfrm ul ul { 192 | margin-left:0.25em; 193 | padding-left:0.25em; 194 | } 195 | 196 | 197 | .idxfrm li { 198 | list-style-type:none; 199 | background-color: ffffff; 200 | color: 000000; 201 | font-size:90%; 202 | padding: 0px; 203 | margin: 0px; 204 | } 205 | 206 | .idxfrm ul li { 207 | background-color: ffffff; 208 | color: 000000; 209 | font-size:70%; 210 | padding: 0px; 211 | margin: 0px; 212 | } 213 | 214 | .idxfrm ul li ul { 215 | background-color: ffffff; 216 | color: 000000; 217 | font-size:50%; 218 | padding: 0px; 219 | margin: 0px; 220 | } 221 | 222 | 223 | /* table of contents */ 224 | .TofC { 225 | background-color: ffffff; 226 | color: 000000; 227 | font-size:100%; 228 | } 229 | 230 | .TofC li { 231 | list-style-type:none; 232 | background-color: ffffff; 233 | color: 000000; 234 | font-size:150%; 235 | } 236 | 237 | .TofC ul li { 238 | background-color: ffffff; 239 | color: 000000; 240 | font-size:100%; 241 | } 242 | 243 | .TofC ul li ul { 244 | background-color: ffffff; 245 | color: 000000; 246 | font-size:80%; 247 | } 248 | -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/src/N64API.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2016 NicoHood 3 | See the readme for credit to other people. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | */ 23 | 24 | // Include guard 25 | #pragma once 26 | 27 | //================================================================================ 28 | // N64 Controller API 29 | //================================================================================ 30 | 31 | CN64Controller::CN64Controller(const uint8_t p) : pin(p){ 32 | // Empty 33 | } 34 | 35 | 36 | void CN64Controller::reset(void) 37 | { 38 | // Reenumerate controller next time 39 | status = N64_Status_t(); 40 | } 41 | 42 | 43 | bool CN64Controller::begin(void) 44 | { 45 | // Try to init the controller 46 | if (!n64_init(pin, &status)) 47 | { 48 | // Reset in any case, as some bytes may have been written. 49 | reset(); 50 | return false; 51 | } 52 | 53 | // No error 54 | return true; 55 | } 56 | 57 | 58 | uint16_t CN64Controller::getDevice(void) 59 | { 60 | // (Optional) swap the first two bytes to compare it easy with the documentation 61 | return status.device; 62 | } 63 | 64 | 65 | bool CN64Controller::connected(void) 66 | { 67 | // Check if the device identifier is at least not zero 68 | return (status.device != NINTENDO_DEVICE_N64_NONE); 69 | } 70 | 71 | 72 | bool CN64Controller::read(void) 73 | { 74 | // Check if the controller was initialized 75 | if (!connected()) 76 | { 77 | // Try to init the controller 78 | if (!begin()) 79 | { 80 | return false; 81 | } 82 | delayMicroseconds(100); 83 | } 84 | 85 | // Read the controller, abort if it fails. 86 | if (!n64_read(pin, &report)) 87 | { 88 | reset(); 89 | return false; 90 | } 91 | 92 | // Return status information for optional use. 93 | // On error the report may have been modified! 94 | return true; 95 | } 96 | 97 | 98 | // TODO 99 | // bool CN64Controller::getRumble(void) 100 | // { 101 | // // Read controller (rumble) state 102 | // return status.rumble; 103 | // } 104 | 105 | 106 | // TODO 107 | // bool CN64Controller::setRumble(bool rumble) 108 | // { 109 | // // Read controller (rumble) state and set new state 110 | // bool oldRumble = getRumble(); 111 | // status.rumble = rumble; 112 | // return oldRumble; 113 | // } 114 | 115 | 116 | bool CN64Controller::end(void) 117 | { 118 | // Try to init the controller 119 | // TODO rumble 120 | if (connected() /* || getRumble() */) 121 | { 122 | // Reset in any case, as some bytes may have been written. 123 | N64_Report_t tmp; 124 | return n64_read(pin, &tmp); 125 | } 126 | 127 | // Error: Already disconnected 128 | return false; 129 | } 130 | 131 | 132 | N64_Status_t CN64Controller::getStatus(void) 133 | { 134 | return status; 135 | } 136 | 137 | 138 | N64_Report_t CN64Controller::getReport(void) 139 | { 140 | return report; 141 | } 142 | 143 | 144 | N64_Data_t CN64Controller::getData(void) 145 | { 146 | // Get the derivated data pointer, copy and return the data 147 | N64_Data_t* dataPtr = this; 148 | N64_Data_t data; 149 | memcpy(&data, dataPtr, sizeof(data)); 150 | return data; 151 | } 152 | 153 | 154 | //================================================================================ 155 | // N64 Console API 156 | //================================================================================ 157 | 158 | CN64Console::CN64Console(const uint8_t p) : pin(p){ 159 | // Empty 160 | } 161 | 162 | 163 | bool CN64Console::write(N64_Data_t &data) 164 | { 165 | // Don't want interrupts getting in the way 166 | uint8_t oldSREG = SREG; 167 | cli(); 168 | 169 | // Write a respond to the N64, depending on what it requests 170 | uint8_t ret = n64_write(pin, &data.status, &data.report); 171 | 172 | // Init 173 | if(ret == 1) 174 | { 175 | // Try to answer a following read request 176 | ret = n64_write(pin, &data.status, &data.report); 177 | } 178 | 179 | // End of time sensitive code 180 | SREG = oldSREG; 181 | 182 | // Return error if no reading was possible 183 | return false; 184 | } 185 | 186 | 187 | bool CN64Console::write(CN64Controller &controller) 188 | { 189 | // Cast controller to its protected (friend) data 190 | N64_Data_t& data = controller; 191 | return write(data); 192 | } 193 | 194 | 195 | bool CN64Console::write(N64_Report_t &report) 196 | { 197 | // Inititalize status and report with default values 198 | N64_Data_t data = defaultN64Data; 199 | 200 | // Copy report into the temporary struct and write to N64 201 | memcpy(&data.report, &report, sizeof(data.report)); 202 | return write(data); 203 | } 204 | -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/src/Gamecube.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2016 NicoHood 3 | See the readme for credit to other people. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | */ 23 | 24 | #include "Gamecube.h" 25 | 26 | //================================================================================ 27 | // Gamecube Controller 28 | //================================================================================ 29 | 30 | bool gc_init(const uint8_t pin, Gamecube_Status_t* status) 31 | { 32 | // Initialize the gamecube controller by sending it a null byte. 33 | // This is unnecessary for a standard controller, but is required for the 34 | // Wavebird. 35 | uint8_t command[] = { 0x00 }; 36 | 37 | // Send the command and read in data 38 | uint8_t receivedBytes = gc_n64_send_get(pin, command, sizeof(command), (uint8_t*)status, sizeof(Gamecube_Status_t)); 39 | 40 | // Return status information for optional use. 41 | // On error the report may have been modified! 42 | return (receivedBytes == sizeof(Gamecube_Status_t)); 43 | } 44 | 45 | 46 | bool gc_origin(const uint8_t pin, Gamecube_Origin_t* origin) 47 | { 48 | // Get the initital controller values when it was plugged in. 49 | // This is used for calibration of the controller. 50 | // If you do not call this function the controller data bit "origin" will be set. 51 | // This results in a not accepting Gamecube host/reinitialization. 52 | uint8_t command[] = { 0x41 }; 53 | 54 | // Send the command and read in data 55 | uint8_t receivedBytes = gc_n64_send_get(pin, command, sizeof(command), (uint8_t*)origin, sizeof(Gamecube_Origin_t)); 56 | 57 | // Return status information for optional use. 58 | // On error the report may have been modified! 59 | return (receivedBytes == sizeof(Gamecube_Origin_t)); 60 | } 61 | 62 | 63 | bool gc_read(const uint8_t pin, Gamecube_Report_t* report, const bool rumble) 64 | { 65 | // Command to send to the gamecube, LSB is rumble 66 | uint8_t command[] = { 0x40, 0x03, rumble }; 67 | 68 | // Send the command and read in data 69 | uint8_t receivedBytes = gc_n64_send_get(pin, command, sizeof(command), (uint8_t*)report, sizeof(Gamecube_Report_t)); 70 | 71 | // Return status information for optional use. 72 | // On error the report may have been modified! 73 | return (receivedBytes == sizeof(Gamecube_Report_t)); 74 | } 75 | 76 | 77 | //================================================================================ 78 | // Gamecube Console 79 | //================================================================================ 80 | 81 | uint8_t gc_write(const uint8_t pin, Gamecube_Status_t* status, Gamecube_Origin_t* origin, Gamecube_Report_t* report) 82 | { 83 | // 0 = no input/error, 1 = init, 2 = origin, 3 = read, 4 = read with rumble 84 | uint8_t ret = 0; 85 | 86 | // Get the port mask and the pointers to the in/out/mode registers 87 | uint8_t bitMask = digitalPinToBitMask(pin); 88 | uint8_t port = digitalPinToPort(pin); 89 | volatile uint8_t* modePort = portModeRegister(port); 90 | volatile uint8_t* outPort = portOutputRegister(port); 91 | volatile uint8_t* inPort = portInputRegister(port); 92 | 93 | // Don't want interrupts getting in the way 94 | uint8_t oldSREG = SREG; 95 | cli(); 96 | 97 | // Read in data from the console 98 | // After receiving the init command you have max 80us to respond (for the data command)! 99 | uint8_t command[3]; 100 | uint8_t receivedBytes = gc_n64_get(command, sizeof(command), modePort, outPort, inPort, bitMask); 101 | 102 | // Init 103 | if (receivedBytes == 1 && command[0] == 0x00) 104 | { 105 | gc_n64_send(status->raw8, sizeof(Gamecube_Status_t), modePort, outPort, bitMask); 106 | ret = 1; 107 | } 108 | // Get origin 109 | else if (receivedBytes == 1 && command[0] == 0x41) 110 | { 111 | gc_n64_send(origin->raw8, sizeof(Gamecube_Origin_t), modePort, outPort, bitMask); 112 | ret = 2; 113 | } 114 | // Get data. Do not check last byte (command[2]), as the flags are unknown 115 | else if (receivedBytes == 3 && command[0] == 0x40 && command[1] == 0x03) 116 | { 117 | gc_n64_send(report->raw8, sizeof(Gamecube_Report_t), modePort, outPort, bitMask); 118 | ret = 3; 119 | // The first byte probably flags a gamecube reading (0x40), as the same 120 | // protocol is also used for N64. The lower nibble seems to be the mode: 121 | // 0x40 (followed by 2 bytes) reading 122 | // 0x41 get origin (1 byte) 123 | // 0x42 (followed by 2 bytes) seems to force mode 0x02 below 124 | // 0x43 (followed by 2 bytes) seems to force mode 0x02 below 125 | 126 | // The 2nd byte (command[1]) seems to request a special data format. 127 | // I've noticed formats that merge the L + R data. 128 | // There seem to be only 4 data formats, the rest is ignore. 129 | // 0x00 First 4 bytes: buttons0+1 + X + Y, C-Stick, L+R minimum of both, 0x00 fixed 130 | // 0x01 First 4 bytes: buttons0+1 + X + Y, C-Stick Horizontal only, R, L, 0x00 fixed 131 | // 0x02 First 4 bytes: buttons0+1 + X + Y, C-Stick Horizontal only, L+R minimum of both, 0x02 fixed, 0x01 fixed 132 | // 0x03 Normal reading 133 | 134 | // I've seen 3 last options for the last byte (command[2]): 135 | // 0x00 Normal reading 136 | // 0x01 Enable rumble 137 | // 0x02 Normal reading, rumble was at least called once 138 | // 0x03 ??? - never seen so far 139 | // Rumble: 1, 5, 9, 13, 17, ... 140 | // You can see that only 4 of those requests are possible, 141 | // the gamecube will ignore the left 6 MSB. 142 | if((command[2] % 4) == 0x01){ 143 | ret = 4; 144 | } 145 | else if((command[2] % 4) == 0x02){ 146 | ret = 5; 147 | } 148 | else if((command[2] % 4) == 0x03){ 149 | ret = 6; 150 | } 151 | } 152 | 153 | // End of time sensitive code 154 | SREG = oldSREG; 155 | 156 | return ret; 157 | } 158 | -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/src/GamecubeAPI.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2016 NicoHood 3 | See the readme for credit to other people. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | */ 23 | 24 | // Include guard 25 | #pragma once 26 | 27 | //================================================================================ 28 | // Gamecube Controller API 29 | //================================================================================ 30 | 31 | CGamecubeController::CGamecubeController(const uint8_t p) : pin(p){ 32 | // Empty 33 | } 34 | 35 | 36 | void CGamecubeController::reset(void) 37 | { 38 | // Reenumerate controller next time 39 | status = Gamecube_Status_t(); 40 | } 41 | 42 | 43 | bool CGamecubeController::begin(void) 44 | { 45 | // Try to init the controller 46 | if (!gc_init(pin, &status)) 47 | { 48 | // Reset in any case, as some bytes may have been written. 49 | reset(); 50 | return false; 51 | } 52 | 53 | // If initialization was successful also get the original controller stats 54 | if (!gc_origin(pin, &origin)) 55 | { 56 | return false; 57 | } 58 | 59 | // No error 60 | return true; 61 | } 62 | 63 | 64 | uint16_t CGamecubeController::getDevice(void) 65 | { 66 | // (Optional) swap the first two bytes to compare it easy with the documentation 67 | return status.device; 68 | } 69 | 70 | 71 | bool CGamecubeController::connected(void) 72 | { 73 | // Check if the device identifier is at least not zero 74 | return (status.device != NINTENDO_DEVICE_GC_NONE); 75 | } 76 | 77 | 78 | bool CGamecubeController::read(void) 79 | { 80 | // Check if the controller was initialized 81 | if (!connected()) 82 | { 83 | // Try to init the controller 84 | if (!begin()) 85 | { 86 | return false; 87 | } 88 | } 89 | 90 | // Read the controller, abort if it fails. 91 | // Additional information: If you press X + Y + Start on the controller for 3 seconds 92 | // It will turn off unless you release the buttons. The recalibration is all done 93 | // on the Gamecube side. If the controller resets origin will have different values for sure. 94 | if (!gc_read(pin, &report, status.rumble)) 95 | { 96 | reset(); 97 | return false; 98 | } 99 | 100 | // Check if controller reported that we read the origin values (check if it disconnected). 101 | // The Gamecube would just request (instantly) the origin again, but we keep things simple. 102 | if (report.origin) { 103 | reset(); 104 | return false; 105 | } 106 | 107 | // Return status information for optional use. 108 | // On error the report may have been modified! 109 | return true; 110 | } 111 | 112 | 113 | bool CGamecubeController::getRumble(void) 114 | { 115 | // Read controller (rumble) state 116 | return status.rumble; 117 | } 118 | 119 | 120 | bool CGamecubeController::setRumble(bool rumble) 121 | { 122 | // Read controller (rumble) state and set new state 123 | bool oldRumble = getRumble(); 124 | status.rumble = rumble; 125 | return oldRumble; 126 | } 127 | 128 | 129 | bool CGamecubeController::end(void) 130 | { 131 | // Try to init the controller 132 | if (connected() || getRumble()) 133 | { 134 | // Reset in any case, as some bytes may have been written. 135 | Gamecube_Report_t tmp; 136 | return gc_read(pin, &tmp, false); 137 | } 138 | 139 | // Error: Already disconnected 140 | return false; 141 | } 142 | 143 | 144 | Gamecube_Status_t CGamecubeController::getStatus(void) 145 | { 146 | return status; 147 | } 148 | 149 | 150 | Gamecube_Origin_t CGamecubeController::getOrigin(void) 151 | { 152 | return origin; 153 | } 154 | 155 | 156 | Gamecube_Report_t CGamecubeController::getReport(void) 157 | { 158 | return report; 159 | } 160 | 161 | 162 | Gamecube_Data_t CGamecubeController::getData(void) 163 | { 164 | // Get the derivated data pointer, copy and return the data 165 | Gamecube_Data_t* dataPtr = this; 166 | Gamecube_Data_t data; 167 | memcpy(&data, dataPtr, sizeof(data)); 168 | return data; 169 | } 170 | 171 | 172 | //================================================================================ 173 | // Gamecube Console API 174 | //================================================================================ 175 | 176 | CGamecubeConsole::CGamecubeConsole(const uint8_t p) : pin(p){ 177 | // Empty 178 | } 179 | 180 | 181 | bool CGamecubeConsole::write(Gamecube_Data_t &data) 182 | { 183 | // Abort if controller was not initialized. 184 | // Gamecube will refuse and weird connect/disconnect errors will occur. 185 | if (data.report.origin) { 186 | return false; 187 | } 188 | 189 | // Don't want interrupts getting in the way 190 | uint8_t oldSREG = SREG; 191 | cli(); 192 | 193 | // Write a respond to the gamecube, depending on what it requests 194 | uint8_t ret = gc_write(pin, &data.status, &data.origin, &data.report); 195 | 196 | // Init 197 | if(ret == 1) 198 | { 199 | // Try to answer a possible following origin request 200 | ret = gc_write(pin, &data.status, &data.origin, &data.report); 201 | } 202 | 203 | // Origin 204 | if(ret == 2){ 205 | // Try to answer a possible following read request 206 | ret = gc_write(pin, &data.status, &data.origin, &data.report); 207 | } 208 | 209 | // End of time sensitive code 210 | SREG = oldSREG; 211 | 212 | // Set rumble depending on read return value 213 | if (ret == 3) { 214 | data.status.rumble = false; 215 | return true; 216 | } 217 | else if (ret == 4) { 218 | data.status.rumble = true; 219 | return true; 220 | } 221 | else if (ret == 5) { 222 | data.status.rumble = false; 223 | return true; 224 | } 225 | 226 | // Return error if no reading was possible 227 | return false; 228 | } 229 | 230 | 231 | bool CGamecubeConsole::write(CGamecubeController &controller) 232 | { 233 | // Cast controller to its protected (friend) data 234 | Gamecube_Data_t& data = controller; 235 | return write(data); 236 | } 237 | 238 | 239 | bool CGamecubeConsole::write(Gamecube_Report_t &report) 240 | { 241 | // Inititalize init and origin with default values 242 | Gamecube_Data_t data = defaultGamecubeData; 243 | 244 | // Copy report into the temporary struct and write to gamecube 245 | memcpy(&data.report, &report, sizeof(data.report)); 246 | return write(data); 247 | } 248 | -------------------------------------------------------------------------------- /ESS-Adapter/ESS-Adapter.ino: -------------------------------------------------------------------------------- 1 | //ESS-Adapter.ino -- master 2 | 3 | //The Following code is released under GNU GENERAL PUBLIC LICENSE Version 3 and uses a nicohood's nintendo library released under MIT license. 4 | 5 | /* Visit Github for more information: https://github.com/Skuzee/ESS-Adapter */ 6 | 7 | /*Basic Wiring Information for ATMEGA: 8 | (Any GPIO pin will work. Please check CONSOLE_PIN and CONT_PIN.) 9 | -CONSOLE_PIN --> DATA to Console 10 | 11 | ***One pull-up resistor per controller DATA pin.*** 12 | -CONT_PIN --> DATA to Controller 13 | -CONT_PIN --> 750ohm Pull-up Resistor --> 3.3v supply from Console 14 | 15 | -5v supply from console --> schottky diode --> Vcc Unregulated Voltage pin. 16 | -GND Pin --> Ground wires 17 | 18 | Make sure the Controller is still connected: insuring the following: 19 | -5v supply from Console --> 5v to Controller (Rumble Motor) 20 | -3.3v supply from Console --> 3.3v wire to Controller 21 | -Grounds from Console --> Grounds to Controller 22 | 23 | If your cable has a braided metal shieding, don't connect it to anything. 24 | */ 25 | 26 | 27 | // Both Controller types can use the same pin, HOWEVER! 28 | // You CANNOT plug two controllers into the same pin at the same time without error. 29 | // If you want two plugs on the same adapter use one pin per controller type. 30 | // (i.e. PIN 4 for GC controller, PIN 3 for N64) Each with their own pull-up resistor. 31 | #define CONT_PIN_GC 4 // GC Controller DATA Pin 32 | #define CONT_PIN_N64 4 // N64 Controller DATA Pin 33 | #define CONSOLE_PIN 2 // Console DATA Pin: 2 yellow, 8 master branch, 2 Dev board 34 | 35 | #define INPUT_DISPLAY // - works on 32u4, needs newest compiled version of NintendoSpy/Retrospy. 36 | #define TRIGGER_THRESHOLD // Makes the L and R triggers act like Gamecube version of OOT. range of sensitivity from 0 to 255. 0 being most sensitive. My controller has a range of ~30 to 240. Comment out to disable. Configurable with controller settings menu. 37 | //#define DEBUG // overwrites IndicatorLights and used for data analyzer. 38 | 39 | //Includes 40 | #include "src/Nintendo/src/Nintendo.h" 41 | #include "ESS.hpp" 42 | #include "extra.hpp" 43 | #include "input-display.hpp" 44 | 45 | #if NINTENDO_VERSION != 1337 46 | #error "Incorrect Nintendo.h library! Compiling with the incorrect version WILL result in 5 volts being output to your controller/console! (Not good.) Make sure the custom Nintendo library (version 1337) is included in the ESS-Adapter/src folder and try again." 47 | #endif 48 | 49 | // Sets CONT_PIN on arduino to read from controller. 50 | CGamecubeController GCcontroller(CONT_PIN_GC); 51 | CN64Controller N64controller(CONT_PIN_N64); 52 | 53 | // Sets CONSOLE_PIN on arduino to write data to console. 54 | CGamecubeConsole console(CONSOLE_PIN); 55 | Gamecube_Data_t data = defaultGamecubeData; // Initilize Gamecube data. Default needed for N64 data to convert correctly. 56 | N64_Report_t N64report = defaultN64Data.report; 57 | 58 | void setup() { 59 | Serial.begin(115200); 60 | loadSettings(); 61 | 62 | #ifdef DEBUG 63 | initializeDebug(); 64 | #else 65 | initilizeStatusLights(); 66 | #endif 67 | } 68 | 69 | void loop() { 70 | static uint8_t deviceID = 0; 71 | 72 | // Need to flush serial buffer if it gets full. else program will halt 73 | 74 | switch (deviceID) { 75 | case 0: // No controller connected. 76 | deviceID = checkConnection(); 77 | break; 78 | 79 | case 5: // N64 controller detected. 80 | deviceID = N64loop(); 81 | break; 82 | 83 | default: // Assume anything else is a GC controller. 84 | deviceID = GCloop(); 85 | } 86 | } 87 | 88 | uint8_t checkConnection() { // Tests for a connection and gets device ID. returns Device ID. 89 | N64_Status_t connectionStatus; // Create a generic Status (N64 and GC status are the same) 90 | connectionStatus.device = 0; // Reset device ID 91 | 92 | // Initilize controller to update device ID 93 | n64_init(CONT_PIN_GC, &connectionStatus); // Check first pin for a controller. 94 | // If CONT_PIN_GC == CONT_PIN_N64 then this will return either controller type. 95 | 96 | if(!connectionStatus.device) { // If no controller connected to first pin: 97 | n64_init(CONT_PIN_N64, &connectionStatus); // Check the other pin. 98 | // If CONT_PIN_GC != CONT_PIN_N64 then this will return n64 if connected. 99 | } 100 | 101 | tryPrint("Searching; ID:"); 102 | tryPrintln(String(connectionStatus.device)); 103 | delay(500); 104 | 105 | return char(connectionStatus.device); 106 | } 107 | 108 | void delayRead(uint8_t readDelay) { // OOT reads the controller twice every 16ms. (as in, two reads in quick succession ~1ms apart.) The ideal timing is to wait as long as possible to read the controller data, so it's fresh when the console requests it. We wait 14ms every other read cycle. Prevents the arduino from reading the controller data too early and then having to wait 15ms to send it to the wii. Input delay added to controller is between 0.6ms and 0.9ms. (average of 0.6ms). 109 | static uint8_t readDelayFlipFlop = 0; 110 | 111 | if (readDelayFlipFlop) 112 | delay(readDelay); 113 | 114 | readDelayFlipFlop = ! readDelayFlipFlop; 115 | } 116 | 117 | uint8_t GCloop() { // Wii vc version of OOT updates controller twice every ~16.68ms (with 1.04ms between the two rapid updates.) 118 | static uint8_t firstRead = 1; // 1 if the previous loop failed. 119 | 120 | delayRead(14); // This helps reduce input lag. remove if a game/controller combo is glitching. 121 | 122 | if (!GCcontroller.read()) { // Attempt to read controller. 123 | tryPrintln("Failed GC read:"); 124 | firstRead = 1; // If it fails to read, assume next successful read will be the first. 125 | } 126 | else { 127 | data = GCcontroller.getData(); // Successful read: copy controller data to access. 128 | 129 | if (firstRead) { // Special case: first read: change settings. 130 | firstRead = changeSettings(data.report); // Loops while settings are being changed. 131 | } 132 | else { 133 | 134 | #ifdef TRIGGER_THRESHOLD // If defined, makes Gamecube controller triggers act more like GC collectors edition. Analog press instead of having to click them all the way down. 135 | analogTriggerToDigitalPress(data.report); 136 | #endif 137 | 138 | #ifdef INPUT_DISPLAY 139 | writeToUSB_BYTE(data); 140 | #endif 141 | } 142 | } 143 | 144 | normalize_origin(&data.report.xAxis, &data.origin.inititalData.xAxis); 145 | 146 | if (settings.ess_map == ESS_ON && settings.game_selection == GAME_OOT) // if OOT and ESS on: 147 | invert_vc_gc(&data.report.xAxis); 148 | 149 | console.write(data); // Loop waits here until console requests an update. 150 | GCcontroller.setRumble(data.status.rumble); // Set controller rumble status. 151 | 152 | return GCcontroller.getDevice(); 153 | } 154 | 155 | uint8_t N64loop() { // Wii vc version of OOT updates controller twice every ~16.68ms (with 1.04ms between the two rapid updates.) 156 | static uint8_t firstRead = 1; // 1 if the previous loop failed. 157 | 158 | 159 | delayRead(14); // This helps reduce input lag. remove if a game/controller combo is glitching. 160 | 161 | if (!N64controller.read()) { // Attempt to read controller. 162 | tryPrintln("Failed N64 read:"); 163 | firstRead = 1; // If it fails to read, assume next successful read will be the first. 164 | } 165 | else { 166 | N64report = N64controller.getReport(); 167 | 168 | if (firstRead || enterSettingsMenuN64Controller(N64report)) { // Special case: first read: change settings. 169 | N64toGC_buttonMap_Generic(N64report, data.report); // Use the generic button map for the settings menu to keep things consistent. 170 | firstRead = changeSettings(data.report); // Loops while settings are being changed. 171 | } 172 | else { 173 | 174 | #ifdef INPUT_DISPLAY 175 | writeToUSB_BYTE(N64report); 176 | #endif 177 | 178 | switch (settings.game_selection) { // Convert N64 data/buttons to GC data/buttons depending on what game/ess setting is selected 179 | 180 | case GAME_OOT: 181 | N64toGC_buttonMap_OOT(N64report, data.report); 182 | break; 183 | 184 | case GAME_YOSHI: 185 | N64toGC_buttonMap_Yoshi(N64report, data.report); 186 | break; 187 | 188 | default: 189 | N64toGC_buttonMap_Generic(N64report, data.report); 190 | } 191 | } 192 | } 193 | 194 | console.write(data); // Loop waits here until console requests an update. 195 | // N64 Rumble motor function??? 196 | 197 | return N64controller.getDevice(); 198 | } 199 | -------------------------------------------------------------------------------- /ESS-Adapter/extra.cpp: -------------------------------------------------------------------------------- 1 | //extra.cpp 2 | 3 | //The Following code is released under GNU GENERAL PUBLIC LICENSE Version 3 and uses a nicohood's nintendo library released under MIT license. 4 | 5 | #include "extra.hpp" 6 | #include "input-display.hpp" 7 | #include 8 | 9 | EEPROM_settings settings; 10 | 11 | void softReset() { 12 | asm volatile (" jmp 0"); // Soft-reset, Assembly command that jumps to the start of the reset vector. 13 | } 14 | 15 | void analogTriggerToDigitalPress(Gamecube_Report_t& GCreport) { // Maps analog L and R presses to digital presses. Range of sensitivity from 0 to 255. 0 being most sensitive. My controller has a range of ~30 to 240 16 | if (settings.trigger_threshold_enabled) 17 | { 18 | if (GCreport.left >= settings.trigger_threshold) 19 | GCreport.l = 1; 20 | if (GCreport.right >= settings.trigger_threshold) 21 | GCreport.r = 1; 22 | } 23 | } 24 | 25 | uint8_t enterSettingsMenuN64Controller(const N64_Report_t& N64report) { 26 | if (N64report.l && N64report.r && N64report.cup && N64report.cdown && N64report.cright && N64report.cleft) 27 | return 1; 28 | else 29 | return 0; 30 | } 31 | 32 | uint8_t changeSettings(Gamecube_Report_t& GCreport) { // read the initial buttons of the controller and set EEPROM accordingly. 33 | IndicatorLights(1, settings.game_selection); 34 | IndicatorLights(2, settings.ess_map); 35 | 36 | if (GCreport.l && GCreport.r) { 37 | 38 | if (GCreport.z) { // Press Z to reset settings to default. 39 | 40 | if(EEPROM.read(0)==EEPROM_VERSION) 41 | EEPROM.update(0, 255); 42 | else 43 | EEPROM.update(0, EEPROM_VERSION); 44 | 45 | tryPrintln("."); 46 | tryPrintln(EEPROM.read(0)==255 ? "Restoring Factory Settings. Z to undo." : "Reset Cancelled."); 47 | delay(MENU_BUTTON_TIMEOUT); 48 | } 49 | 50 | if (GCreport.dright || GCreport.dleft) { // Cycle n64 game button maps 51 | settings.game_selection += GCreport.dright - GCreport.dleft + 3; 52 | settings.game_selection %= 3; 53 | tryPrintln(""); 54 | 55 | switch (settings.game_selection) { 56 | case GAME_OOT: 57 | tryPrintln("OOT."); 58 | settings.ess_map = ESS_ON; 59 | break; 60 | 61 | case GAME_YOSHI: 62 | tryPrintln("Yoshi Story."); 63 | settings.ess_map = ESS_ON; 64 | break; 65 | 66 | case GAME_GENERIC: 67 | tryPrintln("Generic"); 68 | settings.ess_map = ESS_OFF; 69 | break; 70 | } 71 | rumbleMotor(200,300,settings.game_selection+1); 72 | delay(MENU_BUTTON_TIMEOUT); 73 | } 74 | 75 | if (GCreport.dup) { // ESS on 76 | tryPrintln(""); 77 | 78 | if (settings.game_selection == GAME_GENERIC) 79 | tryPrintln("No ESS for Generic"); 80 | else { 81 | settings.ess_map = ESS_ON; 82 | 83 | switch (settings.ess_map) { 84 | case ESS_OFF: 85 | tryPrintln("ESS: OFF."); 86 | break; 87 | 88 | case ESS_ON: 89 | tryPrintln("ESS: ON."); 90 | break; 91 | } 92 | } 93 | rumbleMotor(200+settings.ess_map*600,300,1); 94 | delay(MENU_BUTTON_TIMEOUT); 95 | } 96 | 97 | if (GCreport.ddown) { // ESS off 98 | settings.ess_map = ESS_OFF; 99 | tryPrintln(""); 100 | 101 | switch (settings.ess_map) { 102 | case ESS_OFF: 103 | tryPrintln("ESS: OFF."); 104 | break; 105 | 106 | case ESS_ON: 107 | tryPrintln("ESS: ON."); 108 | break; 109 | } 110 | rumbleMotor(200+settings.ess_map*600,300,1); 111 | delay(MENU_BUTTON_TIMEOUT); 112 | } 113 | 114 | if (GCreport.a) { // Input Display Toggle. 115 | settings.input_display_enabled = !settings.input_display_enabled; 116 | tryPrintln(""); 117 | tryPrint("Input Display: "); 118 | tryPrintln(settings.input_display_enabled ? "ON" : "OFF"); 119 | rumbleMotor(200+settings.input_display_enabled*600,400,1); 120 | delay(MENU_BUTTON_TIMEOUT); 121 | } 122 | 123 | if (GCreport.b) { // Trigger Threshold Toggle. 124 | settings.trigger_threshold_enabled = !settings.trigger_threshold_enabled; 125 | tryPrintln(""); 126 | tryPrint("trigger Threshold: "); 127 | tryPrintln(settings.trigger_threshold_enabled ? String(settings.trigger_threshold) : "OFF"); 128 | rumbleMotor(200+settings.trigger_threshold_enabled*600,400,1); 129 | delay(MENU_BUTTON_TIMEOUT); 130 | } 131 | 132 | if (GCreport.y) { // Trigger Threshold Inc 133 | tryPrintln(""); 134 | 135 | if (settings.trigger_threshold_enabled && settings.trigger_threshold+10<=250) { 136 | settings.trigger_threshold+=10; 137 | tryPrint("trigger Threshold: "); 138 | tryPrintln(settings.trigger_threshold); 139 | rumbleMotor((100+settings.trigger_threshold)*settings.trigger_threshold_enabled,300,1); 140 | } else { 141 | tryPrint("trigger Threshold: "); 142 | tryPrintln(settings.trigger_threshold_enabled ? String(settings.trigger_threshold) : "OFF"); 143 | } 144 | 145 | delay(MENU_BUTTON_TIMEOUT); 146 | } 147 | 148 | if (GCreport.x) { // Trigger Threshold Dec 149 | tryPrintln(""); 150 | 151 | if (settings.trigger_threshold_enabled && settings.trigger_threshold-10>=10) { 152 | settings.trigger_threshold-=10; 153 | tryPrint("trigger Threshold: "); 154 | tryPrintln(settings.trigger_threshold); 155 | rumbleMotor((100+settings.trigger_threshold)*settings.trigger_threshold_enabled,300,1); 156 | } else { 157 | tryPrint("trigger Threshold: "); 158 | tryPrintln(settings.trigger_threshold_enabled ? String(settings.trigger_threshold) : "OFF"); 159 | } 160 | 161 | delay(MENU_BUTTON_TIMEOUT); 162 | } 163 | 164 | tryPrint("."); 165 | delay(50); 166 | 167 | return 1; //if settings are being changed, continue to loop. 168 | } 169 | else { 170 | EEPROM.put(1, settings); // store any changed settings. 171 | tryPrintln(""); 172 | tryPrintln("Changes Saved."); 173 | loadSettings(); // check to see if eeprom was factory reset. 174 | 175 | return 0; // return 0, read controller normally now. 176 | } 177 | } 178 | 179 | void loadSettings() { 180 | if (EEPROM.read(0)!=EEPROM_VERSION) { // if EEPROM (position 0) does not match EEPROM_VERSION, write default settings to EEPROM and update eeprom verion. This allows me to reprogram eeprom on new devices, or if the settings eeprom format changes and it's changes are incompatable with the old format. 181 | settings = {INPUT_DISPLAY_ON, GAME_OOT, ESS_ON, TRIGGER_THRESHOLD_OFF, DEF_TRIGGER_THRESHOLD}; 182 | EEPROM.put(1, settings); 183 | EEPROM.update(0, EEPROM_VERSION); 184 | delay(2000); 185 | tryPrintln(""); 186 | tryPrintln("Saved to EEPROM"); 187 | } 188 | else { 189 | EEPROM.get(1, settings); 190 | } 191 | printSetting(); 192 | } 193 | 194 | void printSetting() { 195 | tryPrint("Input Display: "); 196 | tryPrintln(settings.input_display_enabled ? "ON" : "OFF"); 197 | 198 | tryPrint("trigger Threshold: "); 199 | tryPrintln(settings.trigger_threshold_enabled ? String(settings.trigger_threshold) : "OFF"); 200 | 201 | tryPrint("ESS: "); 202 | switch (settings.ess_map) { 203 | case ESS_OFF: 204 | tryPrintln("OFF"); 205 | break; 206 | 207 | case ESS_ON: 208 | tryPrintln("ON"); 209 | break; 210 | } 211 | 212 | tryPrint("Game : "); 213 | switch (settings.game_selection) { 214 | case GAME_OOT: 215 | tryPrintln("OOT"); 216 | break; 217 | 218 | case GAME_YOSHI: 219 | tryPrintln("Yoshi Story"); 220 | break; 221 | 222 | case GAME_GENERIC: 223 | tryPrintln("Generic"); 224 | break; 225 | } 226 | } 227 | 228 | void initializeDebug() { 229 | pinMode(DEBUG_READ, OUTPUT); 230 | pinMode(DEBUG_ESS, OUTPUT); 231 | pinMode(DEBUG_INPUT, OUTPUT); 232 | pinMode(DEBUG_WRITE, OUTPUT); 233 | } 234 | 235 | void initilizeStatusLights() { 236 | pinMode(LED1_PIN_R, OUTPUT); 237 | pinMode(LED1_PIN_G, OUTPUT); 238 | pinMode(LED1_PIN_B, OUTPUT); 239 | pinMode(LED2_PIN_R, OUTPUT); 240 | pinMode(LED2_PIN_G, OUTPUT); 241 | pinMode(LED2_PIN_B, OUTPUT); 242 | 243 | IndicatorLights(1, settings.game_selection); 244 | IndicatorLights(2, settings.ess_map); 245 | 246 | } 247 | 248 | // Red = 0, Green = 1, Blue = 2 249 | void IndicatorLights(uint8_t LEDNumber, uint8_t LEDcolor) { 250 | if (LEDNumber == 1) { // Set LED for Game. 251 | digitalWrite(LED1_PIN_R, LED_OFF); 252 | digitalWrite(LED1_PIN_G, LED_OFF); 253 | digitalWrite(LED1_PIN_B, LED_OFF); 254 | 255 | switch (LEDcolor) { 256 | case GAME_OOT: 257 | digitalWrite(LED1_PIN_R, LED_ON); 258 | break; 259 | 260 | case GAME_YOSHI: 261 | digitalWrite(LED1_PIN_G, LED_ON); 262 | break; 263 | 264 | case GAME_GENERIC: 265 | digitalWrite(LED1_PIN_B, LED_ON); 266 | break; 267 | } 268 | } else if (LEDNumber == 2) { // Set LED for ESS setting. 269 | digitalWrite(LED2_PIN_R, LED_OFF); 270 | digitalWrite(LED2_PIN_G, LED_OFF); 271 | digitalWrite(LED2_PIN_B, LED_OFF); 272 | 273 | switch (LEDcolor) { 274 | case ESS_OFF: 275 | digitalWrite(LED2_PIN_R, LED_ON); 276 | break; 277 | 278 | case ESS_ON: 279 | digitalWrite(LED2_PIN_G, LED_ON); 280 | break; 281 | } 282 | } else { 283 | digitalWrite(LED1_PIN_R, LED_OFF); 284 | digitalWrite(LED1_PIN_G, LED_OFF); 285 | digitalWrite(LED1_PIN_B, LED_OFF); 286 | digitalWrite(LED2_PIN_R, LED_OFF); 287 | digitalWrite(LED2_PIN_G, LED_OFF); 288 | digitalWrite(LED2_PIN_B, LED_OFF); 289 | } 290 | } 291 | 292 | void rumbleMotor(uint16_t duration, uint16_t pause, uint8_t pulses) { 293 | for (uint8_t i = 0; i Don't Filter COM Ports. To see the arduino. 29 | ![retrospy - don't filter ports](https://raw.githubusercontent.com/Skuzee/ESS-Adapter/master/retrospy-dont-filter.png "retrospy-dont-filter.png") 30 | 31 | 32 | ## Downloading Arduino IDE and Uploading firmware to arduino. 33 | 34 | **Youtube Video Click Below** 35 | [![youtube video](https://img.youtube.com/vi/iTfaGLxnyhg/0.jpg)](https://www.youtube.com/watch?v=iTfaGLxnyhg) 36 | 37 | Download Arduino IDE 38 | https://www.arduino.cc/en/software 39 | 40 | Download and unzip the github files. 41 | https://github.com/Skuzee/ESS-Adapter/archive/refs/heads/master.zip 42 | 43 | Open the ESS-Adapter.ino file with Arduino IDE 44 | 45 | Tools->Board->Board Manager 46 | Search "Sparkfun" 47 | Install "SparkFun AVR Boards" 48 | Close board manager. 49 | 50 | Plug ESS adapter into PC via a good USB cable. 51 | Select Tools->Boards->SparkFun AVR Boards->SparkFun Pro Micro 52 | Select Tools->Processor->ATmega32U4 (5V 16MHz) 53 | Select Tools->Port->COM_X (Usually the highest number, not usually 1 or 2) 54 | 55 | Click the UPLOAD button. ctrl+U 56 | 57 | **If this process does not work, try a different cable, different port, or restart your PC. COM ports can be finicky.** 58 | You can use the arduino IDE Serial Monitor ctrl+shft+M to view the settings menu as text and adjust settings easily. 59 | 60 | ## Settings Menu Controller Shortcuts 61 | ![Serial Monitor Example](https://raw.githubusercontent.com/Skuzee/ESS-Adapter/master/serial-monitor-example.PNG "Arduino IDE Serial Monitor") 62 | Connecting the adapter to a computer via usb and opening a serial monitor (like the one in the Arduino IDE) will allow you to view the current settings. BAUD 115200 63 | Settings are saved in EEPROM and persist through power cycles. 64 | Currently when OOT or Yoshi Story is selected as the active button map, the ESS defaults back to ON. Generic Map does not have ESS functionality and defaults to OFF. 65 | The 'factory default' settings are: Game: OOT, ESS: ON, Input Display: ON, Trigger Fix: OFF, Trigger Threshold: 100. 66 | 67 | **Gamecube Controller:** 68 | Press and Hold L and R triggers all the way in. 69 | Press X Y and Start for ~3 seconds to reset the controller. 70 | Keep L and R held while changing settings. 71 | - D-pad Left/Right will change between Games. Currently There is OOT, Yoshi Story, and a Generic Map. *Affects which N64 button map is used, and what ESS map is used. (Currently only OOT ESS Map Exists) 72 | - D-pad Up Enables ESS. *ESS defaults to ON when game is changed to OOT. 73 | - D-pad Down Disables ESS. *ESS defaults to OFF when game is changed to generic. 74 | - A toggles Input Display 75 | - B Toggles L/R Trigger Fix (partial trigger press activates L and R buttons) 76 | - Y Increase Trigger Threshold (Max of 250) *Lower is more sensitive. Typical Controller has a physical range of ~30 to 230. 77 | - X Decrease Trigger Threshold (Min of 10) 78 | - Z Reset Settings to Default. *Pressing Z, and then exiting the settings menu will reset the settings to "factory default". Press Z again to cancel. 79 | 80 | **N64 Controller:** 81 | Press and Hold L and R buttons. 82 | ~~Mash~~ Press all 4 c buttons at once momentarily. 83 | Keep L and R held. 84 | - D-pad Left/Right will change between N64 button mappings. Currently There is OOT, Yoshi Story, and a Generic Map. 85 | - D-pad Up Enables ESS. *ESS defaults to ON when game is changed to OOT. 86 | - D-pad Down Disables ESS. *ESS defaults to OFF when game is changed to generic. 87 | - A toggles Input Display 88 | - Z Reset Settings to Default. *Pressing Z, and then exiting the settings menu will reset the settings to "factory default". Press Z again to cancel. 89 | 90 | 91 | ![n64-controller-button-map](https://raw.githubusercontent.com/Skuzee/ESS-Adapter/master/n64-controller-button-map.png "n64-controller-button-map.png") 92 | *The n64 generic button map might not be very useful unless maybe you're tying to play a Gamecube game with an N64 controller? (That doesn't need X and Y).* 93 | 94 | ## Wiring 95 | Any digital input pins will work. **Make sure you have them set correctly at the top of the .ino file.** Depending on the board and layout sometimes I use different pins, so double check. Pins 10,14,15,16,18,19 are used for optional RGB indicator lights. 96 | 97 | Pin 2 is the default data pin for the Wii. 98 | Pin 4 is the default data pin for both controller types. 99 | **You CANNOT plug two controllers into the same pin at the same time without error.** 100 | If you want two plugs on the same adapter use one pin per controller type. 101 | (i.e. PIN 4 for GC controller, PIN 3 for N64) Each with their own pull-up resistor. 102 | 103 | Both Common Cathode and Common Anode LEDs work and setting can be set in extras.hpp 104 | LED 1: Red pin 10, Green pin 16, Blue pin 14 105 | LED 2: Red pin 15, Green pin 18(A0), Blue pin 19(A1) 106 | 107 | #### A Note About Powering different Arduinos 108 | There are too many variations for me to correctly suggest how to hook power to the arduino directly from the Wii.Each Arduino has different mosfets/diodes/regulators/wiring. The absolute SAFEST way to power your arduino is from USB only! That means using a short usb cord to one of the wii usb ports, or to your PC (for use with the input display function.) 109 | If you don't intend to use the input display, or you want it to work without the usb cable, it's possible to connect the 5v wire from the controller cable to the arduino directly. As stated above, every arduino is different and I highly suggest you use a diode and know what you are doing. 110 | - Arduino UNO: The safest way to power would be either from a USB cable only (connected to the Wii or computer). It's possible to power it from the Wii 5v controller wire using a step-up DC-DC boost converter (~7v-9v) to the barrel jack. 111 | - Arduino Nano: Power the board from the Wii 5v wire through a Schottky diode to the 5v pin (not the VIN pin) 112 | - Sparkfun Pro Micro 5v: Power from the Wii 5v wire through a Schottky diode to the VCC pin (not the RAW pin). Make sure PCB jumper J1 is not soldered closed. 113 | ![Jumper J1](https://raw.githubusercontent.com/Skuzee/ESS-Adapter/master/JumperJ1.jpg "Jumper J1") 114 | ![Wiring](https://raw.githubusercontent.com/Skuzee/ESS-Adapter/master/GC-Schematic.png "Basic Pro Micro Schematic") 115 | The following wiring information will reference Nintendo's Gamecube coloring scheme! 116 | Be warned, most gamecube extension cables are different. 117 | 118 | |Nintendo Color | Use | Notes 119 | |--- | --- | ---| 120 | |Yellow | 5v Supply | | 121 | |Red | Data 3.3v | | 122 | |Green | Ground | | 123 | |White | Ground | (Shown as Grey in schematic) | 124 | |Black | Shielding | (May not be present on some cables) | 125 | |Blue | 3.3v Supply | | 126 | 127 | ## Parts & Tools 128 | At a minimum you'll need: 129 | - A 16MHz Atmel AVR Arduino/Clone. 130 | - One or Two 750 ohm Resistor (500ohm-1000ohm would work in a pinch.) 131 | - A soldering iron. 132 | - Tools to cut and strip wire. 133 | 134 | Depending on what components you use, you may want: 135 | - Heat shrink tubing 136 | - A project enclosure 137 | - Small cable ties for strain relief. 138 | - Prototype PCB/Perf board. 139 | - Straight and Right Angle pin headers. 140 | - Dupont female plug crimp terminals & crimping tool. 141 | - Assorted lengths of jumper wire. 142 | - Kapton tape. 143 | 144 | ## Community 145 | Join our [Ocarina of Time Speedrunning Discord](https://discord.gg/EYU785K) to chat and ask any questions: Contact Angst#4857 in the #adapters-and-inputdisplays channel. 146 | 147 | -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/extras/Nintendo 64 Controller Information.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | Nintendo 64 Controller Information 4 | 5 | 6 |

intendo 64 Controllers

7 |
8 | 9 |

While I was in Pittsburgh for an extended period of time for work, I got an 10 | urge to interface an N64 controller to one of my projects. Don't ask why, I get 11 | these weird urges sometimes. heh. 12 |

13 | 14 |

Before we get too far into this -- I am only interested in the hardware level 15 | control of the controller and devices attached to the controller. I'm not interested 16 | in emulation of the N64 or c0py1nG y3r 31337 r0m 1m4G35, so DON'T ASK. The 17 | following links are to places with similar technical or emulation interests. I 18 | doubt they are interested in swapping ROMs, either.

19 | 20 |
21 | Dextrose
22 | Steven Hans' N64 Stuff
23 | Ken Kaarvik's Gameboy Stuff
24 |
25 |

26 | 27 | 28 |



29 |

Anyway, upon further inspection, the N64 <--> Controller protocol is relatively 30 | simple to imitate. This page is dedicated to furthering the understanding of all 31 | aspects of the controller. Right now I can get it to return the button status, 32 | and with time I'll figure out how the RumblePak and MemPaks are talked to.

33 | 34 |

The electrical specification chosen by Nintendo seems very close to Apple's 35 | DeskTop Bus protocol, where all data transfer takes place on a single wire, 36 | and the duty cycle of the signal determining the data. Since I'm not familiar 37 | with the ADB protocol any more than that, I can't comment further.

38 | 39 |

However, it is important that anyone who is interested in talking to this 40 | thing on a hardware level knows that there is a particular way in which you 41 | must interface -- DO NOT ever drive the line high! -- The N64 or the 42 | controller may bring the line low at any time and if you're trying to drive it 43 | high you may damage either the controller, the console, or whatever you're using 44 | to talk to it! The controller has a built-in pullup resistor -- either make 45 | damn sure that whatever you're using to play with it doesn't drive the line high 46 | or is incapable of driving it high. Open-collector outputs are handy for this 47 | kind of thing.

48 | 49 | 50 |

51 | Here's the first step: understanding the encoding of bits on the 1-wire 52 | interface. It appears that all bits are 4us wide, and it's the amount of low 53 | time that determines the bit's value. Please note that the assignment of '1' 54 | and '0' has been done my myself; I could have them ass-backwards, but they 55 | seem to make sense to me. :-) 56 |
57 | Also, the communications format includes a stop bit at the end of everything. 58 | All this involves is letting go of the line and waiting 4us before doing 59 | anything else.

60 | 61 |


62 |
63 | 64 |

This is sent out to the controller upon powerup, and some games (StarFox64 65 | does this) send this every time they want to read button status as well. Why, 66 | I don't know. I don't have all the bits figured out yet, but I have 3 scope 67 | traces that give some insight... A controller with a MemoryPak gives a 68 | different response from one with a RumblePak and they both give different responses 69 | than one without any kind of cartridge plugged in. (I'll put this up as 70 | soon as I get the scope software reinstalled.)

71 | 72 |


73 |
74 | 75 |

Sending 0x01 to the controller makes it respond with 32 bits of button 76 | information. Please note that the controller responds VERY QUICKLY -- within 77 | 2 to 3us on my particular unit. Whatever you're using to talk to this thing has 78 | to be very quick. An 8MHz PIC would have a difficult time doing this. Better 79 | to use a 10-20MHz one and bit-bang it.

80 | 81 | The information is in the following format:
82 |

(1 = button pressed, 0 = button released)

83 | 84 | 85 | 86 | 87 |
BitFunction
0A 88 |
1B 89 |
2Z 90 |
3Start 91 |
4Directional Up 92 |
5Directional Down 93 |
6Directional Left 94 |
7Directional Right 95 |
8unknown (always 0) 96 |
9unknown (always 0) 97 |
10L 98 |
11R 99 |
12C Up 100 |
13C Down 101 |
14C Left 102 |
15C Right 103 |
104 | 105 |

The next 16 bits are for the analog joystick.

106 | 107 |

The analog joystick is an optical module and operates very much the same as 108 | most mice. If I recall correctly, it has 24 'positions' in each direction from 109 | center, with center returning 0. If you're not interested in using the whole 110 | controller, this joystick 111 | is a seperate module and can be removed from the controller. It's easy to 112 | use... 6 pin interface, 2 for power, then the remaining 4 are for the X and Y 113 | I/Q signals. Very nice. Not too bad in price, either.

114 | 115 |

The information returned in the last 16 bits from the controller is for the 116 | signed X and Y information, respectively. i.e. the first 8 bits are the signed 117 | X position, and the last 8 are for the signed Y position. From center: up and 118 | right is positive, down and left is negative.

119 | 120 |

28 July 1999 - I finally got around to updating this page. I had made a 121 | mistake in the first edition of this information -- The controller sends a stop 122 | bit which I was mistakenly including as data. The data above is now corrected. 123 | Thanks to those of you who emailled me with that correction. 124 | 125 |

The folks at the N64 Controller Interface link above have discovered how to 126 | make the rumble pak function. That information is coming here shortly for 127 | inclusion. I am also close to having my N64 controller reconnaissance unit 128 | working, so getting full info on the rumble and mempaks should be coming soon. 129 | :-) 130 | 131 |

Right now, here is the base info on how to do it:
132 | To Init: send 03 80 01 followed by 34(!) 80's
133 | To Start Rumble: 03 c0 1b followed by 32 01's
134 | To Stop Rumble: 03 c0 1b followed by 32 00's
135 | After starting or stopping a rumble, the controller returns with 3 bytes. I have 136 | not personally done this before, so I don't know what those three bytes are (yet). 137 | Thanks go to Ken Kaarvic for posting this info to the Gameboy development list, and 138 | further thanks go to Stephen Hans for providing Ken with the original information. 139 | 140 |

Ken Kaarvic has used this information here to successfully connect an N64 141 | controller to a Gameboy through the link port. Head over to his page for more 142 | information on that. 143 | 144 |



145 |

146 | 147 | When I get some time I want to get a little PIC connected up inbetween the 148 | controller and the console and start having it do reconnaissance so I can get 149 | more information regarding RumblePaks and MemPaks. After that I'd like to get 150 | the services of a skilled Windows 95 VXD writer to create a nice VXD so I can 151 | use the N64 as a gaming device, complete with functionality for RumblePaks and 152 | MemPaks. Who knows, this could be marketable. I know there are some out there 153 | now that have adapters but AFAIK nobody can talk to RumblePaks and MemPaks.

154 | 155 |


156 |

Ideas for N64 data:
157 |

    158 |
  • ROOMblePak - modify a standard Rumblepak to drive a small AC motor with an offset 159 | weight and place it under your couch :-) 160 |
  • TerrorPak - Same idea, but give the user a mild electrical shock instead of rumbling 161 |
162 | I've actually had emails regarding the latter idea! My suggestion: optically isolate 163 | and on the side where you connect to your body -- USE A BATTERY -- very important. 164 | You don't want any kind of link to AC or significant current. That keeps your masochistic 165 | tendencies at least safe. :-) 166 | 167 |

Ideas I'm using this information for:
168 |

    169 |
  • N64 Controller Reconnaissance - a PIC which intercepts and echoes console <--> 170 | controller communications and spits out the data to a PC through RS-232. 171 |
  • N64-PC Interface - There are a number of people already doing it. Here is why mine's 172 | different: 173 |
      174 |
    • uses VERY simple interface -- one or two ICs 175 |
    • interfaces to game port like any other joystick 176 |
    • useable by any game through DirectX style interface 177 |
    • open-source, open design model 178 |
    • works with any N64 controller. Jittery third party units will work okay 179 |
    • works with Rumble and Memory Paks through standard interface 180 |
    181 |
  • N64 Macro controller? 182 |
  • N64 Keyboard? 183 |
184 |

Email me if you're interested.

185 | 186 |


187 |
188 |

Last updated: 28 July 1999

189 | 190 | 191 | 192 | -------------------------------------------------------------------------------- /ESS-Adapter/ESS.cpp: -------------------------------------------------------------------------------- 1 | //ESS.cpp 2 | 3 | //The Following code is released under GNU GENERAL PUBLIC LICENSE Version 3 and uses a nicohood's nintendo library released under MIT license. 4 | 5 | #include "ESS.hpp" 6 | #include "extra.hpp" 7 | 8 | const PROGMEM char one_dimensional_map[] = "\x00\x00\x10\x10\x11\x11\x12\x12\x13\x13\x14\x14\x15\x15\x16\x16\x16\x17\x17\x17\x18\x18\x19\x19\x1a\x1a\x1a\x1b\x1b\x1b\x1c\x1c\x1d\x1d\x1d\x1e\x1e\x1e\x1f\x1f !!!\"\"\"###$$$%%%&&&'''((()))***+++,,,,---...///00001111222333344445555666677778888899999::::;;;;;<<<<<=====>>>>>??????@@@"; 9 | const PROGMEM char triangular_map[] = ",,-,.,.,/,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,9,:,:,;,;,<,<,<,=,=,>,>,>,?,?,?,@,--.-.-/-0-0-1-1-2-2-3-3-4-4-5-5-6-6-7-7-8-8-9-9-9-:-:-;-;-<-<-<-=-=->->->-?-?-?-@,..../.0.0.1.1.2.2.3.3.4.4.5.5.6.6.7.7.8.8.9.9.9.:.:.;.;.<.<.<.=.=.>.>.>.?-?-?-?-../.0.0.1.1.2.2.3.3.4.4.5.5.6.6.7.7.8.8.9.9.9.:.:.;.;.<.<.<.=.=.>.>.>.?-?-?-?-//0/0/1/1/2/2/3/3/4/4/5/5/6/6/7/7/8/8/9/9/9/:/:/;/;//>/>/>/>/?-?-000010102020303040405050606070708080909090:0:0;0;0<0<0<0=0=0>/>/>/>/>/>/>/0010102020303040405050606070708080909090:0:0;0;0<0<0<0=0=0=0>/>/>/>/>/>/11112121313141415151616171718181919191:1:1;1;1<1<1<1=0=0=0>/>/>/>/>/>/112121313141415151616171718181919191:1:1;1;1<1<1<1<1<1=0=0>/>/>/>/>/2222323242425252626272728282929292:2:2;2;2<1<1<1<1<1<1=0=0>/>/>/>/22323242425252626272728282929292:2:2;2;2;2<1<1<1<1<1<1<1=0=0>/>/333343435353636373738383939393:3:3;3;3;3;3<1<1<1<1<1<1<1=0=0>/3343435353636373738383939393:3:3;3;3;3;3;3<1<1<1<1<1<1<1<1=044445454646474748484949494:4:4:4;3;3;3;3;3<1<1<1<1<1<1<1<1445454646474748484949494:4:4:4:4;3;3;3;3;3;3<1<1<1<1<1<1555565657575858595959595:4:4:4:4;3;3;3;3;3;3<1<1<1<1<1556565757585859595959595:4:4:4:4;3;3;3;3;3;3<1<1<1<1666676768686869595959595:4:4:4:4;3;3;3;3;3;3;3<1<1667676868686959595959595:4:4:4:4:4;3;3;3;3;3;3<1777777868686959595959595:4:4:4:4:4;3;3;3;3;3;3777777868686869595959595:4:4:4:4:4;3;3;3;3;377777786868686959595959595:4:4:4:4:4;3;3;377777786868686959595959595:4:4:4:4:4;3;377777786868686959595959595:4:4:4:4:4;377777786868686959595959595:4:4:4:4:477777786868686959595959595:4:4:4:47777778686868695959595959595:4:47777778686868695959595959595:4777777868686869595959595959577777786868686959595959595777777868686869595959595777777868686869595959577777786868686869595777777868686868695777777868686868677777786868686777777868686777777868677777786777777777777"; 10 | 11 | //smaller sized map that only goes to 67, but takes less PROGMEM 12 | // const PROGMEM char triangular_map[] = "3343435353636373738383939393:3:3;344445454646474748484949494:4:4:4445454646474748484949494:4:4:4555565657575858595959595:4:4556565757585859595959595:4666676768686869595959595667676868686959595959577777786868695959595777777868686869595777777868686869577777786868686777777868686777777868677777786777777777777"; 13 | 14 | void gc_to_n64(uint8_t coords[2]) { 15 | /* Assume 0 <= y <= x <= 127 16 | 17 | Converts Gamecube analog stick coordinates to N64 analog stick coordinates 18 | 19 | Achievable range on a gamecube controller is 75 in corners and 105 straight 20 | N64 ranges from 70 in the corners to 80 straight. The shape is different. 21 | To maximize precision while allowing full range, we need to scale: 22 | - Straight directions to 80/105 23 | - Corner directions to 70/75 24 | Because this stretching effect warps the shape of the controller, 25 | we'd like to minimize our warping in the center and scale it up near edge. 26 | 27 | First we try to find the intersection point with the edge of the range. 28 | distance = (5x+2y) / 525 29 | closeness_to_corner = 7y / 5x+2y 30 | These range from 0 to 1 and derive from the formula for line intersection: 31 | https://gamedev.stackexchange.com/questions/44720/ 32 | 33 | Our conversion formula becomes: 34 | extra_corner_scaling = 70/75-80/105 35 | scale = distance^3 * closeness_to_corner * extra_corner_scaling + 80/105 36 | return x * scale, y * scale 37 | The cubing of distance means we warp very little in the center. 38 | 39 | We implement the below formula in uint32 integer math: 40 | ((5x + 2y) / 525)^2 * (7y / 525) * (70/75-80/105) + 80/105 41 | Notice that the multiplication cancels out one factor of 5x+2y 42 | 43 | Writes back converted N64 coordinates to x and y on a scale of 0-255 44 | The doubled resolution is to help rounding when inverting the VC mapping 45 | */ 46 | uint32_t scale = 5L * coords[0] + 2L * coords[1]; 47 | if (scale > 525) { 48 | // Multiply by 16 here to reduce precision loss from dividing by scale 49 | scale = 16UL * 525 * 525 * 525 / scale; // clamp distance to 1.0 50 | } else { 51 | scale = 16 * scale * scale; // (5x + 2y)^2, leaving 525^2 to divide later. 52 | } 53 | 54 | scale *= coords[1]; // * y, leaving another 525 to divide later. 55 | 56 | // Now we need to divide by 525^3 and multiply by: 7 * (70/75-80/105) = 1.2 57 | // Double resolution so multiply by 2. And we divide by 2**24 at the end. 58 | // So our final multiplication factor is 525^3 / 1.2 / 2 / 2^24 ~= 32 / 115 59 | scale = scale * 2 / 115; // we already multiplied by an extra *16 above 60 | 61 | // Constants chosen so rounding errors don't affect the end result. 62 | scale += 25565300; // ~= 2 * 80/105 * 2^24 63 | 64 | // Add a bit less than 2^24 so we round up by truncating. 65 | // n-0.5 < box[2n] <= n 66 | // n < box[2n+1] <= n+0.5 67 | coords[0] = (coords[0] * scale + 16774000) >> 24; 68 | coords[1] = (coords[1] * scale + 16774000) >> 24; 69 | } 70 | 71 | void n64_to_gc_generic(const N64_Report_t& N64report, Gamecube_Report_t& GCreport) { // Converts N64 analog stick coordinates to Gamecube analog stick coordinates 72 | GCreport.xAxis = N64report.xAxis + 128; 73 | GCreport.yAxis = N64report.yAxis + 128; 74 | } 75 | 76 | void n64_to_gc_yoshi(const N64_Report_t& N64report, Gamecube_Report_t& GCreport) { 77 | // Scale and convert N64 analog stick to work with Yoshi Story menu and gameplay. Does not contain an VC ESS map at this point. Just simple scaling. 78 | 79 | if (N64report.xAxis * 1.2 > 127) 80 | GCreport.xAxis = 255; 81 | else if (N64report.xAxis * 1.2 < -128) 82 | GCreport.xAxis = 0; 83 | else 84 | GCreport.xAxis = N64report.xAxis * 1.2 + 128; 85 | 86 | if (N64report.yAxis * 1.2 > 127) 87 | GCreport.yAxis = 255; 88 | else if (N64report.yAxis * 1.2 < -128) 89 | GCreport.yAxis = 0; 90 | else 91 | GCreport.yAxis = N64report.yAxis * 1.2 + 128; 92 | } 93 | 94 | uint16_t triangular_to_linear_index(uint8_t row, uint8_t col, uint8_t size) { 95 | /* Adapted from https://math.stackexchange.com/questions/2134011 96 | 97 | Given index i,j of a triangular array stored as a linear 1d array 98 | Returns the index of the linear 1d array. Assumes col >= row (!) 99 | 100 | Since X and Y are symmetrical (reflected), 101 | we only want to store half the values. 102 | */ 103 | return (size * (size - 1) / 2) - (size - row) * ((size - row) - 1) / 2 + col; 104 | } 105 | 106 | void invert_vc(uint8_t coords[2]) { 107 | /* Assume 0 <= y <= x <= 2*127 - double resolution */ 108 | /* Approach is documented in the python implementation */ 109 | if (coords[0] > 2 * OOT_MAX) coords[0] = 2 * OOT_MAX; 110 | if (coords[1] > 2 * OOT_MAX) coords[1] = 2 * OOT_MAX; 111 | 112 | if (coords[0] >= 2 * BOUNDARY && coords[1] >= 2 * BOUNDARY) { 113 | uint8_t remainder = OOT_MAX + 1 - BOUNDARY; 114 | coords[0] = (coords[0] / 2) - BOUNDARY; 115 | coords[1] = (coords[1] / 2) - BOUNDARY; 116 | uint16_t index = triangular_to_linear_index(coords[1], coords[0], remainder); 117 | coords[0] = pgm_read_byte(triangular_map + 2 * index); 118 | coords[1] = pgm_read_byte(triangular_map + 2 * index + 1); 119 | } else { 120 | coords[0] = pgm_read_byte(one_dimensional_map + coords[0]); 121 | coords[1] = pgm_read_byte(one_dimensional_map + coords[1]); 122 | } 123 | } 124 | 125 | void invert_vc_gc(uint8_t coords[2]) { 126 | /* Our other functions exploit symmetry to calculate only 0 <= y <= x <= 127 127 | So convert to the proper range and then fix the signs back up. 128 | 129 | Expects unsigned GC controller input x, y coordinates from 0 - 255 130 | 131 | Returns unsigned GC controller input x, y coordinates from 0 - 255, 132 | that, when mangled by VC, will give the in-game input that best matches. 133 | */ 134 | int x_positive = 0; 135 | int y_positive = 0; 136 | int swap = 0; 137 | 138 | if (coords[0] >= 128) { 139 | x_positive = 1; 140 | coords[0] -= 128; 141 | } else { 142 | if (coords[0] == 0) coords[0] = 127; 143 | else coords[0] = 128 - coords[0]; 144 | } 145 | 146 | if (coords[1] >= 128) { 147 | y_positive = 1; 148 | coords[1] -= 128; 149 | } else { 150 | if (coords[1] == 0) coords[1] = 127; 151 | else coords[1] = 128 - coords[1]; 152 | } 153 | 154 | if (coords[1] > coords[0]) { 155 | swap = 1; 156 | uint8_t temp = coords[0]; 157 | coords[0] = coords[1]; 158 | coords[1] = temp; 159 | } 160 | 161 | gc_to_n64(coords); 162 | invert_vc(coords); 163 | 164 | if (swap) { 165 | uint8_t temp = coords[0]; 166 | coords[0] = coords[1]; 167 | coords[1] = temp; 168 | } 169 | 170 | if (x_positive) coords[0] += 128; 171 | else coords[0] = 128 - coords[0]; 172 | if (y_positive) coords[1] += 128; 173 | else coords[1] = 128 - coords[1]; 174 | } 175 | 176 | void invert_vc_n64(int8_t coords[2], uint8_t ucoords[2]) { 177 | /* Our other functions exploit symmetry to calculate only 0 <= y <= x <= 127 178 | So convert to the proper range and then fix the signs back up. 179 | 180 | Expects signed N64 controller input x, y coordinates from -128 to 127 181 | 182 | Returns unsigned GC controller input x, y coordinates from 0 - 255, 183 | that, when mangled by VC, will give the in-game input that best matches. 184 | */ 185 | int x_positive = 0; 186 | int y_positive = 0; 187 | int swap = 0; 188 | 189 | if (coords[0] >= 0) { 190 | x_positive = 1; 191 | ucoords[0] = 2 * coords[0]; 192 | } else { 193 | if (coords[0] == -128) ucoords[0] = 2 * 127; 194 | else ucoords[0] = -2 * coords[0]; 195 | } 196 | 197 | if (coords[1] >= 0) { 198 | y_positive = 1; 199 | ucoords[1] = 2 * coords[1]; 200 | } else { 201 | if (coords[1] == -128) ucoords[1] = 2 * 127; 202 | else ucoords[1] = -2 * coords[1]; 203 | } 204 | 205 | if (ucoords[1] > ucoords[0]) { 206 | swap = 1; 207 | uint8_t temp = ucoords[0]; 208 | ucoords[0] = ucoords[1]; 209 | ucoords[1] = temp; 210 | } 211 | 212 | invert_vc(ucoords); 213 | 214 | if (swap) { 215 | uint8_t temp = ucoords[0]; 216 | ucoords[0] = ucoords[1]; 217 | ucoords[1] = temp; 218 | } 219 | 220 | if (x_positive) ucoords[0] += 128; 221 | else ucoords[0] = 128 - ucoords[0]; 222 | if (y_positive) ucoords[1] += 128; 223 | else ucoords[1] = 128 - ucoords[1]; 224 | } 225 | 226 | void normalize_origin(uint8_t coords[2], uint8_t origin[2]) { 227 | /* Gamecube controllers store the position of the analog stick when it is powered on. 228 | Coordinates range from 0 to 255 and are centered at 128. 229 | This function interprets coordinates relative to the origin, then centers the origin. 230 | */ 231 | for (int i = 0; i < 2; ++i) { 232 | int16_t normalized = (coords[i] - 128) - (origin[i] - 128); 233 | if (normalized > 127) normalized = 127; 234 | if (normalized < -128) normalized = -128; 235 | coords[i] = normalized + 128; 236 | origin[i] = 128; 237 | } 238 | } 239 | 240 | void N64toGC_buttonMap_Generic(const N64_Report_t& N64report, Gamecube_Report_t& GCreport) { // Converts N64 controller data to Gamecube data. Button Mapping is for VC OOT / GZ Practice ROM. N64 Cbuttons mapped to Gamecube X Y Z because Practice rom uses Gamecube cdown / N64 L to fly. 241 | 242 | GCreport.a = N64report.a; 243 | GCreport.b = N64report.b; 244 | GCreport.start = N64report.start; 245 | GCreport.z = N64report.z; 246 | GCreport.r = N64report.r; 247 | GCreport.right = N64report.r * 255; 248 | GCreport.l = N64report.l; 249 | GCreport.left = N64report.l * 255; 250 | 251 | GCreport.cxAxis = 128 + (N64report.cright * 127) - (N64report.cleft * 127); 252 | GCreport.cyAxis = 128 + (N64report.cup * 127) - (N64report.cdown * 127); 253 | 254 | GCreport.dleft = N64report.dleft; 255 | GCreport.dright = N64report.dright; 256 | GCreport.dup = N64report.dup; 257 | GCreport.ddown = N64report.ddown; 258 | 259 | n64_to_gc_generic(N64report, GCreport); 260 | } 261 | 262 | void N64toGC_buttonMap_OOT(const N64_Report_t& N64report, Gamecube_Report_t& GCreport) { // Converts N64 controller data to Gamecube data. Button Mapping is for VC OOT / GZ Practice ROM. N64 Cbuttons mapped to Gamecube X Y Z because Practice rom uses Gamecube cdown / N64 L to fly. 263 | 264 | GCreport.a = N64report.a; 265 | GCreport.b = N64report.b; 266 | GCreport.start = N64report.start; 267 | GCreport.z = N64report.cdown; // Gamecube Z same as cdown. 268 | GCreport.r = N64report.r; 269 | GCreport.right = N64report.r * 255; 270 | GCreport.l = N64report.z; 271 | GCreport.left = N64report.z * 255; 272 | 273 | GCreport.x = N64report.cright; // Gamecube X same as cleft 274 | GCreport.y = N64report.cleft; // Camecube Y same as cright 275 | GCreport.cyAxis = 128 + (N64report.cup * 127) - (N64report.l * 127); // set cyAxis(up) to N64 cup, set cyAxis(down) to N64 l for flying in GZ. 276 | 277 | GCreport.dleft = N64report.dleft; 278 | GCreport.dright = N64report.dright; 279 | GCreport.dup = N64report.dup; 280 | GCreport.ddown = N64report.ddown; 281 | 282 | if (settings.ess_map == ESS_ON) 283 | invert_vc_n64(&N64report.xAxis, &GCreport.xAxis); 284 | else 285 | n64_to_gc_generic(N64report, GCreport); 286 | } 287 | 288 | void N64toGC_buttonMap_Yoshi(const N64_Report_t& N64report, Gamecube_Report_t& GCreport) { 289 | 290 | GCreport.a = N64report.a; 291 | GCreport.b = N64report.b; 292 | GCreport.start = N64report.start; 293 | GCreport.z = N64report.l; 294 | GCreport.r = N64report.r; 295 | GCreport.right = N64report.r * 255; 296 | GCreport.l = N64report.z || N64report.cpad; // 'z' and all of cpad are throw egg. 297 | GCreport.left = (N64report.z || N64report.cpad) * 255; 298 | 299 | GCreport.dleft = N64report.dleft; 300 | GCreport.dright = N64report.dright; 301 | GCreport.dup = N64report.dup; 302 | GCreport.ddown = N64report.ddown; 303 | 304 | if (settings.ess_map == ESS_ON) 305 | n64_to_gc_yoshi(N64report, GCreport); 306 | else 307 | n64_to_gc_generic(N64report, GCreport); 308 | 309 | } 310 | -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/src/Gamecube_N64.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2016 NicoHood 3 | See the readme for credit to other people. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | */ 23 | 24 | #include "Gamecube_N64.h" 25 | 26 | //================================================================================ 27 | // Gamecube/N64 I/O functions 28 | //================================================================================ 29 | 30 | uint8_t gc_n64_send_get(const uint8_t pin, uint8_t* command, const uint8_t commandLen, 31 | uint8_t* report, const uint8_t reportLen){ 32 | // get the port mask and the pointers to the in/out/mode registers 33 | uint8_t bitMask = digitalPinToBitMask(pin); 34 | uint8_t port = digitalPinToPort(pin); 35 | volatile uint8_t* modePort = portModeRegister(port); 36 | volatile uint8_t* outPort = portOutputRegister(port); 37 | volatile uint8_t* inPort = portInputRegister(port); 38 | 39 | // don't want interrupts getting in the way 40 | uint8_t oldSREG = SREG; 41 | cli(); 42 | 43 | // send the command 44 | gc_n64_send(command, commandLen, modePort, outPort, bitMask); 45 | 46 | // read in data 47 | uint8_t receivedBytes = gc_n64_get(report, reportLen, modePort, outPort, inPort, bitMask); 48 | 49 | // end of time sensitive code 50 | SREG = oldSREG; 51 | 52 | // return received length 53 | return receivedBytes; 54 | } 55 | 56 | 57 | // nop definitions, placed here so the header/user 58 | // doesnt see/use this because it is %[nop] specific 59 | /* 60 | Serial.begin(115200); 61 | for (int n = 0; n < 100; n++) { 62 | Serial.print("#define nopn"); 63 | Serial.print(n); 64 | Serial.print(" nopn"); 65 | Serial.println(n % 3); 66 | } 67 | */ 68 | 69 | #define nopManual(n) nopn ## n 70 | #define nopn0 // (0 % 3) 71 | #define nopn1 "nop\n" // (1 % 3) 72 | #define nopn2 "nop\nnop\n" // (2 % 3) 73 | #define nopn3 nopn0 // (3 % 3) 74 | #define nopn4 nopn1 //.. 75 | #define nopn5 nopn2 76 | #define nopn6 nopn0 77 | #define nopn7 nopn1 78 | #define nopn8 nopn2 79 | #define nopn9 nopn0 80 | #define nopn10 nopn1 81 | #define nopn11 nopn2 82 | #define nopn12 nopn0 83 | #define nopn13 nopn1 84 | #define nopn14 nopn2 85 | #define nopn15 nopn0 86 | #define nopn16 nopn1 87 | #define nopn17 nopn2 88 | #define nopn18 nopn0 89 | #define nopn19 nopn1 90 | #define nopn20 nopn2 91 | #define nopn21 nopn0 92 | #define nopn22 nopn1 93 | #define nopn23 nopn2 94 | #define nopn24 nopn0 95 | #define nopn25 nopn1 96 | #define nopn26 nopn2 97 | #define nopn27 nopn0 98 | #define nopn28 nopn1 99 | #define nopn29 nopn2 100 | #define nopn30 nopn0 101 | #define nopn31 nopn1 102 | #define nopn32 nopn2 103 | #define nopn33 nopn0 104 | #define nopn34 nopn1 105 | #define nopn35 nopn2 106 | #define nopn36 nopn0 107 | #define nopn37 nopn1 108 | #define nopn38 nopn2 109 | #define nopn39 nopn0 110 | #define nopn40 nopn1 111 | #define nopn41 nopn2 112 | #define nopn42 nopn0 113 | #define nopn43 nopn1 114 | #define nopn44 nopn2 115 | #define nopn45 nopn0 116 | #define nopn46 nopn1 117 | #define nopn47 nopn2 118 | #define nopn48 nopn0 119 | #define nopn49 nopn1 120 | #define nopn50 nopn2 121 | #define nopn51 nopn0 122 | #define nopn52 nopn1 123 | #define nopn53 nopn2 124 | #define nopn54 nopn0 125 | #define nopn55 nopn1 126 | #define nopn56 nopn2 127 | #define nopn57 nopn0 128 | #define nopn58 nopn1 129 | #define nopn59 nopn2 130 | #define nopn60 nopn0 131 | #define nopn61 nopn1 132 | #define nopn62 nopn2 133 | #define nopn63 nopn0 134 | #define nopn64 nopn1 135 | #define nopn65 nopn2 136 | #define nopn66 nopn0 137 | #define nopn67 nopn1 138 | #define nopn68 nopn2 139 | #define nopn69 nopn0 140 | #define nopn70 nopn1 141 | #define nopn71 nopn2 142 | #define nopn72 nopn0 143 | #define nopn73 nopn1 144 | #define nopn74 nopn2 145 | #define nopn75 nopn0 146 | #define nopn76 nopn1 147 | #define nopn77 nopn2 148 | #define nopn78 nopn0 149 | #define nopn79 nopn1 150 | #define nopn80 nopn2 151 | #define nopn81 nopn0 152 | #define nopn82 nopn1 153 | #define nopn83 nopn2 154 | #define nopn84 nopn0 155 | #define nopn85 nopn1 156 | #define nopn86 nopn2 157 | #define nopn87 nopn0 158 | #define nopn88 nopn1 159 | #define nopn89 nopn2 160 | #define nopn90 nopn0 161 | #define nopn91 nopn1 162 | #define nopn92 nopn2 163 | #define nopn93 nopn0 164 | #define nopn94 nopn1 165 | #define nopn95 nopn2 166 | #define nopn96 nopn0 167 | #define nopn97 nopn1 168 | #define nopn98 nopn2 169 | #define nopn99 nopn0 170 | #define nop_reg "%[nop]" // in this sketch we named the register like this 171 | #define nop_block(id, N) /* nops have to be >=3 in order to work*/ \ 172 | "ldi " nop_reg ", (" #N "/3)\n" /* (1) ldi, start */ \ 173 | ".L%=_nop_loop" #id ":\n" /* + ((N-1) * (1) dec + (2) brne), (N-1) loops */ \ 174 | "dec " nop_reg "\n" /* + (1) dec + (1) brne, last loop */ \ 175 | "brne .L%=_nop_loop" #id "\n" /* --> (N * 3) nops */ \ 176 | nopManual(N) /* N % 3 manual nops */ 177 | 178 | /** 179 | * This sends the given byte sequence to the controller 180 | * length must be at least 1 181 | */ 182 | void gc_n64_send(const uint8_t* buff, uint8_t len, 183 | volatile uint8_t* modePort, volatile uint8_t* outPort, uint8_t bitMask) 184 | { 185 | // set pin to input, default high (due to pullup at console end) 186 | *outPort &= ~bitMask; 187 | *modePort &= ~bitMask; 188 | 189 | // temporary register values used as "clobbers" 190 | register uint8_t bitCount; 191 | register uint8_t data; 192 | register uint8_t nop; 193 | 194 | asm volatile ( 195 | "; Start of gc_n64_send assembly\n" 196 | 197 | // passed in to this block are: 198 | // the %a[buff] register is the buffer pointer 199 | // %[len] is the register holding the length of the buffer in bytes 200 | 201 | // Instruction cycles are noted in parentheses 202 | // branch instructions have two values, one if the branch isn't 203 | // taken and one if it is 204 | 205 | // %[data] will be the current buffer byte loaded from memory 206 | // %[bitCount] will be the bit counter for the current byte. when this 207 | // reaches 0, we need to decrement the length counter, load 208 | // the next buffer byte, and loop. (if the length counter becomes 209 | // 0, that's our exit condition) 210 | 211 | // This label starts the outer loop, which sends a single byte 212 | ".L%=_byte_loop:\n" 213 | "ld %[data], %a[buff]+\n" // (2) load the next byte and increment byte pointer 214 | "ldi %[bitCount],0x08\n" // (1) set bitcount to 8 bits 215 | 216 | // This label starts the inner loop, which sends a single bit 217 | ".L%=_bit_loop:\n" 218 | "st %a[modePort],%[output]\n" // (2) pull the line low 219 | 220 | // line needs to stay low for 1uS for a 1 bit, 3uS for a 0 bit 221 | // this block figures out if the next bit is a 0 or a 1 222 | // the strategy here is to shift the register left, then test and 223 | // branch on the carry flag 224 | "lsl %[data]\n" // (1) shift left. MSB goes into carry bit of status reg 225 | "brcc .L%=_zero_bit\n" // (1/2) branch if carry is cleared 226 | 227 | 228 | // this block is the timing for a 1 bit (1uS low, 3uS high) 229 | // Stay low for 1uS: 16 - 2 (above lsl,brcc) - 2 (below st) = 12 cycles 230 | nop_block(1, 12) // nop block 1, 12 cycles 231 | 232 | "st %a[modePort],%[input]\n" // (2) set the line high again 233 | // Now stay high for 2uS of the 3uS to sync up with the branch below 234 | // 2*16 - 2 (for the rjmp) = 30 cycles 235 | nop_block(2, 30) // nop block 2, 30 cycles 236 | "rjmp .L%=_finish_bit\n" // (2) 237 | 238 | 239 | // this block is the timing for a 0 bit (3uS low, 1uS high) 240 | // Need to go high in 3*16 - 3 (above lsl,brcc) - 2 (below st) = 43 cycles 241 | ".L%=_zero_bit:\n" 242 | nop_block(3, 43) // nop block 3, 43 cycles 243 | "st %a[modePort],%[input]\n" // (2) set the line high again 244 | 245 | 246 | // The two branches meet up here. 247 | // We are now *exactly* 3uS into the sending of a bit, and the line 248 | // is high again. We have 1uS to do the looping and iteration 249 | // logic. 250 | ".L%=_finish_bit:\n" 251 | "dec %[bitCount]\n" // (1) subtract 1 from our bit counter 252 | "breq .L%=_load_next_byte\n" // (1/2) branch if we've sent all the bits of this byte 253 | 254 | // At this point, we have more bits to send in this byte, but the 255 | // line must remain high for another 1uS (minus the above 256 | // instructions and the jump below and the st instruction at the 257 | // top of the loop) 258 | // 16 - 2(above) - 2 (rjmp below) - 2 (st after jump) = 10 259 | nop_block(4, 10) // nop block 4, 10 cycles 260 | "rjmp .L%=_bit_loop\n" // (2) 261 | 262 | 263 | // This block starts 3 cycles into the last 1uS of the line being high 264 | // We need to decrement the byte counter. If it's 0, that's our exit condition. 265 | // If not we need to load the next byte and go to the top of the byte loop 266 | ".L%=_load_next_byte:\n" 267 | "dec %[len]\n" // (1) len-- 268 | "breq .L%=_loop_exit\n" // (1/2) if the byte counter is 0, exit 269 | // delay block: 270 | // needs to go high after 1uS or 16 cycles 271 | // 16 - 5 (above) - 2 (the jump itself) - 5 (after jump) = 4 272 | nop_block(5, 4) // nop block 5, 4 cycles 273 | "rjmp .L%=_byte_loop\n" // (2) 274 | 275 | 276 | // Loop exit 277 | ".L%=_loop_exit:\n" 278 | 279 | // final task: send the stop bit, which is a 1 (1uS low 3uS high) 280 | // the line goes low in: 281 | // 16 - 6 (above since line went high) - 2 (st instruction below) = 8 cycles 282 | nop_block(6, 8) // nop block 6, 8 cycles 283 | "st %a[modePort],%[output]\n" // (2) pull the line low 284 | // stay low for 1uS 285 | // 16 - 2 (below st) = 14 286 | nop_block(7, 14) // nop block 7, 14 cycles 287 | "st %a[modePort],%[input]\n" // (2) set the line high again 288 | // just stay high. no need to wait 3uS before returning 289 | 290 | // ---------- 291 | // outputs: 292 | : [buff] "+e" (buff), // (read and write) 293 | [modePort] "+e" (modePort), // (read and write) 294 | [bitCount] "=&d" (bitCount), // (output only, ldi needs the upper registers) 295 | [data] "=&r" (data), // (output only) 296 | [nop] "=&d" (nop) // (output only, ldi needs the upper registers) 297 | 298 | // inputs: 299 | : [len] "r" (len), 300 | [output] "r" (*modePort | bitMask), // precalculate new pin states 301 | [input] "r" (*modePort & ~bitMask) // this works because we turn interrupts off 302 | 303 | // no clobbers 304 | ); // end of asm volatile 305 | } 306 | 307 | 308 | /** 309 | * Read bytes from the gamecube controller 310 | * listen for the expected bytes of data back from the controller and 311 | * and pack it into the buff 312 | */ 313 | uint8_t gc_n64_get(uint8_t* buff, uint8_t len, 314 | volatile uint8_t* modePort, volatile uint8_t* outPort, volatile uint8_t * inPort, uint8_t bitMask) 315 | { 316 | // prepare pin for input with no pullup 317 | *outPort &= ~bitMask; 318 | *modePort &= ~bitMask; 319 | 320 | // temporary register values used as "clobbers" 321 | register uint8_t timeoutCount; // counts down the timeout 322 | register uint8_t bitCount; // counts down 8 bits for each byte 323 | register uint8_t inputVal; // temporary variable to save the pin states 324 | register uint8_t data; // keeps the temporary received data byte 325 | register uint8_t receivedBytes; // the return value of the function 326 | register uint8_t initialTimeoutCount; // extra timeout count for initial function call 327 | 328 | asm volatile ( 329 | "; Start of gc_n64_get assembly\n" 330 | 331 | // [bitCount] is our bit counter. We read %[len] bytes 332 | // and increment the byte pointer and receivedBytes every 8 bits 333 | "ldi %[bitCount],0x08\n" // (1) set bitcount to 8 bits 334 | "ldi %[receivedBytes],0x00\n" // (1) default exit value is 0 bytes received 335 | 336 | // Initial loop waits for the line to go low. 337 | // This is especially required when reading the console commands. 338 | // When reading controller commands the normal timeout below is fine. 339 | // The gamecube will poll for an init command every 66ms if not connected. 340 | // If a controller is connected it will be polled every X ms. 341 | // 8ms (Smash Melee), 0.7 - 9ms (GC options), 0.6 - 19 ms (Mario Football) 342 | // This function will wait for 71.22ms to pull the line low. 343 | // This way we ensure to catch at least a single gamecube command if connected. 344 | "ldi %[initialTimeoutCount],0x00\n" // (1) set the outer timeout 345 | 346 | // Inititally try to read more often to get a longer timeout. 347 | // This is the first (of two) extre initital loops. 348 | ".L%=_wait_for_low_extra:\n" 349 | "ldi %[timeoutCount],0x00\n" // (1) set the extra timeout 350 | ".L%=_wait_for_low_loop_extra1:\n" // 7 cycles if loop fails 351 | "ld %[inputVal], %a[inPort]\n" // (2) read the pin (happens before the 2 cycles) 352 | "and %[inputVal], %[bitMask]\n" // (1) compare pinstate with bitmask 353 | "breq .L%=_wait_for_measure\n" // (1/2) jump to the measure part if pin is low 354 | // the following happens if the line is still high 355 | "dec %[timeoutCount]\n" // (1) decrease timeout by 1 356 | "brne .L%=_wait_for_low_loop_extra1\n" // (1/2) loop if the counter isn't 0 357 | 358 | // 2n initial extra loop 359 | "ldi %[timeoutCount],0x00\n" // (1) set the extra timeout 360 | ".L%=_wait_for_low_loop_extra2:\n" // 7 cycles if loop fails 361 | "ld %[inputVal], %a[inPort]\n" // (2) read the pin (happens before the 2 cycles) 362 | "and %[inputVal], %[bitMask]\n" // (1) compare pinstate with bitmask 363 | "breq .L%=_wait_for_measure\n" // (1/2) jump to the measure part if pin is low 364 | // the following happens if the line is still high 365 | "dec %[timeoutCount]\n" // (1) decrease timeout by 1 366 | "brne .L%=_wait_for_low_loop_extra2\n" // (1/2) loop if the counter isn't 0 367 | 368 | 369 | // This first spinloop waits for the line to go low. 370 | // It loops 128 times before it gives up and returns. 371 | // This timeout is used after the initital timeout and times out a lot faster. 372 | // For reading a Gamecube controller this loop timeout would be fine without the outer loop. 373 | ".L%=_wait_for_low:\n" 374 | "ldi %[timeoutCount],%[timeout]\n" // (1) set the timeout 375 | ".L%=_wait_for_low_loop:\n" // 7 cycles if loop fails 376 | "ld %[inputVal], %a[inPort]\n" // (2) read the pin (happens before the 2 cycles) 377 | "and %[inputVal], %[bitMask]\n" // (1) compare pinstate with bitmask 378 | "breq .L%=_wait_for_measure\n" // (1/2) jump to the measure part if pin is low 379 | // The following happens if the line is still high 380 | "dec %[timeoutCount]\n" // (1) decrease timeout by 1 381 | "brne .L%=_wait_for_low_loop\n" // (1/2) loop if the counter isn't 0 382 | 383 | 384 | // Wait for line to initially go low with the outer loop. 385 | // If one bit was already read initialTimeoutCount will be set to 1 to disable the outer loop. 386 | // Calculate delay: (((7 × 255) * 2 + (7 × 128) + 3) × 255) ÷ 16000 = 71,22ms > 66ms 387 | "dec %[initialTimeoutCount]\n" // (1) decrease outer timeout by 1 388 | "brne .L%=_wait_for_low_extra\n" // (1/2) loop if the counter isn't 0 389 | "rjmp .L%=_exit\n" // (2) timeout, jump to the end 390 | 391 | 392 | // Next block. The line has just gone low. Wait approx 2uS 393 | // each cycle is 1/16 uS on a 16Mhz processor 394 | // best case: 32 - 5 (above) - 1 (below) = 26 nops 395 | // worst case: 32 - 5 (above) - 6 (above, worst case) - 1 (below) = 20 nops 396 | // --> 23 nops 397 | ".L%=_wait_for_measure:\n" 398 | // nop block, 23 cycles, use inputVal as temporary reg since we dont need it right now 399 | "ldi %[inputVal], (23/3)\n" /* (1) ldi, start */ 400 | ".L%=_nop_loop1:\n" /* + ((N-1) * (1) dec + (2) brne), (N-1) loops */ 401 | "dec %[inputVal]\n" /* + (1) dec + (1) brne, last loop */ 402 | "brne .L%=_nop_loop1\n" /* --> (N * 3) nops */ 403 | nopManual(1) /* 23 % 3 = 2 manual nops minus the outer timeout disabling below */ 404 | "ldi %[initialTimeoutCount],1\n" // (1) disable the outer timeout 405 | 406 | // save the data 407 | "lsl %[data]\n" // (1) left shift the current byte in %[data] 408 | "ld %[inputVal], %a[inPort]\n" // (2) read the pin (happens before the 2 cycles) 409 | "and %[inputVal], %[bitMask]\n" // (1) compare pinstate with bitmask 410 | "breq .L%=_check_bit_count\n" // (1/2) skip setting data to 1 if pin is low 411 | "sbr %[data],0x01\n" // set bit 1 in %[data] if pin is high 412 | ".L%=_check_bit_count:\n" 413 | "dec %[bitCount]\n" // (1) decrement 1 from our bit counter 414 | "brne .L%=_wait_for_high\n" // (1/2) branch if we've not received the whole byte 415 | 416 | // we received a full byte 417 | "st %a[buff]+,%[data]\n" // (2) save %[data] back to memory and increment byte pointer 418 | "inc %[receivedBytes]\n" // (1) increase byte count 419 | "ldi %[bitCount],0x08\n" // (1) set bitcount to 8 bits again 420 | "cp %[len],%[receivedBytes]\n" // (1) %[len] == %[receivedBytes] ? 421 | "breq .L%=_exit\n" // (1/2) jump to exit if we received all bytes 422 | // dont wait for line to go high again 423 | 424 | 425 | // This next block waits for the line to go high again. 426 | // again, it sets a timeout counter of 128 iterations 427 | ".L%=_wait_for_high:\n" 428 | "ldi %[timeoutCount],%[timeout]\n" // (1) set the timeout 429 | ".L%=_wait_for_high_loop:\n" // 7 cycles if loop fails 430 | "ld %[inputVal], %a[inPort]\n" // (2) read the pin (happens before the 2 cycles) 431 | "and %[inputVal], %[bitMask]\n" // (1) compare pinstate with bitmask 432 | "brne .L%=_wait_for_low\n" // (1/2) line is high. ready for next loop 433 | // the following happens if the line is still low 434 | "dec %[timeoutCount]\n" // (1) decrease timeout by 1 435 | "brne .L%=_wait_for_high_loop\n" // (1/2) loop if the counter isn't 0 436 | // timeout, exit now 437 | ".L%=_exit:\n" 438 | 439 | // ---------- 440 | // outputs: 441 | : [receivedBytes] "=&d" (receivedBytes), // (ldi needs the upper registers) 442 | [buff] "+e" (buff), // (read and write) 443 | [bitCount] "=&d" (bitCount), // (output only, ldi needs the upper registers) 444 | [timeoutCount] "=&r" (timeoutCount), // (output only) 445 | [initialTimeoutCount] "=&r" (initialTimeoutCount), // (output only) 446 | [inputVal] "=&r" (inputVal), // (output only) 447 | [data] "=&r" (data) // (output only) 448 | 449 | // inputs 450 | : [len] "r" (len), 451 | [inPort] "e" (inPort), 452 | [bitMask] "r" (bitMask), 453 | [timeout] "M" (128) // constant 454 | ); // end of asm volatile 455 | 456 | return receivedBytes; 457 | } 458 | -------------------------------------------------------------------------------- /ESS-Adapter/src/Nintendo/extras/Nintendo Gamecube Controller Pinout.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Nintendo Gamecube Controller Pinout 5 | 6 | 7 | 8 | 9 | 10 | 11 |

Nintendo Gamecube Controller Protocol

12 |

13 | Last updated 8th March 2004 (first version was way back on 11th December 2002 14 | :)

15 |

This is a reasonably technical document. If you aren't technically inclined, and 16 | you just want an easy way to connect your gamecube controllers to a PC, you 17 | might be interested to know that a ready made adaptor already exists. It's 18 | called the 19 | Skillz Cube Connection USB and is only sold by 20 | Lik-Sang as far as I'm aware. It only works with the original Nintendo 21 | Gamecube controller (it is not compatible with the Wavebird or any 3rd party 22 | controllers).

23 |

However, if you are interested in homebrew hardware, or just like 24 | dismantling things, read on.

25 |

Introduction

26 |

The controller connects to the Gamecube through a proprietary 6-pin connector, 27 | with screened cable. The official Nintendo controller only seems to wire 5/6 of 28 | these pins, and of those only one seems to be used for data transfer between 29 | the console and the controller. This document includes a pin out of the 30 | controller port, deduced from an examination of the controller and console with 31 | multimeter and oscilloscope, and from some experimentation. Therefore, I make 32 | no guarantee that any of this information is accurate, and you use it at your 33 | own risk. It's my best guess at how this thing works :)

34 |

If anyone has useful information to add to this page, please drop me a line 35 | (contact details are on index page).

36 |

37 |     James, 8th March 2004.

38 |

Connector Pinout

39 |

This is a view of the controller socket on the front of the console, looking 40 | into the socket. The numbering scheme is my own:

41 |

42 | 43 | 44 | 45 | 46 | 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 |
Pin 47 |

Colour

48 |
J1Function
1Yellow25V power supply (used by rumble motor).
2Red3DATA line: bi-directional data to/from console, pull-up to 3.43V
3Green4Ground.
4White5Ground (Skillz interface has pins 3+4 wired as common ground).
5--Unknown: not connected by official controller, or Skillz interface.
6Blue13.43V logic supply.
7Black6Cable shielding / ground. Usually common ground with pin 3.
95 |

In the table above, the pin number on the left corresponds to the diagram of the 96 | controller socket. The colour code is that of the cable from the official 97 | Nintendo Controller (different models might vary), noting that one pin is not 98 | used in this case. The third column marked J1 refers to the pinout of the 99 | connector inside the controller, which you will only be able to get to if you 100 | have the appropriate security screwdriver bit (or improvise your own handmade 101 | tool). You can buy a suitable screwdriver from 102 | Lik-Sang also. The function column on the right is my best guess 103 | at what each pin is for.

104 |

Which pins are needed for a homebrew interface?

105 |

My prototype interface wires pins 3 and 4 together as common ground, uses a 7805 106 | voltage regulator to provide a 5V supply to pin 1, and uses a variable voltage 107 | regulator to provide a 3.43V supply to pin 6. The only other connection that I 108 | make is to the data line on pin 2, for which I use a 1K pull-up resistor to the 109 | 3.43V rail. I notice that the Skillz adaptor uses a 3.3V regulator, and my 110 | initial design also used 3.3V. I suspect that the accuracy of this rail isn't 111 | very important. I choose to use 3.43V currently, only because this is what I 112 | measured on a PAL Gamecube.

113 |

Caution: this should go without saying; but once you have 114 | connected a 3.3V and 5V rail and pull-up resistor to your cable, it would 115 | likely cause some damage if you were to then connect the cable to a 116 | Gamecube. I 117 | only mention this because it would be easy to have an accident if you modified 118 | a controller extension cable as I did - don't forget to unplug it from your cube 119 | first.

120 |

Power Supply and Rumble Motor

121 |

122 | There are two power rails on the connector, a 3.43V supply that is probably 123 | used for the logic, and a 5V supply that appears to be used to power the rumble 124 | motor (and perhaps logic also). The ground (3) and shield (7) are connected 125 | together.

126 |

The 5V power used by the rumble motor is always on, and the motor is controlled 127 | by a command sent to the controller. i.e. the controller contains a power 128 | transistor to switch the motor on/off, rather than the console doing this. The 129 | Yellow 5V power line goes directly to the +ve terminal of the rumble motor, and 130 | it looks like the -ve terminal of the motor is attached to a transistor.

131 |

I've not measured the current drawn by the controller yet.

132 |

Serial Data Interface

133 |

134 | The controller uses one bi-directional data line (Pin 2 - Red) to communicate 135 | with the console. This is an active high 3.43V logic signal, using a pull-up 136 | resistor to hold the line high, and pulling it low with an open-collector 137 | transistor when a low needs to be transmitted. Communication is initiated by 138 | the console sending a 24-bit string to the controller, after which the 139 | controller responds with 64-bits of button state and joystick data.

140 |

Although I first thought that the controller had an internal pull-up 141 | resistor (measured 745 ohms), in practice I had to use an external 1K pull-up 142 | resistor between the 3.43V rail and the the data line in my prototype 143 | interface.

144 |

The transfer speed is rather fast at around 4us per bit. As with the 145 | N64 controller, a low bit is signalled by a 3us low followed by 1us high, and a 146 | high bit is signalled by 1us low followed by 3us high. Yes, it's 147 | just like the N64 controller!

148 |

149 |

When the gamecube or the controller sends a string of bits, it terminates it 150 | with a single (high) stop bit. Therefore, in order to send the string 00000000, 151 | the gamecube would in fact send 000000001.

152 |

Timing Measurements

153 |

Initially (in my Dec.2003 document), I had thought that the timing was around 154 | 5us per bit, but I now believe that was wrong, and that the timebase on the 155 | 'scope was inaccurate. Philipp Kastner sent me a plot from a storage 'scope 156 | that showed 4us per bit, and I then went back and tried to measure the timings 157 | again, using the parallel port.

158 |

Using the parallel port, I timed the interval between the first high-to-low 159 | transition at the start of a command, and the final low-to-high transition at 160 | the end of the reply from the pad. The sample rate of the parallel port was 161 | around 1us per bit, leading to a possible timing error of around plus or minus 162 | 2us. An average of 10 successive measurements gave around 348us total time. 163 | Assuming a total of 24+64 = 88 bits, that equates to 3.95us per bit. This 164 | assumes no significant delay between the command and response from the 165 | pad. These timings were made using QueryPerformanceCounter, under Windows 166 | 2000, on a P4 2.8GHz, i875P chipset.

167 |

Probing for the Controller

168 |

With no controller attached, the gamecube probes for a controller by sending the 169 | sequence 000000001 about every 12ms. The oscilloscope trace below shows a 170 | typical probe sequence, with the 'scope triggered on the negative edge. When 171 | you connect a controller it will respond to this sequence, so you know that it 172 | is attached. More work is needed to examine the initial conversation between 173 | the Gamecube and controller to see if there is any useful information 174 | (e.g. 175 | about what type of controller is attached?)

176 |

177 |

Polling the Controller for Joystick/Button Data

178 |

With an official controller attached, there is a typical interval of about 179 | 6ms between successive updates. In fact, I believe that the update rate is 180 | controlled by the game, perhaps divided from the video frame rate. Each update 181 | lasts around 348us. The sequence starts with a 24-bit command from the console:

182 |

    0100 0000 0000 0011 0000 0010

183 |

After the 24-bit command word, the controller responds with a string of bits 184 | that contain the state of all the buttons along with joystick position data. 185 | The sequence of the returned data is as follows. Note that the 186 | buttons are listed in transmission order, from left to right (i.e. the 187 | left most bit is transmitted first).

188 | 189 | 190 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 216 | 218 | 219 | 220 | 222 | 224 | 225 | 226 | 228 | 230 | 231 | 232 | 234 | 236 | 237 | 238 | 240 | 242 | 243 | 244 | 246 | 249 | 250 |
Byte 191 | 0000StartYXBA
Byte 203 | 11LRZD-UpD-DownD-RightD-Left
Byte 215 | 2 217 |

  Joystick X Value (8 bit)

Byte 221 | 3  223 | Joystick Y Value (8 bit)
Byte 227 | 4  229 | C-Stick X Value (8 bit)
Byte 233 | 5  235 | C-Stick Y Value (8 bit)
Byte 239 | 6  241 | Left Button Value (8 bit) - may be 4-bit mode also?
Byte 245 | 7 247 |

  Right Button Value (8 bit) - may be 4-bit 248 | mode also?

251 |

As listed above, the L/R buttons are the end-stops on the L/R shoulder buttons. 252 | Note that between the A and L buttons, there is a bit that always appears to be 253 | high. Also, the three leading bits do not seem to be affected by the buttons 254 | (so far I have seen the sequence 000 and 001 appear here).

255 |

Making it rumble

256 |

The last bit of the command is the 'rumble' control. Setting this bit to one 257 | enables the rumble motor, and clearing it disables the motor. No initialisation 258 | sequence seems to be needed. As soon as you connect the controller, you can 259 | send the 24-bit command sequence and the pad will respond with data, and can be 260 | made to rumble. 261 |

Other observations

262 |

Given that there are 24-bits in the command word, it seems likely that there 263 | will be a series of different commands to reset the controller, or to perhaps 264 | query what kind of hardware is attached to the console. Further experimentation 265 | is needed to identify other commands.

266 |

Previously, it seemed that there was a delay of about 15us before the pad 267 | responded to a command from the console. However, in recent experiments (on a 268 | different controller - perhaps it varies between controller versions) this 269 | delay seems to be gone. I suspect that this might vary between controllers, or 270 | might be related to how frequently the controller is polled. It was clearly 271 | there in my first experiments though, as can be seen from the 'scope photos 272 | later in this document.

273 |

When examining the output of the Skillz Cube Connection, it looked as 274 | though only 4-bit analogue data is returned for the Left/Right shoulder 275 | buttons. I need to go back and verify this, but it seems that the pad might 276 | support different modes with 4-bit and 8-bit resolution. Having said that, I'm 277 | not sure it matters, who wants 4-bit data when you can have 8-bit? 278 |

What does it look like on an oscilloscope?

279 |

For those that don't have access to an oscilloscope or logic analyser, here are 280 | some blurry photographs badly taken from a tired old 'scope. In this figure, 281 | point A is the start of the 24-bit command word sent by the console, and point 282 | B marks the start of the 64-bit response from the controller. The quality of 283 | the image is quite poor, but it's actually possible to see the individual data 284 | bits. Note that a delay seems to be evident in this 'scope image between 285 | the command word and the start of the reply. In more recent experiments, I 286 | haven't seen this delay.

287 |

288 |

289 | Finally, here is a close-up view of the individual data bits when transmitting 290 | binary 0100:

291 |

292 |

Homebrew Interfacing

293 |

Recently, I built a simple homebrew interface to allow me to experiment further 294 | with the controller. Using this interface, it was possible to reliably read all 295 | the button, joystick, c-stick and left/right shoulder button values from the 296 | original (official Nintendo) wired controller (DOL-003 it says on the bottom of 297 | my controller). When I tried it with a third part controller (MadCatz MicroCon) 298 | it didn't work, but I think it's probably just a bit of tweaking of the 299 | electronics and timing (which are, to be frank, rather poorly implemented at 300 | the moment).

301 |

Anyway, it's a start; it can talk to the official controller, and can even make 302 | it rumble. And you know what the best part is? Yes, you can download the source 303 | code here.

304 |

Prototype Software

305 |

First things first, this was developed on Windows 2000, and has currently only 306 | been tested on a P4 2.8GHz with i875P chipset. Hopefully, that isn't the 307 | minimum specification, but I wouldn't be surprised if the timing messes up 308 | on a slower computer. If it does, let me know, and we will see if it can be 309 | fixed.

310 |

I'm releasing this so that people can experiment with it, assuming 311 | some basic knowledge of electronics and software. This stuff isn't yet 312 | ready for any practical use; i.e. there are no proper drivers yet.

313 |

You will need a few things before you can use this software:

314 |
    315 |
  1. 316 | Some home made hardware (circuit diagram to follow shortly, but there is a 317 | description of pin connections in the source code for the impatient or the 318 | hardcore, which should be just enough to be able to build it). 319 |
  2. 320 | The giveio device driver (download it). 321 |
  3. 322 | Nerves of steel / willingness to potentially destroy your PC and 323 | controller :)
324 |

The program works by using direct port I/O on the parallel port. That isn't 325 | normally allowed from user mode on Windows NT/2000/XP, so I downloaded and 326 | installed a driver called giveio which you can easily find 327 | with the help of google (and I will add a link here soon hopefully). When this 328 | is installed (you will need administrator rights to do this), it basically 329 | breaks the protection mechanism so that your program can do direct port I/O.

330 |

Once you have installed the giveio service, the program will 331 | start and enable the giveio service automatically as required. This means that 332 | you don't need to set the giveio service to automatically start with windows 333 | (and I don't recommend doing this either, from a safety point of view).

334 |

335 | I've not tested the program on Windows 95/98 (does anyone still use it?), and 336 | in fact I would be very surprised if it worked at all on that OS. If you know 337 | different, let me know.

338 |

Finally, here is the source code:

339 |
340 |

gcpad1.cpp

341 |
342 |

It compiles with MS Visual Studio .NET as a console project, and probably will 343 | work with Visual C++ 6. It uses some inline assembler, and the syntax might be 344 | MS specific, but should be easily alterable to work with other compilers I 345 | think.

346 |

What next?

347 |

The first thing to do is to refine the hardware design and then to test it on 348 | more PCs. I think a reasonable minimum specification to aim for would 349 | be P3 1GHz. The software needs improving, with a kernel mode device driver 350 | to talk to the hardware, and a DirectInput driver to allow the controller to be 351 | used as a normal joystick device. Support for more than one controller would be 352 | nice. With the current shift register, there are two inputs, so it should be 353 | fairly easy to support a second controller.

354 |

I've been discussing with Philipp Kastner the possibility of building an 355 | interface around a PIC microcontroller. This would allow a serial or even USB 356 | interface to be developed. One obstacle is that the PICs with USB 357 | support are only available in UV erasable versions (no flashable 358 | version) which makes them a pain to develop for. Unfortunately, not everyone has access to a PIC programmer 359 | of course. However, with a PIC, it should be possible to support several controllers 360 | with a single interface. What would be really, really nice is a front panel 361 | drive bay with four Gamecube controller ports..

362 |

Now, if only there were some decent games on the PC, apart 363 | from boring old 3rd person shooters.

364 |

Credits

365 |

Huge thanks to Philipp Kastner for inspiring me to work on this again, I'd 366 | abandoned it really (I blame work, and of course my friends Zelda, 367 | Link and Mario, for occupying too many hours).

368 |

The shift register hardware design was inspired by the N64 Controller 369 | Interface project first described by Stephan Hans, Simon Nield, 370 | F.P.Earle et.al. nice work!

371 |

The GC Linux project is definitely worth a look, some detail about the 372 | controller commands is emerging in their documentation (search for YAGCD).

373 |

All the people who wrote to me over the last year or so, I can't remember all 374 | the names, but I'll credit you all when I find the old e-mails!

375 |

Thanks to Sara, for putting up with the wires, flashing lights and 376 | tools strewn everywhere :)

377 | 378 | 379 | -------------------------------------------------------------------------------- /generate-map.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | #!/usr/bin/env python 4 | """ Helper functions for creating ESS adapters """ 5 | 6 | #The Following code is released under GNU GENERAL PUBLIC LICENSE Version 3 AND under MIT license. 7 | 8 | 9 | import sys 10 | import numpy as np 11 | 12 | from scipy.spatial.distance import euclidean as euclidean_distance 13 | from scipy.spatial import cKDTree as KDTree 14 | sys.setrecursionlimit(10000) 15 | 16 | 17 | class GCN64Map: 18 | """ Mapping of GC controller inputs to N64 19 | """ 20 | gc_straight_max = 105 21 | n64_straight_max = 80 22 | gc_corner_max = 75 23 | n64_corner_max = 70 24 | 25 | def __init__(self): 26 | self.gamecube_segments = self.make_gamecube_octagon() 27 | 28 | def make_gamecube_octagon(self): 29 | """ Initialize line segments for the max range of a gamecube controller 30 | We always make the line go from a cardinal direction to the corner 31 | Store the arctan so we can tell which segment a point is in by angle 32 | """ 33 | gsm = self.gc_straight_max 34 | gcm = self.gc_corner_max 35 | dtype = [("x1", "int"), 36 | ("y1", "int"), 37 | ("x2", "int"), 38 | ("y2", "int"), 39 | ("atan2", "float")] 40 | 41 | return np.array([ 42 | (0, gsm, gcm, gcm, np.arctan2(gsm, 0)), # N to NE, N 43 | (gsm, 0, gcm, gcm, np.arctan2(gcm, gcm)), # E to NE, NE 44 | (gsm, 0, gcm, -gcm, np.arctan2(0, gsm)), # E to SE, E 45 | (0, -gsm, gcm, -gcm, np.arctan2(-gcm, gcm)), # S to SE, SE 46 | (0, -gsm, -gcm, -gcm, np.arctan2(-gsm, 0)), # S to SW, S 47 | (-gsm, 0, -gcm, -gcm, np.arctan2(-gcm, -gcm)), # W to SW, SW 48 | (-gsm, 0, -gcm, gcm, np.arctan2(0, -gsm)), # W to NW, W 49 | (0, gsm, -gcm, gcm, np.arctan2(gcm, -gcm)), # N to NW, NW 50 | ], dtype=dtype) 51 | 52 | @staticmethod 53 | def find_nearest_below(my_array, target): 54 | """ Adapted from https://stackoverflow.com/questions/17118350/ 55 | 56 | Find the closest match below target in a 1d array. 57 | """ 58 | diff = my_array - target 59 | mask = np.ma.less(diff, 0) 60 | if np.all(mask): 61 | return my_array.argmax() 62 | masked_diff = np.ma.masked_array(diff, mask) 63 | return masked_diff.argmin() 64 | 65 | def find_segment_intersect(self, x_coord, y_coord): 66 | """ Adapted from https://gamedev.stackexchange.com/questions/44720/ 67 | 68 | Draw a line from the origin through the point x, y 69 | Find the intersection with the edge of the gamecube range. 70 | 71 | Selects the correct edge to intersect with by angle. 72 | 73 | Returns how close to a corner we intersected, where 74 | 0 is a cardinal direction and 1 is in the corner, 75 | and the distance from the point to the edge of the range, where 76 | 0 is at the origin and 1 is at the edge of the range. 77 | 78 | Assumes segments start in cardinal direction and end in corner. 79 | """ 80 | segs = self.gamecube_segments 81 | seg = segs[self.find_nearest_below( 82 | segs["atan2"], 83 | np.arctan2(y_coord, x_coord) 84 | )] 85 | 86 | cornerity = ((x_coord * seg["y1"] - y_coord * seg["x1"]) 87 | / (y_coord * (seg["x2"] - seg["x1"]) - x_coord * (seg["y2"] - seg["y1"]))) 88 | distance = ((x_coord * (seg["y2"] - seg["y1"]) - y_coord * (seg["x2"] - seg["x1"])) 89 | / ((seg["y2"] - seg["y1"]) * (seg["x1"]) - (seg["x2"] - seg["x1"]) * seg["y1"])) 90 | 91 | return cornerity, distance 92 | 93 | def map(self, x_coord, y_coord): 94 | """ The N64 controller not only has a smaller range, but it has a different shape. 95 | So we want to squash straight directions by more than the corners. 96 | That way we can get the full range of the N64 controller, but we don't lose 97 | sensitivity in the straight directions. 98 | 99 | We also want to take care to warp less in the center to preserve precision. 100 | Expects signed input from -128 to 127 101 | """ 102 | if (x_coord == 0 and y_coord == 0): 103 | return (0.0, 0.0) 104 | 105 | scale = self.n64_straight_max / self.gc_straight_max 106 | corner_difference = self.n64_corner_max / self.gc_corner_max - scale 107 | 108 | closeness_to_corner, distance = self.find_segment_intersect(x_coord, y_coord) 109 | corner_scale = closeness_to_corner * corner_difference 110 | scale += min(1, distance)**3 * corner_scale # Warp less in center 111 | 112 | return x_coord * scale, y_coord * scale 113 | 114 | def umap(self, x_coord, y_coord): 115 | """ Map unsigned coords from 0 to 255 centered at 128 """ 116 | return self.map(x_coord - 128, y_coord - 128) 117 | 118 | def simple_map(self, x_coord, y_coord): 119 | """ Approximation that does not involve calculating intersections and suchlike 120 | """ 121 | scale = self.n64_straight_max / self.gc_straight_max 122 | corner_difference = self.n64_corner_max / self.gc_corner_max - scale 123 | 124 | # 1 when we're in a corner, 0 when far 125 | closeness_to_corner = abs(x_coord * y_coord) / self.gc_corner_max**2 126 | 127 | scale += closeness_to_corner * corner_difference 128 | return x_coord * scale, y_coord * scale 129 | 130 | def naive_map(self, x_coord, y_coord): 131 | """ Simply scales down to fit gamecube range onto n64 range 132 | Means you don't reach maximum values in the corners, 133 | because the N64 controller shape is different. 134 | """ 135 | scale = self.n64_straight_max / self.gc_straight_max 136 | return x_coord * scale, y_coord * scale 137 | 138 | def plot_map(self, mapping_func=None): 139 | """ Make a plot of a gc -> n64 mapping function 140 | """ 141 | from matplotlib.patches import Circle 142 | import matplotlib.pyplot as plt 143 | 144 | gsm = self.gc_straight_max 145 | gcm = self.gc_corner_max 146 | if mapping_func is None: 147 | mapping_func = self.map 148 | 149 | _, axes = plt.subplots(figsize=(900/96, 900/96), dpi=96) 150 | axes.add_patch(Circle([0, 0], fill=False, color="green", radius=self.n64_straight_max)) 151 | 152 | # The lines that make up the GC octagon 153 | x_coords = np.concatenate([np.linspace(-gsm, -gcm), np.linspace(-gcm, 0), 154 | np.linspace(0, gcm), np.linspace(gcm, gsm), 155 | np.linspace(gsm, gcm), np.linspace(gcm, 0), 156 | np.linspace(0, -gcm), np.linspace(-gcm, -gsm)]) 157 | y_coords = np.concatenate([np.linspace(0, gcm), np.linspace(gcm, gsm), 158 | np.linspace(gsm, gcm), np.linspace(gcm, 0), 159 | np.linspace(0, -gcm), np.linspace(-gcm, -gsm), 160 | np.linspace(-gsm, -gcm), np.linspace(-gcm, 0)]) 161 | 162 | # Create 11 evenly spaced octagons to show how each gets mapped 163 | for scale in np.arange(0.0909, 1, 0.0909): 164 | scaled_x, scaled_y = zip(*[ 165 | mapping_func(*e) for e in zip(x_coords*scale, y_coords*scale) 166 | ]) 167 | axes.plot(scaled_x, scaled_y, c="blue") 168 | 169 | plt.xticks(np.arange(-90, 90+1, 10)) 170 | plt.yticks(np.arange(-90, 90+1, 10)) 171 | plt.grid() 172 | plt.show() 173 | 174 | class OOTVCMap: 175 | """ Implementation of VC's mapping algorithm """ 176 | deadzone = 15 177 | max_length = 56 178 | n64_max = 80 # Full N64 range so the adapter works in menus as well 179 | 180 | def __init__(self, verbose=False): 181 | self.vc_map, self.vc_reachable = self.create_lookup_tables() 182 | self.neighbor_finder = KDTree(np.vstack(self.vc_map)) 183 | self.verbose = verbose 184 | self.one_dimensional_boundary = self.find_1d_boundary() 185 | self.one_dimensional_map, self.triangular_map = self.factorize_lookup_table() 186 | 187 | def subtract_deadzone(self, coord, deadzone=0): 188 | """ Extra deadzone. Any movement within this zone is ignored. 189 | The game itself will also apply its own 7x7 deadzone 190 | """ 191 | if deadzone == 0: 192 | deadzone = self.deadzone 193 | if coord > deadzone: 194 | return coord - deadzone 195 | if coord < -deadzone: 196 | return coord + deadzone 197 | return 0 198 | 199 | def clamp_absolute_length(self, coord, length): 200 | """ Coordinates past a certain radius are scaled down, so 201 | that their distance from the origin is no more than max. 202 | """ 203 | if length > self.max_length: 204 | coord = coord * self.max_length / np.trunc(length) 205 | return np.trunc(coord) 206 | 207 | def map_coord(self, coord): 208 | """ Each coordinate is mapped individually, 209 | but they're not scaled the same. 210 | 211 | This means that angles are not maintained by VC. 212 | """ 213 | coord = np.trunc(coord / self.max_length * 127) 214 | 215 | # The intention was perhaps to have more precision in the center area. 216 | if coord >= 0: 217 | sign = 1 218 | else: 219 | sign = -1 220 | coord /= 127 221 | coord = 1 - np.sqrt(1 - abs(coord)) 222 | coord *= sign 223 | coord *= 127 224 | 225 | return int(np.trunc(coord)) 226 | 227 | def map(self, x_coord, y_coord): 228 | """ VC actually maps each coordinate separately 229 | 230 | Although GC controllers range from 0-255 unsigned, 231 | this function expects signed input from -128 to 127 232 | Use umap for unsigned input. 233 | """ 234 | x_coord = self.subtract_deadzone(int(x_coord)) 235 | y_coord = self.subtract_deadzone(int(y_coord)) 236 | 237 | length = np.sqrt(x_coord**2 + y_coord**2) 238 | x_coord = self.clamp_absolute_length(x_coord, length) 239 | y_coord = self.clamp_absolute_length(y_coord, length) 240 | 241 | return self.map_coord(x_coord), self.map_coord(y_coord) 242 | 243 | def umap(self, x_coord, y_coord): 244 | """ Map unsigned coords from 0 to 255 centered at 128 """ 245 | return self.map(x_coord - 128, y_coord - 128) 246 | 247 | def create_lookup_tables(self): 248 | """ Map all possible inputs and store the result in a 2D lookup table. 249 | 250 | Exploits symmetry to store only the positive quarter of the table. 251 | """ 252 | vc_map = np.zeros((128, 128, 2), dtype=("uint8", "uint8")) 253 | vc_reachable = np.zeros((128, 128), dtype=("bool")) 254 | 255 | for y_coord in range(128): 256 | for x_coord in range(128): 257 | mapped_x, mapped_y = self.map(x_coord, y_coord) 258 | 259 | # Check that we really are symmetrical 260 | assert self.map(-x_coord, y_coord) == (-mapped_x, mapped_y) 261 | assert self.map(x_coord, -y_coord) == (mapped_x, -mapped_y) 262 | assert self.map(-x_coord, -y_coord) == (-mapped_x, -mapped_y) 263 | 264 | vc_map[y_coord, x_coord] = (mapped_x, mapped_y) 265 | vc_reachable[mapped_y, mapped_x] = True 266 | 267 | return vc_map, vc_reachable 268 | 269 | def plot_reachable(self): 270 | """ Make a plot of all input coordinates you can reach in-game 271 | 272 | Only plots the positive quarter, since the vc mapping is symmetric. 273 | """ 274 | import matplotlib.pyplot as plt 275 | from matplotlib.patches import Circle 276 | 277 | # Technically we're plotting unreachables (True/1 = white) making the 278 | # reachables (False/0) black because that's how the color scheme works 279 | # It looks better than the other way around. 280 | vc_unreachable = np.invert(self.vc_reachable) 281 | 282 | _, axes = plt.subplots(figsize=(900/96, 900/96), dpi=96) 283 | image_data = vc_unreachable[0:self.n64_max+2, 0:self.n64_max+2] 284 | axes.imshow(image_data, origin="lower", cmap="gray") 285 | axes.add_patch(Circle([0, 0], fill=False, color="green", radius=self.n64_max)) 286 | 287 | plt.xticks(np.arange(0, self.n64_max+2, 5)) 288 | plt.yticks(np.arange(0, self.n64_max+2, 5)) 289 | plt.show() 290 | 291 | def invert(self, x_coord, y_coord): 292 | """ Return the input that comes closest to getting x, y in game 293 | 294 | Expects signed in-game x, y coordinates. Can be fractional. 295 | Returns unsigned GC controller x, y coordinates. 296 | 297 | The neighbor_finder requires a flat array of coordinates, 298 | so we have to calculate the x and y by dividing by 128 299 | """ 300 | if x_coord >= 0: 301 | x_sign = 1 302 | else: 303 | x_sign = -1 304 | if y_coord >= 0: 305 | y_sign = 1 306 | else: 307 | y_sign = -1 308 | 309 | point = self.clamp_to_max(abs(x_coord), abs(y_coord)) 310 | distance, _ = self.neighbor_finder.query(point) 311 | 312 | # If the nearest neighbor is too far away, move closer to origin 313 | # This prevents flickering between two neighbors that are nearly 314 | # equidistant to P, but very far apart from each other. 315 | if distance > 2.5: 316 | length = np.sqrt(point[0] * point[0] + point[1] * point[1]) 317 | scale = (length - distance + 2.5) / length 318 | point = (point[0] * scale, point[1] * scale) 319 | distance, _ = self.neighbor_finder.query(point) 320 | 321 | inverted_x, inverted_y = self.best_inversion(abs(x_coord), abs(y_coord), point, distance) 322 | 323 | return inverted_x * x_sign + 128, inverted_y * y_sign + 128 324 | 325 | def best_inversion(self, original_x, original_y, point, distance): 326 | """ Multiple inputs map to the same output. 327 | Return the input that is closest to the original. 328 | If equally close, bias towards bottom left (origin). 329 | """ 330 | options = self.neighbor_finder.query_ball_point(point, distance+0.001) 331 | 332 | def distance_length_tuple(option): 333 | """ Return distance, length tuple for easy of sorting """ 334 | inverted_x = option % 128 # column of vc_map 335 | inverted_y = option // 128 # row of vc_map 336 | return ( 337 | euclidean_distance( 338 | self.map(inverted_x, inverted_y), 339 | (original_x, original_y) 340 | ), 341 | np.sqrt(inverted_x ** 2 + inverted_y ** 2), 342 | (inverted_x, inverted_y) 343 | ) 344 | 345 | return sorted([distance_length_tuple(x) for x in options])[0][2] 346 | 347 | @staticmethod 348 | def triangular_to_linear_index(row, col, size): 349 | """ Adapted from https://math.stackexchange.com/questions/2134011 350 | 351 | Given index i,j of a triangular array stored as a linear 1d array 352 | Returns the index of the linear 1d array. Assumes col >= row (!) 353 | 354 | Since X and Y are symmetrical (reflected), 355 | we only want to store half the values. 356 | """ 357 | return (size*(size-1)//2) - (size-row)*((size-row)-1)//2 + col 358 | 359 | def find_1d_boundary(self): 360 | """ See docstring of factorize_lookup_table 361 | Find the boundary where 1d lookups still work 362 | """ 363 | if self.verbose: 364 | print("Finding one dimensional boundary") 365 | for i in range(self.n64_max+1): 366 | if self.verbose: 367 | print("Row {}".format(i)) 368 | for j in range(self.n64_max+1): 369 | inverted_i = self.invert(i, 0)[0] 370 | inverted_j = self.invert(j, 0)[0] 371 | if self.invert(i, j) != (inverted_i, inverted_j): 372 | if self.verbose: 373 | print(inverted_i, inverted_j, self.invert(i, j)) 374 | return i - 1 # Start one lower for rounding 375 | return self.n64_max 376 | 377 | def clamp_to_max(self, x_coord, y_coord): 378 | """ Treat inputs beyond n64_max as the max. 379 | """ 380 | if x_coord > self.n64_max: 381 | x_coord = self.n64_max 382 | if x_coord < -self.n64_max: 383 | x_coord = -self.n64_max 384 | if y_coord > self.n64_max: 385 | y_coord = self.n64_max 386 | if y_coord < -self.n64_max: 387 | y_coord = -self.n64_max 388 | return x_coord, y_coord 389 | 390 | 391 | def factorize_lookup_table(self): 392 | """ VC's mapping is not only symmetrical in the negatives and positives. 393 | It applies its mapping to each coordinate separately, 394 | so it's also symmetrical in the reflection x and y. 395 | 396 | Furthermore, coordinates below absolute max_length in 397 | length after deadzone, don't even interact with the other 398 | coordinate at all! That makes inverting a simple 1D lookup. 399 | 400 | It's only coordinates in the upper right part of the space 401 | that require a 2D lookup. And that lookup is triangular. 402 | 403 | We find the smallest coordinate where 1D lookup breaks down. 404 | Then we build a triangular 2D lookup for the remainder. 405 | 406 | We make the 1d map double resolution so we can round e.g. 407 | 8 to both 7 and 9 depending on which we are closer to. 408 | """ 409 | if self.verbose: 410 | print("Factorizing lookup table") 411 | one_dimensional_map = np.zeros(2*(self.n64_max+1), dtype="uint8") 412 | for i in range(self.n64_max+1): 413 | # Make sure we round both up and down 414 | one_dimensional_map[2*i] = self.invert(i, 0)[0] - 128 415 | one_dimensional_map[2*i+1] = self.invert(i + 0.5, 0)[0] - 128 416 | 417 | boundary = self.one_dimensional_boundary 418 | remainder = self.n64_max + 1 - boundary 419 | 420 | if remainder <= 1: 421 | return one_dimensional_map, None 422 | 423 | index = self.triangular_to_linear_index(remainder-1, remainder-1, remainder) # biggest index 424 | triangular_map = np.zeros((index + 1, 2), dtype=("uint8", "uint8")) 425 | for row in range(remainder): 426 | for col in range(row, remainder): 427 | index = self.triangular_to_linear_index(row, col, remainder) 428 | triangular_map[index] = np.array(self.invert(boundary + col, boundary + row)) - 128 429 | 430 | return one_dimensional_map, triangular_map 431 | 432 | def factorized_invert(self, x_coord, y_coord): 433 | """ Same as invert(), but much faster and needs less memory. 434 | Exploits several symmetries to invert with small lookup tables. 435 | 436 | - Negative and positive are symmetrical, calculate with abolute values 437 | - Y and X are reflected, so we can store half the values 438 | - The game treats large input values all the same, so the region we 439 | need to map is actually reasonably small. 440 | - Within a large part of this region, X and Y can be calculated separately. 441 | Call plot_reachable() for a visual clue to how this works. 442 | 443 | After the calculation we check that our answer matches with invert() 444 | """ 445 | clamped_x, clamped_y = self.clamp_to_max(x_coord, y_coord) 446 | 447 | if clamped_x >= 0: 448 | x_sign = 1 449 | else: 450 | x_sign = -1 451 | clamped_x = abs(clamped_x) 452 | if clamped_y >= 0: 453 | y_sign = 1 454 | else: 455 | y_sign = -1 456 | clamped_y = abs(clamped_y) 457 | 458 | boundary = self.one_dimensional_boundary 459 | remainder = None 460 | if clamped_x > boundary-0.5 and clamped_y > boundary-0.5: 461 | # Outside the one dimensional range 462 | remainder = self.n64_max + 1 - boundary 463 | 464 | # Now x and y must become zero indexed in our 2D lookup 465 | clamped_x -= boundary 466 | clamped_y -= boundary 467 | clamped_x = int(np.round(clamped_x)) 468 | clamped_y = int(np.round(clamped_y)) 469 | 470 | if clamped_y >= clamped_x: 471 | index = self.triangular_to_linear_index(clamped_x, clamped_y, remainder) 472 | inverted_y, inverted_x = self.triangular_map[index] 473 | else: 474 | index = self.triangular_to_linear_index(clamped_y, clamped_x, remainder) 475 | inverted_x, inverted_y = self.triangular_map[index] 476 | else: 477 | inverted_x = self.one_dimensional_map[int(np.ceil(clamped_x*2))] 478 | inverted_y = self.one_dimensional_map[int(np.ceil(clamped_y*2))] 479 | 480 | inverted_x = x_sign * inverted_x + 128 481 | inverted_y = y_sign * inverted_y + 128 482 | 483 | # Check how accurate factorized_invert is vs the canonical self.invert 484 | factorized = self.clamp_to_max(*self.umap(inverted_x, inverted_y)) 485 | canonical = self.clamp_to_max(*self.umap(*self.invert(x_coord, y_coord))) 486 | distance = euclidean_distance(factorized, canonical) 487 | 488 | if remainder: 489 | # Used triangular map (upper right corner of the range) 490 | # We care less about accuracy in the far ranges 491 | # There might be a small rounding error in the 2D lookup 492 | if distance > 5: 493 | print("d>5: {}, x, y: {} {}, ix, iy: {} {}, r: {}, six, siy: {} {}, r: {}".format( 494 | distance, x_coord, y_coord, inverted_x, inverted_y, factorized, 495 | *self.invert(x_coord, y_coord), canonical), file=sys.stderr, flush=True) 496 | assert distance <= 5 497 | else: 498 | # Used one dimensional map 499 | if distance != 0: 500 | print("d>0: {}, x, y: {} {}, ix, iy: {} {}, r: {}, six, siy: {} {}, r: {}".format( 501 | distance, x_coord, y_coord, inverted_x, inverted_y, factorized, 502 | *self.invert(x_coord, y_coord), canonical), file=sys.stderr, flush=True) 503 | assert distance == 0 504 | 505 | return inverted_x, inverted_y 506 | 507 | @staticmethod 508 | def c_style(byte_array): 509 | """ Python likes to print b'bytes' instead of "bytes" 510 | Which isn't legal C. Convert it to double quoted string 511 | """ 512 | return str(byte_array)[:-1].replace("\\'", "'").replace('b\'', '', 1).replace('"', '\\"') 513 | 514 | @staticmethod 515 | def java_style(map: np.ndarray): 516 | return str(map.flatten().tolist()).replace('[', '{').replace(']', '}') 517 | 518 | 519 | 520 | def print_factorized_tables(self): 521 | """ Print out the generated tables for inclusion in C code """ 522 | print("\nBoundary:") 523 | print(self.one_dimensional_boundary) 524 | print("//~~~~~~~~~~~~Copy the Following Exactly~~~~~~~~~~~~") 525 | print("") 526 | print("const PROGMEM char one_dimensional_map[] = ", end="") 527 | print('"{}"'.format(self.c_style(self.one_dimensional_map.tobytes())), end="") 528 | print(";") 529 | print("const PROGMEM char triangular_map[] = ", end="") 530 | print('"{}"'.format(self.c_style(self.triangular_map.tobytes())), end="") 531 | print(";") 532 | print("") 533 | print("//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~") 534 | # print("For Java:") 535 | # print('byte[] one_dimensional_map = {};'.format(self.java_style(self.one_dimensional_map))) 536 | # print('byte[] triangular_map = {};'.format(self.java_style(self.triangular_map))) 537 | 538 | def main(): 539 | """ Invert 10 random coordinates """ 540 | print("Building inversion tables...") 541 | gcmapper = GCN64Map() 542 | vcmapper = OOTVCMap(verbose=True) 543 | vcmapper.print_factorized_tables() 544 | print("\nEnter two coordinates to invert.") 545 | while True: 546 | x_coord = int(input("Enter x: ")) 547 | y_coord = int(input("Enter y: ")) 548 | n64_x, n64_y = gcmapper.umap(x_coord, y_coord) 549 | inverted_x, inverted_y = vcmapper.factorized_invert(n64_x, n64_y) 550 | mapped_x, mapped_y = vcmapper.umap(inverted_x, inverted_y) 551 | print("\nGamecube {:3d},{:3d} converted to n64 {: 6.1f},{: 6.1f}\n" 552 | "inverted {:3d},{:3d} which maps to {: 4d},{: 4d}" 553 | .format(x_coord, y_coord, n64_x, n64_y, 554 | inverted_x, inverted_y, mapped_x, mapped_y)) 555 | 556 | def plot_inversion(): 557 | """ Make a plot of the gamecube range after inverting VC's mapping """ 558 | gcmapper = GCN64Map() 559 | vcmapper = OOTVCMap(verbose=True) 560 | print("Plotting reachable coordinates") 561 | vcmapper.plot_reachable() 562 | print("Plotting post-inversion gamecube range") 563 | def remap(x_coord, y_coord): 564 | n64_x, n64_y = gcmapper.map(x_coord, y_coord) 565 | inverted_x, inverted_y = vcmapper.factorized_invert(n64_x, n64_y) 566 | mapped_x, mapped_y = vcmapper.umap(inverted_x, inverted_y) 567 | return mapped_x, mapped_y 568 | gcmapper.plot_map(remap) 569 | 570 | def print_full_map(): 571 | """ Useful to compare with another implementation """ 572 | gcmapper = GCN64Map() 573 | vcmapper = OOTVCMap() 574 | for y_coord in range(256): 575 | for x_coord in range(256): 576 | mapped_x, mapped_y = vcmapper.factorized_invert(*gcmapper.umap(x_coord, y_coord)) 577 | print("{} {} {} {}".format(x_coord, y_coord, mapped_x, mapped_y)) 578 | 579 | if __name__ == "__main__": 580 | main() 581 | #print_full_map() 582 | #plot_inversion() 583 | --------------------------------------------------------------------------------