Turbo Pascal 3.0 Compiler and Code Generation Internals.htm 47 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330
  1. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/htm14/loose.dtd">
  2. <html><head>
  3. <meta http-equiv="content-type" content="text/html; charset=windows-1252">
  4. <title>Turbo Pascal 3.0 Compiler and Code Generation Internals</title>
  5. <meta name="description" content="Turbo Pascal 3.0 compiler / code generation
  6. internals">
  7. </head>
  8. <body>
  9. <font size="3" face="Trebuchet MS, Arial, Sans Serif">
  10. <table width="100%" align="left" border="0" cellpadding="0" cellspacing="0">
  11. <tbody><tr><td colspan="5" valign="top" align="left" bgcolor="#CCCCCC">
  12. <a href="http://www.pcengines.ch/index.htm" target="_self"><img src="Turbo%20Pascal%203.0%20Compiler%20and%20Code%20Generation%20Internals_files/logo.gif" alt="PC Engines Home" vspace="0" width="331" height="52" hspace="0" border="0"></a></td></tr>
  13. <tr><td colspan="5" width="100%" height="25" bgcolor="#CC0000">
  14. <font face="Trebuchet MS, Arial, sans-serif" color="#FFFFFF">
  15. &nbsp;&nbsp;<a href="http://www.pcengines.ch/alix.htm"><font color="#FFFFFF">ALIX</font></a>
  16. |&nbsp;<a href="http://www.pcengines.ch/cflash.htm"><font color="#FFFFFF">CompactFlash Adapters</font></a>
  17. |&nbsp;<a href="http://www.pcengines.ch/test.htm"><font color="#FFFFFF">Test Tools</font></a>
  18. |&nbsp;<a href="http://www.pcengines.ch/resource.htm"><font color="#FFFFFF">Attic</font></a>
  19. |&nbsp;<a href="http://www.pcengines.ch/about.htm"><font color="#FFFFFF">Info</font></a>
  20. |&nbsp;<a href="http://www.pcengines.ch/order.php"><font color="#FFFFFF">Shop</font></a>
  21. |&nbsp;<a href="http://www.pcengines.ch/support.htm"><font color="#FFFFFF">Support</font></a></font></td></tr>
  22. <tr><td colspan="5" valign="top">
  23. <table>
  24. <tbody><tr><td colspan="5" valign="baseline" bgcolor="#DDDDDD">
  25. <p><font size="5"><b>Turbo Pascal 3.0 Compiler / Code Generation Internals</b></font>
  26. </p></td></tr>
  27. <tr><td colspan="5" valign="top" align="left" bgcolor="#FFFFFF">
  28. <p>The following is documentation I created after reverse engineering the
  29. Turbo Pascal 3.01A compiler. While many features, e.g. units and objects,
  30. have been added, today's compiler is still related to the old code.
  31. </p><p>Before you flame me about stuff that has been fixed, remember that this
  32. is about an OLD version of the compiler.
  33. </p><p>NEW: This file will generate disassembled, commented source from YOUR
  34. 3.01A compiler -&gt; <a href="http://www.pcengines.ch/file/scg.zip">SCG.ZIP</a>
  35. </p></td>
  36. </tr>
  37. <tr>
  38. <td colspan="5" valign="baseline" bgcolor="#EEEEEE">
  39. <p><font size="5"><b>Compiler Structure</b></font></p></td></tr>
  40. <tr>
  41. <td colspan="5" valign="top" align="left" bgcolor="#FFFFFF">
  42. <p>Compilers usually consist of the following functional groups:
  43. </p><ul>
  44. <li>Lexical analysis</li>
  45. <li>Syntactical analysis (parser)</li>
  46. <li>Optimizer</li>
  47. <li>Code generator</li>
  48. <li>Symbol table manager</li>
  49. <li>Error handler</li>
  50. </ul>
  51. <p>TURBO Pascal isn't your usual compiler... The parser is interspersed with
  52. portions of the code generator, and there is no optimizer. Most compilers
  53. need multiple passes to do their work, but TURBO is a (faster)
  54. single pass compiler.
  55. </p><p>Fortunately, programming languages of the Pascal type are designed with simple
  56. compilation in one pass in mind. All symbols must be defined before they are
  57. used. The compiler can easily determine the type of a constant without
  58. looking ahead:
  59. </p><ul>
  60. <li>symbol</li>
  61. <li>$7FFF</li>
  62. <li>12345</li>
  63. <li>12345.5 - This is an exception. TURBO first verifies whether a numeric
  64. constant is an integer or a real constant. Actually, a standard Pascal
  65. compiler doesn't have to do this - standard Pascal requires that the integer
  66. section of a real constant be a valid integer number !</li>
  67. <li>09CFH - This notation (used by many assemblers and languages) is a
  68. negative example. Compilers - like people - read from left to right. To
  69. read a number in this notation, it has to be read into a buffer to recognize
  70. its type, then it can be converted. Guess why TURBO uses $ for hex numbers...
  71. <i>I wonder why Niklaus Wirth chose this notation for Modula-2.</i></li></ul></td>
  72. </tr>
  73. <tr><td colspan="5" valign="baseline" bgcolor="#EEEEEE">
  74. <p><font size="4"><b>Lexical Analysis</b></font></p></td></tr>
  75. <tr><td colspan="5" valign="top" align="left" bgcolor="#FFFFFF">
  76. <p>The task of the lexical analysis is to read the source code from memory or
  77. from an include file, to eliminate comments and to recognize compiler
  78. directives, symbols and keywords.
  79. </p><p>It is called by the parser. On each call a language element (keyword, symbol,
  80. constant...) is read. The starting position is stored. If an error is
  81. recognized the editor will be started and the cursor will point to this
  82. position.</p></td>
  83. </tr>
  84. <tr><td colspan="5" valign="baseline" bgcolor="#EEEEEE">
  85. <p><font size="4"><b>Parsing Program Structures</b></font></p></td></tr>
  86. <tr><td colspan="5" valign="top" align="left" bgcolor="#FFFFFF">
  87. <p>The task of this part of the compiler is to analyze the structure of the
  88. program to be compiled and to check the syntax. Like most Pascal compilers,
  89. TURBO uses a recursive descent parser. The code generation is included in
  90. the parser.
  91. </p><p>The compilation of program structures is quite simple. Usually the syntax is
  92. described in Backus-Naur form or by a "railway diagram". As an example
  93. the IF statement will be covered.
  94. </p><p></p><pre> IF cond THEN stat1 { ELSE stat2 }
  95. ___________________
  96. ____ ______ ______ ___________ / \
  97. -&gt;_IF_-&gt;_expr_-&gt;_THEN_-&gt;_statement_-&lt;&gt; --&gt;
  98. \______ ___________/
  99. _ELSE_-&gt;_statement__
  100. </pre>
  101. <p>After reading an element, the parser takes the applicable track. If there
  102. isn't any the syntax is incorrect, and an error is reported.
  103. </p><p>It is possible to have a parser generated automatically by a so called
  104. compiler-compiler, if the Backus-Naur form of the syntax is given.
  105. Unfortunately this doesn't help very much: The really difficult parts of
  106. a compiler - code generation and optimization - must still be written
  107. manually.
  108. </p><p>How is the IF statement translated ? (The corresponding section in the
  109. compiler is at offset 6C12). The statement procedure reads an IF and calls the
  110. IF procedure. First the condition - actually an arithmetic expression
  111. of type boolean - is evaluated. This is done by calling the expression
  112. procedure. The expression is read until an illegal symbol (THEN) is found.
  113. This terminates the expression, which is checked for type boolean. The
  114. IF procedure inserts a conditional jump to the end of statement 1 here.
  115. The displacement is inserted later - it is not yet known. If the expression
  116. has been terminated by something else than a THEN, an error is reported.
  117. Now the first statement (stat1) is translated. Actually, this is a RECURSIVE
  118. call of the statement procedure (That's why this is a recursive descent
  119. parser). Please note that the syntax definition is recursive, too !
  120. Because of possible nested IF statements the variables of the IF
  121. procedure are saved on the stack. After this statement, an ELSE
  122. may follow. If it does, a jump to the end of stat2 is emitted
  123. and the jump from the beginning of stat1 is patched, then the second statement
  124. is translated and the second jump patched.
  125. </p><p>The code produced looks like this:
  126. </p><p></p><pre> (IF..THEN) (IF..THEN..ELSE)
  127. cond cond
  128. JNZ l1 JNZ l1
  129. JMP l2 JMP l2
  130. l1: stat 1 l1: stat 1
  131. l2: ... JMP l3
  132. l2: stat 2
  133. l3: ...
  134. </pre>
  135. The long jump at the beginning isn't always necessary. Unfortunately, the
  136. compiler cannot predict how long the statement will be. To improve this, the
  137. jump would have to be replaced by a short one and the subsequent code moved,
  138. which would complicate the compiler quite a bit.
  139. <p>All other program structures are translated in a similar way.
  140. </p></td>
  141. </tr>
  142. <tr><td colspan="5" valign="baseline" bgcolor="#EEEEEE">
  143. <p><font size="4"><b>Parsing Arithmetic Expressions</b></font></p></td></tr>
  144. <tr><td colspan="5" valign="top" align="left" bgcolor="#FFFFFF">
  145. <p>The evaluation of expressions is somewhat more complex, as the precedence of
  146. the operations has to be taken into account. The solution in TURBO is, however,
  147. quite simple (code starting at 7A70).
  148. </p><p>Expressions are usually translated to reverse polish notation (as used on
  149. Hewlett-Packard calculators and in the programming language FORTH).
  150. </p><p>There are five groups of operations:
  151. </p><ul>
  152. <li>negation (highest precedence)</li>
  153. <li>NOT</li>
  154. <li>multiplication, division, ...</li>
  155. <li>addition, subtraction, ...</li>
  156. <li>comparisons, IN (lowest precedence)
  157. </li></ul>
  158. <p>This translates into the following program structure:
  159. </p><p></p><pre> PROCEDURE atom; { element }
  160. BEGIN
  161. CASE op OF
  162. CONST:read constant
  163. VAR :read variable { indexing -&gt; recursive }
  164. '(' :read expression { recursive }
  165. ')' must follow
  166. func :read parameters { recursive }
  167. emit function call
  168. TYPE :'(' must follow { type conversion, e.g. Integer(TRUE) }
  169. read expression { recursive }
  170. ')' must follow
  171. convert type -&gt; type wanted
  172. ELSE syntax error;
  173. END;
  174. END;
  175. PROCEDURE neg; { negation - }
  176. VAR negflag:BOOLEAN;
  177. BEGIN
  178. negflag:=(op=neg);
  179. atom;
  180. IF negflag THEN emit negation;
  181. END;
  182. PROCEDURE NOT; { NOT }
  183. VAR notflag:BOOLEAN;
  184. BEGIN
  185. notflag:=(op=NOT);
  186. neg;
  187. IF notflag THEN emit NOT;
  188. END;
  189. PROCEDURE mult_level; { multiplication ... }
  190. VAR mult_op:operation;
  191. BEGIN
  192. NOT;
  193. WHILE op IN mult_ops DO BEGIN
  194. save the result;
  195. mult_op:=op;
  196. NOT;
  197. emit operation(mult_op);
  198. END;
  199. END;
  200. PROCEDURE add_level; { addition ... }
  201. VAR add_op:operation;
  202. BEGIN
  203. mult_level;
  204. WHILE op IN add_ops DO BEGIN
  205. save the result;
  206. add_op:=op;
  207. mult_level;
  208. emit operation(add_op);
  209. END;
  210. END;
  211. PROCEDURE expression; { comparisons, IN }
  212. VAR cmp_op:operation;
  213. BEGIN
  214. add_level;
  215. IF op IN cmp_ops THEN BEGIN
  216. save the result;
  217. cmp_op:=op;
  218. add_level;
  219. emit operation(cmp_op);
  220. END;
  221. END;
  222. </pre>
  223. <p><b>Example 1:</b> Translation of (a+b)=c -&gt; RPN = a , b + c =
  224. </p><p></p><pre> curr. char, stack (active procedure), code produced
  225. ---
  226. (:expression add_level mult_level not neg atom
  227. a:... expression add_level mult_level not neg atom
  228. +:... expression add_level -&gt; MOV AX,a
  229. b:... expression add_level mult_level not neg atom
  230. ):... expression add_level -&gt; ADD AX,b
  231. ):expression add_level mult_level not neg atom
  232. =:expression
  233. c:expression add_level mult_level not neg atom
  234. :expression -&gt; CMP AX,c
  235. </pre>
  236. <p><b>Please note:</b>
  237. </p><ul>
  238. <li>The parentheses trigger a recursive call of the expression procedure.</li>
  239. <li>The code production always lags behind the analysis. This improves the
  240. code produced (e.g. <code>ADD AX,b</code>).</li></ul>
  241. <p><b>Example 2:</b> Translation of a+b*c -&gt; RPN = a , b , c * +
  242. </p><p></p><pre> curr. char, stack (active procedure), code produced
  243. ---
  244. a:expression add_level mult_level not neg atom
  245. +:expression add_level -&gt; MOV AX,a
  246. b:expression add_level mult_level not neg atom
  247. *:expression add_level mult_level -&gt; PUSH AX
  248. -&gt; MOV AX,b
  249. c:expression add_level mult_level not neg atom
  250. :expression add_level mult_level -&gt; IMUL c
  251. :expression add_level -&gt; POP CX
  252. -&gt; ADD AX,CX
  253. </pre>
  254. <p><b>Please note:</b>
  255. </p><ul>
  256. <li>The content of <code>a</code> must be stacked, as the AX register
  257. is needed for the multiplication. This is recognized by setting the
  258. flag <code>push_ax</code>. If subsequent code uses the AX register (destroying
  259. its content), it has to emit <code>PUSH AX</code>. Finally, if this
  260. has happened, the register must be restored by <code>POP CX</code>.</li>
  261. <li>The code produced is rather simple-minded. By transforming the
  262. expression to b*c+a better code could be produced:
  263. <pre> MOV AX,b
  264. IMUL c
  265. ADD AX,a
  266. </pre></li></ul>
  267. During evaluation, type checking and type conversion (Integer -&gt; Real...) is
  268. also done.
  269. <p>The 8088 instruction set is often not used well. a:=a+1 yields this code (INC
  270. a would be better):
  271. </p><pre> MOV AX,a
  272. ADD AX,#1
  273. MOV a,AX
  274. </pre>
  275. Expressions usually account for the bulk of the code produced, so their
  276. translation is very important.
  277. </td>
  278. </tr>
  279. <tr><td colspan="5" valign="baseline" bgcolor="#EEEEEE">
  280. <p><font size="4"><b>Optimization</b></font></p></td></tr>
  281. <tr><td colspan="5" valign="top" align="left" bgcolor="#FFFFFF">
  282. <p>The goal of code optimization is reducing the size and/or execution time of
  283. the code produced. It is usually impossible to find an optimal solution, as a
  284. space-time tradeoff has to be made. TURBO Pascal doesn't have an optimizer.
  285. However, to improve the efficiency of your programs by manual optimizations or
  286. by add-on optimizers, it is good to know how common optimizations work.
  287. </p><p>Optimizations can be local or global: They can cover a single statement or an
  288. entire program. Global optimization is much more difficult and can cause
  289. problems. GOTO's and function or procedure calls can keep the optimizer from
  290. working efficiently.
  291. </p><p>Side effects can cause errors that are hard to find. Try it - you'll get what
  292. you deserve... An example:
  293. </p><pre> FUNCTION funny:INTEGER;
  294. BEGIN
  295. side_effect:=side_effect+1;
  296. funny:=5;
  297. END;
  298. ...
  299. a:=side_effect+funny+side_effect;
  300. </pre>
  301. The evaluation sequence and thus the result depends on the compiler used.
  302. <p>Variables don't necessarily stay constant between assignments. Consider this:
  303. </p><pre> wait_int:=FALSE;
  304. REPEAT UNTIL wait_int;
  305. </pre>
  306. This might wait for an interrupt procedure to set a flag. An optimizing
  307. compiler would convert this to an endless loop... <i>Modern C compilers use
  308. the <code>volatile</code> keyword to avoid this.</i>
  309. <p><b>Use of Register Variables</b>
  310. </p><p>Many load and store operations can be eliminated by using register variables.
  311. On the 8088 this is rather difficult, as there are few registers, often with
  312. special uses.
  313. </p><p><b>Common Subexpressions</b><br>
  314. </p><pre> c:=(a+b)*d;
  315. e:=g-(a+b);
  316. </pre>
  317. The subexpression (a+b) can be used twice. Expressions of the form a[i]:=a[i]+
  318. 1 also are a good target for optimizations.
  319. <p><b>Array Indexing</b>
  320. </p><p>References with constant indices (a[5]) or indices with a constant offset
  321. (a[i+1]) can be optimized. Array indexing in loops can often
  322. be improved considerably, too.
  323. </p><p><b>Constant Folding</b>
  324. </p><p>Programs can be more readable if constants expressions can be written in a
  325. symbolic form. The compiler can evaluate these expressions at compilation time.
  326. <i>Later versions of the compiler do this.</i>
  327. </p><p><b>Strength Reduction</b>
  328. </p><p>This means replacing operations by "cheaper" equivalents, e.g. x*0.2 instead
  329. of x/5 (multiplications are faster than divisions).
  330. </p><p><b>Loop Optimization</b><br>
  331. </p><pre> FOR i:=1 TO 100 DO dest[i]:=a+b;
  332. </pre>
  333. The subexpression a+b can be evaluated outside the loop, as a and b don't
  334. change in the loop.
  335. <p><b>Dead Code Elimination</b><br>
  336. </p><pre> CONST debug=FALSE;
  337. IF debug THEN writeln('Debug');
  338. </pre>
  339. The IF statement can be left out - the condition is never met. The same thing
  340. can be done with procedures which are never used. There are optimizers that
  341. eliminate all unused procedures from the run-time library of programs
  342. translated by TURBO Pascal.
  343. <i>Later versions of the compiler do this.</i>
  344. <p><b>Evaluation of Boolean Expressions</b><br>
  345. </p><pre> IF (a=5) AND (b=6) THEN ... can be changed into
  346. IF (a=5) THEN
  347. IF (b=6) THEN ...
  348. </pre>
  349. The same thing can be done with OR and NOT. Never expect boolean expressions
  350. to be executed completely ! <i>Later versions of the compiler do this.</i>
  351. <p><b>Variable Alignment</b>
  352. </p><p>Variables in the data segment and on the stack should be aligned to even
  353. offsets to improve performance on 16 bit PC's.
  354. </p></td>
  355. </tr>
  356. <tr><td colspan="5" valign="baseline" bgcolor="#EEEEEE">
  357. <p><font size="4"><b>Code Generation</b></font></p></td></tr>
  358. <tr><td colspan="5" valign="top" align="left" bgcolor="#FFFFFF">
  359. <p>The code generator has the difficult task of translating the elements
  360. recognized by the parser into executable code. If it gets difficult to tell
  361. whether the code has been generated by a human programmer or by a compiler
  362. then it is indeed a good one... Don't expect too much of this from TURBO.
  363. </p><p>In the following sections the code produced by TURBO will be explained.
  364. </p><p><b>Program</b><br>
  365. </p><pre> run-time library, if not chain file
  366. CALL initmem ;set segments
  367. W mainflag ;see source code
  368. W turbocs,turbods
  369. W cssize,dssize
  370. w heapsize,maxhpsz
  371. w maxfile,stdinsz,stdoutsz
  372. MOV BP,SP ;stack frame
  373. CALL uncrunch ;expand overlays
  374. W link,*+2
  375. definition part
  376. program part = main program
  377. XOR AX,AX ;Halt
  378. CALL progend
  379. </pre>
  380. <p><b>Definition Part</b>
  381. </p><p>The definition part may contain code, therefore it must be skipped over by:
  382. <br></p><pre> JMP l1
  383. <overlays |="" procedures="" functions="" structured="" constants="">
  384. l1:</overlays></pre>
  385. <p><b>Structured Constants</b>
  386. </p><p>Structured constants are stored in the same format as normal variables.
  387. </p><p><b>Overlays</b>
  388. </p><p>The space needed for overlays is not stored in the COM file. It is freed by
  389. the uncrunch procedure. This means moving up the subsequent code. This is
  390. executed at the beginning of program execution and after loading an overlay
  391. procedure.
  392. </p><pre> CALL rdover ;read overlay file
  393. W $ffff ;overlay procedure now in memory = invalid
  394. B 'over.001' ;name of overlay file
  395. In the section read from the overlay file:
  396. CALL uncrunch ;expand overlay
  397. W link,*+2 ;link for uncrunching
  398. overlay procedure / function
  399. W link,* ;for uncrunching
  400. </pre>
  401. <p><b>Forward Definitions</b>
  402. </p><p>For forward definitions a jump to the final definition is produced. The
  403. displacement is inserted when the real definition is made.
  404. </p><pre> JMP defined_proc
  405. </pre>
  406. <p><b>External Procedures</b>
  407. </p><p>The code read from an external file is not changed.
  408. </p><p><b>Procedure Definitions</b>
  409. </p><p>Local variables of procedures and functions are always stored on the stack.
  410. This means that only active procedures take up space on the stack. This also
  411. enables recursive calls. The transfer of parameters and the allocation of
  412. stack space can be quite complicated, thus slowing down procedure calls.
  413. </p><p>For every procedure a data structure called stack frame or activation record
  414. is built on the stack. The pointer to the stack frame is always stored in the
  415. BP register (the 8088 can't use the stack pointer SP as index register). The
  416. structure of the stack frame is as follows:
  417. </p><pre> BP+.:function result (space allocated by caller)
  418. BP+.:first parameter
  419. BP+4:last parameter
  420. BP+2:return address
  421. BP+0:pointer to caller's stack frame
  422. BP-2:new stack frame
  423. BP-4:local variables
  424. BP-.:stack top
  425. </pre>
  426. The code for a standard procedure entry looks like this:
  427. <pre> PUSH BP ;save old pointer
  428. MOV BP,SP ;set new pointer
  429. PUSH BP ;save new pointer (for display)
  430. definition part ;constants, local procedures
  431. SUB SP,#size ;allocate space for local variables
  432. ;1..2 bytes: DEC SP
  433. program part ;the actual procedure
  434. MOV SP,BP ;forget local variables
  435. POP BP ;restore old pointer
  436. RET prmsize ;return, remove parameters from stack
  437. ;no parameters: RET
  438. </pre>
  439. How function results are passed depends on their type. Scalars (integer...)
  440. are returned in AX, for boolean results the flags are set with OR AX,AX. Reals
  441. are on the stack anyway. Strings must be moved such that they occupy only
  442. their effective length:
  443. <pre> MOV DX,#pos_on_stack
  444. MOV CL,#max_len
  445. MOV SP,BP
  446. POP BP
  447. JMP retstr ;the normal end is omitted
  448. </pre>
  449. Unfortunately, things aren't that simple. Consider nested procedure
  450. definitions:
  451. <pre> PROCEDURE level1;
  452. VAR
  453. i:INTEGER;
  454. PROCEDURE level2;
  455. BEGIN
  456. i:=0;
  457. level2;
  458. END;
  459. BEGIN
  460. level2;
  461. END;
  462. </pre>
  463. The inner procedure level2 uses a local variable of level1, but also calls
  464. itself recursively. The stack offset of i depends on the calling order. TURBO
  465. Pascal uses a so called display to resolve this. The display contains pointers
  466. to the stack frames of calling procedures. Each procedure also adds its own
  467. pointer to the display. The display is an extension of the stack frame.
  468. <pre> BP+0:old pointer
  469. BP-2:display outermost procedure
  470. BP-.:display
  471. BP-.:display current procedure
  472. BP-.:local variables
  473. BP-.:stack top
  474. </pre>
  475. This is maintained by the following code:
  476. <pre> PUSH BP ;save old pointer
  477. MOV AX,SP ;set new pointer - keep BP
  478. PUSH [BP-nest*2] ;build display
  479. .. ;once for each nesting level
  480. MOV BP,AX ;set new pointer
  481. PUSH BP ;add own pointer to display
  482. definition part
  483. </pre>
  484. Newer CPU's (186, 286...) have special commands for these operations (ENTER,
  485. LEAVE). Please note that referencing variables via the display is slower than
  486. normal references. If speed is important don't nest procedure definitions.</td>
  487. </tr>
  488. <tr><td bgcolor="#EEEEEE"><p><font size="4" face="Trebuchet MS, Arial, Sans Serif"><b>Program Structures</b>
  489. </font></p></td></tr>
  490. <tr><td colspan="5" valign="top" align="left" bgcolor="#FFFFFF">
  491. <p><b>Program Part</b>
  492. </p><p></p><pre> statements
  493. l1: JMP l2 ;jump to the end
  494. POP AX ;GOTO, EXIT: clean up the stack
  495. JMP l1
  496. l2:
  497. </pre>
  498. GOTO's and EXIT's aren't really that simple. Sometimes stack variables (FOR,
  499. WITH) must be removed, which is done at the end of the procedure.
  500. <p><b>Statement</b>
  501. </p><p>If the user interrupt directive is set, an INT 3 is emitted for each statement.
  502. This calls a routine which checks for user interrupts. This feature can be
  503. "misused" to trace a program or to profile its execution time. If this isn't
  504. used anywhere in the program, you can also insert breakpoints as INLINE
  505. statements for debugging with DEBUG.
  506. </p><p><b>IF</b>
  507. </p><p>This has been covered above.
  508. </p><p><b>WHILE</b>
  509. </p><pre> l1: condition ;evaluate condition
  510. J.. l2 ;:condition met
  511. JMP l3
  512. l2: statement
  513. JMP l1 ;try again
  514. l3: ;end of loop
  515. </pre>
  516. <p><b>REPEAT</b>
  517. </p><pre> l1: statement
  518. condition ;evaluate condition
  519. J.. l2 ;condition met: end
  520. JMP l1 ;not met: repeat
  521. l2:
  522. </pre>
  523. REPEAT loops are faster than WHILE loops.
  524. <p><b>FOR</b>
  525. </p><p>The counter (stored on stack) and the control variable are independent:
  526. assignments to the control variable don't change the number of loop executions.
  527. </p><pre> starting value -&gt; AX
  528. PUSH AX
  529. ending value -&gt; AX
  530. POP CX
  531. XCHG CX,AX
  532. SUB CX,AX ;calculate difference
  533. JGE l1 ;(DOWNTO: JNG)
  534. JMP l3 ;don't execute
  535. l1: INC CX ;(DEC CX)
  536. <store beginning="" value="" in="" control="" variable="">
  537. l2: PUSH CX ;save counter
  538. <statement>
  539. POP CX ;restore counter
  540. DEC CX ;(INC CX)
  541. JZ l3 ;0: done
  542. INC loop_var ;(DEC) update control variable
  543. JMP l2 ;loop
  544. l3: ;end
  545. </statement></store></pre>
  546. <p><b>CASE</b>
  547. </p><pre> CASE .. OF
  548. 2,5 : .. ;
  549. 7..9: .. ;
  550. ELSE .. ;
  551. END;
  552. <scalar expression=""> ;evaluate selection
  553. CMP AX,#2 ;compare
  554. JZ ok1 ;:yes
  555. CMP AX,#5
  556. JZ ok1 ;:yes
  557. JMP test2 ;try next case
  558. ok1: <statement> ;ok - execute
  559. JMP endcase ;jump to end
  560. test2: CMP AX,#7 ;check subrange
  561. JL test2no ;:no
  562. CMP AX,#9
  563. JLE ok2 ;:yes
  564. test2no: JMP else ;no: execute ELSE part
  565. ok2: <statement> ;ok - execute
  566. JMP endcase
  567. else: <statement> ;ELSE part
  568. endcase:
  569. </statement></statement></statement></scalar></pre>
  570. Complicated CASE statements can exceed the range of short jumps. In this case
  571. so called "hips" are emitted:
  572. <pre> JMP hip2 ;skip hip
  573. hip: JMP ok ;jump to statement part
  574. hip2: ;normal continuation
  575. </pre>
  576. Some compilers also use this technique for IF and other statements.
  577. <p><b>GOTO</b>
  578. </p><p>A jump is emitted. If the GOTO leaves a WITH or a FOR block, the stack must be
  579. cleaned up. This is recognized and fixed at the end of the program part.
  580. </p><p><b>WITH</b>
  581. </p><p>The compiler has an internal WITH stack. The pointers for indexed WITH's are
  582. stored on the stack:
  583. </p><pre> set pointer to variable
  584. PUSH ES ;store pointer on stack
  585. PUSH DI
  586. statement
  587. ADD SP,#4 ;remove pointer from stack
  588. </pre>
  589. If the address is known at compilation time this is not necessary.
  590. <p><b>Procedure Calls</b>
  591. </p><p>If the directive K+ is set, a stack check is executed:
  592. </p><pre> MOV CX,#space_needed
  593. CALL xchkstk
  594. </pre>
  595. Then parameters are evaluated and passed:
  596. <ul>
  597. <li>Normal parameter:
  598. <pre> evaluate expression
  599. optional range check
  600. PUSH DX ;pointer
  601. PUSH AX ;scalar and pointer</pre></li>
  602. <li>String:
  603. <pre> MOV CL,#max_length;string is extended to maximal length
  604. CALL xstrparm ;-&gt; on stack like a local variable</pre></li>
  605. <li>Set:
  606. <pre> MOV CX,#crunch ;set crunch parameter:
  607. ;lo = number of bytes
  608. ;hi = number empty bytes at beginning
  609. CALL xsetparm ;adapt set</pre></li>
  610. <li>Real: already on stack</li>
  611. <li>Structured variable
  612. <pre> set pointer to variable
  613. MOV CX,#size
  614. MOV xblkparm ;copy variable onto stack</pre></li>
  615. <li>VAR parameters: put pointer on stack
  616. <pre> set pointer to variable
  617. PUSH ES
  618. PUSH DI
  619. </pre></li></ul>
  620. <p>For overlay procedures this must be inserted:
  621. </p><pre> MOV AX,#length/256
  622. MOV DX,#pos in overlay file / 256
  623. </pre>
  624. Then the procedure is called by
  625. <pre> CALL proc.
  626. </pre>
  627. <p><b>Function Call</b>
  628. </p><p>Stack space is allocated for the result (SUB SP,#space_needed), everything
  629. else is the same. On return the result is on stack (real, string) or in AX
  630. (integer, scalar). It would be easy to return structured variables - I don't
  631. understand why this isn't a standard feature. It would make things like
  632. complex arithmetics much easier.
  633. </p><p><b>Calling Standard Procedures and Functions</b>
  634. </p><p>Standard procedures like Read and Write can have any number of parameters of
  635. any type. This means much flexibility is needed. This problem is solved in
  636. TURBO in a very efficient way: The standard procedure table only contains the
  637. address of the corresponding translation routine. This routine emits the code
  638. for reading the parameters (some of them passed in registers !) and for
  639. calling the run-time library. Some functions (Swap, Hi, Lo) don't call a
  640. procedure but create inline code instead.
  641. </p><p><b>Assignments and Expressions</b>
  642. </p><ul>
  643. <li>Scalar / pointer: ES and DI are saved, if necessary. This is only
  644. done if the expression does not consist of a constant or a simple variable.</li>
  645. <li>Normal variable:
  646. <pre> set pointer to variable
  647. PUSH ES ;save pointer to destination variable
  648. PUSH DI
  649. evaluate expression
  650. type conversion
  651. store result in destination variable
  652. </pre></li>
  653. <li>Structured variable:
  654. <pre> pointer to second variable
  655. MOV CX,#size ;pointer to destination variable on stack
  656. CALL xmovevar ;copy variable
  657. </pre></li>
  658. <li>Type conversions:
  659. <pre> CALL xintreal ;Integer -&gt; Real
  660. MOV AH,AL ;Char -&gt; String: char -&gt; second byte
  661. MOV AL,01 ;length: 1 char
  662. PUSH AX ;push as string
  663. CALL xstrch ;String -&gt; Char
  664. </pre></li></ul>
  665. <p><b>Expressions</b>
  666. </p><p>The algorithms for translation of expressions were explained in section 4.
  667. Arithmetic operations for scalars are emitted by ecalc. For each operation
  668. there's a parameter block (starting at 973E) controlling the code generation.
  669. </p><pre> expr1 - 5 -&gt; SUB AX,#5
  670. expr1 - var -&gt; SUB AX,var
  671. expr1 - expr2 -&gt; XCHG CX,AX (first result in CX, second in AX)
  672. SUB AX,CX
  673. </pre>
  674. Expressions of the a:=a+1 type aren't translated well. a:=succ(a) is better,
  675. but not optimal.
  676. <p><b>Set Expressions</b>
  677. </p><p>Sets are stored in a compressed form and must be expanded to their full size
  678. (32 bytes) for doing set operations. Because of this set operations tend to be
  679. slow. Set constructors are handled in an inefficient way. [5,var1..var2] is
  680. translated like this:
  681. </p><pre> CALL sldempty ;store empty set on stack
  682. MOV AX,#5 ;expression = 5
  683. CALL setincl ;include element in set
  684. MOV AX,var1 ;first expression subrange
  685. PUSH AX ;save
  686. MOV AX,var2 ;second expression subrange
  687. CALL setinrng ;include subrange in set
  688. </pre>
  689. If the parameters are variable this is all right. For constant sets this is
  690. disastrous.
  691. <ul>
  692. <li><code>IF ch IN ['0'..'9','A'..'Z','a'..'z','_'] THEN ...</code>
  693. <p>The conventional solution. Very slow, as the set is always built when
  694. this is executed. Takes much space for complicated sets.</p></li>
  695. <li>
  696. <pre>CONST setcn:SET OF char = ['0'..'9','A'..'Z','a'..'z','_'];
  697. IF ch IN setcn THEN ...
  698. </pre>
  699. This takes up somewhat more space for this example (set constant takes up
  700. 32 bytes), but is much faster.</li>
  701. <li>
  702. <pre>CASE ch OF
  703. '0'..'9','A'..'Z','a'..'z','_':...;
  704. END;
  705. </pre>
  706. For simple cases this gives the shortest and fastest code.</li></ul>
  707. <p><b>Variable References</b>
  708. </p><p><b>MEM / MEMW</b>
  709. </p><pre> expression: segment
  710. PUSH AX
  711. expression: offset
  712. XCHG DI,AX ;pointer -&gt; ES:DI
  713. POP ES
  714. </pre>
  715. <p>Use ABSOLUTE for variables with a constant address.
  716. </p><p><b>WITH Indexing</b>
  717. </p><p>In a WITH block all variable names must be searched first in the scopes of the
  718. active records and then in the regular symbol table. This can take quite some
  719. time. If the base offset is not known at compilation time (WITH rec[var] DO),
  720. a WITH pointer must be calculated and stored on the stack, otherwise this is
  721. done at compilation time.
  722. </p><p><b>Array Indexing, String Indexing</b>
  723. </p><p>If necessary ES and DI must be saved before evaluation. Different code is
  724. produced depending on the index.
  725. </p><p>Constant index: The index is checked at compilation time, multiplied by the
  726. element size and added to the base offset, no code is emitted.
  727. </p><p>Variable index with range checking:
  728. </p><pre> SUB AX,#lower_bound (max be DEC AX / nothing)
  729. MOV CX,#upper_bound+1
  730. CALL xindchk ;check index
  731. </pre>
  732. Variable index without range checking: The subtraction of the lower bound can
  733. be omitted, it is multiplied by the element size and then subtracted from the
  734. base offset.
  735. <p>The index is multiplied by the element size. This is optimized for some
  736. important element sizes:
  737. </p><pre> no code ;size = 1
  738. SHL AX,1 ;size = 2
  739. SHL AX,1 ;size = 4
  740. SHL AX,1
  741. SHL AX,1 ;size = 6
  742. MOV CX,AX
  743. SHL AX,1
  744. ADD AX,CX
  745. MOV CX,#size ;other element sizes
  746. MUL CX
  747. </pre>
  748. <p align="left"> The index is then stored in DI:
  749. </p><pre> XCHG DI,AX
  750. /pre&gt;
  751. <p align="left"> or added to the existing index:
  752. </p><pre> ADD DI,AX
  753. </pre>
  754. <p><b>Record Indexing</b>
  755. </p><p>This is very simple: The offset of the record variable is added to the memory
  756. offset of the variable.
  757. </p><p><b>Pointer indexing</b>
  758. Pointers are loaded with LES DI,pointer_var.
  759. </p><p><b>Use of Addressing Modes</b>
  760. </p><p>The procedure <code>einstr</code> emits a command using the correct
  761. addressing mode. If necessary a segment prefix (CS: or ES:) is inserted.
  762. </p><ul>
  763. <li>not indexed, not on stack:
  764. <pre> MOV AX,var
  765. </pre></li>
  766. <li>indexed:
  767. <pre> MOV AX,[DI] ;no offset
  768. MOV AX,[DI]offs8 ;short offset (-128..127)
  769. MOV AX,[DI]offs16 ;long offset (0..65535)
  770. </pre></li>
  771. <li>stack, local variables:
  772. <pre> MOV AX,[BP]offs ;not indexed
  773. MOV AX,[BP+DI]offs;indexed
  774. </pre></li>
  775. <li>stack, callers local variables:
  776. <pre> MOV BX,[BP]-lev*2 ;read display pointer
  777. SS: ;indexed by BX - need prefix
  778. MOV AX,[BX]offs ;not indexed
  779. MOV AX,[BX+DI]offs;or indexed
  780. </pre></li></ul>
  781. <p><b>Calculate Pointer to Variable</b>
  782. </p><pre> indexing
  783. </pre>
  784. <p> The offset is read into DI:
  785. </p><pre> MOV DI,#offset ;not indexed
  786. ADD DI,#offset ;indexed: short or long offset
  787. LEA DI,[BP]offs ;stack: load effective address
  788. </pre>
  789. <p> The segment is handed over on the stack:
  790. </p><pre> PUSH CS/DS/ES/SS
  791. </pre>
  792. <p><b>Read Variable</b>
  793. </p><ul>
  794. <li>Scalar:
  795. <pre> MOV AX,var ;integer, not indexed, not on stack
  796. MOVB AL,var ;byte, not indexed, not on stack
  797. MOV AX,.. ;other integer (emitted by einstr)
  798. MOVB AL,.. ;other byte
  799. </pre>
  800. For byte variables XOR AH,AH is inserted - TURBO always uses integer variables
  801. internally.</li>
  802. <li>Pointer:
  803. <pre> LES AX,ptr_var ;AX = offset
  804. MOV DX,ES ;DX = segment
  805. </pre></li>
  806. <li>Real:
  807. <pre> set pointer to variable
  808. CALL xldreal
  809. </pre></li>
  810. <li>Set:
  811. <pre> set pointer to variable
  812. MOV CX,#set_crunch
  813. CALL xldset
  814. </pre></li>
  815. <li>String:
  816. <pre> set pointer to variable
  817. CALL strload
  818. </pre></li></ul>
  819. <p><b>Store Variable</b>
  820. </p><ul>
  821. <li>Scalar: If R+ is set, range checking is done:
  822. <pre> MOV CX,#lower_bound
  823. MOV DX,#upper_bound
  824. CALL xrngchk
  825. </pre>
  826. If the variable is neither indexed nor on the stack, there's a short form
  827. again:
  828. <pre> MOV var,AX ;integer
  829. MOVB var,AL ;byte
  830. </pre>
  831. Otherwise the correct MOV is emitted by <code>einstr</code>.</li>
  832. <li>Pointer:
  833. <pre> MOV dest,AX ;offset
  834. MOV dest+2,DX ;segment
  835. </pre></li>
  836. <li>Real:
  837. <pre> set pointer to variable
  838. CALL xstoreal
  839. </pre></li>
  840. <li>String:
  841. <pre> set pointer to variable
  842. MOV CL,#max_length
  843. CALL strstore
  844. </pre></li>
  845. <li>Set:
  846. <pre> set pointer to variable
  847. MOV CX,#set_crunch
  848. CALL setsto
  849. </pre></li></ul>
  850. </pre></td>
  851. </tr>
  852. <tr><td colspan="5" valign="baseline" bgcolor="#EEEEEE">
  853. <p><font size="4"><b>Symbol Table</b></font></p></td>
  854. </tr>
  855. <tr><td colspan="5" valign="baseline" bgcolor="#FFFFFF">
  856. <p>The symbol table manager has to insert and search symbols of any type. The
  857. difficulty is that definitions may be of any complexity and names may have any
  858. length. The structure implemented in TURBO closely represents the definition
  859. and reference sequence, making it easy to "navigate" through complex types.
  860. </p><p>Symbols are searched beginning with the most recent definition. For new
  861. definitions, the current block (limited by the "fence" set at the beginning of
  862. a procedure definition) and the keyword table are searched for duplicate
  863. definitions. Thus it is possible to override old definitions. At the end of a
  864. procedure local variables may be removed from the symbol table. This reduces
  865. memory usage and search time.
  866. </p><p><b>Symbol Table Entry Structure</b>
  867. </p><p>The symbol table "symtab" is stored in the stack segment (the actual stack
  868. doesn't need much space) and grows down. The entries have this basic structure:
  869. </p><pre> off :next entry
  870. --
  871. off-2:tag word = entry type
  872. off-4:name length
  873. off-5:name
  874. off-.:entry
  875. 0:offset to next entry
  876. </pre>
  877. The symbol table is always searched backwards, looking at the most recent
  878. entries first. A linear search is used. Sample symbol table entries can be
  879. found in the compiler tables (starting from 9277).
  880. <p><b>tag = 0100: Label</b>
  881. </p><pre> - 1:procedure nesting (to prevent jumps into or out of procedures)
  882. - 2:0=ok, FF=not yet defined
  883. - 4:offset
  884. </pre>
  885. <p><b>tag = 0200: Constant</b>
  886. </p><pre> - 1:type of constant
  887. - 3:constant - strings are stored backwards
  888. </pre>
  889. Structured constants are initialized variables stored in the code segment and
  890. are entered as such in the symbol table.
  891. <p><b>tag = 0300: Type</b>
  892. </p><pre> - 2:pointer to type definition
  893. </pre>
  894. <p><b>tag = 0400: Variable</b>
  895. </p><p>For subvariables of a record the low byte of the tag word is the number of the
  896. record definition this entry belongs to.
  897. </p><pre> - 2:pointer to type definition
  898. - 4:offset
  899. - 5:0=normal, FF=indirect (VAR)
  900. - 6:segment
  901. FF = DS
  902. FE = CS
  903. FD = ES (via pointer)
  904. .. = SS, the number corresponds to the procedure nesting level
  905. </pre>
  906. For ABSOLUTE variables of the form $B800:0 a pointer is stored in the code
  907. segment, the symbol table entry id CS indirect.
  908. <p><b>tag = 0500: Procedure</b>
  909. </p><p><b>tag = 0600: Function</b>
  910. </p><pre> - 2:function only - pointer to result type
  911. - 4:function only - offset on stack
  912. - 5:function only - 0 = normal, FF = indirect
  913. - 6:function only - segment: SS
  914. - 8:code offset of procedure / function
  915. - A:position in overlay file / of forward jump
  916. - C:0=ok, FF=forward definition
  917. - E:length of overlay procedure
  918. -10:number of parameter lists
  919. - 2:type pointer |may be repeated
  920. - 3:0=normal, FF=VAR parameter |
  921. - 4:number of parameters of this type |
  922. - 5:names |
  923. </pre>
  924. The parameters are also listed as local variables.
  925. <p><b>Structure of Subentries</b>
  926. </p><p>The normal entries aren't sufficient for the description of complex types.
  927. Unnamed subentries are used for this. For complex definitions this gives a
  928. tree structure. ARRAY[1..15] OF INTEGER is an array having the subrange 1..15
  929. as index type and INTEGER as element type.
  930. </p><p><b>tag = 0000, 0800: Subentries</b>
  931. </p><pre> - 2:component size
  932. - 4:lower bound / pointer to type definition
  933. - 6:upper bound / pointer to index type definition
  934. - 7:flag / record number
  935. - 8:component type
  936. 1 = array
  937. 2 = record
  938. 3 = set
  939. 4 = pointer
  940. 5 = typed file (FILE OF)
  941. 6 = text file (TEXT)
  942. 7 = untyped file (FILE)
  943. 8 = string
  944. 9 = real
  945. A = integer
  946. B = boolean
  947. C = char
  948. . = enumeration types (numbered)
  949. </pre>
  950. The compiler often uses this register assignment:
  951. <pre> CL = component type
  952. CH = where's the result ?
  953. 0 = constant
  954. 1 = variable
  955. 2 = in AX / on stack
  956. 3 = flags set (jz/jnz)
  957. 4 = comparison, use branch opcode brnchop
  958. </pre>
  959. The use of the subentry fields depends on the component type:
  960. <p>Array
  961. </p><pre> - 2:component size
  962. - 4:type pointer
  963. - 6:pointer to index type
  964. - 8:01
  965. </pre>
  966. Record
  967. <pre> - 2:component size
  968. - 7:record number
  969. - 8:02
  970. </pre>
  971. Set
  972. <pre> - 2:component size
  973. - 4:pointer to index type
  974. - 8:03
  975. </pre>
  976. Pointer
  977. <pre> - 2:component size = 4
  978. - 6:pointer to type / type name
  979. - 7:0=ok, FF=not yet resolved
  980. - 8:04
  981. </pre>
  982. Pointers can be forward defined. In this case the type name is stored as an
  983. invisible entry. The type pointer is then inserted later.
  984. <p>String
  985. </p><pre> - 2:length + 1
  986. - 8:08
  987. </pre>
  988. Text file with nonstandard buffer size
  989. <pre> - 2:component size
  990. - 8:06
  991. </pre>
  992. Enumeration type, subrange
  993. <pre> - 2:size: 1 or 2 bytes
  994. - 4:lower bound
  995. - 6:upper bound
  996. - 8:number of enumeration type
  997. </pre>
  998. The elements of an enumeration type are stored as constants.
  999. <p><b>Symbol Table Search</b>
  1000. </p><p>TURBO uses a linear search to find symbols in the symbol table. This can be
  1001. slow. A worst case program can get compilation speed down to 0.022 lines
  1002. per second...
  1003. </p><p>There is a better way: Hashing. <i>This is used in later versions of the
  1004. compiler.</i>
  1005. </p></td>
  1006. </tr>
  1007. <tr><td colspan="5" valign="baseline" bgcolor="#EEEEEE">
  1008. <p><font size="4"><b>Error Handler</b></font></p></td></tr>
  1009. <tr><td colspan="5" valign="top" align="left" bgcolor="#FFFFFF">
  1010. <p>Many compilers try to continue compilation after an error has been found. The
  1011. difficulty about this is that this shouldn't trigger an avalanche of
  1012. meaningless error messages.
  1013. </p><p>TURBO always stops if an error is found. This can also be seen as an
  1014. advantage: The programmer is forced to solve problems one at a time.
  1015. </p></td>
  1016. </tr>
  1017. <tr><td colspan="5" valign="baseline" bgcolor="#EEEEEE">
  1018. <p><font size="4"><b>Run-time Library</b></font></p></td>
  1019. </tr>
  1020. <tr><td colspan="5" valign="baseline" bgcolor="#FFFFFF">
  1021. <p>In the run-time library all standard procedures and functions needed for the
  1022. execution of programs are stored. TURBO does this in a rather wasteful (but
  1023. simple) way: it always inserts all procedures. <i>Later versions of the
  1024. compiler only include the procedures that are actually used.</i>
  1025. </p><p><b>Memory Map</b>
  1026. </p><p>The segments are allocated as follows:
  1027. </p><pre> --
  1028. stack (SS) (grows down)
  1029. --
  1030. free
  1031. --
  1032. heap (grows up)
  1033. --
  1034. variables (DS)
  1035. --
  1036. code (CS)
  1037. --
  1038. </pre>
  1039. <p><b>Heap</b>
  1040. </p><p>Heap storage is allocated in blocks having a size that is a multiple of 8
  1041. bytes. The free blocks are kept track of with this structure:
  1042. </p><pre> +0: pointer to next free block -&gt; linked list
  1043. +4: length of free block
  1044. +8: free block
  1045. </pre>
  1046. Hpstrt points to the first free block. The last block in the list - the free
  1047. space between stack and heap - is marked by a 0. If there isn't enough space
  1048. between heap and stack an error is reported.
  1049. <p><b>Floating Point Arithmetics</b>
  1050. </p><p>Floating point numbers are divided into two parts: The exponent gives the
  1051. order of magnitude, the mantissa gives the accuracy needed.
  1052. </p><pre> number = mantissa * 2 ^ exponent
  1053. </pre>
  1054. The mantissa is a binary fraction with an accuracy of 40 bits. The mantissa
  1055. always represents a number between 0.5 and 1, i.e. it is normalized. This
  1056. means the most significant bit is always 1. Here the sign is stored. The
  1057. examples use decimal numbers for simplicity.
  1058. <p>A floating point addition works as follows:
  1059. </p><pre> 0.95 E+00
  1060. + 0.60 E-01
  1061. 0.95 E+00 The number with the smaller exponent is
  1062. + 0.06 E+00 adjusted (de-normalized) to match the other.
  1063. = 1.01 E+00 The numbers can now be added as usual.
  1064. The result is too big, it must be normalized
  1065. = 0.10 E+01 and rounded up.
  1066. </pre>
  1067. Please note that floating point additions and subtractions can cause large
  1068. round off errors, if the exponents differ too much.
  1069. <p>It is often claimed that BCD arithmetics cause less errors. This is not
  1070. correct - they are not as visible. Some calculators actually do cosmetic
  1071. rounding, if the result is near to an integer number. The best way to avoid
  1072. round off errors is to calculate money amounts in cents and not in dollars.
  1073. BCD multiplications and divisions are slow. However, BCD does have one
  1074. advantage: Conversions to and from ASCII are much faster. As business
  1075. applications usually consist mainly of additions and subtractions, BCD can
  1076. actually be faster.
  1077. </p><p>Floating point multiplication
  1078. </p><pre> 0.13 E+00 The exponents are added (division: subtracted)
  1079. * 0.49 E+03 and the mantissas are multiplied.
  1080. 0.13 E+00
  1081. * 0.49 E+00
  1082. * E+03
  1083. = 0.0637 The additional digits are cut off and the
  1084. * E+03 result is normalized and rounded up.
  1085. = 0.64 E+02
  1086. </pre>
  1087. Square roots are evaluated using Newton's approximation. This is a good
  1088. solution, but not the best: The 8087 does square roots faster than divisions !
  1089. Transcendental functions are evaluated using the standard polynomials found in
  1090. math books. I have seen better algorithms to do this. Don't expect much speed
  1091. and precision from TURBO transcendentals.</td>
  1092. </tr>
  1093. <tr><td colspan="5" valign="baseline" bgcolor="#DDDDDD">
  1094. <p><font size="5"><b>Appendix</b></font></p></td></tr>
  1095. <tr><td colspan="5" valign="top" align="left" bgcolor="#EEEEEE">
  1096. <p><font size="4"><b>Bugs</b></font></p></td></tr>
  1097. <tr><td colspan="5" valign="top" align="left" bgcolor="#FFFFFF">
  1098. <p>Thanks to the relative simplicity of the algorithms used TURBO Pascal is
  1099. almost bug-free. Well, almost.
  1100. </p><p><b>Set as Procedure Parameter</b>
  1101. </p><p>If the last element is in the range 248..255 and the first above 7, this
  1102. doesn't work. Redefine the set or pass it as a VAR parameter.
  1103. </p><p><b>SizeOf</b>
  1104. </p><p>Sometimes a redundant load is emitted.
  1105. </p><p><b>UpCase</b>
  1106. </p><p>The argument type is not checked. Try UpCase(15).
  1107. </p><p><b>WHILE</b>
  1108. </p><p>DO can be omitted.</p></td>
  1109. </tr>
  1110. <tr><td colspan="5" valign="top" align="left" bgcolor="#EEEEEE">
  1111. <p><font size="4"><b>Compiler Speed</b></font></p></td>
  1112. </tr>
  1113. <tr><td colspan="5" valign="top" align="left" bgcolor="#FFFFFF">
  1114. <p>TURBO may be faster than most other compilers, but there's still a wide margin
  1115. for improvements:
  1116. </p><ul>
  1117. <li>symbol table: use hashing</li>
  1118. <li>include files: use larger buffer</li>
  1119. <li>don't copy source lines into another buffer</li>
  1120. </ul>
  1121. The editor could be much faster. The screen is re-displayed in an "intelligent"
  1122. way (using DelLine and InsLine). On a terminal this may be faster - with
  1123. memory mapped video this is a nuisance. Search and replace is slow.</td>
  1124. </tr>
  1125. <tr><td colspan="5" valign="top" align="left" bgcolor="#EEEEEE">
  1126. <p><font size="4"><b>Write Faster Programs Using TURBO Pascal</b></font></p></td>
  1127. </tr>
  1128. <tr><td colspan="5" valign="top" align="left" bgcolor="#FFFFFF">
  1129. <p>Avoid the standard string functions.
  1130. </p><ul>
  1131. <li>Use ord(st[0]) instead of length(st)</li>
  1132. <li>Use move instead of string assignments:
  1133. <pre> move(st1,st2,max_len+1);
  1134. </pre></li></ul>
  1135. <p>Write real constants with decimal point (10.0 instead of 10). This eliminates
  1136. conversions.
  1137. </p><p>GOTO statement considered harmful. So what... If a GOTO is the best way to
  1138. express a program structure - use it ! TURBO GOTO's are still better than
  1139. BASIC GOTO's: names can be used instead of numbers.
  1140. </p><p>I/O can be improved if larger buffers are used (use multiples of 512 bytes
  1141. for
  1142. best results). Use BlockRead and BlockWrite. If large blocks are read or
  1143. written the MS-DOS and TURBO overhead doesn't matter as much as for small
  1144. blocks.</p></td>
  1145. </tr>
  1146. </tbody></table>
  1147. </td></tr>
  1148. <tr><td colspan="5" valign="middle" height="25" align="center" bgcolor="#CCCCCC">
  1149. <font size="3" face="TrebuchetMS, Arial, sans-serif" color="#CC0000">
  1150. © 2002-2010 PC Engines GmbH. All rights reserved.</font>
  1151. </td></tr></tbody></table>
  1152. </font>
  1153. </body></html>