txt2 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  1. .\" Implementation details
  2. .\"
  3. .\" $Id$
  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.