Page 1 of 1

Indirect Addressing - a more efficient way to programming

Posted: Sun Jun 30, 2013 9:38 pm
by bitfogav
Hi guys,

Most PIC Languages support some way of Indirect Addressing, what is Indirect Addressing you may ask!?. A simple form of accessing a PIC Register or storage location in a more efficient way and very useful if you need to access alot of data in this case an Array.

Firewing for an example supports this in more of an easier way to understand that I can rememeber on how to do this with Assembly language..

David Barker quick reference guide:

Byte Access (addr0, addr1 or baddr0, baddr1)

Code: Select all

addr = AddressOf(var) ' put address of var into addr
var = *(addr)         ' get byte value at address
var = *(-addr)        ' decrement address by one byte, then get byte value
var = *(+addr)        ' increment address by one byte, then get byte value
var = *(addr-)        ' get byte value, then decrement address by one byte
var = *(addr+)        ' get byte value, then increment address by one byte
Word Access (waddr0, waddr1)

Code: Select all

waddr = AddressOf(var) ' put address of var into waddr
var = *(waddr)         ' get word value at address
var = *(-waddr)        ' decrement address by two bytes, then get word value
var = *(+waddr)        ' increment address by two bytes, then get word value
var = *(waddr-)        ' get word value, then decrement address by two bytes
var = *(waddr+)        ' get word value, then increment address by two bytes
Ive done some comparison tests with Copying one Array data to another Array data:

This first example uses a For Loop to Copy the Array data to another Array

Code: Select all

dim myArray(5) as byte = {1,2,3,4,5}
dim myNewArray(5) as ushort = {0,0,0,0,0}

Sub Main()               
   for i as byte = 0 to 4
      myNewArray(i) = myArray(i)
   next   
                        
   for i as byte = 0 to 4
      Console.Write("myNewArray = ",str(myNewArray(i)),13,10) 
   next
End Sub
This next example uses the same theory but this time we are using Indirect Addressing:
The indirect addressing command *(addr1+) and *(addr0+) will automatically increase the register, see the quick reference guide above.

Code: Select all

dim count as byte = 0
dim myArray(5) as byte = {1,2,3,4,5}
dim myNewArray(5) as ushort = {0,0,0,0,0}

Sub Main()            
   addr0 = addressof(myArray)
   addr1 = addressof(myNewArray)   
   Do                        
      *(addr1+) = *(addr0+)          
      count += 1 
   loop until count = 5 
                           
   for i as byte = 0 to 4
      Console.Write("myNewArray = ",str(myNewArray(i)),13,10) 
   next
End Sub
To show what this achieves can only be seen in the Compilation Output from the Compiler, has you can see you save quite a bit of programme bytes and variable bytes:
Comparasionimage.jpg
Comparasionimage.jpg (18.7 KiB) Viewed 12065 times
- VERY IMPORTANT BIT TO REMEMBER WHEN USING INDIRECT ADDRESSING -
When using any indirect addressing command you need to remember that Firewing also uses these commands within Subs/Functions within the Compiler, and this will currupt the indirect addressing location you are trying to point too. Always check the Subs/Functions that you are calling within the Compiler for an example if you set a register address to command addr0 and then make a sub call to the compiler Console.Write() Sub then the addr0 will be changed as the Console.Write() sub uses the addr0 command.

Re: Indirect Addressing - a more efficient way to programmin

Posted: Wed Jul 03, 2013 7:39 am
by brad
Thanks for the great write up Gav. It's good to see ways of improving code - especially the sort of code that I would use a lot (accessing arrays).

One question - what is the significance of the * in front of the variables?

Re: Indirect Addressing - a more efficient way to programmin

Posted: Thu Jul 04, 2013 5:13 am
by bitfogav
The asterisk basically means "use the contents of the register as a pointer". In many ways its like Pointers in C, if you are familiar with the C language?.

So.. addr0 = AddressOf(myArray).
So.. *(addr0) accesses the contents of myArray(0).

The great thing about the commands *(addr0+) and *(addr0-) will automatically Increase or Decrease the address, so the next time you read addr0 the register will have the contents of the next or previous Array value.

Code: Select all

*(addr1+) = *(addr0+)
In the example above, All I am doing is reading addr0(myArray(n)) and storing it into addr1(myNewArray(n)) and then increasing both addresses of my Arrays.

Re: Indirect Addressing - a more efficient way to programmin

Posted: Wed Jul 10, 2013 12:07 pm
by brad
I've been reading over this a few times and I think I get it now.

So you set up an array with 5 variables. each of these variables is stored in memory, one after the other.

Code: Select all

dim myArray(5) as byte = {1,2,3,4,5}
You then have a variable that initially stores the location of the first variable.

Code: Select all

addr0 = AddressOf(myArray)
If you were to write Addr0 to the serial terminal it would show you a number (which represents the address of the first variable in myArray)

However if you were to write *Addr0 to the serial terminal it would actually show you the number 1 (which is the first number in the array).

Does that sound about right?

Re: Indirect Addressing - a more efficient way to programmin

Posted: Thu Jul 11, 2013 5:25 am
by bitfogav
Yes that sounds about right Brad.. Ive done another example based on what you was just saying with a serial terminal..

Code: Select all

   dim myArray(5) as byte = {1,2,3,4,5}
          
   for x as byte = 0 to ubound(myArray)
      addr0 = addressof(myArray(x))
      dim address as ushort = addr0
      dim value as byte = *(addr0)
      
      Console.Write("Address = ",str(address),13,10)                          
      Console.Write("Value   = ",str(value),13,10) 
   next 
Serial Output:
Address = 2126
Value = 1
Address = 2127
Value = 2
Address = 2128
Value = 3
Address = 2129
Value = 4
Address = 2130
Value = 5

wow I only posted this about a week ago and it as over 2000 views? lol.. :lol:
views1.jpg
views1.jpg (12.06 KiB) Viewed 9974 times

Re: Indirect Addressing - a more efficient way to programmin

Posted: Thu Jul 11, 2013 7:00 am
by brad
Thanks for the extra info Gav, very handy indeed and I think I will eventually get into some Firewing one day, I still very much like using Swordfish though!

I too noticed the huge amount of views - you're a popular man!

Re: Indirect Addressing - a more efficient way to programming

Posted: Sat Jun 10, 2017 4:14 pm
by Garth
Firewing above appears to be a cross between BASIC and C. It's new to me. The PIC16's indirect addressing method is rather poor IMO, so I'm sure something like Firewing makes it a littler easier for those not yet cozy with the PIC16's instruction and register set.

PIC16 uses special-function registers FSR (file-select register) and INDF (indirect file) rather than having the indirect addressing be part of the instruction set. It's awkward and inefficient, but it does work. Indirect indexed or indexed indirect gets all the more hairy. Reading from arrays or strings in ROM is not done the same way as in RAM, and it's especially a mess if it's not in the same page, ie, if the data is not in the same page where the code reading it is.

Yeah, I'm quite critical of the PIC16, after having brought a lot of products to market with it. My reasons for having used it for them was that it's inexpensive, comes in a lot of variations, with lots of onboard support including things like onboard oscillator, power-up timer, WDT, counter/timers, serial ports, interrupt-on-change pins, etc., and that it's widely available from lots of stocking distributors. I got into it in the mid-1990's. Now there are other microcontrollers that are probably better choices, like AVR, MSP430, and even higher-ranking PICs like PIC18, PIC24, PIC32.

I've written a lot of macros that I use to raise the level of the PIC16's assembly language though; so I have things like this routine to display a RAM string in variable EDITABL_STR in an LCD:

Code: Select all

DISP_RAM_STR:                         ; Display RAM string, in string variable EDITABL_STR.
        MOVF    MIN_CURSOR_POS, W
        CALL    SET_LCD_ADR           ; Uses variable LCD_TEMP.

        PUT     EDITABL_STR, IN, FSR  ; Here we put the literal addr of, not the contents of, EDITABL_STR, in the FSR.
        BEGIN
            MOVF   INDF, W
            RETURN_IF_ZERO            ; (0 would be the null terminator, meaning we're done.  Watch for it.)
            CALL   WR_LCD_AUTO        ; We don't do multi-line editing, so auto cursor advance is fine.  Uses variable TEMP_2.
            INCF   FSR, F
        AGAIN
 ;------------------
If I had to do more of this kind of thing, I would probably extend the macros to further automate the process, making the source code even more concise, while still remaining in assembly language.

Re: Indirect Addressing - a more efficient way to programming

Posted: Sat Jun 10, 2017 8:56 pm
by brad
I remember making the switch from 16F pics to 18F pics back in 2009/ 2010. The memory paging issues were a big determining factor in making that switch because I remember having huge issues with it.