Procedures and Event Handlers
Procedures: Subs and Functions
Tibbo BASIC offers two kinds of procedures: subs, which are procedures that do not return a value (directly), and functions, which are procedures that do return a value.
Both subs and functions can have arguments.
The parallel structures in Tibbo C are a void function and a non-void function.
You must always specify the return type for the function. We do not allow omitting this.
sub fill_the_screen(color as word) lcd.forecolor=color lcd.fill(0,0,lcd.width,lcd.height) end sub function sum_total(a as word, b as word) as word sum_total=a+b 'this is how you return a value from a Tibbo BASIC function end function
void fill_the_screen(unsigned int color){ lcd.forecolor=color; lcd.fill(0,0,lcd.width,lcd.height); } unsigned int sum_total(unsigned int a, unsigned int b){ return a+b; }
Notice how the BASIC function returns the value: a function named sum_total will have a local automatically created variable with the same name. Set this variable to the desired return value.
Procedures Must Be Defined or Declared Before Use
In Tibbo BASIC/C, procedures must be defined or declared before they are used.
The procedure definition is its body. Place the body above the first call to this procedure, and there is no need for a declaration:
function sum_total(a as word, b as word) as word sum_total=a+b end function sub on_sys_init dim i as word=sum_total(10,20) end sub
unsigned int sum_total(unsigned int a, unsigned int b){ return a+b; } void on_sys_init(){ unsigned int i=sum_total(10,20); }
If a procedure is called first and defined later, place a declaration before the first call:
declare function sum_total(a as word, b as word) as word sub on_sys_init dim i as word=sum_total(10,20) end sub function sum_total(a as word, b as word) as word sum_total=a+b end function
unsigned int sum_total(unsigned int a, unsigned int b); void on_sys_init(){ unsigned int i=sum_total(10,20); } unsigned int sum_total(unsigned int a, unsigned int b){ return a+b; }
As each source code (and HTML file) is compiled separately, it is necessary to declare procedures defined in other files before they can be called. Such declarations are usually placed in header files — .TPH for Tibbo BASIC and .TH for Tibbo C. Header files must be included into your source code file; it's not enough to simply have them in the project tree:
include "global.tbh"
#include "global.th"
Event Handlers
Event handlers are subs in Tibbo BASIC and void functions in Tibbo C. These require no declaration because they are already declared in platform files.
Whenever the VM gets to handling a certain event, it calls the corresponding event handler for this event, provided such handler exists in your code.
sub on_sys_init 'always called once on boot end sub sub on_sys_timer 'called periodically (by default, at 0.5 second intervals) end sub
void on_sys_init(){ //always called once on boot } void on_sys_timer(){ //called periodically (by default, at 0.5 second intervals) }
How do you tell if a procedure is an event handler or not? Names of event handlers are highlighted in green. Additionally, all event handler names start with on_, which means on x happening, do....
The list of available events can be found in the Browser-Project pane: View > Browser-Project -> Events.
Inactive events (those with no event handlers) are grayed out. Double-click on an inactive event to add its event handler to the bottom of the current file.
Double-click on an active event to "teleport" to this event handler's body.
Events are actually a part of platform objects. For more information, see Objects and Syscalls.
Direct Argument Passing
In Tibbo BASIC, procedure arguments can be passed by value (ByVal) or by reference (ByRef). ByVal is the default mode, so you don't have to specify it.
The ByVal argument passes a copy of the value. If the procedure called modifies this value, then only that copy is modified and the original instance stays the same.
The equivalent of this in the C world is passing an argument and not a pointer to this argument.
sub print(i as word) 'you could write "sub print(ByVal i as word)" sys.debugprint(str(i)+chr(13)+chr(10)) end sub
void print(unsigned int i){ sys.debugprint(str(i)+chr(13)+chr(10)); }
In Tibbo BASIC and C, even "bulky" items can be passed directly.
For Tibbo BASIC, such bulky items are string variables and types (structures). For Tibbo C, these are string variables, arrays, structures, and unions.
Have you noticed? Arrays are not listed for BASIC! In Tibbo BASIC it's not possible to pass an array to a sub/function or return an array from a function.
Here is an example of a function that increments each member of an array and then returns the array back. Since arrays cannot be passed to/from BASIC procedures, the BASIC code example below wraps an array into a type.
Notice a very interesting syntax that is used to specify that the C functions returns an array.
'TERRIBLY INEFFICIENT but will work! type type_a arr(5) as byte end type function arr_increment(input as type_a) as type_a dim f as byte for f=0 to countof(input.arr)-1 input.arr(f)=input.arr(f)+1 next f arr_increment=input end function sub on_sys_init() dim master_arr as type_a ... master_arr=arr_increment(master_arr) end sub
//TERRIBLY INEFFICIENT but will work! unsigned char arr_increment(unsigned char input[5])[5]{ //the highlighted [5] above specifies that this function returns an array unsigned char f; for(f=0;f<sizeof(input);f++) input[f]++; return input; } void on_sys_init(){ unsigned char master_arr[5]; ... master_arr=arr_increment(master_arr); }
Indirect Argument Passing
In Tibbo BASIC, a ByRef argument passes a reference to the value. If the procedure called modifies this argument, then the original instance gets modified.
This is roughly equivalent to using a pointer in C, except the pointer itself is not available to the BASIC code and can't be modified by it.
ByRef argument passing in BASIC and pointers in C are recommended for bulky types. This saves memory and improves performance.
ByRef/pointers can also be used to indirectly return a value from a procedure (void function).
Here is the previous array increment example, now rewritten to use a ByRef argument and a pointer. It is much more efficient as no unnecessary copying takes place.
type type_a arr(5) as byte end type sub indir_arr_increment(byref input as type_a) dim f as byte for f=0 to countof(input.arr)-1 input.arr(f)=input.arr(f)+1 next f end sub sub on_sys_init() dim master_arr as type_a ... indir_arr_increment(master_arr) end sub
void indir_arr_increment(unsigned char *ptr,unsigned char len){ unsigned char f; for(f=0;f<len;f++) (*ptr++)++; } void on_sys_init(){ unsigned char master_arr[5]}; ... indir_arr_increment2(&master_arr,sizeof(master_arr)); }
Tibbo BASIC and C will never automatically switch to using the ByRef (pointer) method instead of passing the value itself.
Some languages practice automatic switching whenever there is a bulky item to pass. For example, there are systems that won't pass a structure directly and won't tell you about the substitution that took place.
We consider this to be extremely misleading. In Tibbo BASIC (and C), "what you define is what you get."
No Recursion, Re-Entrant Code, or Function Pointers
Tibbo BASIC and Tibbo C do not allow recursion. Meaning: procedures cannot call themselves (even indirectly).
It is also not possible to create re-entrant functions.
Additionally, pointers to functions are not allowed in Tibbo C.
All of this has to do to how the memory is allocated for procedures.