Johnny Starr wrote:
Even if I label my subroutine "my_routine", the memory address will still have a numeric value right? Say $2002 for example.
It goes without saying that all memory locations have a numeric value. I think your confusion is in how that value gets established.
Quote:
Am I to understand that when using ZP pointers, and $2002 is created based on the values of what is stored at a pair of ZP registers, that it would override what was currently in $2002?
Only if your code writes on that location. For example:
Code:
ldx #<$2002
ldy #>$2002
stx $80
sty $81
merely sets locations $80 and $81 to point to location $2002. Specifically, the value $02 is written into $80 and $20 is written into $81. Nothing gets written to the memory cell at $2002 until you execute something like this:
Code:
lda #data
ldy #0
sta ($80),y
at which time the value DATA gets written into $2002, replacing what was previously there. Similarly:
Code:
lda #data
ldy #1
sta ($80),y
writes the value DATA into $2003. In other words, the MPU takes the address stored at $80 and $81 and adds to it the value in the .Y register. The result is the "effective address," which is where the actual store (or load or other) operation will occur. This technique is referred to as postindexed indirect addressing and is a very common idiom in the 6502 universe. Postindexed indirection is the assembly language analog to using a pointer in C.
Keep in mind that all memory is common, so it's your job to verify that code, data and uninitialized storage don't overlap. As Garth pointed out, good style and structure go a long way toward writing programs that work. If you are a C programmer, you are already familiar with the structured, top-down style that was demonstrated in the K&R whitebook. Those same basic techniques work in assembly language. All you are doing that C isn't is determining where in memory your code and data are going.
Quote:
I don't know much about the PC counter yet, so you may have answered this question in your examples. If that is the case, I'm not sure which portion that would be.
Understanding the program counter (PC) is basic to being able to write an assembly language program for any MPU. In the 65xx family, the PC is a 16 bit register that points to the address of the next instruction that is to be executed. The MPU loads the instruction opcode at the location pointed to by PC, determines what it is, and whether it needs an operand. If it does, the MPU increments PC to point to the operand, which may be one or two bytes. If two bytes, the least significant byte (LSB) is loaded, the PC is again incremented, and then the most significant byte (MSB) is loaded. The MPU carries out this current instruction, increments PC to point to the next instruction, etc. Execution normally proceeds linearly, but if a branch is taken or a subroutine called, PC is changed to point to the branch target or subroutine. Prior to calling a subroutine, the MPU will write the current value of the PC on the stack so it knows where to resume when the subroutine is exited.
In an assembly language program, you set the starting address for your program, using a directive such as *=$2000. In most assemblers, the asterisk (*) is the symbol for the PC and can be manipulated in various ways. Aside from the *=$2000 statement, you can advance the PC N bytes with the expression *=*+N, which I used in my previous post about defining a record. In general, once the assembler's notion of the PC has been set, it takes care of itself. The assembler knows how many bytes are occupied by each instruction and advances the PC as required. Until you get reasonably comfortable with how an assembler works and can write programs that assemble and execute without errors, don't obsess about what is going on. Just let the assembler do its job.