* Makefile
* config/*
* contrib/*
* doc/*
* extras/*
* include/*
* lib/*
* package/*
* src/*
* tests/*
* utils/*
* removed empty lines left after removed '$' + 'Id' + '$' identifiers
126 lines
3.7 KiB
Plaintext
126 lines
3.7 KiB
Plaintext
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 (2A) LINE 0 2A 00 00
|
|
0003 (2A) LINE 3 2A 03 00
|
|
0006 (13) SYMF [QOUT] 13 02 00
|
|
0009 (01) PUSHC "Hello world!" 01 ...
|
|
0018 (27) DO(1) 27 01 00
|
|
001B (2A) LINE 5 2A 05 00
|
|
001E (7B) UNDEF 7B
|
|
001F (79) SAVE_RET 79
|
|
0020 (1E) JMP 0023 1E 00 00
|
|
0023 (60) ENDPROC 60
|
|
|
|
|
|
We could define a hbpcode.h file to better read that pcode:
|
|
|
|
hbpcode.h
|
|
|
|
#define LINE 0x2A
|
|
#define SYMF 0x13
|
|
#define PUSHC 0x01
|
|
#define DO 0x27
|
|
#define UNDEF 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; // Clipper in fact keeps an array here (11 bytes).
|
|
BYTE bScope;
|
|
LPVOID pVoid;
|
|
} SYMBOL;
|
|
|
|
#define PUBLIC 0 // 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 => We are located at line 0
|
|
LINE 3, 0 => We are located at line 3
|
|
SYMF 2, 0 => We are going to call QQOUT from our symbol table
|
|
PUSHC ... => This string is going to be used as a parameter
|
|
DO 1, 0 => ok, jump to QQOUT and remember we have just supplied 1 parameter
|
|
LINE 5, 0 => We are back from QQOUT and we are located at line 5
|
|
UNDEF => we are going to return this value (NIL)
|
|
SAVE_RET => Ok, return it
|
|
JMP 0 => We don't jump to elsewhere, just continue to next pcode byte
|
|
ENDPROC => 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
|