PSIONICS FILE - OPO.FMT ======================= Format of OPO and OPA files Last modified 1998-02-28 =========================== [Much of the information in this file is supplied by Mike Rudin , author of Revtran. My thanks to Mike, who will answer questions, but asks that if you find things unclear, first try analysing fragments of real q-code and working it out for yourself.] File format ----------- [Because this is somewhat different to normal, a summary is included at the end.] An OPO file (OPL translator output) consists of two headers, one or more procedure bodies, and then the procedure table. An OPA file differs in that there are one or more embedded files between the two headers. The first header has a varying length and the following format: Offset 0 (cstr): "OPLObjectFile**" Offset 16 (word): format version (currently 1) Offset 18 (word): offset in file of second header Offset 20 (qstr): source filename The source filename may have a trailing zero (which will be included in the size of the qstr). In an OPA file, the embedded files are each preceeded by a word giving the length of the file. There are normally two embedded files: a PIC file holding the icon, and an OPA information file (always 36 bytes): Offset 0 to 13: name, a dot, and then the extension Offset 14 to 33: path Offset 34 (word): type This information is taken from the APP ... ENDA section; the first two fields are padding out with zero bytes. The second header is 12 bytes long and has the following format: Offset 0 (long): file length Offset 4 (word): translator version (S3t: $110F, S3a: $111F) Offset 6 (word): required runtime version ($110F, $111F) Offset 8 (long): offset of procedure table within the file The translator and runtime versions seen are $110F for the Series 3t, and $111F for the Series 3a. The procedure table consists of one or more entries; the first one is always the procedure that is executed at the start of the program. Each entry has the format: Offset 0 (qstr): procedure name without trailing colon (length L) Offset L+1 (long): offset of procedure within the file Offset L+5 (word): line number in the source of the PROC line, minus 1 The table is ended by a zero-length name (always followed by another zero byte, which will be the last byte in the file). Each procedure body consists of a declarations block followed by the "q-code" that is actually executed. The declarations block consists of: * Optimisation section (translator version $111F and above only) * Space control section * Parameter section * Global declaration section * Called procedures section * Global references section * String control section * Array control section Various sections refer to a "variable type code". This is a byte describing the type of a variable: bits 0 to 6: 0 = word, 1 = long, 2 = real, 3 = string bit 7: set for an array, clear for a simple variable The optimisation section consists of a single word, giving the length of the rest of the declarations block. The space control section has the format: Offset 0 (word): size of data stack frame Offset 2 (word): length of q-code Offset 4 (word): amount of dynamic stack used The parameter section consists of a byte giving the number of parameters, followed by one variable type code per parameter, in reverse order. The global declaration section describes the global variables defined in the procedure, and has the format: Offset 0 (word): size of rest of section (S): Offset 2 to S+1: zero or more per-variable blocks Each per-variable block has the format: Offset 0 (qstr): name including suffix (length L) Offset L+1 (byte): variable type code Offset L+2 (word): offset within the data stack frame The called procedure section lists all the procedures called by this one. It consists of a word giving the length of the rest of the section, followed by zero or more blocks, each consisting of a qstr (the name, excluding the trailing colon) followed by a byte (the number of arguments). The global references section lists all the global variables called but not declared by this procedure. It consists of a list of blocks, each consisting of a qstr (the name including the suffix) and a variable type code; the list is ended by a zero byte (forming an empty qstr). The string control section consists of a block for each string variable declared by the procedure. The block has the format: Offset 0 (word): location in the data stack frame Offset 2 (byte): maximum length of the string It is terminated by a zero word. The array control section consists of a block for each array variable declared by the procedure. The block has the format: Offset 0 (word): location in the data stack frame Offset 2 (word): number of elements It is terminated by a zero word. Summary of file format ---------------------- A file consists of the following items: - first header (20 bytes + qstr) - zero (OPO) or more (OPA): - (word) length of embedded file - embedded file - second header (12 bytes) - one or more procedure bodies, each: - declarations block: - optimisation section (some translator versions only, 2 bytes) - space control section (6 bytes) - parameter section (length byte, then that number of bytes) - global declaration section: - (word) size of rest of section - zero or more per-variable blocks (qstr then 3 bytes) - called procedures section: - (word) size of rest of section - zero or more per-procedure blocks (qstr then 1 byte) - global references section: - zero or more per-reference blocks (qstr then 1 byte) - (byte) zero - string control section: - zero or more per-string blocks (3 bytes) - (word) zero - array control section: - zero or more per-array blocks (4 bytes) - (word) zero - q-code - one or more procedure table entries (each qstr + 6 bytes) - (word) zero Organisation of memory ---------------------- When an OPL procedure is called, the run-time system allocates a block of memory associated with the call - the "data stack frame" - which will be freed when the procedure call returns. This block holds the values of all of the local and global variables defined in the procedure, and also information required to locate global variables, parameters, and other procedures referred to by the procedure. Space is allocated for variables defined in the procedure as follows: x% 2 bytes x%(N) 2N+2 bytes x& 4 bytes x&(N) 4N+2 bytes x 8 bytes x(N) 8N+2 bytes x$(L) L+2 bytes x$(N,L) 3+N(L+1) bytes The location of a variable (or, more precisely, of its value) in the stack frame is the address of some byte within this space: x% offset 0 x%(N) offset 2 x& offset 0 x&(N) offset 2 x offset 0 x(N) offset 2 x$(L) offset 1 x$(N,L) offset 3 and this is what is used by the q-code. Arrays have the array size N placed at offset 0 within the space, and it is here that the array control section points. Strings have the maximum length L placed at offset 0 (or offset 2 for string arrays) within the space, and it is here that the string control section points. That is, suppose we declare x$(2,4), and set the two elements to "abc" and "x" respectively, and the variable is placed at location 22 within the data stack frame. Then the memory will contain: Location: 22 23 24 25 26 27 28 29 30 31 32 33 34 Value: $0002 4 3 'a' 'b' 'c' ? 1 'x' ? ? ? The array control section will contain 22 as the location, the string control section will contain 24, and the q-code will use 25. Q-code ------ Q-code is a series of codes of varying length, each of which is an operation. Most operations involve pushing values on to a stack or popping them. The first byte of each code is the opcode, with the following bytes being arguments. The following conventions are used in this list. % (suffix) a word & (suffix) a long $ (suffix) a qstr * (suffix) a real = (suffix) an address ! (suffix) a single byte + (suffix) see below push% push the word result on to the stack pop= the address at the top of the stack, popped off where more than one value is popped off, pop=1 pop=2 etc is used to show the order; pop=1 starts at the top of the stack addr=& the long word whose address is that given L* two bytes giving the location of a real in the data stack frame the variable is referred to as LL* E* two bytes giving the external reference code for a global variable declared elsewhere or for a procedure (in each case referred to as EE*); see below for details V* 8 bytes forming a real, referred to as VV* D a byte (00 to 03) naming an open data file (A to D) N a byte giving the number of arguments N' a byte giving the number of arguments minus 1 Na a byte giving the number of arguments, or 0 meaning 2 special arguments: the address of an array and then a word Nq a byte that is 0 or 1 for OFF or ON respectively, or else the number of arguments minus 1 Nx a byte controlling the number of arguments as described Q a byte that is 0 or 1 for OFF or ON respectively Qn a byte that is 0, 1, or $FF for OFF, ON, and omitted respectively Qa a byte that is 0 or 1 for OFF or ON, or some other value J two bytes giving a location in the Q-code relative to the current operation (that is, 0 means this opcode) referred to as JJ ZZ other bytes described in the notes Certain opcodes differ only in the type of some operands. In this case, the opcode is given as a range (e.g. $00-3 or $4C-E), and the affected operands are shown by a + suffix. This should be replaced by % & * and $ respectively for the four opcodes (omitting $ if there are only three). Unless stated otherwise, keywords take their arguments from the stack in reverse order. For example, opcode B9 is actually "RANDOMIZE pop&" and opcode 9E is actually "AT pop%2, pop%1". For certain keywords and builtin functions, some of the arguments are required to be variables (for example, the first argument of IOOPEN). In this case, the appropriate argument is handled by pushing the address of the variable as a word, not as an address (an address is converted to a word using the ADDR opcode - 57 00). This is indicated in the following list by putting [v2] (indicating that the second argument is treated in this way) after the meaning. External references ------------------- Global variables, parameters, and procedures called by a procedure are each given an external reference code and a code size. The code of the first item is always 18, and the codes of all other items are found by adding the code size to the code of the previous item. The codes are allocated in the following order: * The global variables defined by the procedure; the code size for a variable is 4 more than the length of the name including the type suffix, so the code size for "abc&" is 8. These external references do not appear in the Q-code. * The procedures called by this procedure, in the order given in the called procedure section; the code size for each procedure is 1 more than the length of the name including the colon, so the code size for "xx:" is 4. These external references are only used by opcode 53. * The parameters of the procedure in order; the code size is 2 per parameter. These external references are used by opcodes 08 to 0B and 18 to 1B. * The global variables referenced, but not defined, by the procedure, in the order given in the global references section; the code size is 2 per variable. These external references are used by opcodes 08 to 0F and 18 to 1F. Operation Meaning ------------------ 00-3 L+ push+ the value of LL+ 04-7 L+ push= the address of LL+ 08-B E+ push+ the value of EE+ 0C-F E+ push= the address of EE+ [cannot be a parameter] 10-3 L+ push+ the value of LL+(pop%) 14-7 L+ push= the address of LL+(pop%) 18-B E+ push+ the value of EE+(pop%) 1C-F E+ push= the address of EE+(pop%) [cannot be a parameter] 20-3 D push+ the value of field pop$+ of data file D 24-7 D push= the address of field pop$+ of data file D 28-B V+ push+ VV+ 2C-F [[apparently not used]] 30-3 push% pop+2 < pop+1 34-7 push% pop+2 <= pop+1 38-B push% pop+2 > pop+1 3C-F push% pop+2 >= pop+1 40-3 push% pop+2 = pop+1 (compare, not assign) 44-7 push% pop+2 <> pop+1 48-B push+ pop+2 + pop+1 4C-E push+ pop+2 - pop+1 4F V! push% VV! ($80 to $FF convert to $FF80 to $FFFF) 50-2 push+ pop+2 * pop+1 53 E: call procedure EE: (see below) 54-6 push+ pop+2 / pop+1 57 see below 58-A push+ pop+2 ** pop+1 5B J if pop% is zero, jump to JJ 5C-E push% pop+2 AND pop+1 5F V! push& VV! ($80 to $FF convert to &FFFFFF80 to &FFFFFFFF) 60-2 push% pop+2 OR pop+1 63 V% push& VV% ($8000 to $FFFF convert to &FFFF8000 to &FFFFFFFF) 64-6 push% NOT pop+ 67 [[apparently not used]] 68-A push+ - pop+ 6B ZZ @ operator (see below) 6C push* pop*2 < pop*1 % [These are the "x+y%" operators] 6D push* pop*2 > pop*1 % 6E push* pop*2 + pop*1 % 6F push* pop*2 - pop*1 % 70 push* pop*2 * pop*1 % 71 push* pop*2 / pop*1 % 72 [[apparently not used]] 73 [[apparently not used]] 74-7 RETURN no value from type + procedure 78 push% value of pop& 79 push% value of pop* 7A push& value of pop* 7B push& value of pop% 7C push* value of pop% 7D push* value of pop& 7E [[apparently not used]] 7F [[apparently not used]] 80-3 pop+ and discard value 84-7 store pop+1 in location with address pop=2 88-B PRINT pop+ ; (i.e. with no following space or newline) 8C-F LPRINT pop+ ; (i.e. with no following space or newline) 90 PRINT , (i.e. with no following newline) 91 LPRINT , (i.e. with no following newline) 92 PRINT (i.e. newline at end of printing) 93 LPRINT (i.e. newline at end of printing) 94-7 INPUT+ into location with address pop= 98-B POKE+ pop+1 in location with address pop=2 9C POKEB pop%1 in location with address pop=2 9D APPEND 9E AT 9F BACK A0 BEEP A1 CLOSE A2 CLS A3 COMPRESS A4 COPY A5 D ZZ CREATE pop$ (see below for details) A6 Qa CURSOR (Qa is 2, 3, or 4 for 1, 4, or 5 parameters respectively) A7 DELETE A8 ERASE A9 Q ESCAPE AA FIRST AB V! ZZ VECTOR pop% (see below for details) AC LAST AD LCLOSE AE LOADM AF LOPEN B0 NEXT B1 J ONERR JJ (0 means ONERR OFF) B2 OFF B3 OFF pop% B4 D ZZ OPEN pop$ (see below for details) B5 PAUSE B6 POSITION B7 IOSIGNAL B8 RAISE B9 RANDOMIZE BA RENAME BB STOP BC TRAP action immediately following BD UPDATE BE D USE BF J GOTO JJ C0 RETURN pop+ (type popped depends on procedure type) C1 UNLOADM C2 EDIT C3 SCREEN pop%2, pop%1 C4 D ZZ OPENR pop$ (see below for details) C5 Nx gSAVEBIT (Nx: 0 = 1 argument, 1 = 3 arguments) C6 gCLOSE C7 gUSE C8 N gSETWIN C9 Q gVISIBLE CA gFONT CB gUNLOADFONT CC gGMODE CD gTMODE CE gSTYLE CF gORDER D0 gINFO [v1] D1 gCLS D2 gAT D3 gMOVE D4-7 gPRINT pop+ ; (i.e. with no following space or newline) D8 gPRINT , (i.e. with no following newline) D9 N' gPRINTB DA gLINEBY DB gBOX DC [[apparently not used]] DD [[apparently not used]] DE gPOLY [v1] DF gFILL E0 gPATT E1 gCOPY E2 N gSCROLL E3 Qn gUPDATE E4 GETEVENT [v1] E5 gLINETO E6 gPEEKLINE [v4] E7 SCREEN pop%4, pop%3, pop%2, pop%1 E8 IOWAITSTAT [v1] E9 IOYIELD EA mINIT EB Nx mCARD (there are Nx menu items, and 2Nx+1 arguments) EC N dINIT ED see below EE SETNAME EF Nq STATUSWIN F0 N BUSY (0 arguments means OFF) F1 Q LOCK F2 gINVERT F3 gXPRINT F4 N gBORDER F5 Nq gCLOCK F6 V! push* value of calculator memory VV F7 V! push= address of calculator memory VV F8 MKDIR F9 RMDIR FA SETPATH FB SECSTODATE [v2, v3, v4, v5, v6, v7, v8] FC N' GIPRINT FD [[apparently not used]] FE [[apparently not used]] FF see below Each of the opcodes 57, ED, and FF are followed by a second opcode byte. Opcode 57 is used for builtin functions; unless stated otherwise, these take their arguments from the stack in reverse order and push the result. For example, opcode 57 33 is actually "push% gPRINTCLIP pop$2, pop%1". Operation Meaning ------------------ 57 00 push% ADDR pop= (address of a word, long, or real) 57 01 ASC 57 02 N CALL 57 03 COUNT 57 04 DAY 57 05 DOW 57 06 EOF 57 07 ERR 57 08 EXIST 57 09 FIND 57 0A GET 57 0B IOA [v3, v4, v5] 57 0C IOW [v3, v4] 57 0D IOOPEN [v1] (second argument is a string) 57 0E IOWRITE 57 0F IOREAD 57 10 IOCLOSE 57 11 IOWAIT 57 12 HOUR 57 13 KEY 57 14 LEN 57 15 LOC 57 16 MINUTE 57 17 MONTH 57 18 PEEKB 57 19 PEEKW 57 1A POS 57 1B RECSIZE 57 1C SECOND 57 1D USR 57 1E YEAR 57 1F push% ADDR pop= (address of a string) 57 20 WEEK 57 21 IOSEEK [v3] 57 22 KMOD 57 23 KEYA [v1, v2] 57 24 KEYC [v1] 57 25 IOOPEN [v1] (second argument is a word) 57 26 gCREATE (5 arguments) 57 27 gCREATEBIT 57 28 N gLOADBIT 57 29 gLOADFONT 57 2A gRANK 57 2B gIDENTITY 57 2C gX 57 2D gY 57 2E gWIDTH 57 2F gHEIGHT 57 30 gORIGINX 57 31 gORIGINY 57 32 gTWIDTH 57 33 gPRINTCLIP 57 34 TESTEVENT 57 35 N OS 57 36 MENU without argument 57 37 DIALOG 57 38 N ALERT 57 39 gCREATE (6 arguments) 57 3A MENU with argument [v1] 57 3B CREATESPRITE 57 3C LOADLIB [v1] 57 3D UNLOADLIB 57 3E FINDLIB [v1] 57 3F GETLIBH 57 40 DAYS 57 41 IABS 57 42 INT 57 43 PEEKL 57 44 SPACE 57 45 DATETOSECS 57 46 NEWOBJ 57 47 NEWOBJH 57 48 N SEND [v3, v4, v5] (arguments 3 to 5 are optional) 57 49 N ENTERSEND [v3, v4, v5] (arguments 3 to 5 are optional) 57 4A N ENTERSEND0 [v3, v4, v5] (arguments 3 to 5 are optional) 57 4B ALLOC 57 4C REALLOC 57 4D ADJUSTALLOC 57 4E LENALLOC 57 4F N IOC [v3, v4, v5] 57 50 UADD 57 51 USUB 57 52 IOCANCEL 57 53 STATWININFO [v2] 57 54 FINDFIELD 57 58 POPUP @@@@S5@@@@ ? 57 80 ABS 57 81 ACOS 57 82 ASIN 57 83 ATAN 57 84 COS 57 85 DEG 57 86 EXP 57 87 FLT 57 88 INTF 57 89 LN 57 8A LOG 57 8B PEEKF 57 8C PI 57 8D RAD 57 8E RND 57 8F SIN 57 90 SQR 57 91 TAN 57 92 VAL 57 93 Na MAX 57 94 Na MEAN 57 95 Na MIN 57 96 Na STD 57 97 Na SUM 57 98 Na VAR 57 99 EVAL 57 C0 CHR$ 57 C1 DATIM$ 57 C2 DAYNAME$ 57 C3 DIR$ 57 C4 ERR$ 57 C5 FIX$ 57 C6 GEN$ 57 C7 GET$ 57 C8 HEX$ 57 C9 KEY$ 57 CA LEFT$ 57 CB LOWER$ 57 CC MID$ 57 CD MONTH$ 57 CE NUM$ 57 CF PEEK$ 57 D0 REPT$ 57 D1 RIGHT$ 57 D2 SCI$ 57 D3 UPPER$ 57 D4 USR$ 57 D5 GETCMD$ 57 D6 CMD$ 57 D7 PARSE$ [v3] 57 D8 ERRX$ @@@@S5@@@@ ED 00 Nx dTEXT (Nx is 2 less than the number of arguments) ED 01 dCHOICE pop=3, pop$2, pop$1 ED 02 dLONG pop=4, pop$3, pop&2, pop&1 ED 03 dFLOAT pop=4, pop$3, pop*2, pop*1 ED 04 dTIME pop=5, pop$4, pop%3, pop&2, pop&1 ED 05 dDATE pop=4, pop$3, pop&2, pop&1 ED 06 dEDIT pop=2, pop$1 ED 07 dEDIT pop=3, pop$2, pop%1 ED 08 dXINPUT pop=2, pop$1 ED 09 dFILE pop=3, pop$2, pop%1 ED 0A Nx dBUTTONS (Nx is the number of buttons, there are 2Nx arguments) ED 0B dPOSITION pop%2, pop$1 FF 00 gGREY FF 01 DEFAULTWIN FF 02 N DIAMINIT FF 03 DIAMPOS FF 04 FONT FF 05 STYLE FF 06 USESPRITE FF 07 APPENDSPRITE (Nx: 0 = 2 arguments, 1 = 4 arguments) FF 08 DRAWSPRITE FF 09 CHANGESPRITE (Nx: 0 = 3 arguments, 1 = 5 arguments) FF 0A POSSPRITE FF 0B CLOSESPRITE FF 0C FREEALLOC FF 0D LINKLIB FF 0E Qa CACHE (Qa is the number of parameters if 2 or more) FF 0F gBUTTON FF 10 N gXBORDER FF 11 gDRAWOBJECT FF 12 ODBINFO [v1] FF 13 CACHETIDY FF 14 SCREENINFO [v1] FF 15 CACHEHDR pop% FF 16 CACHEREC pop%2, pop%1 FF 17 N dINITS FF 18 CALLOPX @@@@S5@@@@ FF 22 GETEVENT32 @@@@S5@@@@ FF 23 GETEVENTA32 @@@@S5@@@@ FF 24 GCOLOR @@@@S5@@@@ FF 26 SETDOC @@@@S5@@@@ FF 29 IOWAITSTAT32 @@@@S5@@@@ FF 30 MCASC @@@@S5@@@@ FF 33 DCHECK @@@@S5@@@@ Notes ----- Procedures expect two values on the stack for each parameter: a value and a type code. The top of the stack is the variable type code for the last parameter. The @ operator expects the procedure name to be below the other values on the stack. The opcode is followed by two bytes: the first is the number of arguments to the call, and the second is one of %%, %&, 0, or %$. So @$(x$):(y%,z$) will appear as [code to push x$] [code to push y%] [code to push% 0 (type code for word)] [code to push z$] [code to push% 3 (type code for string)] 6B 02 24 The opcodes for CREATE, OPEN, and OPENR are followed by a D byte and then information describing the field names. For each field in turn there is a variable type code followed by the name; the list is ended by $FF. So OPEN x$,c,AAA$,B% will appear as [code to push x$] B4 02 03 04 41 41 41 24 00 02 42 25 FF C <--- AAA$ ---> <- B$ -> The VECTOR keyword is followed by a byte giving the number of labels, and then that number of relative addresses J1, J2, etc.