SubsAndFuncs

Reference.SubsAndFuncs History

Hide minor edits - Show changes to output

Changed line 202 from:
!![[#FrameRecyle]]Frame Recycling
to:
!![[#FrameRecycle]]Frame Recycling
Changed lines 379-384 from:
||Firewing supports the '''overloads''' keyword, although it is not strictly needed by the compiler to identify an overloaded sub of function.  However, it can be helpful to use '''overloads''' in terms of documenting your program. For example,
=firewing [=
Private Overloads Sub MySub(Val As Byte)
End Sub
=]

||
to:
||Firewing supports the '''overloads''' keyword, although it is not strictly needed by the compiler to identify an overloaded sub of function.  However, it can be helpful to use '''overloads''' in terms of documenting your program.||
Changed lines 76-77 from:
The Print() subroutine declaration will now output any string value passed to it.
to:
The ''Print''() subroutine declaration will now output any string value passed to it.

|| border=0 style="border-collapse:collapse" cellpadding=8 align=center width=90%  bgcolor=#dcdcdc
||You do not have to explicitly give the size of a formal parameter string when passing a string argument to a subroutine or function. For example, pStr as string(20). This is because the Firewing compiler has a powerful mechanism for calculating at compile time the maximum RAM needed for any string passed by value.||

Added lines 118-120:
|| border=0 style="border-collapse:collapse" cellpadding=8 align=center width=90%  bgcolor=#dcdcdc
||Unlike arrays, structures can be passed by value. However, if your structure has a large number of variables (or uses arrays and strings) it would be more computationally efficient to pass by reference, rather than the compiler having to copy large amounts of data, as would be required if passed by value.||

Added lines 301-303:
|| border=0 style="border-collapse:collapse" cellpadding=8 align=center width=90%  bgcolor=#dcdcdc
||Forgetting to assign a value to a function return type is a very common error. You should always ensure that a function result is assigned a value before the function exits. If you don't, the function value will be left in an undetermined state leading to very erratic program behavior which may be difficult to debug.||

Added lines 378-385:
|| border=0 style="border-collapse:collapse" cellpadding=8 align=center width=90%  bgcolor=#dcdcdc
||Firewing supports the '''overloads''' keyword, although it is not strictly needed by the compiler to identify an overloaded sub of function.  However, it can be helpful to use '''overloads''' in terms of documenting your program. For example,
=firewing [=
Private Overloads Sub MySub(Val As Byte)
End Sub
=]
||

Added lines 423-425:
|| border=0 style="border-collapse:collapse" cellpadding=8 align=center width=90%  bgcolor=#dcdcdc
||Because a compound subroutine will pass each argument in turn, it is essential that the subroutine to be called has been declared with exactly one parameter. Failure to do so will generate a compiler error message.||

Changed line 171 from:
=firewing =[
to:
=firewing [=
Changed line 199 from:
to:
=firewing [=
Changed lines 201-203 from:
   Dim Array(1000) As Byte
  For Index As Byte = 0 To UBound(Array)
      Array(Index) = 0
to:
   Dim array(1000) As Byte
  For indexAs Byte = 0 To UBound(array)
      array(index) = 0
Changed lines 208-210 from:
   Dim Array(1000) As Byte
  For Index As Byte = 0 To UBound(Array)
      Array(Index) = 0
to:
   Dim array(1000) As Byte
  For index As Byte = 0 To UBound(array)
      array(index) = 0
Changed lines 213-215 from:

The subroutine MySubA() allocates just over one thousand RAM bytes for its frame, to support the Array and Index declarations. MySubB() does exactly the same. However, when you call both of the subroutines from your program,
 
to:
=]
The subroutine ''MySubA''() allocates just over one thousand RAM bytes for its frame, to support the ''array'' and ''index'' declarations. ''MySubB''() does exactly the same. However, when you call both of the subroutines from your program,
=firewing [=
Changed lines 218-220 from:
 
the
compiler will just allocate RAM for one frame only (a little over one thousand bytes). This is because the subroutine calls are not dependent on each other, which means MySubB() can overlay its frame over the one allocated for MySubA(). Of course, if MySubB() made call to MySubA(), then twice as much frame RAM would be needed. This is to ensure that the variable and working register state of MySubB() is preserved, preventing MySubA() from overwriting it.
to:
=]
the
compiler will just allocate RAM for one frame only (a little over one thousand bytes). This is because the subroutine calls are not dependent on each other, which means MySubB() can overlay its frame over the one allocated for ''MySubA''(). Of course, if ''MySubB''() made call to ''MySubA''(), then twice as much frame RAM would be needed. This is to ensure that the variable and working register state of ''MySubB''() is preserved, preventing ''MySubA''() from overwriting it.
Changed lines 223-228 from:
17.6 - Inline

When an inline subroutine or function is generated, the computational expense of a call and return is removed by inserting the subroutine or function statement block at the point where
 
the
original call was made. For example, the following Print() subroutine
to:
!![[#Inline]]Inline

When an inline subroutine or function is generated, the computational expense of a call and return is removed by inserting the subroutine or function statement block at the point where the original call was made. For example, the following Print() subroutine
=firewing [=
Changed line 237 from:
to:
=]
Changed line 239 from:
to:
=firewing [=
Changed line 245 from:
to:
=]
Changed lines 248-249 from:
17.7 - Function Return Types
to:
!![[#Return]]Function Return Types
Changed lines 251-253 from:

Function Multiply(pValue As Byte) As UShort
  Return pValue * pValue
to:
=firewing [=
Function
Multiply(valueAs Byte) As UShort
  Return value * value
Changed lines 258-259 from:
   Dim Value As UShort = 100
  Value = Multiply(Value) + Multiply(Value * 2) - 50
to:
   Dim value As UShort = 100
  value = Multiply(value) + Multiply(value * 2) - 50
Changed lines 261-265 from:

Functions can return boolean, bit, byte, word, longword, shortint, integer, longint, string, float, char and structures. In the above example, the result was returned using the return keyword. This assigns the expression value to the function return value and then immediately exits the function. Alternatively, to assign a value to a function without immediately exiting the routine, you can use its name. For example,

Function Multiply(pValue As Byte) As UShort
  Multiply = pValue * pValue
to:
=]
In the above example
, the result was returned using the '''return''' keyword. This assigns the expression value to the function return value and then immediately exits the function. Alternatively, to assign a value to a function without immediately exiting the routine, you can use its name. For example,
=firewing [=
Function Multiply(value As Byte) As UShort
  Multiply = value * value
Changed line 267 from:
to:
=]
Changed lines 269-272 from:

Function Multiply(pValue As Byte) As UShort
  Multiply = pValue
  Multiply = Multiply * pValue
to:
=firewing [=
Function
Multiply(value As Byte) As UShort
  Multiply = value
  Multiply = Multiply * value
Changed line 274 from:
to:
=]
Changed lines 276-277 from:

Function SetUpper(pValue As Byte) As UShort
to:
=firewing [=
Function
SetUpper(value As Byte) As UShort
Changed line 279 from:
   SetUpper.Byte1 = pValue
to:
   SetUpper.Byte1 = value
Changed line 281 from:
to:
=]
Changed lines 283-285 from:

Function StrCopy(pStr As String) As String
  StrCopy = pStr
to:
=firewing [=
Function
StrCopy(str As String) As String
  StrCopy = str
Changed lines 287-290 from:

Here, the compiler will ensure result is allocated enough RAM to hold the return value, which depends on the value of pStr. It may be the case that you don't explicitly assign a string value to the function result. For example, when using assembler you will be manipulating the result string directly. The compiler therefore cannot calculate how much RAM to allocate, so you need to give the return string an explicit size. For example,

Function MyFunc(pStr As String) As String(32)
to:
=]
Here, the compiler will ensure result is allocated enough RAM to hold the return value, which depends on the value of ''str''. It may be the case that you don't explicitly assign a string value to the function result. For example, when using assembler you will be manipulating the result string directly. The compiler therefore cannot calculate how much RAM to allocate, so you need to give the return string an explicit size. For example,
=firewing [=
Function
MyFunc(strAs String) As String(32)
Changed line 292 from:
to:
=]
Changed lines 295-298 from:
 

17.8 -
Subroutine and Function Aliasing
to:
!![[#Alias]]Subroutine and Function Aliasing
Changed line 298 from:
to:
=firewing [=
Changed lines 304-306 from:
Dim HSerOut As USART.Write
Dim LCDWrite As LCD.Write
to:
Dim serOut As USART.Write
Dim LcdWrite As LCD.Write
Changed lines 310-311 from:
   HSerOut("Hello World", 13, 10)
  LCDWrite("Hello World")
to:
   serOut("Hello World", 13, 10)
  LcdWrite("Hello World")
Changed lines 313-321 from:

In this example, the standard library routines for writing have been renamed to match the naming conventions used by some other PICฎ microcontroller BASIC compilers.

17.9 - Overloading

Overloading enables you to have multiple subroutines and functions in the same scope that share
the same name. The compiler will select the most appropriate routine to call, based on its signature. A subroutine or function signature is constructed by using the number of formal parameters and also the type of each parameter. An overloaded routine must therefore have a unique combination of parameters, so that the compiler can identify which routine to call during compilation. For example,

Function Multiply(pValueA As Byte, pValueB As Byte) As UShort
  Multiply = pValueA * pValueB
to:
=]

!![[#Overloading]]Overloading

Overloading enables you to have multiple subroutines and functions in the same scope that share
the same name. The compiler will select the most appropriate routine to call, based on its ''signature''. A subroutine or function signature is constructed by using the number of formal parameters and also the type of each parameter. An overloaded routine must therefore have a unique combination of parameters, so that the compiler can identify which routine to call during compilation. For example,
=firewing [=
Function Multiply(valueA As Byte, valueB As Byte) As UShort
  Multiply = valueA * valueB
Changed lines 323-324 from:
Function Multiply(pValueA As Byte, pValueB As Byte) As Single
  Multiply = pValueA * pValueB
to:
Function Multiply(valueA As Byte, valueB As Byte) As Single
  Multiply = valueA * valueB
Changed line 326 from:
to:
=]
Changed lines 328-330 from:

Function Multiply(pValueA As Byte, pValueB As Byte) As UShort
  Multiply = pValueA * pValueB
to:
=firewing [=
Function
Multiply(valueA As Byte, valueB As Byte) As UShort
  Multiply = valueA * valueB
Changed lines 333-334 from:
Function Multiply(pValueA As Single, pValueB As Single) As Single
  Multiply = pValueA * pValueB
to:
Function Multiply(valueA As Single, valueB As Single) As Single
  Multiply = valueA * valueB
Changed line 339 from:
   Dim Result As UShort = Multiply(10,20)
to:
   Dim result As UShort = Multiply(10,20)
Changed line 341 from:
to:
=]
Changed line 343 from:
to:
=firewing [=
Changed lines 345-350 from:

then the second function will be called, because the floating point parameter is the only one that can accommodate a value of -10.

If the type of the value to be returned is to be the only unique way of identifying an overloaded routine a subroutine should be used and the result of the routine passed ByRef in the parameters.  For example:

Sub MySub(ByRef pValue As Byte)
to:
=]
then the second function will be called, because the floating point parameter is the only one that can accommodate a value of -10. If the type of the value to be returned is to be the only unique way of identifying an overloaded routine a subroutine should be used and the result of the routine passed '''ByRef''' in the parameters.  For example:
=firewing [=
Sub
MySub(ByRef value As Byte)
Changed line 351 from:
Sub MySub(ByRef pValue As UShort)
to:
Sub MySub(ByRef value As UShort)
Changed line 353 from:
to:
=]
Changed lines 355-356 from:

Sub MySub(pValueA As Byte, Optional pValueB As UShort = 0)
to:
=firewing [=
Sub
MySub(valueA As Byte, Optional valueB As UShort = 0)
Changed line 359 from:
Sub MySub(pValueA As Byte)
to:
Sub MySub(valueA As Byte)
Changed line 366 from:
to:
=]
Changed lines 369-379 from:
 

17.10 - Compound Subroutines

[private | public] compound sub identifier ([subroutine {, subroutine}])

• Private – An optional keyword which ensures that a compound subroutine is only available from within the module it is declared. Subroutine declarations are private by default.
• Public – An optional keyword which ensures that a compound subroutine is available to other programs or modules.
• Identifier – A mandatory compound subroutine name, which follows the standard identifier naming conventions
• Subroutine – One or more previously declared subroutine identifiers. Please note that a compound subroutine can only call other subroutines, not functions.
to:
!![[#Compound]]Compound Subroutines

['''private''' | '''public'''] '''compound''' '''sub''' identifier ([subroutine {, subroutine}])

*''Private'' – An optional keyword which ensures that a compound subroutine is only available from within the module it is declared. Subroutine declarations are private by default.
*''Public'' – An optional keyword which ensures that a compound subroutine is available to other programs or modules.
*''Identifier'' – A mandatory compound subroutine name, which follows the standard identifier naming conventions
*''Subroutine'' – One or more previously declared subroutine identifiers. Please note that a compound subroutine can only call other subroutines, not functions.
Changed line 379 from:
to:
=firewing [=
Changed line 383 from:
to:
=]
Changed line 385 from:
to:
=firewing [=
Changed line 387 from:
to:
=]
Changed line 389 from:
to:
=firewing [=
Changed lines 391-393 from:

Each time the compiler encounters the compound subroutine Write(), it takes each parameter argument in turn and generates a call to WriteByte(). You can have more than one subroutine contained in the compound declaration parameter list. For example,
to:
=]
Each time the compiler encounters the compound subroutine Write(), it takes each parameter argument in turn and generates a call to ''WriteByte''(). You can have more than one subroutine contained in the compound declaration parameter list. For example,
=firewing [=
Changed line 395 from:
to:
=]
Changed line 397 from:
to:
=firewing [=
Changed line 399 from:
to:
=]
Changed line 401 from:
to:
=firewing [=
Changed lines 405-407 from:

 
to:
=]
Changed line 407 from:
to:
=firewing [=
Changed line 409 from:
Sub WriteItem(pValue As String)
to:
Sub WriteItem(value As String)
Changed line 413 from:
Sub WriteItem(pValue As Byte)
to:
Sub WriteItem(value As Byte)
Changed lines 423-424 from:

In this example, the compound subroutine Write() is declared with an overloaded subroutine parameter called WriteItem(). When Write() is called from the main program, the compiler will make a call to the overloaded subroutine WriteItem(), based on the argument type. This allows you to create compound calls which accept arguments of different types and in any order.
to:
=]
In this example, the compound subroutine ''Write''() is declared with an overloaded subroutine parameter called ''WriteItem''(). When ''Write''() is called from the main program, the compiler will make a call to the overloaded subroutine ''WriteItem''(), based on the argument type. This allows you to create compound calls which accept arguments of different types and in any order.
Added lines 1-435:
Subroutines and functions enable you to divide a program into smaller parts. A subroutine or function is a named group of statements, constants, variables and other declarations that perform a particular purpose. A function is identical to a subroutine in every respect, with the one exception: it can return a single value to the calling program. Firewing subroutines and functions are non-reentrant, that is, you cannot make recursive subroutine or functions calls. 

!![[#SubDec]]Subroutine Declarations

 ['''private''' | '''public'''] ['''inline'''] '''sub''' identifier ([param {, param}])
  {declarations}
  {statements}
''' end sub'''

*''Private'' – An optional keyword which ensures that a subroutine is only available from within the module it is declared. Subroutine declarations are private by default.
*''Public'' – An optional keyword which ensures that a subroutine is available to other programs or modules.
*''Identifier'' – A mandatory subroutine name, which follows the standard identifier naming conventions
*''Param'' – One or more optional formal parameters

A formal parameter has the following parts

 ['''optional'''] ['''byval''' | '''byref''' | '''byrefconst'''] identifier '''as''' type [ = constexp]

*''Optional'' -An optional keyword that indicates an optional parameter. Must be used with a constant expression.
*''ByVal'' – An optional keyword indicating that an argument is passed by value. By default, formal parameters arguments are passed by value.
*''ByRef'' – An optional keyword indicating that an argument is passed by reference.
*''ByRefConst'' – An optional keyword indicating that a code constant is passed by reference
*''Type'' – A mandatory data type. Supported types include boolean, bit, byte, sbyte, short, ushort, integer, uinteger, single, structures, string and char.
*''ConstExp'' – An optional constant expression, which must include the optional keyword.

!![[#FuncDec]]Function Declarations

 ['''private''' | '''public'''] ['''inline'''] '''function''' identifier ([param {, param}]) '''as''' type
  {declarations}
  {statements}
''' end function'''

*''Private'' – An optional keyword which ensures that a subroutine is only available from within the module it is declared. Subroutine declarations are private by default.
*''Public'' – An optional keyword which ensures that a subroutine is available to other programs or modules.
*''Identifier'' – A mandatory subroutine name, which follows the standard identifier naming conventions
*''Param'' – One or more optional formal parameters
*''Type'' – A mandatory data type. Supported types include enumerated, boolean, bit, byte, sbyte, short, ushort, integer, uinteger, single, structures, string and char. Array types are not supported.

A formal parameter has the following parts

 ['''optional'''] ['''byval''' | '''byref''' | '''byrefconst'''] identifier '''as''' type [ = constexp]

*''Optional'' -An optional keyword that indicates an optional parameter. Must be used with a constant expression.
*''ByVal'' – An optional keyword indicating that an argument is passed by value. By default, formal parameters arguments are passed by value.
*''ByRef'' – An optional keyword indicating that an argument is passed by reference.
*''ByRefConst'' – An optional keyword indicating that a code constant is passed by reference
*''Type'' – A mandatory data type. Supported types include boolean, bit, byte, sbyte, short, ushort, integer, uinteger, single, structures, string and char.
*''ConstExp'' – An optional constant expression, which must include the optional keyword.

!![[#Params]]Parameters

Subroutines and function headings that do not have any formal parameters are written in the following way,
=firewing [=
' print subroutine...
Sub Print()
  Console.Write("Hello World", 13, 10)
End Sub

' main program...
Sub Main()
  Print
End Sub
=]
The subroutine declaration Print() outputs "Hello World" each time it is called. Note that although no formal parameters have been declared, start and end round brackets are still required. A more useful example would enable any string to be output. To do this, a formal parameter is added,
=firewing [=
' print subroutine...
Sub Print(text As String)
  Console.Write(text, 13, 10)
End Sub

' main program...
Sub Main()
  Print("Hello World")
End Sub
=]
The Print() subroutine declaration will now output any string value passed to it.

!![[#ParamBy]]ByVal, ByRef and ByRefConst

In the previous example, the string parameter argument was passed to the subroutine using the compilers default mechanism of by value (byval). Passing by value means that a local copy of the variable is created, and the subroutine or function operates on a copy. If your subroutine or function statement block changes the parameter value, it doesn't change the value of the actual variable being passed. This is in contrast to passing a variable by reference (byref). Passing by reference means that a subroutine or function receiving the variable can modify the contents of the variable being passed. This is sometimes referred to as a variable parameter. For example,
=firewing [=
Sub NoChange(value As Byte)
  value = 10
End Sub

Sub ChangeValue(ByRef value As Byte)
  value = 10
End Sub

Sub Main()
  Dim value As Byte = 0
  NoChange(value)
  Console.Write("Value : ", CStr(value), 13, 10)
  ChangeValue(value)
  Console.Write("Value : ", CStr(value), 13, 10)
End Sub
=]
The first subroutine NoChange() has a formal parameter that accepts arguments passed by value. The second subroutine ChangeValue() has a formal parameter that accepts arguments passed by reference. When the following lines are executed,
=firewing [=
NoChange(value)
Console.Write("Value : ", CStr(value), 13, 10)
=]
The value output will be 0, because NoChange() has received a copy of the contents of Value. When the following lines are executed,
=firewing [=
ChangeValue(value)
Console.Write("Value : ", CStr(value), 13, 10)
=]
The value output will now be 10, because ChangeValue() has received the actual RAM address of Value. Some declaration types, such as arrays, must always be passed by reference. For example,
=firewing [=
Sub PassArray(ByRef array() As Byte)
End Sub
=]
Notice that ''array'' is followed by open and closing round brackets. This is to inform the compiler that an array is being passed. Without the brackets, the compiler would just interpret the parameter argument as a single byte type.

It is important to remember that when a parameter argument is passed by reference, you can only call a subroutine or function with a single variable type. For example, given the declaration
=firewing [=
Sub MySub(ByRef value As UShort)
End Sub
=]
then an error 'cannot be passed by reference' message is generated when any of the following calls are made,
=firewing [=
MySub(10)
MySub(Index * Index) 
=]
Remember, passing by reference forces the compiler to pass the RAM address of a variable, allowing it to be changed from within a subroutine or function. Constants or expressions do not have RAM addresses associated with them and so cannot be used if a parameter argument is expecting pass by reference. If your subroutine or function parameter declaration is likely to be passed as a constant or expression, then you must always pass by value.

There is a third parameter passing mechanism, which is primarily used for constant arrays. On a PICฎ microcontroller, constant arrays are stored differently from data RAM which requires the use of '''byrefconst'''. This ensures that a ROM address is passed and not a RAM address. For example,
=firewing [=
' display names routine...
Sub DisplayNames(ByRefConst names() As String)
  For indexAs Byte = 0 To UBound(names)
      Console.Write(names(Index), 13, 10)
  Next
End Sub

' main program...
Sub Main()
  Const names(3) As String = {"David", "Fred", "Peter"}
  DisplayNames(names)   
End Sub
=]
In this example, DisplayNames() will output all the string values contained in the constant array Names.

!![[#Optional]]Optional Parameters

When a parameter is passed by value, it is sometimes useful to initialize the argument with a constant. For example,
=firewing [=
Sub Print(str As String, Optional terminator As Char = 13)
  Console.Write(str, terminator)
End Sub
=]
The formal parameter ''terminator'' has a default value of 13, which corresponds to a carriage return. If the subroutine Print() is called without a ''terminator'' argument value, then ''terminator'' will default to 13 (carriage return) when Write() is called. If you wish to explicitly override the formal parameter default, then call your subroutine with the required value, like this
=firewing [=
Print("Hello World", nothing)
=]
Here, ''terminator'' is set to the null terminator when Write() is called. It should be noted that you can only assign constants if the formal parameter argument is passed by value. In addition, you can only assign constants to parameters that appear at the end of the formal parameter list. For example,
=firewing =[
Sub MySub(a As Byte, Optional b As Byte = 10)
End Sub
=]
is correct, but
=firewing [=
Sub MySub(Optional a As Byte = 10, b As Byte)
End Sub
=]
will generate a compilation error.

!![[#Static]]Static Variables

When you normally declare a variable in a subroutine or function, it ceases to exist as soon as you exit the procedure. However, a static variable remains in existence and retains its most recent value. The next time your code calls the subroutine or function, the variable is not reinitialised and will hold the last value assigned to it.  For example,
=firewing =[
' sub to display static index...
Sub MySub()
  Static index As Byte = 0
  index += 1
  Console.Write("Index = ", CStr(index),13,10)
End Sub

' main program...
Sub Main()
  While True
      MySub
      DelayMS(1000)
  End While 
End Sub
=]
Will display

 Index = 1
 Index = 2
 Index = 3
 Index = 4

…and so on

!![[#FrameRecyle]]Frame Recycling

A frame describes the area of RAM reserved for use by local variables and parameters. Variables and parameters that are declared local to a subroutine or function are recycled by the compiler, whenever possible. For example,

Sub MySubA()
  Dim Array(1000) As Byte
  For Index As Byte = 0 To UBound(Array)
      Array(Index) = 0
  Next
End Sub

Sub MySubB()
  Dim Array(1000) As Byte
  For Index As Byte = 0 To UBound(Array)
      Array(Index) = 0
  Next
End Sub

The subroutine MySubA() allocates just over one thousand RAM bytes for its frame, to support the Array and Index declarations. MySubB() does exactly the same. However, when you call both of the subroutines from your program,
 
MySubA
MySubB
 
the compiler will just allocate RAM for one frame only (a little over one thousand bytes). This is because the subroutine calls are not dependent on each other, which means MySubB() can overlay its frame over the one allocated for MySubA(). Of course, if MySubB() made call to MySubA(), then twice as much frame RAM would be needed. This is to ensure that the variable and working register state of MySubB() is preserved, preventing MySubA() from overwriting it.

Frame recycling is a very powerful mechanism for minimizing RAM usage on microcontrollers with limited resources. If at all possible, declare working variables inside the scope of a subroutine or function, rather than at the program or module level, to fully exploit frame recycling. 

17.6 - Inline

When an inline subroutine or function is generated, the computational expense of a call and return is removed by inserting the subroutine or function statement block at the point where
 
the original call was made. For example, the following Print() subroutine

' print routine...
Inline Sub Print()
  Console.Write("Hello World", 13, 10)
End Sub

' main program...
Sub Main()
  Print
  Console.Write("The End", 13, 10)
End Sub

would be converted to inline, which is the same as writing,

' main program...
Sub Main()
  Console.Write("Hello World", 13, 10)
  Console.Write("The End", 13, 10)
End Sub

Take care when explicitly making a subroutine or function inline. Although inline routines remove the time overhead associated with making a call, there can be a significant cost in terms of code space used. Generally, you should only use inline for very small routines that need to execute quickly.

17.7 - Function Return Types

A function is identical to a subroutine in every respect, with the one important exception: it can return a single value to the calling program. You can use functions in expressions anywhere you would normally use a constant or variable of the same type. For example,

Function Multiply(pValue As Byte) As UShort
  Return pValue * pValue
End Function

' main program...
Sub Main()
  Dim Value As UShort = 100
  Value = Multiply(Value) + Multiply(Value * 2) - 50
End Sub

Functions can return boolean, bit, byte, word, longword, shortint, integer, longint, string, float, char and structures. In the above example, the result was returned using the return keyword. This assigns the expression value to the function return value and then immediately exits the function. Alternatively, to assign a value to a function without immediately exiting the routine, you can use its name. For example,

Function Multiply(pValue As Byte) As UShort
  Multiply = pValue * pValue
End Function

The function return type can be used on the left and right hand side of an expression. For example,

Function Multiply(pValue As Byte) As UShort
  Multiply = pValue
  Multiply = Multiply * pValue
End Function

You can also use modifiers with the function return type, like you would any other variable. For example,

Function SetUpper(pValue As Byte) As UShort
  SetUpper.Byte0 = 0
  SetUpper.Byte1 = pValue
End Function

String return types are a special case. They can be declared in a number of ways. The first uses the same method as a formal parameter declaration. That is, no explicit size is given.

Function StrCopy(pStr As String) As String
  StrCopy = pStr
End Function

Here, the compiler will ensure result is allocated enough RAM to hold the return value, which depends on the value of pStr. It may be the case that you don't explicitly assign a string value to the function result. For example, when using assembler you will be manipulating the result string directly. The compiler therefore cannot calculate how much RAM to allocate, so you need to give the return string an explicit size. For example,

Function MyFunc(pStr As String) As String(32)
End Function

will allocate 32 bytes, including the null terminator, for the result. Your return string must therefore never exceed 31 characters.

 

17.8 - Subroutine and Function Aliasing

Subroutines and functions can be aliased, in much the same way as you would alias a variable. For example,

' import modules...
Imports USART
Imports LCD

' rename standard library functions...
Dim HSerOut As USART.Write
Dim LCDWrite As LCD.Write

' main program...
Sub Main()
  ' use the alias names
  HSerOut("Hello World", 13, 10)
  LCDWrite("Hello World")
End Sub

In this example, the standard library routines for writing have been renamed to match the naming conventions used by some other PICฎ microcontroller BASIC compilers.

17.9 - Overloading

Overloading enables you to have multiple subroutines and functions in the same scope that share the same name. The compiler will select the most appropriate routine to call, based on its signature. A subroutine or function signature is constructed by using the number of formal parameters and also the type of each parameter. An overloaded routine must therefore have a unique combination of parameters, so that the compiler can identify which routine to call during compilation. For example,

Function Multiply(pValueA As Byte, pValueB As Byte) As UShort
  Multiply = pValueA * pValueB
End Function

Function Multiply(pValueA As Byte, pValueB As Byte) As Single
  Multiply = pValueA * pValueB
End Function 

will generate an error because the overloaded function signatures are identical. That is, they both have two parameters each of type byte. It is important to note that the compiler does not use function return types as part of the signature, only parameters The previous problem can be corrected by overloading the function with a unique parameter signature, like this,

Function Multiply(pValueA As Byte, pValueB As Byte) As UShort
  Multiply = pValueA * pValueB
End Function

Function Multiply(pValueA As Single, pValueB As Single) As Single
  Multiply = pValueA * pValueB
End Function

' main program...
Sub Main()
  Dim Result As UShort = Multiply(10,20)
End Sub

In this example, the first overloaded function is called because the parameter arguments are of type byte. The compiler will try and invoke the routine whose parameters have the smallest range that will accommodate the arguments in the call. For example, if the call to Multiply() is made with the following arguments,

Result = Multiply(-10,20)

then the second function will be called, because the floating point parameter is the only one that can accommodate a value of -10.

If the type of the value to be returned is to be the only unique way of identifying an overloaded routine a subroutine should be used and the result of the routine passed ByRef in the parameters.  For example:

Sub MySub(ByRef pValue As Byte)
End Sub

Sub MySub(ByRef pValue As UShort)
End Sub

If any parameters are assigned a constant in an overloaded routine, care should be taken to ensure you don't inadvertently create a situation where a routine cannot be called, for example,

Sub MySub(pValueA As Byte, Optional pValueB As UShort = 0)
End Sub

Sub MySub(pValueA As Byte)
End Sub

' main program...
Sub Main()
  MySub(10)
End Sub

In this example, the compiler cannot determine if the first or second overloaded routine should be called, because the parameter arguments are ambiguous.

 

17.10 - Compound Subroutines

[private | public] compound sub identifier ([subroutine {, subroutine}])

• Private – An optional keyword which ensures that a compound subroutine is only available from within the module it is declared. Subroutine declarations are private by default.
• Public – An optional keyword which ensures that a compound subroutine is available to other programs or modules.
• Identifier – A mandatory compound subroutine name, which follows the standard identifier naming conventions
• Subroutine – One or more previously declared subroutine identifiers. Please note that a compound subroutine can only call other subroutines, not functions.

A compound subroutine allows you to assign a single identifier that can be used to make multiple calls to a named subroutine, in one single statement. For example, rather than writing

WriteByte(10)
WriteByte(100)
WriteByte(5)

you can declare a compound subroutine,

Compound Sub Write(WriteByte)

and then call it from your main program,

Write(10,100,5)

Each time the compiler encounters the compound subroutine Write(), it takes each parameter argument in turn and generates a call to WriteByte(). You can have more than one subroutine contained in the compound declaration parameter list. For example,

Compound Sub Write(SetAddress, WriteByte)

When declaring a compound subroutine with more than one subroutine parameter, only the last subroutine in the parameter list will be called multiple times. For example,

Write(100,100,20)

Would be the same as writing

SetAddress(100)
WriteByte(100)
WriteByte(20)

 

Compound subroutines are extremely powerful when used in conjunction with overloaded subroutines. For example,

' overloaded sub to output a string...
Sub WriteItem(pValue As String)
End Sub

' overloaded sub to output a byte...
Sub WriteItem(pValue As Byte)
End Sub

' create compound subroutine...
Compound Sub Write(WriteItem)

' make the call...
Sub Main()
  Write(10,"Hello World", 13, 10)
End Sub

In this example, the compound subroutine Write() is declared with an overloaded subroutine parameter called WriteItem(). When Write() is called from the main program, the compiler will make a call to the overloaded subroutine WriteItem(), based on the argument type. This allows you to create compound calls which accept arguments of different types and in any order.