Tutorial 6 – C Programming in 6502 – Reading Joysticks


games Tutorial 6 - C Programming in 6502 - Reading Joysticks 6502 8 bit c / c++ code code library compiler interpreter / compiler Nintendo Entertainment System

Up to now, we have 5 tutorials on C programming for 6502 platforms (e.g. NES). [1][2][3][4][5] but we haven’t touched the field of handling inputs yet. Without inputs, a program cannot be so much useful, especially because it is impossible to make games without inputs from users.

So, what are the inputs for Nintendo Entertainment System? The answer is Joystick. There are two Joysticks that can be handled by default (maybe more can be handled, but not come with the default game console).

nes-controller Tutorial 6 - C Programming in 6502 - Reading Joysticks 6502 8 bit c / c++ code code library compiler interpreter / compiler Nintendo Entertainment System

So, I guess, we all are very familiar with above configurations, a four-directional button, control (often used to select options), enter (also known start button), B and A (often used for different gaming purposes, e.g. Jump, Shoot, Speed up …)

So, we are going to explore how to read the values from Joysticks in 6502 C programming.

Previously, we have presented the following macro, which is handy to read and write memory address.

1
2
typedef unsigned char u8;
#define address(add) (*(u8 *)(add))
typedef unsigned char u8;
#define address(add) (*(u8 *)(add))

The Joystick 1 is mapped/handled at memory address 0x4016 and the Joystick 2 is mapped/handled at memory address 0x4017.

To read values from Joysticks, you have to write 0x01 and 0x00 sequentially to reset the state of a joystick. And then, you can read 8 bytes one by one for button A, B, Control, Start, Up, Down, Left and Right.

For each byte, the bit ZERO indicates that if that button is pressed. The bit ONE indicates that if the joystick is present (if used in emulator, you don’t actually need to test this). For other bits, it is set to unused.

Therefore, to read Joystick 1, we can write a small function to gather all 8 possible buttons in one byte:

1
2
3
4
5
6
7
8
9
10
11
u8 read_joystick_1()
{
    u8 n = 8, joy_state = 0;
    address(0x4016)=01;     
    address(0x4016)=00;     
    while(n){
        joy_state = (joy_state << 1) | address(0x4016) & 1;
        --n;
    }
    return joy_state;
}
u8 read_joystick_1()
{
	u8 n = 8, joy_state = 0;
	address(0x4016)=01;		
	address(0x4016)=00;		
	while(n){
		joy_state = (joy_state << 1) | address(0x4016) & 1;
		--n;
	}
	return joy_state;
}

For Joystick 2, we just need to change the address to 0x4017.

1
2
3
4
5
6
7
8
9
10
11
u8 read_joystick_1()
{
    u8 n = 8, joy_state = 0;
    address(0x4017)=01;     
    address(0x4017)=00;     
    while(n){
        joy_state = (joy_state << 1) | address(0x4017) & 1;
        --n;
    }
    return joy_state;
}
u8 read_joystick_1()
{
	u8 n = 8, joy_state = 0;
	address(0x4017)=01;		
	address(0x4017)=00;		
	while(n){
		joy_state = (joy_state << 1) | address(0x4017) & 1;
		--n;
	}
	return joy_state;
}

We can make another function that specifies the joystick in its parameter.

1
2
3
4
5
6
7
8
9
10
11
u8 read_joystick(unsigned int _joystick)            
{
    u8 n=8,joy_state=0;
    address(_joystick)=01;      
    address(_joystick)=00;      
    while(n){
        joy_state=(joy_state<<1)| address(_joystick)&1;
        --n;
    }
    return joy_state;
}
u8 read_joystick(unsigned int _joystick)			
{
	u8 n=8,joy_state=0;
	address(_joystick)=01;		
	address(_joystick)=00;		
	while(n){
		joy_state=(joy_state<<1)| address(_joystick)&1;
		--n;
	}
	return joy_state;
}

And, define two macros:

1
2
#define read_joystick_1()   read_joystick(0x4016)
#define read_joystick_2()   read_joystick(0x4017)
#define	read_joystick_1()	read_joystick(0x4016)
#define	read_joystick_2()	read_joystick(0x4017)

Let’s test the Joystick by printing the pressed values:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include "conio.h"
#include "nes.h"
#include "stdio.h"
typedef unsigned char u8;
#define address(add) (*(u8 *)(add))
 
u8 read_joystick_1()
{
    u8 n=8,joy_state=0;
    address(0x4016)=01;
    address(0x4016)=00;
    while(n){
        joy_state=(joy_state<<1)|address(0x4016)&1;
        --n;
    }
    return joy_state;
}
 
void main()
{   
    unsigned char key;
    while(1){       
        key=read_joystick_1();
        gotoxy(1,1);
        cprintf("key=%u   ",key);
    }
}
#include "conio.h"
#include "nes.h"
#include "stdio.h"
typedef unsigned char u8;
#define address(add) (*(u8 *)(add))

u8 read_joystick_1()
{
	u8 n=8,joy_state=0;
	address(0x4016)=01;
	address(0x4016)=00;
	while(n){
		joy_state=(joy_state<<1)|address(0x4016)&1;
		--n;
	}
	return joy_state;
}

void main()
{	
	unsigned char key;
	while(1){		
		key=read_joystick_1();
		gotoxy(1,1);
		cprintf("key=%u   ",key);
	}
}

This gives the following output (ROM can be downloaded here).

6502-joysticks Tutorial 6 - C Programming in 6502 - Reading Joysticks 6502 8 bit c / c++ code code library compiler interpreter / compiler Nintendo Entertainment System

How to we interpret the values for this combined-single byte? We can define the following values for each button.

1
2
3
4
5
6
7
8
#define button_A    0x80
#define button_B    0x40
#define button_SELECT   0x20
#define button_START    0x10
#define button_UP   0x08
#define button_DOWN 0x04
#define button_LEFT 0x02
#define button_RIGHT    0x01
#define button_A	0x80
#define button_B	0x40
#define button_SELECT	0x20
#define button_START	0x10
#define button_UP	0x08
#define button_DOWN	0x04
#define button_LEFT	0x02
#define button_RIGHT	0x01

Then we can do the AND operator on the byte to see it that button is pressed.

1
#define presskey(k) (key & (k))
#define presskey(k) (key & (k))

For example,

1
if(presskey(button_UP))--y;
if(presskey(button_UP))--y;

Now, let’s take a look at the following example to handle two joysticks at a time.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include "conio.h"
#include "nes.h"
#include "stdio.h"
typedef unsigned char u8;
#define address(add) (*(u8 *)(add))
 
#define button_A    0x80
#define button_B    0x40
#define button_SELECT   0x20
#define button_START    0x10
#define button_UP   0x08
#define button_DOWN 0x04
#define button_LEFT 0x02
#define button_RIGHT    0x01
 
u8 read_joystick(unsigned int _joystick)
{
    u8 n=8,joy_state=0;
    address(_joystick)=01;
    address(_joystick)=00;  
    while(n){
        joy_state=(joy_state<<1)| address(_joystick)&1;
        --n;
    }
    return joy_state;
}
 
void delay(int i)
{
    while(i--){
        waitvblank();
    }
}
#define presskey(k) (key & (k))
#define read_joystick_1()   read_joystick(0x4016)
#define read_joystick_2()   read_joystick(0x4017)
 
void main()
{   
    unsigned char key;
    u8 x=0,y=0,oldx=0,oldy=0;
    u8 x2=0,y2=0,oldx2=0,oldy2=0;   
    cprintf("waiting for press joystick....",);
    while(1){   
        key=read_joystick_1();
        if(presskey(button_UP))--y;
        if(presskey(button_DOWN))++y;
        if(presskey(button_LEFT))--x;
        if(presskey(button_RIGHT))++x;
        key=read_joystick_2();      
        if(presskey(button_UP))--y2;
        if(presskey(button_DOWN))++y2;
        if(presskey(button_LEFT))--x2;
        if(presskey(button_RIGHT))++x2;     
        if(x!=oldx || y!=oldy || x2!=oldx2 || y2!=oldy2){   
            clrscr();   
            gotoxy(1,1);
            cprintf("x=%u y=%u x=%u y=%u",x,y,x2,y2);   
            gotoxy(x,y);    
            cprintf("A");   
            gotoxy(x2,y2);
            cprintf("B");
            oldx=x;
            oldy=y;
            oldx2=x2;
            oldy2=y2;
            delay(10);
        }
    }
}
#include "conio.h"
#include "nes.h"
#include "stdio.h"
typedef unsigned char u8;
#define address(add) (*(u8 *)(add))

#define button_A	0x80
#define button_B	0x40
#define button_SELECT	0x20
#define button_START	0x10
#define button_UP	0x08
#define button_DOWN	0x04
#define button_LEFT	0x02
#define button_RIGHT	0x01

u8 read_joystick(unsigned int _joystick)
{
	u8 n=8,joy_state=0;
	address(_joystick)=01;
	address(_joystick)=00;	
	while(n){
		joy_state=(joy_state<<1)| address(_joystick)&1;
		--n;
	}
	return joy_state;
}

void delay(int i)
{
	while(i--){
		waitvblank();
	}
}
#define presskey(k) (key & (k))
#define	read_joystick_1()	read_joystick(0x4016)
#define	read_joystick_2()	read_joystick(0x4017)

void main()
{	
	unsigned char key;
	u8 x=0,y=0,oldx=0,oldy=0;
	u8 x2=0,y2=0,oldx2=0,oldy2=0;	
	cprintf("waiting for press joystick....",);
	while(1){	
		key=read_joystick_1();
		if(presskey(button_UP))--y;
		if(presskey(button_DOWN))++y;
		if(presskey(button_LEFT))--x;
		if(presskey(button_RIGHT))++x;
		key=read_joystick_2();		
		if(presskey(button_UP))--y2;
		if(presskey(button_DOWN))++y2;
		if(presskey(button_LEFT))--x2;
		if(presskey(button_RIGHT))++x2;		
		if(x!=oldx || y!=oldy || x2!=oldx2 || y2!=oldy2){	
			clrscr();	
			gotoxy(1,1);
			cprintf("x=%u y=%u x=%u y=%u",x,y,x2,y2);	
			gotoxy(x,y);	
			cprintf("A");	
			gotoxy(x2,y2);
			cprintf("B");
			oldx=x;
			oldy=y;
			oldx2=x2;
			oldy2=y2;
			delay(10);
		}
	}
}

The above code is self-explanatory. In a NES emulator, you probably have to configure the joysticks first.

configure-joystick Tutorial 6 - C Programming in 6502 - Reading Joysticks 6502 8 bit c / c++ code code library compiler interpreter / compiler Nintendo Entertainment System

This gives the following output (ROM can be downloaded here).

test-joysticks Tutorial 6 - C Programming in 6502 - Reading Joysticks 6502 8 bit c / c++ code code library compiler interpreter / compiler Nintendo Entertainment System

–EOF (The Ultimate Computing & Technology Blog) —

GD Star Rating
loading...
1148 words
Last Post: Coding Exercise - C++ - Single Number II - Online Judge
Next Post: Assoc and Ftype on Windows Command Shell

The Permanent URL is: Tutorial 6 – C Programming in 6502 – Reading Joysticks

Leave a Reply