V4.0: Add complete N64 Controller Test

This commit is contained in:
sanni 2019-09-27 17:06:17 +02:00
parent f6f442f47a
commit 1fe0b53a0d
8 changed files with 657 additions and 973 deletions

View File

@ -2,8 +2,8 @@
Cartridge Reader for Arduino Mega2560 Cartridge Reader for Arduino Mega2560
Author: sanni Author: sanni
Date: 20-09-2019 Date: 27-09-2019
Version: 3.9 Version: 4.0
SD lib: https://github.com/greiman/SdFat SD lib: https://github.com/greiman/SdFat
LCD lib: https://github.com/adafruit/Adafruit_SSD1306 LCD lib: https://github.com/adafruit/Adafruit_SSD1306
@ -43,7 +43,7 @@
**********************************************************************************/ **********************************************************************************/
#include <SdFat.h> #include <SdFat.h>
char ver[5] = "3.9"; char ver[5] = "4.0";
/****************************************** /******************************************
Options Options
@ -373,9 +373,9 @@ static const char modeItem7[] PROGMEM = "Reset";
static const char* const modeOptions[] PROGMEM = {modeItem1, modeItem2, modeItem3, modeItem4, modeItem5, modeItem6, modeItem7}; static const char* const modeOptions[] PROGMEM = {modeItem1, modeItem2, modeItem3, modeItem4, modeItem5, modeItem6, modeItem7};
// Add-ons submenu // Add-ons submenu
static const char addonsItem1[] PROGMEM = "PC Engine/TG16"; static const char addonsItem1[] PROGMEM = "NES/Famicom";
static const char addonsItem2[] PROGMEM = "Flashrom Programmer"; static const char addonsItem2[] PROGMEM = "Flashrom Programmer";
static const char addonsItem3[] PROGMEM = "NES/Famicom"; static const char addonsItem3[] PROGMEM = "PC Engine/TG16";
static const char addonsItem4[] PROGMEM = "Sega Master System"; static const char addonsItem4[] PROGMEM = "Sega Master System";
static const char addonsItem5[] PROGMEM = "Reset"; static const char addonsItem5[] PROGMEM = "Reset";
static const char* const addonsOptions[] PROGMEM = {addonsItem1, addonsItem2, addonsItem3, addonsItem4, addonsItem5}; static const char* const addonsOptions[] PROGMEM = {addonsItem1, addonsItem2, addonsItem3, addonsItem4, addonsItem5};
@ -483,7 +483,7 @@ void addonsMenu() {
switch (addonsMenu) switch (addonsMenu)
{ {
case 0: case 0:
pcsMenu(); nesMenu();
break; break;
case 1: case 1:
@ -491,7 +491,7 @@ void addonsMenu() {
break; break;
case 2: case 2:
nesMenu(); pcsMenu();
break; break;
case 3: case 3:

View File

@ -42,12 +42,15 @@ unsigned long romBase = 0x10000000;
byte flashramType = 1; byte flashramType = 1;
boolean MN63F81MPN = false; boolean MN63F81MPN = false;
//ControllerTest
bool quit = 1;
/****************************************** /******************************************
Menu Menu
*****************************************/ *****************************************/
// N64 start menu // N64 start menu
static const char n64MenuItem1[] PROGMEM = "Game Cartridge"; static const char n64MenuItem1[] PROGMEM = "Game Cartridge";
static const char n64MenuItem2[] PROGMEM = "Controller Pak"; static const char n64MenuItem2[] PROGMEM = "Controller";
static const char n64MenuItem3[] PROGMEM = "Flash Repro"; static const char n64MenuItem3[] PROGMEM = "Flash Repro";
static const char n64MenuItem4[] PROGMEM = "Flash Gameshark"; static const char n64MenuItem4[] PROGMEM = "Flash Gameshark";
static const char n64MenuItem5[] PROGMEM = "Reset"; static const char n64MenuItem5[] PROGMEM = "Reset";
@ -155,7 +158,8 @@ void n64ControllerMenu() {
case 0: case 0:
display_Clear(); display_Clear();
display_Update(); display_Update();
readController(); controllerTest();
quit = 1;
break; break;
case 1: case 1:
@ -830,35 +834,533 @@ void get_button()
} }
} }
void readController() {
bool quit = 1; /******************************************
N64 Controller Test
*****************************************/
#define CENTER 64
void oledPrint(const char string[], int x, int y) {
if (x == CENTER)
x = 64 - (strlen(string) / 2) * 6;
display.setCursor(x, y);
display.print(string);
}
void oledPrint(int number, int x, int y) {
display.setCursor(x, y);
display.print(number);
}
void printSTR(String st, int x, int y)
{
char buf[st.length() + 1];
st.toCharArray(buf, st.length() + 1);
oledPrint(buf, x, y);
}
void controllerTest() {
// on which screens do we start
int startscreen = 1;
int mode = 0;
int test = 1;
//name of the current displayed result
String anastick = "";
int prevStickX = 0;
// Graph
int xax = 22 + 24; // midpoint x
int yax = 24; // midpoint y
int zax = 24; // size
// variables to display test data of different sticks
int upx = 0;
int upy = 0;
int uprightx = 0;
int uprighty = 0;
int rightx = 0;
int righty = 0;
int downrightx = 0;
int downrighty = 0;
int downx = 0;
int downy = 0;
int downleftx = 0;
int downlefty = 0;
int leftx = 0;
int lefty = 0;
int upleftx = 0;
int uplefty = 0;
// variables to save test data
int bupx = 0;
int bupy = 0;
int buprightx = 0;
int buprighty = 0;
int brightx = 0;
int brighty = 0;
int bdownrightx = 0;
int bdownrighty = 0;
int bdownx = 0;
int bdowny = 0;
int bdownleftx = 0;
int bdownlefty = 0;
int bleftx = 0;
int blefty = 0;
int bupleftx = 0;
int buplefty = 0;
int results = 0;
while (quit) { while (quit) {
display_Clear();
// Get Button and analog stick // Get Button and analog stick
get_button(); get_button();
println_Msg(F("Controller Test")); switch (startscreen)
println_Msg(""); {
println_Msg(button); case 1:
println_Msg(""); {
display.clearDisplay();
oledPrint("Button Test", CENTER, 0);
display.drawLine(22 + 0, 10, 22 + 84, 10, WHITE);
// Print Button
printSTR(" " + button + " ", CENTER, 20);
// Print Stick X Value
String stickx = String("X: " + String(N64_status.stick_x, DEC) + " "); String stickx = String("X: " + String(N64_status.stick_x, DEC) + " ");
println_Msg(stickx); printSTR(stickx, 22 + 0, 38);
// Print Stick Y Value
String sticky = String("Y: " + String(N64_status.stick_y, DEC) + " "); String sticky = String("Y: " + String(N64_status.stick_y, DEC) + " ");
println_Msg(sticky); printSTR(sticky, 22 + 60, 38);
println_Msg("");
println_Msg(F("Press START to quit"));
display_Update(); printSTR("(Continue with START)", 0, 55);
delay(100); //Update LCD
display.display();
if (button == F("START")) { // go to next screen
if (button == "Press a button" && lastbutton == "START")
{
// reset button
lastbutton = "N/A";
display.clearDisplay();
if (startscreen != 4)
startscreen = startscreen + 1;
else
{
startscreen = 1;
test = 1;
}
}
else if (button == "Press a button" && lastbutton == "Z" && startscreen == 4)
{
// Quit
quit = 0; quit = 0;
} }
break;
}
case 2:
{
oledPrint("Range Test", CENTER, 55);
display.drawLine(22 + 0, 50, 22 + 84, 50, WHITE);
// Print Stick X Value
String stickx = String("X:" + String(N64_status.stick_x, DEC) + " ");
printSTR(stickx, 22 + 54, 8);
// Print Stick Y Value
String sticky = String("Y:" + String(N64_status.stick_y, DEC) + " ");
printSTR(sticky, 22 + 54, 18);
// Draw Axis
//display.drawLine(xax - zax, yax, xax + zax, yax, WHITE);
//display.drawLine(xax, yax - zax, xax, yax + zax, WHITE);
display.drawPixel(xax, yax, WHITE);
display.drawPixel(xax, yax - 80 / 4, WHITE);
display.drawPixel(xax, yax + 80 / 4, WHITE);
display.drawPixel(xax + 80 / 4, yax, WHITE);
display.drawPixel(xax - 80 / 4, yax, WHITE);
// Draw corners
display.drawPixel(xax - 68 / 4, yax - 68 / 4, WHITE);
display.drawPixel(xax + 68 / 4, yax + 68 / 4, WHITE);
display.drawPixel(xax + 68 / 4, yax - 68 / 4, WHITE);
display.drawPixel(xax - 68 / 4, yax + 68 / 4, WHITE);
//Draw Analog Stick
if (mode == 1)
{
display.drawPixel(xax + N64_status.stick_x / 4, yax - N64_status.stick_y / 4, WHITE);
//Update LCD
display.display();
}
else
{
display.drawCircle(xax + N64_status.stick_x / 4, yax - N64_status.stick_y / 4, 2, WHITE);
//Update LCD
display.display();
display.clearDisplay();
}
// switch mode
if (button == "Press a button" && lastbutton == "Z")
{
if (mode == 0)
{
mode = 1;
display.clearDisplay();
}
else
{
mode = 0;
display.clearDisplay();
}
}
// go to next screen
if (button == "Press a button" && lastbutton == "START")
{
// reset button
lastbutton = "N/A";
display.clearDisplay();
if (startscreen != 4)
startscreen = startscreen + 1;
else
{
startscreen = 1;
test = 1;
}
}
else if (button == "Press a button" && lastbutton == "Z" && startscreen == 4)
{
// Quit
quit = 0;
}
break;
}
case 3:
{
display.drawPixel(22 + prevStickX, 40, BLACK);
oledPrint("Skipping Test", CENTER, 0);
display.drawLine(22 + 0, 10, 22 + 83, 10, WHITE);
display.drawRect(22 + 0, 15, 22 + 59, 21, WHITE);
if (N64_status.stick_x > 0) {
display.drawLine(22 + N64_status.stick_x, 15, 22 + N64_status.stick_x, 35, WHITE);
display.drawPixel(22 + N64_status.stick_x, 40, WHITE);
prevStickX = N64_status.stick_x;
}
printSTR("Try to fill box by", 0, 45);
printSTR("slowly moving right", 0, 55);
//Update LCD
display.display();
if (button == "Press a button" && lastbutton == "Z")
{
// reset button
lastbutton = "N/A";
display.clearDisplay();
}
// go to next screen
if (button == "Press a button" && lastbutton == "START")
{
// reset button
lastbutton = "N/A";
display.clearDisplay();
if (startscreen != 4)
startscreen = startscreen + 1;
else
{
startscreen = 1;
test = 1;
}
}
else if (button == "Press a button" && lastbutton == "Z" && startscreen == 4)
{
// Quit
quit = 0;
}
break;
}
case 4:
{
switch ( test )
{
case 0: // Display results
{
switch (results)
{
case 0:
{
anastick = "YOURS";
upx = bupx;
upy = bupy;
uprightx = buprightx;
uprighty = buprighty;
rightx = brightx;
righty = brighty;
downrightx = bdownrightx;
downrighty = bdownrighty;
downx = bdownx;
downy = bdowny;
downleftx = bdownleftx;
downlefty = bdownlefty;
leftx = bleftx;
lefty = blefty;
upleftx = bupleftx;
uplefty = buplefty;
if (button == "Press a button" && lastbutton == "A")
{
// reset button
lastbutton = "N/A";
results = 1;
}
break;
}
case 1:
{
anastick = "ORIG";
upx = 1;
upy = 84;
uprightx = 67;
uprighty = 68;
rightx = 83;
righty = -2;
downrightx = 67;
downrighty = -69;
downx = 3;
downy = -85;
downleftx = -69;
downlefty = -70;
leftx = -85;
lefty = 0;
upleftx = -68;
uplefty = 68;
if (button == "Press a button" && lastbutton == "A")
{
// reset button
lastbutton = "N/A";
results = 0;
}
break;
}
} //results
display.clearDisplay();
printSTR(anastick, 22 + 50, 0);
oledPrint("U:", 22 + 50, 10);
oledPrint(upy, 100, 10);
oledPrint("D:", 22 + 50, 20);
oledPrint(downy, 100, 20);
oledPrint("L:", 22 + 50, 30);
oledPrint(leftx, 100, 30);
oledPrint("R:", 22 + 50, 40);
oledPrint(rightx, 100, 40);
display.drawLine(xax + upx / 4, yax - upy / 4, xax + uprightx / 4, yax - uprighty / 4, WHITE);
display.drawLine(xax + uprightx / 4, yax - uprighty / 4, xax + rightx / 4, yax - righty / 4, WHITE);
display.drawLine(xax + rightx / 4, yax - righty / 4, xax + downrightx / 4, yax - downrighty / 4, WHITE);
display.drawLine(xax + downrightx / 4, yax - downrighty / 4, xax + downx / 4, yax - downy / 4, WHITE);
display.drawLine(xax + downx / 4, yax - downy / 4, xax + downleftx / 4, yax - downlefty / 4, WHITE);
display.drawLine(xax + downleftx / 4, yax - downlefty / 4, xax + leftx / 4, yax - lefty / 4, WHITE);
display.drawLine(xax + leftx / 4, yax - lefty / 4, xax + upleftx / 4, yax - uplefty / 4, WHITE);
display.drawLine(xax + upleftx / 4, yax - uplefty / 4, xax + upx / 4, yax - upy / 4, WHITE);
display.drawPixel(xax, yax, WHITE);
printSTR("(Quit with Z)", 25, 55);
//Update LCD
display.display();
break;
} //display results
case 1:// +y Up
{
oledPrint("Hold Stick Up", CENTER, 18);
oledPrint("then press A", CENTER, 28);
//myOLED.drawBitmap(110, 60, ana1);
if (button == "Press a button" && lastbutton == "A")
{
bupx = N64_status.stick_x;
bupy = N64_status.stick_y;
// reset button
lastbutton = "N/A";
display.clearDisplay();
test = 2;
}
break;
}
case 2:// +y+x Up-Right
{
oledPrint("Up-Right", CENTER, 22 );
//myOLED.drawBitmap(110, 60, ana2);
if (button == "Press a button" && lastbutton == "A")
{
buprightx = N64_status.stick_x;
buprighty = N64_status.stick_y;
test = 3;
// reset button
lastbutton = "N/A";
display.clearDisplay();
}
break;
}
case 3:// +x Right
{
oledPrint("Right", CENTER, 22 );
//myOLED.drawBitmap(110, 60, ana3);
if (button == "Press a button" && lastbutton == "A")
{
brightx = N64_status.stick_x;
brighty = N64_status.stick_y;
test = 4;
// reset button
lastbutton = "N/A";
display.clearDisplay();
}
break;
}
case 4:// -y+x Down-Right
{
oledPrint("Down-Right", CENTER, 22 );
//myOLED.drawBitmap(110, 60, ana4);
if (button == "Press a button" && lastbutton == "A")
{
bdownrightx = N64_status.stick_x;
bdownrighty = N64_status.stick_y;
test = 5;
// reset button
lastbutton = "N/A";
display.clearDisplay();
}
break;
}
case 5:// -y Down
{
oledPrint("Down", CENTER, 22 );
//myOLED.drawBitmap(110, 60, ana5);
if (button == "Press a button" && lastbutton == "A")
{
bdownx = N64_status.stick_x;
bdowny = N64_status.stick_y;
test = 6;
// reset button
lastbutton = "N/A";
display.clearDisplay();
}
break;
}
case 6:// -y-x Down-Left
{
oledPrint("Down-Left", CENTER, 22 );
//myOLED.drawBitmap(110, 60, ana6);
if (button == "Press a button" && lastbutton == "A")
{
bdownleftx = N64_status.stick_x;
bdownlefty = N64_status.stick_y;
test = 7;
// reset button
lastbutton = "N/A";
display.clearDisplay();
}
break;
}
case 7:// -x Left
{
oledPrint("Left", CENTER, 22 );
//myOLED.drawBitmap(110, 60, ana7);
if (button == "Press a button" && lastbutton == "A")
{
bleftx = N64_status.stick_x;
blefty = N64_status.stick_y;
test = 8;
// reset button
lastbutton = "N/A";
display.clearDisplay();
}
break;
}
case 8:// +y+x Up-Left
{
oledPrint("Up-Left", CENTER, 22);
//myOLED.drawBitmap(110, 60, ana8);
if (button == "Press a button" && lastbutton == "A")
{
bupleftx = N64_status.stick_x;
buplefty = N64_status.stick_y;
test = 0;
// reset button
lastbutton = "N/A";
display.clearDisplay();
}
break;
}
}
if (test != 0)
{
oledPrint("Benchmark", CENTER, 0);
display.drawLine(22 + 0, 9, 22 + 83, 9, WHITE);
printSTR("(Quit with Z)", 25, 55);
}
display.display();
// go to next screen
if (button == "Press a button" && lastbutton == "START")
{
// reset button
lastbutton = "N/A";
display.clearDisplay();
if (startscreen != 4)
startscreen = startscreen + 1;
else
{
startscreen = 1;
test = 1;
}
}
else if (button == "Press a button" && lastbutton == "Z" && startscreen == 4)
{
// Quit
quit = 0;
}
break;
}
}
} }
} }
/****************************************** /******************************************
N64 Controller Pak Functions N64 Controller Pak Functions
(connected via Controller) (connected via Controller)
@ -1189,7 +1691,7 @@ static const uint32_t crc_32_tab[] PROGMEM = { /* CRC polynomial 0xedb88320 */
}; };
// improved strcmp function that ignores case to prevent checksum comparison issues // improved strcmp function that ignores case to prevent checksum comparison issues
int strcicmp(char const *a, char const *b) int strcicmp(char const * a, char const * b)
{ {
for (;; a++, b++) { for (;; a++, b++) {
int d = tolower((unsigned char) * a) - tolower((unsigned char) * b); int d = tolower((unsigned char) * a) - tolower((unsigned char) * b);

View File

@ -1,7 +1,7 @@
//****************************************** //******************************************
// NINTENDO POWER MODULE // NINTENDO POWER MODULE
//****************************************** //******************************************
// (GB Memory starts at around line 1743) // (GB Memory starts at around line 1740)
/****************************************** /******************************************
SF Memory Cassette SF Memory Cassette

View File

@ -1,812 +0,0 @@
/**********************************************************************************
Nintendo 64 Controller Test for Arduino Mega
Author: sanni
Date: 2016-04-15
Version: V2
OLED lib: http://www.rinkydinkelectronics.com/library.php?id=79
Thanks to:
Andrew Brown/Peter Den Hartog - N64 send/get functions
**********************************************************************************/
#include <OLED_I2C.h>
extern uint8_t SmallFont[];
// define LCD pins
OLED myOLED(SDA, SCL, 8);
//define LED pin
int ledPin = 10;
// These two macros toggle the eepDataPin/ControllerDataPin between input and output
// External 1K pull-up resistor from eepDataPin to VCC required
// 0x10 = 00010000 -> Port H Pin 4
#define N64_HIGH DDRH &= ~0x10
#define N64_LOW DDRH |= 0x10
// Read the current state(0/1) of the eepDataPin
#define N64_QUERY (PINH & 0x10)
// received Controller data
char N64_raw_dump[33]; // 1 received bit per byte
String rawStr = ""; // above char array read into a string
struct {
char stick_x;
char stick_y;
}
N64_status;
// on which screens do we start
int startscreen = 0;
int mode = 0;
int test = 1;
//stings that hold the buttons
String button = "N/A";
String lastbutton = "N/A";
//name of the current displayed result
String anastick = "";
// Graph
int xax = 22 + 24; // midpoint x
int yax = 24; // midpoint y
int zax = 24; // size
// variables to display test data of different sticks
int upx = 0;
int upy = 0;
int uprightx = 0;
int uprighty = 0;
int rightx = 0;
int righty = 0;
int downrightx = 0;
int downrighty = 0;
int downx = 0;
int downy = 0;
int downleftx = 0;
int downlefty = 0;
int leftx = 0;
int lefty = 0;
int upleftx = 0;
int uplefty = 0;
// variables to save test data
int bupx = 0;
int bupy = 0;
int buprightx = 0;
int buprighty = 0;
int brightx = 0;
int brighty = 0;
int bdownrightx = 0;
int bdownrighty = 0;
int bdownx = 0;
int bdowny = 0;
int bdownleftx = 0;
int bdownlefty = 0;
int bleftx = 0;
int blefty = 0;
int bupleftx = 0;
int buplefty = 0;
int results = 0;
void N64_send(unsigned char *buffer, char length);
void N64_get();
void setup()
{
// Communication with controller on this pin
// Don't remove these lines, we don't want to push +5V to the controller
// Output a low signal
PORTH &= ~(1 << 4);
// Set Controller Data Pin(PH4) to Input
DDRH &= ~(1 << 4);
// Led
pinMode(ledPin, OUTPUT);
// OLED
myOLED.begin();
myOLED.setFont(SmallFont);
}
// This sends the given byte sequence to the controller
// length must be at least 1
// Oh, it destroys the buffer passed in as it writes it
void N64_send(unsigned char *buffer, char length)
{
// Send these bytes
char bits;
bool bit;
// This routine is very carefully timed by examining the assembly output.
// Do not change any statements, it could throw the timings off
//
// We get 16 cycles per microsecond, which should be plenty, but we need to
// be conservative. Most assembly ops take 1 cycle, but a few take 2
//
// I use manually constructed for-loops out of gotos so I have more control
// over the outputted assembly. I can insert nops where it was impossible
// with a for loop
asm volatile (";Starting outer for loop");
outer_loop:
{
asm volatile (";Starting inner for loop");
bits = 8;
inner_loop:
{
// Starting a bit, set the line low
asm volatile (";Setting line to low");
N64_LOW; // 1 op, 2 cycles
asm volatile (";branching");
if (*buffer >> 7) {
asm volatile (";Bit is a 1");
// 1 bit
// remain low for 1us, then go high for 3us
// nop block 1
asm volatile ("nop\nnop\nnop\nnop\nnop\n");
asm volatile (";Setting line to high");
N64_HIGH;
// nop block 2
// we'll wait only 2us to sync up with both conditions
// at the bottom of the if statement
asm volatile ("nop\nnop\nnop\nnop\nnop\n"
"nop\nnop\nnop\nnop\nnop\n"
"nop\nnop\nnop\nnop\nnop\n"
"nop\nnop\nnop\nnop\nnop\n"
"nop\nnop\nnop\nnop\nnop\n"
"nop\nnop\nnop\nnop\nnop\n"
);
}
else {
asm volatile (";Bit is a 0");
// 0 bit
// remain low for 3us, then go high for 1us
// nop block 3
asm volatile ("nop\nnop\nnop\nnop\nnop\n"
"nop\nnop\nnop\nnop\nnop\n"
"nop\nnop\nnop\nnop\nnop\n"
"nop\nnop\nnop\nnop\nnop\n"
"nop\nnop\nnop\nnop\nnop\n"
"nop\nnop\nnop\nnop\nnop\n"
"nop\nnop\nnop\nnop\nnop\n"
"nop\n");
asm volatile (";Setting line to high");
N64_HIGH;
// wait for 1us
asm volatile ("; end of conditional branch, need to wait 1us more before next bit");
}
// end of the if, the line is high and needs to remain
// high for exactly 16 more cycles, regardless of the previous
// branch path
asm volatile (";finishing inner loop body");
--bits;
if (bits != 0) {
// nop block 4
// this block is why a for loop was impossible
asm volatile ("nop\nnop\nnop\nnop\nnop\n"
"nop\nnop\nnop\nnop\n");
// rotate bits
asm volatile (";rotating out bits");
*buffer <<= 1;
goto inner_loop;
} // fall out of inner loop
}
asm volatile (";continuing outer loop");
// In this case: the inner loop exits and the outer loop iterates,
// there are /exactly/ 16 cycles taken up by the necessary operations.
// So no nops are needed here (that was lucky!)
--length;
if (length != 0) {
++buffer;
goto outer_loop;
} // fall out of outer loop
}
// send a single stop (1) bit
// nop block 5
asm volatile ("nop\nnop\nnop\nnop\n");
N64_LOW;
// wait 1 us, 16 cycles, then raise the line
// 16-2=14
// nop block 6
asm volatile ("nop\nnop\nnop\nnop\nnop\n"
"nop\nnop\nnop\nnop\nnop\n"
"nop\nnop\nnop\nnop\n");
N64_HIGH;
}
void N64_get()
{
// listen for the expected 8 bytes of data back from the controller and
// blast it out to the N64_raw_dump array, one bit per byte for extra speed.
// Afterwards, call translate_raw_data() to interpret the raw data and pack
// it into the N64_status struct.
asm volatile (";Starting to listen");
unsigned char timeout;
char bitcount = 32;
char *bitbin = N64_raw_dump;
// Again, using gotos here to make the assembly more predictable and
// optimization easier (please don't kill me)
read_loop:
timeout = 0x3f;
// wait for line to go low
while (N64_QUERY) {
if (!--timeout)
return;
}
// wait approx 2us and poll the line
asm volatile (
"nop\nnop\nnop\nnop\nnop\n"
"nop\nnop\nnop\nnop\nnop\n"
"nop\nnop\nnop\nnop\nnop\n"
"nop\nnop\nnop\nnop\nnop\n"
"nop\nnop\nnop\nnop\nnop\n"
"nop\nnop\nnop\nnop\nnop\n"
);
*bitbin = N64_QUERY;
++bitbin;
--bitcount;
if (bitcount == 0)
return;
// wait for line to go high again
// it may already be high, so this should just drop through
timeout = 0x3f;
while (!N64_QUERY) {
if (!--timeout)
return;
}
goto read_loop;
}
void get_button()
{
// Command to send to the gamecube
// The last bit is rumble, flip it to rumble
// yes this does need to be inside the loop, the
// array gets mutilated when it goes through N64_send
unsigned char command[] = {
0x01
};
// don't want interrupts getting in the way
noInterrupts();
// send those 3 bytes
N64_send(command, 1);
// read in data and dump it to N64_raw_dump
N64_get();
// end of time sensitive code
interrupts();
// The get_N64_status function sloppily dumps its data 1 bit per byte
// into the get_status_extended char array. It's our job to go through
// that and put each piece neatly into the struct N64_status
int i;
memset(&N64_status, 0, sizeof(N64_status));
// bits: joystick x value
// These are 8 bit values centered at 0x80 (128)
for (i = 0; i < 8; i++) {
N64_status.stick_x |= N64_raw_dump[16 + i] ? (0x80 >> i) : 0;
}
for (i = 0; i < 8; i++) {
N64_status.stick_y |= N64_raw_dump[24 + i] ? (0x80 >> i) : 0;
}
// read char array N64_raw_dump into string rawStr
rawStr = "";
for (i = 0; i < 16; i++) {
rawStr = rawStr + String(N64_raw_dump[i], DEC);
}
// Buttons (A,B,Z,S,DU,DD,DL,DR,0,0,L,R,CU,CD,CL,CR)
if (rawStr.substring(0, 16) == "0000000000000000") {
lastbutton = button;
button = "Press a button";
digitalWrite(ledPin, LOW);
}
else
{
digitalWrite(ledPin, HIGH);
for (int i = 0; i < 16; i++)
{
// seems to be 16, 8 or 4 depending on what pin is used
if (N64_raw_dump[i] == 16)
{
switch (i)
{
case 7:
button = "D-Right";
break;
case 6:
button = "D-Left";
break;
case 5:
button = "D-Down";
break;
case 4:
button = "D-Up";
break;
case 3:
button = "START";
break;
case 2:
button = "Z";
break;
case 1:
button = "B";
break;
case 0:
button = "A";
break;
case 15:
button = "C-Right";
break;
case 14:
button = "C-Left";
break;
case 13:
button = "C-Down";
break;
case 12:
button = "C-Up";
break;
case 11:
button = "R";
break;
case 10:
button = "L";
break;
}
}
}
}
}
void printSTR(String st, int x, int y)
{
char buf[st.length() + 1];
st.toCharArray(buf, st.length() + 1);
myOLED.print(buf, x, y);
}
void nextscreen()
{
if (button == "Press a button" && lastbutton == "START")
{
// reset button
lastbutton = "N/A";
myOLED.clrScr();
if (startscreen != 4)
startscreen = startscreen + 1;
else
{
startscreen = 1;
test = 1;
}
}
}
void loop()
{
// Get Button and analog stick
get_button();
switch (startscreen)
{
case 0: // Logo Screen
{
myOLED.print("ControllerTest", CENTER, 8);
myOLED.print("V1.0", CENTER, 18);
myOLED.drawLine(22 + 0, 28, 22 + 84, 28);
myOLED.print("2013 sanni", CENTER, 32);
myOLED.update();
delay(1500);
startscreen = 1;
myOLED.clrScr();
break;
}
case 1:
{
myOLED.print("Button Test", CENTER, 0);
myOLED.drawLine(22 + 0, 10, 22 + 84, 10);
// Print Button
printSTR(" " + button + " ", CENTER, 20);
// Print Stick X Value
String stickx = String("X: " + String(N64_status.stick_x, DEC) + " ");
printSTR(stickx, 22 + 0, 38);
// Print Stick Y Value
String sticky = String("Y: " + String(N64_status.stick_y, DEC) + " ");
printSTR(sticky, 22 + 42, 38);
//Update LCD
myOLED.update();
// go to next screen
nextscreen();
break;
}
case 2:
{
myOLED.print("Range", 22 + 52, 5);
myOLED.print("Test", 22 + 52, 15);
myOLED.drawRect(22 + 50, 0, 22 + 83, 25);
// Print Stick X Value
String stickx = String("X:" + String(N64_status.stick_x, DEC) + " ");
printSTR(stickx, 22 + 50, 28);
// Print Stick Y Value
String sticky = String("Y:" + String(N64_status.stick_y, DEC) + " ");
printSTR(sticky, 22 + 50, 38);
// Draw Axis
myOLED.drawLine(xax - zax, yax, xax + zax, yax);
myOLED.drawLine(xax, yax - zax, xax, yax + zax);
myOLED.clrPixel(xax, yax - 80 / 4);
myOLED.clrPixel(xax, yax + 80 / 4);
myOLED.clrPixel(xax + 80 / 4, yax);
myOLED.clrPixel(xax - 80 / 4, yax);
//Draw Analog Stick
if (mode == 1)
{
myOLED.setPixel(xax + N64_status.stick_x / 4, yax - N64_status.stick_y / 4);
//Update LCD
myOLED.update();
}
else
{
myOLED.drawCircle(xax + N64_status.stick_x / 4, yax - N64_status.stick_y / 4, 2);
//Update LCD
myOLED.update();
myOLED.clrScr();
}
// switch mode
if (button == "Press a button" && lastbutton == "Z")
{
if (mode == 0)
{
mode = 1;
myOLED.clrScr();
}
else
{
mode = 0;
myOLED.clrScr();
}
}
// go to next screen
nextscreen();
break;
}
case 3:
{
myOLED.print("Skipping Test", CENTER, 0);
myOLED.drawLine(22 + 0, 10, 22 + 83, 10);
myOLED.drawRect(22 + 0, 20, 22 + 83, 44);
if (N64_status.stick_x > 0)
myOLED.drawLine(22 + N64_status.stick_x, 20, 22 + N64_status.stick_x, 44);
//Update LCD
myOLED.update();
if (button == "Press a button" && lastbutton == "Z")
{
// reset button
lastbutton = "N/A";
myOLED.clrScr();
}
// go to next screen
nextscreen();
break;
}
case 4:
{
switch ( test )
{
case 0: // Display results
{
switch (results)
{
case 0:
{
anastick = "YOURS";
upx = bupx;
upy = bupy;
uprightx = buprightx;
uprighty = buprighty;
rightx = brightx;
righty = brighty;
downrightx = bdownrightx;
downrighty = bdownrighty;
downx = bdownx;
downy = bdowny;
downleftx = bdownleftx;
downlefty = bdownlefty;
leftx = bleftx;
lefty = blefty;
upleftx = bupleftx;
uplefty = buplefty;
if (button == "Press a button" && lastbutton == "A")
{
// reset button
lastbutton = "N/A";
results = 1;
}
break;
}
case 1:
{
anastick = "ORIG";
upx = 1;
upy = 84;
uprightx = 67;
uprighty = 68;
rightx = 83;
righty = -2;
downrightx = 67;
downrighty = -69;
downx = 3;
downy = -85;
downleftx = -69;
downlefty = -70;
leftx = -85;
lefty = 0;
upleftx = -68;
uplefty = 68;
if (button == "Press a button" && lastbutton == "A")
{
// reset button
lastbutton = "N/A";
results = 0;
}
break;
}
} //results
myOLED.clrScr();
printSTR(anastick, 22 + 50, 0);
myOLED.print("U:", 22 + 50, 10);
myOLED.printNumI(upy, RIGHT, 10);
myOLED.print("D:", 22 + 50, 20);
myOLED.printNumI(downy, RIGHT, 20);
myOLED.print("L:", 22 + 50, 30);
myOLED.printNumI(leftx, RIGHT, 30);
myOLED.print("R:", 22 + 50, 40);
myOLED.printNumI(rightx, RIGHT, 40);
myOLED.drawLine(xax + upx / 4, yax - upy / 4, xax + uprightx / 4, yax - uprighty / 4);
myOLED.drawLine(xax + uprightx / 4, yax - uprighty / 4, xax + rightx / 4, yax - righty / 4);
myOLED.drawLine(xax + rightx / 4, yax - righty / 4, xax + downrightx / 4, yax - downrighty / 4);
myOLED.drawLine(xax + downrightx / 4, yax - downrighty / 4, xax + downx / 4, yax - downy / 4);
myOLED.drawLine(xax + downx / 4, yax - downy / 4, xax + downleftx / 4, yax - downlefty / 4);
myOLED.drawLine(xax + downleftx / 4, yax - downlefty / 4, xax + leftx / 4, yax - lefty / 4);
myOLED.drawLine(xax + leftx / 4, yax - lefty / 4, xax + upleftx / 4, yax - uplefty / 4);
myOLED.drawLine(xax + upleftx / 4, yax - uplefty / 4, xax + upx / 4, yax - upy / 4);
myOLED.setPixel(xax, yax);
//Update LCD
myOLED.update();
break;
} //display results
case 1:// +y Up
{
myOLED.print("Hold Stick Up", CENTER, 18);
myOLED.print("then press A", CENTER, 28);
//myOLED.drawBitmap(110, 60, ana1);
if (button == "Press a button" && lastbutton == "A")
{
bupx = N64_status.stick_x;
bupy = N64_status.stick_y;
// reset button
lastbutton = "N/A";
myOLED.clrScr();
test = 2;
}
break;
}
case 2:// +y+x Up-Right
{
myOLED.print("Up-Right", CENTER, 22 );
//myOLED.drawBitmap(110, 60, ana2);
if (button == "Press a button" && lastbutton == "A")
{
buprightx = N64_status.stick_x;
buprighty = N64_status.stick_y;
test = 3;
// reset button
lastbutton = "N/A";
myOLED.clrScr();
}
break;
}
case 3:// +x Right
{
myOLED.print("Right", CENTER, 22 );
//myOLED.drawBitmap(110, 60, ana3);
if (button == "Press a button" && lastbutton == "A")
{
brightx = N64_status.stick_x;
brighty = N64_status.stick_y;
test = 4;
// reset button
lastbutton = "N/A";
myOLED.clrScr();
}
break;
}
case 4:// -y+x Down-Right
{
myOLED.print("Down-Right", CENTER, 22 );
//myOLED.drawBitmap(110, 60, ana4);
if (button == "Press a button" && lastbutton == "A")
{
bdownrightx = N64_status.stick_x;
bdownrighty = N64_status.stick_y;
test = 5;
// reset button
lastbutton = "N/A";
myOLED.clrScr();
}
break;
}
case 5:// -y Down
{
myOLED.print("Down", CENTER, 22 );
//myOLED.drawBitmap(110, 60, ana5);
if (button == "Press a button" && lastbutton == "A")
{
bdownx = N64_status.stick_x;
bdowny = N64_status.stick_y;
test = 6;
// reset button
lastbutton = "N/A";
myOLED.clrScr();
}
break;
}
case 6:// -y-x Down-Left
{
myOLED.print("Down-Left", CENTER, 22 );
//myOLED.drawBitmap(110, 60, ana6);
if (button == "Press a button" && lastbutton == "A")
{
bdownleftx = N64_status.stick_x;
bdownlefty = N64_status.stick_y;
test = 7;
// reset button
lastbutton = "N/A";
myOLED.clrScr();
}
break;
}
case 7:// -x Left
{
myOLED.print("Left", CENTER, 22 );
//myOLED.drawBitmap(110, 60, ana7);
if (button == "Press a button" && lastbutton == "A")
{
bleftx = N64_status.stick_x;
blefty = N64_status.stick_y;
test = 8;
// reset button
lastbutton = "N/A";
myOLED.clrScr();
}
break;
}
case 8:// +y+x Up-Left
{
myOLED.print("Up-Left", CENTER, 22);
//myOLED.drawBitmap(110, 60, ana8);
if (button == "Press a button" && lastbutton == "A")
{
bupleftx = N64_status.stick_x;
buplefty = N64_status.stick_y;
test = 0;
// reset button
lastbutton = "N/A";
myOLED.clrScr();
}
break;
}
}
if (test != 0)
{
myOLED.print("Benchmark", CENTER, 0);
myOLED.drawLine(22 + 0, 9, 22 + 83, 9);
}
myOLED.update();
// go to next screen
nextscreen();
break;
}
}
}

View File

@ -1,2 +0,0 @@
This is my portable N64 Controller Tester edited to run on the Cart Reader Shield.
The N64 controller's data line is connected to Arduino Mega Pin 7.

View File

@ -1,4 +0,0 @@
Unzip and copy those into your Arduino library folder e.g. C:\Users\sanni\Documents\Arduino\libraries
You can also get the latest versions from their creators page:
Oled lib: http://www.rinkydinkelectronics.com/library.php?id=79