2

Neon Nexus: Your Game Entity

Commodore 64 • Phase 1 • Tier 1

Add your first controllable game entity to Neon Nexus! Create a geometric player shape that moves smoothly around your neon grid world while learning how the 6502 tracks position with registers.

easy
⏱️ 30-45 minutes
💻 Code Examples
🛠️ Exercise
🎯

Learning Objectives

  • Create a controllable geometric player entity in your game world
  • Learn X and Y registers through player position tracking
  • Understand memory as your game's data storage system
  • Master the STA instruction through game object management
  • Experience smooth player movement on the C64
🧠

Key Concepts

Game entity creation and positioning X and Y registers for position tracking Memory storage for game state Player movement and screen coordinates Game object management fundamentals

Lesson 2: Neon Nexus - Your Game Entity

Your neon world needs a player! In the last lesson, you created the striking visual foundation of Neon Nexus. Today, you’ll add the most important element - a controllable geometric entity that moves smoothly through your world. Get ready to see your first interactive game come to life!

Building Your Player Entity

We’ll create a player character step by step, testing each addition to understand how it works.

Step 1: Start with Your Arena

Create a new file player.s with the foundation from lesson 1:

; Neon Nexus - Lesson 2
; Adding our player entity

*= $0801

; BASIC stub: 10 SYS 2061
!word next_line
!word 10
!byte $9e
!text "2061"
!byte 0
next_line:
!word 0

; Start of our program
start:
        jsr setup_arena
        rts

setup_arena:
        ; Set up arena colors
        lda #$06        ; Dark blue border
        sta $d020
        lda #$00        ; Black background
        sta $d021
        
        ; Clear screen
        lda #$93
        jsr $ffd2
        
        rts

Build and test to make sure your foundation works:

acme -f cbm -o player.prg player.s
x64sc player.prg

Type RUN - you should see your dark blue border and black background.

Step 2: Add a Simple Player Character

Let’s put a player on screen. Add this new subroutine after setup_arena:

create_player:
        ; Put player character on screen
        lda #$5a        ; Diamond character
        sta $0400       ; Top-left of screen
        
        ; Make it bright yellow
        lda #$07        ; Yellow color
        sta $d800       ; Color RAM for top-left
        
        rts

Then update start: to call it:

start:
        jsr setup_arena
        jsr create_player  ; ADD THIS LINE
        rts

Build and test - you should see a yellow diamond in the top-left corner!

Step 3: Move Player to Center

Top-left isn’t great for a player start position. Let’s center it. Replace your create_player: subroutine:

create_player:
        ; Put player at center of screen
        ; Center = row 12, column 20
        ; Position = (12 * 40) + 20 = 480 + 20 = 500
        
        lda #$5a        ; Diamond character
        sta $0400 + 500 ; Center position
        
        ; Make it bright yellow
        lda #$07        ; Yellow color
        sta $d800 + 500 ; Color at same position
        
        rts

Build and test - the diamond should now appear centered on screen!

Key concept: The C64 screen has 40 columns × 25 rows. To find any position: (row × 40) + column

Step 4: Store Player Position

Hard-coding positions isn’t flexible. Let’s use memory to track the player’s location. First, we’ll add position variables at the end of our program (after all subroutines) to avoid breaking the BASIC stub address:

; Player data (we'll add this at the END of the file)
player_x:   !byte 20    ; X position (column)
player_y:   !byte 12    ; Y position (row)

This reserves two bytes of memory to store our player’s X and Y coordinates.

Step 5: Use Variables for Positioning

Now let’s use these variables. For now, we’ll keep the fixed position but load the values to prepare for the next step. Replace your create_player: subroutine:

create_player:
        ; For now, still use fixed position
        ; We'll calculate from X,Y in next step
        
        lda #$5a        ; Diamond character
        sta $0400 + 500 ; Center position
        
        lda #$07        ; Yellow color
        sta $d800 + 500 ; Color at same position
        
        ; Load position into registers
        ldx player_x    ; Load X position
        ldy player_y    ; Load Y position
        
        rts

Build and test - it should still work the same, but now we’re loading the position values.

Step 6: Calculate Screen Position

Time for the real magic - calculating screen position from X,Y coordinates. Add this new subroutine:

calculate_screen_pos:
        ; Calculate screen address from player_x and player_y
        ; Formula: $0400 + (Y * 40) + X
        
        ; Start with base screen address
        lda #$00
        sta $fb         ; Low byte
        lda #$04        ; High byte ($0400)
        sta $fc
        
        ; Add Y offset (Y * 40)
        ldy player_y
        beq add_x       ; Skip if Y=0
        
multiply_y:
        ; Add 40 for each row
        lda $fb
        clc
        adc #40
        sta $fb
        bcc no_carry
        inc $fc         ; Handle carry to high byte
no_carry:
        dey
        bne multiply_y
        
add_x:
        ; Add X offset
        lda $fb
        clc
        adc player_x
        sta $fb
        bcc done
        inc $fc
done:
        rts

This stores the calculated screen address in zero page locations $FB-$FC.

Step 6b: Add Variables at End of Program

Important: Now add the player variables at the very end of your program file (after all subroutines):

; Player data (at end to avoid breaking BASIC stub)
player_x:   !byte 20    ; X position (column)
player_y:   !byte 12    ; Y position (row)

Why at the end? If we put variables between the BASIC stub and our code, they would shift the memory addresses and break the SYS 2061 call!

Step 7: Use Calculated Position

Now let’s use our calculation. Replace create_player: again:

create_player:
        ; Calculate screen position
        jsr calculate_screen_pos
        
        ; Place character at calculated position
        lda #$5a        ; Diamond character
        ldy #$00
        sta ($fb),y     ; Indirect addressing!
        
        ; Calculate color RAM position (same offset but base $D800)
        lda #$00
        sta $fd
        lda #$d8        ; High byte for color RAM
        sta $fe
        
        ; Add same Y*40+X offset
        ldy player_y
        beq add_x_color
        
multiply_y_color:
        lda $fd
        clc
        adc #40
        sta $fd
        bcc no_carry_color
        inc $fe
no_carry_color:
        dey
        bne multiply_y_color
        
add_x_color:
        lda $fd
        clc
        adc player_x
        sta $fd
        bcc place_color
        inc $fe
        
place_color:
        ; Set color
        lda #$07        ; Yellow
        ldy #$00
        sta ($fd),y
        
        rts

Build and test - the player should still appear in the center, but now it’s positioned using our variables!

Step 8: Test Different Positions

Let’s verify our positioning works. Change the player starting position:

; Player data
player_x:   !byte 5     ; Was 20
player_y:   !byte 5     ; Was 12

Build and test - the player should now appear near the top-left. Try different values:

  • player_x: !byte 35, player_y: !byte 20 - bottom-right area
  • player_x: !byte 0, player_y: !byte 0 - exact top-left
  • player_x: !byte 20, player_y: !byte 12 - back to center

Step 9: Clean Up Screen Clearing

The KERNAL clear screen can have side effects. Let’s do it manually. Replace the clear screen section in setup_arena:

setup_arena:
        ; Set up arena colors
        lda #$06        ; Dark blue border
        sta $d020
        lda #$00        ; Black background
        sta $d021
        
        ; Clear screen manually      ; REPLACE OLD CLEAR
        lda #$20        ; Space character
        ldx #$00
clear_loop:
        sta $0400,x     ; Screen page 1
        sta $0500,x     ; Screen page 2
        sta $0600,x     ; Screen page 3
        sta $0700,x     ; Screen page 4
        inx
        bne clear_loop
        
        rts

This clears the entire screen memory without using KERNAL routines.

Step 10: Add Program Loop

Right now our program exits immediately. Let’s keep it running. Replace the rts at the end of start: with:

start:
        jsr setup_arena
        jsr create_player
        
game_loop:              ; REPLACE RTS WITH THIS
        jmp game_loop   ; Infinite loop

Now the program stays running, ready for future additions like keyboard input!

Complete Working Examples

The complete working code for this lesson is available in our code samples repository:

📁 Lesson 2 Code Examples

All examples are tested and ready to assemble with ACME:

acme -f cbm -o player.prg complete.s
x64sc player.prg

You’ll see a yellow diamond character positioned at the center of your neon arena!

Understanding What You Built

Screen Memory Layout

The C64 screen is a grid of 1000 characters (40×25):

Commodore 64 Memory Map

64KB Address Space

Zero Page $0000-$00FF
Fast access memory for variables and pointers
Stack $0100-$01FF
Processor stack for subroutines and interrupts
User RAM $0200-$03FF
General purpose RAM for programs
Screen RAM $0400-$07E7
Character codes displayed on screen (1000 bytes)
User RAM $07E8-$0FFF
More general purpose RAM
User RAM $1000-$9FFF
Main program area (36KB)
BASIC ROM $A000-$BFFF
BASIC interpreter (8KB)
User RAM $C000-$CFFF
RAM under BASIC ROM (4KB)
I/O & Color $D000-$DFFF
VIC-II, SID, CIA chips & color RAM
KERNAL ROM $E000-$FFFF
Operating system routines (8KB)

Memory Overview

Total RAM: 64KB
Available RAM: ~38KB
Screen Memory: 1KB
Zero Page: 256 bytes

Important Addresses

$0400: Screen memory start
$D000: VIC-II chip
$D400: SID sound chip
$D800: Color RAM
$FFD2: CHROUT routine

Programming Tips

Zero Page: Fastest memory access
$0801: BASIC program start
Screen RAM: Direct character control
I/O Area: Hardware programming

Each position on screen corresponds to a memory address:

  • Top-left: $0400
  • Top-right: $0427 (39 decimal)
  • Bottom-left: $07C0
  • Bottom-right: $07E7

Position Calculation

To find any screen position:

  1. Start with base address ($0400)
  2. Add Y × 40 (moves down Y rows)
  3. Add X (moves right X columns)

Example: Position (10, 5)

  • Base: $0400
  • Y offset: 5 × 40 = 200 ($C8)
  • X offset: 10 ($0A)
  • Final: $0400 + $C8 + $0A = $04D2

Indirect Addressing

The instruction sta ($fb),y is powerful:

  • It stores A at the address pointed to by $FB-$FC
  • The Y register adds an offset
  • This lets us use calculated addresses

Experiment and Expand

Try these modifications:

  1. Different starting positions:

    player_x: !byte 0     ; Far left
    player_x: !byte 39    ; Far right
    player_y: !byte 24    ; Bottom row
    
  2. Different player characters:

    lda #$51    ; Circle
    lda #$53    ; Heart  
    lda #$58    ; X shape
    lda #$2a    ; Star
    
  3. Different colors:

    lda #$01    ; White
    lda #$02    ; Red
    lda #$05    ; Green
    lda #$0d    ; Light green
    
  4. Multiple players:

    ; After first player
    lda #$0a
    sta player_x
    lda #$05
    sta player_y
    jsr create_player  ; Creates second player!
    

What You’ve Accomplished

Created a game entity with position tracking
Learned memory variables for storing game state
Mastered screen positioning with coordinate math
Used indirect addressing for flexible memory access
Built a reusable system for placing objects on screen

You now have the foundation for any game object - enemies, items, obstacles - they all use this same positioning system!

Common Issues and Solutions

Player doesn’t appear:

  • Check X coordinate is 0-39
  • Check Y coordinate is 0-24
  • Verify color isn’t same as background

Wrong position:

  • Remember: X is column (0-39), Y is row (0-24)
  • Top-left is (0,0), not (1,1)

Calculation seems off:

  • Make sure to clear carry (CLC) before additions
  • Handle carries to high byte in address calculation

Coming Next

In Lesson 3, you’ll make your player move! You’ll learn:

  • Reading keyboard input
  • Updating position based on keypresses
  • Redrawing the player at new positions
  • Creating smooth, responsive movement

Your static player is about to come alive!

Quick Reference

New Instructions:

  • LDX address - Load X register from memory
  • LDY address - Load Y register from memory
  • STA ($addr),Y - Store using indirect addressing
  • CLC - Clear carry flag before addition
  • ADC #value - Add with carry

Memory Locations:

  • $0400-$07E7 - Screen memory
  • $D800-$DBE7 - Color memory
  • $FB-$FC - Zero page pointer (screen)
  • $FD-$FE - Zero page pointer (color)

Position Formula:

Screen address = $0400 + (Y × 40) + X
Color address = $D800 + (Y × 40) + X

Ready for lesson 3? Time to make your player move!