Game 1 Unit 4 of 64 1 hr learning time
The Background
Draw a bordered arena using the nametable.
6% of Neon Nexus
What You’re Building
A bordered play area. The player is now confined to an arena.

This is the game world taking shape.
The Nametable
The NES background is a 32×30 grid of tile indices. Each byte in the nametable references a tile from the pattern table.
- Nametable starts at
$2000 - 960 bytes total (32 × 30)
- Each byte = one 8×8 tile
To draw the arena, we fill this grid with border and floor tiles.
Drawing the Arena
draw_arena:
; Set PPU address to nametable start
lda #$20
sta PPUADDR
lda #$00
sta PPUADDR
; Draw 30 rows
ldy #0
@draw_row:
; First/last 2 rows are all border
cpy #0
beq @border_row
cpy #1
beq @border_row
cpy #28
beq @border_row
cpy #29
beq @border_row
; Middle rows: border + floor + border
jmp @middle_row
The PPU address auto-increments after each write to PPUDATA. Write 960 tiles and you’ve filled the screen.
Tile Layout
We define three tiles in CHR-ROM:
| Tile | Purpose | Appearance |
|---|---|---|
| 0 | Empty | Transparent |
| 1 | Border | Solid cyan |
| 2 | Floor | Black |
| 3 | Player | Arrow sprite |
The Code
; =============================================================================
; NEON NEXUS - Unit 4: The Background
; =============================================================================
; Add a bordered arena using the nametable.
; =============================================================================
; -----------------------------------------------------------------------------
; NES Hardware Addresses
; -----------------------------------------------------------------------------
PPUCTRL = $2000
PPUMASK = $2001
PPUSTATUS = $2002
OAMADDR = $2003
PPUSCROLL = $2005
PPUADDR = $2006
PPUDATA = $2007
OAMDMA = $4014
JOYPAD1 = $4016
JOYPAD2 = $4017
; Controller buttons
BTN_A = %10000000
BTN_B = %01000000
BTN_SELECT = %00100000
BTN_START = %00010000
BTN_UP = %00001000
BTN_DOWN = %00000100
BTN_LEFT = %00000010
BTN_RIGHT = %00000001
; -----------------------------------------------------------------------------
; Game Constants
; -----------------------------------------------------------------------------
PLAYER_START_X = 124
PLAYER_START_Y = 116
PLAYER_SPEED = 2
; Tile indices
TILE_EMPTY = 0
TILE_BORDER = 1
TILE_FLOOR = 2
TILE_PLAYER = 3
; Arena boundaries (in pixels, accounting for border)
ARENA_LEFT = 16 ; 2 border tiles × 8
ARENA_RIGHT = 232 ; 256 - 16 - 8 (sprite width)
ARENA_TOP = 16
ARENA_BOTTOM = 208 ; 240 - 16 - 16 (overscan + border)
; Palette
BG_COLOUR = $0F ; Black background
; -----------------------------------------------------------------------------
; Memory Layout
; -----------------------------------------------------------------------------
.segment "ZEROPAGE"
player_x: .res 1
player_y: .res 1
buttons: .res 1
temp: .res 1
.segment "OAM"
oam_buffer: .res 256
.segment "BSS"
; -----------------------------------------------------------------------------
; iNES Header
; -----------------------------------------------------------------------------
.segment "HEADER"
.byte "NES", $1A
.byte 2
.byte 1
.byte $01
.byte $00
.byte 0,0,0,0,0,0,0,0
; -----------------------------------------------------------------------------
; Code
; -----------------------------------------------------------------------------
.segment "CODE"
reset:
sei
cld
ldx #$40
stx $4017
ldx #$FF
txs
inx
stx PPUCTRL
stx PPUMASK
stx $4010
@vblank1:
bit PPUSTATUS
bpl @vblank1
lda #0
@clear_ram:
sta $0000, x
sta $0100, x
sta $0200, x
sta $0300, x
sta $0400, x
sta $0500, x
sta $0600, x
sta $0700, x
inx
bne @clear_ram
@vblank2:
bit PPUSTATUS
bpl @vblank2
; Load palette
jsr load_palette
; Draw the arena
jsr draw_arena
; Set up player
lda #PLAYER_START_X
sta player_x
lda #PLAYER_START_Y
sta player_y
; Initialise player sprite
lda player_y
sta oam_buffer+0
lda #TILE_PLAYER
sta oam_buffer+1
lda #0
sta oam_buffer+2
lda player_x
sta oam_buffer+3
; Hide other sprites
lda #$FF
ldx #4
@hide_sprites:
sta oam_buffer, x
inx
bne @hide_sprites
; Reset scroll
lda #0
sta PPUSCROLL
sta PPUSCROLL
; Enable rendering
lda #%10000000 ; NMI on, background and sprites from pattern table 0
sta PPUCTRL
lda #%00011110 ; Show sprites and background
sta PPUMASK
; === Main Loop ===
main_loop:
jsr read_controller
jsr move_player
jmp main_loop
; -----------------------------------------------------------------------------
; Load Palette
; -----------------------------------------------------------------------------
load_palette:
bit PPUSTATUS
lda #$3F
sta PPUADDR
lda #$00
sta PPUADDR
ldx #0
@loop:
lda palette_data, x
sta PPUDATA
inx
cpx #32
bne @loop
rts
; -----------------------------------------------------------------------------
; Draw Arena
; -----------------------------------------------------------------------------
draw_arena:
; Set PPU address to start of nametable ($2000)
bit PPUSTATUS
lda #$20
sta PPUADDR
lda #$00
sta PPUADDR
; Draw 30 rows
ldy #0 ; Row counter
@draw_row:
; Check if first row, last row, or middle
cpy #0
beq @border_row
cpy #1
beq @border_row
cpy #28
beq @border_row
cpy #29
beq @border_row
; Middle row: border + floor + border
jmp @middle_row
@border_row:
; All border tiles
ldx #32
@border_loop:
lda #TILE_BORDER
sta PPUDATA
dex
bne @border_loop
jmp @next_row
@middle_row:
; Left border (2 tiles)
lda #TILE_BORDER
sta PPUDATA
sta PPUDATA
; Floor (28 tiles)
ldx #28
@floor_loop:
lda #TILE_FLOOR
sta PPUDATA
dex
bne @floor_loop
; Right border (2 tiles)
lda #TILE_BORDER
sta PPUDATA
sta PPUDATA
@next_row:
iny
cpy #30
bne @draw_row
rts
; -----------------------------------------------------------------------------
; Read Controller
; -----------------------------------------------------------------------------
read_controller:
lda #1
sta JOYPAD1
lda #0
sta JOYPAD1
ldx #8
@read_loop:
lda JOYPAD1
lsr a
rol buttons
dex
bne @read_loop
rts
; -----------------------------------------------------------------------------
; Move Player
; -----------------------------------------------------------------------------
move_player:
; UP
lda buttons
and #BTN_UP
beq @check_down
lda player_y
sec
sbc #PLAYER_SPEED
cmp #ARENA_TOP
bcc @check_down
sta player_y
@check_down:
lda buttons
and #BTN_DOWN
beq @check_left
lda player_y
clc
adc #PLAYER_SPEED
cmp #ARENA_BOTTOM
bcs @check_left
sta player_y
@check_left:
lda buttons
and #BTN_LEFT
beq @check_right
lda player_x
sec
sbc #PLAYER_SPEED
cmp #ARENA_LEFT
bcc @check_right
sta player_x
@check_right:
lda buttons
and #BTN_RIGHT
beq @done
lda player_x
clc
adc #PLAYER_SPEED
cmp #ARENA_RIGHT
bcs @done
sta player_x
@done:
rts
; === NMI ===
nmi:
pha
txa
pha
tya
pha
; DMA sprites
lda #0
sta OAMADDR
lda #>oam_buffer
sta OAMDMA
; Update sprite position
lda player_y
sta oam_buffer+0
lda player_x
sta oam_buffer+3
; Reset scroll (important after any PPU access)
lda #0
sta PPUSCROLL
sta PPUSCROLL
pla
tay
pla
tax
pla
rti
irq:
rti
; -----------------------------------------------------------------------------
; Data
; -----------------------------------------------------------------------------
palette_data:
; Background palettes
.byte BG_COLOUR, $12, $21, $30 ; Palette 0: black, blue, cyan (border), white
.byte BG_COLOUR, $12, $21, $30 ; Palette 1
.byte BG_COLOUR, $12, $21, $30 ; Palette 2
.byte BG_COLOUR, $12, $21, $30 ; Palette 3
; Sprite palettes
.byte BG_COLOUR, $30, $20, $16 ; Palette 0: black, white, red, orange
.byte BG_COLOUR, $30, $20, $16 ; Palette 1
.byte BG_COLOUR, $30, $20, $16 ; Palette 2
.byte BG_COLOUR, $30, $20, $16 ; Palette 3
; -----------------------------------------------------------------------------
; Vectors
; -----------------------------------------------------------------------------
.segment "VECTORS"
.word nmi
.word reset
.word irq
; -----------------------------------------------------------------------------
; CHR-ROM
; -----------------------------------------------------------------------------
.segment "CHARS"
; Tile 0: Empty (transparent)
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00
; Tile 1: Border (solid cyan block)
.byte $00,$00,$00,$00,$00,$00,$00,$00 ; Low plane = 0
.byte $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; High plane = 1 (colour 2 = cyan)
; Tile 2: Floor (empty/dark)
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00
; Tile 3: Player sprite (arrow)
.byte %00011000
.byte %00111100
.byte %01111110
.byte %11111111
.byte %00111100
.byte %00111100
.byte %00111100
.byte %00111100
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
; Fill rest of CHR-ROM
.res 8192 - 64, $00
Build It
ca65 nexus.asm -o nexus.o
ld65 -C nes.cfg nexus.o -o nexus.nes
Move with the D-pad. The player now bounces off the arena walls.
Updated Boundaries
The movement code now uses arena boundaries instead of screen edges:
ARENA_LEFT = 16 ; 2 border tiles × 8 pixels
ARENA_RIGHT = 232
ARENA_TOP = 16
ARENA_BOTTOM = 208
The player can’t escape the play area.
Next
The arena works but looks plain. Unit 5 adds custom tile graphics.
What Changed
Unit 3 → Unit 4
+158-67
| 1 | 1 | ; ============================================================================= | |
| 2 | - | ; NEON NEXUS - Unit 2: Moving the Player | |
| 2 | + | ; NEON NEXUS - Unit 4: The Background | |
| 3 | 3 | ; ============================================================================= | |
| 4 | - | ; The player sprite now moves with the D-pad. | |
| 5 | - | ; Press Up, Down, Left, Right to move around the screen. | |
| 4 | + | ; Add a bordered arena using the nametable. | |
| 6 | 5 | ; ============================================================================= | |
| 7 | 6 | | |
| 8 | 7 | ; ----------------------------------------------------------------------------- | |
| ... | |||
| 12 | 11 | PPUMASK = $2001 | |
| 13 | 12 | PPUSTATUS = $2002 | |
| 14 | 13 | OAMADDR = $2003 | |
| 14 | + | PPUSCROLL = $2005 | |
| 15 | 15 | PPUADDR = $2006 | |
| 16 | 16 | PPUDATA = $2007 | |
| 17 | 17 | OAMDMA = $4014 | |
| 18 | 18 | | |
| 19 | - | JOYPAD1 = $4016 ; Controller 1 | |
| 20 | - | JOYPAD2 = $4017 ; Controller 2 | |
| 19 | + | JOYPAD1 = $4016 | |
| 20 | + | JOYPAD2 = $4017 | |
| 21 | 21 | | |
| 22 | - | ; Controller button masks | |
| 22 | + | ; Controller buttons | |
| 23 | 23 | BTN_A = %10000000 | |
| 24 | 24 | BTN_B = %01000000 | |
| 25 | 25 | BTN_SELECT = %00100000 | |
| ... | |||
| 34 | 34 | ; ----------------------------------------------------------------------------- | |
| 35 | 35 | PLAYER_START_X = 124 | |
| 36 | 36 | PLAYER_START_Y = 116 | |
| 37 | - | PLAYER_TILE = 1 | |
| 38 | - | BG_COLOUR = $12 | |
| 37 | + | PLAYER_SPEED = 2 | |
| 39 | 38 | | |
| 40 | - | PLAYER_SPEED = 2 ; Pixels per frame (try 1, 2, or 3) | |
| 39 | + | ; Tile indices | |
| 40 | + | TILE_EMPTY = 0 | |
| 41 | + | TILE_BORDER = 1 | |
| 42 | + | TILE_FLOOR = 2 | |
| 43 | + | TILE_PLAYER = 3 | |
| 41 | 44 | | |
| 42 | - | ; Screen boundaries | |
| 43 | - | SCREEN_LEFT = 0 | |
| 44 | - | SCREEN_RIGHT = 248 ; 256 - 8 (sprite width) | |
| 45 | - | SCREEN_TOP = 0 | |
| 46 | - | SCREEN_BOTTOM = 224 ; 240 - 16 (sprite height + overscan) | |
| 45 | + | ; Arena boundaries (in pixels, accounting for border) | |
| 46 | + | ARENA_LEFT = 16 ; 2 border tiles × 8 | |
| 47 | + | ARENA_RIGHT = 232 ; 256 - 16 - 8 (sprite width) | |
| 48 | + | ARENA_TOP = 16 | |
| 49 | + | ARENA_BOTTOM = 208 ; 240 - 16 - 16 (overscan + border) | |
| 50 | + | | |
| 51 | + | ; Palette | |
| 52 | + | BG_COLOUR = $0F ; Black background | |
| 47 | 53 | | |
| 48 | 54 | ; ----------------------------------------------------------------------------- | |
| 49 | 55 | ; Memory Layout | |
| ... | |||
| 51 | 57 | .segment "ZEROPAGE" | |
| 52 | 58 | player_x: .res 1 | |
| 53 | 59 | player_y: .res 1 | |
| 54 | - | buttons: .res 1 ; Current button state | |
| 60 | + | buttons: .res 1 | |
| 61 | + | temp: .res 1 | |
| 55 | 62 | | |
| 56 | 63 | .segment "OAM" | |
| 57 | 64 | oam_buffer: .res 256 | |
| ... | |||
| 63 | 70 | ; ----------------------------------------------------------------------------- | |
| 64 | 71 | .segment "HEADER" | |
| 65 | 72 | .byte "NES", $1A | |
| 66 | - | .byte 2 ; 32KB PRG-ROM | |
| 67 | - | .byte 1 ; 8KB CHR-ROM | |
| 68 | - | .byte $01 ; Mapper 0, vertical mirroring | |
| 73 | + | .byte 2 | |
| 74 | + | .byte 1 | |
| 75 | + | .byte $01 | |
| 69 | 76 | .byte $00 | |
| 70 | 77 | .byte 0,0,0,0,0,0,0,0 | |
| 71 | 78 | | |
| ... | |||
| 108 | 115 | bpl @vblank2 | |
| 109 | 116 | | |
| 110 | 117 | ; Load palette | |
| 111 | - | bit PPUSTATUS | |
| 112 | - | lda #$3F | |
| 113 | - | sta PPUADDR | |
| 114 | - | lda #$00 | |
| 115 | - | sta PPUADDR | |
| 118 | + | jsr load_palette | |
| 116 | 119 | | |
| 117 | - | ldx #0 | |
| 118 | - | @load_palette: | |
| 119 | - | lda palette_data, x | |
| 120 | - | sta PPUDATA | |
| 121 | - | inx | |
| 122 | - | cpx #32 | |
| 123 | - | bne @load_palette | |
| 120 | + | ; Draw the arena | |
| 121 | + | jsr draw_arena | |
| 124 | 122 | | |
| 125 | 123 | ; Set up player | |
| 126 | 124 | lda #PLAYER_START_X | |
| ... | |||
| 131 | 129 | ; Initialise player sprite | |
| 132 | 130 | lda player_y | |
| 133 | 131 | sta oam_buffer+0 | |
| 134 | - | lda #PLAYER_TILE | |
| 132 | + | lda #TILE_PLAYER | |
| 135 | 133 | sta oam_buffer+1 | |
| 136 | 134 | lda #0 | |
| 137 | 135 | sta oam_buffer+2 | |
| ... | |||
| 145 | 143 | sta oam_buffer, x | |
| 146 | 144 | inx | |
| 147 | 145 | bne @hide_sprites | |
| 146 | + | | |
| 147 | + | ; Reset scroll | |
| 148 | + | lda #0 | |
| 149 | + | sta PPUSCROLL | |
| 150 | + | sta PPUSCROLL | |
| 148 | 151 | | |
| 149 | 152 | ; Enable rendering | |
| 150 | - | lda #%10010000 | |
| 153 | + | lda #%10000000 ; NMI on, background and sprites from pattern table 0 | |
| 151 | 154 | sta PPUCTRL | |
| 152 | - | lda #%00011110 | |
| 155 | + | lda #%00011110 ; Show sprites and background | |
| 153 | 156 | sta PPUMASK | |
| 154 | 157 | | |
| 155 | 158 | ; === Main Loop === | |
| 156 | 159 | main_loop: | |
| 157 | - | ; Read controller | |
| 158 | 160 | jsr read_controller | |
| 159 | - | | |
| 160 | - | ; Move player based on input | |
| 161 | 161 | jsr move_player | |
| 162 | - | | |
| 163 | 162 | jmp main_loop | |
| 163 | + | | |
| 164 | + | ; ----------------------------------------------------------------------------- | |
| 165 | + | ; Load Palette | |
| 166 | + | ; ----------------------------------------------------------------------------- | |
| 167 | + | load_palette: | |
| 168 | + | bit PPUSTATUS | |
| 169 | + | lda #$3F | |
| 170 | + | sta PPUADDR | |
| 171 | + | lda #$00 | |
| 172 | + | sta PPUADDR | |
| 173 | + | | |
| 174 | + | ldx #0 | |
| 175 | + | @loop: | |
| 176 | + | lda palette_data, x | |
| 177 | + | sta PPUDATA | |
| 178 | + | inx | |
| 179 | + | cpx #32 | |
| 180 | + | bne @loop | |
| 181 | + | rts | |
| 182 | + | | |
| 183 | + | ; ----------------------------------------------------------------------------- | |
| 184 | + | ; Draw Arena | |
| 185 | + | ; ----------------------------------------------------------------------------- | |
| 186 | + | draw_arena: | |
| 187 | + | ; Set PPU address to start of nametable ($2000) | |
| 188 | + | bit PPUSTATUS | |
| 189 | + | lda #$20 | |
| 190 | + | sta PPUADDR | |
| 191 | + | lda #$00 | |
| 192 | + | sta PPUADDR | |
| 193 | + | | |
| 194 | + | ; Draw 30 rows | |
| 195 | + | ldy #0 ; Row counter | |
| 196 | + | | |
| 197 | + | @draw_row: | |
| 198 | + | ; Check if first row, last row, or middle | |
| 199 | + | cpy #0 | |
| 200 | + | beq @border_row | |
| 201 | + | cpy #1 | |
| 202 | + | beq @border_row | |
| 203 | + | cpy #28 | |
| 204 | + | beq @border_row | |
| 205 | + | cpy #29 | |
| 206 | + | beq @border_row | |
| 207 | + | | |
| 208 | + | ; Middle row: border + floor + border | |
| 209 | + | jmp @middle_row | |
| 210 | + | | |
| 211 | + | @border_row: | |
| 212 | + | ; All border tiles | |
| 213 | + | ldx #32 | |
| 214 | + | @border_loop: | |
| 215 | + | lda #TILE_BORDER | |
| 216 | + | sta PPUDATA | |
| 217 | + | dex | |
| 218 | + | bne @border_loop | |
| 219 | + | jmp @next_row | |
| 220 | + | | |
| 221 | + | @middle_row: | |
| 222 | + | ; Left border (2 tiles) | |
| 223 | + | lda #TILE_BORDER | |
| 224 | + | sta PPUDATA | |
| 225 | + | sta PPUDATA | |
| 226 | + | | |
| 227 | + | ; Floor (28 tiles) | |
| 228 | + | ldx #28 | |
| 229 | + | @floor_loop: | |
| 230 | + | lda #TILE_FLOOR | |
| 231 | + | sta PPUDATA | |
| 232 | + | dex | |
| 233 | + | bne @floor_loop | |
| 234 | + | | |
| 235 | + | ; Right border (2 tiles) | |
| 236 | + | lda #TILE_BORDER | |
| 237 | + | sta PPUDATA | |
| 238 | + | sta PPUDATA | |
| 239 | + | | |
| 240 | + | @next_row: | |
| 241 | + | iny | |
| 242 | + | cpy #30 | |
| 243 | + | bne @draw_row | |
| 244 | + | | |
| 245 | + | rts | |
| 164 | 246 | | |
| 165 | 247 | ; ----------------------------------------------------------------------------- | |
| 166 | 248 | ; Read Controller | |
| 167 | 249 | ; ----------------------------------------------------------------------------- | |
| 168 | 250 | read_controller: | |
| 169 | - | ; Strobe the controller | |
| 170 | 251 | lda #1 | |
| 171 | 252 | sta JOYPAD1 | |
| 172 | 253 | lda #0 | |
| 173 | 254 | sta JOYPAD1 | |
| 174 | 255 | | |
| 175 | - | ; Read 8 buttons into 'buttons' variable | |
| 176 | 256 | ldx #8 | |
| 177 | 257 | @read_loop: | |
| 178 | 258 | lda JOYPAD1 | |
| 179 | - | lsr a ; Bit 0 -> Carry | |
| 180 | - | rol buttons ; Carry -> buttons | |
| 259 | + | lsr a | |
| 260 | + | rol buttons | |
| 181 | 261 | dex | |
| 182 | 262 | bne @read_loop | |
| 183 | - | | |
| 184 | 263 | rts | |
| 185 | 264 | | |
| 186 | 265 | ; ----------------------------------------------------------------------------- | |
| 187 | 266 | ; Move Player | |
| 188 | 267 | ; ----------------------------------------------------------------------------- | |
| 189 | 268 | move_player: | |
| 190 | - | ; Check UP | |
| 269 | + | ; UP | |
| 191 | 270 | lda buttons | |
| 192 | 271 | and #BTN_UP | |
| 193 | 272 | beq @check_down | |
| 194 | - | | |
| 195 | 273 | lda player_y | |
| 196 | 274 | sec | |
| 197 | 275 | sbc #PLAYER_SPEED | |
| 198 | - | cmp #SCREEN_TOP | |
| 199 | - | bcc @check_down ; Don't go above top | |
| 276 | + | cmp #ARENA_TOP | |
| 277 | + | bcc @check_down | |
| 200 | 278 | sta player_y | |
| 201 | 279 | | |
| 202 | 280 | @check_down: | |
| 203 | 281 | lda buttons | |
| 204 | 282 | and #BTN_DOWN | |
| 205 | 283 | beq @check_left | |
| 206 | - | | |
| 207 | 284 | lda player_y | |
| 208 | 285 | clc | |
| 209 | 286 | adc #PLAYER_SPEED | |
| 210 | - | cmp #SCREEN_BOTTOM | |
| 211 | - | bcs @check_left ; Don't go below bottom | |
| 287 | + | cmp #ARENA_BOTTOM | |
| 288 | + | bcs @check_left | |
| 212 | 289 | sta player_y | |
| 213 | 290 | | |
| 214 | 291 | @check_left: | |
| 215 | 292 | lda buttons | |
| 216 | 293 | and #BTN_LEFT | |
| 217 | 294 | beq @check_right | |
| 218 | - | | |
| 219 | 295 | lda player_x | |
| 220 | 296 | sec | |
| 221 | 297 | sbc #PLAYER_SPEED | |
| 222 | - | cmp #SCREEN_LEFT | |
| 223 | - | bcc @check_right ; Don't go past left edge | |
| 298 | + | cmp #ARENA_LEFT | |
| 299 | + | bcc @check_right | |
| 224 | 300 | sta player_x | |
| 225 | 301 | | |
| 226 | 302 | @check_right: | |
| 227 | 303 | lda buttons | |
| 228 | 304 | and #BTN_RIGHT | |
| 229 | 305 | beq @done | |
| 230 | - | | |
| 231 | 306 | lda player_x | |
| 232 | 307 | clc | |
| 233 | 308 | adc #PLAYER_SPEED | |
| 234 | - | cmp #SCREEN_RIGHT | |
| 235 | - | bcs @done ; Don't go past right edge | |
| 309 | + | cmp #ARENA_RIGHT | |
| 310 | + | bcs @done | |
| 236 | 311 | sta player_x | |
| 237 | 312 | | |
| 238 | 313 | @done: | |
| ... | |||
| 252 | 327 | lda #>oam_buffer | |
| 253 | 328 | sta OAMDMA | |
| 254 | 329 | | |
| 255 | - | ; Update sprite from player position | |
| 330 | + | ; Update sprite position | |
| 256 | 331 | lda player_y | |
| 257 | 332 | sta oam_buffer+0 | |
| 258 | 333 | lda player_x | |
| 259 | 334 | sta oam_buffer+3 | |
| 335 | + | | |
| 336 | + | ; Reset scroll (important after any PPU access) | |
| 337 | + | lda #0 | |
| 338 | + | sta PPUSCROLL | |
| 339 | + | sta PPUSCROLL | |
| 260 | 340 | | |
| 261 | 341 | pla | |
| 262 | 342 | tay | |
| ... | |||
| 272 | 352 | ; Data | |
| 273 | 353 | ; ----------------------------------------------------------------------------- | |
| 274 | 354 | palette_data: | |
| 275 | - | .byte BG_COLOUR, $00, $10, $20 | |
| 276 | - | .byte BG_COLOUR, $00, $10, $20 | |
| 277 | - | .byte BG_COLOUR, $00, $10, $20 | |
| 278 | - | .byte BG_COLOUR, $00, $10, $20 | |
| 279 | - | .byte BG_COLOUR, $30, $20, $0F | |
| 280 | - | .byte BG_COLOUR, $30, $16, $0F | |
| 281 | - | .byte BG_COLOUR, $30, $12, $0F | |
| 282 | - | .byte BG_COLOUR, $30, $14, $0F | |
| 355 | + | ; Background palettes | |
| 356 | + | .byte BG_COLOUR, $12, $21, $30 ; Palette 0: black, blue, cyan (border), white | |
| 357 | + | .byte BG_COLOUR, $12, $21, $30 ; Palette 1 | |
| 358 | + | .byte BG_COLOUR, $12, $21, $30 ; Palette 2 | |
| 359 | + | .byte BG_COLOUR, $12, $21, $30 ; Palette 3 | |
| 360 | + | ; Sprite palettes | |
| 361 | + | .byte BG_COLOUR, $30, $20, $16 ; Palette 0: black, white, red, orange | |
| 362 | + | .byte BG_COLOUR, $30, $20, $16 ; Palette 1 | |
| 363 | + | .byte BG_COLOUR, $30, $20, $16 ; Palette 2 | |
| 364 | + | .byte BG_COLOUR, $30, $20, $16 ; Palette 3 | |
| 283 | 365 | | |
| 284 | 366 | ; ----------------------------------------------------------------------------- | |
| 285 | 367 | ; Vectors | |
| ... | |||
| 294 | 376 | ; ----------------------------------------------------------------------------- | |
| 295 | 377 | .segment "CHARS" | |
| 296 | 378 | | |
| 297 | - | ; Tile 0: Empty | |
| 379 | + | ; Tile 0: Empty (transparent) | |
| 298 | 380 | .byte $00,$00,$00,$00,$00,$00,$00,$00 | |
| 299 | 381 | .byte $00,$00,$00,$00,$00,$00,$00,$00 | |
| 300 | 382 | | |
| 301 | - | ; Tile 1: Player sprite | |
| 383 | + | ; Tile 1: Border (solid cyan block) | |
| 384 | + | .byte $00,$00,$00,$00,$00,$00,$00,$00 ; Low plane = 0 | |
| 385 | + | .byte $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; High plane = 1 (colour 2 = cyan) | |
| 386 | + | | |
| 387 | + | ; Tile 2: Floor (empty/dark) | |
| 388 | + | .byte $00,$00,$00,$00,$00,$00,$00,$00 | |
| 389 | + | .byte $00,$00,$00,$00,$00,$00,$00,$00 | |
| 390 | + | | |
| 391 | + | ; Tile 3: Player sprite (arrow) | |
| 302 | 392 | .byte %00011000 | |
| 303 | 393 | .byte %00111100 | |
| 304 | 394 | .byte %01111110 | |
| ... | |||
| 316 | 406 | .byte %00000000 | |
| 317 | 407 | .byte %00000000 | |
| 318 | 408 | | |
| 319 | - | .res 8192 - 32, $00 | |
| 409 | + | ; Fill rest of CHR-ROM | |
| 410 | + | .res 8192 - 64, $00 | |
| 320 | 411 | |