FB II Compiler

PG PRO

Debugging

Memory

System

Mathematics

Resources

Disk I/O

Windows

Controls

Menus

Mouse

Keyboard

Text

Fonts

Drawing

Sound

Clipboard

Printing

Communication

ASM

Made with FB

FB II COMPILER

Advices and technics of programming



Food for thought
(Proviso: this is for lurkers, not for the usual posters for whom this is old hat - but all reactions welcomed)

When beginning to program we tend:
1/ to be disorganised
2/ to use glabals for everything
3/ not to use LOCAL MODE on FN's as we don't see how.

So this is a discussion on an aspect of, what has come to be known as 'bit-twiddling', to stimulate debate.

First off: why use LOCAL MODE on an FN? Reusability. If you write your FNs as local, with documentation on input and results, with all variables DIMmed, it is much easier to just grab it, reuse it in a another project, or pass it on to someone else.

An FN can give a result to say that it executed OK, or it can test something, this leads to lines like this:

IF( FN myDoSomethingGreat( doItOnThis) <> _noErr) THEN FN doOops

or

IF( FN doesTheDogWantToGoWalkies ) THEN FN goWalkies

We are getting back either a boolean (yes/no, true/false) or a range of values( 0= _noErr, 1 = _soAndSoError etc.)

But what happens when you need to return _more_than_one_ result? At this point, you will probably either pass a record, and change info in the passed record, or use a range of globals, which will decrease portability of your code.
Passing a record can be great (albeit confusing at first, but that's not the subject today); using a handful of globals, especially if the only time they're used is to carry back the results is bothersome, memory consuming and a bunch of other ms words.

So how to get round this? Lets examine our dumb FN

CLEAR LOCAL MODE
LOCAL FN doSomethingDumb( passedValue)
  DIM result%
' here something dumb is done
' and the value, or values will have
' to passed back.
...
END FN = result%

We tend to think of things around us as simple well defined blobs (car, house, train) and we carry this into programming.
What is result%? Why it's a variable for an integer number that can take 0-65535 different values, usually in the range -32768 to +32768, although with a bit of hassle we can get them to range from 0-65535. A bit-twiddler however will see them as 16 continous bit spaces in memory that can contain 16 different boolean states, but they can be even more than that!

Here goes:
Suppose that FN doSomethingDumb must return 2 pieces of information - a boolean and a value. Providing that the value is in the range 0-32768, you just calculate the return value and then use the sign to set the boolean. Coming back from the FN you would then test for these:

result% = FN doSomethingDumb( myValue%)
LONG IF( result% < 0)
  result% = ABS( result%)
  FN actOnResult( result%)
' these 2 lines can even be simplified to one:
' FN actOnResult( ABS( result%))
XELSE
  FN actOnResult( result%)
END IF

Second proposition you need to return 2 values, neither of which will exceed 256.
At the end of FN doSomethingDumb you have your 2 values, so it goes like this:

result% = myValue1%<<8 'effectively multiplying by 256
'or 'pushing' up 8 bit positions
result% = result% + myValue2%
END FN = result%

And when the values get back to the calling FN, you do the same thing but in reserve:

result% = FN doSomethingDumb( myValue%)
myValue1% = result% AND &FF00 'the will 'mask out' the value in the low bytes
myValue1% = result%>>8 'pushing back down by 8 bit positions
'Of course this operation can be simplified
'but that's is left to readers to try.
myValue2% = result% AND &00FF 'just grab lowest 8 bits for the second value

You can even combine boolean values and numeric values! In this case we have 2 numeric values 0-31 and a boolean state (true or false). Again, in the FN doSomethingDumb FN

DIM result%
DIM myBoolean1%
DIM myBoolean2%
DIM myNumber1%
DIM myNumber2%

'... do the stuff here
' now package for returning:
myBoolean1% = myBoolean1%<<5 'pushes up the value assuming that
'1=_true and 0=_false. If you use _zTrue
'then you must do an ABS(myBoolean1%) first.
myNumber1% = myBoolean1% + myNumber1%
myBoolean2% = myBoolean2%<<5 'same comments as above
myNumber2% = myBoolean2% + myNumber2%
result% = ( myNumber1%<<8) +myNumber2%
END FN = result%

Then you reverse the operation to extract the info. (Again, I'll leave this for you to try.)

Hope that this has given you some insights into using FNs in more creative ways, and th epower of thinking of memory as a line of shoeboxes that can be manipulated, rather than as fixed numerical values. Have fun, and I hope that others will post other 'fun' techniques.


jonathan

PS. this is typed from head, please check all code before using as is, and if you find an error or an ambiguity, please post.



OK, how 'bout this: Suppose I have a whole bunch of variables that are just one-bit toggles: on/off, true/false, yes/no. If I wanted to pare unused memory to an absolute minimum, could I pack them 16 at a time into a single integer variable and pick out the bit I want? For example:

IF monsterVar% AND 2^n THEN FN doMyStuff(n)

Lucy



Yes indeed. But it would be more efficient to do the comparison like this:

IF monsterVar% AND BIT(n) THEN FN doMyStuff(n)

because "2^n" invokes some nasty time-consuming exponential math calculations, while BIT(n) does not.

Rick


Yep, you've just discovered the immense fun of bit-twiddling Lucy ( a wonderful hobby for all the family :-), or I do it with constants:

_fred = &8000
_albert = &4000
_rene = &2000
_robert = &1000
_charles = &0800
'.. and so on
IF( gMonsterVar% AND _robert) THEN doItForLucy( _robert)

jonathan



Why not? That's what these toolbox calls are for:

CALL BITSET(bitPtr&,whichBit&)
CALL BITCLR(bitPtr&,whichBit&)
bitState% = FN BITTST(bitPtr&,whichBit&)

I think you will have to use a separate long var to hold the pointer, (myBitPtr& = @myBitFlags%) as I couldn't get CALL BITSET(@myBitFlags%,whichBit&) to work. I wonder if adding "#" might do it? CALL BITSET(#@myBitFlags%,whichBit&)

Happy twiddling,

Jay