thecodingidiot.com

Writing 6502 AssemblyButton Input

Button Input

The LED program writes output. This one reads input. A button connected to PB7 controls the LED on PB0: press the button, the LED lights; release it, the LED goes off.

Reading an input pin

When a VIA Port B pin is configured as an input (DDRB bit = 0), the chip activates an internal pull-up resistor on that pin. The pin is held near +5V by default. If you connect a wire from that pin to ground through a pushbutton, pressing the button pulls the pin to 0V. Releasing it lets the pull-up restore it to a high level.

This is called active-low logic. The button is "active" — doing its thing — when the pin is low, not high. Reading $00 on the pin means the button is pressed; reading $01 means it is not. The program has to test for the low condition, not the high one.

button.s

PORTB = $4000
DDRB  = $4002
 
  .org $8000
 
reset:
  lda #$01
  sta DDRB        ; PB0 = output, PB7 = input (internal pull-up)
 
loop:
  lda PORTB
  and #$80        ; isolate PB7
  bne off         ; PB7 high = button not pressed
  lda #$01
  sta PORTB       ; button pressed (PB7 low) → LED on
  jmp loop
 
off:
  lda #$00
  sta PORTB       ; LED off
  jmp loop
 
  .org $fffc
  .word reset
  .word reset

lda #$01 loads $01 (%00000001) into A. sta DDRB writes it to the Data Direction Register at $4002. Bit 0 = 1 means PB0 is an output. Bit 7 = 0 means PB7 is an input. Every other bit is also 0, leaving those pins as inputs, but only PB0 and PB7 are connected to anything in this circuit.

lda PORTB reads the full eight-bit state of Port B into A. This includes all eight pins at once. PB0's current logic level is in bit 0, PB7's current logic level is in bit 7.

and #$80 ANDs A with $80 (binary %10000000). Every bit except bit 7 is forced to zero. The result in A is either $80 (if PB7 was high) or $00 (if PB7 was low). This isolates the single bit that tells us about the button, discarding everything else.

bne off tests the Z flag. If the result of the AND is non-zero — meaning $80, meaning PB7 is high, meaning the button is not pressed — Z is clear and the branch is taken to off. If the result is zero — PB7 is low, button is pressed — Z is set and the branch is not taken, so execution falls through to the next instruction.

lda #$01 / sta PORTB: the button is pressed. Load $01 and write it to PORTB ($4000). PB0 goes high, lighting the LED. PB1–PB7 go low. Note that writing to PORTB on an input-configured pin has no effect on the external signal — PB7 still reads whatever the button is doing regardless of what is written here.

jmp loop goes back to the top to read the button again.

At off: lda #$00 / sta PORTB writes all zeros to PORTB. PB0 goes low, extinguishing the LED. jmp loop returns to the top.

Wiring the button

Connect one leg of a pushbutton to the PB7 pin on the VIA. Connect the other leg to the ground rail. No external resistor is needed — the VIA's internal pull-up handles the idle-high state.

Assemble, burn, verify

vasm6502_oldstyle -Fbin -dotdir button.s -o button.bin
minipro -p AT28C256 -w button.bin

Power the board. The LED should be off. Press and hold the button — the LED lights. Release — the LED goes off. The loop runs fast enough that there is no perceptible lag between pressing the button and seeing the LED respond.

The read-modify-write cycle

The pattern used here — read the full port, AND with a mask, branch on the result — is the standard approach for testing any individual input pin on the 6502. You cannot read a single bit directly; the CPU always reads a full byte. The mask isolates the bit you care about, and the flags tell the branch instruction what to do next.

This same pattern scales up: to test two buttons on PB6 and PB7, you would read PORTB once, then AND with $40 to test PB6, then AND with $80 to test PB7, branching accordingly after each test. Reading the port once and masking repeatedly is more efficient than reading it again for every bit — and it means all the pin states are sampled at the same moment, which matters when inputs can change between reads.