Skip to content

Grid Movement with Bounds

Move an entity on a grid with boundary checking. Prevents moving outside the play area.

Taught in Game 1, Unit 1 movementboundsgridinput

Overview

Grid-based games need movement that respects boundaries. Check direction input, verify the new position is valid, then update. This pattern prevents the cursor or player from leaving the play area while keeping movement responsive.

Code

; =============================================================================
; GRID MOVEMENT WITH BOUNDS - ZX SPECTRUM
; Move cursor/entity with boundary checking
; Taught: Game 1 (Ink War), Unit 1
; CPU: ~80 cycles | Memory: ~60 bytes
; =============================================================================

BOARD_SIZE  equ     8               ; Grid dimensions (8x8)

; Move entity based on key_pressed value
; key_pressed: 1=up, 2=down, 3=left, 4=right
move_cursor:
        ld      a, (key_pressed)
        or      a
        ret     z               ; No key pressed

        call    clear_cursor    ; Remove old cursor visual

        ld      a, (key_pressed)

        ; === Check Up ===
        cp      1
        jr      nz, .not_up
        ld      a, (cursor_row)
        or      a               ; At row 0?
        jr      z, .done        ; Already at top - can't move
        dec     a
        ld      (cursor_row), a
        jr      .done

.not_up:
        ; === Check Down ===
        cp      2
        jr      nz, .not_down
        ld      a, (cursor_row)
        cp      BOARD_SIZE-1    ; At last row?
        jr      z, .done        ; Already at bottom - can't move
        inc     a
        ld      (cursor_row), a
        jr      .done

.not_down:
        ; === Check Left ===
        cp      3
        jr      nz, .not_left
        ld      a, (cursor_col)
        or      a               ; At column 0?
        jr      z, .done        ; Already at left - can't move
        dec     a
        ld      (cursor_col), a
        jr      .done

.not_left:
        ; === Check Right ===
        cp      4
        jr      nz, .done
        ld      a, (cursor_col)
        cp      BOARD_SIZE-1    ; At last column?
        jr      z, .done        ; Already at right - can't move
        inc     a
        ld      (cursor_col), a

.done:
        call    draw_cursor     ; Draw new cursor
        ret

; Placeholder routines
clear_cursor:   ret
draw_cursor:    ret

; Variables
cursor_row:     defb    0       ; Current row (0 to BOARD_SIZE-1)
cursor_col:     defb    0       ; Current column (0 to BOARD_SIZE-1)
key_pressed:    defb    0       ; Direction: 0=none, 1-4=direction

Trade-offs

AspectCost
CPU~80 cycles per move
Memory~60 bytes
LimitationOnly handles one direction per frame

When to use: Grid-based games (board games, puzzle games, tactical games).

When to avoid: Continuous pixel-based movement - use velocity and pixel coordinates instead.

How the Bounds Check Works

Each direction uses a different boundary condition:

DirectionCheckBoundary
Upor a (is zero?)Row 0
Downcp SIZE-1Row 7
Leftor a (is zero?)Column 0
Rightcp SIZE-1Column 7

The or a instruction sets the zero flag if A is 0, providing a quick “at minimum” check without needing a compare.

Extending to Variable Boundaries

For non-square or scrolling play areas:

MIN_ROW     equ     2           ; Top boundary
MAX_ROW     equ     21          ; Bottom boundary
MIN_COL     equ     1           ; Left boundary
MAX_COL     equ     30          ; Right boundary

; Up check becomes:
        ld      a, (cursor_row)
        cp      MIN_ROW         ; At minimum row?
        jr      z, .done

; Down check becomes:
        ld      a, (cursor_row)
        cp      MAX_ROW
        jr      z, .done

Patterns: Keyboard Reading, Game Loop (HALT)

Vault: ZX Spectrum