Controller control

What is a game without being able to control it? On this page we’ll go through how to deal with receiving (and sending) data to and from the Nintendo 64’s controller interface in order to get complete controller control. You can build projects without this, but you’ll be limited to doing some tech demos without any interactivity.

Data structures

Libdragon includes a few data structures to standardise and package the data being transferred from the controller. Some of them are referring to input from a Nintendo Gamecube controller or interfacing with accessories like the Controller Pak or Transfer Pak, but those are out of scope for this tutorial.

controller_data

The main data structure for controller data is, well, the controller_data data structure. Here is how it is defined:

typedef struct controller_data
{
	struct SI_condat c[4];
	struct SI_condat_gc gc[4];
} SI_controllers_state_t;

Pretty much what it is is two arrays of length [4] each which hold the data for each controller. The top SI_condat is for N64 controller memory that we will be using and the second one SI_condat_gc is for Gamecube controller data, which we will be ignoring.

SI_condat

This stands for “Serial Interface Controller Data”, which is basically the data structure that contains the status of an individual controller.

typedef struct SI_condat
{
    /** @brief Unused padding bits */
    unsigned : 16;
    /** @brief Status of the last command */
    unsigned err : 2;
    /** @brief Unused padding bits */
    unsigned : 14;

    union
    {
        struct
        {
            /** @brief 32-bit data sent to or returned from SI */
            unsigned int data : 32;
        };
        struct
        {
            /** @brief State of the A button */
            unsigned A : 1;
            /** @brief State of the B button */
            unsigned B : 1;
            /** @brief State of the Z button */
            unsigned Z : 1;
            /** @brief State of the start button */
            unsigned start : 1;
            /** @brief State of the up button */
            unsigned up : 1;
            /** @brief State of the down button */
            unsigned down : 1;
            /** @brief State of the left button */
            unsigned left : 1;
            /** @brief State of the right button */
            unsigned right : 1;
            /** @brief Unused padding bits */
            unsigned : 2;
            /** @brief State of the L button */
            unsigned L : 1;
            /** @brief State of the R button */
            unsigned R : 1;
            /** @brief State of the C up button */
            unsigned C_up : 1;
            /** @brief State of the C down button */
            unsigned C_down : 1;
            /** @brief State of the C left button */
            unsigned C_left : 1;
            /** @brief State of the C right button */
            unsigned C_right : 1;
            /** @brief State of the analog stick (X axis) */
            signed x : 8;
            /** @brief State of the analog stick (Y axis) */
            signed y : 8;
        };
    };
} _SI_condat;

This looks like a long struct but it’s actually fairly simple. It starts with 32 bits to represent the status of the last command (only 2 of them are used). It then has a 32-bit unsigned integer that holds the controller data, with each row showing how many bits each data point has.

Thanks to the union, we can retrieve it as a single integer or as individual members. Here is a visualisation of how controller input data is recieved. This is just me pressing random keys and printing out the 32-bit integer in binary form and in then as a signed integer. Expand the code to see the logic of how it works.

Expand for source
#include <stdio.h>
#include <libdragon.h>

int main(void)
{
	// Initialise things
	console_init();
	controller_init();

	// Controller info struct
	struct controller_data controllers;
	// Loop iterator
	int i;
	// Array to hold printable binary output
	char buttons[33];
	
	while(1) {
		// Get controller data
		controller_scan();
		controllers = get_keys_held();

		// Convert controller data to binary string
		for (i=0; i<32; i++) {
			buttons[i] = ((controllers.c[0].data >> i) & 1)?'1':'0';
		}
		buttons[32] = '\0';

		// Print binary string and raw signed int
		printf("[%s] [%i]\n", buttons, controllers.c[0].data);
	}
}

Detecting controllers

This is a fairly simple step, but necessary to have good data integrity. This function tells you which controllers are present in the form of a bitmask.

Basically, it returns a 32-bit int whose upper 16 bits are empty and the lower 16 bits are used for controller data. Basically, a full controller set looks like 0x0000FFFF, and having only controller inserted looks like 0x00000000F. Though that doesn’t really matter since you can use the “CONTROLLER_X_INSERTED” declarations to check if a controller is present. Like this:

// Prototypes and declarations for reference
int get_controllers_present(void);
#define CONTROLLER_1_INSERTED   0xF000
#define CONTROLLER_2_INSERTED   0x0F00
#define CONTROLLER_3_INSERTED   0x00F0
#define CONTROLLER_4_INSERTED   0x000F

int main(void) {
	// Controller info struct
	int controllers;
	// Get controller data
	controllers = get_controllers_present() & CONTROLLER_1_INSERTED;
	
	while(1) {
	}
}

You don’t even need to use controller_scan() or even controller_init() so this can be done with very little overhead. Though most games only do a check for this upon booting since people don’t really plug/unplug controllers very often.

How to get controller data

Once you have your controller data structure set up, you can start to put controller functions into play. There are a few you can use in your game.

Setting things up

The first thing you need to do if you want to use controllers in your libdragon project is initialise the controller subsystem and set up the controller struct. You want to do this at the very start of your program (outside of your main loop) since it only needs to be done once, like this:

int main(void) {
	// Initialise controller stuff
	controller_init();
	struct controller_data controllers;

	// Main loop
	while(1) {
	}
}

It might also be a good idea to put your controller_data struct outside of your main() function as a global variable so that it can be accessible from any function:

// Controller struct
struct controller_data controllers;

int main(void) {
	// Initialise controller stuff
	controller_init();

	// Main loop
	while(1) {
	}
}

Populating the controller struct

At this point, the controller struct should be all zeroes. So if we want to populate it with useful data, we’ll need to use some additional functions since it isn’t done automatically.

Using controller_read()

The simplest way to do this is to use the controller_read() function. It populates the controller_data struct with the current data from all controllers. It is however very slow and only gives us the current status so it should be avoided whenever possible.

int main(void) {
	// Initialise controller stuff
	controller_init();
	struct controller_data controllers;

	// Main loop
	while(1) {
		controller_read(&controllers);
		// Do stuff
	}
}

Using the controller_scan() functions

The preferred way of populating the controller_data struct is to use controller_scan(). This function just synchronises the internal state of the controllers so it’s very fast. It doesn’t do anything on its own, but it sets the stage for the rest of the functions that do produce output.

This then enables you to pull from this cache by using the following functions which all serve a different purpose depending on the context of your game:

  • get_keys_down() gets the buttons that were pressed since the last read.
  • get_keys_up() gets the buttons that were released since the last read.
  • get_keys_held() gets the buttons that were pressed in the last read and are still pressed in the current read.
  • get_keys_pressed() gets the current button status regardless of their stat in the previous frame.

Here is an example for get_keys_down():

// Prototypes for reference
void controller_scan(void);
struct controller_data get_keys_down(void);
struct controller_data get_keys_up(void);
struct controller_data get_keys_held(void);
struct controller_data get_keys_pressed(void);

int main(void) {
	// Initialise controller stuff
	controller_init();
	struct controller_data controllers;

	// Main loop
	while(1) {
		// Get controller data
		controller_scan();
		controllers = get_keys_down();
		// Do stuff
		//
	}
}

Use get_dpad_direction()

This function is another way to get controller data. It’s similar to the ones above, except that it doesn’t require a controller_data struct to work, only an int to store the result. All you need to do is chuck in the controller number and you’ll get back the direction where that controller’s D-pad is pointing.

It returns an integer from 0-7, moving counter-clockwise from the right position at 0. So if you’re used to using radian notation, it follows that kind of order. If there is no button pressed or there is a conflict (eg left and right both pressed), it will return -1.

// Prototypes for reference
void controller_scan(void);
int get_dpad_direction(int controller);

int main(void) {
	// Initialise controller stuff
	controller_init();
	int direction;

	// Main loop
	while(1) {
		// Get controller data for Player #0
		controller_scan();
		direction = get_dpad_direction(0);
		// Do stuff
		//
	}
}

Accessories & advanced functionality

This section has not been written yet, but might be in the future 🙂

Further reading

Search

Subscribe to the mailing list

Follow N64 Squid

  • RSS Feed
  • YouTube

Random featured posts