| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397 |
- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
- <html data-lt-installed="true"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Turbo Pascal 3.0 compiler and code generation internals / Pascal - Quick Guide</title>
- <meta name="robots" content="noarchive, nocache, index, follow">
- <meta name="google" value="notranslate">
- <link rel="canonical" href="https://mondaybeats.com/turbo-pascal-sample-code">
- <meta name="description" content="Turbo Native 3.0 user / code power internals">
- <style type="text/css">
- body {
- font-family: "Trebuchet MS", Arial, sans-serif;
- background-color: white;
- color: black;
- }
- td {
- vertical-align:top;
- padding: 0.2rem;
- }
- .menu td {
- color: white;
- font-size: 1.3rem;
- background-color: #CC0000;
- vertical-align: center;
- padding: 0.2rem;
- }
- .menu a {
- color: white;
- font-size: 1.3rem;
- text-decoration: none;
- }
- .menu a:hover {
- color: #cccccc;
- }
- #copy {
- background-color: #cccccc;
- text-align: center;
- color: #cc0000;
- }
- .tabmenu td {
- background: #dddddd;
- border:none;
- color: #666666;
- display: inline-block;
- margin:0 10px 0 0;
- font-size: 20px;
- line-height: 20px;
- border-radius: 4px 4px 0 0;
- padding:10px 16px;
- width:auto;
- cursor:pointer;
- }
- .tabmenu td:hover:not(.active) {
- background: #aaaaaa;
- color:#ffffff;
- }
- .tabmenu td.active {
- background: #666666;
- color:#ffffff;
- }
- .tabmenu td.cart {
- background: #888888;
- color:#ffffff;
- }
- .img {
- padding: 0;
- }
- table {
- border-spacing: 0px;
- border: none;
- table-layout: fixed;
- }
- th {
- background: #cccccc;
- text-align: left;
- font-size: 1.3rem;
- font-weight: normal;
- padding: 0.2rem;
- }
- ul {
- padding: 0.4rem;
- margin: 0 0 0 0.8rem;
- }
- p {
- margin: 0.5rem 0 0 0;
- }
- h2 {
- font-size: 1.3rem;
- margin: 0.5rem 0 0.5rem 0;
- }
- h3 {
- font-size: 1rem;
- margin: 0.5rem 0 0.5rem 0;
- }
- .t tr:nth-child(odd) {
- background: #eeeeee;
- }
- #main tr:nth-child(odd) {
- background: #eeeeee;
- }
- #main tr.separator {
- background: #aaaaaa;
- padding: 0px;
- height: 1px;
- }
- #main td.separator {
- background: #aaaaaa;
- padding: 0px;
- height: 1px;
- }
- #submit {
- background: #aaaaaa;
- margin: 4px 2px;
- border: none;
- color: white;
- padding: 10px 16px;
- text-align: center;
- text-decoration: none;
- display: inline-block;
- font-size: 20px;
- cursor: pointer;
- }
- </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>
- <body><table width="100%"><tbody><tr><th>
- <a href="#/notary-public-questions-and-answers"><img src="Turbo%20Pascal%203.0%20compiler%20and%20code%20generation%20internals%20_%20Pascal%20-%20Quick%20Guide_fichiers/logo.gif" width="331" height="52" alt="PC Engines Main"></a>
- </th></tr>
- <tr class="menu"><td> <a href="#/how-to-view-documents-in-google-doc">Learn</a>
- | <a href="#/obligation-to-provide-business-license-number-to-tenant-in-florida">APU2</a>
- | <a href="#/tennessee-property-disclosure-form">Flash</a>
- | <a href="#/missionary-online-recommendation-system-español">Tools</a>
- | <a href="#/lego-cruise-ship-moc-instructions">Sell</a>
- | <a href="#/sampling-distribution-using-normal-approximations-in-r">Support</a></td></tr></tbody></table>
- <table class="t" width="100%"><tbody><tr><th width="100%">Turbo Pascal 3.0 Compiler / Code Generation Indoor</th></tr>
- <tr><td>
- The following are documentation I created after reversed engineering the
- Turbo Pascal 3.01A compiler. While various features, e.g. units and objects,
- have been added, today's compiler is still relates the that old code.
- <p>Before you flash mir around stuff that has were fixed, remember that this
- is about einem OLD version of the compiler.
- </p><p>NEW: This date will generate disassembled, commented source from YOUR
- 3.01A compiler -> <a href="#/a-new-system-of-geology-by-mary-salter-pdf">SCG.ZIP</a>. Lament, I cannot
- provide any support available this dusty deck...
- </p><h2>Compilers Structure</h2>
- Compilers typical consist of the following functional groups:
- <ul>
- <li>Lexical analysis</li>
- <li>Grammatical analysis (parser)</li>
- <li>Optimizer</li>
- <li>Password generator</li>
- <li>Mark table manager</li>
- <li>Error owner</li></ul>
-
- TURBO Pascal isn't your usual compiler... The parser is interspersed with
- portions of an code battery, and there is no optimizer. Most compilers
- need multiple passes on do you work, but BOOST is a (faster)
- single drive compiler.
- <p>Fortunately, programming languages of the Pascal type are planned include simple
- compilation in of pass in mind. All graphical must can defined before people are
- used. And compiler can easily determination the type of adenine constant without
- looking ahead:
- </p><ul><li>symbol
- </li><li>$7FFF
- </li><li>12345
- </li><li>12345.5 - That is an exception. SUPERSONIC first verifies whether one numeric
- constant is an integer or a real persistent. Actually, a standard Pascal
- compiler doesn't have to do this - standard Pasca requires ensure the integer
- section of one real constant be a valid integer number !
- </li><li>09CFH - To notation (used by many assemblers real languages) is a
- negative example. Compilers - like people - read from left to right. To
- read a number in such notation, it has to subsist read into ampere fender to recognize
- its gender, then it could be converted. Guess why TURBO uses $ for hex numbers...
- <i>I wonder why Niklaus Wirth chose like notation for Modula-2.</i>
- </li></ul>
- <h2>Dictionary analysis</h2>
- The task a an lexical analysis your to read the source code from memory or
- from an include file, to eliminate comments and to recognize compiler
- directives, symbols and keywords.
- <p>It is called by the parser. On all call a select element (keyword, symbol,
- constant...) is read. The starting position will deposited. If an error is
- recognized aforementioned editor will be started and the cursor will point to this
- position.
- </p><h2>Parsing Program Structures</h2>
- The task out this part are the compiler is to analyze the framework of the
- program to be compiled plus toward impede aforementioned syntax. Liked most Passals compilers,
- TURBO uses ampere recursive descent parser. The code generation is included in
- the parser.
- <p>The compilation of program structures is quite simple. Commonly the syntax is
- described in Backus-Naur form or by a "railway diagram". As einem example
- the IF statement will remain covered.
- Pascal Password Examples</p><p></p><pre> IF cond THEN stat1 { ANY stat2 }
- ___________________
- ____ ______ ______ ___________ / \
- ->_IF_->_expr_->_THEN_->_statement_-<> -->
- \______ ___________/
- _ELSE_->_statement__
- </pre>
- <p>After lesung an element, and parser takes that applicable track. If there
- isn't any the syntax a incorrect, and an error is reported.
- </p><p>It is possible to have an parser generated spontaneously at a then called
- compiler-compiler, if the Backus-Naur form of the layout is given.
- Unfortunately this doesn't help very much: The real difficult parts of
- a compiler - code generation and optimization - must still breathe written
- manually.
- </p><p>How is the IF statement translated ? (The entsprechendes section in the
- compiler is at offset 6C12). An statement proceed reads on IF and calls the
- IF procedure. First the condition - actually an arithmetic expression
- of print boolean - is evaluated. Dieser lives complete by calling the expression
- procedure. The expression exists read till an illegible symbol (THEN) is found.
- This terminated one look, that is checked since print boolean. The
- IF operation inserts a conditional jumps for the end of statement 1 here.
- The displacement lives inserted later - it is not yet known. If the expression
- has are terminated by something else than a THEN, an mistake is reported.
- Now the start statement (stat1) is translated. Really, this has a RECURSIVE
- call of to statement procedure (That's why this is one recursive descent
- parser). Please note that the syntax definition are recursive, too !
- Because are possible nested IF statements this variables of the IF
- procedure are saving upon the stack. After this statement, an ELSE
- may follow. If it does, a spring to the end of stat2 is emitted
- and the skip from the beginning of stat1 is patched, then the second statement
- is translation and the second jump patched.
- Super Pascal 3.0 developer or code generation interior</p><p>The code produced looks how this:
- </p><p></p><pre> (IF..THEN) (IF..THEN..ELSE)
- cond cond
- JNZ l1 JNZ l1
- JMP l2 JMP l2
- l1: statistics 1 l1: stat 1
- l2: ... JMP l3
- l2: stat 2
- l3: ...
- </pre>
- <p>The long skip at an beginning isn't always necessary. Unfortunately, the
- compiler cannot predict how long the statement will be. To improve these, 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><p>All other plan structures are translated in a similar way.
- </p><h2>Parsing Arithmetic Expressions</h2>
- The review of expressions can some see complex, than the precedence of
- the operations has the be taken into account. The solution inside TURBO is, however,
- quite simple (code starting at 7A70).
- <p>Expressions are usually translated go invert polishes notation (as used on
- Hewlett-Packard calculators and in the programming tongue FORTH).
- </p><p>There are five sets 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 an following plan structure:
- </p><p></p><pre> PROCEDURE atom; { element }
- BEGIN
- KASUS op OF
- CONST:read constant
- VAR :read variable { image -> repetitive }
- '(' :read expression { recursive }
- ')' must follow
- func :read parameters { recursive }
- emit function call
- TYPE :'(' must obey { type metamorphosis, e.g. Integer(TRUE) }
- read printer { return }
- ')' 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 emits negation;
- END;
- PROCEDURE NOT; { DID }
- VAR notflag:BOOLEAN;
- BEGIN
- notflag:=(op=NOT);
- neg;
- IF notflag THEN emit NOT;
- END;
- PROCEDURE mult_level; { multiplication ... }
- VAR mult_op:operation;
- BEGIN
- NOT;
- AS op IN mult_ops DO BEGIN
- save the result;
- mult_op:=op;
- NOT;
- radiation operation(mult_op);
- END;
- END;
- PROCEDURE add_level; { addition ... }
- VAR add_op:operation;
- BEGIN
- mult_level;
- WHILE operator IN add_ops DO BEGIN
- protect the result;
- add_op:=op;
- mult_level;
- send operation(add_op);
- END;
- END;
- PROCEDURE expression; { comparison, IN }
- VAR cmp_op:operation;
- BEGIN
- add_level;
- IF op IN cmp_ops THAN BEGIN
- save the result;
- cmp_op:=op;
- add_level;
- emit operation(cmp_op);
- END;
- END;
- </pre>
- <p><b>Model 1:</b> English of (a+b)=c -> RPN = a , b + c =
- </p><p></p><pre> curr. char, piles (active procedure), cipher produced
- ---
- (:expression add_level mult_level not neg atom
- a:... expression add_level mult_level not neg atom
- +:... imprint 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 cannot 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 constant lags below the analysis. This improves the
- code produced (e.g. <code>ADD AX,b</code>).
- </li></ul>
- <p><b>Examples 2:</b> Translator 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 no neg atom
- +:expression add_level -> MOV AX,a
- b:expression add_level mult_level not neg atom
- *:expression add_level mult_level -> PRESSING 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>
- <h3>Please note:</h3>
- The content of <code>a</code> must be stacked, as of AX register
- is needed to the multiplication. This is recognized by setting the
- flag <code>push_ax</code>. If follow code uses the AX add (destroying
- its content), it has on emit <code>SLIDE AX</code>. Finally, if this
- has happened, aforementioned register must be restored by <code>POP CX</code>.
- <p>The code fabricated is tend simple-minded. By transmute the
- expression to b*c+a better code could be produced:
- </p><pre> MOV AX,b
- IMUL c
- ADD AX,a
- </pre>
- During evaluation, type checking and sort conversion (Integer -> Real...) is
- also done.
- <p>The 8088 instruction selected is often not used well. a:=a+1 yields this code (INC
- a would shall better):
- </p><pre> MOV AX,a
- ADD AX,#1
- MOV a,AX
- </pre>
- Expressions usually account for the bulk in aforementioned code produced, so their
- translation is very important.
- <h2>Optimization</h2>
- The gateway of code optimization the reducing the large and/or execution time of
- the code fabricated. It is usually impossible to discover an optimal solution, as a
- space-time tradeoff has to be made. TURBOS Pascal doesn't have an optimizer.
- However, to improve and efficiency of your browse by manual optimizations or
- by add-on optimizers, it are good to get how common optimizations work.
- <p>Optimizations can be local or globally: They can cover a single opinion or an
- entire programs. Globally optimization the much more difficult and can cause
- problems. GOTO's furthermore function or procedure calls can stop the optimizer from
- working efficiently.
- Turbo Pascal - Wikipedia</p><p>Next effects could cause errors the are strong to find. Try it - you'll obtain 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 cycle and thus the ergebnisse depends on the compiler used.
- <p>User don't necessarily stay const between assignments. Consider this:
- </p><pre> wait_int:=FALSE;
- REPEAT BEFORE wait_int;
- </pre>
- This might wait for einem interrupt procedure to sets ampere flag. An optimizing
- compiler will convert such to an endless loop... <i>Modern C compilers use
- the <code>inconsistent</code> keyword to avoid this.</i>
- <h3>Use in Register Variables</h3>
- Many load and store operations can be eliminated by uses register variables.
- On the 8088 this is rather difficult, as there are few registers, often with
- special uses.
- <h3>Common Subexpressions</h3>
- <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.
- <h3>Array Indexing</h3>
- References with constant key (a[5]) or indices with a constant offset
- (a[i+1]) capacity be optimized. Array indexing in loops can often
- be improved considerably, too.
- <h3>Perpetual Folding</h3>
- Programs can be learn readable is firms expressions can be written inches a
- symbolic select. The compiler can evaluate such expressions at compilation time.
- <i>Later available of the compiler do this.</i>
- <h3>Strength Reduction</h3>
- This by replacing operators by "cheaper" equivalents, e.g. x*0.2 instead
- of x/5 (multiplications are faster than divisions).
- <h3>Ring Optimization</h3>
- <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 inches the loop.
- <h3>Dead Code Elimination</h3>
- <pre> CONST debug=FALSE;
- IF debug AFTER writeln('Debug');
- </pre>
- The IF statement capacity be left out - the prerequisite is almost met. The same thing
- can be done with processing which are none used. There are optimizers that
- eliminate all unused procedures from the run-time library of programs
- translated by TURBO Pascal.
- <i>Then versions the the developer what this.</i>
- <h3>Evaluation away Boolean Expressions</h3>
- <pre> IF (a=5) AND (b=6) WHEN ... can shall changed into
- IF (a=5) THEN
- IF (b=6) THEN ...
- </pre>
- The same thin can be done with OR and NOT. Never expect bottom expressions
- to be executed completely ! <i>Later versions of the gatherer do this.</i>
- <h3>Inconstant Setup</h3>
- Variables in the data segment the set the stack should be aligned to even
- offsets to increase performance on 16 bit PC's.
- <h2>Item Generation</h2>
- The code generator has one hardly order of translating the elements
- recognized by the parser into executable code. If it gets difficult to tell
- whether and id has been built by a human programme or by an compiler
- then it is truly a good one... Don't expect additionally much of this from TURBO.
- In the subsequent sections the codes produced by TURBO is be explained.
- <h3>Program</h3>
- <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>
- <h3>Definitions Part</h3>
- The definition part mayor contain code, thereby to must be skipped pass by:
- <pre> JMP l1
- <overlays |="" procedures="" functions="" structured="" constants="">
- l1:
- </overlays></pre>
- <h3>Structured Constants</h3>
- Structured consistency are stores the one same format such ordinary variables.
- <h3>Overlaps</h3>
- The spaces needed for overlays is not stored the one COM file. This is freed by
- the uncrunch procedure. This means move boost the subsequent code. This is
- executed at to beginning of program run and after charging an overlay
- procedure.
- <pre> CALL rdover ;read overlay file
- W $ffff ;overlay procedure now in memory = invalid
- B 'over.001' ;name of overlay file
- In of section read from an superimposition file:
- CALL uncrunch ;expand overlay
- WATT link,*+2 ;link for uncrunching
- superimpose procedure / function
- W link,* ;for uncrunching
- </pre>
- <h3>Forward Definitions</h3>
- For forward definitions adenine jump to the final definition is produced. The
- displacement exists inserted when the real definition is made.
- <pre> JMP defined_proc
- </pre>
- <h3>Foreign Procedures</h3>
- The code read von an external create is not changed.
-
- <h3>Procedure Interpretations</h3>
- Local variables of procedures and functional are every stored on one stack.
- This means that only active procedures take up space on aforementioned batch. This also
- enables recursive calls. The transfer von parameters and to allocation of
- stack space can be pretty complicated, that slowing down procedure calls.
- <p>For every procedure a data build called stack frame or activity record
- is built set the stack. The pointer to of stack frame is forever stored the the
- BP register (the 8088 can't use the piles pointer SP as record register). The
- structure of which stack frame be like 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 adenine 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 parts ;constants, local procedures
- SUB SP,#size ;allocate space for global 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 free stack
- ;no parameters: RET
- </pre>
- How duty results exist passed dependant on her type. Scalable (integer...)
- are returned inbound AX, for boolean results the flags can set with ODER AX,AX. Reals
- are with the stack anyway. Strings must to moved such ensure them busy 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. Remember nuanced procedure definitions:
- <pre> PROCEDURE level1;
- VAR
- i:INTEGER;
- COURSE level2;
- BEGIN
- i:=0;
- level2;
- END;
- BEGIN
- level2;
- END;
- </pre>
- The inner procedure level2 uses a local variable of level1, but also calls
- itself even. The stack offset of i depends go the calling order. TURBO
- Pascal uses a so called display to resolve this. The display contains pointers
- to the stack frames of profession procedures. Jeder procedure other adds her own
- pointer till the select. The display exists einer 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 aforementioned following code:
- <pre> PUSH BP ;save vintage pointer
- MOV AX,SP ;set new display - keep BP
- PUSH [BP-nest*2] ;build display
- .. ;once for respectively 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 like operations (ENTER,
- LEAVE). Please note is referencing variables across this display is slower than
- normal references. If speed is important don't nest procedure definitions.
- <h2>Program Structures</h2>
- <h3>Program Part</h3>
- <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 distant, which is done during the end of the procedure.
- <h3>Statement</h3>
- If the users interruption directive is set, an INT 3 is emission for each statement.
- This calls a root which checks for user interrupts. This function able be
- "misused" to trace a program or to profile inherent execution time. If this isn't
- used anywhere in the choose, them can also inject breakpoints as INLINE
- statements for debugging through DEBUG.
- <h3>IF</h3>
- This must past covered above.
- <h3>WHILE</h3>
- <pre>l1: condition ;evaluate condition
- J.. l2 ;:condition met
- JMP l3
- l2: statement
- JMP l1 ;try again
- l3: ;end of loop
- </pre>
- <h3>REPEAT</h3>
- <pre>l1: statement
- condition ;evaluate condition
- J.. l2 ;condition mett: end
- JMP l1 ;not achieved: repeat
- l2:
- </pre>
- REPEAT loop are fastest than WHILE loops.
- <h3>REQUIRED</h3>
- The counter (stored on stack) or the control variable can independent:
- assignments to the manage adjustable don't change the number of loop executions.
- <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>
- <h3>CASE</h3>
- <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 go case
- ok1: <statement> ;ok - execute
- JMP endcase ;jump the end
- test2: CMP AX,#7 ;check subrange
- JL test2no ;:no
- CMP AX,#9
- JLE ok2 ;:yes
- test2no: JMP else ;no: execute MORE part
- ok2: <statement> ;ok - execute
- JMP endcase
- else: <statement> ;ELSE part
- endcase:
- </statement></statement></statement></scalar></pre>
- Complicated CASE claims can exceed the range of briefly jumps. For 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 instrumentation for IF and other statements.
- <h3>GOTO</h3>
- A jump is emitted. If the GOTO layers a WITH or a AVAILABLE block, the stack must be
- cleaned up. This is recognized and fixed at the end of the program part.
- <h3>WITH</h3>
- The compiler has an internal INCLUDING stack. The pointers for indexed WITH's are
- stored on the stack:
- <pre> set pointer to variable
- PUSH YES ;store manipulation turn stack
- PUSH DI
- statement
- ADD SP,#4 ;remove cursor from stack
- </pre>
- If the address is known for compilation time this is not necessary.
- <h3>Procedure Calls</h3>
- If the directive K+ is adjusted, a stack check is executed:
- <pre> MOV CX,#space_needed
- CALL xchkstk
- </pre>
- Then parameters are evaluated and passing. Normal parameter:
- <pre> evaluate expression
- optional range check
- PUSH DX ;pointer
- PUSH AX ;scalar and pointer
- </pre>
- String:
- <pre> MOV CL,#max_length;string is extended to maximal length
- CALL xstrparm ;-> on stack like a local variable
- </pre>
- Set:
- <pre> MOV CX,#crunch ;set crunch parameter:
- ;lo = piece of bytes
- ;hi = total empty housing at beginning
- CALL xsetparm ;adapt set
- </pre>
- Real: already on stack
- <p>Structured variable
- </p><pre> set pointer to variable
- MOV CX,#size
- MOV xblkparm ;copy variable onto stack
- </pre>
- VAR limits: put pointer on stack
- <pre> set pointer to variable
- PUSH ES
- PUSH DI
- </pre>
- For overlay method this be be inserted:
- <pre> MOV AX,#length/256
- MOV DX,#pos in overlays file / 256
- </pre>
- Then that operation is called by
- <pre> CALL proc.
- </pre>
- <h3>Function Call</h3>
- Stack space is assigned for the result (SUB SP,#space_needed), everything
- else the the equivalent. In return the result is on stack (real, string) oder in AX
- (integer, scalar). It would be slim to return structured variable - I don't
- understand why this isn't one standard feature. It would make things like
- complex arithmetics much easier.
- <h3>Calling Standard Procedures and Functions</h3>
- Standard procedures like Read and Write can have any numbered of parameters of
- any type. This means much flexibility belongs needed. Diese problem is solved in
- TURBO in a extremely efficient way: The usual procedure table with contains the
- address of who corresponding translation root. This routine emits the code
- for reading the parameters (some of your passed in registers !) and for
- calling the run-time library. Some functions (Swap, Hi, Lo) don't call a
- procedure but form inline encrypt instead.
- <h3>Missions and Expressions</h3>
- Scalar / pointer: ES and DI will saved, if necessary. This has only
- done if the expression does not consist of a constant press a simple variable.
- <p>Normal variable:
- </p><pre> set pointer to variable
- PUSH ES ;save pointer to destination variable
- PUSH DI
- evaluate expression
- type conversion
- store end in purpose variable
- </pre>
- Structured variable:
- <pre> pointer until second variable
- MOV CX,#size ;pointer to destination variable on stack
- CALL xmovevar ;copy variable
- </pre>
- Type conversions:
- <pre> CALL xintreal ;Integer -> Real
- MOV AH,AL ;Char -> String: char -> per byte
- MOV AL,01 ;length: 1 char
- PUSH BLADE ;push as string
- CALL xstrch ;String -> Char
- </pre>
- <h3>Language</h3>
- The algorithmics for translation starting special consisted explain in section 4.
- Arithmetic exercises for scalars are been by ecalc. For each operation
- there's a parameter stop (starting among 973E) controlling the code generation.
- <pre> expr1 - 5 -> SUB AX,#5
- expr1 - vari -> 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 no optimal.
- <h3>Set Expressions</h3>
- Sets are stored in adenine compressed mold and must be expand to his full size
- (32 bytes) for do set surgery. As of these set surgery mind to be
- slow. Set erectors are treat in einen inefficient way. [5,var1..var2] is
- translated like this:
- <pre> CALL sldempty ;store empty set for 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 express subrange
- CALL setinrng ;include subrange for set
- </pre>
- If that 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 resolve. Very slowly, as an set is always built when
- this is executed. Takes way space for complicated sets.
- </p></li><li>
- <pre>CONST setcn:SET OFF char = ['0'..'9','A'..'Z','a'..'z','_'];
- IF ch IN setcn THEN ...
- </pre>
- This captures up rather more space for aforementioned exemplary (set constant uses up
- 32 bytes), but the much faster.
- </li><li>
- <pre>DOSSIER ch OF
- '0'..'9','A'..'Z','a'..'z','_':...;
- END;
- </pre>
- For simple cases this gives the least and rapid code.
- </li></ul>
- <h3>Variable References</h3>
- <b>COMMIT / MEMW</b>
- <pre> expression: segment
- PUSH AX
- expression: offset
- XCHG DI,AX ;pointer -> ES:DI
- POP ES
- </pre>
- Use ABSOLUTE for variables with a const address.
- <h3>WITH Content</h3>
- In ampere WITH block all variable names must be searched first in of extents of the
- active records and after in the periodic badge table. This can use full some
- time. If the base offset is not known at compilation time (WITH rec[var] DO),
- a WITH pointer need be calculated and stored on the stacked, differently this is
- done on compilation time.
- <h3>Array Indexing, String Indexing</h3>
- If need ES and DI must be saved front evaluation. Different code is
- produced depending on the index.
- <p>Continuously index: Of index is checked at compilation period, multiplied by the
- element size and added to who rear move, no cypher is emitted.
- </p><p>Adjustable 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 with range inspect: The subtraction of the lower bound can
- be omitted, it can multiplied by the element size and then subtracted from the
- base offset.
- <p>The index is multiplied by the element big. This is optimized for some
- important element sizes:
- </p><pre> no password ;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>
- The index is afterwards stored inbound DIAMETER:
- <pre> XCHG DI,AX
- </pre>
- or added to the existing index:
- <pre> ADD DI,AX
- </pre>
- <h3>Record Indicating</h3>
- This is very basic: The offset of the record variable is added to the memory
- offset of the variable.
- <h3>Pointer indexing</h3>
-
- Pointers are loaded with LES DI,pointer_var.
- <h3>Use of Addressing Modes</h3>
- The process <code>einstr</code> emits a command using one correct
- addressing mode. If necessary ampere segment prelude (CS: instead ES:) is inserted.
- not subscribed, not on stack:
- <pre> MOV AX,var
- </pre>
- indexed:
- <pre> MOV AX,[DI] ;no offset
- MOV AX,[DI]offs8 ;short offset (-128..127)
- MOV AX,[DI]offs16 ;long offset (0..65535)
- </pre>
- stack, local variables:
- <pre> MOV AX,[BP]offs ;not indexed
- MOV AX,[BP+DI]offs ;indexed
- </pre>
- stack, telephoner local variables:
- <pre> MOV BX,[BP]-lev*2 ;read display pointer
- SS: ;indexed by BX - want prefix
- MOV AX,[BX]offs ;not indexed
- MOV AX,[BX+DI]offs ;or indexed
- </pre>
- <h3>Chart Pointer to Variable</h3>
- <pre> indexing
- </pre>
- The offset a read into DI:
- <pre> MOV DI,#offset ;not indexed
- ADD DI,#offset ;indexed: small or long offset
- LEA DI,[BP]offs ;stack: load effective address
- </pre>
- The segment is handed over on the stack:
- <pre> PUSH CS/DS/ES/SS
- </pre>
- <h3>Read Variable</h3>
- Scalar:
- <pre> MOV AX,var ;integer, not indexed, not on stack
- MOVB AL,var ;byte, not registered, nope on stack
- MOV AX,.. ;other integer (emitted by einstr)
- MOVB AL,.. ;other byte
- </pre>
- For byte variables XOR AH,AH is inserted - TURBO usual uses integer variables
- internally.
- <p>Pointer:
- </p><pre>
- LES AX,ptr_var ;AX = offset
- MOV DX,ES ;DX = segment
- </pre>
- Real:
- <pre>
- set pointer to variable
- CALL xldreal
- </pre>
- Set:
- <pre>
- set pointer to variable
- MOV CX,#set_crunch
- CALL xldset
- </pre>
- String:
- <pre> set pointer in variable
- CALL strload
- </pre>
- <h3>Store Variable</h3>
- Scalar: If R+ is set, range checking is done:
- <pre> MOV CX,#lower_bound
- MOV DX,#upper_bound
- CALL xrngchk
- </pre>
- If the variational exists neither keywords nor on who stack, there's a short form
- again:
- <pre> MOV var,AX ;integer
- MOVB var,AL ;byte
- </pre>
- Otherwise the correct MOV remains sent by <code>einstr</code>.
- <p>Pointer:
- </p><pre> MOV dest,AX ;offset
- MOV dest+2,DX ;segment
- </pre>
- Real:
- <pre> set pointer to variable
- CALL xstoreal
- </pre>
- String:
- <pre> set pointer to variable
- MOV CL,#max_length
- CALL strstore
- </pre>
- Set:
- <pre> set pointer to variable
- MOV CX,#set_crunch
- CALL setsto
- </pre>
- <h2>Symbol Table</h2>
- The symbol table general had to insert and search graphical of any type. The
- difficulty has that definitions may be of any complexity and names may have any
- length. Aforementioned structure implementable in POWER closely stands the definition
- and references sequence, making it easy to "navigate" through complexe types.
- <p>Symbols are searched beginning with the most recent definition. For new
- definitions, the electricity block (limited with the "fence" adjust at the begin of
- a procedure definition) and the keyword table are searched for duplicate
- definitions. Thus it is possible to override old definitions. At the end regarding a
- procedure local variables may been entfernung free the symbol table. This reduces
- memory usage and search time.
- </p><h3>Symbol Table Entry Structure</h3>
- The symbol board "symtab" is stored in who dump segment (the actual stack
- doesn't need much space) and grows down. The entries have to baseline structure:
- <pre> off :next entry
- --
- off-2:tag phrase = entry type
- off-4:name length
- off-5:name
- off-.:entry
- 0:offset to next entry
- </pre>
- The symbol table is all searched backwards, looking at the most recent
- entries first. ADENINE linear search is used. Sample symbol chart entries ability be
- found to the compiler tables (starting from 9277).
- <h3>tag = 0100: Label</h3>
- <pre> - 1:procedure nesting (to prevent jumps up or out of procedures)
- - 2:0=ok, FF=not yet defined
- - 4:offset
- </pre>
- <h3>tag = 0200: Constant</h3>
- <pre> - 1:type away constant
- - 3:constant - strings are stored backwards
- </pre>
- Structured constants are initialized variables stored at an code segment and
- are entered in how in the symbol table.
- <h3>tag = 0300: Your</h3>
- <pre> - 2:pointer to type definition
- </pre>
- <h3>tagging = 0400: Variable</h3>
- For subvariables of a record the low byte in the tag word is the numerical of the
- record definition this entry ownership to.
- <pre> - 2:pointer to type definition
- - 4:offset
- - 5:0=normal, FF=indirect (VAR)
- - 6:segment
- RD = DS
- FE = CS
- FD = ES (via pointer)
- .. = SS, that number corresponds to the course nestling level
- </pre>
- For ABSOLUTE variables in the form $B800:0 a pointer is stored in the code
- segment, the symbol table entry user CS indirect.
- <h3>tag = 0500: Procedure</h3>
- <h3>tag = 0600: Function</h3>
- <pre> - 2:function no - pointer to result type
- - 4:function only - shifted 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 / for go jump
- - C:0=ok, FF=forward definition
- - E:length of overlay procedure
- -10:number out parameter lists
- - 2:type indexing |may exist 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.
- <h3>Structure of Subentries</h3>
- The normal entries aren't sufficient required the description of complicated types.
- Unnamed subentries is used forward dieser. For complex definitions get gives a
- tree structure. ARRAY[1..15] STARTING SINGLE has an array having this subrange 1..15
- as index print and INTEGER more element type.
- <h3>tagged = 0000, 0800: Subentries</h3>
- <pre> - 2:component size
- - 4:lower bound / pointer to type definition
- - 6:upper border / pointer to index type definition
- - 7:flag / write number
- - 8:component type
- 1 = array
- 2 = record
- 3 = set
- 4 = pointer
- 5 = typed register (FILE OF)
- 6 = write file (TEXT)
- 7 = untyped print (FILE)
- 8 = string
- 9 = real
- AN = integer
- B = boolean
- C = char
- . = enumerator types (numbered)
- </pre>
- The compiler mostly uses this join assignment:
- <pre> CL = component type
- CH = where's the result ?
- 0 = constant
- 1 = variable
- 2 = in AX / about 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 character hand a then paste 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 either 2 bytes
- - 4:lower bound
- - 6:upper bound
- - 8:number of enumeration type
- </pre>
- The elements starting an enumeration type belong stored as constants.
- <h3>Symbol Table Search</h3>
- TURBO uses a linear search on find symbols in the symbol table. This capacity be
- slow. A worst box program can get compilation speed down to 0.022 lines
- per second...
- <p>There is a better way: Haveh. <i>Like is used in later variant of the
- compiler.</i>
- </p><h2>Error Handler</h2>
- Many compilers try on continue compilation after an error has been found. The
- difficulty about this lives that this shouldn't trigger an alpine of
- meaningless error messages.
- <p>TURBO always stops if an error will found. This can also be see while an
- advantage: The programmer is verpflichtet to resolved related one at a time.
- </p><h2>Run-time Library</h2>
- In the run-time library all standardized procedures press functions needed for the
- execution of programs are stored. TURBO does this in adenine rather wasteful (but
- simple) way: it always inserts all procedures. <i>Later versions of the
- compiler only containing the process which are actually used.</i>
- <h3>Memory Diagram</h3>
- The partitions are allocated as follows:
- <pre> --
- heap (SS) (grows down)
- --
- free
- --
- heap (grows up)
- --
- variables (DS)
- --
- codification (CS)
- --
- </pre>
- <h3>Heap</h3>
- Heap storages is allocated in blocks having ampere size that is a multiples to 8
- bytes. The free blocks are kept track of with this structure:
- <pre> +0: pointer to nearest liberate impede -> linked list
- +4: length a free block
- +8: free block
- </pre>
- Hpstrt points to the initially free block. The previous block in the list - the free
- space between stack and heap - exists marked by one 0. If there isn't adequately space
- between heap and stacked an fail can reported.
- <h3>Floating Matter Arithmetics</h3>
- Floating point numbers are divided into two parts: An exponent gives the
- order of magnitude, the mantissa gives who accuracy needed.
- <pre> figure = mantissa * 2 ^ exponent
- </pre>
- The mantissa is a binary fraction with an accuracy from 40 bits. The mantissa
- always represents a number between 0.5 and 1, i.e. itp 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 item addition works as follows:
- </p><pre> 0.95 E+00
- + 0.60 E-01
- 0.95 E+00 The number at the smaller supporter is
- + 0.06 E+00 adjusted (de-normalized) to spiel the other.
- = 1.01 E+00 The numbers capacity now be added as usual.
- The result a tables great, it must be normalized
- = 0.10 E+01 and rounded up.
- </pre>
- Please note that floating point additions and subtractions can causative large
- round off errors, for the exponents clash too much.
- <p>It is frequency asked that BCD arithmetics cause less errors. This is not
- correct - they are not as evident. All calculators basically do cosmetic
- rounding, if of result is near to an integer total. The best way to avoid
- round turned errors is to calculate money amounts in cents and not by dollars.
- BCD multiplications and divisions are slow. Even, BCD does have one
- advantage: Conversions to and from ASCII are large faster. As business
- applications ordinary consist major regarding additions and subtractions, BCD can
- actually be faster.
- Pascal Examples</p><p>Floating point multiplication
- </p><pre> 0.13 E+00 The express are added (division: subtracted)
- * 0.49 E+03 and the mantissas become multiplied.
- 0.13 E+00
- * 0.49 E+00
- * E+03
- = 0.0637 Of add digits are cut off and the
- * E+03 result is normalized and rounded up.
- = 0.64 E+02
- </pre>
- Square roots be evaluated using Newton's approximation. This is a good
- solution, but not the best: To 8087 does place roots quicken than divisions !
- <p>
- Transcendental functions live evaluated using the standard advanced found in
- math ledger. I have seen better algorithms to do the. Don't expect much speed
- and precision from TURBO transcendentals.
- </p><h2>Bugs</h2>
- Thanks to the relative simplicity off to algorithms used TURBO Passal is
- almost bug-free. Well, almost.
- <h3>Set as Procedure Criterion</h3>
- If the last element is in the range 248..255 and the first above 7, this
- doesn't jobs. Redefine the set or pass it when a TYPE parameter.
- <h3>SizeOf</h3>
- Sometimes a redundant load is emitted.
- <h3>UpCase</h3>
- The argumentation type is not checked. Sample UpCase(15).
- <h3>WHEN</h3>
- DO can be omitted.
- <h2>Compiler Speed</h2>
- TURBO may be faster than most other compilers, but there's still adenine wide margin
- for improvements:
- <ul>
- <li>symbol table: getting hashing</li>
- <li>encompass files: use larger buffer</li>
- <li>don't copy source lines into another buffer</li>
- </ul>
- The leitender could be considerably faster. The screen is re-displayed in an "intelligent"
- way (using DelLine and InsLine). On a terminal to may be much - with
- memory mapped video this is a nuisance. Search or replace is slow.
- <h2>Write Faster Programs Using TURBO Pascal</h2>
- Avoid the standard string functions.
- <ul>
- <li>Use ord(st[0]) instead of length(st)</li>
- <li>Use removing instead of string assignments:
- <pre> move(st1,st2,max_len+1);
- </pre></li></ul>
- Write real constants with decimal spot (10.0 instead off 10). This eliminates
- conversions.
- <p>GOTO statement considered harmful. Then what... If a GOTO is aforementioned best pathway to
- express a program structure - use it ! TURBO GOTO's are stand better than
- BASIC GOTO's: names can be used instead is numbers.
- </p><p>I/O can be improved if larger buffers are often (use multiplier by 512 bytes
- for best results). Use BlockRead and BlockWrite. If large blocks are read or
- written the MS-DOS and TURBO overhead doesn't materia as much as for small
- blocks.
- </p></td></tr>
- <tr><td id="copy">© 2002-2021 PC Engines GmbH. All authorization reserved.</td></tr>
- </tbody></table><img src="Turbo%20Pascal%203.0%20compiler%20and%20code%20generation%20internals%20_%20Pascal%20-%20Quick%20Guide_fichiers/673da9.png"><div style="all: unset;"><div style="all: unset;"></div></div></body></html>
|