Two programs. The first writes nothing to the VIA — it just increments a byte in RAM, forever, as a sanity check that the new address decode works and that RAM is still at 3FFF after rewiring CE# to A14. The second writes to the VIA for the first time and lights a LED.
counter.s
.org $8000
reset:
lda #$00
sta $0000 ; initialise RAM[0] to 0
loop:
inc $0000 ; increment RAM[0]
jmp loop ; repeat
.org $fffc
.word reset
.word resetWalking through it line by line.
.org $8000 tells the assembler that the code which follows lives
at address $8000 in the CPU's address space. The binary it produces
will be padded so that the bytes assemble at the right offsets. When
the EEPROM is placed at $8000–$FFFF in the circuit, the CPU fetches
instructions from the correct addresses.
lda #$00 loads the literal value zero into A. The # means
immediate — the zero is right there in the instruction, not fetched
from memory.
sta $0000 stores A (currently zero) into the byte at address
$0000. This is RAM, now that A14 is the chip-select and $0000 has
A14=0. The instruction initialises the counter to zero on startup.
inc $0000 is the heart of the loop. It reads the byte at $0000,
adds 1 to it, and writes the result back. Five cycles total: two
to fetch the opcode and the address byte, one to read the old value,
one to increment, one to write the new value. On a 1 MHz clock,
the loop body executes around 125,000 times per second.
jmp loop sets PC back to the loop label unconditionally. The
CPU never stops incrementing.
The .org $fffc / .word reset / .word reset block is the reset
vector. When the 6502 is powered on or reset, it reads the two bytes
at $FFFC–$FFFD, interprets them as a 16-bit address (low byte first),
and jumps there. Both the reset vector ($FFFC) and the IRQ/BRK vector
($FFFE) are set to reset — the program does not use interrupts, so
the IRQ vector pointing at reset is harmless and keeps the code
short.
Assemble and burn
vasm6502_oldstyle -Fbin -dotdir counter.s -o counter.bin
minipro -p AT28C256 -w counter.bin-Fbin produces a flat binary — no headers, just raw bytes starting
at $8000 offset into the 32KB file. -dotdir enables the dot-prefix
directives (.org, .word).
Running counter.s
Power the board. The address-line LEDs will change more rapidly than
they did with the bare jmp reset test program from f03b, because
inc $0000 forces a RAM access on every cycle rather than just
fetching a three-byte jump instruction. The pattern on the LEDs
advances faster.
At 1 MHz the counter wraps through 0–255 so quickly you cannot read it directly from address LEDs. A logic probe or a slow clock (a manual clock button works well) lets you step through one increment at a time and watch A0 toggle on every other write cycle. The important thing is that the CPU is not stuck and RAM is responding — if it were not, the address lines would show a different pattern, or the CPU would crash when it tried to read back the incremented value.
led.s
Now use the VIA. The program configures all eight Port B pins as outputs and drives PB0 high, which lights a LED.
PORTB = $4000
DDRB = $4002
.org $8000
reset:
lda #$ff
sta DDRB ; all PORTB pins = output
lda #$01
sta PORTB ; PB0 high — LED on
loop:
jmp loop
.org $fffc
.word reset
.word resetPORTB = $4000 and DDRB = $4002 are assembler constants, not
instructions. They give human-readable names to the VIA register
addresses. PORTB ($4000) is the Port B data register — write a byte
there and the corresponding VIA pins take on those logic levels.
DDRB ($4002) is the Data Direction Register for Port B — each bit
controls whether the matching pin is an output (1) or an input (0).
lda #$ff loads $FF (all eight bits set) into A.
sta DDRB writes $FF to the DDRB register at $4002. With every bit
set to 1, every Port B pin is configured as an output. The VIA now
drives all eight PB0–PB7 lines.
lda #$01 loads $01 into A. In binary: %00000001 — only bit 0 set.
sta PORTB writes $01 to the PORTB register at $4000. The VIA
drives PB0 high (bit 0 = 1) and the remaining seven pins low (bits
1–7 = 0). A LED connected to PB0 sees a voltage difference across
it and lights.
jmp loop loops forever. Nothing else needs to happen — the VIA
holds the output level until the CPU writes a new value or the power
is removed.
Wiring the LED
Connect a 1kΩ resistor from PB0 to the anode (longer leg) of a standard LED. Connect the cathode (shorter leg) to the ground rail. The resistor limits current to a safe level for both the LED and the VIA output driver.
Assemble, burn, verify
vasm6502_oldstyle -Fbin -dotdir led.s -o led.bin
minipro -p AT28C256 -w led.binPower the board. The LED should light immediately — there is no
startup delay, no loop condition to meet. The CPU writes $FF to DDRB
and $01 to PORTB in the first few instructions, then parks itself
in jmp loop. If the LED does not light, check the DDRB and PORTB
wiring (RS0–RS3 on A0–A3, CS1 on A14, CS2B on A15) before assuming
a code problem.