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.
|
|