You are on page 1of 3

The Clipper OBJ and pcode model (GNU|Open|Clipper project)

==========================================================
Let's consider the following Clipper sample test.prg:
FUNCTION Main()
? "Hello world!"
RETURN NIL
Once it gets compiled into a OBJ, what is there inside it?
In fact, what we get is the equivalent to the following C language
application:
SYMBOL symbols[] = { ... };
void MAIN( void )
{
BYTE pcode[] = { ... };
VirtualMachine( pcode, symbols );
}
Basically, test.prg source code has been converted into a sequence
of pcode bytes contained in the array pcode[] = { ... }. All our MAIN()
function does is invoke, at run-time, a Clipper VirtualMachine() that will
process those pcode bytes.
Let's review the test.prg pcode structure in more detail:
0000
0003
0006
0009
0018
001B
001E
001F
0020
0023

(2A)
(2A)
(13)
(01)
(27)
(2A)
(7B)
(79)
(1E)
(60)

LINE 0
LINE 3
SYMF [QOUT]
PUSHC "Hello world!"
DO(1)
LINE 5
UNDEF
SAVE_RET
JMP 0023
ENDPROC

2A
2A
13
01
27
2A
7B
79
1E
60

00 00
03 00
02 00
...
01 00
05 00
00 00

We could define a hbpcode.h file to better read that pcode:


hbpcode.h
#define
#define
#define
#define
#define
...

LINE
SYMF
PUSHC
DO
UNDEF

0x2A
0x13
0x01
0x27
0x7B

So finally it will look like:

BYTE pcode[] = { LINE, 0, 0,


LINE, 3, 0,
SYMF, 2, 0,
PUSHC, 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '
!', '0',
DO, 1, 0,
LINE, 5, 0,
UNDEF,
SAVE_RET,
JMP, 0, 0,
ENDPROC };
And what is SYMBOL symbols[] ? Clipper creates a symbol table in
the OBJ that later on will be used to create a dynamic symbol table
shared by the entire application. Each of those symbols has the following
structure:
typedef struct
{
char * szName;
BYTE bScope;
LPVOID pVoid;
} SYMBOL;
#define PUBLIC 0

// Clipper in fact keeps an array here (11 bytes).

// the scope of the function!

SYMBOL symbols[] = { { "MAIN", PUBLIC, MAIN },


{ "QQOUT", PUBLIC, QQOUT } };
Let's remember that the name of a function (MAIN, QQOUT) is the address of the
function, so our symbol table will be ready to use it to jump and execute any
linked function.
In fact, the pcode SYMF 2, 0 in our sample, will instruct the VirtualMachine()
to use the 2 symbol, which is QQOUT.
Let's read the pcode:
LINE 0, 0
LINE 3, 0
SYMF 2, 0
PUSHC ...
DO 1, 0
LINE 5, 0
UNDEF
SAVE_RET
JMP 0
ENDPROC

=>
=>
=>
=>
=>
=>
=>
=>
=>
=>

We are located at line 0


We are located at line 3
We are going to call QQOUT from our symbol table
This string is going to be used as a parameter
ok, jump to QQOUT and remember we have just supplied 1 parameter
We are back from QQOUT and we are located at line 5
we are going to return this value (NIL)
Ok, return it
We don't jump to elsewhere, just continue to next pcode byte
This is the end. We have completed this function execution

All these instructions will be evaluated from our VirtualMachine() function


(Clipper names it _plankton()). All functions end using ENDPROC, so when
the VirtualMachine() finds ENDPROC it knows it has reached the end of a
function pcode.
Now that we clearly understand this basic model we are ready to start
implementing 'production rules' on our yacc (clipper.y) syntax to generate
the specific output file (test.c) with the above structure (or we could

easily just generate the OBJ file for it).


to be continued...
Antonio Linares
www.fivetech.com

You might also like