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