RC2025 – Part 7 – Moving a pixel with a NES controller

A pixel being shown on an RC2014 computer

In previous posts, I’ve covered how to read an NES controller from an RC2014 computer, and also how to use an 8×8 LED matrix. I now want to combine these and use the NES controller to move a pixel on the LED matrix.

The first thing we want to do is to draw a pixel in the top left corner of the LED matrix. To do this, I’ll use register b for the Row data and register c for the Column data. We need to set a single bit in each because we only want a single pixel lit.

        ORG $9000

ROW     EQU 0
COLUMN  EQU 2

setup:
        ld b, 0b00000001  ; Row bit for the pixel
        ld c, 0b10000000  ; Column bit for the pixel

main:

.draw_pixel:
        xor a             ; clear a
        out (COLUMN), a   ; clear the columns
        ld a, b           ; get the current row bit
        out (ROW), a      ; select the row
        ld a, c           ; get the column data for this row
        out (COLUMN), a   ; output the column data

        jr main           ; repeat forever

        END

Now that we have a pixel, we need to read the NES controller and react to movement on the joypad.

We can use Z80 rlc and rrc operations to move the single bit to the left or right in the Row and Column registers. If we reach the end of the byte, the bit will roll over to the other end of the byte.

To test which button is being pressed, we can use a bit operation. If the button isn’t being pressed, we can jump ahead to test the next button.

In our main loop, we can add the following.

UP_BTN   EQU $04
DOWN_BTN EQU $05
LEFT_BTN EQU $06
RIGHT_BTN EQU $07

main:
        call get_buttons   ; Get NES controller button states in A

.test_left:
        bit LEFT_BTN, a
        jr nz, .test_right
        rlc c
.test_right:
        bit RIGHT_BTN, a
        jr nz, .test_up
        rrc c
.test_up:
        bit UP_BTN, a
        jr nz, .test_down
        rrc b
.test_down:
        bit DOWN_BTN, a
        jr nz, .draw_pixel
        rlc b

.draw_pixel:

We also need to include the get_buttons routine we previously wrote. This returns the buttons being pressed on the NES controller in register a.

        INCLUDE "NESController.inc"

        END

At the moment, this is executing far too fast, so we end up with an entire row or column being lit. We need to add a delay. We can write a very simple Z80 routine to just loop X times as a simple delay.

delay:
        ld de, 50000
.delay_loop:
        dec de
        ld a, d
        or e
        jr nz, .delay_loop
        ret

The final working code

Putting this all together, we end up with the following code…


; A simple program to move a pixel on an 8x8 LED matrix
; connected to the RC2014 Z80 computer.
; Robert Price - 19th October 2025

        ORG $9000

ROW     EQU 0
COLUMN  EQU 2

UP_BTN   EQU $04
DOWN_BTN EQU $05
LEFT_BTN EQU $06
RIGHT_BTN EQU $07

setup:
        ld b, 0b00000001  ; Row bit for the pixel
        ld c, 0b10000000  ; Column bit for the pixel

main:
        call get_buttons   ; Get NES controller button states in A

.test_left:
        bit LEFT_BTN, a
        jr nz, .test_right
        rlc c
.test_right:
        bit RIGHT_BTN, a
        jr nz, .test_up
        rrc c
.test_up:
        bit UP_BTN, a
        jr nz, .test_down
        rrc b
.test_down:
        bit DOWN_BTN, a
        jr nz, .draw_pixel
        rlc b

.draw_pixel:
        xor a             ; clear a
        out (COLUMN), a   ; clear the columns
        ld a, b           ; get the current row bit
        out (ROW), a      ; select the row
        ld a, c           ; get the column data for this row
        out (COLUMN), a   ; output the column data

        call delay        ; small delay to make movement visible

        jr main           ; repeat forever

delay:
        ld de, 50000
.delay_loop:
        dec de
        ld a, d
        or e
        jr nz, .delay_loop
        ret

        INCLUDE "NESController.inc"

        END

Here is a video of the code in action.