-
Notifications
You must be signed in to change notification settings - Fork 0
P32, 32 bit pascal compiler by Johan Prins
License
DosWorld/p32
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
P32, 32 bit pascal compiler by Johan Prins http://surf.to/p32 1. Introduction 1.1. Requirements To use to compiler you need to following equipment: . PC with at least a 386 processor . Some free harddisk space The compiler produces files containing assembly code. This code can be processed by an assembler. Good examples are TASM or NASM. See section 1.4 for more information on the tools. The next step is 'linking' the generated object files. You need a linker for this, several linkers are described in section 1.4. Short summary of the process: pascal file -> P32 -> assembly file -> ASM -> object file -> LINK -> executable Because there are a lot of good assemblers and linkers I didn't bother about writing my own ones. Maybe I will add them in the very far future. 1.1. History P32 is a free 32 bit Pascal compiler. The history of P32 started in 1996 when I downloaded PowerPascal. This little 'compiler', which produced code for OS/2, was the first compiler that had understandable source code. The first version of P32 was a simple conversion to the DOS protected mode (DOS32) platform. I realized the potential of the compiler and started adding a lot of new functions, like: support for procedures/functions, more data types, support for floating point numbers, etc. When I wanted to add more advanced functions like strong type checking, support for units, type conversios I discovered that the structure of the compiler wasn't strong enough. To come around this I completely rewrote the compiler so that it used symboltables and a parse tree. All identifiers were stored in symboltables and the parse tree stored the parsed pascal program. From the parse tree the compiler generated the assembly code. Up to now this structure is still sufficient and will remain the same for a long time, I hope. 1.2. Freeware P32 is being released as freeware/sourceware. This means that you don't need to pay anything for the program, besides that also all the sources to create the executables are included. Some people 2. User manual 2.1. 2.2. 2.3. Procedures and functions One of the main aspects of the Pascal programming language is the use of procedures and functions. These can greatly enhance the structure of your program. P32 adds several powerful features to procedures which make them more versatile. This is accomplished by adding some, optional, keywords. These keywords will be described later. First the syntax of the procedure (and function) is described: procedure <name> ( <parameters> ); <keyword> ; <string constant> ; <block statement> function <name> ( <parameters> ): <type> ; <keyword> ; <string constant> ; <block statement> As you might now everything behind the <name> is optional and can be combined in different ways. 2.3.1. String constant The <string constant> _needs_ to be the last element on the declaration line. The string constant can contain the actual name that P32 uses for the procedure/function when creating the assembly file. This can become very handy when linking with external object files. e.g: procedure modplay(var s: string); external; 'mxmplay'; This will create a procedure called 'modplay' but that procedure is actually a function in an third party library called 'mxmplay'. 2.3.2. Keywords. far, near, interrupt Added for compatibility with other compilers. The're ignored by P32, for clearity a warning message is outputted. assembler This forces you to use an asm-statement (asm..end) for the whole procedure or function. This helps you to write fast procedures in assembly language. The function result is returned on various places depending on the data type. . Integers, pointers, booleans and enumerated types return the result in the CPU register: EAX. . Floating point numbers need to be returned on the first FPU stack position, called: ST(0). . Other structures, such as string are not fully supported yet, so don't use the assembler keyword for these. register inline external win32api 2.4. Inline assembler The inline assembler can be invoked by using the ASM..END keywords. All statements between these two keywords are seen as assembler statements. The assembler parser isn't very sophisticated. This means that a lot of syntax errors aren't catched by the parser, the external assembler will than complain about them. There are a few rules you should keep in mind when using the inline assembler. When accessing variables always prefix the variable with one of the strings in the table, also you're forced to use brackets ('[',']') around the variables. Examples: mov eax, dword ptr [myvar] {correct} mov eax, myvar {incorrect} mov eax, dword ptr fs:[myvar] {correct} mov eax, dword ptr fs:eax {incorrect} mov eax, dword ptr fs:[eax] {correct} | Prefix | Size of variable | |-----------|------------------| | BYTE PTR | 1 | | WORD PTR | 2 | | DWORD PTR | 4 | | QWORD PTR | 8 | | TBYTE PTR | 10 | It's possible to let the compiler calculate certain values. This means that you can include a formula (_only_ containing constants of course) which will be optimized to a normal integer number. You can use numbers in decimal and hexadecimal format. Example: mov eax, (256*100)+080h Known incompatibilities - @RESULT isn't recognized 3. P32 Internals 3.1. Structure of P32 This chapter will explain the structure of P32. The structure of P32 is not derived from any book or whatever. Most of the theories and code are designed by myself or inspired by other compilers. The compiler consist of several parts that can be characterized by the following names: Program-parts: scanner parser optimizer code generator Data-structures: symbol-table constant-table parse-tree The heart of the compiler can be found in the combination of the scanner, parser, code-generator, symboltable and parse-tree. The following paragraphs will describe the functions of these parts. 3.2. Scanner 3.2.1. General The scanner, some may call it a lexical analyser, takes care of the reading of the source. The source is a normal textfile containing regular Pascal code. The scanner will recognize keywords, identifiers, operators and numbers. The operators (e.g. +, -, /, *) and keywords (e.g. BEGIN, END) are converted to so-called tokens. These tokens are easier (and faster) to handle for the compiler as strings. The identifiers are returned as strings and numbers are returned as either integers or floating points, depending on their type. The scanner is called by the parser. P32 uses a parser-driven scanner techniques. This means that when the parser needs more information the scanner is called. The scanner returns the requested information and the parser will process it. 3.2.2. Tokens The compiler use token to simplify the source code analyzing. The following structure is a simplified structure of the one used in P32. It's printed here as an example. Example token structure: Token = (_unknown, _string_constant, _integer_constant, _char_constant, _real_constant, _name, _program, _var, _const, _type_, _begin, _while, _repeat, _until, _lparen, _rparen, _separator, _assign, _equal, _greater, _less, _less_eq, _greater_eq, _not_eq, _colon); The token strings are stored in typed constant array. The actual reading and analysing is done by the GetToken procedure. This procedure looks like this: procedure GetToken; begin case Look of {skip comments} '{' : begin getchar; if look='$' then DoDirectives; repeat getchar; until Look = '}'; getchar; end; '0'..'9': begin {number, integer/floating point} while Look in ['0'..'9'] do begin current_string:=current_string+look; GetChar; end; if (Look='.') or (upcase(Look)='E') then if (Ahead='.') then begin {subrange, like: 1..100} val(current_string, current_Number, code); current_token := _integer_constant; end else begin if (upcase(Look) in ['.','0'..'9','E','-']) then begin {real constant: 3. or 2.0} current_string:=current_string+look; GetChar; while (upcase(Look) in ['0'..'9','E','-']) do begin current_string:=current_string+look; GetChar; end; val(current_string, current_float, code); current_token := _real_constant; end; end else begin val(current_string,Current_Number,code); current_token := _integer_constant; end; end; '_', 'A'..'Z', 'a'..'z' : begin {identifier} while Look in ['_', '0'..'9','A'..'Z','a'..'z' ] do begin Current_String := Current_String + upcase(Look); GetChar; end; for i := 0 to MaxToken do if Current_String = TokenName[token(i)] then begin Current_Token := Token(i); end; if Current_Token=_unknown then Current_Token:=_name; end; else Current_String := upcase(Look); GetChar; repeat J := 0; for i := 0 to MaxToken do if (Current_string + upcase(Look)) = TokenName[token(i)] then J := i; if J <> 0 then begin Current_String := Current_String + upcase(Look); GetChar; end; until J = 0; for i := 0 to MaxToken do if Current_String = TokenName[token(i)] then J := i; Current_Token := Token(j); end; end; This is a simplified example but it should give you an idea of the working of the scanner. It's the easiest part of the compiler, but very important. Because without it the compiler won't work... Reference: P32_SCAN.PAS 3.2.3. Pre-processor The compiler is equipped with a simple pre-processor. Most compiler have a pre-processor that analyse the source code before it's scanned. P32 has combines the scanner and pre-processor. The pre-processor is in this compiler uses for analysing compiler directives. This can be a compiler specific option, like $A+ to turn on data alignment or a directive for conditional compiling. For conditional compiling the following directives are supported: $ifdef, $else, $endif. In the following example you can see that the strings that control the compilation of source code are pushed on a virtual stack. case current_directive of _ifdef: begin GetToken; b:=FindStringName(directive_names, current_string); IfPush(b); if b=FALSE then EatCode; end; _else : if IfInverted then LineError(LineCount, 'Error with $ELSE') else IfInvert; _endif: if IfEmpty then LineError(LineCount, 'Error with $ENDIF') else IfPop; end; This small routine controls the conditional compiling. See the source code for more information. Reference: P32_PREP.PAS 3.3. Parser 3.4. Optimizer 3.5. Code generator <more to come> Appendix A. P32 unit format The unit header is 32 bytes long, it uses the following structure: unitstart : record id : array[1..3] of char; { P32 } brk : char; { #26 } version : word; { version number } code : longint; symbols : word; { # of symbols } types : word; { # of types } comp : boolean; { is compression enabled? } comptype : byte; { cur: 0 = none, 1 = rle } reserved : array[1..16] of char; end; The unit saving routines use RLE compression to minimize diskspace usage. The RLE compression is built up like this: ? = Value set as needed. Part Size Offset Value ---------------------------------------------- Header 32 0 ? * id 3 0 'P32' * brk 1 3 #26 * version 2 4 6 * code 4 6 ? * symbols 2 10 ? * types 2 12 ? * comp 1 14 TRUE (or FALSE) * comptype 1 15 Current: 0 = none, 1 = rle * reserved 16 16 Current: #0#0#0#0...#0#0 RLEHeader 8 32 ? * packs 2 32 ? * packsize 2 34 ? * reserved 4 36 Current: #0#0#0#0 RLEDatas 8 40 ? * datatype 2 40 ? * datacode 1 42 ? * datasize 2 43 ? * reserved 3 45 Current: #0 * Data ? 48 ? RLEData 8 ? ? ... x packs With this RLE compression method, maximum size of an unit file, is 2^32 (4 GB). Though version 6 uses a packsize of 8192, which limits the size of an unitfile to 536870912 bytes. (512 MB) RLEData.datatype is a word describing the RLE compression method used. Current implemented: 0 - Store (no compression at all, just read next RLEPackSize bytes) 1 - RLE (usual RLE 8 bit compression) RLEData.datacode is the escape sequence to signal that something special is coming DATA Decompression examples: Magic byte is $ff Byte in: $55 $66 $77 $88 Byte out: $55 $66 $77 $88 Byte in: $55 $ff $06 $88 $99 (magic,count,value) Byte out: $55 $88 $88 $88 $88 $88 $88 $99 Byte in: $ff $00 $66 (magic, count = 0, means store magic) Byte out: $ff $66 Byte in: ... (pos RLEPAckSize-1) $ff Byte out: $ff Appendix B. Assemblers and DOS extenders The following table shows that assembler and DOS-extender combinations that are supported by P32 v0.4. ----------------------- | DOS32 | WDOSX | PRO32 | |--------------------------------| | TASM | x | x | - | |--------------------------------| | NASM | ? | ? | - | |--------------------------------| | PASS32 | - | x | ? | |--------------------------------| x supported - not supported o under development ? not sure or partially supported Note: NASM is not fully supported because it contains some bugs that prevends P32 from using it. Target: TASM/DOS32 ------------------ Required files: TASM .EXE (v3.1 or better) DLINK .EXE (v1.3 or better) DOS32 .EXE (v3.3 or better) TASM.EXE /m3 /t /uT310 <name> Note: Repeat this for all units. DLINK.EXE -t -p <name> <units> Note: The executable requires DOS32.EXE to run. Where to get it: TASM.EXE from Borland (e.g. BP7 package) DLINK.EXE and DOS32.EXE from the DOS32V33.ZIP Debugging: Use DEBUG.LIB from DOS32V33.ZIP to debug DOS32 executables, you need to add a line 'call debug' to the main source and you need to add ',,,debug.lib' to the linker commandline. Target: TASM/WDOSX ------------------ Required files: TASM .EXE (v3.1 or better) TLINK32 .EXE STUBIT .EXE (v0.94 or better) TASM.EXE /m3 /t /uT310 <name> Note: Repeat this for all units. TLINK32.EXE <name> <units> STUBIT.EXE <result.exe> Where to get it: TASM.EXE from Borland (e.g. BP7 package) TLINK32.EXE from Borland (e.g. TASM 4.0 package) STUBIT.EXE from WDOSX094.ZIP Debugging: Use WUDEBUG.EXE from WDOSX094.ZIP to debug WDOSX executables. you can start it with WUDEBUG <result.exe> Target: PASS32/WDOSX -------------------- Required files: PASS32 .EXE (v2.1 or better) WDOSX .DX (v0.94 or better) PASS32.EXE <name> -o -im:<unit> Note: PASS32 supports 'smart-linking'! Where to get it: PASS32.EXE from PASS32V2.ZIP WDOSX.DX from STUBIT.EXE (WDOSX094.ZIP), use -extract options to get it. (you can also download PASSWDX.ZIP from the P32 homepage) Debugging: Use WUDEBUG.EXE from WDOSX094.ZIP to debug WDOSX executables. you can start it with WUDEBUG <result.exe>
About
P32, 32 bit pascal compiler by Johan Prins
Topics
Resources
License
Stars
Watchers
Forks
Releases
No releases published
Packages 0
No packages published