Skip to content
Game 1 Unit 8 of 64 1 hr learning time

Enemy Movement

Make enemies patrol the arena with bounce patterns.

13% of Neon Nexus

What You’re Building

Enemies that patrol. They bounce off walls, constantly hunting.

Enemy Movement

The arena is now alive with danger.

Direction Variables

Each enemy needs a direction for X and Y movement:

enemy_dir_x:  .res NUM_ENEMIES   ; +1 (right) or -1 (left)
enemy_dir_y:  .res NUM_ENEMIES   ; +1 (down) or -1 (up)

We use $FF for -1 (two’s complement). When added to a position, it subtracts 1.

Initialising Directions

Each enemy starts moving in a different diagonal:

init_enemies:
    ; Enemy 0: Top-left, moving right and down
    lda #1
    sta enemy_dir_x+0
    sta enemy_dir_y+0

    ; Enemy 1: Top-right, moving left and down
    lda #$FF            ; -1
    sta enemy_dir_x+1
    lda #1
    sta enemy_dir_y+1
    ; ...

Movement Logic

Each frame, move each enemy and check for wall collisions:

move_enemies:
    ldx #0

@enemy_loop:
    ; Move X
    lda enemy_x, x
    clc
    adc enemy_dir_x, x
    sta enemy_x, x

    ; Check bounds
    cmp #ARENA_LEFT
    bcs @check_right
    ; Hit left wall - reverse
    lda #1
    sta enemy_dir_x, x
    lda #ARENA_LEFT
    sta enemy_x, x
    jmp @move_y

@check_right:
    cmp #ARENA_RIGHT
    bcc @move_y
    ; Hit right wall - reverse
    lda #$FF
    sta enemy_dir_x, x
    ; ... same pattern for Y

The Bounce

When an enemy hits a wall:

  1. Reverse its direction for that axis
  2. Clamp its position to the boundary

This creates the classic “bounce” behaviour seen in many early games.

Calling Movement

Add enemy movement to the main loop:

main_loop:
    jsr read_controller
    jsr move_player
    jsr move_enemies        ; NEW
    jsr update_enemy_sprites
    jmp main_loop

The Code

; =============================================================================
; NEON NEXUS - Unit 8: Enemy Movement
; =============================================================================
; Make enemies patrol the arena with bounce patterns.
; =============================================================================

; -----------------------------------------------------------------------------
; 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
ENEMY_SPEED    = 1

NUM_ENEMIES    = 4

; Tile indices
TILE_EMPTY     = 0
TILE_BORDER    = 1
TILE_FLOOR     = 2
TILE_CORNER_TL = 3
TILE_CORNER_TR = 4
TILE_CORNER_BL = 5
TILE_CORNER_BR = 6

SPRITE_PLAYER  = 7
SPRITE_ENEMY   = 8

; Arena boundaries
ARENA_LEFT   = 16
ARENA_RIGHT  = 232
ARENA_TOP    = 16
ARENA_BOTTOM = 208

; Direction flags
DIR_RIGHT = 1
DIR_LEFT  = $FF     ; -1 in two's complement
DIR_DOWN  = 1
DIR_UP    = $FF

BG_COLOUR = $0F

; -----------------------------------------------------------------------------
; Memory Layout
; -----------------------------------------------------------------------------
.segment "ZEROPAGE"
player_x:     .res 1
player_y:     .res 1
buttons:      .res 1
temp:         .res 1
row_counter:  .res 1
frame_count:  .res 1

; Enemy data
enemy_x:      .res NUM_ENEMIES
enemy_y:      .res NUM_ENEMIES
enemy_dir_x:  .res NUM_ENEMIES   ; +1 or -1
enemy_dir_y:  .res NUM_ENEMIES   ; +1 or -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

    jsr load_palette
    jsr draw_arena
    jsr set_attributes
    jsr init_enemies

    lda #PLAYER_START_X
    sta player_x
    lda #PLAYER_START_Y
    sta player_y

    ; Player sprite
    lda player_y
    sta oam_buffer+0
    lda #SPRITE_PLAYER
    sta oam_buffer+1
    lda #0
    sta oam_buffer+2
    lda player_x
    sta oam_buffer+3

    jsr update_enemy_sprites

    ; Hide remaining sprites
    lda #$FF
    ldx #20
@hide_sprites:
    sta oam_buffer, x
    inx
    bne @hide_sprites

    lda #0
    sta PPUSCROLL
    sta PPUSCROLL
    sta frame_count

    lda #%10000000
    sta PPUCTRL
    lda #%00011110
    sta PPUMASK

main_loop:
    jsr read_controller
    jsr move_player
    jsr move_enemies
    jsr update_enemy_sprites
    jmp main_loop

; -----------------------------------------------------------------------------
; Initialise Enemies with positions and directions
; -----------------------------------------------------------------------------
init_enemies:
    ; Enemy 0: Top-left, moving right and down
    lda #48
    sta enemy_x+0
    lda #48
    sta enemy_y+0
    lda #DIR_RIGHT
    sta enemy_dir_x+0
    lda #DIR_DOWN
    sta enemy_dir_y+0

    ; Enemy 1: Top-right, moving left and down
    lda #200
    sta enemy_x+1
    lda #48
    sta enemy_y+1
    lda #DIR_LEFT
    sta enemy_dir_x+1
    lda #DIR_DOWN
    sta enemy_dir_y+1

    ; Enemy 2: Bottom-left, moving right and up
    lda #48
    sta enemy_x+2
    lda #176
    sta enemy_y+2
    lda #DIR_RIGHT
    sta enemy_dir_x+2
    lda #DIR_UP
    sta enemy_dir_y+2

    ; Enemy 3: Bottom-right, moving left and up
    lda #200
    sta enemy_x+3
    lda #176
    sta enemy_y+3
    lda #DIR_LEFT
    sta enemy_dir_x+3
    lda #DIR_UP
    sta enemy_dir_y+3

    rts

; -----------------------------------------------------------------------------
; Move Enemies - Bounce off arena walls
; -----------------------------------------------------------------------------
move_enemies:
    ldx #0

@enemy_loop:
    ; Move X
    lda enemy_x, x
    clc
    adc enemy_dir_x, x
    sta enemy_x, x

    ; Check X bounds
    cmp #ARENA_LEFT
    bcs @check_right
    ; Hit left wall - reverse X direction
    lda #DIR_RIGHT
    sta enemy_dir_x, x
    lda #ARENA_LEFT
    sta enemy_x, x
    jmp @move_y

@check_right:
    cmp #ARENA_RIGHT
    bcc @move_y
    ; Hit right wall - reverse X direction
    lda #DIR_LEFT
    sta enemy_dir_x, x
    lda #ARENA_RIGHT
    sec
    sbc #1
    sta enemy_x, x

@move_y:
    ; Move Y
    lda enemy_y, x
    clc
    adc enemy_dir_y, x
    sta enemy_y, x

    ; Check Y bounds
    cmp #ARENA_TOP
    bcs @check_bottom
    ; Hit top wall - reverse Y direction
    lda #DIR_DOWN
    sta enemy_dir_y, x
    lda #ARENA_TOP
    sta enemy_y, x
    jmp @next_enemy

@check_bottom:
    cmp #ARENA_BOTTOM
    bcc @next_enemy
    ; Hit bottom wall - reverse Y direction
    lda #DIR_UP
    sta enemy_dir_y, x
    lda #ARENA_BOTTOM
    sec
    sbc #1
    sta enemy_y, x

@next_enemy:
    inx
    cpx #NUM_ENEMIES
    bne @enemy_loop

    rts

; -----------------------------------------------------------------------------
; Update Enemy Sprites in OAM
; -----------------------------------------------------------------------------
update_enemy_sprites:
    ldx #0
    ldy #4

@loop:
    lda enemy_y, x
    sta oam_buffer, y
    iny

    lda #SPRITE_ENEMY
    sta oam_buffer, y
    iny

    lda #%00000001
    sta oam_buffer, y
    iny

    lda enemy_x, x
    sta oam_buffer, y
    iny

    inx
    cpx #NUM_ENEMIES
    bne @loop

    rts

; -----------------------------------------------------------------------------
; 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:
    bit PPUSTATUS
    lda #$20
    sta PPUADDR
    lda #$00
    sta PPUADDR

    lda #0
    sta row_counter

@draw_row:
    lda row_counter

    cmp #0
    beq @top_row
    cmp #1
    beq @top_row

    cmp #28
    beq @bottom_row
    cmp #29
    beq @bottom_row

    jmp @middle_row

@top_row:
    lda row_counter
    cmp #0
    bne @top_row_inner

    lda #TILE_CORNER_TL
    sta PPUDATA
    lda #TILE_BORDER
    ldx #30
@top_fill:
    sta PPUDATA
    dex
    bne @top_fill
    lda #TILE_CORNER_TR
    sta PPUDATA
    jmp @next_row

@top_row_inner:
    lda #TILE_BORDER
    ldx #32
@top_inner_fill:
    sta PPUDATA
    dex
    bne @top_inner_fill
    jmp @next_row

@bottom_row:
    lda row_counter
    cmp #29
    bne @bottom_row_inner

    lda #TILE_CORNER_BL
    sta PPUDATA
    lda #TILE_BORDER
    ldx #30
@bottom_fill:
    sta PPUDATA
    dex
    bne @bottom_fill
    lda #TILE_CORNER_BR
    sta PPUDATA
    jmp @next_row

@bottom_row_inner:
    lda #TILE_BORDER
    ldx #32
@bottom_inner_fill:
    sta PPUDATA
    dex
    bne @bottom_inner_fill
    jmp @next_row

@middle_row:
    lda #TILE_BORDER
    sta PPUDATA
    sta PPUDATA

    lda #TILE_FLOOR
    ldx #28
@floor_fill:
    sta PPUDATA
    dex
    bne @floor_fill

    lda #TILE_BORDER
    sta PPUDATA
    sta PPUDATA

@next_row:
    inc row_counter
    lda row_counter
    cmp #30
    beq @done_drawing
    jmp @draw_row

@done_drawing:
    rts

; -----------------------------------------------------------------------------
; Set Attribute Table
; -----------------------------------------------------------------------------
set_attributes:
    bit PPUSTATUS
    lda #$23
    sta PPUADDR
    lda #$C0
    sta PPUADDR

    ldx #8
    lda #$00
@attr_top:
    sta PPUDATA
    dex
    bne @attr_top

    ldx #6
@attr_floor_rows:
    lda #$00
    sta PPUDATA
    lda #%01010101
    sta PPUDATA
    sta PPUDATA
    sta PPUDATA
    sta PPUDATA
    sta PPUDATA
    sta PPUDATA
    lda #$00
    sta PPUDATA
    dex
    bne @attr_floor_rows

    ldx #8
    lda #$00
@attr_bottom:
    sta PPUDATA
    dex
    bne @attr_bottom

    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:
    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

    lda #0
    sta OAMADDR
    lda #>oam_buffer
    sta OAMDMA

    lda player_y
    sta oam_buffer+0
    lda player_x
    sta oam_buffer+3

    inc frame_count

    lda #0
    sta PPUSCROLL
    sta PPUSCROLL

    pla
    tay
    pla
    tax
    pla
    rti

irq:
    rti

; -----------------------------------------------------------------------------
; Data
; -----------------------------------------------------------------------------
palette_data:
    .byte BG_COLOUR, $11, $21, $31
    .byte BG_COLOUR, $13, $23, $33
    .byte BG_COLOUR, $19, $29, $39
    .byte BG_COLOUR, $16, $26, $36
    .byte BG_COLOUR, $30, $27, $17
    .byte BG_COLOUR, $16, $26, $36
    .byte BG_COLOUR, $30, $27, $17
    .byte BG_COLOUR, $30, $27, $17

; -----------------------------------------------------------------------------
; Vectors
; -----------------------------------------------------------------------------
.segment "VECTORS"
    .word nmi
    .word reset
    .word irq

; -----------------------------------------------------------------------------
; CHR-ROM
; -----------------------------------------------------------------------------
.segment "CHARS"

; Tile 0: Empty
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00

; Tile 1: Border
.byte %11111111,%10000001,%10000001,%11111111
.byte %11111111,%00010001,%00010001,%11111111
.byte %00000000,%01111110,%01111110,%00000000
.byte %00000000,%11101110,%11101110,%00000000

; Tile 2: Floor
.byte %00000000,%00000000,%00000000,%00000000
.byte %00000000,%00000000,%00000000,%10000001
.byte %00000000,%00000000,%00000000,%00000000
.byte %00000000,%00000000,%00000000,%00000000

; Tile 3: Corner TL
.byte %11111111,%11000000,%10100000,%10010000
.byte %10001000,%10000100,%10000010,%10000001
.byte %00000000,%00111111,%01011111,%01101111
.byte %01110111,%01111011,%01111101,%01111110

; Tile 4: Corner TR
.byte %11111111,%00000011,%00000101,%00001001
.byte %00010001,%00100001,%01000001,%10000001
.byte %00000000,%11111100,%11111010,%11110110
.byte %11101110,%11011110,%10111110,%01111110

; Tile 5: Corner BL
.byte %10000001,%10000010,%10000100,%10001000
.byte %10010000,%10100000,%11000000,%11111111
.byte %01111110,%01111101,%01111011,%01110111
.byte %01101111,%01011111,%00111111,%00000000

; Tile 6: Corner BR
.byte %10000001,%01000001,%00100001,%00010001
.byte %00001001,%00000101,%00000011,%11111111
.byte %01111110,%10111110,%11011110,%11101110
.byte %11110110,%11111010,%11111100,%00000000

; Tile 7: Player
.byte %00011000,%00011000,%00111100,%01111110
.byte %11111111,%10111101,%00100100,%00100100
.byte %00000000,%00011000,%00011000,%00111100
.byte %01000010,%01000010,%00011000,%00000000

; Tile 8: Enemy
.byte %00011000,%00111100,%01111110,%11111111
.byte %11111111,%01111110,%00111100,%00011000
.byte %00000000,%00011000,%00100100,%01000010
.byte %01000010,%00100100,%00011000,%00000000

; Fill rest
.res 8192 - 144, $00

Build It

ca65 nexus.asm -o nexus.o
ld65 -C nes.cfg nexus.o -o nexus.nes

Enemies now patrol endlessly. The arena feels dangerous.

Next

Movement creates threat, but there’s no consequence for touching enemies yet. Unit 9 adds collision detection.

What Changed

Unit 7 → Unit 8
+166-172
11 ; =============================================================================
2-; NEON NEXUS - Unit 7: Enemies Appear
2+; NEON NEXUS - Unit 8: Enemy Movement
33 ; =============================================================================
4-; Add enemy sprites to the arena.
4+; Make enemies patrol the arena with bounce patterns.
55 ; =============================================================================
66
77 ; -----------------------------------------------------------------------------
...
3535 PLAYER_START_X = 124
3636 PLAYER_START_Y = 116
3737 PLAYER_SPEED = 2
38+ENEMY_SPEED = 1
3839
3940 NUM_ENEMIES = 4
4041
41-; Tile indices (background)
42+; Tile indices
4243 TILE_EMPTY = 0
4344 TILE_BORDER = 1
4445 TILE_FLOOR = 2
...
4748 TILE_CORNER_BL = 5
4849 TILE_CORNER_BR = 6
4950
50-; Sprite tiles
5151 SPRITE_PLAYER = 7
5252 SPRITE_ENEMY = 8
5353
...
5656 ARENA_RIGHT = 232
5757 ARENA_TOP = 16
5858 ARENA_BOTTOM = 208
59+
60+; Direction flags
61+DIR_RIGHT = 1
62+DIR_LEFT = $FF ; -1 in two's complement
63+DIR_DOWN = 1
64+DIR_UP = $FF
5965
6066 BG_COLOUR = $0F
6167
...
6369 ; Memory Layout
6470 ; -----------------------------------------------------------------------------
6571 .segment "ZEROPAGE"
66-player_x: .res 1
67-player_y: .res 1
68-buttons: .res 1
69-temp: .res 1
70-row_counter: .res 1
72+player_x: .res 1
73+player_y: .res 1
74+buttons: .res 1
75+temp: .res 1
76+row_counter: .res 1
77+frame_count: .res 1
7178
72-; Enemy positions (4 enemies)
73-enemy_x: .res NUM_ENEMIES
74-enemy_y: .res NUM_ENEMIES
79+; Enemy data
80+enemy_x: .res NUM_ENEMIES
81+enemy_y: .res NUM_ENEMIES
82+enemy_dir_x: .res NUM_ENEMIES ; +1 or -1
83+enemy_dir_y: .res NUM_ENEMIES ; +1 or -1
7584
7685 .segment "OAM"
77-oam_buffer: .res 256
86+oam_buffer: .res 256
7887
7988 .segment "BSS"
8089
...
132141 jsr set_attributes
133142 jsr init_enemies
134143
135- ; Set up player
136144 lda #PLAYER_START_X
137145 sta player_x
138146 lda #PLAYER_START_Y
139147 sta player_y
140148
141- ; Initialise player sprite (OAM slot 0)
149+ ; Player sprite
142150 lda player_y
143151 sta oam_buffer+0
144152 lda #SPRITE_PLAYER
145153 sta oam_buffer+1
146- lda #0 ; Attributes: palette 0, no flip
154+ lda #0
147155 sta oam_buffer+2
148156 lda player_x
149157 sta oam_buffer+3
150158
151- ; Set up enemy sprites in OAM
152159 jsr update_enemy_sprites
153160
154- ; Hide remaining sprites (after player + 4 enemies = 5 sprites used)
161+ ; Hide remaining sprites
155162 lda #$FF
156- ldx #20 ; Start after 5 sprites (5*4=20)
163+ ldx #20
157164 @hide_sprites:
158165 sta oam_buffer, x
159166 inx
...
162169 lda #0
163170 sta PPUSCROLL
164171 sta PPUSCROLL
172+ sta frame_count
165173
166174 lda #%10000000
167175 sta PPUCTRL
...
171179 main_loop:
172180 jsr read_controller
173181 jsr move_player
182+ jsr move_enemies
183+ jsr update_enemy_sprites
174184 jmp main_loop
175185
176186 ; -----------------------------------------------------------------------------
177-; Initialise Enemies - Place 4 enemies at corners of play area
187+; Initialise Enemies with positions and directions
178188 ; -----------------------------------------------------------------------------
179189 init_enemies:
180- ; Enemy 0: Top-left area
190+ ; Enemy 0: Top-left, moving right and down
181191 lda #48
182192 sta enemy_x+0
183193 lda #48
184194 sta enemy_y+0
195+ lda #DIR_RIGHT
196+ sta enemy_dir_x+0
197+ lda #DIR_DOWN
198+ sta enemy_dir_y+0
185199
186- ; Enemy 1: Top-right area
200+ ; Enemy 1: Top-right, moving left and down
187201 lda #200
188202 sta enemy_x+1
189203 lda #48
190204 sta enemy_y+1
205+ lda #DIR_LEFT
206+ sta enemy_dir_x+1
207+ lda #DIR_DOWN
208+ sta enemy_dir_y+1
191209
192- ; Enemy 2: Bottom-left area
210+ ; Enemy 2: Bottom-left, moving right and up
193211 lda #48
194212 sta enemy_x+2
195213 lda #176
196214 sta enemy_y+2
215+ lda #DIR_RIGHT
216+ sta enemy_dir_x+2
217+ lda #DIR_UP
218+ sta enemy_dir_y+2
197219
198- ; Enemy 3: Bottom-right area
220+ ; Enemy 3: Bottom-right, moving left and up
199221 lda #200
200222 sta enemy_x+3
201223 lda #176
202224 sta enemy_y+3
225+ lda #DIR_LEFT
226+ sta enemy_dir_x+3
227+ lda #DIR_UP
228+ sta enemy_dir_y+3
229+
230+ rts
231+
232+; -----------------------------------------------------------------------------
233+; Move Enemies - Bounce off arena walls
234+; -----------------------------------------------------------------------------
235+move_enemies:
236+ ldx #0
237+
238+@enemy_loop:
239+ ; Move X
240+ lda enemy_x, x
241+ clc
242+ adc enemy_dir_x, x
243+ sta enemy_x, x
244+
245+ ; Check X bounds
246+ cmp #ARENA_LEFT
247+ bcs @check_right
248+ ; Hit left wall - reverse X direction
249+ lda #DIR_RIGHT
250+ sta enemy_dir_x, x
251+ lda #ARENA_LEFT
252+ sta enemy_x, x
253+ jmp @move_y
254+
255+@check_right:
256+ cmp #ARENA_RIGHT
257+ bcc @move_y
258+ ; Hit right wall - reverse X direction
259+ lda #DIR_LEFT
260+ sta enemy_dir_x, x
261+ lda #ARENA_RIGHT
262+ sec
263+ sbc #1
264+ sta enemy_x, x
265+
266+@move_y:
267+ ; Move Y
268+ lda enemy_y, x
269+ clc
270+ adc enemy_dir_y, x
271+ sta enemy_y, x
272+
273+ ; Check Y bounds
274+ cmp #ARENA_TOP
275+ bcs @check_bottom
276+ ; Hit top wall - reverse Y direction
277+ lda #DIR_DOWN
278+ sta enemy_dir_y, x
279+ lda #ARENA_TOP
280+ sta enemy_y, x
281+ jmp @next_enemy
282+
283+@check_bottom:
284+ cmp #ARENA_BOTTOM
285+ bcc @next_enemy
286+ ; Hit bottom wall - reverse Y direction
287+ lda #DIR_UP
288+ sta enemy_dir_y, x
289+ lda #ARENA_BOTTOM
290+ sec
291+ sbc #1
292+ sta enemy_y, x
293+
294+@next_enemy:
295+ inx
296+ cpx #NUM_ENEMIES
297+ bne @enemy_loop
203298
204299 rts
205300
...
207302 ; Update Enemy Sprites in OAM
208303 ; -----------------------------------------------------------------------------
209304 update_enemy_sprites:
210- ldx #0 ; Enemy index
211- ldy #4 ; OAM offset (after player sprite)
305+ ldx #0
306+ ldy #4
212307
213308 @loop:
214- ; Y position
215309 lda enemy_y, x
216310 sta oam_buffer, y
217311 iny
218312
219- ; Tile
220313 lda #SPRITE_ENEMY
221314 sta oam_buffer, y
222315 iny
223316
224- ; Attributes: palette 1 (different colour from player)
225317 lda #%00000001
226318 sta oam_buffer, y
227319 iny
228320
229- ; X position
230321 lda enemy_x, x
231322 sta oam_buffer, y
232323 iny
...
485576 lda #>oam_buffer
486577 sta OAMDMA
487578
488- ; Update player sprite position
489579 lda player_y
490580 sta oam_buffer+0
491581 lda player_x
492582 sta oam_buffer+3
583+
584+ inc frame_count
493585
494586 lda #0
495587 sta PPUSCROLL
...
509601 ; Data
510602 ; -----------------------------------------------------------------------------
511603 palette_data:
512- ; Background palettes
513- .byte BG_COLOUR, $11, $21, $31 ; Palette 0: Blue
514- .byte BG_COLOUR, $13, $23, $33 ; Palette 1: Purple
515- .byte BG_COLOUR, $19, $29, $39 ; Palette 2: Green
516- .byte BG_COLOUR, $16, $26, $36 ; Palette 3: Red
517- ; Sprite palettes
518- .byte BG_COLOUR, $30, $27, $17 ; Palette 0: Player (white/orange)
519- .byte BG_COLOUR, $16, $26, $36 ; Palette 1: Enemy (red)
604+ .byte BG_COLOUR, $11, $21, $31
605+ .byte BG_COLOUR, $13, $23, $33
606+ .byte BG_COLOUR, $19, $29, $39
607+ .byte BG_COLOUR, $16, $26, $36
608+ .byte BG_COLOUR, $30, $27, $17
609+ .byte BG_COLOUR, $16, $26, $36
520610 .byte BG_COLOUR, $30, $27, $17
521611 .byte BG_COLOUR, $30, $27, $17
522612
...
537627 .byte $00,$00,$00,$00,$00,$00,$00,$00
538628 .byte $00,$00,$00,$00,$00,$00,$00,$00
539629
540-; Tile 1: Border (brick pattern)
541-.byte %11111111
542-.byte %10000001
543-.byte %10000001
544-.byte %11111111
545-.byte %11111111
546-.byte %00010001
547-.byte %00010001
548-.byte %11111111
549-.byte %00000000
550-.byte %01111110
551-.byte %01111110
552-.byte %00000000
553-.byte %00000000
554-.byte %11101110
555-.byte %11101110
556-.byte %00000000
630+; Tile 1: Border
631+.byte %11111111,%10000001,%10000001,%11111111
632+.byte %11111111,%00010001,%00010001,%11111111
633+.byte %00000000,%01111110,%01111110,%00000000
634+.byte %00000000,%11101110,%11101110,%00000000
557635
558-; Tile 2: Floor (subtle grid)
559-.byte %00000000
560-.byte %00000000
561-.byte %00000000
562-.byte %00000000
563-.byte %00000000
564-.byte %00000000
565-.byte %00000000
566-.byte %10000001
567-.byte %00000000
568-.byte %00000000
569-.byte %00000000
570-.byte %00000000
571-.byte %00000000
572-.byte %00000000
573-.byte %00000000
574-.byte %00000000
636+; Tile 2: Floor
637+.byte %00000000,%00000000,%00000000,%00000000
638+.byte %00000000,%00000000,%00000000,%10000001
639+.byte %00000000,%00000000,%00000000,%00000000
640+.byte %00000000,%00000000,%00000000,%00000000
575641
576642 ; Tile 3: Corner TL
577-.byte %11111111
578-.byte %11000000
579-.byte %10100000
580-.byte %10010000
581-.byte %10001000
582-.byte %10000100
583-.byte %10000010
584-.byte %10000001
585-.byte %00000000
586-.byte %00111111
587-.byte %01011111
588-.byte %01101111
589-.byte %01110111
590-.byte %01111011
591-.byte %01111101
592-.byte %01111110
643+.byte %11111111,%11000000,%10100000,%10010000
644+.byte %10001000,%10000100,%10000010,%10000001
645+.byte %00000000,%00111111,%01011111,%01101111
646+.byte %01110111,%01111011,%01111101,%01111110
593647
594648 ; Tile 4: Corner TR
595-.byte %11111111
596-.byte %00000011
597-.byte %00000101
598-.byte %00001001
599-.byte %00010001
600-.byte %00100001
601-.byte %01000001
602-.byte %10000001
603-.byte %00000000
604-.byte %11111100
605-.byte %11111010
606-.byte %11110110
607-.byte %11101110
608-.byte %11011110
609-.byte %10111110
610-.byte %01111110
649+.byte %11111111,%00000011,%00000101,%00001001
650+.byte %00010001,%00100001,%01000001,%10000001
651+.byte %00000000,%11111100,%11111010,%11110110
652+.byte %11101110,%11011110,%10111110,%01111110
611653
612654 ; Tile 5: Corner BL
613-.byte %10000001
614-.byte %10000010
615-.byte %10000100
616-.byte %10001000
617-.byte %10010000
618-.byte %10100000
619-.byte %11000000
620-.byte %11111111
621-.byte %01111110
622-.byte %01111101
623-.byte %01111011
624-.byte %01110111
625-.byte %01101111
626-.byte %01011111
627-.byte %00111111
628-.byte %00000000
655+.byte %10000001,%10000010,%10000100,%10001000
656+.byte %10010000,%10100000,%11000000,%11111111
657+.byte %01111110,%01111101,%01111011,%01110111
658+.byte %01101111,%01011111,%00111111,%00000000
629659
630660 ; Tile 6: Corner BR
631-.byte %10000001
632-.byte %01000001
633-.byte %00100001
634-.byte %00010001
635-.byte %00001001
636-.byte %00000101
637-.byte %00000011
638-.byte %11111111
639-.byte %01111110
640-.byte %10111110
641-.byte %11011110
642-.byte %11101110
643-.byte %11110110
644-.byte %11111010
645-.byte %11111100
646-.byte %00000000
661+.byte %10000001,%01000001,%00100001,%00010001
662+.byte %00001001,%00000101,%00000011,%11111111
663+.byte %01111110,%10111110,%11011110,%11101110
664+.byte %11110110,%11111010,%11111100,%00000000
647665
648-; Tile 7: Player sprite
649-.byte %00011000
650-.byte %00011000
651-.byte %00111100
652-.byte %01111110
653-.byte %11111111
654-.byte %10111101
655-.byte %00100100
656-.byte %00100100
657-.byte %00000000
658-.byte %00011000
659-.byte %00011000
660-.byte %00111100
661-.byte %01000010
662-.byte %01000010
663-.byte %00011000
664-.byte %00000000
666+; Tile 7: Player
667+.byte %00011000,%00011000,%00111100,%01111110
668+.byte %11111111,%10111101,%00100100,%00100100
669+.byte %00000000,%00011000,%00011000,%00111100
670+.byte %01000010,%01000010,%00011000,%00000000
665671
666-; Tile 8: Enemy sprite (hostile diamond shape)
667-.byte %00011000
668-.byte %00111100
669-.byte %01111110
670-.byte %11111111
671-.byte %11111111
672-.byte %01111110
673-.byte %00111100
674-.byte %00011000
675-.byte %00000000
676-.byte %00011000
677-.byte %00100100
678-.byte %01000010
679-.byte %01000010
680-.byte %00100100
681-.byte %00011000
682-.byte %00000000
672+; Tile 8: Enemy
673+.byte %00011000,%00111100,%01111110,%11111111
674+.byte %11111111,%01111110,%00111100,%00011000
675+.byte %00000000,%00011000,%00100100,%01000010
676+.byte %01000010,%00100100,%00011000,%00000000
683677
684-; Fill rest of CHR-ROM
678+; Fill rest
685679 .res 8192 - 144, $00
686680