home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
DP Tool Club 24
/
CD_ASCQ_24_0995.iso
/
vrac
/
tsfaqp26.zip
/
FAQPAS3.TXT
< prev
next >
Wrap
Internet Message Format
|
1995-07-23
|
45KB
From ts@uwasa.fi Sun Jul 23 00:00:00 1995
Subject: FAQPAS3.TXT contents
Copyright (c) 1993-1995 by Timo Salmi
All rights reserved
FAQPAS3.TXT The third set of frequently (and not so frequently)
asked Turbo Pascal questions with Timo's answers. The items are in
no particular order.
You are free to quote brief passages from this file provided you
clearly indicate the source with a proper acknowledgment.
Comments and corrections are solicited. But if you wish to have
individual Turbo Pascal consultation, please post your questions to
a suitable Usenet newsgroup like news:comp.lang.pascal.borland. It
is much more efficient than asking me by email. I'd like to help,
but I am very pressed for time. I prefer to pick the questions I
answer from the Usenet news. Thus I can answer publicly at one go if
I happen to have an answer. Besides, newsgroups have a number of
readers who might know a better or an alternative answer. Don't be
discouraged, though, if you get a reply like this from me. I am
always glad to hear from fellow Turbo Pascal users.
....................................................................
Prof. Timo Salmi Co-moderator of news:comp.archives.msdos.announce
Moderating at ftp:// & http://garbo.uwasa.fi archives 193.166.120.5
Department of Accounting and Business Finance ; University of Vaasa
ts@uwasa.fi http://uwasa.fi/~ts BBS 961-3170972; FIN-65101, Finland
--------------------------------------------------------------------
61) What are Binary Coded Decimals? How to convert them?
62) How can I copy a file in a Turbo Pascal program?
63) How can I use C code in my Turbo Pascal program?
64) How do I get started with the Turbo Profiler?
65) How can I detect if the shift/ctrl/alt etc key is pressed?
66) How do I get a base 10 logarithm in TP?
67) If Delay procedure does not work properly, how do I fix it?
68) How much memory will my TP program require?
69) How to detect if a drive is a CD-ROM drive?
70) How do I convert an array of characters into a string?
71) How do I get started with graphics programming?
72) Where to I find the different sorting source codes?
73) A beginner's how to write and compile units.
74) What are and how do I use pointers?
75) How can I read another program's errorlevel value in TP?
76) What are the current Pascal newsgroups on the Usenet news?
77) How do I detect the CapsLock status, how do I turn it on/off?
78) How do I detect if the F11 or F12 key has been pressed?
79) How do I extract (parse) substrings from an input string?
80) How do I find out the size of any kind of a file?
81) How do I format graphics output like in textmode writeln?
--------------------------------------------------------------------
From ts@uwasa.fi Sun Jul 23 00:01:01 1995
Subject: Binary Coded Decimals
61. *****
Q: What are Binary Coded Decimals? How to convert them?
A: Let us look at full integers only and skip the even more
difficult question of BCD reals and BCD operations.
Decimal Hexa BCD
1 $1 1
: $9 9
10 $A ..
: : :
12 $C ..
: : :
16 $10 10
17 $11 11
18 $12 12
: : :
Consider the last value, that is BCD presentation of 12. The
corresponding hexadecimal is $12 (not $C as in normal decimal to
hexadecimal conversion). The crucial question is how to convert
12BCD to $12 (or its normal decimal equivalent 18). Here is my
sample code:
type BCDType = array [0..7] of char;
{}
procedure StrToBCD (s : string; var b : BCDType);
var i, p : byte;
begin
FillChar(b, SizeOf(b), '0');
p := Length (s);
if p > 8 then exit;
for i := p downto 1 do b[p-i] := s[i];
end; (* strtobcd *)
{}
function BCDtoDec (b : BCDType; var ok : boolean) : longint;
const Digit : array [0..9] of char = '0123456789';
var i, k : byte;
y, d : longint;
begin
y := 0;
d := 1;
ok := false;
for i := 0 to 7 do begin
k := Pos (b[i], Digit);
if k = 0 then exit;
y := y + (k-1) * d;
if i < 7 then d := 16 * d;
end; { for }
ok := true;
BCDtoDec := y;
end; (* bcdtodec *)
{}
{}
procedure TEST;
var i : byte;
b : BCDType;
x : longint;
ok : boolean;
s : string;
begin
s := '12';
StrToBCD (s, b);
write ('The BCD value : ');
for i := 7 downto 0 do write (b[i], ' ');
writeln;
x := BCDtoDec (b, ok);
if ok then writeln ('is ', x, ' as an ordinary decimal')
else writeln ('Error in BCD');
end; (* test *)
{}
begin TEST; end.
Next we can ask, what if the BCD value is given as an integer.
Simple, first convert the integer into a string. For example in
the procedure TEST put
Str (12, s);
Finally, what about converting an ordinary decimal to the
corresponding BCD but given also as a decimal variable. For example
18 --> 12?
function LHEXFN (decimal : longint) : string;
const hexDigit : array [0..15] of char = '0123456789ABCDEF';
var i : byte;
s : string;
begin
FillChar (s, SizeOf(s), ' ');
s[0] := chr(8);
for i := 0 to 7 do
s[8-i] := HexDigit[(decimal shr (4*i)) and $0F];
lhexfn := s;
end; (* lhexfn *)
{}
function DecToBCD (x : longint; var ok : boolean) : longint;
const Digit : array [0..9] of char = '0123456789';
var hexStr : string;
var i, k : byte;
y, d : longint;
begin
hexStr := LHEXFN(x);
y := 0;
d := 1;
ok := false;
for i := 7 downto 0 do begin
k := Pos (hexStr[i+1], Digit);
if k = 0 then exit;
y := y + (k-1) * d;
if i > 0 then d := 10 * d;
end; { for }
ok := true;
DecToBCD := y;
end; (* dectobcd *)
{}
procedure TEST2;
var i : byte;
x10 : longint;
xBCD : longint;
ok : boolean;
begin
x10 := 18;
writeln ('The ordinary decimal value : ', x10);
xBCD := DecToBCD (x10, ok);
if ok then writeln ('is ', xBCD, ' as a binary coded decimal')
else writeln ('Error in BCD');
end; (* test2 *)
{}
begin TEST; end.
--------------------------------------------------------------------
From ts@uwasa.fi Sun Jul 23 00:01:02 1995
Subject: Copying with TP
62. *****
Q: How can I copy a file in a Turbo Pascal program?
A: Here is the code. Take a close look. It has some instructive
features besides the copying, like handling the filemode and using
dynamic variables (using pointers).
procedure SAFECOPY (fromFile, toFile : string);
type bufferType = array [1..65535] of char;
type bufferTypePtr = ^bufferType; { Use the heap }
var bufferPtr : bufferTypePtr; { for the buffer }
f1, f2 : file;
bufferSize, readCount, writeCount : word;
fmSave : byte; { To store the filemode }
begin
bufferSize := SizeOf(bufferType);
if MaxAvail < bufferSize then exit; { Assure there is enough memory }
New (bufferPtr); { Create the buffer }
fmSave := FileMode; { Store the filemode }
FileMode := 0; { To read also read-only files }
Assign (f1, fromFile);
{$I-} Reset (f1, 1); {$I+} { Note the record size 1, important! }
if IOResult <> 0 then exit; { Does the file exist? }
Assign (f2, toFile);
{$I-} Reset (f2, 1); {$I+} { Don't copy on an existing file }
if IOResult = 0 then begin close (f2); exit; end;
{$I-} Rewrite (f2, 1); {$I+} { Open the target }
if IOResult <> 0 then exit;
repeat { Do the copying }
BlockRead (f1, bufferPtr^, bufferSize, readCount);
{$I-} BlockWrite (f2, bufferPtr^, readCount, writeCount); {$I+}
if IOResult <> 0 then begin close (f1); exit; end;
until (readCount = 0) or (writeCount <> readCount);
writeln ('Copied ', fromFile, ' to ', toFile,
' ', FileSize(f2), ' bytes');
close (f1); close (f2);
FileMode := fmSave; { Restore the original filemode }
Dispose (bufferPtr); { Release the buffer from the heap }
end; (* safecopy *)
Of course a trivial solution would be to invoke the MS-DOS copy
command using the Exec routine. (See the item "How do I execute an
MS-DOS command from within a TP program?")
--------------------------------------------------------------------
From ts@uwasa.fi Sun Jul 23 00:01:03 1995
Subject: C modules in TP
63. *****
Q: How can I use C code in my Turbo Pascal program?
A: I have very little information on this question, since I do not
program in C myself. However in reading Turbo Pascal textbooks I
have come across a couple of references I can give. They are Edward
Mitchell (1993), Borland Pascal Developer's Guide, pp. 60-64, and
Stoker & Ohlsen (1989), Turbo Pascal Advanced Techniques, Ch 4.
--------------------------------------------------------------------
From ts@uwasa.fi Sun Jul 23 00:01:04 1995
Subject: Using Turbo Profiler
64. *****
Q: How do I get started with the Turbo Profiler?
A: Borland's separate Turbo Profiler is a powerful tool for
improving program code and enhancing program performance, but far
from an easy to use. It is an advanced tool. In fact setting it up
the first time is almost a kind of detective work.
Let's walk through the steps with Turbo Profiler version 1.01 to
see where a running Turbo Pascal program takes its time.
Assume a working directory r:\
1. Copy the target .PAS file to r:\
2. Compile it with TURBO.EXE using the following Compiler and
Debugger options. The standalone debugging option is crucial.
Code generation
[ ] Force far calls [X] Word align data
[ ] Overlays allowed [ ] 286 instructions
Runtime errors Syntax options
[ ] Range checking [X] Strict var-strings
[X] Stack checking [ ] Complete boolean eval
[ ] I/O checking [X] Extended syntax
[ ] Overflow checking [ ] Typed @ operator
[ ] Open parameters
Debugging
[X] Debug information Numeric processing
[X] Local symbols [ ] 8087/80287
[ ] Emulation
Debugging Display swapping
[X] Integrated ( ) None
[X] Standalone () Smart
( ) Always
3) Call TPROF.EXE
4) Load the .EXE file produced by compilation in item 2.
5) Choose from the TPROF menus
Statistics
Profiling options...
Profile mode
() Active ( ) Passive
Run count
1
Maximum areas
200
6) Choose from the TPROF menus
Options
Save options...
[X] Options
[ ] Layout
[ ] Macros
Save To
r:\tfconfig.tf
7) Press Alt-F10 for the Local Menu. Choose
Add areas
All routines
and so on.
8) Choose Run from the TPROF menus (or F9)
9) Choose from the TPROF menus
Print
Options...
Width
80
Height
9999
( ) Printer ( ) Graphics
() File () ASCII
Destination File
r:\report.lst
10) Print
Module...
All modules
Statistics
Overwrite
Also see Edward Mitchell (1993), Borland Pascal Developer's Guide.
It has a a very instructive chapter "Program Optimization" on the
Turbo Profiler. The material in the Turbo Profiler manual is so
complicated that additional guidance like Mitchell's is very much
needed.
--------------------------------------------------------------------
From ts@uwasa.fi Sun Jul 23 00:01:05 1995
Subject: Detecting shift status
65. *****
Q: How can I detect if the shift/ctrl/alt etc key is pressed? I
know how to get the scan codes with the ReadKey function, but I
can't find the procedure for detecting these keys.
A: Detecting pressing the special keys or getting the toggle status
cannot be done with ReadKey. You'll need to access the Keyboard
Flags Byte at $0040:$0017. You can do this either by a direct "Mem"
access, or using interrupt $16 function $02. For more details
including the bitfields for the shift flags see in Ralf Brown's
interrupt list ftp://garbo.uwasa.fi/pc/programming/inter46a.zip (or
whatever is the current version). For example to see if the alt key
is pressed you can use
uses Dos;
function ALTDOWN : boolean;
var regs : registers;
begin
FillChar (regs, SizeOf(regs), 0);
regs.ah := $02;
Intr ($16, regs);
altdown := (regs.al and $08) = $08;
end;
For the enhanced keyboard flags see interrupt $16 function $12. It
can distinguish also between the right and the left alt and ctlr
keys.
A tip from Martijn Leisink martijnl@sci.kun.nl. Be careful [if
you use the $0040:$0017 memory position to set a toggle]: On several
computers you have to call int 16h after the new setting is shown by
the LED's on the keyboard. Not doing so might give the user wrong
information.
A tip from Dr John Stockton jrs@dclf.npl.co.uk. Going via a
BytePointer set to Ptr(Seg0040, $0017) is almost as easy as "Mem",
and also works in Protected mode.
--------------------------------------------------------------------
From ts@uwasa.fi Sun Jul 23 00:01:06 1995
Subject: Base 10 logarithm
66. *****
Q: How do I get a base 10 logarithm in TP?
A: Just define
function log (x : real) : real;
begin log := ln(x) / ln(10); end;
This result is based on some elementary math. By definition
y = log(x) in base 10 is equivalent to x = 10^y (where the ^
indicates an exponent). Thus ln(x) = y ln(10) and hence
y = ln(x) / ln(10).
--------------------------------------------------------------------
From ts@uwasa.fi Sun Jul 23 00:01:07 1995
Subject: Replacing Delay procedure
67. *****
Q: If Delay procedure does not work properly, how do I fix it?
A: The Delay procedure in the Crt unit delays a specified number of
milliseconds. It is declared as "procedure Delay(MS: Word);". There
are two problems. The procedure requires using the Crt unit and
there is a bug in it in TP 6.0, at least. The alternative is to use
the procedure GetTime(var Hour, Minute, Second, Sec100: Word) as
shown by the skeleton below
GetTime (...)
initialTime := ...
repeat
GetTime (...)
interval := ... - initialTime;
until interval >= YourDelay;
There are two things you will have to see to. You will have to
convert the time to sec100, and you will have to take care of the
possibility of the interval spanning the midnight. If you do not
wish to program the alternative Delay procedure yourself, you can
use "DOSDELAY Delay without needing the Crt unit" from TSUNTD.TPU
from ftp://garbo.uwasa.fi/pc/ts/tspa3470.zip.
A2: Dr John Stockton jrs@dclf.npl.co.uk suggested procedure that is
expanded below. It has the advantage of being concise and working in
the protected mode. The disadvantage is that it requires a later TP
version. The solution is quite instructuve.
uses Dos;
{... John's procedure ...}
procedure WAIT (SecondsDelay : real) ;
Var Tptr : ^longint ; Finish : longint ;
begin
Tptr := Ptr(Seg0040, $006C) ;
Finish := Tptr^ + Round(18.2*SecondsDelay) ;
repeat until Tptr^ > Finish ;
end;
{... now let's test it ...}
var h1, m1, s1, sa100 : word;
h2, m2, s2, sb100 : word;
begin
GetTime (h1, m1, s1, sa100);
WAIT (3);
GetTime (h2, m2, s2, sb100);
writeln (h1, ':', m1, ':', s1, '.' ,sa100);
writeln (h2, ':', m2, ':', s2, '.' ,sb100);
end.
--------------------------------------------------------------------
From ts@uwasa.fi Sun Jul 23 00:01:08 1995
Subject: TP program memory requirement
68. *****
Q: How much memory will my TP program require?
A: Get MAPMEM.EXE from ftp://garbo.uwasa.fi/pc/memutil/tsrcom35.zip
and put the following code within your Turbo Pascal program:
Program faq;
uses Dos;
:
SwapVectors;
Exec (GetEnv('comspec'), '/c mapmem');
Swapvectors;
Then you'll see a MAPMEM output something like this
Psp Cnt Size Name Command Line Hooked Vectors
---- --- ------ ---------- ------------------- --------------
2 26,896 DOS
0694 2 3,392 COMMAND 2E
1 64 ---free---
0776 2 1,488 MARK scrollit
07D6 2 70,816 FAQ FF
1923 3 2,752 command 22 23 24
19D2 2 549,712 ---free---
655,344 ---total--
The memory requirement of your program FAQ.PAS is 70,816. Do not
confuse this figure with the physica size of your program. The
memory requirement affected among other things by the Memory
Allocation Sizes Directive. For example you might have
{$M 16384,0,50000}
-Date: Sun, 12 Jun 1994 10:22:18
-From: dmurdoch@mast.queensu.ca (Duncan Murdoch)
-Newsgroups: comp.lang.pascal
-Subject: Re: How much memory will my TP program require?
I think this is a hard question, and probably needs a longer
answer than you gave. Yours isn't quite right, because TP will
allocate memory that it doesn't need if you set the heapmax
parameter too high. Your program will run in less memory than
MAPMEM reports. Here's a quick attempt at it:
TP DOS programs use memory in 4 or 5 blocks: fixed code, static
data, the stack, sometimes overlaid code, and the heap. TP Windows
programs add a local heap to this list, but don't use overlays. The
discussion below deals with real mode DOS programs.
The size of the code is determined by which procedures and
functions you use in your program. In DOS, if you don't use
overlays, this is all fixed code, and the size is reported as "Code
size" in the Compile| Information listing in the IDE. The ways to
reduce it are to use fewer procedures or make them smaller, or move
them to overlays.
Static data consists of all the global variables and typed
constants in every unit. It is reported as "Data size" in the
Compile|Information listing. You can reduce it by declaring fewer
or smaller variables.
If you use the $O directive to move code to overlays, then those
units won't count as part of your fixed code needs. You will need
an overlay buffer at run-time; by default, it's the size of the
largest unit you use, but normally you'll change the size with
OvrSetBuf. It's difficult to work out the best size of this block
except by trial and error: if your program spends too much time
swapping, then make it larger; if you run out of memory, make it
smaller. You'll need to use the .MAP file (see the Options| Linker
dialog to create one) to find the size of each unit. Remember to
subtract the size of overlaid units from the reported "Code size"
when working out the size of fixed code.
The stack is used for local variables in procedures. Its size is
controlled by the first parameter to the $M directive; the default
size is 16K. It's hard to predict exactly how much stack space your
program will use. One way is to keep reducing the value until your
program aborts with a stack overflow, then use a slightly larger
value. Another way is to fill the stack with a fixed value at the
start of your program, and at the end, see how many values were
changed. Again, it's a good idea to allow for a margin of safety,
because hardware interrupts will use this space, and their size is
hard to predict.
The heap is where New and Getmem get their allocated memory. The
size is controlled by the 2nd and 3rd parameters to the $M
directive. The heapmin value will always be allocated; if extra
memory is available, your program will ask for as much as possible,
up to heapmax. If not enough memory is available to load all your
fixed code, data, stack and heapmin, DOS will refuse to load your
program. You have nearly complete control over the size of the heap
that you need, determined by how much you use New and Getmem. The
only exception is that some of the standard units use heap space;
GRAPH and all the TurboVision units are examples. To find how much
your program actually uses, you can reduce Heapmax until it fails,
fill the heap with a special value and look for changes, or monitor
the value of HeapPtr as your program progresses.
--------------------------------------------------------------------
From ts@uwasa.fi Sun Jul 23 00:01:09 1995
Subject: Detecting a CD-ROM drive?
69. *****
Q: How to detect if a drive is a CD-ROM drive?
A: There are several methods to do this. Here is one option.
(* Is a drive a CD-ROM with MSCDEX driver installed *)
function CDROMFN (drive : char) : boolean;
var regs : registers;
begin
cdromfn := false;
if swap(DosVersion) < $0200 then exit;
drive := UpCase(drive);
if (drive < 'A') or (drive > 'Z') then exit;
FillChar (regs, SizeOf(regs), 0);
regs.cx := ord(drive) - ord('A');
regs.ax := $150B;
Intr ($2F, regs);
cdromfn := (regs.ax <> 0) and (regs.bx = $ADAD);
end; (* cdromfn *)
The other relevant $2F interrupt functions you can use are $1500,
$1501, and in particular $150D.
--------------------------------------------------------------------
From ts@uwasa.fi Sun Jul 23 00:01:10 1995
Subject: Array of chars into string
70. *****
Q: How do I convert an array of characters to a string? More
specifically, I haven't been able to convert an array of characters
into a string, so that I can write it to a file. The only way I have
been able to do it, is writing 1 char at a time.
A: Carefully study these two simple test examples. Note the
difference in the array's dimensions in the tests.
type atype = array [0..20] of char;
type stype = string[20];
var s : stype;
a : atype absolute s;
begin
FillChar (a, SizeOf(a), '*');
s[0] := chr(20);
writeln (s);
end.
type atype = array [1..20] of char;
var s : string;
a : atype;
begin
FillChar (a, Sizeof(a), '*');
Move (a, s[1], 20);
s[0] := chr(20);
writeln (s);
end.
Of course, you could also assign the array's characters one by one
to the string using a simple for loop (left as an exercise), but the
above methods are more efficient.
--------------------------------------------------------------------
From ts@uwasa.fi Sun Jul 23 00:01:11 1995
Subject: Graphics programming primer
71. *****
Q: How do I get started with graphics programming?
A:
(* This simple test shows the rudiments of getting started with Turbo
Pascal graphics programming *)
uses Crt, Graph;
var grDriver : integer;
grMode : integer;
ErrCode : integer;
i, j : integer;
xm, ym : integer;
const CharSize : integer = 3;
begin
{ Request graphics driver autodetection }
grDriver := Detect;
{ Initialize graphics system and put hardware into graphics mode }
{ The relevant .bgi driver is needed in the current directory
for example egavga.bgi }
InitGraph (grDriver, grMode, ' ');
{ Return an error code for the previous graphic operation }
ErrCode := GraphResult;
{ Test for initialialization success }
if ErrCode <> grOk then begin
Writeln ('Graphics error:', GraphErrorMsg(ErrCode)); halt; end;
{ Clear the output device and home the current pointer }
ClearDevice;
{}
{ Use your own coordinates }
xm := Round (GetMaxX / 100.0);
ym := Round (GetMaxY / 100.0);
{}
{ Set the current line width and style, optional }
SetLineStyle (SolidLn, 0, ThickWidth);
{ Set the drawing color }
SetColor (Yellow);
{ Draw a line }
Line (70*xm, 50*ym, 90*xm, 80*ym);
{}
{ Drawing bars }
{ Set the fill pattern and color }
SetFillStyle (SolidFill, Red);
Bar (0, 0, 25*xm, 25*ym);
{}
SetColor (Magenta);
SetFillStyle (SolidFill, Blue);
Bar3D (30*xm, 20*ym, 50*xm, 60*ym, 8*xm, TopOn);
{}
{ Writing text in the graphics mode }
{ Set the drawing color }
SetColor (LightCyan);
{ Set the current background color }
SetBkColor (Black);
{ Set style for text output in graphics mode }
SetTextStyle(DefaultFont, HorizDir, CharSize);
OutTextXY (0, 80*ym, 'Press any key');
{}
repeat until KeyPressed;
{}
{ Restore the original screen mode before graphics was initialized }
RestoreCrtMode;
writeln ('That''s all folks');
{ Shut down the graphics system }
CloseGraph;
end.
For an example what you can do with graphics, see
111673 Oct 8 1993 ftp://garbo.uwasa.fi/pc/ts/tsdemo16.zip
tsdemo16.zip Assorted graphics demonstrations of functions etc
(or whatever is the current version).
--------------------------------------------------------------------
From ts@uwasa.fi Sun Jul 23 00:01:12 1995
Subject: Sorting it out
72. *****
Q: Where to I find the different sorting source codes?
A: I'll answer very briefly by giving two references:
303771 May 2 1991 ftp://garbo.uwasa.fi/pc/turbopas/nrpas13.zip
nrpas13.zip Numerical Recipes Pascal shareware version
and
Gary Martin (1992), Turbo Pascal, Theory and Practice of Good
Programming, Chapter 15.
--------------------------------------------------------------------
From ts@uwasa.fi Sun Jul 23 00:01:13 1995
Subject: TP units
73. *****
Q: A beginner's how to write and compile units.
A1: Many of the text-books in the bibliography section of this FAQ
discuss using units in Turbo Pascal. For example see Tom Swan
(1989), Mastering Turbo Pascal 5.5, Chapters 9 and 10 for a more
detailed discussion than the rudiments given in the current item.
Likewise see your Turbo Pascal (7.0) User's Guide Chapter 6, "Turbo
Pascal units".
You can and need to write your own units if you need recurring or
common routines in your programs and/or your program becomes so big
that it cannot be handled as a single entity.
A Turbo Pascal unit is a separate file which you compile. The
following trivial example to calculate the sum of two reals
illustrates the basic structure of a unit.
{ The name of this file must be faq73.pas to correspond. }
unit faq73;
{}
{ The interface section lists definitions and routines that are }
{ available to the other programs or units. }
interface
function SUMFN (a, b : real) : real;
{}
{ The implementation section contains the actual unit program }
implementation
function SUMFN (a, b : real) : real;
begin
sumfn := a + b;
end;
{}
end.
When you compile the file FAQ73.PAS a unit FAQ73.TPU results. Next
an example utilizing the faq73 unit in the main program.
uses faq73;
{}
procedure TEST;
var x, y, z : real;
begin
x := 12.34;
y := 56.78;
z := SUMFN (x, y);
writeln (z);
end;
{}
begin
TEST;
end.
A2: Most often you would be compiling a Turbo Pascal program
using the IDE (Integrated Development Environment). If you have
precompiled units you must see to it that you have informed the IDE
of the path to them.
Press F10 and invoke the "Options" menu (or press alt-O). Select
"Directories...". Press tab two times to get to "Unit directories"
and edit the path accordingly. Here is what I have entered myself
EXE & TPU directory r:\
Include directories r:\
Unit directories f:\progs\turbo70\tpu70
Object directories f:\progs\turbo70\tpu70
As you see I keep all my precompiled Turbo Pascal 7.0 units in the
f:\progs\turbo70\tpu70 directory.
--------------------------------------------------------------------
From ts@uwasa.fi Sun Jul 23 00:01:14 1995
Subject: Beginners' pointers
74. *****
Q: What are and how do I use pointers?
A: This is a beginner's simplified introduction. A pointer is a
variable type used to hold the address of another variable, that is
to point to it. Pointers are used to
1) To refer to and manipulate variables indirectly.
2) In Turbo Pascal to obtain access to the heap storage area, which
is not restricted to 64Kbytes.
Consider the following example
{$M 16384,0,80000}
var yPtr : ^real;
begin
New(yPtr);
yPtr^ := 3.14159;
writeln ('2 times pi = ', 2.0 * yPtr^);
Dispose(yPtr);
yPtr := nil;
end.
Before we can discuss pointers we have to consider some rudiments of
what a kind of a memory model a compiled Turbo Pascal program uses.
This is a highly simplified presentation. For a more detailed
presentation of the TP memory model see for example Tischer (1990b).
+-------------------------+
| Heap |
|-------------------------|
| Data Segment |
|-------------------------|
| Code |
|-------------------------|
| Program Segment Prefix |
+-------------------------+
When you write and compile a Turbo Pascal program it usually
consists of (this is a simplification!) of the three lowest parts.
When you define a global variable, it goes to the Data Segment. For
example defining at the beginning of your program
var x : real;
requires 6 bytes from the data segment. (Local variables are placed
on the stack.)
Now, the catch is that because of the underlying 16-bit nature of
MS-DOS, the size of the data segment cannot exceed 64Kb. On occasion
the 64Kb is insufficient. However, if you use pointers, the
corresponding variable values are held on the heap instead of the
data segment or the stack. Before you can use the heap, you have to
reserve it for your program. The following compiler directive makes
a heap of 80000 bytes available to your program {$M 16384,0,80000}.
(The syntax is {$M Stack size, Low heap limit, High heap limit}).
With pointers you do not refer to a variable directly, but you
point to it. For example, define
var yPtr : ^real;
Before you can use this pointer, you have to create this new dynamic
variable as follows:
New(yPtr);
The New(yPtr) statement "Creates a new dynamic variable and sets a
pointer variable to point to it." This pointer, yPtr, will point to
the actual value, which the program puts on the heap. In your
program you can write, for example
yPtr^ := 3.14159;
Think about the difference between yPtr and yPtr^. The former
contains the value of the memory address where you now have put the
value 3.14159. The latter gives that value. Hence yPtr^ can be used
like any ordinary real variable. The difference is that it is on the
heap, not on the data segment (or stack). Thus you can now use this
pointer. For example you n write
writeln ('2 times pi = ', 2.0 * yPtr^);
When you do not need the pointer any more in your program you can
dispose of it to release the memory allocated for other purposes:
Dispose(yPtr);
yPtr := nil;
"After a call to Dispose, the value of yPtr is undefined and it is
an error to reference yPtr. The reserved word nil denotes a pointer
type constant that does not point to anything." Setting yPtr := nil
is just good programming practice, because then you can later easily
test whether the pointer is available or not. Disposing of a pointer
within your program is not necessary unless the amount of memory is
a critical consideration in your program. The heap will be released
when your program terminates.
To recount. What yPtr actually contains is the memory address of
the value on the heap. When you write yPtr^, the caret indicates
that you do not mean the pointer itself, but the pointed memory
location in the heap. In this example that memory location in the
heap was made to contain 3.14159.
You can also define the pointer types. Our second example
illustrates. It displays the squares from one to ten.
{$M 16384,0,80000}
type arrayType = array [1..10] of real;
type arrayPtrType = ^arrayType;
var A : arrayPtrType;
i : integer;
begin
if SizeOf(arrayType) > MaxAvail then begin
writeln ('Out of memory');
halt;
end;
New(A);
for i := 1 to 10 do A^[i] := i*i;
writeln (A^[9]);
end.
For an actual application using pointers, see the item "How can I
copy a file in a Turbo Pascal program?"
--------------------------------------------------------------------
From ts@uwasa.fi Sun Jul 23 00:01:15 1995
Subject: Reading errorlevel
75. *****
Q: How can I read another program's errorlevel value in TP?
A: This question is best answered by an example. Here is a very
elementary program that returns errorlevel 14 on exiting.
program faq2;
begin
writeln ('Hello world...');
halt(14);
end.
Below is the program that calls FAQ2.EXE and detects its errorlevel.
{$M 2000,0,0}
uses Dos;
begin
SwapVectors;
Exec ('r:\faq2.exe', ''); (* Execution *)
SwapVectors;
WriteLn('...back from Exec');
if DosError <> 0 then
WriteLn('Dos error #', DosError)
else
WriteLn('Success; child process errorlevel = ', lo(DosExitCode));
end.
The output should be
Hello world...
...back from Exec
Success; child process errorlevel = 14
--------------------------------------------------------------------
From ts@uwasa.fi Sun Jul 23 00:01:16 1995
Subject: Usenet Pascal newsgroups
76. *****
Q: What are the current Pascal newsgroups on the Usenet news?
A: The following new Pascal newsgroups were created June 12, 1995
to replace the old comp.lang.pascal. The following new Delphi
newsgroups were created around July 10, 1995.
A special note about Delphi postings. Please use the new delphi
newsgroups for the Delphi related postings. In particular, don't let
the names mislead you. The newsgroup comp.lang.pascal.borland does
NOT cover Delphi.
A second special note. Please avoid crossposting between the Pascal
newsgroups. In particular do not crosspost between the old
comp.lang.pascal and the new Pascal newsgroups. It is slows the
transition to the new system. (This automatic posting breaches the
non-crossposting tenet only because it is relevant information about
the arrangements of all the Pascal newsgroups.)
NEW:
comp.lang.pascal.ansi-iso Pascal according to ANSI and ISO standards.
comp.lang.pascal.borland Borland's Pascal incl. Turbo Pascal (not Delphi!)
comp.lang.pascal.mac Macintosh based Pascals.
comp.lang.pascal.misc Pascal in general and ungrouped Pascals.
comp.lang.pascal.delphi.databases Database aspects of Borland Delphi.
comp.lang.pascal.delphi.components Writing components in Borland Delphi.
comp.lang.pascal.delphi.misc General issues with Borland Delphi.
RELATED of potential interest:
comp.os.msdos.programmer.turbovision Borland's text application libraries
OLD:
comp.lang.pascal Discussion about Pascal. (Please cease using!)
For more information about the new Pascal newsgroups please see
52703 Jun 14 21:37 ftp://garbo.uwasa.fi/pc/doc-net/pasgroup.zip
pasgroup.zip Information about the comp.lang.pascal.* newsgroups
18086 Jul 11 08:18 ftp://garbo.uwasa.fi/pc/doc-net/delphi.zip
delphi.zip Vote results of the comp.lang.pascal.delphi.* newsgroups
If your site is not getting the new Pascal newsgroups, please
contact your own site's newsmaster about the situation.
--------------------------------------------------------------------
From ts@uwasa.fi Sun Jul 23 00:01:17 1995
Subject: Capslock status and toggling
77. *****
Q: How do I detect the CapsLock status, how do I turn it on/off?
A: Here are the relevant Turbo Pascal routines in answer to these
questions.
{}
Uses Dos; { The Dos unit is needed }
{}
(* Is CapsLock on *)
function CAPSONFN : boolean;
var regs : registers;
KeyStatus : byte;
begin
FillChar (regs, SizeOf(regs), 0);
regs.ax := $0200; { Get shift flags }
Intr ($16, regs); { The keyboard interrupt }
KeyStatus := regs.al; { AL = shift status bits }
if (KeyStatus and $40) > 0 then { bit 6 }
capsonfn := true
else
capsonfn := false;
end; (* capsonfn *)
{}
(* Set CapsLock. Use true to turn on, false to turn off *)
procedure CAPS (TurnOn : boolean);
var keyboardStatus : byte absolute $0040:$0017;
regs : registers;
begin
if TurnOn then
keyboardStatus := keyboardStatus or $40
else
keyboardStatus := keyboardStatus and $BF;
{ Interrrupt "check for keystroke" to ensure the LED status }
FillChar (regs, SizeOf(regs), 0);
regs.ah := $01;
Intr ($16, regs);
end; (* caps *)
{}
As you see, CapsLock is indicated by bit 6. The other toggles can be
handled in an equivalent way using this information about the memory
location Mem[$0040:$0017]:
ScrollLock = bit 4 $10 $EF
NumLock = bit 5 $20 $DF
CapsLock = bit 6 $40 $BF
--------------------------------------------------------------------
From ts@uwasa.fi Sun Jul 23 00:01:18 1995
Subject: Detecting F11 and F12
78. *****
Q: How do I detect if the F11 or F12 key has been pressed?
A: Here is a sample program
uses Dos;
(* Enhanced keyboard ReadKey, no Crt unit needed. Detects also F11
and F12, and distinguishes between the numeric keypad and the
gray keys. Lower part of the word returns the first scan code,
the higher part the second *)
function RDENKEFN : word;
var regs : registers;
keyboard : byte absolute $40:$96;
begin
rdenkefn := 0;
if ((keyboard shr 4) and 1) = 0 then exit;
FillChar (regs, SizeOf(regs), 0);
regs.ah := $10;
Intr ($16, regs);
rdenkefn := regs.ax;
end; (* rdenkefn *)
{}
procedure TEST;
var key : word;
begin
while Lo(key) <> 27 do { esc exits }
begin
key := RDENKEFN;
if (Lo(key) = 0) and (Hi(key) = 133) then
writeln ('F11 was pressed');
if (Lo(key) = 0) and (Hi(key) = 134) then
writeln ('F12 was pressed');
end;
end;
{}
begin TEST; end.
--------------------------------------------------------------------
From ts@uwasa.fi Sun Jul 23 00:01:19 1995
Subject: Substrings from a string
79. *****
Q: How do I extract (parse) substrings from an input string?
A: Carefully study these two routines which I have included in
19593 Jun 1 12:12 ftp://garbo.uwasa.fi/pc/research/simirr10.zip
simirr10.zip Deriving IRR from ARR: A Simulation Testbench, TS+IV
They use space (and anything in ascii below it) as the separator.
Change the while tests if you wish to have a different set of
separators.
(* Number of substrings in a string *)
function PARSENFN (sj : string) : integer;
var i, n, p : integer;
begin
p := Length(sj);
n := 0;
i := 1;
repeat
while (sj[i] <= #32) and (i <= p) do Inc(i);
if i > p then begin parsenfn := n; exit; end;
while (sj[i] > #32) and (i <= p) do Inc(i);
Inc(n);
if i > p then begin parsenfn := n; exit; end;
until false;
end; (* parsenfn *)
{}
(* Get substrings from a string *)
function PARSERFN (sj : string; PartNumber : integer) : string;
var i, j, n, p : integer;
stash : string;
begin
if (PartNumber < 1) or (PartNumber > PARSENFN(sj)) then
begin PARSERFN := ''; exit; end;
p := Length(sj);
n := 0;
i := 1;
repeat
while (sj[i] <= #32) and (i <= p) do Inc(i);
Inc(n);
if n = PartNumber then
begin
j := 0;
while (sj[i] > #32) and (i <= p) do
begin
Inc(j);
stash[0] := chr(j);
stash[j] := sj[i];
Inc(i);
end;
PARSERFN := stash;
exit;
end
else
while (sj[i] > #32) and (i <= p) do Inc(i);
until false;
end; (* parserfn *)
{}
{... A separate, but useful function from the same package ...}
(* Delete trailing white spaces etc rubble from a string *)
function TRAILFN (sj : string) : string;
var i : byte;
begin
i := Length (sj);
while (i > 0) and (sj[i] <= #32) do i := i - 1;
sj[0] := chr(i); trailfn := sj;
end; (* trailfn *)
{}
{... Another separate, but useful function from the same package ...}
(* Delete leading white spaces etc subble from a string *)
function LEADFN (sj : string) : string;
var i, p : byte;
begin
p := Length (sj); i := 1;
while (i <= p) and (sj[i] <= #32) do i := i + 1;
leadfn := Copy (sj, i, p-i+1);
end; (* leadfn *)
--------------------------------------------------------------------
From ts@uwasa.fi Sun Jul 23 00:01:20 1995
Subject: Size of a file
80. *****
Q: How do I find out the size of any kind of a file?
A: Well, to begin with the FileSize keyword and an example code are
given in the manual (and help function of later TP versions) so
those, as usual, are the first places to look at. But the example
solution can be somewhat improved, and there is also an alternative
solution. The FSIZEFN should never be applied on an open file.
function FSIZEFN (filename : string) : longint;
var fle : file of byte; { declare as a file of byte }
fmSave : byte;
begin
fmSave := FileMode; { save the current filemode }
FileMode := 0; { to handle also read-only files }
assign (fle, filename);
{$I-} reset (fle); {$I+} { to do your own error detection }
if IOResult <> 0 then begin
fsizefn := -1; FileMode := fmSave; exit;
end;
fsizefn := FileSize(fle);
close (fle);
FileMode := fmSave; { restore the original filemode }
end; (* fsizefn *)
The second, general alternative is
uses Dos;
function FSIZE2FN (FileName : string) : longint;
var FileInfo : SearchRec; { SearchRec is declared in the Dos unit }
begin
fsize2fn := -1; { return -1 if anything goes wrong }
FindFirst (filename, AnyFile, FileInfo);
if DosError <> 0 then exit;
if (FileInfo.Attr and VolumeId = 0) and
(FileInfo.Attr and Directory = 0) then
fsize2fn := FileInfo.Size;
end; (* fsize2fn *)
--------------------------------------------------------------------
From ts@uwasa.fi Sun Jul 23 00:01:21 1995
Subject: Formatting graphics output
81. *****
Q: How do I format graphics output like in textmode writeln?
A: In the graphics mode the positioned text output procedure is
OutTextXY (X ,Y : integer; TextString : string); It does not have
the same output formatting capabilities as the write procedure. It
only accepts the one TextString. Therefore all the output formatting
must be done previously on the string. The Str procedure has such
capabilities. The example below gives the rudiments.
uses Crt, Graph;
var grDriver : integer;
grMode : integer;
ErrCode : integer;
s, s1 : string;
v1 : real;
begin
grDriver := Detect;
InitGraph (grDriver, grMode, ' ');
ErrCode := GraphResult;
if ErrCode <> grOk then begin
Writeln ('Graphics error:', GraphErrorMsg(ErrCode)); halt; end;
ClearDevice;
{}
{ Writing text in the graphics mode }
{ Set the drawing color }
SetColor (Yellow);
{ Set the current background color }
SetBkColor (Black);
{ Set style for text output in graphics mode }
SetTextStyle (DefaultFont, HorizDir, 2);
{ Preprocess the text }
v1 := 2.345;
Str (v1 : 10:2, s1);
s := 'The first value is' + s1 + '.';
{ Output the text }
OutTextXY (100, 30, s);
OutTextXY (100, 50, 'Press any key');
{}
repeat until KeyPressed;
{}
RestoreCrtMode;
writeln ('That''s all folks');
CloseGraph;
end.
Besides not having the same output formatting capabilities OutTextXY
and OutText procedures do not scroll the screen. If you wish to
achieve such an effect, you will have to code it yourself step by
step. You can see the effect in
111673 Oct 8 1993 ftp://garbo.uwasa.fi/pc/ts/tsdemo16.zip
tsdemo16.zip Assorted graphics demonstrations of functions etc
Coding the scrolling is a straight-forward but a laborious task.
Hence it is beyond this FAQ. The outline, however, is that you must
keep track where on the screen you are. When you come to the bottom
of your window you have to move the above region upwards before you
output new text. You can move graphics regions using the ImageSize,
GetImage and PutImage procedures.
As for readln-type input in a graphics mode, that is a complicated
issue. You will have to build the input routine reading a character
at a time with ReadKey. The rudiments of using ReadKey are shown in
the first question of FAQPAS.TXT. The demo, referred to a few lines
back, will show the effect.
--------------------------------------------------------------------