From ts@uwasa.fi Sun Jun 12 00:00:00 1994
Subject: FAQPAS3.TXT contents

                            Copyright (c) 1993, 1994 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 rather post your
questions to a suitable UseNet newsgroup like comp.lang.pascal. 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 comp.archives.msdos.announce
Moderating at garbo.uwasa.fi anonymous FTP  archives  128.214.87.1
Faculty of Accounting & Industrial Management; University of Vaasa
Internet: ts@uwasa.fi   BBS +(358)-61-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?
-------------------------------------------------------------------

From ts@uwasa.fi Sun Jun 12 00:01:01 1994
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 Jun 12 00:01:02 1994
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 MsDos copy
command using the Exec routine. (See the item "How do I execute an
MsDos command from within a TP program?"
------------------------------------------------------------------

From ts@uwasa.fi Sun Jun 12 00:01:03 1994
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 Jun 12 00:01:04 1994
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 Jun 12 00:01:05 1994
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 garbo.uwasa.fi:/pc/programming/inter41a.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.
------------------------------------------------------------------

From ts@uwasa.fi Sun Jun 12 00:01:06 1994
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 Jun 12 00:01:07 1994
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 garbo.uwasa.fi:/pc/ts/tspa33??.zip.
------------------------------------------------------------------

From ts@uwasa.fi Sun Jun 12 00:01:08 1994
Subject: TP program memory requirement

68. *****

 Q: How much memory will my TP program require?

 A: Get MAPMEM.EXE from 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.
------------------------------------------------------------------
