| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330 |
- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/htm14/loose.dtd">
- <html><head>
- <meta http-equiv="content-type" content="text/html; charset=windows-1252">
- <title>Turbo Pascal 3.0 Compiler and Code Generation Internals</title>
- <meta name="description" content="Turbo Pascal 3.0 compiler / code generation
- internals">
- </head>
- <body>
- <font size="3" face="Trebuchet MS, Arial, Sans Serif">
- <table width="100%" align="left" border="0" cellpadding="0" cellspacing="0">
- <tbody><tr><td colspan="5" valign="top" align="left" bgcolor="#CCCCCC">
- <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>
- <tr><td colspan="5" width="100%" height="25" bgcolor="#CC0000">
- <font face="Trebuchet MS, Arial, sans-serif" color="#FFFFFF">
- <a href="http://www.pcengines.ch/alix.htm"><font color="#FFFFFF">ALIX</font></a>
- | <a href="http://www.pcengines.ch/cflash.htm"><font color="#FFFFFF">CompactFlash Adapters</font></a>
- | <a href="http://www.pcengines.ch/test.htm"><font color="#FFFFFF">Test Tools</font></a>
- | <a href="http://www.pcengines.ch/resource.htm"><font color="#FFFFFF">Attic</font></a>
- | <a href="http://www.pcengines.ch/about.htm"><font color="#FFFFFF">Info</font></a>
- | <a href="http://www.pcengines.ch/order.php"><font color="#FFFFFF">Shop</font></a>
- | <a href="http://www.pcengines.ch/support.htm"><font color="#FFFFFF">Support</font></a></font></td></tr>
- <tr><td colspan="5" valign="top">
- <table>
- <tbody><tr><td colspan="5" valign="baseline" bgcolor="#DDDDDD">
- <p><font size="5"><b>Turbo Pascal 3.0 Compiler / Code Generation Internals</b></font>
- </p></td></tr>
- <tr><td colspan="5" valign="top" align="left" bgcolor="#FFFFFF">
- <p>The following is documentation I created after reverse engineering the
- Turbo Pascal 3.01A compiler. While many features, e.g. units and objects,
- have been added, today's compiler is still related to the old code.
- </p><p>Before you flame me about stuff that has been fixed, remember that this
- is about an OLD version of the compiler.
- </p><p>NEW: This file will generate disassembled, commented source from YOUR
- 3.01A compiler -> <a href="http://www.pcengines.ch/file/scg.zip">SCG.ZIP</a>
- </p></td>
- </tr>
- <tr>
- <td colspan="5" valign="baseline" bgcolor="#EEEEEE">
- <p><font size="5"><b>Compiler Structure</b></font></p></td></tr>
- <tr>
- <td colspan="5" valign="top" align="left" bgcolor="#FFFFFF">
- <p>Compilers usually consist of the following functional groups:
- </p><ul>
- <li>Lexical analysis</li>
- <li>Syntactical analysis (parser)</li>
- <li>Optimizer</li>
- <li>Code generator</li>
- <li>Symbol table manager</li>
- <li>Error handler</li>
- </ul>
- <p>TURBO Pascal isn't your usual compiler... The parser is interspersed with
- portions of the code generator, and there is no optimizer. Most compilers
- need multiple passes to do their work, but TURBO is a (faster)
- single pass compiler.
- </p><p>Fortunately, programming languages of the Pascal type are designed with simple
- compilation in one pass in mind. All symbols must be defined before they are
- used. The compiler can easily determine the type of a constant without
- looking ahead:
- </p><ul>
- <li>symbol</li>
- <li>$7FFF</li>
- <li>12345</li>
- <li>12345.5 - This is an exception. TURBO first verifies whether a numeric
- constant is an integer or a real constant. Actually, a standard Pascal
- compiler doesn't have to do this - standard Pascal requires that the integer
- section of a real constant be a valid integer number !</li>
- <li>09CFH - This notation (used by many assemblers and languages) is a
- negative example. Compilers - like people - read from left to right. To
- read a number in this notation, it has to be read into a buffer to recognize
- its type, then it can be converted. Guess why TURBO uses $ for hex numbers...
- <i>I wonder why Niklaus Wirth chose this notation for Modula-2.</i></li></ul></td>
- </tr>
- <tr><td colspan="5" valign="baseline" bgcolor="#EEEEEE">
- <p><font size="4"><b>Lexical Analysis</b></font></p></td></tr>
- <tr><td colspan="5" valign="top" align="left" bgcolor="#FFFFFF">
- <p>The task of the lexical analysis is to read the source code from memory or
- from an include file, to eliminate comments and to recognize compiler
- directives, symbols and keywords.
- </p><p>It is called by the parser. On each call a language element (keyword, symbol,
- constant...) is read. The starting position is stored. If an error is
- recognized the editor will be started and the cursor will point to this
- position.</p></td>
- </tr>
- <tr><td colspan="5" valign="baseline" bgcolor="#EEEEEE">
- <p><font size="4"><b>Parsing Program Structures</b></font></p></td></tr>
- <tr><td colspan="5" valign="top" align="left" bgcolor="#FFFFFF">
- <p>The task of this part of the compiler is to analyze the structure of the
- program to be compiled and to check the syntax. Like most Pascal compilers,
- TURBO uses a recursive descent parser. The code generation is included in
- the parser.
- </p><p>The compilation of program structures is quite simple. Usually the syntax is
- described in Backus-Naur form or by a "railway diagram". As an example
- the IF statement will be covered.
- </p><p></p><pre> IF cond THEN stat1 { ELSE stat2 }
- ___________________
- ____ ______ ______ ___________ / \
- ->_IF_->_expr_->_THEN_->_statement_-<> -->
- \______ ___________/
- _ELSE_->_statement__
- </pre>
- <p>After reading an element, the parser takes the applicable track. If there
- isn't any the syntax is incorrect, and an error is reported.
- </p><p>It is possible to have a parser generated automatically by a so called
- compiler-compiler, if the Backus-Naur form of the syntax is given.
- Unfortunately this doesn't help very much: The really difficult parts of
- a compiler - code generation and optimization - must still be written
- manually.
- </p><p>How is the IF statement translated ? (The corresponding section in the
- compiler is at offset 6C12). The statement procedure reads an IF and calls the
- IF procedure. First the condition - actually an arithmetic expression
- of type boolean - is evaluated. This is done by calling the expression
- procedure. The expression is read until an illegal symbol (THEN) is found.
- This terminates the expression, which is checked for type boolean. The
- IF procedure inserts a conditional jump to the end of statement 1 here.
- The displacement is inserted later - it is not yet known. If the expression
- has been terminated by something else than a THEN, an error is reported.
- Now the first statement (stat1) is translated. Actually, this is a RECURSIVE
- call of the statement procedure (That's why this is a recursive descent
- parser). Please note that the syntax definition is recursive, too !
- Because of possible nested IF statements the variables of the IF
- procedure are saved on the stack. After this statement, an ELSE
- may follow. If it does, a jump to the end of stat2 is emitted
- and the jump from the beginning of stat1 is patched, then the second statement
- is translated and the second jump patched.
- </p><p>The code produced looks like this:
- </p><p></p><pre> (IF..THEN) (IF..THEN..ELSE)
- cond cond
- JNZ l1 JNZ l1
- JMP l2 JMP l2
- l1: stat 1 l1: stat 1
- l2: ... JMP l3
- l2: stat 2
- l3: ...
- </pre>
- The long jump at the beginning isn't always necessary. Unfortunately, the
- compiler cannot predict how long the statement will be. To improve this, the
- jump would have to be replaced by a short one and the subsequent code moved,
- which would complicate the compiler quite a bit.
- <p>All other program structures are translated in a similar way.
- </p></td>
- </tr>
- <tr><td colspan="5" valign="baseline" bgcolor="#EEEEEE">
- <p><font size="4"><b>Parsing Arithmetic Expressions</b></font></p></td></tr>
- <tr><td colspan="5" valign="top" align="left" bgcolor="#FFFFFF">
- <p>The evaluation of expressions is somewhat more complex, as the precedence of
- the operations has to be taken into account. The solution in TURBO is, however,
- quite simple (code starting at 7A70).
- </p><p>Expressions are usually translated to reverse polish notation (as used on
- Hewlett-Packard calculators and in the programming language FORTH).
- </p><p>There are five groups of operations:
- </p><ul>
- <li>negation (highest precedence)</li>
- <li>NOT</li>
- <li>multiplication, division, ...</li>
- <li>addition, subtraction, ...</li>
- <li>comparisons, IN (lowest precedence)
- </li></ul>
- <p>This translates into the following program structure:
- </p><p></p><pre> PROCEDURE atom; { element }
- BEGIN
- CASE op OF
- CONST:read constant
- VAR :read variable { indexing -> recursive }
- '(' :read expression { recursive }
- ')' must follow
- func :read parameters { recursive }
- emit function call
- TYPE :'(' must follow { type conversion, e.g. Integer(TRUE) }
- read expression { recursive }
- ')' must follow
- convert type -> type wanted
- ELSE syntax error;
- END;
- END;
- PROCEDURE neg; { negation - }
- VAR negflag:BOOLEAN;
- BEGIN
- negflag:=(op=neg);
- atom;
- IF negflag THEN emit negation;
- END;
- PROCEDURE NOT; { NOT }
- VAR notflag:BOOLEAN;
- BEGIN
- notflag:=(op=NOT);
- neg;
- IF notflag THEN emit NOT;
- END;
- PROCEDURE mult_level; { multiplication ... }
- VAR mult_op:operation;
- BEGIN
- NOT;
- WHILE op IN mult_ops DO BEGIN
- save the result;
- mult_op:=op;
- NOT;
- emit operation(mult_op);
- END;
- END;
- PROCEDURE add_level; { addition ... }
- VAR add_op:operation;
- BEGIN
- mult_level;
- WHILE op IN add_ops DO BEGIN
- save the result;
- add_op:=op;
- mult_level;
- emit operation(add_op);
- END;
- END;
- PROCEDURE expression; { comparisons, IN }
- VAR cmp_op:operation;
- BEGIN
- add_level;
- IF op IN cmp_ops THEN BEGIN
- save the result;
- cmp_op:=op;
- add_level;
- emit operation(cmp_op);
- END;
- END;
- </pre>
- <p><b>Example 1:</b> Translation of (a+b)=c -> RPN = a , b + c =
- </p><p></p><pre> curr. char, stack (active procedure), code produced
- ---
- (:expression add_level mult_level not neg atom
- a:... expression add_level mult_level not neg atom
- +:... expression add_level -> MOV AX,a
- b:... expression add_level mult_level not neg atom
- ):... expression add_level -> ADD AX,b
- ):expression add_level mult_level not neg atom
- =:expression
- c:expression add_level mult_level not neg atom
- :expression -> CMP AX,c
- </pre>
- <p><b>Please note:</b>
- </p><ul>
- <li>The parentheses trigger a recursive call of the expression procedure.</li>
- <li>The code production always lags behind the analysis. This improves the
- code produced (e.g. <code>ADD AX,b</code>).</li></ul>
- <p><b>Example 2:</b> Translation of a+b*c -> RPN = a , b , c * +
- </p><p></p><pre> curr. char, stack (active procedure), code produced
- ---
- a:expression add_level mult_level not neg atom
- +:expression add_level -> MOV AX,a
- b:expression add_level mult_level not neg atom
- *:expression add_level mult_level -> PUSH AX
- -> MOV AX,b
- c:expression add_level mult_level not neg atom
- :expression add_level mult_level -> IMUL c
- :expression add_level -> POP CX
- -> ADD AX,CX
- </pre>
- <p><b>Please note:</b>
- </p><ul>
- <li>The content of <code>a</code> must be stacked, as the AX register
- is needed for the multiplication. This is recognized by setting the
- flag <code>push_ax</code>. If subsequent code uses the AX register (destroying
- its content), it has to emit <code>PUSH AX</code>. Finally, if this
- has happened, the register must be restored by <code>POP CX</code>.</li>
- <li>The code produced is rather simple-minded. By transforming the
- expression to b*c+a better code could be produced:
- <pre> MOV AX,b
- IMUL c
- ADD AX,a
- </pre></li></ul>
- During evaluation, type checking and type conversion (Integer -> Real...) is
- also done.
- <p>The 8088 instruction set is often not used well. a:=a+1 yields this code (INC
- a would be better):
- </p><pre> MOV AX,a
- ADD AX,#1
- MOV a,AX
- </pre>
- Expressions usually account for the bulk of the code produced, so their
- translation is very important.
- </td>
- </tr>
- <tr><td colspan="5" valign="baseline" bgcolor="#EEEEEE">
- <p><font size="4"><b>Optimization</b></font></p></td></tr>
- <tr><td colspan="5" valign="top" align="left" bgcolor="#FFFFFF">
- <p>The goal of code optimization is reducing the size and/or execution time of
- the code produced. It is usually impossible to find an optimal solution, as a
- space-time tradeoff has to be made. TURBO Pascal doesn't have an optimizer.
- However, to improve the efficiency of your programs by manual optimizations or
- by add-on optimizers, it is good to know how common optimizations work.
- </p><p>Optimizations can be local or global: They can cover a single statement or an
- entire program. Global optimization is much more difficult and can cause
- problems. GOTO's and function or procedure calls can keep the optimizer from
- working efficiently.
- </p><p>Side effects can cause errors that are hard to find. Try it - you'll get what
- you deserve... An example:
- </p><pre> FUNCTION funny:INTEGER;
- BEGIN
- side_effect:=side_effect+1;
- funny:=5;
- END;
- ...
- a:=side_effect+funny+side_effect;
- </pre>
- The evaluation sequence and thus the result depends on the compiler used.
- <p>Variables don't necessarily stay constant between assignments. Consider this:
- </p><pre> wait_int:=FALSE;
- REPEAT UNTIL wait_int;
- </pre>
- This might wait for an interrupt procedure to set a flag. An optimizing
- compiler would convert this to an endless loop... <i>Modern C compilers use
- the <code>volatile</code> keyword to avoid this.</i>
- <p><b>Use of Register Variables</b>
- </p><p>Many load and store operations can be eliminated by using register variables.
- On the 8088 this is rather difficult, as there are few registers, often with
- special uses.
- </p><p><b>Common Subexpressions</b><br>
- </p><pre> c:=(a+b)*d;
- e:=g-(a+b);
- </pre>
- The subexpression (a+b) can be used twice. Expressions of the form a[i]:=a[i]+
- 1 also are a good target for optimizations.
- <p><b>Array Indexing</b>
- </p><p>References with constant indices (a[5]) or indices with a constant offset
- (a[i+1]) can be optimized. Array indexing in loops can often
- be improved considerably, too.
- </p><p><b>Constant Folding</b>
- </p><p>Programs can be more readable if constants expressions can be written in a
- symbolic form. The compiler can evaluate these expressions at compilation time.
- <i>Later versions of the compiler do this.</i>
- </p><p><b>Strength Reduction</b>
- </p><p>This means replacing operations by "cheaper" equivalents, e.g. x*0.2 instead
- of x/5 (multiplications are faster than divisions).
- </p><p><b>Loop Optimization</b><br>
- </p><pre> FOR i:=1 TO 100 DO dest[i]:=a+b;
- </pre>
- The subexpression a+b can be evaluated outside the loop, as a and b don't
- change in the loop.
- <p><b>Dead Code Elimination</b><br>
- </p><pre> CONST debug=FALSE;
- IF debug THEN writeln('Debug');
- </pre>
- The IF statement can be left out - the condition is never met. The same thing
- can be done with procedures which are never used. There are optimizers that
- eliminate all unused procedures from the run-time library of programs
- translated by TURBO Pascal.
- <i>Later versions of the compiler do this.</i>
- <p><b>Evaluation of Boolean Expressions</b><br>
- </p><pre> IF (a=5) AND (b=6) THEN ... can be changed into
- IF (a=5) THEN
- IF (b=6) THEN ...
- </pre>
- The same thing can be done with OR and NOT. Never expect boolean expressions
- to be executed completely ! <i>Later versions of the compiler do this.</i>
- <p><b>Variable Alignment</b>
- </p><p>Variables in the data segment and on the stack should be aligned to even
- offsets to improve performance on 16 bit PC's.
- </p></td>
- </tr>
- <tr><td colspan="5" valign="baseline" bgcolor="#EEEEEE">
- <p><font size="4"><b>Code Generation</b></font></p></td></tr>
- <tr><td colspan="5" valign="top" align="left" bgcolor="#FFFFFF">
- <p>The code generator has the difficult task of translating the elements
- recognized by the parser into executable code. If it gets difficult to tell
- whether the code has been generated by a human programmer or by a compiler
- then it is indeed a good one... Don't expect too much of this from TURBO.
- </p><p>In the following sections the code produced by TURBO will be explained.
- </p><p><b>Program</b><br>
- </p><pre> run-time library, if not chain file
- CALL initmem ;set segments
- W mainflag ;see source code
- W turbocs,turbods
- W cssize,dssize
- w heapsize,maxhpsz
- w maxfile,stdinsz,stdoutsz
- MOV BP,SP ;stack frame
- CALL uncrunch ;expand overlays
- W link,*+2
- definition part
- program part = main program
- XOR AX,AX ;Halt
- CALL progend
- </pre>
- <p><b>Definition Part</b>
- </p><p>The definition part may contain code, therefore it must be skipped over by:
- <br></p><pre> JMP l1
- <overlays |="" procedures="" functions="" structured="" constants="">
- l1:</overlays></pre>
- <p><b>Structured Constants</b>
- </p><p>Structured constants are stored in the same format as normal variables.
- </p><p><b>Overlays</b>
- </p><p>The space needed for overlays is not stored in the COM file. It is freed by
- the uncrunch procedure. This means moving up the subsequent code. This is
- executed at the beginning of program execution and after loading an overlay
- procedure.
- </p><pre> CALL rdover ;read overlay file
- W $ffff ;overlay procedure now in memory = invalid
- B 'over.001' ;name of overlay file
- In the section read from the overlay file:
- CALL uncrunch ;expand overlay
- W link,*+2 ;link for uncrunching
- overlay procedure / function
- W link,* ;for uncrunching
- </pre>
- <p><b>Forward Definitions</b>
- </p><p>For forward definitions a jump to the final definition is produced. The
- displacement is inserted when the real definition is made.
- </p><pre> JMP defined_proc
- </pre>
- <p><b>External Procedures</b>
- </p><p>The code read from an external file is not changed.
-
- </p><p><b>Procedure Definitions</b>
- </p><p>Local variables of procedures and functions are always stored on the stack.
- This means that only active procedures take up space on the stack. This also
- enables recursive calls. The transfer of parameters and the allocation of
- stack space can be quite complicated, thus slowing down procedure calls.
- </p><p>For every procedure a data structure called stack frame or activation record
- is built on the stack. The pointer to the stack frame is always stored in the
- BP register (the 8088 can't use the stack pointer SP as index register). The
- structure of the stack frame is as follows:
- </p><pre> BP+.:function result (space allocated by caller)
- BP+.:first parameter
- BP+4:last parameter
- BP+2:return address
- BP+0:pointer to caller's stack frame
- BP-2:new stack frame
- BP-4:local variables
- BP-.:stack top
- </pre>
- The code for a standard procedure entry looks like this:
- <pre> PUSH BP ;save old pointer
- MOV BP,SP ;set new pointer
- PUSH BP ;save new pointer (for display)
- definition part ;constants, local procedures
- SUB SP,#size ;allocate space for local variables
- ;1..2 bytes: DEC SP
- program part ;the actual procedure
- MOV SP,BP ;forget local variables
- POP BP ;restore old pointer
- RET prmsize ;return, remove parameters from stack
- ;no parameters: RET
- </pre>
- How function results are passed depends on their type. Scalars (integer...)
- are returned in AX, for boolean results the flags are set with OR AX,AX. Reals
- are on the stack anyway. Strings must be moved such that they occupy only
- their effective length:
- <pre> MOV DX,#pos_on_stack
- MOV CL,#max_len
- MOV SP,BP
- POP BP
- JMP retstr ;the normal end is omitted
- </pre>
- Unfortunately, things aren't that simple. Consider nested procedure
- definitions:
- <pre> PROCEDURE level1;
- VAR
- i:INTEGER;
- PROCEDURE level2;
- BEGIN
- i:=0;
- level2;
- END;
- BEGIN
- level2;
- END;
- </pre>
- The inner procedure level2 uses a local variable of level1, but also calls
- itself recursively. The stack offset of i depends on the calling order. TURBO
- Pascal uses a so called display to resolve this. The display contains pointers
- to the stack frames of calling procedures. Each procedure also adds its own
- pointer to the display. The display is an extension of the stack frame.
- <pre> BP+0:old pointer
- BP-2:display outermost procedure
- BP-.:display
- BP-.:display current procedure
- BP-.:local variables
- BP-.:stack top
- </pre>
- This is maintained by the following code:
- <pre> PUSH BP ;save old pointer
- MOV AX,SP ;set new pointer - keep BP
- PUSH [BP-nest*2] ;build display
- .. ;once for each nesting level
- MOV BP,AX ;set new pointer
- PUSH BP ;add own pointer to display
- definition part
- </pre>
- Newer CPU's (186, 286...) have special commands for these operations (ENTER,
- LEAVE). Please note that referencing variables via the display is slower than
- normal references. If speed is important don't nest procedure definitions.</td>
- </tr>
- <tr><td bgcolor="#EEEEEE"><p><font size="4" face="Trebuchet MS, Arial, Sans Serif"><b>Program Structures</b>
- </font></p></td></tr>
- <tr><td colspan="5" valign="top" align="left" bgcolor="#FFFFFF">
- <p><b>Program Part</b>
- </p><p></p><pre> statements
- l1: JMP l2 ;jump to the end
- POP AX ;GOTO, EXIT: clean up the stack
- JMP l1
- l2:
- </pre>
- GOTO's and EXIT's aren't really that simple. Sometimes stack variables (FOR,
- WITH) must be removed, which is done at the end of the procedure.
- <p><b>Statement</b>
- </p><p>If the user interrupt directive is set, an INT 3 is emitted for each statement.
- This calls a routine which checks for user interrupts. This feature can be
- "misused" to trace a program or to profile its execution time. If this isn't
- used anywhere in the program, you can also insert breakpoints as INLINE
- statements for debugging with DEBUG.
- </p><p><b>IF</b>
- </p><p>This has been covered above.
- </p><p><b>WHILE</b>
- </p><pre> l1: condition ;evaluate condition
- J.. l2 ;:condition met
- JMP l3
- l2: statement
- JMP l1 ;try again
- l3: ;end of loop
- </pre>
- <p><b>REPEAT</b>
- </p><pre> l1: statement
- condition ;evaluate condition
- J.. l2 ;condition met: end
- JMP l1 ;not met: repeat
- l2:
- </pre>
- REPEAT loops are faster than WHILE loops.
- <p><b>FOR</b>
- </p><p>The counter (stored on stack) and the control variable are independent:
- assignments to the control variable don't change the number of loop executions.
- </p><pre> starting value -> AX
- PUSH AX
- ending value -> AX
- POP CX
- XCHG CX,AX
- SUB CX,AX ;calculate difference
- JGE l1 ;(DOWNTO: JNG)
- JMP l3 ;don't execute
- l1: INC CX ;(DEC CX)
- <store beginning="" value="" in="" control="" variable="">
- l2: PUSH CX ;save counter
- <statement>
- POP CX ;restore counter
- DEC CX ;(INC CX)
- JZ l3 ;0: done
- INC loop_var ;(DEC) update control variable
- JMP l2 ;loop
- l3: ;end
- </statement></store></pre>
- <p><b>CASE</b>
- </p><pre> CASE .. OF
- 2,5 : .. ;
- 7..9: .. ;
- ELSE .. ;
- END;
- <scalar expression=""> ;evaluate selection
- CMP AX,#2 ;compare
- JZ ok1 ;:yes
- CMP AX,#5
- JZ ok1 ;:yes
- JMP test2 ;try next case
- ok1: <statement> ;ok - execute
- JMP endcase ;jump to end
- test2: CMP AX,#7 ;check subrange
- JL test2no ;:no
- CMP AX,#9
- JLE ok2 ;:yes
- test2no: JMP else ;no: execute ELSE part
- ok2: <statement> ;ok - execute
- JMP endcase
- else: <statement> ;ELSE part
- endcase:
- </statement></statement></statement></scalar></pre>
- Complicated CASE statements can exceed the range of short jumps. In this case
- so called "hips" are emitted:
- <pre> JMP hip2 ;skip hip
- hip: JMP ok ;jump to statement part
- hip2: ;normal continuation
- </pre>
- Some compilers also use this technique for IF and other statements.
- <p><b>GOTO</b>
- </p><p>A jump is emitted. If the GOTO leaves a WITH or a FOR block, the stack must be
- cleaned up. This is recognized and fixed at the end of the program part.
- </p><p><b>WITH</b>
- </p><p>The compiler has an internal WITH stack. The pointers for indexed WITH's are
- stored on the stack:
- </p><pre> set pointer to variable
- PUSH ES ;store pointer on stack
- PUSH DI
- statement
- ADD SP,#4 ;remove pointer from stack
- </pre>
- If the address is known at compilation time this is not necessary.
- <p><b>Procedure Calls</b>
- </p><p>If the directive K+ is set, a stack check is executed:
- </p><pre> MOV CX,#space_needed
- CALL xchkstk
- </pre>
- Then parameters are evaluated and passed:
- <ul>
- <li>Normal parameter:
- <pre> evaluate expression
- optional range check
- PUSH DX ;pointer
- PUSH AX ;scalar and pointer</pre></li>
- <li>String:
- <pre> MOV CL,#max_length;string is extended to maximal length
- CALL xstrparm ;-> on stack like a local variable</pre></li>
- <li>Set:
- <pre> MOV CX,#crunch ;set crunch parameter:
- ;lo = number of bytes
- ;hi = number empty bytes at beginning
- CALL xsetparm ;adapt set</pre></li>
- <li>Real: already on stack</li>
- <li>Structured variable
- <pre> set pointer to variable
- MOV CX,#size
- MOV xblkparm ;copy variable onto stack</pre></li>
- <li>VAR parameters: put pointer on stack
- <pre> set pointer to variable
- PUSH ES
- PUSH DI
- </pre></li></ul>
- <p>For overlay procedures this must be inserted:
- </p><pre> MOV AX,#length/256
- MOV DX,#pos in overlay file / 256
- </pre>
- Then the procedure is called by
- <pre> CALL proc.
- </pre>
- <p><b>Function Call</b>
- </p><p>Stack space is allocated for the result (SUB SP,#space_needed), everything
- else is the same. On return the result is on stack (real, string) or in AX
- (integer, scalar). It would be easy to return structured variables - I don't
- understand why this isn't a standard feature. It would make things like
- complex arithmetics much easier.
- </p><p><b>Calling Standard Procedures and Functions</b>
- </p><p>Standard procedures like Read and Write can have any number of parameters of
- any type. This means much flexibility is needed. This problem is solved in
- TURBO in a very efficient way: The standard procedure table only contains the
- address of the corresponding translation routine. This routine emits the code
- for reading the parameters (some of them passed in registers !) and for
- calling the run-time library. Some functions (Swap, Hi, Lo) don't call a
- procedure but create inline code instead.
- </p><p><b>Assignments and Expressions</b>
- </p><ul>
- <li>Scalar / pointer: ES and DI are saved, if necessary. This is only
- done if the expression does not consist of a constant or a simple variable.</li>
- <li>Normal variable:
- <pre> set pointer to variable
- PUSH ES ;save pointer to destination variable
- PUSH DI
- evaluate expression
- type conversion
- store result in destination variable
- </pre></li>
- <li>Structured variable:
- <pre> pointer to second variable
- MOV CX,#size ;pointer to destination variable on stack
- CALL xmovevar ;copy variable
- </pre></li>
- <li>Type conversions:
- <pre> CALL xintreal ;Integer -> Real
- MOV AH,AL ;Char -> String: char -> second byte
- MOV AL,01 ;length: 1 char
- PUSH AX ;push as string
- CALL xstrch ;String -> Char
- </pre></li></ul>
- <p><b>Expressions</b>
- </p><p>The algorithms for translation of expressions were explained in section 4.
- Arithmetic operations for scalars are emitted by ecalc. For each operation
- there's a parameter block (starting at 973E) controlling the code generation.
- </p><pre> expr1 - 5 -> SUB AX,#5
- expr1 - var -> SUB AX,var
- expr1 - expr2 -> XCHG CX,AX (first result in CX, second in AX)
- SUB AX,CX
- </pre>
- Expressions of the a:=a+1 type aren't translated well. a:=succ(a) is better,
- but not optimal.
- <p><b>Set Expressions</b>
- </p><p>Sets are stored in a compressed form and must be expanded to their full size
- (32 bytes) for doing set operations. Because of this set operations tend to be
- slow. Set constructors are handled in an inefficient way. [5,var1..var2] is
- translated like this:
- </p><pre> CALL sldempty ;store empty set on stack
- MOV AX,#5 ;expression = 5
- CALL setincl ;include element in set
- MOV AX,var1 ;first expression subrange
- PUSH AX ;save
- MOV AX,var2 ;second expression subrange
- CALL setinrng ;include subrange in set
- </pre>
- If the parameters are variable this is all right. For constant sets this is
- disastrous.
- <ul>
- <li><code>IF ch IN ['0'..'9','A'..'Z','a'..'z','_'] THEN ...</code>
- <p>The conventional solution. Very slow, as the set is always built when
- this is executed. Takes much space for complicated sets.</p></li>
- <li>
- <pre>CONST setcn:SET OF char = ['0'..'9','A'..'Z','a'..'z','_'];
- IF ch IN setcn THEN ...
- </pre>
- This takes up somewhat more space for this example (set constant takes up
- 32 bytes), but is much faster.</li>
- <li>
- <pre>CASE ch OF
- '0'..'9','A'..'Z','a'..'z','_':...;
- END;
- </pre>
- For simple cases this gives the shortest and fastest code.</li></ul>
- <p><b>Variable References</b>
- </p><p><b>MEM / MEMW</b>
- </p><pre> expression: segment
- PUSH AX
- expression: offset
- XCHG DI,AX ;pointer -> ES:DI
- POP ES
- </pre>
- <p>Use ABSOLUTE for variables with a constant address.
- </p><p><b>WITH Indexing</b>
- </p><p>In a WITH block all variable names must be searched first in the scopes of the
- active records and then in the regular symbol table. This can take quite some
- time. If the base offset is not known at compilation time (WITH rec[var] DO),
- a WITH pointer must be calculated and stored on the stack, otherwise this is
- done at compilation time.
- </p><p><b>Array Indexing, String Indexing</b>
- </p><p>If necessary ES and DI must be saved before evaluation. Different code is
- produced depending on the index.
- </p><p>Constant index: The index is checked at compilation time, multiplied by the
- element size and added to the base offset, no code is emitted.
- </p><p>Variable index with range checking:
- </p><pre> SUB AX,#lower_bound (max be DEC AX / nothing)
- MOV CX,#upper_bound+1
- CALL xindchk ;check index
- </pre>
- Variable index without range checking: The subtraction of the lower bound can
- be omitted, it is multiplied by the element size and then subtracted from the
- base offset.
- <p>The index is multiplied by the element size. This is optimized for some
- important element sizes:
- </p><pre> no code ;size = 1
- SHL AX,1 ;size = 2
- SHL AX,1 ;size = 4
- SHL AX,1
- SHL AX,1 ;size = 6
- MOV CX,AX
- SHL AX,1
- ADD AX,CX
- MOV CX,#size ;other element sizes
- MUL CX
- </pre>
- <p align="left"> The index is then stored in DI:
- </p><pre> XCHG DI,AX
- /pre>
- <p align="left"> or added to the existing index:
- </p><pre> ADD DI,AX
- </pre>
- <p><b>Record Indexing</b>
- </p><p>This is very simple: The offset of the record variable is added to the memory
- offset of the variable.
- </p><p><b>Pointer indexing</b>
-
- Pointers are loaded with LES DI,pointer_var.
- </p><p><b>Use of Addressing Modes</b>
- </p><p>The procedure <code>einstr</code> emits a command using the correct
- addressing mode. If necessary a segment prefix (CS: or ES:) is inserted.
- </p><ul>
- <li>not indexed, not on stack:
- <pre> MOV AX,var
- </pre></li>
- <li>indexed:
- <pre> MOV AX,[DI] ;no offset
- MOV AX,[DI]offs8 ;short offset (-128..127)
- MOV AX,[DI]offs16 ;long offset (0..65535)
- </pre></li>
- <li>stack, local variables:
- <pre> MOV AX,[BP]offs ;not indexed
- MOV AX,[BP+DI]offs;indexed
- </pre></li>
- <li>stack, callers local variables:
- <pre> MOV BX,[BP]-lev*2 ;read display pointer
- SS: ;indexed by BX - need prefix
- MOV AX,[BX]offs ;not indexed
- MOV AX,[BX+DI]offs;or indexed
- </pre></li></ul>
- <p><b>Calculate Pointer to Variable</b>
- </p><pre> indexing
- </pre>
- <p> The offset is read into DI:
- </p><pre> MOV DI,#offset ;not indexed
- ADD DI,#offset ;indexed: short or long offset
- LEA DI,[BP]offs ;stack: load effective address
- </pre>
- <p> The segment is handed over on the stack:
- </p><pre> PUSH CS/DS/ES/SS
- </pre>
- <p><b>Read Variable</b>
- </p><ul>
- <li>Scalar:
- <pre> MOV AX,var ;integer, not indexed, not on stack
- MOVB AL,var ;byte, not indexed, not on stack
- MOV AX,.. ;other integer (emitted by einstr)
- MOVB AL,.. ;other byte
- </pre>
- For byte variables XOR AH,AH is inserted - TURBO always uses integer variables
- internally.</li>
- <li>Pointer:
- <pre> LES AX,ptr_var ;AX = offset
- MOV DX,ES ;DX = segment
- </pre></li>
- <li>Real:
- <pre> set pointer to variable
- CALL xldreal
- </pre></li>
- <li>Set:
- <pre> set pointer to variable
- MOV CX,#set_crunch
- CALL xldset
- </pre></li>
- <li>String:
- <pre> set pointer to variable
- CALL strload
- </pre></li></ul>
- <p><b>Store Variable</b>
- </p><ul>
- <li>Scalar: If R+ is set, range checking is done:
- <pre> MOV CX,#lower_bound
- MOV DX,#upper_bound
- CALL xrngchk
- </pre>
- If the variable is neither indexed nor on the stack, there's a short form
- again:
- <pre> MOV var,AX ;integer
- MOVB var,AL ;byte
- </pre>
- Otherwise the correct MOV is emitted by <code>einstr</code>.</li>
- <li>Pointer:
- <pre> MOV dest,AX ;offset
- MOV dest+2,DX ;segment
- </pre></li>
- <li>Real:
- <pre> set pointer to variable
- CALL xstoreal
- </pre></li>
- <li>String:
- <pre> set pointer to variable
- MOV CL,#max_length
- CALL strstore
- </pre></li>
- <li>Set:
- <pre> set pointer to variable
- MOV CX,#set_crunch
- CALL setsto
- </pre></li></ul>
- </pre></td>
- </tr>
- <tr><td colspan="5" valign="baseline" bgcolor="#EEEEEE">
- <p><font size="4"><b>Symbol Table</b></font></p></td>
- </tr>
- <tr><td colspan="5" valign="baseline" bgcolor="#FFFFFF">
- <p>The symbol table manager has to insert and search symbols of any type. The
- difficulty is that definitions may be of any complexity and names may have any
- length. The structure implemented in TURBO closely represents the definition
- and reference sequence, making it easy to "navigate" through complex types.
- </p><p>Symbols are searched beginning with the most recent definition. For new
- definitions, the current block (limited by the "fence" set at the beginning of
- a procedure definition) and the keyword table are searched for duplicate
- definitions. Thus it is possible to override old definitions. At the end of a
- procedure local variables may be removed from the symbol table. This reduces
- memory usage and search time.
- </p><p><b>Symbol Table Entry Structure</b>
- </p><p>The symbol table "symtab" is stored in the stack segment (the actual stack
- doesn't need much space) and grows down. The entries have this basic structure:
- </p><pre> off :next entry
- --
- off-2:tag word = entry type
- off-4:name length
- off-5:name
- off-.:entry
- 0:offset to next entry
- </pre>
- The symbol table is always searched backwards, looking at the most recent
- entries first. A linear search is used. Sample symbol table entries can be
- found in the compiler tables (starting from 9277).
- <p><b>tag = 0100: Label</b>
- </p><pre> - 1:procedure nesting (to prevent jumps into or out of procedures)
- - 2:0=ok, FF=not yet defined
- - 4:offset
- </pre>
- <p><b>tag = 0200: Constant</b>
- </p><pre> - 1:type of constant
- - 3:constant - strings are stored backwards
- </pre>
- Structured constants are initialized variables stored in the code segment and
- are entered as such in the symbol table.
- <p><b>tag = 0300: Type</b>
- </p><pre> - 2:pointer to type definition
- </pre>
- <p><b>tag = 0400: Variable</b>
- </p><p>For subvariables of a record the low byte of the tag word is the number of the
- record definition this entry belongs to.
- </p><pre> - 2:pointer to type definition
- - 4:offset
- - 5:0=normal, FF=indirect (VAR)
- - 6:segment
- FF = DS
- FE = CS
- FD = ES (via pointer)
- .. = SS, the number corresponds to the procedure nesting level
- </pre>
- For ABSOLUTE variables of the form $B800:0 a pointer is stored in the code
- segment, the symbol table entry id CS indirect.
- <p><b>tag = 0500: Procedure</b>
- </p><p><b>tag = 0600: Function</b>
- </p><pre> - 2:function only - pointer to result type
- - 4:function only - offset on stack
- - 5:function only - 0 = normal, FF = indirect
- - 6:function only - segment: SS
- - 8:code offset of procedure / function
- - A:position in overlay file / of forward jump
- - C:0=ok, FF=forward definition
- - E:length of overlay procedure
- -10:number of parameter lists
- - 2:type pointer |may be repeated
- - 3:0=normal, FF=VAR parameter |
- - 4:number of parameters of this type |
- - 5:names |
- </pre>
- The parameters are also listed as local variables.
- <p><b>Structure of Subentries</b>
- </p><p>The normal entries aren't sufficient for the description of complex types.
- Unnamed subentries are used for this. For complex definitions this gives a
- tree structure. ARRAY[1..15] OF INTEGER is an array having the subrange 1..15
- as index type and INTEGER as element type.
- </p><p><b>tag = 0000, 0800: Subentries</b>
- </p><pre> - 2:component size
- - 4:lower bound / pointer to type definition
- - 6:upper bound / pointer to index type definition
- - 7:flag / record number
- - 8:component type
- 1 = array
- 2 = record
- 3 = set
- 4 = pointer
- 5 = typed file (FILE OF)
- 6 = text file (TEXT)
- 7 = untyped file (FILE)
- 8 = string
- 9 = real
- A = integer
- B = boolean
- C = char
- . = enumeration types (numbered)
- </pre>
- The compiler often uses this register assignment:
- <pre> CL = component type
- CH = where's the result ?
- 0 = constant
- 1 = variable
- 2 = in AX / on stack
- 3 = flags set (jz/jnz)
- 4 = comparison, use branch opcode brnchop
- </pre>
- The use of the subentry fields depends on the component type:
- <p>Array
- </p><pre> - 2:component size
- - 4:type pointer
- - 6:pointer to index type
- - 8:01
- </pre>
- Record
- <pre> - 2:component size
- - 7:record number
- - 8:02
- </pre>
- Set
- <pre> - 2:component size
- - 4:pointer to index type
- - 8:03
- </pre>
- Pointer
- <pre> - 2:component size = 4
- - 6:pointer to type / type name
- - 7:0=ok, FF=not yet resolved
- - 8:04
- </pre>
- Pointers can be forward defined. In this case the type name is stored as an
- invisible entry. The type pointer is then inserted later.
- <p>String
- </p><pre> - 2:length + 1
- - 8:08
- </pre>
- Text file with nonstandard buffer size
- <pre> - 2:component size
- - 8:06
- </pre>
- Enumeration type, subrange
- <pre> - 2:size: 1 or 2 bytes
- - 4:lower bound
- - 6:upper bound
- - 8:number of enumeration type
- </pre>
- The elements of an enumeration type are stored as constants.
- <p><b>Symbol Table Search</b>
- </p><p>TURBO uses a linear search to find symbols in the symbol table. This can be
- slow. A worst case program can get compilation speed down to 0.022 lines
- per second...
- </p><p>There is a better way: Hashing. <i>This is used in later versions of the
- compiler.</i>
- </p></td>
- </tr>
- <tr><td colspan="5" valign="baseline" bgcolor="#EEEEEE">
- <p><font size="4"><b>Error Handler</b></font></p></td></tr>
- <tr><td colspan="5" valign="top" align="left" bgcolor="#FFFFFF">
- <p>Many compilers try to continue compilation after an error has been found. The
- difficulty about this is that this shouldn't trigger an avalanche of
- meaningless error messages.
- </p><p>TURBO always stops if an error is found. This can also be seen as an
- advantage: The programmer is forced to solve problems one at a time.
- </p></td>
- </tr>
- <tr><td colspan="5" valign="baseline" bgcolor="#EEEEEE">
- <p><font size="4"><b>Run-time Library</b></font></p></td>
- </tr>
- <tr><td colspan="5" valign="baseline" bgcolor="#FFFFFF">
- <p>In the run-time library all standard procedures and functions needed for the
- execution of programs are stored. TURBO does this in a rather wasteful (but
- simple) way: it always inserts all procedures. <i>Later versions of the
- compiler only include the procedures that are actually used.</i>
- </p><p><b>Memory Map</b>
- </p><p>The segments are allocated as follows:
- </p><pre> --
- stack (SS) (grows down)
- --
- free
- --
- heap (grows up)
- --
- variables (DS)
- --
- code (CS)
- --
- </pre>
- <p><b>Heap</b>
- </p><p>Heap storage is allocated in blocks having a size that is a multiple of 8
- bytes. The free blocks are kept track of with this structure:
- </p><pre> +0: pointer to next free block -> linked list
- +4: length of free block
- +8: free block
- </pre>
- Hpstrt points to the first free block. The last block in the list - the free
- space between stack and heap - is marked by a 0. If there isn't enough space
- between heap and stack an error is reported.
- <p><b>Floating Point Arithmetics</b>
- </p><p>Floating point numbers are divided into two parts: The exponent gives the
- order of magnitude, the mantissa gives the accuracy needed.
- </p><pre> number = mantissa * 2 ^ exponent
- </pre>
- The mantissa is a binary fraction with an accuracy of 40 bits. The mantissa
- always represents a number between 0.5 and 1, i.e. it is normalized. This
- means the most significant bit is always 1. Here the sign is stored. The
- examples use decimal numbers for simplicity.
- <p>A floating point addition works as follows:
- </p><pre> 0.95 E+00
- + 0.60 E-01
- 0.95 E+00 The number with the smaller exponent is
- + 0.06 E+00 adjusted (de-normalized) to match the other.
- = 1.01 E+00 The numbers can now be added as usual.
- The result is too big, it must be normalized
- = 0.10 E+01 and rounded up.
- </pre>
- Please note that floating point additions and subtractions can cause large
- round off errors, if the exponents differ too much.
- <p>It is often claimed that BCD arithmetics cause less errors. This is not
- correct - they are not as visible. Some calculators actually do cosmetic
- rounding, if the result is near to an integer number. The best way to avoid
- round off errors is to calculate money amounts in cents and not in dollars.
- BCD multiplications and divisions are slow. However, BCD does have one
- advantage: Conversions to and from ASCII are much faster. As business
- applications usually consist mainly of additions and subtractions, BCD can
- actually be faster.
- </p><p>Floating point multiplication
- </p><pre> 0.13 E+00 The exponents are added (division: subtracted)
- * 0.49 E+03 and the mantissas are multiplied.
- 0.13 E+00
- * 0.49 E+00
- * E+03
- = 0.0637 The additional digits are cut off and the
- * E+03 result is normalized and rounded up.
- = 0.64 E+02
- </pre>
- Square roots are evaluated using Newton's approximation. This is a good
- solution, but not the best: The 8087 does square roots faster than divisions !
- Transcendental functions are evaluated using the standard polynomials found in
- math books. I have seen better algorithms to do this. Don't expect much speed
- and precision from TURBO transcendentals.</td>
- </tr>
- <tr><td colspan="5" valign="baseline" bgcolor="#DDDDDD">
- <p><font size="5"><b>Appendix</b></font></p></td></tr>
- <tr><td colspan="5" valign="top" align="left" bgcolor="#EEEEEE">
- <p><font size="4"><b>Bugs</b></font></p></td></tr>
- <tr><td colspan="5" valign="top" align="left" bgcolor="#FFFFFF">
- <p>Thanks to the relative simplicity of the algorithms used TURBO Pascal is
- almost bug-free. Well, almost.
- </p><p><b>Set as Procedure Parameter</b>
- </p><p>If the last element is in the range 248..255 and the first above 7, this
- doesn't work. Redefine the set or pass it as a VAR parameter.
- </p><p><b>SizeOf</b>
- </p><p>Sometimes a redundant load is emitted.
- </p><p><b>UpCase</b>
- </p><p>The argument type is not checked. Try UpCase(15).
- </p><p><b>WHILE</b>
- </p><p>DO can be omitted.</p></td>
- </tr>
- <tr><td colspan="5" valign="top" align="left" bgcolor="#EEEEEE">
- <p><font size="4"><b>Compiler Speed</b></font></p></td>
- </tr>
- <tr><td colspan="5" valign="top" align="left" bgcolor="#FFFFFF">
- <p>TURBO may be faster than most other compilers, but there's still a wide margin
- for improvements:
- </p><ul>
- <li>symbol table: use hashing</li>
- <li>include files: use larger buffer</li>
- <li>don't copy source lines into another buffer</li>
- </ul>
- The editor could be much faster. The screen is re-displayed in an "intelligent"
- way (using DelLine and InsLine). On a terminal this may be faster - with
- memory mapped video this is a nuisance. Search and replace is slow.</td>
- </tr>
- <tr><td colspan="5" valign="top" align="left" bgcolor="#EEEEEE">
- <p><font size="4"><b>Write Faster Programs Using TURBO Pascal</b></font></p></td>
- </tr>
- <tr><td colspan="5" valign="top" align="left" bgcolor="#FFFFFF">
- <p>Avoid the standard string functions.
- </p><ul>
- <li>Use ord(st[0]) instead of length(st)</li>
- <li>Use move instead of string assignments:
- <pre> move(st1,st2,max_len+1);
- </pre></li></ul>
- <p>Write real constants with decimal point (10.0 instead of 10). This eliminates
- conversions.
- </p><p>GOTO statement considered harmful. So what... If a GOTO is the best way to
- express a program structure - use it ! TURBO GOTO's are still better than
- BASIC GOTO's: names can be used instead of numbers.
- </p><p>I/O can be improved if larger buffers are used (use multiples of 512 bytes
- for
- best results). Use BlockRead and BlockWrite. If large blocks are read or
- written the MS-DOS and TURBO overhead doesn't matter as much as for small
- blocks.</p></td>
- </tr>
- </tbody></table>
- </td></tr>
- <tr><td colspan="5" valign="middle" height="25" align="center" bgcolor="#CCCCCC">
- <font size="3" face="TrebuchetMS, Arial, sans-serif" color="#CC0000">
- © 2002-2010 PC Engines GmbH. All rights reserved.</font>
- </td></tr></tbody></table>
- </font>
- </body></html>
|