Pristine Ack-5.5
[Ack-5.5.git] / doc / int / txt2
1 .\"     Implementation details
2 .\"
3 .\"     $Id: txt2,v 2.3 1994/06/24 10:05:35 ceriel Exp $
4 .bp
5 .NH
6 IMPLEMENTATION DETAILS.
7 .PP
8 The pertinent issues are addressed below, in arbitrary order.
9 .NH 2
10 Stack manipulation and start-up
11 .PP
12 It is not at all easy to start the EM machine with the stack in a reasonable
13 and consistent state.  One reason is the anomalous value of the ML register
14 and another is the absence of a proper RSB.  It may be argued that the initial
15 stack does not have to be in a consistent state, since the first instruction
16 proper is only executed after \fIargc\fP, \fIargv\fP and \fIenviron\fP
17 have been stacked (which takes care of the empty stack) and the initial
18 procedure has been called (which creates a RSB).  We would, however, like to
19 preform the stacking of these values and the calling of the initial procedure
20 using the normal stack and call routines, which again require the stack to be
21 in an acceptable state.
22 .NH 3
23 The anomalous value of the ML register
24 .PP
25 All registers in the EM machine point to word boundaries, and all of them,
26 except ML, address the even-numbered byte at the boundary.
27 The exception has a good reason: the even numbered byte at the ML boundary does
28 not exist.
29 This problem is not particular to EM but is inherent in the number system: the
30 number of N-digit numbers can itself not be expressed in an N-digit number, and
31 the number of addresses in an N-bit machine will itself not fit in an N-bit
32 address.  The problem is solved in the interpreter by having ML point to the
33 highest word boundary that has bytes on either side; this makes ML+1
34 expressible.
35 .NH 3
36 The absence of an initial Return Status Block
37 .PP
38 When the stack is empty, there is no legal value for AB, since there are no
39 actuals; LB can be set naturally to ML+1.  This is all right when the
40 interpreter starts with a call of the initial routine which stores the value
41 of LB in the first RSB, but causes problems when finally this call returns.  We
42 want this call to return completely before stopping the interpreter, to check
43 the integrity of the last RSB; restoring information from it will, however,
44 cause illegal values to be stored in LB and AB (ML+1 and ML+1+rsbsize, resp.).
45 On top of this, the initial (illegal) Procedure Identifier of the running
46 procedure will be restored; then, upon restoring the likewise illegal PC will
47 cause a check to see if it still is inside the running procedure.  After a few
48 attempts at writing special cases, we have decided that it is possible, but not
49 worth the effort; the final (= initial) RSB will not be unstacked.
50 .NH 2
51 Floating point numbers.
52 .PP
53 The interpreter is capable of working with 4- and 8-byte floating point (FP)
54 numbers.
55 In C-terms, this corresponds to objects of type float and double respectively.
56 Both types fit in a C-double so the obvious way to manipulate these entities
57 internally is in doubles.
58 Pushing a 8-byte FP, all bytes of the C-double are pushed.
59 Pushing a 4-byte FP causes the 4 bytes representing the smallest fraction
60 to be discarded.
61 .PP
62 In EM, floats can be obtained in two different ways: via conversion
63 of another type, or via initialization in the loadfile.
64 Initialized floats are represented in the loadfile by an ASCII string in
65 the syntax of a Pascal real (signed \fPUnsignedReal\fP).
66 I.e. a float looks like:
67 .DS
68 [ \fISign\fP ] \fIDigit\fP+ [ . \fIDigit\fP+ ] [ \fIExp\fP [ \fISign\fP ] \fIDigit\fP+ ]                                (G1)
69 .DE
70 followed by a null byte.
71 Here \fISign\fP = {+, \-}; \fIDigit\fP = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
72 \fIExp\fP = {e, E}; [ \fIAnything\fP ] means that \fIAnything\fP is optional;
73 and a + means one or more times.
74 To accommodate some loose code generators, the actual grammar accepted is:
75 .DS
76 [ \fISign\fP ] \fIDigit\fP\(** [ . \fIDigit\fP\(** ] [ \fIExp\fP [ \fISign\fP ] \fIDigit\fP+ ]                                (G2)
77 .DE
78 followed by a null byte. Here \(** means zero or more times.  A floating
79 denotation which is in G2 but not in G1 draws a warning, one that is not even
80 in G2 causes a fatal error.
81 .LP
82 A string, representing a float which does not fit in a double causes a
83 warning to be given.
84 In that case, the returned value will be the double 0.0.
85 .LP
86 Floating point arithmetic is handled by some simple routines, checking for
87 over/underflow, and returning appropriate values in case of an ignored error.
88 .PP
89 Since not all C compilers provide floating point operations, there is a
90 compile time flag NOFLOAT, which, if defined, suppresses the use of all
91 fp operations in the interpreter.  The resulting interpreter will still load
92 EM files with floats in the global data area (and ignore them) but will give a
93 fatal error upon attempt to execute a floating point instruction; consequently
94 code involving floating point operations can be run as long as the actual
95 instructions are avoided.
96 .NH 2
97 Pointers.
98 .PP
99 The following sub-sections both deal with problems concerning pointers.
100 First, something is said about pointer arithmetic in general.
101 Then, the null-pointer problem is dealt with.
102 .NH 3
103 Pointer arithmetic.
104 .PP
105 Strictly speaking, pointer arithmetic is defined only within a \fBfragment\fP.
106 From the explanation of the term fragment however (as given in [1], page 3),
107 it is not quite clear what a fragment should look like
108 from an interpreter's point of view.
109 For this reason we introduced the term \fBsegment\fP,
110 bordering the various areas within which pointer arithmetic is allowed.
111 Every stack-frame is a segment, and so are the global data area (GDA) and
112 the heap area.
113 Thus, the number of segments varies over time, and at some point in time is
114 given by the number of currently active stack-frames
115 (#CAL + #CAI \- #RET \- #RTT) plus 2 (gda, heap).
116 Pointers in the area between heap and stack (which is inaccessible by
117 definition), are assumed to be in the heap segment.
118 .PP
119 The interpreter, while building a new stack-frame (i.e. segment), stores the
120 value of the last ActualBase in a pointer-array  (\fIAB_list[\ ]\fP).
121 When a pointer (say \fIP\fP) is available for arithmetic, the number
122 of the segment where it points (say \fIS\d\s-2P\s+2\u\fP),
123 is determined first.
124 Next, the arithmetic is performed, followed by a check on the number
125 of the segment where the resulting pointer \fIR\fP points
126 (say \fIS\d\s-2R\s+2\u\fP).
127 Now, if \fIS\d\s-2P\s+2\u != S\d\s-2R\s+2\u\fP, a warning is given:
128 \fBPointer arithmetic yields pointer to bad segment\fP.
129 .br
130 It may also be clear now, why the illegal area between heap and stack
131 was joined with the heap segment.
132 When calculating a new heap pointer (\fIHP\fP), one will obtain intermediate
133 results being pointers in this area just before it is made legal.
134 We do not want error messages all of the time, just because someone is
135 allocating space in the heap.
136 .LP
137 A similar treatment is given to the pointers in the SBS instruction; they have
138 to point into the same fragment for subtraction to be meaningful.
139 .LP
140 The length of the \fIAB_list[\ ]\fP is initially 100,
141 and it is reallocated in the same way the dynamically growing partitions
142 are (see 1.1).
143 .NH 3
144 Null pointer.
145 .PP
146 Because the EM language lacks an instruction for loading a null pointer,
147 most programs solve this problem by loading a pointer-sized integer of
148 value zero, and using this as a null pointer (this is also proposed in [1]).
149 \fBInt\fP allows this, and will not complain.
150 A warning is given however, when an attempt is made to add something to a
151 null pointer (i.e. the pointer-sized integer zero).
152 .LP
153 Since many programming languages use a pointer to location 0 as an illegal
154 value, it is desirable to detect its use.
155 The big problem is though that 0 is a perfectly legal EM address;
156 address 0 holds the current line number in the source file.  It may be freely
157 read but is written only by means of the LIN instruction.  This allows us to
158 declare the area consisting of the line number and the file name pointer to be
159 read-only memory.  Thus a store will be caught (and result in a warning) but a
160 read will succeed (and yield the EM information stored there).
161 .NH 2
162 Function Return Area (FRA).
163 .PP
164 The Function Return Area (\fIFRA[\ ]\fP) has a default size of 8 bytes;
165 this default can
166 be overridden through the use of the \fB\-r\fP-option, but cannot be
167 made smaller than the size of two pointers, in accordance with the
168 remark on page 5 of [1].
169 The global variable \fIFRASize\fP keeps track of how many bytes were
170 stored in the FRA, the last time a RET instruction was executed.
171 The LFR instruction only works when its argument is equal to this size.
172 If not, the FRA contents are loaded anyhow, but one of the following warnings
173 is given:
174 \fBReturned function result too large\fP (\fIFRASize\fP > LFR size) or
175 \fBReturned function result too small\fP (\fIFRASize\fP < LFR size).
176 .LP
177 Note that a C-program, falling through the end of its code without doing
178 a proper \fIreturn\fP or \fIexit()\fP, will generate this warning.
179 .PP
180 The only instructions that do not disturb the contents of the FRA are
181 GTO, BRA, ASP and RET.
182 This is expressed in the program by setting \fIFRA_def\fP to "undefined"
183 in any instruction except these four.
184 We realize this is a useless action most of the time, but a more
185 efficient solution does not seem to be at hand.
186 If a result is loaded when \fIFRA_def\fP is "undefined", the warning:
187 \fBReturned function result may be garbled\fP is generated.
188 .LP
189 Note that the FRA needs a shadow-FRA in order to store the shadow
190 information when performing a LFR instruction.
191 .NH 2
192 Environment interaction.
193 .PP
194 The EM machine represented by \fBint\fP can communicate with
195 the environment in three different ways.
196 A first possibility is by means of (UNIX) interrupts;
197 the second by executing (relatively) high level system calls (called
198 monitor calls).
199 A third means of interaction, especially interesting for the debugging
200 programmer, is via internal variables set on the command line.
201 The former two techniques, and the way they are implemented will be described
202 in this section.
203 The latter has been allotted a separate section (3).
204 .NH 3
205 Traps and interrupts.
206 .PP
207 Simple user programs will generally not mess around with UNIX-signals.
208 In interpreting these programs, the default actions will be taken
209 when a signal is received by the program: it gives a message and
210 stops running.
211 .LP
212 There are programs however, which try to handle certain signals
213 themselves.
214 In C, this is achieved by the system call \fIsignal(\ sig_no,\ catch\ )\fP,
215 which calls the handling routine \fIcatch()\fP, as soon as signal
216 \fBsig_no\fP occurs.
217 EM does not provide this call; instead, the \fIsigtrp()\fP monitor call
218 is available for mapping UNIX signals onto EM traps.
219 This implies that a \fIsignal()\fP call in a C-program
220 must be translated by the EM library routine to a \fIsigtrp()\fP call in EM.
221 .PP
222 The interpreter keeps an administration of the mapping of UNIX-signals
223 onto EM traps in the array \fIsig_map[NSIG]\fP.
224 Initially, the signals all have their default values.
225 Now assume a \fIsigtrp()\fP occurs, telling to map signal \fBsig_no\fP onto
226 trap \fBtrap_no\fP.
227 This results in:
228 .IP 1.
229 setting the relevant array element
230 \fIsig_map[sig_no]\fP to \fBtrap_no\fP (after saving the old value),
231 .IP 2.
232 catching the next to come \fBsig_no\fP signal with the handling routine
233 \fIHndlEMSig\fP (by a plain UNIX \fIsignal()\fP of course), and
234 .IP 3.
235 returning the saved map-value on the stack so the user can know the previous
236 trap value onto which \fBsig_no\fP was mapped.
237 .LP
238 On an incoming signal,
239 the handling routine for signal \fBsig_no\fP arms the
240 correct EM trap by calling the routine \fIarm_trap()\fP with argument
241 \fIsig_map[sig_no]\fP.
242 At the end of the EM instruction the proper call of \fItrap()\fP is done.
243 \fITrap()\fP on its turn examines the value of the \fIHaltOnTrap\fP variable;
244 if it is set, the interpreter will stop with a message. In the normal case of
245 controlled trap handling this bit is not on and the interpreter examines
246 the value of the \fITrapPI\fP variable,
247 which contains the procedure identifier of the EM trap handling routine.
248 It then initiates a call to this routine and performs a \fIlongjmp()\fP
249 to the main
250 loop to bypass all further processing of the instruction that caused the trap.
251 \fITrapPI\fP should be set properly by the library routines, through the
252 SIG instruction.
253 .LP
254 In short:
255 .IP 1.
256 A UNIX interrupt is caught by the interpreter.
257 .IP 2.
258 A handling routine is called which generates the corresponding EM trap
259 (according to the mapping).
260 .IP 3.
261 The trap handler calls the corresponding EM routine which emulates a UNIX
262 interrupt for the benefit of the interpreted program.
263 .PP
264 When considering UNIX signals, it is important to notice that some of them
265 are real signals, i.e., messages coming from outside the program, like DEL
266 and QUIT, but some are actually program-caused synchronous traps, like Illegal
267 Instruction.  The latter, if they happen, are incurred by the interpreter
268 itself and consequently are of no concern to the interpreted program: it
269 cannot catch them.  The present code assumes that the UNIX signals between
270 SIGILL (4) and SIGSYS (12) are really traps; \fIdo_sigtrp()\fP
271 will fail on them.
272 .LP
273 To avoid losing the last line(s) of output files, the interpreter should
274 always do a proper close-down, even in the presence of signals.  To this end,
275 all non-ignored genuine signals are initially caught by the interpreter,
276 through the routine \fIHndlIntSig\fP, which gives a message and preforms a
277 proper close-down.
278 Synchronous trap can only be caused by the interpreter itself; they are never
279 caught, and consequently the UNIX default action prevails.  Generally they
280 cause a core dump.
281 Signals requested by the interpreted program are caught by the routine
282 \fIHndlEMSig\fP, as explained above.
283 .NH 3
284 Monitor calls.
285 .PP
286 For the convenience of the programmer, as many monitor calls as possible
287 have been implemented.
288 The list of monitor calls given in [1] pages 20/21, has been implemented
289 completely, except for \fIptrace()\fP, \fIprofil()\fP and \fImpxcall()\fP.
290 The semantics of \fIptrace()\fP and \fIprofil()\fP from an interpreted program
291 is unclear; the data structure passed to \fImpxcall()\fP is non-trivial
292 and the system call has low portability and applicability.
293 For these calls, on invocation a warning is generated, and the arguments which
294 were meant for the call are popped properly, so the program can continue
295 without the stack being messed up.
296 The errorcode 5 (IOERROR) is pushed onto the stack (twice), in order to
297 fake an unsuccessful monitor call.
298 No other \- more meaningful \- errorcode is available in the errno-list.
299 .LP
300 Now for the implemented monitor calls.
301 The returned value is zero for a successful call.
302 When something goes wrong, the value of the external \fIerrno\fP variable
303 is pushed, thus enabling the user to find out what the reason of failure was.
304 The implementation of the majority of the monitor calls is straightforward.
305 Those working with a special format buffer, (e.g. \fIioctl()\fP,
306 \fItime()\fP and \fIstat()\fP variants), need some extra attention.
307 This is due to the fact that working with varying word/pointer size
308 combinations may cause alignment problems.
309 .LP
310 The data structure returned by the UNIX system call results from
311 C code that has been translated with the regular C compiler, which,
312 on the VAX, happens to be a 4-4 compiler.
313 The data structure expected by the interpreted program conforms
314 to the translation by \fBack\fP of the pertinent include file.
315 Depending on the exact call of \fBack\fP, sizes and alignment may differ.
316 .LP
317 An example is in order. The EM MON 18 instruction in the interpreted program
318 leads to a UNIX \fIstat()\fP system call by the interpreter.
319 This call fills the given struct with stat information, the contents
320 and alignments of which are determined by the version of UNIX and the
321 used C compiler, resp.
322 The interpreter, like any program wishing to do system calls that fill
323 structs, has to be translated by a C compiler that uses the
324 appropriate struct definition and alignments, so that it can use, e.g.,
325 \fIstab.st_mtime\fP and expect to obtain the right field.
326 This struct cannot be copied directly to the EM memory to fulfill the
327 MON instruction.
328 First, the struct may contain extraneous, system-dependent fields,
329 pertaining, e.g., to symbolic links, sockets, etc.
330 Second, it may contain holes, due to alignment requirements.
331 The EM program runs on an EM machine, knows nothing about these
332 requirements and expects UNIX Version 7 fields, with offsets as
333 determined by the em22, em24 or em44 compiler, resp.
334 To do the conversion, the interpreter has a built-in table of the
335 offsets of all the fields in the structs that are filled by the MON
336 instruction.
337 The appropriate fields from the result of the UNIX \fIstat()\fP are copied
338 one by one to the appropriate positions in the EM memory to be filled
339 by MON 18.
340 .PP
341 The \fIioctl()\fP call (MON 54) poses additional problems. Not only does it
342 have a second argument which is a pointer to a struct, the type of
343 which is dynamically determined, but its first argument is an opcode
344 that varies considerably between the versions of UNIX.
345 To solve the first problem, the interpreter examines the opcode (request) and
346 treats the second argument accordingly.  The second problem can be solved by
347 translating the UNIX Version 7 \fIioctl()\fP request codes to their proper
348 values on the various systems.  This is, however, not always useful, since
349 some EM run-time systems use the local request codes.  There is a compile-time
350 flag, V7IOCTL, which, if defined, will restrict the \fIioctl()\fP call to the
351 version 7 request codes and emulate them on the local system; otherwise the
352 request codes of the local system will be used (as far as implemented).
353 .PP
354 Minor problems also showed up with the implementation of \fIexecve()\fP
355 and \fIfork()\fP.
356 \fIExecve()\fP expects three pointers on the stack.
357 The first points to the name of the program to be executed,
358 the second and third are the beginnings of the \fBargv\fP and \fBenvp\fP
359 pointer arrays respectively.
360 We cannot pass these pointers to the system call however, because
361 the EM addresses to which they point do not correspond with UNIX
362 addresses.
363 Moreover, (it is not very likely to happen but) what if someone constructs
364 a program holding the contents for one of these pointers in the stack?
365 The stack is implemented upside down, so passing the pointer to
366 \fIexecve()\fP causes trouble for this reason too.
367 The only solution was to copy the pointer contents completely
368 to fresh UNIX memory, constructing vectors which can be passed to the
369 system call.
370 Any impending memory fault while making these copies results in failure of the
371 system call, with \fIerrno\fP set to EFAULT.
372 .PP
373 The implementation of the \fIfork()\fP call faced us with problems
374 concerning IO-channels.
375 Checking messages (as well as logging) must be divided over different files.
376 Otherwise, these messages will coincide.
377 This problem was solved by post-fixing the default message file
378 \fBint.mess\fP (as well as the logging file \fBint.log\fP) with an
379 automatically leveled number for every new forked process.
380 Children of the original process do their diagnostics
381 in files with postfix 1,2,3 etc.
382 Second generation processes are assigned files numbered 11, 12, 21 etc.
383 When 6 generations of processes exist at one moment, the seventh will
384 get the same message file as the sixth, for the length of the filename
385 will become too long.
386 .PP
387 Some of the monitor calls receive pointers (addresses) from to program, to be
388 passed to the kernel; examples are the struct stat for \fIstat()\fP, the area
389 to be filled for \fIread()\fP, etc. If the address is wrong, the kernel does
390 not generate a trap, but rather the system call returns with failure, while
391 \fIerrno\fP is set to EFAULT.  This is implemented by consistent checking of
392 all pointers in the MON instruction.
393 .NH 2
394 Internal arithmetic.
395 .PP
396 Doing arithmetic on signed integers, the smallest negative integer
397 (\fIminsint\fP) is considered a legal value.
398 This is in contradiction with the EM Manual [1], page 14, which proposes using
399 \fIminsint\fP for uninitialized integers.
400 The shadow bytes already check for uninitialized integers however,
401 so we do not need this special illegal value.
402 Although the EM Manual provides two traps, for undefined integers and floats,
403 undefined objects occur so frequently (e.g. in block copying partially
404 initialized areas) that the interpreter just gives a warning.
405 .LP
406 Except for arithmetic on unsigneds, all arithmetic checks for overflow.
407 The value that is pushed on the stack after an overflow occurs depends
408 on the UNIX behavior with regard to that particular calculation.
409 If UNIX would not accept the calculation (e.g. division by zero), a zero
410 is pushed as a convention.
411 Illegal computations which UNIX does accept in silence (e.g. one's
412 complement of \fIminsint\fP), simply push the UNIX-result after giving a
413 trap message.
414 .NH 2
415 Shadow bytes implementation.
416 .PP
417 A great deal of run-time checking is performed by the interpreter (except if
418 used in the fast version).
419 This section gives all details about the shadow bytes.
420 In order to keep track of information about the contents of D-space (stack
421 and global data area), there is one shadow-byte for each byte in these spaces.
422 Each bit in a shadow-byte represents some piece
423 of information about the contents of its corresponding 'sun-byte'.
424 All bits off indicates an undefined sun-byte.
425 One or more bits on always guarantees a well-defined sun-byte.
426 The bits have the following meaning:
427 .IP "\(bu bit 0:" 8
428 indicates that the sun-byte is (a part of) an integer.
429 .IP "\(bu bit 1:" 8
430 the sun-byte is a part of a floating point number.
431 .IP "\(bu bit 2:" 8
432 the sun-byte is a part of a pointer in dataspace.
433 .IP "\(bu bit 3:" 8
434 the sun-byte is a part of a pointer in the instruction space.
435 According to [1] (paragraph 6.4), there are two types pointers which
436 must be distinguishable.
437 Conversion between these two types is impossible.
438 The shadow-bytes make the distinction here.
439 .IP "\(bu bit 4:" 8
440 protection bit.
441 Indicates that the sun-byte is part of a protected piece of memory.
442 There is a protected area in the stack, the Return Status Block.
443 The EM machine language has no possibility to declare protected
444 memory, as is possible in EM assembly (the ROM instruction).  The protection
445 bit is, however, set for the line number and filename pointer area near
446 location 0, to aid in catching references to location 0.
447 .IP "\(bu bit 5/6/7:" 8
448 free for later use.
449 .LP
450 The shadow bytes are managed by the routines declared in \fIshadow.h\fP.
451 The warnings originating from checking these shadow-bytes during
452 run-time are various.
453 A list of them is given in appendix A, together with suggestions
454 (primarily for the C-programmer) where to look for the trouble maker(s).
455 .LP
456 A point to notice is, that once a warning is generated, it may be repeated
457 thousands of times.
458 Since repetitive warnings carry little information, but consume much
459 file space, the interpreter keeps track of the number of times a given warning
460 has been produced from a given line in a given file.
461 The warning message will
462 be printed only if the corresponding counter is a power of four (starting at
463 1).  In this way, a logarithmic back-off in warning generation is established.
464 .LP
465 It might be argued that the counter should be kept for each (warning, PC
466 value) pair rather than for each (warning, file position) pair.  Suppose,
467 however, that two instruction in a given line would cause the same message
468 regularly; this would produce two intertwined streams of identical messages,
469 with their counters jumping up and down.  This does not seem desirable.
470 .NH 2
471 Return Status Block (RSB)
472 .PP
473 According to the description in [1], at least the return address and the
474 base address of the previous RSB have to be pushed when performing a call.
475 Besides these two pointers, other information can be stored in the RSB
476 also.
477 The interpreter pushes the following items:
478 .IP \-
479 a pointer to the current filename,
480 .IP \-
481 the current line number (always four bytes),
482 .IP \-
483 the Local Base,
484 .IP \-
485 the return address (Program Counter),
486 .IP \-
487 the current procedure identifier
488 .IP \-
489 the RSB code, which distinguishes between initial start-up, normal call,
490 returnable trap and non-returnable trap (a word-size integer).
491 .LP
492 Consequently, the size of the RSB varies, depending on
493 word size and pointer size; its value is available as \fIrsbsize\fP.
494 When the RSB is removed from the stack (by a RET or RTT) the RSB code is under
495 the Stack Pointer for immediate checking.  It is not clear what should be done
496 if RSB code and return instruction do not match; at present we give a message
497 and continue, for what it is worth.
498 .PP
499 The reason for pushing filename and line number is that some front-ends tend
500 to forget the LIN and FIL instructions after returning from a function.
501 This may result in error messages in wrong source files and/or line numbers.
502 .PP
503 The procedure identifier is kept and restored to check that the PC will not
504 move out of the running procedure.  The PI is an index in the proctab, which
505 tells the limits in the text segment of the running procedure.
506 .PP
507 If the Return Status Block is generated as a result of a trap, more is
508 stacked.  Before stacking the normal RSB, the trap function pushes the
509 following items:
510 .IP \-
511 the contents of the entire Function Return Area,
512 .IP \-
513 the number of bytes significant in the above (a word-size integer),
514 .IP \-
515 a word-size flag indicating if the contents of the FRA are valid,
516 .IP \-
517 the trap number (a word-size integer).
518 .LP
519 The latter is followed directly by the RSB, and consequently acts as the only
520 parameter to the trap handler.
521 .NH 2
522 Operand access.
523 .PP
524 The EM Manual mentions two ways to access the operands of an instruction.  It
525 should be noticed that the operand in EM is often not the direct operand of the
526 operation; the operand of the ADI instruction, e.g., is the width of the
527 integers to be added, not one of the integers themselves.  The various operand
528 types are described in [1].  Each opcode in the text segment identifies an
529 instruction with a particular operand type; these relations are described in
530 computer-readable format in a file in the EM tree, \fIip_spec.t\fP.
531 .PP
532 The interpreter uses the third method.  Several other approaches
533 can be designed, with increasing efficiency and equally increasing complexity.
534 They are briefly treated below.
535 .NH 3
536 The Dispatch Table, Method 1.
537 .PP
538 When the interpreter starts, it reads the ip_spec.t file and constructs from it
539 a dispatch table.  This table (of which there are actually three,
540 for primary, secondary
541 and tertiary opcodes) has 256 entries, each describing an instruction with
542 indications on how to decode the operand.  For each instruction executed, the
543 interpreter finds the entry in the dispatch table, finds information there on
544 how to access the operand, constructs the operand and calls the appropriate
545 routine with the operand as calculated.  There is one routine for each
546 instruction, which is called with the ready-made operand.  Method 1 is easy to
547 program but requires constant interpretation of the dispatch table.
548 .NH 3
549 Intelligent Routines, Method 2.
550 .PP
551 For each opcode there is a separate routine, and since an opcode uniquely
552 defines the instruction and the operand format, the routine knows how to get
553 the operand; this knowledge is built into the routine.  Preferably the heading
554 of the routine is generated automatically from the ip_spec.t file.  Operand
555 decoding is immediate, and no dispatch table is needed.  Generation of the
556 469 required routines is, however, far from simple.  Either a generated array
557 of routine names or a generated switch statement is used to map the opcode onto
558 the correct routine.  The switch approach has the advantage that parameters can
559 be passed to the routines.
560 .NH 3
561 Intelligent Calls, Method 3.
562 .PP
563 The call in the switch statement does full operand construction, and the
564 resulting operand is passed to the routine.  This reduces the number of
565 routines to 133, the number of EM instructions.  Generation of the switch
566 statement from ip_spec.t is more complicated, but the routine space is
567 much cleaner.  This does not give any speed-up since the same actions are still
568 required; they are just performed in a different place.
569 .NH 3
570 Static Evaluation.
571 .PP
572 It can be observed that the evaluation of the operand of a given instruction in
573 the text segment will always give the same result.  It is therefore possible to
574 preprocess the text segment, decomposing the instructions into structs which
575 contain the address, the instruction code and the operand.  No operand decoding
576 will be necessary at run-time: all operands have been precalculated.  This will
577 probably give a considerable speed-up.  Jumps, especially GTO jumps, will,
578 however, require more attention.
579 .NH 2
580 Disassembly.
581 .PP
582 A disassembly facility is available, which gives a readable but not
583 letter-perfect disassembly of the EM object.  The procedure structure is
584 indicated by placing the indication  \fBP[n]\fP  at the entry point of each
585 procedure, where \fBn\fP is the procedure identifier.  The number of locals is
586 given in a comment.
587 .LP
588 The disassembler was generated by the software in the directory \fIswitch\fP
589 and then further processed by hand.