Alright, so essentially direct addressing and indirect addressing are simply two different ways of accessing registers 0x00 through 0x1FF?
Yes exactly. The register FSR (File Select Register) is the address pointer register. The register INDF (INDirect File) is the indirect addressing data buffer. Register INDF holds the data that resides at the address that is currently written to the FSR register.
For example, let's say we wanted to indirectly address register location 0x25. We would do -
Code:
movlw 0x25 ;write address to RAM address pointer
movwf FSR
movfw INDF ;place contents of location in FSR into W
This would take the contents of address location 0x25 and place the contents in W. You can do what you want with it from there.
There is some trickery to be had with using indirect addressing. When you have registers that are placed sequentially and you have to access data from these registers in sequential order, you can set up a loop to increment the FSR address and then pull the data from INDF. I use this exact technique for multiplexing LED displays. I have 4 registers set up for each digit on the display that contain the segment data for each digit and I usually place these buffers at addresses 0x20-0x23. Then everytime timer 0 overflows, it goes to the timer 0 interrupt, increments the FSR, then pulls the next segment data from INDF and drops the data into the port that is the segment driver port. When the address in FSR reaches the value of 0x24, I have the code reset the address in FSR to 0x20 and the cycle repeats.
Does this mean the directive banksel internally changes bits RP0 and RP1 depending on the name of the register we pass into it?
Banksel is not a directive. It is a built in macro. When you invoke the banksel macro, it assembles in the required bsf/bcf instructions that set/clear RP0 and RP1 for the bank you're wanting to access.
Also, in order to be safe, should I always banksel a certain register in case I do not know what my STATUS register is currently pointing to and I am accessing a register not in the common ram space?
You as the programmer should never NOT know which bank is the currently active bank. You're the one writing the code and the bank select bits don't get altered unless you directly change them.
Now to jump back to bank 0, you can always do "banksel 0". This works because register 0 resides in bank 0. The correct syntax for banksel is "banksel ADDRESS". ADDRESS can either be a physical address in the bank you're selecting or a label that has been equated to an address value in the bank that you're selecting. "Banksel 0" would select bank 0, "banksel 0x80" would select bank 1, "banksel 0x100" would select bank 2 while "banksel 0x800" would select bank 3.
You only need to banksel when you need to go to a different bank. Not everytime you access a register in a bank other than 0. Overusing banksel will just generate bloated code as it would assemble in redundant/unnecessary bsf/bcf STATUS,RP0 and bsf/bcf STATUS,RP1 instructions everytime you invoke it.
Does this also mean that the following line of code specifies where VAR will be stored and I can later change its contents?
The EQU directive just equates a label to a constant. The context you use the label in determines whether the instruction will access a memory location or whether it will load a value into W.
For example -
This would load the value of 0x71 into the W register.
This would load whatever is in W into memory location 0x71.
The first operand in instructions that have an "f" in the instruction is always a physical address. The first operand in instructions that have an "l" in the instruction is always a literal value.
Comparing the above line of code to this one:
Would I have to banksel to access VAR2 while not having to banksel to access VAR?
Only if you're not in bank 0. Register address 0x69 resides in bank 0 so if you're already in bank 0 then you will not need to banksel to access it. VAR however, resides in the common RAM space so you can access that register regardless of the currently active bank.
When using the directive udata in the following way:
Where is VAR3 stored exactly? Do I need to banksel it in order to use it? Will the pic ever put it in the common RAM space?
udata would place VAR3 in the banked memory. udata_shr would place it in the common shared RAM. As far as which exact locations they end up in is up to the assembler. For relocatable code, this is a handy tool.
However, udata/res are directives that you use only when writing relocatable code. If you're writing absolute code (i.e. code that will only be intended for a specific PIC), you can just use cblock/endc to equate labels to a specific set of constants that correspond with the RAM address locations that you will use them to access. For example, my cblock for the common RAM would look like such -
Code:
cblock 0x70
W_TEMP ;W context save
STATUS_TEMP ;STATUS context save
PCLATH_TEMP ;PCLATH context save
COUNT ;delay counter
T1COUNT ;TMR1 interrupt counter
MIDI_TEMP ;MIDI data buffer
LEDSTAT ;LED status buffer
PCSTAT ;MIDI status message buffer
CCCONT ;MIDI controller number buffer
PCDATA ;MIDI data message buffer
DATA_EE_ADDR ;data EEPROM address buffer
DATA_EE_DATA ;data EEPROM data buffer
PCSTORE ;program change store address
COUNT1 ;delay counter 1
COUNT2 ;delay counter 2
COUNT3 ;delay counter 3
endc
As shown above, the assembler will equate the labels in order in which they appear in the cblock to constant values 0x70-0x7F starting with the first one in the list being equated to 0x70. The address value after the "cblock" directive is the starting address of the cblock (cblock stands for "constant block").
Lastly, in the link you posted, near the end, you said it is better to store the delay counters in the common ram space. Could you also store that in other general purpose (non common ram space) registers and simply select that bank when you call your delay subroutine?
You can place them wherever you want. Placing them in common RAM just makes things easier as you will be able to call the delay routines regardless of the active bank.
Quite honestly, I tend to place the registers that will be accessed the most in common RAM as this drastically reduces the amount of bank selecting you will have to do to access them. This cuts down on bank select instructions, which keeps your code tighter.