Tutorial 8 – C Programming in 6502 – Sprites


The Essence of the 8-bit 6502 programming for NES (Nintendo Entertainment System) should be the Sprites. So, what is a Sprite? A Sprite is a moving object in the game, e.g. Super Mario, plane, bullet.

Almost in every 8-bit NES games, there are spirits. There are at most 64 Sprites supported, which has the index from 0 to 63 inclusive. You can control the spirits and let them move in the screen.

There are two sizes/dimensions that can be defined for Sprites, either 8×8 (Single tile) or 8×16 pixels (Double tile). Every Sprite takes four byte to define, so 64 spirits takes up 256 bytes, in the SPRAM (Spirit RAM), which is independent from VRAM and RAM.

The way to access (read or write) SPRAM is quite similar to VRAM except that it only takes 1 byte (8 bit) to specify the address and every time when the data is written to SPRAM, the pointer/address is automatically incremented by 1 byte. So if you have some continuous data, you just have to set the address once.

The CPU mapping address is 0x2003 for SPRAM and the data address is 0x2004. For example, if you want to write 1 and 2 to the 7-th and 8-th byte of the SPRAM, you can do this:

1
2
3
4
#define address(add) (*(u8 *)(add)) // macro to access the memory
address(0x2003) = 7;  // set the location to 7
address(0x2004) = 1;  // write value 1 to 7-th
address(0x2004) = 2;  // write value 2 to 8-th (pointer address increments one)
#define address(add) (*(u8 *)(add)) // macro to access the memory
address(0x2003) = 7;  // set the location to 7
address(0x2004) = 1;  // write value 1 to 7-th
address(0x2004) = 2;  // write value 2 to 8-th (pointer address increments one)

The 256 bytes represent 64 spirits. The byte offset 0 to 3 denotes the first Sprite, the byte offset 4 to 7 denotes the second Sprite and this goes on to the 63 Sprite which is represented by byte offset 0xfc-0xff.

For each Sprite, the first byte is the y coordinate in pixel and the fourth byte is the x coordinate in pixel. The second byte is the tile number (known as index number of the pattern). The third number is the attribute for the Sprite (we will cover that in the future tutorial).

We can then have a macro to set the specified Sprite.

1
2
3
4
5
6
#define address(add) (*(u8 *)(add)) // macro to access the memory
#define putSP(n, x, y, t, a) address(0x2003)=n*4;\
                             address(0x2004)=y;\
                             address(0x2004)=t;\
                             address(0x2004)=a;\
                             address(0x2004)=x
#define address(add) (*(u8 *)(add)) // macro to access the memory
#define putSP(n, x, y, t, a) address(0x2003)=n*4;\
                             address(0x2004)=y;\
                             address(0x2004)=t;\
                             address(0x2004)=a;\
                             address(0x2004)=x

Now, we are going to show a tiny example that controls the spirit using joystick. [This tutorial] covers the details of using joysticks in 8-bit NES.

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
#include "conio.h"
#include "nes.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.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
 
#define presskey(k) (key & (k))
#define iskey(k) (key == (k))
#define downkey(k) (okey!=key && (k)&key)
#define upkey(k) (okey!=key && (k)&okey)
#define putSP(n, x, y, t, a) address(0x2003)=n*4;\
                             address(0x2004)=y;\
                             address(0x2004)=t;\
                             address(0x2004)=a;\
                             address(0x2004)=x
 
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()
{    
      u8 key,x=100,y=60;
      address(0x2000) = 0x80;     
      address(0x2001) = 0x1e;     
      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;
           waitvblank();      
           putSP(0,x,y,'*',0);
           gotoxy(0,0);
           cprintf("x:%-3d y:%-3d", x, y);     
      }
}
#include "conio.h"
#include "nes.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.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

#define presskey(k) (key & (k))
#define iskey(k) (key == (k))
#define downkey(k) (okey!=key && (k)&key)
#define upkey(k) (okey!=key && (k)&okey)
#define putSP(n, x, y, t, a) address(0x2003)=n*4;\
                             address(0x2004)=y;\
                             address(0x2004)=t;\
                             address(0x2004)=a;\
                             address(0x2004)=x

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()
{    
      u8 key,x=100,y=60;
      address(0x2000) = 0x80;     
      address(0x2001) = 0x1e;     
      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;
           waitvblank();      
           putSP(0,x,y,'*',0);
           gotoxy(0,0);
           cprintf("x:%-3d y:%-3d", x, y);     
      }
}

To compile the above example, use command cl65 -t nes -o sp.nes sp.c assumed the source C file is sp.c. You can then use any NES emulator (e.g. Virtual NES) to run the example.

The above example reads from joystick and control the Sprite which is the star and prints out the coordinates on the top-left corner of the screen.

spirits Tutorial 8 – C Programming in 6502 – Sprites 6502 8 bit animation c / c++ code code library implementation programming languages

The pre-compiled NES can be downloaded here.

–EOF (The Ultimate Computing & Technology Blog) —

GD Star Rating
loading...
805 words
Last Post: Bouncing Balls Animation Made in Processing Programming Language
Next Post: How to Revive Old Posts using PHP and Crontab

The Permanent URL is: Tutorial 8 – C Programming in 6502 – Sprites

15 Comments

  1. Zarnes
      • zarnes
        • zarnes
    • Milescoder
  2. Ualas
  3. John
  4. matt
  5. sugar
  6. Eric Slattery

Leave a Reply