Liberty Basic is develeopped by Carl Gundel
Original Newsletter compiled by Alyce Watson and Brosco
Translation to HTML: Raymond Roumeas

The Liberty Basic Newsletter - Issue #48 - AUG 99

"Knowledge is a gift we receive from others." - Michael T. Rankin

In this issue:

Procedure Parameter Passing and Encapsulation by Herman

In future issues:

Serial Communications by Herman

Thunking 32-bit API Calls


We are delighted to publish another great article by Herman. He has also written an article on serial

communications that will be published soon. Thanks, and thanks again, Herman!!!

Contact Herman Oosthuysen:

http://www.AerospaceSoftware.com mailto:aerosof-@aerospacesoftware.com


Procedure Parameter Passing and Encapsulation

General

A severe shortcoming of BASIC, but also the main feature that makes it so easy to use, is the lack of parameters in subroutine calls. This creates a happy-go-lucky programming environment, where all variables are global and everything can be accessed at any time. The problem being that this approach not only makes coding easy, it also makes it very easy to produce the most weird and wonderful bugs.

As an example of how things work on the other side of the fence, I created a method to parameterize functions through the use of temporary variables, by borrowing a simple trick often used in quick and dirty assembly language programming and by creating a proper stack frame, which is just a little more complex.

The advantages of subroutine encapsulation is tremendous, since it localizes bugs and can provide re-entrancy, a useful feature for some specialized problems, such as recursive descent parsers.

In BASIC, it does have its problems though, since there are two types of arrays, for numerics and for strings, so the stack method presented here is only a curiosity - not practical, although it actually works!

Anyhow, read on and you'll see...

Static Subroutine Framing

If we dedicate the variables p1, p2...pn to parameters, and the variables r1, r2...rn to return values, then we can write very neat and tidy code with limited subroutine encapsulation. This encapsulation, prevents the 'rapid spreading of bugs throughout a program and limits a bug to a specific subroutine, since the called subroutine now always use temporary variables unrelated to the variables used by the calling code.

If a subroutine would inadvertantly modify an input parameter, it will not affect the calling code. Similarly, one subroutine cannot affect another subroutine, since the temporary variables used in the subroutines are always overwritten when the subroutine is called.

The Static Call

The trick is to use the colon separator, to create a one line subroutine call as shown below in the main routine. Visually, this creates an effect similar to a function call in other high level languages, tying the parameters and the called function together. This is simply to enhance the human friendlyness of the call, a majour factor in the prevention of bugs.

A subroutine call with 2 static parameters and one return value will then look like this:

p1 = 1: p2 = 2: gosub [teststatic]: result = r1

The important thing to note is that these parameters are only useable until another subroutine is called. Therefore the one line call syntax, forcing one to re-assign the parameters immediately, to prevent inadvertant bugs.

This method works OK, provided that subroutines don't call other subroutines, in which case the short life of the parameters become a real head-ache, requiring one to fall back to global variables, negating the whole idea.

Non re-entrant

Note that because of the use of static variables for parameter passing, the routines are also not re-entrant. That is, a subroutine should never call itself, since doing so will cause unexpected re-use of variables, causing the program to get confused.

Dynamic Subroutine Framing

Adding a push down, pop up, parameter stack to function calls is a bit more complex but provides re-entrant encapsulation of procedures. The code now starts to resemble the code generated by high level compilers and is not BASIC like anymore.

We create subroutine frames on a stack, whikch is simply a large array called s() and a stack pointer p, which is an index into this array.

The stack is declared as follows:

dim s(1024) 'parameter stack
p = 0 'stack pointer

In order to keep the call construct manageable, I resorted to single letter variables, something which is usually best avoided!

The Parameter Stack

The stack need only manage parameters. The return address is already handled by the BASIC interpreter. The stack is simply an array, used to store values passed to the subroutines. The advantage of using a parameter stack, is that the variables stay valid for the duration of the procedure. These parameters will not be overwritten, until the stack is freed upon subroutine return. Parameters can be accessed, even after another subroutine was called. This contrasts the stack method from the static parameter method described first.

When we call a function, we push the parameters down onto the stack as follows:

s(p)=value: p=p+1 'push one parameter onto the stack

What happens is that we save the value in the array at the current stack pointer p, then we increment the stack pointer to point to the next free position, ready to handle the next parameter. This way, we can pass any number of parameters, subject to the size of the stack definition of course.

Inside the function, we access the parameters again by using p, but with a negative offset, without changing the value of the pointer p, like this:

value=s(p-1) 'read the last parameter pushed

Finally, when we exit the subroutine to return to the caller, we remove the parameters from the stack, by decrementing the stack pointer with a value equal to the number of parameters passed to the subroutine, like this:

p=p-1: return 'remove one parameter from the stack

The trick with this approach, is to use a template for the function calls to avoid bugs with the stack management.

In practice, the calls and returns are a little more complex, since we also have to handle temporary variables and return value(s), as shown in the examples below.

Subroutines with Local (Automatic) Variables

Automatic variables are variables declared on the stack frame, for local use inside a function. The advantage of doing this is that the variables only exist while the subroutine is active, making it impossible for a subroutine to interfere with the operation of another subroutine, or even with another instance of itself. If a subroutine would use only automatic variables and no globals, then the subroutine would become re-entrant, which means that it could be called from an interrupt routine or that it could call itself recursively, a useful feature for some parsing algorithms.

In order to accommodate automatic variables, we reserve space on the stack upon subroutine entry.

We increment the stack pointer by the number of temporary variables we need in the subroutine, so that the stack pointer points to the first position after the temporary variables:

p=p+2 'make space for 2 temporary variables

Once we have done this, the stack is ready to allow another subroutine call and we can access the parameters and temporary variables at our leisure.

We would need some leisure, since things are now getting a bit complicated.

Where is it now?

The single parameter pushed onto the stack in the above example now resides at p-3 and the two temporary variables are at p-2 and p-1. So, provided that you comment the code properly, this works just dandy:

parameter=s(p-3) 'access the last parameter
auto1=s(p-2) 'access the first automatic variable
auto2=s(p-1) 'access the second automatic variable

Upon return, we have to clean off 3 values from the stack, the parameter and the 2 automatic variables:

p=p-3: return 'remove 2 automatics and 1 parameter

Readabilty and Anti-Bugging

Well, we started off trying to improve the anti-bugging capabilities of BASIC and ended up with something so complex that the implementation itself becomes bug prone. There is a simple way to get things back under control.

Assume that all subroutines will have the same number of variables and parameters. This may sound inefficient, but who cares about what the processor thinks anyway, the idea is to make things human friendly. All subroutine calls will then look the same and you'll quickly get used to accessing the parameters and automatic variables in a consistent fashion.

Return Values

With return values, we are still limited as before and has to return data in a global (unless the C calling convention is used, see below). However, a typical subroutine consumes information and has far more parameters than return values, so a single return value is usually sufficient.

Pascal vs C

The stack management process described here is used by the Pascal (Delphi) language. In this calling convention, every procedure has to clean up its own mess, before returning. In the C calling convention, the calling procedure has to clean up the stack after the return was executed, which is a bit more messy, but it allows the parent to access the stack of the child after the child expired, which can be a useful way to pass information back to the parent. Take your pick and stick to one or the other method.

Whatever you do, don't mix the two methods, or you'll get mighty confused.

With our do-it-yourself stack, it is possible for a child subroutine to access any automatic variable belonging to a parent subroutine. However, you should not do that. Rather pass the variable as a parameter when needed, since stack digging causes a strong link between the caller and the called, rendering the subroutine unuseable in another context.

Penalties

As always, there is no free lunch. The disadvantage of doing things this way, is a slight decrease in speed of execution, but the time saved during debugging and the far easier maintainability of the code makes up for it. Anyway, if you are worried about speed of execution, you should not be using BASIC, so that is essentially a non-issue.

Another problem is a loss of descriptiveness of the variables used in a subroutine. However, subroutines should be small and fit on one screen, so the code should still be easy to understand. This loss should therefore not be significant and can be offset by more judicious use of comments.

Compilers

After working through this, you should have appreciation for the work done by a typical compiler, to provide one with a bug proof code environment. This example also serves as a valuable aid in understanding how to interface assembly code with a high level language, a frequent requirement in embedded systems.

Have fun,

Herman Oosthuysen http://www.AerospaceSoftware.com

'---------------------------------------------------------------------
'Declarations
'---------------------------------------------------------------------
'Define the parameter stack and stack pointer
dim s(1024)
p = 0
 
'---------------------------------------------------------------------
'Name: main
'Description: Program entry point
'Constraints: none
'---------------------------------------------------------------------
[main]
print "BASIC with encapsulation and parameterized function calls!"
print
 
'call a subroutine with 2 static parameters and 1 return value
print "Static subroutine frame:"
p1 = 1: p2 = 2: gosub[teststatic]: result=r1
 
'display the returned value
print "result="; result
print
 
'Pass 2 dynamic parameters to the subroutine and return a single
value
print "Dynamic subroutine frame:"
s(p)=1: p=p+1: s(p)=2: p=p+1: gosub[testdynamic]: result=r1
 
print "result="; result
print
 
print "Have fun!"
 
end
 
'---------------------------------------------------------------------
'Name: teststatic
'Description: Test function with static local variables in a simple
' subroutine frame
'Inputs: p1, p2: test values
'Outputs: r1: test value
'---------------------------------------------------------------------
[teststatic]
'do something with the parameters - print them!
print "p1="; p1; " "; "p2="; p2
print
 
'return a value to the calling program
r1 = p1+p2: return
 
'---------------------------------------------------------------------
'Name: testdynamic
'Description: Test function with dynamic local variables in the
' subroutine stack frame.
'Inputs: p1, p2: test values
'Outputs: r1: test value
'---------------------------------------------------------------------
[testdynamic]
'make space on the stack for 2 automatic variables
p=p+2
 
'do something with the automatics
s(p-2) = 3
s(p-1) = s(p-2) + 7
 
'do something with the parameters - print them!
print"p1="; s(p-4); " "; "p2="; s(p-3)
print
 
'clean up the stack (2 automatics and 2 parameters) and return
r1=s(p-4)+s(p-3): p=p-4: return


Newsletter compiled and edited by: Brosco and Alyce.

Comments, requests or corrections: Hit 'REPLY' now!

mailto:brosc-@orac.net.au

or

mailto:awatso-@mail.wctc.net