Pointer arithmetic

Pointer arithmetic is an obscenely powerful and dangerous technique. However, it's the most straightforward way to deal with enormous arrays, structs, indexable stacks, and nearly everything you do in C. (C has no native array or string types primarily because it allows arbitrary pointer arithmetic, which is strong enough to handle all of those without complaint and at blazing speed. It also allows for all kinds of buffer overrun security holes, but let's face it, who's going to be cracking root on your Apple II?) There are a number of ways to implement this on the 6502. We'll deal with them in increasing order of design complexity.

The straightforward, slow way

When computing a pointer value, you simply treat the pointer as if it were a 16-bit integer. Do all the math you need, then when the time comes to dereference it, simply do a direct dereference as above. This is definitely doable, and it's not difficult. However, it is costly in both space and time.

When dealing with arbitrary indices large enough that they won't fit in the Y register, or when creating values that you don't intend to dereference (such as subtracting two pointers to find the length of a string), this is also the only truly usable technique.

The clever fast way

But wait, you say. Often when we compute a value, at least one of the operations is going to be an addition, and we're almost certain to have that value be less than 256! Surely we may save ourselves an operation by loading that value into the Y register and having the load operation itself perform the final addition!

Very good. This is the fastest technique, and sometimes it's even the most readable. These cases usually involve repeated reading of various fields from a structure or record. The base pointer always points to the base of the structure (or the top of the local variable list, or what have you) and the Y register takes values that index into that structure. This lets you keep the pointer variable in memory largely static and requires no explicit arithmetic instructions at all.

However, this technique is highly opaque and should always be well documented, indicating exactly what you think you're pointing at. Then, when you get garbage results, you can compare your comments and the resulting Y values with the actual definition of the structure to see who's screwing up.

For a case where we still need to do arithmetic, consider the classic case of needing to clear out a large chunk of memory. The following code fills the 4KB of memory between $C000 and $D000 with zeroes:

        lda #$C0        ; Store #$C000 in mem (low byte first)
        sta mem+1
        lda #$00
        sta mem
        ldx #$04        ; x holds number of times to execute outer loop
        tay             ; accumulator and y are both 0
loop:   sta (mem), y
        iny
        bne loop        ; Inner loop ends when y wraps around to 0
        inc mem+1       ; "Carry" from the iny to the core pointer
        dex             ; Decrement outer loop count, quit if done
        bne loop

Used carefully, proper use of the Y register can make your code smaller, faster, and more readable. Used carelessly it can make your code an unreadable, unmaintainable mess. Use it wisely, and with care, and it will be your greatest ally in writing flexible code.