The Retro Desk returns with a third installment in its Commander X16 assembly tutorial series. This time, the topic is 6502 assembly branching—a core concept in building programs that do more than just run in a straight line. Using conditional instructions and subroutines, Matt explains how to give your programs intelligence and flexibility. The episode builds on the previous lessons and introduces flow control techniques that shift behavior based on data and logic.
If you’ve ever wondered how a simple “if” statement translates to 6502 instructions—or how loops actually work without a high-level language—this episode breaks it all down in clear, practical steps. You’ll see 6502 assembly branching in real programs, and even write subroutines that behave like C functions. By the end, you’ll understand how to make smarter, tighter code using only a few bytes.
1. Understanding Flow Control in Assembly
Flow control in 6502 assembly means altering a program’s behavior based on data. Unlike C, where “if”, “while”, and “for” make things obvious, assembly uses direct branching instructions like BEQ
, BNE
, BMI
, and BRA
. These respond to specific bits in the processor’s status register. A simple “if/else” becomes a careful dance of compares and jumps.
Matt compares equivalent C and assembly code side by side, showing how status flags like the zero and negative bits determine the outcome. You’ll see that 6502 assembly branching often mirrors the exact instructions a C compiler would use—but you’re in the driver’s seat.
2. Relative Addressing Gotchas
Branching with relative addresses comes with limits. The 65C02 uses signed 8-bit offsets, meaning jumps must stay within -128 to +127 bytes. Exceed that range, and your code won’t assemble. The video explores ways to fix this using absolute jumps (JMP
) or inverted logic to reach farther labels—an important trick when your project outgrows a small loop.
3. Status Register Bits That Matter
The 6502’s status register has eight bits, but only a few affect branches: negative (N
), overflow (V
), zero (Z
), and carry (C
). Each has its own branch instruction. For example:
BEQ
follows if theZ
bit is set.BMI
follows if theN
bit is set.BCC
andBCS
respond to theC
bit.
Matt explains what each bit means and when to use them. Overflow conditions are tricky, but you’ll see how to anticipate and handle them. This is core knowledge for writing flexible code.
4. Subroutines in Assembly
Assembly doesn’t use function names or argument lists, but subroutines still work. A label starts the routine, RTS
ends it, and JSR
jumps in. The 65C02 stores the return address on the stack so it knows how to get back.
Matt rewrites a simple C function in assembly, showing how to increment a value, return, and rejoin the main program. It’s lean, fast, and uses fewer resources than the equivalent C call.
5. Using Commander X16 Kernal Functions
You don’t have to write everything from scratch. The Commander X16 includes ROM-based subroutines like CHROUT
to print characters—just like putc
in C. Calling it is as easy as loading the accumulator and jumping to the known address. It saves space and speeds up development, especially when compared to including full C libraries.
6. Putting It All Together: The Example Program
Matt finishes with a hands-on demonstration. The program loops through a byte array three times, applying different operations:
- It prints indexes where values are greater than
0x55
. - It checks for carry after adding
0x55
, then prints. - It checks for overflow after another add and prints again.
Each loop showcases a different condition bit in action. The logic is compact and efficient, and the end result is printed to the X16 screen via ROM routines. It’s a tight demo of 6502 assembly branching and shows how much can be done with just a few instructions.