Commodore 64 Assembly Sprite

In this lesson, we provided building block steps that will teach you how to get a Commodore 64 assembly sprite up on the screen and moving.  This C64 Assembly Sprite Tutorial was developed with the beginner in mind. It will be helpful if you do have a minor grasp on Basic as a lot of those techniques can be applied here.

For the first part we access the register $d020 (53280) in order to control the border color. The Commodore 64 utilizes memory “registers” to access specific functions that can be used within a program.  The hardware chip that manages the border color is known as VIC II (Video Interface Controller).

The Border color has the ability to produce colors from 1-16. These colors are set and cannot be changed. The first set from 0-8 is known as standard colors and the remaining 9-16 are the multi colors. The color palette is defined below for our Commodore 64 Assembly Sprite project.

Note: The sprite tutorial is brief currently and will eventually be updated with more content. Let me know if you need more explanation of the code below. I’m always happy to further elaborate the tutorial to make things more clear.

VIC-II Memory

The Commodore 64 allows you to produce up to 8 sprites in memory and display them on the screen. The registers below dictate what they control. A specific microprocessor chip known as VIC (Video Interface Controller) contains 4K of memory between registers 53248-53294 ($D000 – $D02E). The VIC chip (or “VIC II”) is primarily responsible for the graphics and sprites that can be displayed on your system. The video display memory must be contained in a 16K that makes up display memory, character dot data and sprite shapes.

Mapping the Commodore 64

Feel free to view the Compute! PDF book “Mapping the Commodore 64” to explore more about these registers in detail. You can learn so much about your Commodore 64 just by consuming this book. It’s an excellent resource!

Sprite X Horizontal Sprite Y Vertical
53248 ($D000) 53249 ($D001)
53250 ($D002) 53251 ($D003)
53252 ($D004) 53253 ($D005)
53254 ($D006) 53255 ($D007)
53256 ($D008) 53257 ($D009)
53258 ($D00A) 53259 ($D00B)
53260 ($D00C) 53261 ($D00D)
53262 ($D00E) 53263 ($D00F)

Below is a listing of the program in PDF format

[wonderplugin_pdf src=”https://www.c64brain.com/wp-content/uploads/2021/05/C64-Assembly-Sprite-Tutorial.pdf” width=”100%” height=”600px” style=”border:0;”]

Basic Program Example

Listed here is the original Basic program that spawned this idea. One day as I was playing the game Pitfall II I wondered if I could capture the game sprites and replay the animation and this program is the result.

Color 0 = Black

Color 1 = White

Color 2 = Red

Color 3 = Cyan (Blue/Green)

Color 4 = Purple

Color 5 = Green

Color 6 = Blue<

Color 7 = Yellow

Color 8 = Orange

Color 9 = Brown

Color 10 = Light Red

Color 11 = Dark Grey

Color 12 = Medium Grey

Color 13 = Light Green

Color 14 = Light Blue

Color 15 = Light Grey

The program starts off by setting the Commodore 64 border color.

Then we turn on the sprite, set the multi colors, sprite colors, background color, sprite x/y position, and set the sprite on the screen by setting the sprite shape pointer.

Note: Not all the registers will be seen here, but only the ones we need to first setup our sprite and others of importance.

Sprite Registers Description
53264 ($D010) Most Significant Bit of Sprites 0-7 Horizontal Positions
53269 ($D015) Sprite Enable Registers (Bit 0=Off, Bit 1=On): Makes sprites visible
53271 ($D017) Sprite Vertical Expansion Register (Bit =0 – Normal Height, Bit = 1 – Double Height: Sprite Y Size
53275 ($D01B) Sprite to Foreground Display Priority Register (Bit = 0 – Sprite in front, Bit = 1 Sprite in back
53276 ($D01C) Sprite Multicolor Registers (Bit 0=Off, Bit 1= On: Controls individual colors in bit pairs
53277 ($D01D) Sprite Horizontal Expansion Register (Bit 0 = Normal width, Bit 1 = Double Width): Sprite X Size
53278 ($D01E) Sprite to Sprite Collision Register (Bits 0-7: Determines if a sprite collided (Bit = 1 – collision occurred)
53279 ($D01F) Sprite to Foreground Collision Register (Bits 0-7) – Bit = Off – no collision, Bit = On – collision occurred

* = $8000

; Found on Youtube
; Title: Commodore 128D: Episode 3: Hardware Sprites
; Channel: Nybbles and Bytes

lda #13
sta $d020 ; set the border color
jsr $e544  ; clear the screen (using a kernal call)

Sprite Color Registers

The table below sets the Sprite Color Registers. The sprites color are from 0-15 and we can use any colors from the standard color set.

Sprite Number Sprite Register for Color
Sprite 0 53287 ($D027)
Sprite 1 53288 ($D028)
Sprite 2 53289 ($D029)
Sprite 3 53290 ($D02A)
Sprite 4 53291 ($D02B)
Sprite 5 53292 ($D02C)
Sprite 6 53293: ($D02D)
Sprite 7 53294 ($D02E)

The following lines set the multi-color for our sprites, background color, and position the sprites at a starting area on the screen.

lda #7
sta $d015 ; enable sprite
lda #7
sta $d01c ; activate multicolor bits
lda #$f4
sta $d025 ; sprite multicolor reg #1
lda #$f9
sta $d026 ; sprite color #0
lda #$05
sta $d027 ; sprite color #1
lda #0
sta $d021 ; background color
lda #24
sta $d000 ;sprite horizontal(x) position
sta $fb
sta $fc
lda #$90
sta $d001 ;sprite vertical(y) position

Sprite Shape Data

As usual, examining another table should help to simply a few things. The data will not be in a particular order, just in ascending order as seen in the code below so we can stay on course with this tutorial.

Each sprite (0-7) is defined by a pattern that ranges from 24 (across) and 21 (down). They occupy a total of 63 bytes for the shape patterns. The register locations 2040-2047 ($7F8 – $7FF) below setup a pointer in memory where that sprite data exists and can be displayed.

Sprite Shape Data Pointer Description
2040 ($7F8) Sprite Shape Data 1
2041 ($7F9) Sprite Shape Data 2
2042 ($7FA) Sprite Shape Data 3
2043 ($7FB) Sprite Shape Data 4
2044 ($7FC) Sprite Shape Data 5
2045 ($7FD) Sprite Shape Data 6
2046 ($7FE) Sprite Shape Data 7
2047 ($7FF) Sprite Shape Data 8

In this area we set the shape pointer for sprite 0 that will be running across the screen from left to right.

lda #$c0
sta $7f8 ; sprite shape pointer

Position upper left Sprite

The next section will position the example sprite to be visible in the upper left corner. Try changing the x and y coordinate values to see it move around the screen.

Note: Be sure to refer back to the table above if you need to review the sprite registers again.

; show sprite 1 in corner
lda #24
sta $d002 ;sprite 1 X coordinate
lda #50
sta $d003 ;sprite 1 Y coordinate

Below is the colors for sprite 1 (stored in $D028) and we set the shape pointer here also. This is the sprite that appears in the upper left corner (used here for demonstration of having multiple sprites on a single screen).

lda #7
sta $d028 ; sprite 1 color reg
lda #$c4 ; sprite 1 shape data pointer
sta $7f9

Drawing the Floor

Registers 1024-2023 ($400-$7E7) contain the “Video Screen Memory Area” (resides in 1k of memory). Essentially this is the display you see when you boot up your computer or the VICE C64 Emulator. Each row (across) goes from 0-39 and 0-24 (down). So each character positions is reading a value from one of the registers here depending on which line the screen character has been typed on. You can peek into memory here in Basic to learn more about the data the exists here. It can read any character value from 0-255 (in the PET ASCII character set).

Screen Memory Registers Description
1024-1063 ($0400-$0427): Line 1  Screen Memory Data for Line 1
1064-1103 ($0428-$044F): Line 2  Screen Memory Data for Line 2
1104-1143 ($0450-$0477): Line 3  Screen Memory Data for Line 3
1144-1183 ($0478-$049F): Line 4  Screen Memory Data for Line 4
1184-1223 ($04A0-$04C7): Line 5  Screen Memory Data for Line 5
1224-1263 ($04C8-$04EF): Line 6  Screen Memory Data for Line 6
1264-1303 ($04F0-$0517): Line 7  Screen Memory Data for Line 7
1304-1343 ($0518-$053F): Line 8  Screen Memory Data for Line 8
1344-1383 ($0540-$0567): Line 9  Screen Memory Data for Line 9
1384-1423 ($0568-$058F): Line 10  Screen Memory Data for Line 10
1424-1463 ($0590-$05B7): Line 11  Screen Memory Data for Line 11
1464-1503 ($05B8-$05DF): Line 12  Screen Memory Data for Line 12
1504-1543 ($05E0-$0607): Line 13  Screen Memory Data for Line 13
1544-1583 ($0608-$062F): Line 14  Screen Memory Data for Line 14
1584-1623 ($0630-$0657): Line 15  Screen Memory Data for Line 15
1624-1663 ($0658-$067F): Line 16  Screen Memory Data for Line 16
1664-1703 ($0680-$06A7): Line 17  Screen Memory Data for Line 17
1704-1743 ($06A8-$06CF): Line 18  Screen Memory Data for Line 18
1744-1783 ($6D0-$06F7): Line 19  Screen Memory Data for Line 19
1784-1823 ($06F8-$071F): Line 20  Screen Memory Data for Line 20
1824-1863 ($0720-$0747): Line 21  Screen Memory Data for Line 21
1864-1903 ($0748-$076F): Line 22  Screen Memory Data for Line 22
1904-1943 ($0770-$0797): Line 23  Screen Memory Data for Line 23
1944-1983 ($0798-$07BF): Line 24  Screen Memory Data for Line 24

Screen Display Codes

The Commodore 64 retains a character set in memory that can be written to the screen in the area of 1024-2023. The example program here draws an area under the sprite serving as a floor he can walk on. The screen reads in 40 bytes of data and draws one line after the other.

Click on the button to view the screen display codes (0-255). Therefore only values of 0-255 can be placed into screen memory.

Floor placed below the sprite

Here is the code that displays the ground under our animated sprite. Again looking at the Screen Display Codes PDF will help clarify the data that is being written to the screen.

ldx #$28
draw_grnd
lda #102
sta 1583x
lda #13
sta 55855,x

lda #78
sta 1623,x
lda #3
sta 55895,x

lda #77
sta 1663,x
lda #86
sta 1703,x
lda #5
sta 55975,x

dex
bne draw_grnd

Color Memory Map

On the flip side of drawing the screen, we also have to take time to consider the coloring of the background text characters on the screen. This resides in an area known as Color Ram reserved between registers 55296-56295 ($D800-$DBE7). One important thing to remember is that color memory never changes, so you don’t have to copy it anywhere else even  if you decide later you need more room (such as for a massive map environment).

Color Memory Registers Description
55296-55335 ($D800-$D827)  Color Memory Data for Line 1
55336-55375 ($D828-$D84F)  Color Memory Data for Line 2
55376-55415 ($D84F-$D877)  Color Memory Data for Line 3
55416-55455 ($D878-$D89F)  Color Memory Data for Line 4
55456-55495 ($D8A0-$D8C7)  Color Memory Data for Line 5
55496-55495 ($D8C8-$D8EF)  Color Memory Data for Line 6
55536-55575 ($D8F0-$D917)  Color Memory Data for Line 7
55576-55615 ($D918-$D93F)  Color Memory Data for Line 8
55616-55657 ($D940-$D969)  Color Memory Data for Line 9
55656-55697 ($D968-$D991)  Color Memory Data for Line 10
55696-55735 ($D990-$D9B7)  Color Memory Data for Line 11
55736-55775 ($D9B8-$D9DF)  Color Memory Data for Line 12
55776-55815 ($D9E0-$DA07)  Color Memory Data for Line 13
55816-55855 ($DA08-$DA2F)  Color Memory Data for Line 14
55856-55895 ($DA30-$DA57)  Color Memory Data for Line 15
55896-55935 ($DA58-$DA7F)  Color Memory Data for Line 16
55936-55975 ($DA80-$DAA7)  Color Memory Data for Line 17
55976-56015 ($DAA8-$DACF)  Color Memory Data for Line 18
56016-56055 ($DAD0-$DAF7)  Color Memory Data for Line 19
56056-56135 ($DAF8-$DB47)  Color Memory Data for Line 20
56136-56175 ($DB48-$DB6F)  Color Memory Data for Line 21
56176-56215 ($DB70-$DB97)  Color Memory Data for Line 22
56216-56255 ($DB98-$DBBF)  Color Memory Data for Line 23
56256-56295 ($DBC0-$DBE7)  Color Memory Data for Line 24

Reserve Sprite Memory Area

Now that the basic sprite setup is complete, the sprite shapes need to be drawn next. The Commodore 64 registers 2048-40959 ($0800-$9FFF) contain an area of memory dedicated to Basic programming, while the memory area starting at 12288 ($3000) can hold up to 8 sprites.

When you get into more advance techniques later you can move the sprite memory to another area that will allow for the design of
many more sprite shapes. Examine the code below and you will see that is where we begin reading from.

Recall again that each sprite can occupy 63 bytes in that reserved memory area. So for example our sprite memory looks like this:

Sprite Number Sprite Memory Area
Sprite 0 12288-12350 ($3000-$303E)
Sprite 1 12352-12414 ($3040-$307E)
Sprite 2 12416-12478 ($3080-$30BE)
Sprite 3 12480-12542 ($30C0-$30FE)
Sprite 4 12544-12606 ($3100-$313E)
Sprite 5 12608-12670 ($3140-$317E)
Sprite 6 12672-12734 ($3180-$31BE)
Sprite 7 12736-12798 ($31C0-$31FE)

In Basic the sprite shape at 2040 for memory register 12288 is calculated by 192*64 = 12288.
The variables sprite_data1 through sprite_data2 are used to read the sprites in blocks of 63 at a time.

Keep in mind this reserved memory will only hold data for our animated sprite example, but moving memory around later could allow us to store multiple sprites with animations.

Sprite data is read using labels “data1” through “data2” in this example. These variables are actually reading the bytes at the end of this tutorial (look for the “Pitfall Harry sprite data” section).

A closer examination of the bytes section will reveal that there is also sprite data contained in labels “data3” up to “data10”. As you can see, this manages the sprite moving to the left. So essentially we can move the sprite back and forth, but to keep it simplistic it was omitted for this tutorial.

; read in sprite data 0: frame 1
ldx #64
rd_sp1
lda sprite_data1,x
sta 12288,x
dex
bne rd_sp1

; read in sprite 0: frame 3
ldx #64
rd_sp3
lda sprite_data3,x
sta 12416,x
dex
bne rd_sp3

; read in sprite 0: frame 5
ldx #64
rd_sp5
lda sprite_data2,x
sta 12544,x
dex
bne rd_sp5
ldx #0
stx $d000

; read in sprite 0: frame 2
ldx #64
rd_sp2
lda sprite_data2,x
sta 12352,x
dex
bne rd_sp2

; read in sprite 0: frame 4
ldx #64
rd_sp4
lda sprite_data2,x
sta 12480,x
dex
bne rd_sp4

Below are the visual sprite shapes that would occupy areas 12288-12606 (frames 0-3).

Sprite 0: Frame 1

Sprite 0: Frame 2

Sprite 0: Frame 3

Sprite 0: Frame 4

Sprite 0: Frame 5

The first code block sets up our sprite for movement across the screen automatically. A loop cycles to keep the animated sprite walking across the screen. The  sprite animation cycles through frames starting at $07F8 (2040) as we learned about earlier. Then once the frames have exceeded 196 that means we have reached frame 5 and we need to go back to frame 0, which is 192.

start
tax
tay
sty $fb
spr_loop
stx $d000 ; move sprite X from left to right
stx $d000 ; move sprite X from left to right
stx $d000 ; move sprite X from left to right
iny
bne spr_loop

inc $7f8 ; shift through sprite data animation frames
lda $7f8
cmp #$c4 ;check when to flip the frames
bne skip_anim
update_anim
lda #$c0
sta $07f8 ;reset sprite frame animation
inx

Is Sprite Horizontal position = 255?

Now we begin the sprite movement from left to right being sure to check the MSB (most significant bit – register 53264) when the sprite has exceeded the 255th position. This forces the bit to reset and be placed in the Sprite X high byte in order to keep the sprite moving across the screen.

The example image below demonstrates the illusion if the sprite reaches the red line that would indicate that the horizontal position (53248 = $D010) is equal to 255, and now the high bit must be set to allow the sprite to continue the journey to the far right.

When the sprite has exceeded this position, the screen will flash to indicate that this took place.

Finally we finish out the simple sprite animation program by filling memory with the sprite data (from sprite_data1 through sprite_data10).

; Pitfall Harry sprite data

sprite_data1 ; $c0
byte 0,60,0,0,20,0,0,20,0,0,16,0,0,40,0,0,40,0,0,40,0,0,40,128,0,42,128,0
byte 42,0,0,40,0,0,60,0,0,60,0,0,60,0,0,60,0,0,60,0,0,60,0,0,60,0,0
byte 63,0,0,48,0,0,60,0,0

sprite_data2 ; $c1
byte 0,60,0,0,20,0,0,20,0,0,16,0,0,40,0,0,40,0,0,168,0,0,168,128,0
byte 170,128,0,170,0,0,40,0,0,60,0,0,60,0,0,63,0,0,63,0,0,51,0,0,51,0,3
byte 243,0,3,0,240,3,0,192,0,0

sprite_data3 ; $c2
byte 0,60,0,0,20,0,0,20,0,0,16,0,0,40,0,0,40,0,0,40,0,0,40,0,0,40,0,0
byte 42,0,0,42,0,0,60,0,0,60,0,0,63,0,0,15,192,0,12,192,0,255,192,0
byte 204,0,0,204,0,0,12,0,0,15,0,0

sprite_data4 ; $c3
byte 0,60,0,0,20,0,0,20,0,0,16,0,0,40,0,0,40,0,0,168,0,0,168,128,0
byte 170,128,0,170,0,0,40,0,0,60,0,0,63,0,0,255,192,0,243,192,3,192,192,3
byte 192,192,15,0,240,12,0,0,12,0,0,0,0,0

sprite_data5 ; $c4
byte 0,60,0,0,20,0,0,20,0,0,16,0,0,40,0,0,40,128,0,170,128,2,170,0,2
byte 40,0,2,40,0,0,40,0,0,63,0,0,63,192,15,60,192,3,240,192,0,240,240
byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

; Left Man Data

sprite_data6
byte 0,60,0,0,20,0,0,20,0,0,4,0,0,40,0,0,40,0,2,40,0,2,168,0,0,168,0,0
byte 168,0,0,40,0,0,60,0,0,60,0,0,60,0,0,60,0,0,60,0,0,60,0,0,60,0,0
byte 252,0,0,12,0,0,60,0

sprite_data7
byte 0,60,0,0,20,0,0,20,0,0,4,0,0,40,0,0,40,0,0,42,0,2,42,0,2,170,0,0
byte 170,0,0,40,0,0,60,0,0,60,0,0,252,0,0,252,0,0,204,0,0,207,192,0
byte 192,192,15,0,192,3,0,0,0,0,0

sprite_data8
byte 0,60,0,0,20,0,0,20,0,0,4,0,0,40,0,0,40,0,0,40,0,0,40,0,0,40,0,0
byte 168,0,0,168,0,0,60,0,0,60,0,0,252,0,3,240,0,3,48,0,3,255,0,0
byte 51,0,0,51,0,0,48,0,0,240,0

sprite_data9
byte 0,60,0,0,20,0,0,20,0,0,4,0,0,40,0,0,40,0,0,40,0,0,40,0,0,168,0,0
byte 168,0,0,40,0,0,60,0,0,252,0,3,252,0,3,204,0,3,15,0,3,207,0,0
byte 195,0,3,3,0,0,3,0,0,12,0

sprite_data10
byte 0,60,0,0,20,0,0,20,0,0,4,0,0,40,0,0,40,0,0,42,0,2,42,0,2,170,0,0
byte 170,0,0,40,0,0,60,0,0,252,0,3,255,0,3,207,0,3,3,192,3,3,192,15
byte 0,240,0,0,48,0,0,48,0,0,0

If you enjoyed this article on Commodore 64 Assembly Sprites be sure to check out the section on this website called Machine Language Project which demonstrates how to create a professional application regarding sprites.