Turbo Pascal 3.0 compiler and code generation internals.html 43 KB

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