-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathREADME.TXT
More file actions
513 lines (400 loc) · 17.2 KB
/
README.TXT
File metadata and controls
513 lines (400 loc) · 17.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
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>