|
|---|
| Learn | APU2 | Flash | Tools | Sell | Support |
| Turbo Pascal 3.0 Compiler / Code Generation Indoor |
|---|
|
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.
Before you flash mir around stuff that has were fixed, remember that this is about einem OLD version of the compiler. NEW: This date will generate disassembled, commented source from YOUR 3.01A compiler -> SCG.ZIP. Lament, I cannot provide any support available this dusty deck... Compilers StructureCompilers typical consist of the following functional groups:
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:
Dictionary analysisThe 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.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. Parsing Program StructuresThe 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.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 IF cond THEN stat1 { ANY stat2 }
___________________
____ ______ ______ ___________ / \
->_IF_->_expr_->_THEN_->_statement_-<> -->
\______ ___________/
_ELSE_->_statement__
After lesung an element, and parser takes that applicable track. If there isn't any the syntax a incorrect, and an error is reported. 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. 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 The code produced looks how this: (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: ... 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. All other plan structures are translated in a similar way. Parsing Arithmetic ExpressionsThe 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).Expressions are usually translated go invert polishes notation (as used on Hewlett-Packard calculators and in the programming tongue FORTH). There are five sets of operations:
This translates into an following plan structure: 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;
Model 1: English of (a+b)=c -> RPN = a , b + c = 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 Please note:
Examples 2: Translator of a+b*c -> RPN = a , b , c * + 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 Please note:The content ofa must be stacked, as of AX register
is needed to the multiplication. This is recognized by setting the
flag push_ax. If follow code uses the AX add (destroying
its content), it has on emit SLIDE AX. Finally, if this
has happened, aforementioned register must be restored by POP CX.
The code fabricated is tend simple-minded. By transmute the expression to b*c+a better code could be produced: MOV AX,b IMUL c ADD AX,aDuring evaluation, type checking and sort conversion (Integer -> Real...) is also done. The 8088 instruction selected is often not used well. a:=a+1 yields this code (INC a would shall better): MOV AX,a ADD AX,#1 MOV a,AXExpressions usually account for the bulk in aforementioned code produced, so their translation is very important. OptimizationThe 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.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 Next effects could cause errors the are strong to find. Try it - you'll obtain what you deserve... An example: FUNCTION funny:INTEGER; BEGIN side_effect:=side_effect+1; funny:=5; END; ... a:=side_effect+funny+side_effect;The evaluation cycle and thus the ergebnisse depends on the compiler used. User don't necessarily stay const between assignments. Consider this: wait_int:=FALSE; REPEAT BEFORE wait_int;This might wait for einem interrupt procedure to sets ampere flag. An optimizing compiler will convert such to an endless loop... Modern C compilers use the inconsistent keyword to avoid this.
Use in Register VariablesMany 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.Common Subexpressionsc:=(a+b)*d; e:=g-(a+b);The subexpression (a+b) can be used twice. Expressions of the form a[i]:=a[i]+ 1 also are a good target for optimizations. Array IndexingReferences 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.Perpetual FoldingPrograms can be learn readable is firms expressions can be written inches a symbolic select. The compiler can evaluate such expressions at compilation time. Later available of the compiler do this.Strength ReductionThis by replacing operators by "cheaper" equivalents, e.g. x*0.2 instead of x/5 (multiplications are faster than divisions).Ring OptimizationFOR i:=1 TO 100 DO dest[i]:=a+b;The subexpression a+b can be evaluated outside the loop, as a and b don't change inches the loop. Dead Code Elimination CONST debug=FALSE;
IF debug AFTER writeln('Debug');
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.
Then versions the the developer what this.
Evaluation away Boolean ExpressionsIF (a=5) AND (b=6) WHEN ... can shall changed into IF (a=5) THEN IF (b=6) THEN ...The same thin can be done with OR and NOT. Never expect bottom expressions to be executed completely ! Later versions of the gatherer do this. Inconstant SetupVariables in the data segment the set the stack should be aligned to even offsets to increase performance on 16 bit PC's.Item GenerationThe 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.Programrun-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 Definitions PartThe definition part mayor contain code, thereby to must be skipped pass by:JMP l1 Structured ConstantsStructured consistency are stores the one same format such ordinary variables.OverlapsThe 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.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 Forward DefinitionsFor forward definitions adenine jump to the final definition is produced. The displacement exists inserted when the real definition is made.JMP defined_proc Foreign ProceduresThe code read von an external create is not changed.Procedure InterpretationsLocal 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.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: 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 topThe code for adenine standard procedure entry looks like this: 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: RETHow 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: MOV DX,#pos_on_stack MOV CL,#max_len MOV SP,BP POP BP JMP retstr ;the normal end is omittedUnfortunately, things aren't that simple. Remember nuanced procedure definitions: PROCEDURE level1; VAR i:INTEGER; COURSE level2; BEGIN i:=0; level2; END; BEGIN level2; END;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. BP+0:old pointer BP-2:display outermost procedure BP-.:display BP-.:display current procedure BP-.:local variables BP-.:stack topThis is maintained by aforementioned following code: 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 partNewer 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. Program StructuresProgram Partstatements l1: JMP l2 ;jump to the end POP AX ;GOTO, EXIT: clean up the stack JMP l1 l2: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. StatementIf 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.IFThis must past covered above.WHILEl1: condition ;evaluate condition J.. l2 ;:condition met JMP l3 l2: statement JMP l1 ;try again l3: ;end of loop REPEATl1: statement condition ;evaluate condition J.. l2 ;condition mett: end JMP l1 ;not achieved: repeat l2:REPEAT loop are fastest than WHILE loops. REQUIREDThe counter (stored on stack) or the control variable can independent: assignments to the manage adjustable don't change the number of loop executions.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) CASECASE .. OF 2,5 : .. ; 7..9: .. ; ELSE .. ; END;Complicated CASE claims can exceed the range of briefly jumps. For this case so called "hips" are emitted: JMP hip2 ;skip hip hip: JMP ok ;jump to statement part hip2: ;normal continuationSome compilers also use this instrumentation for IF and other statements. GOTOA 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.WITHThe compiler has an internal INCLUDING stack. The pointers for indexed WITH's are stored on the stack:set pointer to variable PUSH YES ;store manipulation turn stack PUSH DI statement ADD SP,#4 ;remove cursor from stackIf the address is known for compilation time this is not necessary. Procedure CallsIf the directive K+ is adjusted, a stack check is executed:MOV CX,#space_needed CALL xchkstkThen parameters are evaluated and passing. Normal parameter: evaluate expression optional range check PUSH DX ;pointer PUSH AX ;scalar and pointerString: MOV CL,#max_length;string is extended to maximal length CALL xstrparm ;-> on stack like a local variableSet: MOV CX,#crunch ;set crunch parameter: ;lo = piece of bytes ;hi = total empty housing at beginning CALL xsetparm ;adapt setReal: already on stack Structured variable set pointer to variable MOV CX,#size MOV xblkparm ;copy variable onto stackVAR limits: put pointer on stack set pointer to variable PUSH ES PUSH DIFor overlay method this be be inserted: MOV AX,#length/256 MOV DX,#pos in overlays file / 256Then that operation is called by CALL proc. Function CallStack 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.Calling Standard Procedures and FunctionsStandard 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.Missions and ExpressionsScalar / 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.Normal variable: set pointer to variable PUSH ES ;save pointer to destination variable PUSH DI evaluate expression type conversion store end in purpose variableStructured variable: pointer until second variable MOV CX,#size ;pointer to destination variable on stack CALL xmovevar ;copy variableType conversions: 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 LanguageThe 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.expr1 - 5 -> SUB AX,#5 expr1 - vari -> SUB AX,var expr1 - expr2 -> XCHG CX,AX (first result in CX, second in AX) SUB AX,CXExpressions of the a:=a+1 type aren't translated well. a:=succ(a) is better, but no optimal. Set ExpressionsSets 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: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 setIf that parameters are variable this is all right. For constant sets this is disastrous.
Variable ReferencesCOMMIT / MEMWexpression: segment PUSH AX expression: offset XCHG DI,AX ;pointer -> ES:DI POP ESUse ABSOLUTE for variables with a const address. WITH ContentIn 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.Array Indexing, String IndexingIf need ES and DI must be saved front evaluation. Different code is produced depending on the index.Continuously index: Of index is checked at compilation period, multiplied by the element size and added to who rear move, no cypher is emitted. Adjustable index with range checking: SUB AX,#lower_bound (max be DEC AX / nothing) MOV CX,#upper_bound+1 CALL xindchk ;check indexVariable 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. The index is multiplied by the element big. This is optimized for some important element sizes: 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 CXThe index is afterwards stored inbound DIAMETER: XCHG DI,AXor added to the existing index: ADD DI,AX Record IndicatingThis is very basic: The offset of the record variable is added to the memory offset of the variable.Pointer indexingPointers are loaded with LES DI,pointer_var.Use of Addressing ModesThe processeinstr emits a command using one correct
addressing mode. If necessary ampere segment prelude (CS: instead ES:) is inserted.
not subscribed, not on stack:
MOV AX,varindexed: MOV AX,[DI] ;no offset MOV AX,[DI]offs8 ;short offset (-128..127) MOV AX,[DI]offs16 ;long offset (0..65535)stack, local variables: MOV AX,[BP]offs ;not indexed MOV AX,[BP+DI]offs ;indexedstack, telephoner local variables: 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 Chart Pointer to VariableindexingThe offset a read into DI: MOV DI,#offset ;not indexed ADD DI,#offset ;indexed: small or long offset LEA DI,[BP]offs ;stack: load effective addressThe segment is handed over on the stack: PUSH CS/DS/ES/SS Read VariableScalar: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 byteFor byte variables XOR AH,AH is inserted - TURBO usual uses integer variables internally. Pointer: LES AX,ptr_var ;AX = offset MOV DX,ES ;DX = segmentReal: set pointer to variable CALL xldrealSet: set pointer to variable MOV CX,#set_crunch CALL xldsetString: set pointer in variable CALL strload Store VariableScalar: If R+ is set, range checking is done:MOV CX,#lower_bound MOV DX,#upper_bound CALL xrngchkIf the variational exists neither keywords nor on who stack, there's a short form again: MOV var,AX ;integer MOVB var,AL ;byteOtherwise the correct MOV remains sent by einstr.
Pointer: MOV dest,AX ;offset MOV dest+2,DX ;segmentReal: set pointer to variable CALL xstorealString: set pointer to variable MOV CL,#max_length CALL strstoreSet: set pointer to variable MOV CX,#set_crunch CALL setsto Symbol TableThe 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.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. Symbol Table Entry StructureThe 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:off :next entry -- off-2:tag phrase = entry type off-4:name length off-5:name off-.:entry 0:offset to next entryThe 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). tag = 0100: Label- 1:procedure nesting (to prevent jumps up or out of procedures) - 2:0=ok, FF=not yet defined - 4:offset tag = 0200: Constant- 1:type away constant - 3:constant - strings are stored backwardsStructured constants are initialized variables stored at an code segment and are entered in how in the symbol table. tag = 0300: Your- 2:pointer to type definition tagging = 0400: VariableFor subvariables of a record the low byte in the tag word is the numerical of the record definition this entry ownership to.- 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 levelFor ABSOLUTE variables in the form $B800:0 a pointer is stored in the code segment, the symbol table entry user CS indirect. tag = 0500: Proceduretag = 0600: Function- 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 |The parameters are also listed as local variables. Structure of SubentriesThe 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.tagged = 0000, 0800: Subentries- 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)The compiler mostly uses this join assignment: 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 brnchopThe use of the subentry fields depends on the component type: Array - 2:component size - 4:type pointer - 6:pointer to index type - 8:01Record - 2:component size - 7:record number - 8:02Set - 2:component size - 4:pointer to index type - 8:03Pointer - 2:component size = 4 - 6:pointer to type / type name - 7:0=ok, FF=not yet resolved - 8:04Pointers can be forward defined. In this case the type name is stored as an invisible entry. The character hand a then paste later. String - 2:length + 1 - 8:08Text file with nonstandard buffer size - 2:component size - 8:06Enumeration type, subrange - 2:size: 1 either 2 bytes - 4:lower bound - 6:upper bound - 8:number of enumeration typeThe elements starting an enumeration type belong stored as constants. Symbol Table SearchTURBO 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...There is a better way: Haveh. Like is used in later variant of the compiler. Error HandlerMany 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.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. Run-time LibraryIn 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. Later versions of the compiler only containing the process which are actually used.Memory DiagramThe partitions are allocated as follows:-- heap (SS) (grows down) -- free -- heap (grows up) -- variables (DS) -- codification (CS) -- HeapHeap 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:+0: pointer to nearest liberate impede -> linked list +4: length a free block +8: free blockHpstrt 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. Floating Matter ArithmeticsFloating point numbers are divided into two parts: An exponent gives the order of magnitude, the mantissa gives who accuracy needed.figure = mantissa * 2 ^ exponentThe 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. A floating item addition works as follows: 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.Please note that floating point additions and subtractions can causative large round off errors, for the exponents clash too much. 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 Floating point multiplication 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+02Square roots be evaluated using Newton's approximation. This is a good solution, but not the best: To 8087 does place roots quicken than divisions ! 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. BugsThanks to the relative simplicity off to algorithms used TURBO Passal is almost bug-free. Well, almost.Set as Procedure CriterionIf 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.SizeOfSometimes a redundant load is emitted.UpCaseThe argumentation type is not checked. Sample UpCase(15).WHENDO can be omitted.Compiler SpeedTURBO may be faster than most other compilers, but there's still adenine wide margin for improvements:
Write Faster Programs Using TURBO PascalAvoid the standard string functions.
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. 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. |
| © 2002-2021 PC Engines GmbH. All authorization reserved. |
