41
- 1 - ThinBasic Journal 2/2008 your resource to keep up with development of passion powered interpreter Exploring features of thinBASIC 1.7.0.0 TBGL canvas Using CALLBACKs Programming in x86 ThinBasic Adventure Builder Entry function Matrix calculations Motion blur

EDITORIAL ThinBasic Journal

  • Upload
    others

  • View
    6

  • Download
    0

Embed Size (px)

Citation preview

Page 1: EDITORIAL ThinBasic Journal

- 1 -

EDITORIAL

ThinBasic Journal 2/2008

your resource to keep up with development of passion powered interpreter

Exploring features of

thinBASIC 1.7.0.0

TBGL canvas

Using CALLBACKs

Programming in x86

ThinBasic Adventure

Builder

Entry function

Matrix calculations

Motion blur

Page 2: EDITORIAL ThinBasic Journal

- 2 -

EDITORIAL

Welcome to the second issue,

ThinBasic 1.7.0.0 is ready and Journal cannot be left behind!

As new version represents major leap forward, this issue will introduce you to the most

important additions. Callbacks, matrices, TBGL control, we have it all covered for your

convenience. But not only new features are things of interest for true ThinBasic programmer,

so we come with more.

If you always wanted to understand “that assembler thing” to produce incredibly fast code,

you can jump after the article opening the series of assembly programming basics, written by

author of Oxygen module.

ThinBasic has big advantage in being modular, so what about first article about developing

modules? Text covers even some interesting details about how ThinBasic works inside, so

definitely worth reading.

Sure that is not all, we got article on creating motion blur, TBGL effect prototyped using real

world model scene. It is simple, it is fast and it comes with source code, hope you will like it.

New section introduces you to world of most complex ThinBasic script – ThinBasic

Adventure Builder, again written by the author himself.

We should not forget last part of the magazine, “Bits and pieces” demonstrating some useful

tips for your next script.

It is very nice to watch the gang of authors for this issue is bigger than two man show last

time. But don’t be mistaken, it does not mean we don’t want even more contributors! If you

have anything to submit for next issue, wait no longer and send your article to our new email

box at [email protected].

Enough talking, we hope you will enjoy this issue!

Authors

Page 3: EDITORIAL ThinBasic Journal

- 3 -

CONTENTS

In this issue

Second issue of ThinBasic journal offers following materials for study.

New in core

Introduction of entry function ................................................................................................ 4

New functions to master strings ............................................................................................. 6

New in modules

Callbacks – new approach for UI programming .................................................................... 8

New Oxygen features ........................................................................................................... 11

Matrix calculus using Math module ..................................................................................... 17

TBGL and UI cooperation for rendering to dialog canvas ................................................... 20

Introducing FileLine module ................................................................................................ 23

Articles

Programming in x86 Assembly code ................................................................................... 24

Faking motion blur with fixed pipeline ................................................................................ 27

Insider

ThinBasic inside ................................................................................................................... 32

Proggies

Understanding adventure coding in ThinBASIC Adventure Builder .................................. 39

Bits and pieces

Alias keyword ...................................................................................................................... 41

Casting data type .................................................................................................................. 41

Page 4: EDITORIAL ThinBasic Journal

- 4 -

NEW IN CORE

New in core

New ThinBasic brings some very

important enhancements – again in

multiple areas.

Big utility value is represented by

new string handling functions –

Patch$, Grab$, Digit$ and others.

They will be presented to you in

separate article.

Other very interesting addition is

concept of entry function. Entry

function is one of the approaches

which might help you to organize

program code better. Let’s have a

look at it first.

Introduction of entry function

Petr Schreiber

As you will notice in text about UI module too, new ThinBasic

made an interesting step towards better modularization of

source code.

Beginning with version 1.7, you can start writing the code into

so called entry function.

It is function invoked only once during the script execution,

and takes similar role as main function in C language for

example.

Entry function is named TBMAIN by default, but if you dislike

this fact, you can use APP_SetEntryPoint to specify your own

function. You can retrieve entry function name using

APP_GetEntryPoint as well.

This change to the way scripts are written is completely

optional, so if you don’t see any advantage for your project

(for example in quick scripts in few lines), you are not

obligated to use this approach.

For bigger scripts where code aesthetics matter, it is

recommended to use concept of entry point.

Order of parsing

After initial pre parsing stage ThinBasic interpreted script in

following order:

Global space

User functions, if called

With concept of TBMAIN function, the order is modified to:

Global space

TBMAIN, or other specified entry function

User functions, if called

From this scheme it is evident you will most probably use

global space just for module specification, global variables and

global constants declaration, including header files or declaring

API access generally. The main application code will be now

encapsulated in TBMAIN function. TBMAIN can handle just its

own data necessary and call user functions.

Important difference from other user defined function is in

fact, that after processing all lines in TBMAIN program ends.

Entry point function also does not take any parameters.

Page 5: EDITORIAL ThinBasic Journal

- 5 -

NEW IN CORE

The most typical task for TBMAIN is

retrieving initial input data or definition of

main application dialog.

Possible use of entry function

I would recommend using entry point

function in case of bigger applications

generally. Code is a bit better organised this

way, you always know where you begin.

There are also other cases as well, for

example console utilities. Imagine you write

a simple tool to encrypt or decrypt file. It

would take as parameter whether to decrypt

or encrypt file and the file itself. So here are

possible uses:

C:\myTool encrypt file.txt

C:\myTool decrypt file.txt

To make your code simply organised, you

can develop two programs represented by

function, one handling encryption, and other

decryption. Once the script is executed, you

can decide which main function will be

executed based on first parameter of

program.

Did you know?

Different programming

languages take different

approach on main function.

Some do not feature it, some

make it obligatory, and only

few allow hybrid approach with

possibility to choose which

function becomes main.

Page 6: EDITORIAL ThinBasic Journal

- 6 -

NEW IN CORE

New functions to master strings

Petr Schreiber

Comfortable string handling is spice of every

good BASIC, ThinBasic makes no

exception.

Set of string commands has been expanded

in latest version with Patch$, Grab$,

Letter$, Digit$, Remain$ and StrFormat$.

Following text will introduce you to their

use.

Patch$

This function serves to replace contents of

special part of passed string. It operates on

string according to passed pair of delimiters,

delimiting tag.

Consider you need to replace wrong text in

second parenthesis with “elephant seal”:

myString =“There is lot of relatively small

animals in Antarctica ( such as penguins ),

but there are some big ones as well ( krill ).”

This is very easy to accomplish with Patch$.

We can imagine tag in this case as any text

in brackets. Code you would use would look

like:

myString = PATCH$( myString, _

“(”, “)”, _

2, _

“elephant seal”)

As you can see, the syntax is quite simple,

first parameter is string we work with,

second and third are matching delimiters,

fourth parameter specifies which parenthesis

to replace and finally the last parameter

contains string to replace text in brackets

with.

As the delimiters are user defined you can

customize Patch$ to work on elements of

HTML code, or other cases where something

like tag is used.

Grab$

This function is complementary one to

Patch$. It allows retrieving text from

specified nth

tag. Consider you have a web

page, and you want to list all alternative

descriptions for images which this web page

contains.

HTML syntax for alternative text looks like

this:

alt=”Nice picture of Yetti”

So you need to retrieve anything between

alt=” and “. This is very easy now. Let’s

presume you have HTML in string named

htmlSource and you want to output any

hyperlinks found to console.

Then following brief script can do the job for

you.

USES "Console"

' -- Page source from clipboard

DIM htmlSource AS STRING = _

TRIMFULL$(CLIPBOARD_GETTEXT)

DIM altText AS STRING

DIM counter AS LONG

' -- Seek hyperlinks

counter = 0

DO

INCR counter

' -- GRAB$ extracts hyperlink

altText = GRAB$(htmlSource, _

"alt="+$DQ, $DQ, counter)

' -- If nothing found then end

IF LEN(altText) = 0 THEN EXIT DO

' -- Add link to our list

PRINTL altText

LOOP

' -- Wait for user key press

WAITKEY

Page 7: EDITORIAL ThinBasic Journal

- 7 -

NEW IN CORE

Remain$

This function represents nice complement to

already existing Extract$. While Extract$

returns part of the string up to the first

occurrence of passed match string, Remain$

does the opposite by returning text from the

end of specified match string till end.

Following code will write “It is nice

program” and then “ it runs fast”.

USES “Console”

DIM s AS STRING = _

“It is nice program, it runs fast”

PRINTL EXTRACT$(s, “,”)

PRINTL REMAIN$(s, “,”)

WAITKEY

This kind of commands is useful in case you

do not know the position of match character.

In other case you might consider Left$ and

Right$ for example.

But Extract$ and Remain$ goes a bit more

far, as they allow to optionally setup position

from which their effect starts as well as

specify whether to ignore or not the

character case.

Digit$ and Letter$

These two functions allow you to retain just

specific characters from passed string. The

first one, Digit$, returns just numeric digits

from the string. Following example will

return string “32”.

s = DIGIT$("We have 32 whales”)

On the other side following will return just

letters, so “Wehavewhales”.

s = LETTER$("We have 32 whales”)

You might ask which characters are

considered letters and which digits, and if it

can be changed.

The answer to first question are functions

Digit_GetMask$ and Letter_GetMask$.

And if you want to change the characters in

mask, you might use Digit_SetMask$ and

Letter_SetMask$.

So imagine you want to use Digit$ to

retrieve digits of hexadecimal number.

Hexadecimal numbers are represented using

symbols 0-9 and A-F. You can extend the

mask and use the Digit$ to do the job in

following way.

DIGIT_SetMask$(“0123456789ABCDEF”)

s = DIGIT$(“Value is 1A”)

As mask contains all the necessary

characters, the code above will store “1A”

correctly to the string variable s.

StrFormat$

The last added function comes handy in

situations when you need to present results

of your calculation in text form. While STR$

and FORMAT$ do their job well, you can

build string now without any concatenations.

The code you would do in older releases as

following: STR$(x)+”,”+STR$(y)+”,”+STR$(z)

You can do now like this:

StrFormat$(“{1}, {2}, {3}”, x, y, z)

The numbers in curly brackets contain order

of parameter to be printed. The best part of it

is that you don’t have to point to parameters

linearly; you can reference 3rd

parameter first

and so on.

Page 8: EDITORIAL ThinBasic Journal

- 8 -

NEW IN MODULES

New in modules

Many modules have been improved

since last release.

User Interface brings tweaks to

many of its commands, but most

importantly it adds support for

callback functions. New version

means big jump forward from both

code maintainability and speed

point of view.

Oxygen, ThinBasic secret killer

weapon, has been incredibly

expanded as well – do you want to

code OOP with assembler, do you

want to compile own ThinBasic

module with Oxygen? No problem

now.

Math module provides very useful

set of commands to handle matrix

calculations. You can use array as

matrix now, and perform all the

common operations with it. Not just

a toy for mathematicians, matrix

calculations have very wide

practical use.

TBGL, module for 3D graphics

adds possibility to render to dialog

control, which opens new range of

possibilities to editor and TBGL

support tools developers.

And then we have here new

FileLine module – how it will

simplify your next job can be

studied in separate article.

Callbacks – new approach for UI programming

Petr Schreiber

UI module in ThinBasic provides interface for creating dialogs

with controls for long time. Till recent version, the only

supported type of dialog was modeless, which in older

versions meant you could create dialog and continue with

script to handle its events.

In most cases you just put loop processing messages in code

after displaying such a dialog. This way is flexible, allows

handling of all possible Windows messages and events

occurring to dialog.

But - when your script uses such a dialog, ThinBasic parser

processes the lines of message pump loop again and again,

until reaction to some event allows exiting this loop and

closing the window.

This way possibly leads to loss of some script performance

because of this message pump overhead, which is increasing

with growing number of dialogs used in application.

In most cases this approach does not cause any observable

trouble, but it is always better to go faster, isn’t it?

So what is callback?

Every dialog, every control in Windows receives messages.

Now imagine you do not need any kind of (almost) never-

ending loop in your script. With new ThinBasic, you can

handle events using your own callback function, which is fired

automatically only when some message arrives.

In ThinBasic 1.7, you can specify two types of callbacks – for

dialog, and for controls. You can specify both, or just one of

them. Of course, you don’t have to use them, but I think you

won’t go back to continuous loops, let me explain a bit more.

Callback for

Cancel button Callback

for

dialog

Callback for

Submit button

Page 9: EDITORIAL ThinBasic Journal

- 9 -

NEW IN MODULES

Dialog callback

This kind of function comes handy with both

modal and modeless dialogs.

When you create modal dialog, the execution

of script is halted until destruction of the

dialog.

Modeless dialog is type present in ThinBasic

till now, it means you create dialog and

continue with script evaluation.

Dialog callback serves for very similar

purpose as the internals of previous looping

approach served in this case.

Let’s compare the old message loop and new

callback approach on code example.

Old style message loop looks like the

following:

DIALOG SHOW MODELESS hDlg

WHILE ISWINDOW(hDlg)

' -- Get dialog messages

Msg = GETMESSAGE(hDlg, _

wParam, lParam)

SELECT CASE Msg

CASE %WM_INITDIALOG

' -- Some initial setup here

CASE %WM_Command

' -- Handle controls

Ctrl = LoWrd(wParam)

CtrlMsg = HiWrd(wParam)

SELECT CASE Ctrl

' -- Processing controls

END SELECT

CASE %WM_SYSCOMMAND

If wParam = %SC_Close Then

' -- Last action before end

EXIT WHILE

END IF

END SELECT

WEND

As you can see, you simply created dialog,

and then script checked messages coming to

dialog in loop, which ended only when

requested.

You can note you had to do following

things:

check if dialog exists with IsWindow

obtain messages using GetMessage

keep looping

While this approach is usable, it keeps

ThinBasic busy parsing the loop. The

callback approach works slightly differently.

Let’s see the same functionality

implemented using callback function.

DIALOG SHOW MODAL hDlg, _

CALL dlgCallback()

CALLBACK FUNCTION dlgCallback()

SELECT CASE CBMSG

CASE %WM_INITDIALOG

„ – Some initial setup here

CASE %WM_COMMAND

„ – Handling controls

SELECT CASE CBCTL

' -- Processing controls

END SELECT

CASE %WM_CLOSE

„ – Last action before end

END SELECT

END FUNCTION

Using this function, you can handle events

for dialog as such, but you can take care of

its controls as well. So the functionality is

the same as for old approach, but code is

shorter for example.

But there are more differences, let’s have a

look at them.

Page 10: EDITORIAL ThinBasic Journal

- 10 -

NEW IN MODULES

What is different from the old approach is:

introduction of read only variables

related to messages

callback function is executed only if

something happens to dialog or its

controls

The mentioned read only variables always

begin with prefix CB, like callback. They

help you to recognize what happened to

dialog and provide all functionality

GetMessage processing offered and a bit

more.

CBHNDL returns handle of dialog to which

the event occurred (caller).

CBMSG returns code of message.

CBWPARAM returns the wParam parameter

of message received.

CBLPARAM returns the lParam parameter

of message received.

CBCTL returns id of control.

CBCTLMSG returns message sent to

control.

Another difference comparing to the old way

is fact you can use the same dialog callback

for multiple dialogs, variables above will be

filled with appropriate handle and other

things automatically. This is very good sign

for reusable code. And it is fast, very fast.

Very nice feature attached to this approach is

no need for keeping dialog handle as global

variable – CBHNDL always return the

handle of caller dialog.

Control callback

Well, with dialog callbacks we got some

nice new possibilities and better code

comfort, but fact is that handling control

events in dialog callback, although possible,

could still result in not very easy to maintain

nested tests for control events. Let’s let

dialog callback care just about the dialog.

Controls are complex things, so they deserve

their very own callback functions. The

definition of callback is as easy as adding

command after control parameters.

CONTROL ADD BUTTON, hDlg, %btnOn, _

"On" , 520, 5, 100, 30,,, _

CALL ctrlCallback

Similarly to dialog callbacks, multiple

controls can share the same callback as well.

To be able to differentiate between controls,

each control callback again contains all the

mentioned read only variables. That means

CBCTL returns called control id in this case

and CBCTLMSG the message which

arrived. In control callback, those variables

will return data related only to caller control,

so it filters the right messages for you.

You can use one control callback for more

controls if you like, you can then use code

similar to the following.

CALLBACK FUNCTION ctrlButtons()

IF CBMSG = %WM_COMMAND THEN

„ – Get control id

SELECT CASE CBCTL

CASE %btnOpen

IF CBCTLMSG = %BN_CLICKED THEN

„ – Do something

END IF

CASE %btnClose

IF CBCTLMSG = %BN_CLICKED THEN

„ – Do something else

END IF

END SELECT

END IF

END CALLBACK

As you can see, new callback approach

brings new blood to user interface handling –

more speed, more transparent code and lot of

things done automatically for you. Do you

still want to use old approach?

Page 11: EDITORIAL ThinBasic Journal

- 11 -

NEW IN MODULES

New Oxygen features

Charles Pegge

With the wettest Summer on record - our

western seaboard has become the favourite

tourist spot for all the rain-clouds in the

North Atlantic. As the wind roars through

the trees, rain lashes the windows and gutters

overflow; these are ideal conditions for

software development - in front of a nice

warm computer.

So Oxygen has come quite a bit further in

the last month or so. I have taken the radical

step of introducing OOP infrastructure into

the assembler. Now you would think that

Object Oriented programming is a genre that

should be confined to high level

programming. But it turns out that assembler

can produce very elegant OOP with a little

help from the preprocessor, avoiding much

of the clutter that occurs when OOP is

attempted in languages which are not

adapted to it.

OOP is the most recent , but there has been

another major development - Assembling to

sand-alone EXE and DLL files directly from

thinBasic without a linker or any other

intermediaries. It is also possible to generate

functions in Oxygen and call them directly

from thinBasic as though they were from an

external DLL. But these are subjects for

another article.

We can start with some of the minor

features.

Defs

Defs is an efficient way to consolidate

families of equates, using dotted names. For

example:

defs key a 97 b 98 c 99 d 100 ...

These can then be referred to as key.a key.b

key.c etc.

With

The with instruction is used to avoid

repeating long names when doing multiple

assignments.

with myobject.material.color

.red=.5

.green=.4

.blue=.6

.alpha=.9

with ``

with can optionally use quotes.

I have not made with a block structure. It

simply holds the prefix until a new one is

given or until it is nulled. This gives it

considerable flexibility. With can take a

whole name or part of a name involved in

the assignment - anything you wish to avoid

repeating. Quotes are optional but allow the

possibility of prefixing several words.

This is some basic code for testing these new

features and showing how the syntax words:

Unions

type tvec 4 x 4 y 4 z 4 w = 4 red

4 green 4 blue 4 alpha

Any number of unions are possible in a type

statement. Here is another in block form

type tvec4

(

4 x 4 y 4 z 4 w

=

4 red 4 green 4 blue 4 alpha

=

4 cyan 4 magenta 4 yellow 4 alpha

=

16 v

)

Page 12: EDITORIAL ThinBasic Journal

- 12 -

NEW IN MODULES

Assignments

Most programming involves moving values

from one variable to another - so building

the assignment operator in the Assembler

can greatly decrease the line count. Though

the '=' sign still has a restricted meaning, its

functionality has been extended to include

most 32 bit assignments via the eax register.

a=b

is translated as:

mov eax,b

mov a,eax

String literals are represented by their

starting address, so it is possible to say

var 4 a

a=`hello world`

a can then be used directly many SDK calls

which take a string pointer. String literals are

always terminated with 2 nulls.

Addresses

c=&d

this is the equivalent of ThinBasic

c=varptr(d)

Indirection

**a=*b

the PowerBasic equivalent of this is

@@a=@b

New Macro Encodings

%l Line break - you can now write a def on

a single line that expands into several lines

of code.

%h Ascii character (in 2 digit hexadecimal

for example: %h82 for 'é'.

Object Oriented Programming

Unlike previous models this does not rely on

user defined macros. It uses virtual tables

and pointers in a similar manner to COM.

OOP is built into the Asmosphere

preprocessor, taking care of all the tricky

bits.

This model also supports multiple

inheritance - i.e. a class can be derived from

several ancestors.

The extra notation required is minimal. In

fact the words class and object are not used

at all. The system is an extension of type.

To declare a class, the type statement is

divided into 2 parts: The first part contains

methods, inherited structures and class static

members. The second part contains the set of

properties assigned to each object, (inherited

properties are automatically appended)

This is a class defined in a single line: !

denotes a method. Slash / divides the two

parts of the class, and inherited types must

be followed by a comma as usual.

type ClassAB

(

classA, ; inherited

class

classB, ; inherited

class

4 methodA! ; method pointer

added for this class (note the !

suffix)

4 methodB! ; another

4 fstatic ; a static

class member ( no ! suffix)

Page 13: EDITORIAL ThinBasic Journal

- 13 -

NEW IN MODULES

; this becomes accessible as

'ClassAB_table.fstatic'

; object members:

/

; pointer to the

; virtual function table

; (always present)

4 pvft

4 va ; a property

4 vb ; another

property

4 vc ; ...

)

this is immediately followed by a scoped

class builder and a set of methods

(

build classAB

exit

var ClassAB this

methodA:

...

ret 4

methodB:

...

ret 4

...

)

to create an object:

var ClassAB myObject

myObject.vft=&ClassAB_table

which gives us an empty object except for its

virtual table pointer, which allows the object

to call any of its methods, (including

inherited methods)

edx=myObject.methodA ...

Because all the methods are defined inside

the brackets, they are not visible to the rest

of the program.

Below is a simple piece used to test the basic

functionality of the model.

indexers `esi` offset 0 ascending

esi=dataspace 0x100

type ClassA

(

4 a1!

4 a2!

4 fstatic

/

4 pvft

4 pep

4 pip

)

(

build ClassA

exit

var classA this

a1:

this=&ClassA_Table

ret 4

a2:

this=&ClassA_Table

inc this.pip

ret 4

)

type ClassB

(

4 b1!

4 b2!

/

4 pvft

4 pep

4 pip

)

(

build ClassB

exit

var classB this

b1:

this=&ClassB_Table

mov eax,66

ret 4

b2:

this=&ClassB_Table

inc this.pip

ret 4

)

type classAB

(

classA,

4 fna!

4 fnb!

4 fnc!

4 fstatic

classB,

/

4 pvft

4 aa

Page 14: EDITORIAL ThinBasic Journal

- 14 -

NEW IN MODULES

4 bb

)

(

build classAB

exit

var ClassAB this

o2 /+4

fna:

this=&ClassAB_Table

inc this.aa

[#vv]=sizeof ClassAB

ret 4

fnb:

this=&ClassAB_Table

ret 4

fnc:

this=&ClassAB_Table

ret 4

)

var classAB myobject

myobject=&ClassAB_table

; test method fna

ecx=myobject.fna

; test method fnb

ecx=myobject.fnb

; test inherited method

[#vv]=myobject.b1

ret

The invisible work done by the system takes

care of constructing tables of function

pointers (virtual functions) and organising

the elements of inherited structures in

general. This was really too complicated to

handle in macros so it made sense to

internalise it.

Method Overloading

Extending the OOP facilities, it is now

possible to deploy several versions of the

same method, each taking different

parameters. During assembly, the

preprocessor checks the param signature

against the function signatures for a match.

If no match is found then an error will

eventually be flagged by the linker.

This mechanism also ensures that the correct

param types are used with all methods,

whether there are multiple versions of the

method or not.

These are examples of method signatures:

(commas are ignored)

myfunc: ( a ) ...

myfunc: ( a,a ) ...

myfunc: ( a, a, a ) ...

myfunc: ( a a a ) ..

myfunc: ( long long ) ..

myfun: ( long double)

...

ret 16

In the body of the type declaration, methods

have a pling postfix so the format is:

myfunc! (long long)

myfunc! (long double)

Here comes real example of overloading:

; signatures

; with

;

indexers `esi` offset 0 ascending

esi=dataspace 0x100

var 4 ab

type a 4 v

var a abc

type ClassA 4 a1! 4 a2! 4 fstaticA

/ 4 pvft 4 pp1 4 pp2

(

build ClassA

exit

var classA this

a1:

this=&ClassA_Table

ret 4

a2:

this=&ClassA_Table

inc this.pp1

ret 4

)

type ClassB 4 b1! (a a) 4 b2! / 4

pvft 4 pp1 4 pp2

(

build ClassB

exit

var classB this

b1: (a a)

this=&ClassB_Table

mov eax,66

Page 15: EDITORIAL ThinBasic Journal

- 15 -

NEW IN MODULES

ret 12

b2:

this=&ClassB_Table

inc this.pp1

ret 4

)

type classAB

(

classA ova

classB,

4 fna!

4 fnb! ()

4 fnc! ()

4 fnc! ( a )

4 fnc! ( a a )

4 fnc! ( a a a )

4 fstatic

/

4 pvft

4 aa

4 bb

)

(

build classAB

exit

var ClassAB this

fna:

this=&ClassAB_Table

inc this.aa

[#vv]=sizeof ClassAB

ret 4

fnb:()

this=&ClassAB_Table

ret 4

fnc: ()

[#vv]=-5

ret 4

fnc: (a)

[#vv]=10

ret 8

fnc: (a,a)

[#vv]=2

ret 12

fnc: (a,a,a)

this=&ClassAB_Table

[#vv]=42

ret 16

)

var classAB myobject

myobject=&ClassAB_table

; test method fna

edi=myobject.fna

; test method fnb

edi=myobject.fnb

; test method fnc with sig 3

edi=myobject.fnc abc 1 2

; test method fnc with sig 2

edi=myobject.fnc 1 1

; test method fnc with sig 1

edi=myobject.fnc abc

; test method fnc with sig 0

edi=myobject.fnc

; test inherited method

[#vv]=myobject.b1 1 1

; class static

classA_table.fstaticA =1

; class static

classAB_table.fstatic =1

; class static inherited

classAB_table.ova.fstaticA=1

; own property

myobject.aa=3

; inherited property

myobject.pp1=2

; inherited property with extended

; name

myobject.ova.pp1=2

; same as above using WITH prefix

; alternative ..

with myobject.ova

; same as above using WITH quoted

prefix

with `myobject.ova`

.pp1=2

; clear WITH

; alternative ..

with

; clear WITH by empty quotes

with ``

ret

Page 16: EDITORIAL ThinBasic Journal

- 16 -

NEW IN MODULES

Adaptations for COM programmming

The quest is to produce tidy COM in

Assembler using the Oxygen OOP model.

This is really more of a versatility test than a

proper implementation of COM at this stage.

One of the little problems that arose when I

tried Oxygen's OOP model with COM is that

multiple inheritance proved to be a

mismatch, so I introduced a way of

indicating single inheritance using the

keyword of.

type IUnknown

(

4 QueryInterface (a p)

4 AddRef ()

4 Release()

4 Counter

/

4 pvft

)

type IKnown

(

of IUnknown,

/

4 test_interface

4 other_interface

)

To help with virtual structures and

pointering, a variable can now be associated

with a specific register and have a structure

associated with it. Example:

var IUnknown this [ecx]

...

mov ecx,[esp+4]

...

this.addref

...

The provided sample code1 does very little

other than to test the COM structures and

pointering are performing correctly. An

1 SampleScripts/Oxygen/test_COM.tBasic

interface is switched by calling

QueryInterface, with one of the GUIDs. A

pointer to the new interface itest is granted.

This interface is adopted then released.

Private Members

These are elements which are invisible to

derived classes - but still exist in the object.

So they are only accessible using methods

inherited from the ancestral class.

Any element can be made private by

appending a dash (minus sign) after the

element definition.

Abstract Members

These are denoted by any element that has a

size of zero!

Derived types must provide matching real

elements to replace these before they can be

used to instantiate objects. They specify the

general structure of a type before getting

down to specific implementations.

These I hope complete the Oxygen OOP

facilities. But there is much to explore and so

many different combinations.

What's Next...

The next step for Oxygen will be an internal

one. So far, the largest program has been

about 61k of source code and the test suite is

about 200k. Oxygen needs to undergo 'case-

hardening' to ensure that it can handle much

larger programs and catch all the obvious

errors that might occur during project

development. One idea is to develop a

'CodeBlaster' to generate large volumes of

source code, and see what gets past the error

checking. This can be highly automated in

thinBasic - and largely unbiased as to what

kind of code is generated. Some evolutionary

feedback in the system, will steer it towards

producing plausible code.

Page 17: EDITORIAL ThinBasic Journal

- 17 -

NEW IN MODULES

Matrix calculus using Math module

Petr Schreiber

One of the most interesting things which you

learned in you math class (or maybe it still

awaits you) is the concept of matrix.

Although this matrix is not the one making

you look cool in dark glasses, it is still very

powerful thing thanks to versatility of its

possible uses.

This article will use the word matrix in

meaning of 2D matrix.

What is the matrix?

You can imagine matrix as organized set of

numbers. In case of most common 2D

matrices we can see numbers are organized

in rows and columns.

Each member of matrix is identified by

index, let’s see how on following picture

describing matrix 3 by 3.

You can observe that first index of element

represents row the number is placed in and

second number the column.

Matrix filled with numbers can look like

this:

To represent matrix in ThinBasic, you can

use array. To create matrix like the above

you can simply use array commands.

DIM myMatrix(3, 3) AS EXT

myMatrix(1, 1) = 1, 2, 3

myMatrix(2, 1) = 4, 5, 6

myMatrix(3, 1) = 7, 8, 9

How math module supports matrices?

Math module in ThinBasic now brings

mechanisms to perform operations with

matrices using very simple notation of mat

statement.

You can sum, subtract or multiply matrices

using the appropriate operator.

MAT A() = B() + C()

MAT A() = B() – C()

MAT A() = B() * C()

Division of matrices isn’t usually done using

slash symbol in math, and it is defined as

following.

A = B C-1

If you do not remember, that “-1” means

inverted matrix.

Matrix inversion is supported by math

module as well, using INV keyword. So to

divide matrices B and C to produce matrix

A, you would use following notation.

MAT C() = INV(C())

MAT A() = B() * C()

That is not all; you can similarly transpose

matrices as well.

MAT A() = TRN(B())

Math module helps you with matrix filling as

well. You can make matrix identity (all

zeros, just on the diagonal with number one).

MAT A() = IDN

You can also set all elements of matrix to

specific number. To zero whole matrix, you

can pick ZER keyword.

MAT A() = ZER

Page 18: EDITORIAL ThinBasic Journal

- 18 -

NEW IN MODULES

To fill matrix with number or result of

expression, you can use more versatile CON

keyword as well.

MAT A() = CON(3.14)

But what is all this good for?

As I said in the introduction, matrices can be

used to achieve wide range of tasks.

You can find example of practical use of

matrix in every math book, let’s see some

real world example now.

Something like being trapped by evil

magicianTM

occurs to most of us once or

twice a year.

You know for sure how it goes in such a

case, but for those lucky to be not captured

by evil magicianTM

yet, let’s see how it

usually goes.

Magician: Earthworm!

You: Me?

Magician: Yes!

You: Uh!

Magician: Solve following problem or I will

change you to something nasty!

“There is garden, where they have apples,

oranges and bananas. I, the mighty warlock,

want to keep in shape so I always take some

fruits to keep me fresh. To maximize the

refreshing effect, I must eat 23 fruits in total.

I always eat one orange less than bananas

and twice as many apples as bananas. How

many fruits of each kind I eat to keep me

looking as superb as I do look now?

This kind of task can be solved using brute

force algorithm (which we do not consider

elegant enough) or as a system of equations.

You might remember systems of equations

can be solved using matrices. And ThinBasic

supports matrix math. So we can make

magician angry just by using PC with

ThinBasic installed.

If you rewrite task rules into equations, using

symbol “a” for apples, “o” for oranges and

“b” for bananas, you get following system:

a + o + b = 23

o = b – 1

a = 2 * b

Once you rewrite this system to have just

unknowns on one left and the rest on the

right side:

1a + 1o + 1b = 23

0a + 1o - 1b = –1

1a + 0o - 2b = 0

...you can use power of matrices to solve this

system in no time. Matrix approach to solve

system of linear equations is following:

Ax = b

x = A-1 b

So you just create one matrix for left side

unknowns coefficients (A), one for right side

numbers (b) and one for the result (x).

DIM A(3, 3) AS EXT

DIM b(3, 1) AS EXT

DIM x(3, 1) AS EXT

A(1,1) = 1, 1, 1

A(2,1) = 0, 1,-1

A(3,1) = 1, 0,-2

b(1,1) = 23

b(2,1) = -1

b(3,1) = 0

To re-implement math formula we need to

do just two basic steps. The first is to

calculate inverse matrix of A:

MAT A() = INV( A() )

Then we can finally calculate the unknowns

using following statement.

Page 19: EDITORIAL ThinBasic Journal

- 19 -

NEW IN MODULES

MAT x() = A() * b()

Using two mat statements we just solved evil

magician’s task. Matrix array x now contains

result – 12 oranges, 5 apples and 6 bananas.

What is even better, this two lines of code

will serve us to solve any properly defined

system of equations – 2, 5 or 1000

unknowns, matrices will do the job for us in

no time.

Sure thing such a trivial task I demonstrated

can be solved using pen and paper in

acceptable time as well. But imagine more

complex equations, where making a mistake

thanks to inattention is much more probable.

In such a cases matrix statements are

something you can simply rely on.

Page 20: EDITORIAL ThinBasic Journal

- 20 -

NEW IN MODULES

TBGL and UI cooperation for

rendering to dialog canvas

Petr Schreiber

TBGL module provided support for

hardware accelerated graphic rendering for

long time; it was used in multiple games and

graphic demos.

All rendering was made into separate

window, which could be optionally created

in full screen mode.

In case you needed to create tools like

editors, requiring some controls, you had

only two options:

Render GUI using TBGL commands

Use TBGL window and separate

modeless dialog to hold the controls

Both approaches are usable, but have their

issues. First mentioned custom GUI

rendering has disadvantage of eating part of

rendering performance for drawing of

controls on screen, the second approach was

more usable, but definitely not standard in

world of Windows programs.

TBGL control canvas basics

TBGL 0.2.2 preview, bundled with current

ThinBasic, adds one more powerful feature

to your arsenal – possibility to render

graphics inside standard dialog created using

UI module, which represents the best way to

handle tools like editors or model viewers.

To maintain all perfect features UI control

handling provides, such as automatic

resizing, TBGL canvas for drawing is

implemented on following principle.

Create control which will serve as

canvas for TBGL drawing

Bind this control to TBGL system

If control changes update graphics

proportions

If it is no longer needed to render to

dialog, release the control from

rendering

This means that you can design dialog layout

using UI commands as you are used to.

Including styles, dimensioning, resize

anchors and others.

3D graphics in dialog

To be able to render to such a control, you

will need its handle. This is identifier, which

can be retrieved both during control creation,

or any time later using control handle UI

command.

hCtrl = CONTROL ADD LABEL ...

Once the handle is obtained, you can bind it

for rendering, using TBGL command.

TBGL_BindCanvas( hCtrl )

To check whether control is bound to

rendering or not, you can use

TBGL_CanvasBound( hCtrl )

Once the control is bound, you can

immediately render to it, create entity

systems ... simply work the same way you

are used to work with TBGL window.

If you no longer need to render to control

canvas, simply call

Page 21: EDITORIAL ThinBasic Journal

- 21 -

NEW IN MODULES

TBGL_ReleaseCanvas( hCtrl )

Attention - unlike TBGL_DestroyWindow

you know from older module revisions,

TBGL_ReleaseCanvas does not destroy the

control it hijacked for rendering, it simply

stops drawing to it and deallocates all

resources created during the work with the

control. Similarly to TBGL window, it is

possible to have only one TBGL canvas

inside dialog, but you can still define

multiple viewports inside the rendering area

for multiple views of same or even different

scene.

As you can see, all canvas commands have

control handle as parameter, which means

first step to multiple canvas support in some

of following TBGL module revisions.

Best practices for using canvas

When designing computer games in

ThinBasic, we usually follow the rule of

“more frames per second, better”, because it

is presumed you play the game and do not

run any other programs, therefore you try to

use all horsepower of hardware you run on,

just placing doevents to main loop to let PC

rest a bit.

In case of windowed applications, especially

various editors and viewers, we should think

a bit differently. It is highly probable user of

our program will have other tasks running as

well, like graphic editor for example.

To not hog PC too much, in most

applications it will be needed to render

image only in following situations:

Window is being repainted

Control is being sized

That basically means handling WM_PAINT,

WM_SIZE and WM_SIZING in dialog

callback. After receiving WM_PAINT, your

program will just react with calling the

procedure handling repainting.

In case of WM_SIZE and WM_SIZING, you

will need to call

TBGL_UpdateCanvasProportions _

( hCtrl )

As TBGL canvas control will be resized or

repainted in case the same occurs to its

parent dialog, you can handle all those

events in dialog callback2.

It is recommended to set colour of control

you will use for rendering to equivalent of

TBGL_BackColor used in your program, for

better results during resizing.

This could look like in following example.

CALLBACK FUNCTION dlgCallback()

SELECT CASE CBMSG

CASE %WM_PAINT

RenderMyImage()

CASE %WM_SIZE, %WM_SIZING

TBGL_UpdateCanvasProportions_

(hCtrl)

RenderMyImage()

END SELECT

END FUNCTION

Of course, you are not limited to events

mentioned, in case you for example drag

something on TBGL canvas, you can update

canvas more frequently.

When we need continuous refresh

There are cases you do not need refresh

canvas once in a while, but continuously –

for purpose of animation for example.

In such a case, if you still want to design

CPU friendly application, you can take

advantage of timers.

2 If you are not familiar with term „dialog callback“,

please see the article Callbacks – new approach for

UI programming

Page 22: EDITORIAL ThinBasic Journal

- 22 -

NEW IN MODULES

You can imagine timer as message emitter,

which sends WM_TIMER event to dialog in

specified discrete moments. So once timer

ticks, you can call the rendering functions.

Even lot of times per second, it will still

mean low use of CPU.

Integrated timers are one of the new features

of UI module. You just create timer with

specified period on the beginning, and

destroy it on the end.

Following dialog callback shows again a

typical handling of mentioned situation with

help of timers.

CALLBACK FUNCTION dlgCallback()

SELECT CASE CBMSG

CASE %WM_INITDIALOG

DIALOG SET TIMER CBHNDL, _

%myTimer, _

%timeOut, _

%NULL

CASE %WM_SIZE, %WM_SIZING

TBGL_UpdateCanvasProportions_

(hCtrl)

RenderMyImage()

CASE %WM_TIMER

RenderMyImage()

CASE %WM_CLOSE

DIALOG KILL TIMER CBHNDL, _

%myTimer

END SELECT

END FUNCTION

You can even handle canvas binding and

releasing in the dialog callback, that is up to

you personal preference.

In case you forget to release canvas before

script ends, module garbage collector takes

care of releasing necessary data

automatically.

Page 23: EDITORIAL ThinBasic Journal

- 23 -

NEW IN MODULES

Introducing FileLine module

Petr Schreiber

File processing is one of most common tasks

in programming. ThinBasic provides File

module for general purpose file manipulation

already.

There are situations, when you need to

process line based ASCII file formats. That

sounds like a trivial task, but it has one

possible catch – there is lot of operating

systems around, each handling lines

differently.

This makes your head spin when you come

in touch with file formats used on Windows,

Macs and Unix. Each of mentioned systems

marks end of line differently. Typical multi

OS file format is Wavefront OBJ, used to

represent 3D models.

And that is when FileModule comes in – it

allows reading files line by line, does not

matter on which system they were produced.

Using File module you could do the same,

but you would have to detect line end

sequence on your own and then filter special

characters out for each line.

Typical use

Let’s have a look at minimal example

reading file and writing it line by line to

console.

USES “FileLine”, “Console”

DIM hFile AS DWORD

DIM sLine AS STRING

hFile = FileLine_Open(“C:\test.txt”)

IF hFile THEN

WHILE NOT FileLine_IsEOF(hFile)

FileLine_LineInput(hFile, sLine)

PRINTL sLine

WEND

END IF

FileLine_Close(hFile)

WAITKEY

You can see the file is simply opened by its

file name, no need to specify mode or

anything as FileLine module is currently

designed for reading files only.

If opening the file was successful, returned

handle is nonzero value. Handle serves as

file identifier for all consequent operations;

it is shorter than file name.

Lines from the file can be safely read until

end of file is found.

Conclusion

FileLine module is tool serving well for

specific area of file interactions. It is very

useful for processing ASCII based files

produced by application from unknown

operating system.

It can do the job well when you scan text

files for specific information as well.

Have a look to ThinBasic help file for more

functions provided by FileModule.

Did you know?

Windows world uses both

carriage return and line feed

characters to end the line.

Users of Linux are used to just

line feed, while Macs rely on

single carriage return character.

Page 24: EDITORIAL ThinBasic Journal

- 24 -

ARTICLES

Articles

Oxygen is one of the special

modules in ThinBasic. But

assembly programming might not

be so simple to understand for

newcomers. This is why Journal

brings you series of articles on the

topic of x86 assembly, written by

the creator of Oxygen module

himself.

For anybody interested in advanced

graphic techniques we prepared

article on creating motion blur with

ThinBasic and TBGL, hope you

will find interesting.

Programming in x86 Assembly code

Charles Pegge

This is a series of short articles of an introductory nature,

exploring low-level programming - where there is a clear

connection between the code and the hardware that interacts

with it. Assuming you already program in Basic this wont, I

hope be too challenging.

Introducing the x86 CPU registers

Programmers are not always clear what a CPU register might

be. (Is it something to do with the Windows Registry?). In fact

it is one of the fundamental components that make a CPU

work. From an electronics point of view, a register is like a

single memory location, usually 32 bits wide in our case.

Registers simply hold data for arithmetical and logical

operations, and for transferring data to and from memory. With

current CPU architectures all data has to pass through registers

even if it is only being transferred from one memory location

to another.

The x86 has many registers, even registers which are totally

inaccessible to the programmer, but we can focus on the most

common ones used in the main CPU leaving aside the Floating

Point Processor and SIMD extensions for the time being.

You can think of these as very local integer variables. Each

register started out in the early days having a distinct role to

play and their own set of special features. The advent of 32 bit

processing made the architecture more uniform, but the

ancestral genes and tradition remain firmly embedded in the

system.

If you call a function outside your program, the EAX register

is often used to return a result, and the original contents of the

ECX and EDX registers will be lost. By convention publicly

available functions must preserve or restore the original values

for the EBX, ESI, EDI and EBP registers.

In 32 bit mode, there are only eight registers you need to know

about. They can all do arithmetic and logic but some are

specialized or have a traditional use.

Page 25: EDITORIAL ThinBasic Journal

- 25 -

ARTICLES

EAX - the busiest of them all is often used

as the accumulator for arithmetical

operations.

ECX - often used for counting in a looping

operation. There are a few legacy

instructions which automatically increment

or decrement the ecx register, but these are

microcoded and no more efficient than doing

the job using more elementary instructions.

EDX - When using mul, imul, div or idiv;

EDX is used with EAX as an extended (64

bit) register. With multiplication, EDX

catches the overflow from EAX. With

division EDX is used to contain the upper 32

bits of the operand to be divided. After the

division EDX will hold the remainder

(modulus).

EBX - traditionally used o hold the base

address for an array of variables.

ESP - The stack pointer. Only used for stack

operations - never to be trifled with. To

obtain stack space for local variables, you

subtract the number of bytes you require -

which should always be in multiples of four.

The best way to do this is first preserve the

original EBP value by pushing it onto the

stack, then move the ESP value into the EBP

register then subtract the required bytes from

the ESP register. The EBP register is the

used to index locations in this newly

allocated space (Using negative offsets).

When this local space is no longer required

the value in EBP is passed back to ESP then

the original EBP value is popped back into

EBP.

EBP - Companion of ESP above, invariably

used to hold the base offset for local

variables. It is generally understood that its

original value is always preserved between

function calls - no matter what operating

system is being used.

ESI - Often used for indexing strings, and

used in conjunction with its twin EDI for

copying blocks of data from one location to

another.

EDI - twin of ESI. Some legacy operations

involve the use of EAX ESI EDI and ECX to

copy strings.

The lower 16 bits of these registers are

accessible for 16 bit operations: Just remove

the E, and you get the original 8086 16 bit

registers: AX CX DX BX SP BP SI DI.

Some registers have direct access to the first

2 bytes by name: (L lower H higher)

AL AH CL CH DL DH BL BH

32 bits

AH AL

AX

EAX

Scheme of EAX register

Two further registers play an important part

in programming:

EIP - the instruction pointer is used to feed

instructions from memory to the CPU. You

cannot change its contents in the same way

as the other registers, but call or jump

instructions do this implicitly. Changing the

sequence of instructions conditionally or

unconditionally throughout the program.

EFLAGS - holds various bit flags which flip

in response to various events, for example if

an arithmetical operation results in a

negative value then the Sign flag is set. If a

result is zero then the zero flag is set. This

allows conditional jumps like jg, jl, jz or jnz

to be activated.

Page 26: EDITORIAL ThinBasic Journal

- 26 -

ARTICLES

Using the registers to access memory

Most of the time, fixed addresses are not

used in assembly programming (except for

small micro-controllers). A base address is

set in a register then a fixed offset is used to

access an individual variable among a set of

variables. If you see the name of a register

enclosed in square brackets [..] it means that

the register hold the base address of a

memory location.

Did you know?

Oxygen module allows even

compiling EXE and DLL files

on the fly. If you will follow

series of articles on assembly in

ThinBasic, you will be able to

write your own compiler or

even create ThinBasic module

directly from ThinBasic.

Did you know?

You can find various samples

on assembly programming in

SampleScrips/Oxygen, under

ThinBasic installation folder.

Provided samples cover both

basic use and advanced topics

like collision detection.

Page 27: EDITORIAL ThinBasic Journal

- 27 -

ARTICLES

Faking motion blur with fixed

pipeline

Petr Schreiber

If you study look of real time graphics in last

few years, you definitely noticed that there is

a big push towards post processing rendered

picture in some way. Glow, HDR, depth of

field and radial blur can be seen very often

even in main stream games, which is thing

hardly imaginable 10 years ago3.

This article will demonstrate you technique

to bring motion blur, one of the visually

most interesting effects, to your ThinBasic

scripts.

What is motion blur?

Motion blur is something we could describe

as phenomenon, manifesting as blurring of

objects during fast movement. The

propagation of blur as such is always

proportional to the speed and direction of

moving object.

Pessimist would describe motion blur as

defect, preventing us from seeing moving

objects sharp. But using motion blur has

some benefits, as it serves well to emphasize

object speed.

Train captured by inexperienced photographer

3 With few exceptions like the game Outcast

Goals we want to achieve

Purpose of this article is to show motion blur

post processing technique, which should

have following characteristics:

Be compatible with most hardware

Be “automatic”, not requiring to

know object speeds

Be independent on amount of

geometry rendered

Look a bit as real motion blur :)

There is a lot of approaches to produce

motion blur on today’s hardware; like using

accumulation buffer to accumulate geometry

and render it at once, slightly shifted, or

various shader based approaches. Sadly

neither of these two fulfils all requirements

we pointed out a while ago.

The road we will take

We will try to use similar approach to the

one we used to replicate glow and radial blur

in ThinBasic before – rendering to texture.

In fact we will need to use more than one

texture.

The basic idea is – motion blur manifests as

blurred object, which means we see “ghosts”

of the object from last frames. To replicate

this, we need to keep few last frames in

memory and render them one over each

other.

We will test the effect on basic scene with

aircraft in canyon.

Preparing textures

There is little problem with limitation of

older hardware, which needs us to use power

of two textures. As we usually render to

window of arbitrary size (for example

640x480), we need to handle this somehow.

Solution is to create texture with sides of

power of two, which fits best the target

resolution. TBGL can help you on this, just

Page 28: EDITORIAL ThinBasic Journal

- 28 -

ARTICLES

use following to convert resolution to its

nearest power of two representation.

DIM w, h AS LONG

TBGL_EvaluatePOTMatch(640,480,w,h)

In this case command will return 512 and

256 to passed variables, as nearest safe

match.

As you can see, there will be noticeable loss

of detail for such an image. This situation

gets better with higher resolutions, where

NPOT4 / POT pixel ratio gets lower.

Once we have the recommended POT

texture dimensions, we can use

TBGL_MakeTexture to pre-allocate space for

rendering to texture.

But we should not forget owners of newer

cards, which support textures of any

proportions.

To check whether architecture we run script

on can handle arbitrary texture, we can take

advantage of new command, which serves to

retrieve info on texturing subsystem -

TBGL_TexturingQuery. This function

currently allows retrieving following

information:

maximum texture width

maximum texture height

whether NPOT is supported

4 Not power of two

Here is example how to retrieve mentioned

data for textures.

DIM texInfo AS TBGL_tTexturingInfo

TBGL_TexturingQuery( texInfo )

Please note that you do not need to seek

definition for TBGL_tTexturingInfo

anywhere, thanks to flexible way ThinBasic

modules work, TBGL prepares everything

for you.

Now the texInfo variable contains data in

UDT members maxWidth, maxHeight and

NPOTSupport. The last one contains true in

case you can get NPOT textures, the first

two obviously maximum dimensions in

pixels.

With all the information we just learned we

can determine safe size for texture on any

GPU architecture.

Initial setup

Now we know how to determine correct

texture dimension, but there is a more to

decide:

how many last frames to keep

how often to capture them

According to my tests, to not hog memory

too much and hold acceptable performance,

we would like to use something between 4 to

8 frames.

The second problem is trickier, it is not

possible to just capture last 8 frames and

draw them one over other. That would result

in different output on different PCs as it

would be frame rate dependant. So it is

better to determine time interval, typically in

milliseconds, in which we will capture the

frames. To evaluate time I recommend using

ThinBasic HiResTimer commands.

Be careful to not specify timeout below

precision of timer. Too big timeout can hurt

the quality of output as well. As we will

Page 29: EDITORIAL ThinBasic Journal

- 29 -

ARTICLES

render just the captured frames in the end,

timeout delimits the frame rate up to some

level. To get smooth speed, time value

should not exceed 33ms.

So when we put it all together, function to

init motion blur could look like following: SUB MotionBlur_Init( xRes AS LONG, _

yRes AS LONG, _

nTexture AS LONG,

nTimeout AS LONG )

„ -- Validate xRes and yRes

„ -– Allocate textures

„ –- Save timeout

END SUB

Capturing the frames

To render to texture, we need to do just few

basic steps – setup viewport to texture size,

render scene and copy it to appropriate

texture slot.

Sample frame representing aircraft in canyon

If you are using entity based approach, the

capture can get as easy as following:

IF MotionBlur_BeginPass() THEN

TBGL_ClearFrame

TBGL_SceneRender(%SCENE1)

MotionBlur_EndPass()

END IF

In this case MotionBlur_BeginPass() just

checks if it is time to render, and if yes then

it sets viewport to size of texture.

MotionBlur_EndPass() selects the

appropriate texture for this pass (by looping

through our pre-allocated textures) and

copies viewport contents to it.

During the capture process nothing is drawn

to the screen, all is just moved in memory as

we do not call TBGL_DrawFrame.

Rendering final composition

Once we have the capturing system done, we

can focus on how to combine textures to

produce the motion blur effect. As we need

to combine multiple images to one, it is clear

we will use some sort of blending. To be

more precise, we should follow these steps:

set up 2D mode using

TBGL_RenderMatrix2D

enable blending

disable depth mask

render all captured frames, stretched

over whole screen

cleanup state changes

Sounds very simple, doesn’t it? Well, there

is one catch. If we would render polygons

with textures at full color, we would get

terribly bright image.

Wrong output of layer rendering at full color bright

To prevent this issue, we just need to scale

brightness of each layer according to number

of textures used. If we draw for example 8

blended polygons over each other, we need

Page 30: EDITORIAL ThinBasic Journal

- 30 -

ARTICLES

to set colour of each of them to 1/8th

of

brightness. That means not render them all

with default white colour (255, 255, 255),

but evaluate each colour component as

256/number of passes.

Output of proper frame layer blending

That will darken each of the layers but

thanks to the blending of the colours we will

get correctly exposed image in the end.

Comparing reality and computer graphics

But does this effect really represent motion

blur correctly for used objects? The only

answer is real world test. For this purpose I

created model5 of aircraft you now know

from the canyon images.

Final wooden model levitating thanks to thinWire :)

Original design of aircraft is by Mike

Hartlef. I tried to keep look of computer

5 Noble name for pieces of wood sticked together

with glue

model and 3D model identical at least in the

most characteristic shapes and proportions.

As I surprisingly do not have much rock

canyons of tiny model dimensions in nearest

environs, I found the nearest matching

location to do the job and replicated it in 3D

for reference.

Setup of test scene

First task to solve was to think of scene setup

basic enough to prepare in virtual and real

world.

I decided to use scene, where airplane

trajectory is set as straight line at fixed

distance from background wall. Whole scene

is captured by camera looking at the object

from side.

With this in mind, I could attempt to perform

test in real world first. As my model for

unknown reason did not featured

antigravitation device, I had to help it with

wooden beam, attached to positioning

device, where digital camera also lied. I set

up camera to take single picture during 1/30s

time. Longer time was chosen because else it

would be hard to get the blur effect.

During the shooting, the positioning device

was set to constant speed, so we got sharp

airplane and blurred background. You can

see the result on attached picture.

Page 31: EDITORIAL ThinBasic Journal

- 31 -

ARTICLES

Photo of real model

1/30s time exposure can be approximated in

script via capturing 8 frames during 4 ms

interval. Then it was just needed to move

aircraft at the same speed as real model, and

we could finally see the resultant rendering.

Screenshot from the testing script

Summary

We got some motion blur finally - the

approach described here is compatible with

older hardware, but thanks to some

branching it allows to take advantage of new

cards as well. It does not depend on the

amount of geometry rendered as all blurring

is done using simple 2D transformation.

Thanks to the same fact it does not require to

have any information on object speeds.

With one eye closed and with finger in other

we can say that the real world and rendered

images are similar up to some level, and

therefore our experiment ends with success.

Summary seems quite optimistic so far, to be

correct I must admit it has few problems as

well.

The main issue is related to use of blending.

This algorithm is quite fill rate hungry,

which means there is noticeable performance

hit with growing resolution.

On high end cards it won’t be noticeable, but

especially on “value” cards and onboard

chips you can notice the dependency speed-

resolution can be cruel. This side effect can

be eliminated by lowering the number of

textures we render to, sacrificing final

quality.

The result of this approach you can see on

the front page of this issue as well. If you are

interested in full source code including the

scene, you can get it on ThinBasic forum as

usual. As the script relies on latest version of

TBGL module, you will need at least

ThinBasic 1.7.0.0 to use it.

Page 32: EDITORIAL ThinBasic Journal

- 32 -

INSIDER

Insider

If you ever wondered how

ThinBasic works inside, or how the

modules are developed, this new

section will satisfy your needs.

Support for module development is

one of the key features of

ThinBasic as a such, after reading

through articles in this section you

will be able to develop your own

module and customize ThinBasic to

suit your needs best.

ThinBasic inside

Eros Olmi

This is the first of a group of articles where we will discuss

about how thinBasic works from an engine and module point

of view. We have repeated many times that thinBasic is a

modular language. But what does modular language mean? We

will see in depth what this means and how thinBasic modules

work together allowing:

1. extreme flexibility in developing thinBasic applications

2. possibility for 3rd

party developers to create

personalized modules

3. improve thinBasic functionalities with a distributed

development concept

Figure 1.1: main thinBasic infrastructure

Figure 1.1 summarizes thinBasic modular software

architecture. There are basically 3 software layers each with

different duties.

Page 33: EDITORIAL ThinBasic Journal

- 33 -

INSIDER

1.1 Application layer

It is the execution process, the main application. This layer must accomplish 3 steps:

initialize the underline Core layer,

tells to Core Layer which thinBasic application (we will call it “script” for the rest of

the article) to execute

de-initialize the Core engine.

ThinBasic provides 2 different applications to complete the above steps:

thinBasic.exe (a 32bit GUI - Graphical User Interface – application)

thinBasicC.exe (a 32bit Console – text mode – application featuring standard input,

output)

We will see in future articles that the application layer process can be substituted by

any 3rd

party application able to call dll functions.

1.2 Core layer

This is the most important part of thinBasic programming language. It is the glue that keeps

together all the different parts that play a role in thinBasic execution. It contains the main

thinBasic Core language and is in charge of:

load and parse the script

manage all memory handling (allocation / de-allocation of variables, function stacks,

internal structures in place for module handling)

expose a series of functions interface used by modules to communicate with main

thinBasic engine

module handling on request (find the correct module dll, opening it, link the keywords with

the module functions, call functions implemented by modules, releasing modules when

needed)

This layer is inside a single dll: thinCore.dll. You can consider this dll the heart of thinBasic

language.

1.3 Module layer

Here we are: Module Layer. This layer is a set of official or user modules developed by

thinBasic development team or by any thinBasic user using thinBasic SDK. A module is a

single dll containing a set of functionalities that are loaded at script run-time when the Core

Layer requests for them.

In thinBasic terminology “module” term is used instead of “dll” mainly because a module is a

special dll that must follow some rules we will se in future articles. For the moment it is

sufficient to know that a thinBasic module must export 2 specific functions:

LoadLocalSymbols: this function must be mandatory exported by every thinBasic module

and is called automatically by Core Layer when the module is loaded. This function

serves two main reasons: initialize any data needed by the module, let Core Layer know

about new functionalities developed inside the module

Page 34: EDITORIAL ThinBasic Journal

- 34 -

INSIDER

UnLoadLocalSymbols: this function is not mandatory. It will be called automatically by

Core Layer when module is released so it can contains any code needed to de-initialize

module data (if any)

2. Script execution

Look Figure 2.1. The following is a step-by-step description of what happen when a script

(that doesn’t need any module) is executed:

1. Main Process loads thinCore.dll and calls thinBasic_Init function. This function

prepares the Core Layer for a script execution. It is a quite complex step. All keywords

implemented by thinBasic Core engine are linked to the relevant compiled function in

charge to manage them. Internal memory structures are prepared to receive execution,

variables, stacks

2. Main Process calls thinBasic_Run function passing the name of the script to be

executed plus a series of other parameters used to check that the Main Process is

authorized to execute a script. Here control is passed to the Core Engine and will

remain there till the end of the execution process

3. Core Layer makes a first parsing pass: pre-parsing. This step is targeted to resolve

some script declarations. In particular three aspects are important in this phase:

identification of needed modules (in this example no modules are needed),

identification of UDT (user defined types) and identification of Functions and Subs

(including any function parameters)

4. Real script execution starts. Every token (sequence of chars, a keyword, a quoted

string, a number, …) is parsed, recognized, executed

5. Execution ends. Core engine stops it and pass control back to Main Process

6. Main Process calls thinBasic_Release function. This function de-allocates all memory

structures used for script execution, releases used memory, releases modules (not in

this example) and ends the process.

Case 2: a script asking to use a module, in this example FILE module, because a functionality

to load file content into a string buffer is needed.

Page 35: EDITORIAL ThinBasic Journal

- 35 -

INSIDER

Figure 2.2: logic execution flow of a script that needs a thinBasic module.

Look Figure 2.2. The following is a step-by-step description of what happen when a script

that needs a module is executed (description of steps covered in above Case 1 will not be

repeated here):

1. Main Process loads thinCore.dll and calls thinBasic_Init function.

2. Main Process calls thinBasic_Run function

3. Core Layer makes a first parsing pass: pre-parsing.

4. During pre-parsing, Core Engine has identified that script needs a module named

“FILE”. To request a module is very simple: USES keyword followed by a string

expression representing the module name, that’s all. ThinBasic will identify module

dll on disk and will load it. How thinBasic searches and finds modules will be

covered in chapter 3

5. Immediately after, Core Engine will call the special function LoadLocalSymbolds

that MUST be present in any thinBasic module. This function, among other usage, is

in change to instruct Core Engine about the new keywords the module implements,

how to call them, what kind of parameter the keyword return

6. Real script execution starts. Every token (sequence of chars, a keyword, a quoted

string, a number, …) is parsed, recognized, executed

Page 36: EDITORIAL ThinBasic Journal

- 36 -

INSIDER

7. Execution ends. Core engine stops it and pass control back to Main Process

8. Main Process calls thinBasic_Release function

9. Because a module was loaded, thinBasic_Release, will check if the loaded module

have exported a function called UnLoadLocalSymbol. If yes, it will be invoked by

Core Engine. This process is repeated for all the modules loaded. After this step is

performed, process is ended.

There are thinBasic scripts that do not require any module (Case 1) or there are scripts that

require just one module (Case 2) but there are scripts that can require many different modules.

In this case the process is exactly like the one described into Case 2 but repeated for every

single module the script needs.

3. Where are thinBasic modules located and how Core Engine finds them?

During thinBasic installation process, all thinBasic modules are placed into \thinBasic\Lib\

directory. So you can assume the above directory as the default one for all thinBasic modules.

But, in order to simplify thinBasic programming and avoid what was known DLL hell (see

Wikipedia at http://en.wikipedia.org/wiki/DLL_hell, thinBasic modules are, at the end, just

standard DLLs) that directory is not the only one checked by thinBasic engine when it search

for modules to load. The following table summarize the sequence used by thinBasic when

main Core engine is asked to load a module.

To understand path examples, imagine your script is located into C:\MyWorks\tbScripts\

directory, thinBasic is installed in C:\thinBasic\ directory and script is trying to load FILE

module with the following code: USES "FILE"

Sequence Path used for module search Example

1 Source script path C:\MyWorks\tbScripts\

2 Source script path + Lib C:\MyWorks\tbScripts\Lib\

3 Source script path + Bin C:\MyWorks\tbScripts\Bin\

4 Source script path + Mod C:\MyWorks\tbScripts\Mod\

5 Source script path + Lib + "thinBasic_" +

ModuleName

C:\MyWorks\tbScripts\Lib\thinBasic_File\

6 Source script path + Bin + "thinBasic_" +

ModuleName

C:\MyWorks\tbScripts\Bin\thinBasic_File\

7 Source script path + Mod + "thinBasic_" +

ModuleName

C:\MyWorks\tbScripts\Mod\thinBasic_File\

11 thinBasic path C:\thinBasic\

12 thinBasic path + Lib C:\thinBasic\Lib\

13 thinBasic path + Bin C:\thinBasic\Bin\

14 thinBasic path + Mod C:\thinBasic\Mod\

15 thinBasic path + Lib + "thinBasic_" +

ModuleName

C:\thinBasic\Lib\thinBasic_File\

16 thinBasic path + Bin + "thinBasic_" +

ModuleName

C:\thinBasic\Bin\thinBasic_File\

17 thinBasic path + Mod + "thinBasic_" +

ModuleName

C:\thinBasic\Mod\thinBasic_File\

Following the above sequence, thinBasic Core engine will use the first path where the

requested module will be found. This method adds precision and freedom at the same time.

Precision because programmer exactly knows what thinBasic does internally and because

Page 37: EDITORIAL ThinBasic Journal

- 37 -

INSIDER

sequence gives higher priority to the script path instead to the interpreter path. Freedom

because there are many options to choose from.

4. Developing a thinBasic module: thinBasic SDK

So, we have seen how a thinBasic modules work, how they interact with thinBasic Core

Layer. But how to develop a thinBasic module? Next TBJ issues will cover this part in depth

showing how to develop a thinBasic module using different programming languages. In this

issue we will just have an overview at what we mean by “thinBasic SDK” (thinBasic

Software Development Kit).

ThinBasic SDK is a set of functionalities exposed by thinBasic Core Layer (thinCore.dll)

meant to allow programmers to add new keywords to thinBasic language. ThinBasic SDK not

only let you say what keyword you want to add but will let you “parse” source code the way

you prefer without any limitation.

Let’s have a look at a possible real example.

Imagine you want to develop a module that implements a new keyword whose name is

MyReverseMID$ and this new function has to extract a piece of text from a string reversing its

content. Now you decide that the new keyword will have the following syntax:

OutString = MyReverseMID$ ( InString, PosStart, ByLen )

where InString will be the input string to work with, PosStart will be the first byte from

which to start extracting the string and ByLen will be the number of bytes to keep.

As you can see we have decided the name of the new keyword, how many parameters it will

have, and the type of parameters (one string and two numbers). We have decided that

parameters must be enclosed into parenthesis but we could have decided that no parentheses

are needed or just optional. The complete syntax is up to you (as programmer). No

impositions will be forced by thinBasic in choosing how to develop your keyword.

Now let’s see what thinBasic SDK gives to the programmer to be able to parse the above

syntax. The first pass is to identify each of the tokens that play a role in the syntax. We have

to think that one of the very first steps thinBasic performs when looking at source code is

splitting it into pieces called tokens. Each token is than analyzed ad resolved at script runtime.

The following is a PowerBasic example of the parsing process of the syntax we have

previously decided: first column have each of the tokens.

Page 38: EDITORIAL ThinBasic Journal

- 38 -

INSIDER

Syntax broken

into single

tokens

Line PowerBasic source code that would be present in an hypothetical

user module

( 1 If thinBasic_CheckOpenParens_Mandatory then

InString 2 thinBasic_ParseString InString

, 3 if thinBasic_CheckComma_Mandatory then

PosStart 4 thinBasic_ParseNumber PosStart

, 5 if thinBasic_CheckComma_Mandatory then

ByLen 6 thinBasic_ParseNumber ByLen

) 7 If thinBasic_CheckCloseParens_Mandatory then

8 ‘---Do whatever job with the variables

Function = strreverse$(mid$(InString, PosStart, ByLen))

9 End if

10 End if

11 End if

12 End if

We can note immediately some important points:

there is a one to one link between the single syntax tokens and the

programming steps needed to parse them

for each token type present in the syntax (delimiters or numeric or string

expressions) thinBasic SDK has the right interface function to handle it

very human understandable interface function names will keep your

development easy to read and to maintain

whatever complex will be the code written inside the thinBasic script using

your brand new keyword, you code will be the same. InString will be parsed

exactly the same regardless it will be a single variable, a quoted string or a

complex string expression. The same for all other numeric parameters. Your

module code will be the same. thinBasic Core Engine will take care of all the

steps needed to give you one string or one number or whatever other

expression

Page 39: EDITORIAL ThinBasic Journal

- 39 -

PROGGIES

Proggies

This new section will inform you

on topics related to software created

with ThinBasic.

You will find here both

presentations of new software and

description of their functionality.

First program which made it into

this section is famous TAB,

program to author text adventures.

It is one of the biggest applications

made in ThinBasic up to date, and

in the last time it is experiencing

increased interest, resulting in

growing user community.

Understanding adventure coding in ThinBASIC

Adventure Builder

Philip Richmond

TAB is a text adventure/interactive fiction maker for Windows

in Alpha Development and made with thinBASIC.

Homepage: http://tab.thinbasic.com/

TAB consists of two programs:

The Editor: Create, build and test your text adventure.

The Player: Standalone distributable to play your

finished adventures.

The Editor allows you to enter the various types of data the

adventure requires such as Locations, Objects, Characters,

Messages, Game Settings and Vocabulary and so on...

More importantly it is in here that you can also construct the

responses to player commands and build the many puzzles,

problems and quests that the player will encounter as he/she

journeys through the game world.

This is done by using a condition/action language and by

devising appropriate coding entries according to a strict format

and syntax.

It is a little like simple if/then constructs.

The format of each coding entry must contain 3 tags enclosed

in square brackets:

[start]......[acts].......[end]

After a [start] tag you must enter the conditions that make the

entry work.

The first condition in a Response entry is always a words

condition.

If there are any other conditions required then the # (hash

symbol) is used to separate them.

When the required entry conditions have been typed in the

[acts] tag is inserted.

After this any action commands are listed.

If there is more than one action then the # (hash symbol) is

used to separate them.

Page 40: EDITORIAL ThinBasic Journal

- 40 -

PROGGIES

When the entry actions are finished, the

entry can be closed with an [end] tag.

Example entry format:

[start]words_conditions#condition2#c

ondition3[acts]action1#action2[end]

Roughly translated this means:

If the words conditions match and condition

1 is true and condition 2 is true then do

action 1 then do action 2 then exit the coding

list.

Recently introduced for more flexibility and

power is the extension to use else if and then

if.

Example using else and then coding format:

[start]words

conditions#condition#condition[acts]

action#action#else#condition#conditi

on[acts]action#action#else#condition

#condition[acts]action#action#else#[

acts]action#then#condition[acts]acti

on[end]

Therefore you can have multiple conditions

and actions in respect of the same starting

"words conditions".

This can save writing several individual

entries to build up a particular game puzzle

and also cuts down on typing too!

Think of action "#else#" as meaning else if.

The last use of action "#else#" in this

example shows how to do an else do type of

statement.

In other words if none of the previous entry

conditions and actions were executed then

this default action in respect of the valid

words conditions will always be done.

Notice there are no conditions before the

[acts] tag for this "catch-all" usage of

"#else#"

TAB also allows all conditions to be

prefixed by "or_" for or checking...

Example using or conditions:

[start]words

conditions#or_condition#or_condition

#condition[acts]action#action#else#c

ondition#condition[acts]action#then#

or_condition#or_condition[acts]actio

n[end]

In this second example the "then" action is

only carried out if the set of actions

following the "else" were done.

I'll finish this article by providing 2 actual

code snippets from the tutorial game in

TAB.

[start]eat

sandwich#here1[acts]destroy1#cmessYu

m Yum... You greedily consume the

egg sandwich.[end]

[start]eat

sandwich#absent1[acts]cmessYou don't

seem to own a sandwich![end]

You can probably figure out what these

entries do and they are quite simple to create

and type in. They could also be combined

using #else#, like so:

[start]eat

sandwich#here1[acts]destroy1#cmessYu

mYum... You greedily consume the egg

sandwich.#else#absent1[acts]cmessYou

don't seem to own a sandwich![end]

Page 41: EDITORIAL ThinBasic Journal

- 41 -

BITS AND PIECES

Bits and pieces

This section shows various quick

hints and brief tips on ThinBasic

usage.

While topics discussed here are not

big enough for complete article,

hints posted here can make your

programming more elegant and

efficient.

Alias keyword

Michael Clease

A keyword for a keyword, although flexible it cannot change

the way a keyword is called just what it is called.

Let’s use the FILE_OPEN keyword from the FILE module

n = FILE_OPEN(FileName, Mode, [RecordSize])

Now my Alias

ALIAS FILE_OPEN AS OpenFile

n = OpenFile(FileName, Mode, [RecordSize])

As you can see the keyword is now OpenFile but the

parameters are unchanged, that is very important to

remember.

Alias can also be used to make keywords smaller thus

making scripts smaller

Alias QueryPerformanceFrequency AS QPF

Alias QueryPerformanceCounter AS QPC

Alias HiResTimer_Get AS HiTimeGet

Casting data type

Petr Schreiber

When you need to specify precision of numeric literals, most

BASICs provide mechanism where you add some special

character after literal, like “!” or “#”.

ThinBasic provides slightly different approach. It is a bit

wordier, but clear as it doesn’t make numeric expression

look like swearword from comics.

You simply enclose numeric literal or even expression into

brackets, and add AS <type>.

Here is little example:

howMany = 5 + ( variable * 5.5 ) AS LONG