News:

Let's find out together what makes a PIC Tick!

Main Menu

Procedure question please

Started by joesaliba, Mar 04, 2022, 05:56 PM

Previous topic - Next topic

joesaliba

Hi,

Can a procedure be called from another procedure please?

Can a procedure return two results?

Thank you

Joe

top204

#1
QuoteCan a procedure be called from another procedure

Yes. However, each call to a procedure is added to the device's call stack, the same as Gosub, so on a standard 8-bit device, do not go too deep with the calls from procedure within procedure within procedure etc... Enhanced 14-bit core devices have a larger call stack so they can be called a bit deeper, and the standard 18F devices have a larger stack depth again. The new 18FxxQxx devices I am currently working on adding to the compiler, have a call stack depth of 128. :-) The compiler optimises some Calls into Gotos, but it cannot do that with every procedure call.

The PIC24 and dsPIC33 devices have a user defined call stack and can be any size, up to the maximum of the RAM available on a device, which is just the way it "should" have been on the newer 18F devices as well!

QuoteCan a procedure return two results?

Yes. A procedure has a few ways of returning multiple results, thanks to the compiler's aliasing mechanism. For example, if 4 bytes are needed as a return, make the return variable a Dword, then load each byte of the Dword with the values to return. The same if 2 word variables are required etc... Then use the alias mechanism to give each part of the return variable a name or use. If more variables are required as returns, return an array, and load each element of the array with a result from within the procedure.

A more elaborate way, and something commonly used in C, is to indirectly access a parameter, so the parameter holds the address of the variable that was placed into it. Then load the passed address with the result of something within the procedure. See the listing below for an example:

'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' Demo to show how to return a value from a procedure using an indirect operation.
' Written for the Positron8 compiler by Les Johnson
'
    Device = 18F25K20
    Declare Xtal = 16
'
' Setup USART1
'   
    Declare Hserial_Baud = 9600
    Declare HRSOut1_Pin = PORTC.6
'
' Create some variables
'  
    Dim MyWordIn As Word
    Dim MyWordOut1 As Word
    Dim MyWordOut2 As Word
   
'--------------------------------------------------------------------
Main:   
    MyWordIn = 10
 
    MyWordOut2 = TestProc(MyWordIn, MyWordOut1)             ' Call the procedure, and MyWordOut1 will also be loaded with a result
   
    HRSOutLn "MyWordIn = ", Dec MyWordIn
    HRSOutLn "MyWordOut1 = ", Dec MyWordOut1
    HRSOutLn "MyWordOut2 = ", Dec MyWordOut2
 
'--------------------------------------------------------------------
' A test procedure to show how a parameter can be used to hold a return value
' Input     : pValIn holds the value to add too
' Output    : Returns a simple addition of pValIn
'           : pValOut holds the address of the variable that will be added too
' Notes     : Uses ByRef and Ptr16 to pass a return back as a parameter
'
Proc TestProc(pValIn As Word, ByRef pValOut As Word), Word
    Ptr16(pValOut) = pValIn + 10        ' Load the 16-bit address held in pValOut, with the result of an addition
    Result = pValIn + 20                ' Return another addition
EndProc

On the serial terminal, it will display:
MyWordIn = 10
MyWordOut1 = 20
MyWordOut2 = 30

joesaliba

Thank you Les,

I have to work out why a variable in main routine is changing when a procedure is called and another procedure is called within that procedure. I am sure doing something wrong.

With regards to C language and pointers, I was going to open a new thread, but you answered it before I even made the question:)

When I read this article I noticed that when indirectly accessed variable is used it is to make a code faster. Does this apply same to PDS?

Thank you
Regards

Joe

top204

#3
QuoteI have to work out why a variable in main routine is changing when a procedure is called and another procedure is called within that procedure. I am sure doing something wrong.

Within the assembler listing (Press the F2 button), see the address of the variable that is being changed, and make sure no other variable or alias has the same address. I've made the address' and names very easy to read and understand in the assembler listing. Also, make sure you are not using indirect commands and overwriting it. And make sure you are not over-running any arrays or strings.

QuoteWhen I read this article I noticed that when indirectly accessed variable is used it is to make a code faster. Does this apply same to PDS?

Indirect operations are, sometimes, more efficient when working with Strings and Arrays, but not faster or more efficient with standard variable types on "any" 8-bit device, be it Atmel or PIC etc, because they are not RAM based, so the address of the variable in question has to be loaded into, at least, two SFRs then the address has to be loaded or read, which takes more time and space than simply loading the variable in question into another variable, or vice-versa. But try telling that to C and C++ programmers and they will laugh because 90% of them have no idea what indirection is, they just know it as "pointers", and it is part of the language that is so over used it makes the code listing into gibberish, and extremely inefficient. :-)

With C, it is mostly copy and paste from programs that are written for computers, which are RAM based and Stack based. So indirect operations are a main part of its instruction set. The same with most 32-bit microcontrollers that have a true RAM stack and mnemonics that make indirect operations extremely efficient, as in the old days of 8-bit microprocessors. :-) But not with any 8-bit microcontoller device!!!

Because all microcontrollers are RAM and ROM (Flash) based, they are never as efficient as microcprocessors that are purely RAM based. It's just a shame that microcontroller's have not been developed that transfer all the ROM code into RAM, then work as a microprocessor with a true multi-purpose RAM Stack, but still keep the original code in ROM for when it is powered Off-On. :-) That would be really nice and would make true emulation of microcprocessors possible, and make code operate much, much faster. A single chip Z80 or 6502 or 6800 with built in ROM and RAM and IO ports and peripherals. That's what dreams are made of. LOL I did something similar back in the 1980s, but it took a PCB with quite a few EPROM and RAM and PIO and UART chips on it, as well as the Z80 itself, and not forgetting the TTL 74 series chips to get the addressing correct for them all. :-)

joesaliba

Quote from: top204 on Mar 04, 2022, 08:09 PMso the address of the variable in question has to be loaded into, at least, two SFRs then the address has to be loaded or read, which takes more time and space than simply loading the variable in question into another variable, or vice-versa.

And when reading that article about pointers that what I thought. That you still have to load the address to make changes. In my applications, most probably I will not need such, so I better stick with what I learned rather than scratch the few hair remaining. 😂😂

Thanks Les

Joe

trastikata

Hello Les,

could you give me a hint how to address the Result of a Proc at a later stage in the code, something like:

DoSomething
MyProc1
DoSomethingElse
MyProc2(Result from MyProc1)

Thank you

tumbleweed

Save the result of MyProc1 to a variable so you can use it later.
Dim bMyByte as byte

Proc MyProc1(pBytein as Byte), Byte
  Result= pBytein   ' Transfer the parameter to the return variable
  Result= Result + 1  ' Add one to it
EndProc

bMyByte = MyProc1(1)     ' save return value from MyProc1
' do other stuff
MyProc2(bMyByte)        

trastikata

Quote from: tumbleweed on Mar 06, 2022, 12:17 AMSave the result of MyProc1 to a variable so you can use it later.

Thank you tumbleweed, this is the obvious, but not my question  ;) .

top204

I don't understand your question trastikata.

To store the result from a procedure, use a temporary variable if it will be changed elsewere in the code, otherwise, use the Result variable itself. Remember, Result is an actual local variable, so you can do things like MyVar = Result or Result = MyVar.


trastikata

Hello Les,

my understanding is that the Result of a Procedure is held in a local variable and the value, stored in this variable, doesn't change until the Procedure is called again?

What I want is to call the Procedure at some place in the code and later on to use the Result of that procedure.

Of course I can store the result in a temporary variable, but if the Result of a Procedure is already held in a local variable, this would mean wasting of RAM for the temporary variable.

I know it doesn't look much, one temporary variable - but in my case it will simplify the code and every RAM byte counts  :). Here's a simplified example of what I mean:

Dim c As Byte
Dim d As Byte

c = 5 * AddProc(6,4)                    'c = 5*(6+4) = 5*10 = 50
d = c - SubProc(Result from AddProc,5)  'd = 50-(10-5) = 50-5 = 45

Proc AddProc (a As Byte, b As Byte), Byte
    Result = a + b
EndProc 

Proc SubProc (e As Byte, f As Byte), Byte
    Result = e - f
EndProc

top204

The Result variable from a procedure is held in RAM for all of the rest of the program to see, and its name will be: "SubProcResult", for the procedure "SubProc".

So, as long as the procedure has been called from within a program, so the variables are created, you can use that variable. i.e. SubProcResult = MyVar or MyVar = SubProcResult.

It can even be used as an alias for another procedure's Result or parameter, and it will retain its size. i.e. Byte, Word, Long, Dword etc...

trastikata

Quote from: top204 on Mar 06, 2022, 12:16 PMThe Result variable from a procedure is held in RAM for all of the rest of the program to see, and its name will be: "SubProcResult", for the procedure "SubProc".

Thank you Les, this thoroughly answers my question!

The way you introduced Procedures, makes programming much easier and readable.

trastikata

Quote from: top204 on Mar 06, 2022, 12:16 PMSo, as long as the procedure has been called from within a program, so the variables are created, you can use that variable. i.e. SubProcResult = MyVar or MyVar = SubProcResult.

Les,

it seems that I can not access individual bits of the result variable from a Procedure, is this normal?

Thank you

Device=18F26K22         
Declare Xtal=8

Dim i As Byte
Dim bTemp As Byte

Main:
    TestProc()
   
    'This works
    bTemp = TestProcResult
   
    'This doesn't work
    bTemp.7 = TestProcResult.7


Proc TestProc(), Byte
    For i = 7 DownTo 0 Step -1
        LoadBit Result, i, 1
    Next
EndProc

Yasin

"getbit" you should use this.

trastikata

#14
Quote from: Yasin on May 08, 2022, 09:03 AM"getbit" you should use this.

I know that GetBit can be used  :), however accessing individual bits is easier to read, nevertheless that was not the question.

If the Result of a Proc is a Variable that can be accessed from anywhere, then maybe not being able to access its individual bits is an anomaly because nonetheless you can access the individual bits of the Result within the Proc itself?

top204

#15
I'll take a look as to why a procedure's global result variable can be used as the assignment, but not the operator, unless it is in an expression. A very strange one to find! :-)

Yasin

I am sorry. because of my poor english. I always get it wrong.  :(

top204

No you did not get it wrong Yasin. The GetBit function would also work.

keytapper

I noticed the keyword DownTo. Is that meaning to count a For downward?
Ignorance comes with a cost

trastikata

#19
Quote from: keytapper on May 08, 2022, 01:34 PMI noticed the keyword DownTo. Is that meaning to count a For downward?

By default the normal loop For ... To ... Next will assume an increment of 1. If you intended a downwards countdown and you have forgotten to assign a negative step, then your loop will not work as intended and will not give you any warning of this omission.

Indeed, if Step -i is omitted (or forgotten as I sometimes do) the countdown when using DownTo is in decrements of 1. Thus if you make it a habit of using it, then there is a less chance of wasting time in debugging code that is supposed to work but it doesn't, as I do :) .

Note the following:
    For i = 7 To 0 Step -1 : Next      'Will work
    For i = 7 To 0 : Next            'Won't work

    For i = 7 DownTo 0 Step -1 : Next  'Will work
    For i = 7 DownTo 0 : Next          'Will work