Contributor: SWAG SUPPORT TEAM 
 ANSWERS TO FREQUENTLY ASKED PASCAL QUESTIONS
 ============================================
1...
 
 Q. How do I pass an error level code when my program finishes? 
 
 A. The halt procedure takes an optional parameter of type word. Thus - 
 
 halt(1); 
 
 terminates the program with an errorlevel of 1. If halt is used without 
 a parameter it is the same as - 
 
 halt(0); 
 
 Note: When a program is terminated using the halt procedure any exit 
 procedure that has previously been set up is executed. 
2...
 
 Q. How do I empty the keyboard buffer? 
 
 A. There are several ways that this can be achieved. However the safest 
 is - 
 
 while Keypressed do ch := ReadKey; 
 
 This requires that a variable ch of type char is declared and the crt 
 unit be used. To do it without using a variable - 
 
 while Keypressed do while ReadKey = #0 do; 
 
 or if using TP6 with extended syntax enabled - 
 
 while KeyPressed do ReadKey; 
 
 If you do not wish to incur the substantial overhead involved with the 
 use of the CRT unit and there is no requirement for the program to run 
 under a multi-tasker - 
 
 var 
 head : byte absolute 40ドル:1ドルc; 
 tail : byte absolute 40ドル:1ドルe; 
 
 tail := head; 
3...
 Q. When I redirect the screen output of my programs to a file the file is 
 empty and the output still appears on the screen. What am I doing 
 wrong? 
 
 A. You are probably using the CRT unit and its default method of writing 
 to stdout is by direct screen writes. In order to enable output to be 
 redirected all writes must be done by DOS. Setting the variable 
 DirectVideo to false has no effect on redirection as all it does is use 
 the BIOS for screen writes - not DOS. 
 
 To enable redirection you must not use the CRT unit 
 
 OR 
 
 assign(output,''); 
 rewrite(output); 
 
 This will make all output go through DOS and thus can be redirected if 
 desired. To restore the default situation - 
 
 AssignCRT(output); rewrite(output); 
4...
 Q. How do I make a string that is lower or mixed case all uppercase?
 
 A. There are several ways to convert lower case characters to upper case. 
 Here are some of them. 
 
 As a procedure (excluding asm code this is the fastest way) 
 
 procedure StrUpper(var st: string); 
 var x : byte; 
 begin 
 for x := 1 to length(st) do 
 st[x] := UpCase(st[x]); 
 end; 
 
 As a function (slower but sometimes more convenient) - 
 
 function StrUpper(st: string): string; 
 var x : byte; 
 begin 
 StrUpper[0] := st[0]; 
 for x := 1 to length(st) do 
 StrUpper[x] := UpCase(st[x]); 
 end; 
 
 Both the above are suitable for the English language . However from 
 version 4.0 onwards, DOS has had the facility to do this in a way that 
 is country (language) specific. I am indebted to Norbert Igl for the 
 basic routine. I have modified his code slightly. For the anti-goto 
 purists this is a good example of a goto that is convenient, efficient, 
 self-documenting and structured. The dos calls would make this method 
 the slowest of all. 
 
 function StrUpper(s: string): string; 
 { Country specific string-to-uppercase conversion. Requires DOS unit } 
 label 
 fail; 
 var 
 regs : registers; 
 x : byte; 
 begin 
 if lo(DosVersion)>= 4 then begin 
 with regs do begin 
 ax := 6521ドル; 
 ds := seg(s); 
 dx := ofs(s[1]); 
 cx := length(s); 
 msdos(regs); 
 if odd(flags) then { the attempted conversion failed so } 
 goto fail; 
 end; { with } 
 end { if DOS>= 4.0 } else 
 fail: 
 for x := 1 to length(s) do 
 s[x] := UpCase(s[x]); 
 StrUpper := s; 
 end; { StrUpper } 
5...
 
 Q. When I include ANSI codes in a string and write that string to the 
 screen the actual codes appear on the screen, rather than the results 
 they are supposed to achieve. 
 
 A. In order for ANSI codes to be interpreted, screen writes must be 
 directed through DOS and there must have been a suitable driver loaded 
 via the config.sys file at boot time. All output can be directed 
 through DOS and the driver by - 
 
 Not using the crt unit 
 
 OR - 
 
 assign(output,''); 
 rewrite(output); 
 
 in which case ALL screen writes are "ANSI code sensitive" 
 
 OR - 
 
 You can set up write procedures that will be "ANSI code sensitive". 
 (You will need an initialisation procedure to set this up.) 
 
 var 
 ansi : text; 
 
 procedure AssignANSI(var ansifile : text); 
 begin 
 assign(ansifile,'CON'); 
 rewrite(ansifile); 
 end; { AssignANSI } 
 
 procedure WriteANSI(var st: string); 
 begin 
 write(ansi,st) 
 end; { WriteANSI } 
 
 procedure WriteLnANSI(var st: string); 
 begin 
 writeANSI(st); 
 writeln(ansi); 
 end; { WriteANSI } 
 
 ObviousLy, if the ANSI.SYS driver (or an equivalent) is not installed 
 none of the above can work. 
 
 Setting the variable DirectVideo in the CRT unit to false will not 
 achieve the desired result as this merely turns off direct screen 
 writes and uses the BIOS for all screen output. 
6...
 
 Q. When I try to shell to DOS nothing happens. What am I doing wrong? 
 
 A. In order to be able to execute any child process there must be 
 sufficient memory available for it to load and execute. Unless you 
 advise differently at compile time, a Turbo Pascal program grabs all 
 available memory for itself when it is loaded. To reserve memory for a 
 child process use the compiler memory directive - 
 
 {$M 16384,0,0) 
 the default is - 
 {$M 16384,0,655360} 
 
 The first figure - StackMin - is the amount of memory to be allocated 
 for the stack: 
 
 Minimum is: 1024 
 Default is: 16384 
 Maximum is: 65520 
 
 The next figure - HeapMin -is the minumum amount of memory to be 
 allocated for the heap. If there is less memory available than this 
 figure the program will not load. 
 
 Minimum is: 0 
 Default is: 0 
 Maximum is: 655360 In practice it will be the amount of free 
 memory less the space required for the stack, 
 less the code space of the program. You should 
 set this to 0 unless your program uses the 
 heap. In that case, set it to the lowest 
 possible figure to prevent heap allocation 
 errors. In most cases it is best to leave it 
 at zero and do error checking within the 
 program for sufficient memory at allocation 
 time. 
 
 The last figure is the crucial on as regards child processes. It 
 should always be low enough to leave memory left over for a child 
 process and high enough not to cause problems for the program when 
 allocating heap memory. 
 
 Minimum is: HeapMin 
 Default is: 655360 
 Maximum is: 655360 If less than the requested amount is available 
 no error is reorted. Instead all available 
 memory is allocated for heap use. 
7...
 
 Q. How do I shell to DOS? 
 
 A. SwapVectors; 
 exec(GetEnv('COMSPEC',''); 
 SwapVectors; 
 
 Read previous section on memory allocation. 
 
 I find that it is a good idea to write my own Exec function which will 
 do everything that is needed for me. I have it return an integer value 
 that is the DosError code. 
 
 function Exec(p1,p2: string); 
 begin 
 SwapVectors; 
 Dos.Exec(p1,p2); 
 SwapVectors; 
 Exec := DosError; 
 end; 
 
 This enables me to have a statement such as - 
 
 ReportError(Exec(GetEnv('COMPSEC'),'')); 
 
 Now you can have an empty ReportError procedure or you can make it 
 report the error - whatever is suitable for you application. 
8...
 
 Q. When I execute a child process redirection does not work. Why? 
 
 A. Redirection of a child process's output only works if it is run under 
 another copy of the command processor. So - 
 
 exec('YourProg.exe','> nul'); will not work but 
 exec(GetEnv('COMSPEC'),'/c YourProg> nul'); will work. 
9...
 Q. How do I read an errorlevel from a child process?
 A. After executing a child process the errorlevel returned can be read
 by calling the DosExitCode function which returns a word. The low
 byte is the errorlevel. A full description is in the manual.
 If the command interpreter is the child process and it in turn
 executes a child process then the errorlevel of the second child
 process cannot be read without resorting to some trickery.
10...
 Q. When I read a text file that has lines exceeding 255 characters I
 lose all those characters from the 256th one on each time there is a
 line that exceeds that length. How can I prevent this?
 A. Turbo Pascal's readln procedure reads a line up to the 255th
 character then skips to the next line. To get around this you
 should declare a buffer at least as large as the longest possible
 line and then use the read procedure. The best size for the buffer
 is a multiple of 2048 bytes.
 const
 BufferSize = 2048;
 LineLength = 78;
 type
 textbuffer = array[1..BufferSize] of char;
 var
 st : string;
 f : text;
 buffer : textbuffer;
 function ReadTxtLn(var tf: text; var s: string; max: byte): integer;
 { Reads a string of a maximum length from a text file }
 var
 len : byte absolute s;
 begin
 len := 0;
 {$I-}
 while (len < max) and not eoln(tf) do begin inc(len); read(tf); end; if eoln(tf) then readln(tf); ReadTxtLn := IOResult; {$I+} end; { ReadTxtLn } begin assign(f,filename); reset(f); SetTextBuf(f,buffer); while not eof(f) and (ReadTxtLn(f,st,LineLength) = 0) do writeln(st); close(f); end. 11... Q. How do I convert nul terminated asciiz strings to Turbo Pascal strings? A. Here is a function that will do that - function Asc2Str(var s; max: byte): string; { Converts an ASCIIZ string to a Turbo Pascal string } { with a maximum length of max. } var starray : array[1..255] of char absolute s; len : integer; begin len := pos(#0,starray)-1; { Get the length } if (len> max) or (len < 0) then { length exceeds maximum } len := max; { so set to maximum } Asc2Str := starray; Asc2Str[0] := chr(len); { Set length } end; { Asc2Str } 12... Q. How can I tell if a particular bit of a variable is set or not? How can I set it? How can I turn it off? How can I make a large bit map and then determine if a particular bit - say bit 10000 is on/of? A. This question, or a variation of it, is one of the most commonly asked questions in the echo and there are several ways of doing what is wanted. None are necessarily right or wrong. The way I will describe is designed to take up as little code/data space as possible. I do not attempt to explain the theory behind these functions as this can be obtained from any good book. Question 16 also contains valuable extra help on the subject of truth tables. The use of sets can be the best bit manipulation method if you have control over the data being used. Here is an example of a byte variable for a BBS program which sets various user access level flags. Bit 0 = Registered User 1 = Twit 2 = Normal 3 = Extra 4 = Privileged 5 = Visiting Sysop 6 = Assistant Sysop 7 = Sysop type status_type = (Registered, Twit, Normal, Extra, Privileged, VisitingSysop, AssistantSysop, Sysop); status_level = set of status_type; var access_flags : status_level; Let us assume you have someone who logs on and you wish to determine his user access level. After reading access_flags from the user data file - if Sysop in access_flags then .... To set the sysop flag - access_flags := access_flags + [Sysop]; To reset the sysop flag - access_flags := access_flags - [Sysop]; However on many occasions using a set may not be a suitable method. You may simply need to know if bit 5 is set or not. Here is the method that I consider the best - function BitIsSet(var V, bit: byte): boolean; begin BitIsSet := odd(V shr bit); end; To set a bit - procedure SetBit(var V: byte; bit: byte); begin V := V or (1 shl bit); end; To reset a bit - procedure ResetBit(var V: byte; bit: byte); begin V := V and not(1 shl bit); end; To toggle (flip) a bit - procedure ToggleBit(var V: byte; bit: byte); begin V := V xor (1 shl bit); end; Now a bit map can be made up from an array of bytes. If stored on the heap you can test any bit up to number 524159 (zero based). Here's how. type map = array[0..maxsize] of byte; { set maxsize to number of bits div 8 -1 needed in the bit map } function BitSetInBitMap(var x; numb : longint): boolean; { Tests the numb bit in the bitmap array } var m: map absolute x; begin BitSetInBitMap := odd(m[numb shr 3] shr (numb and 7)); end; procedure SetBitInBitMap(var x; numb: word); { Sets the numb bit in the bitmap array } var m: map absolute x; begin m[numb shr 3] := m[numb shr 3] or (1 shl (numb and 7)) end; procedure ResetBitInBitMap(var x; numb : longint); { Resets the numb bit in the bitmap array } var m: map absolute x; begin m[numb shr 3] := m[numb shr 3] and not(1 shl (numb and 7)); end; procedure ToggleBitInBitMap(var x; numb : longint); { Toggles (flips) the numb bit in the bitmap array } var m: map absolute x; begin m[numb shr 3] := m[numb shr 3] xor (1 shl (numb and 7)); end; 13... Q. How can I find a particular string in any file - text or binary? A. The Boyer-Moore string search algorithm is considered to be the fastest method available. However in a rare worst-case scenario it can be slightly slower than a linear brute-force method. The following demonstration program will show how it works and could easily be modified to allow for command line paramters etc. program BMSearchDemo; type bigarray = array[0..32767] of byte; baptr = ^bigarray; BMTable = array[0..255] of byte; const KeyStr : string = 'Put whatever you want found here'; fname : string = 'f:\Filename.txt'; var Btable : BMtable; buffer : baptr; f : file; result, position : word; offset : longint; finished, Strfound : boolean; procedure MakeBMTable(var t : BMtable; var s); { Makes a Boyer-Moore search table. s = the search string t = the table } var st : BMtable absolute s; slen: byte absolute s; x : byte; begin FillChar(t,sizeof(t),slen); for x := slen downto 1 do if (t[st[x]] = slen) then t[st[x]] := slen - x end; function BMSearch(var buff,st; size : word): word; { Not quite a standard Boyer-Moore algorithm search routine } { To use: pass buff as a dereferenced pointer to the buffer} { st is the string being searched for } { size is the size of the buffer } { If st is not found, returns $ffff } var buffer : bigarray absolute buff; s : array[0..255] of byte absolute st; len : byte absolute st; s1 : string absolute st; s2 : string; count, x : word; found : boolean; begin s2[0] := chr(len); { sets the length to that of the search string } found := false; count := pred(len); while (not found) and (count < (size - len)) do begin if (buffer[count] = s[len]) then { there is a partial match } begin if buffer[count-pred(len)] = s[1] then { less partial! } begin move(buffer[count-pred(len)],s2[1],len); found := s1 = s2; { if = it is a complete match } BMSearch := count - pred(len); { will stick unless not found } end; inc(count); { bump by one char - match is irrelevant } end else inc(count,Btable[buffer[count]]); { no match so increment maximum } end; if not found then BMSearch := $ffff; end; { BMSearch } begin new(buffer); assign(f,fname); reset(f,1); offset := 0; MakeBMTable(Btable,KeyStr); repeat BlockRead(f,buffer^,sizeof(buffer^),result); position := BMSearch(buffer^,KeyStr,result); finished := (result < sizeof(buffer^)) or (position  $ffff);
 if position = $ffff then
 inc(offset,result);
 Strfound := position  $ffff;
 until finished;
 close(f);
 if Strfound then
 writeln('Found at offset ',offset)
 else
 writeln('Not found');
 end.
14...
 Q. How can I put a apostrophe in a string?
 A. Just put in extra apostrophes. If you want st to be equal to the
 string -
 The word 'quoted' is in quotes
 do this -
 st := 'The word ''quoted'' is in quotes';
 if you want the following to be written to screen -
 'This is a quoted string'
 do this -
 writeln('''This is a quoted string''');
15...
 Q. What are the best books to purchase to help me learn Turbo Pascal?
 A. There are many good books for learners. Here are a few -
 Complete Turbo Pascal - Third Edition - Jeff Duntemann
 Mastering Turbo Pascal 6 - Tom Swann
 Turbo Pascal - The Complete Reference - O'Brien.
 For advanced users there are also many good books. Here are a few
 that I have found useful - (Those marked with an asterisk are not
 purely for Turbo Pascal)
 Turbo Pascal 6 - Techniques and Utilities - Rubenking
 Turbo Pascal Internals - Tischer
 * PC System Programming for Developers - Tischer
 * Undocumented DOS - Schulman
 Any learner would be well advised to obtain a well known library
 such as Technojock's Turbo Toolkit (TTT) which is shareware and
 study the source code.
 16.
 Q. hat are "truth tables" and how do they work?
 A. Truth tables are a set of rules that are used to determine the result of
 logical operations. The logical operators are -
 NOT
 AND
 OR
 XOR.
 Here is a brief explanation of truth tables. When two values are
 logically compared by using a logical operator each bit of one value is
 directly compared to the corresponding bit in the other value and the
 same bit in the returned value is set or reset according to the
 following truth table.
 NOT AND OR XOR
 not 1 = 0 0 and 0 = 0 0 or 0 = 0 0 xor 0 = 0
 not 0 = 1 0 and 1 = 0 0 or 1 = 1 0 xor 1 = 1
 1 and 0 = 0 1 or 0 = 1 1 xor 0 = 1
 1 and 1 = 1 1 or 1 = 1 1 xor 1 = 0
 NOT reverses the bit.
 AND sets the returned bit if both compared bits are set.
 OR sets the returned bit if either of the compared bits are set.
 XOR sets the returned bit if the compared bits are not the same.
 17.
 Q. What are pointers and how can I use them? I have heard that they are
 variables that can be created and discarded as required thus saving
 memory. Is this true?
 A. A pointer is a variable that contains a memory address.
 The heap is all of that memory allocated by DOS to a program for its
 use that has not been used by the program for its code, global data or
 stack.
 Dynamic variables are variables that have had space allocated for them
 on the heap.
 Dynamic variables have no identifier (are unnamed). Because of this
 they need an associated variable that can be used to find where they
 reside in memory. Pointers are ideal for this but need some method to
 define what type of data it is that they are pointing at. Pascal
 provides this method.
 type
 Str10Ptr = ^string[10];
 { This means Str10Ptr is a pointer that points to data of type }
 { string[10]. }
 var
 S : Str10Ptr;
 In the above example S is a pointer that has been defined as pointing
 to an address in memory that will contain (or should contain) data of
 type string[10].
 However how does S get this value? How does it know where that data's
 address is supposed to be? Well until the programmer allocates memory
 for that data S's value is undefined, so it could be literally
 pointing anywhere. So it is *vital* that before we try to use it to
 use/assign data from/to that memory location we give S a memory
 address that is not being used for any other purpose at the moment and
 that is big enough to hold the data that we want to place into it - in
 this case at least 11 bytes. We do this by -
 new(S);
 Pascal has now allocated at least 11 bytes of heap and has allocated S
 with the address of the FIRST byte of that allocation.
 Ok... so far so good! How do we access that data (remembering that it
 has no name). Well we "dereference" the pointer. This is done by
 placing a carat sign immediately following the pointer's identifier.
 S^ := 'Joe Bloggs';
 This statement actually means "Place the string 'Joe Bloggs' into the
 memory address that S contains". This is referred to as "derferencing"
 the pointer S.
 To "reference" a dynamic variable we "dereference" its associated
 pointer variable. We cannot say -
 S := 'Joe Bloggs';
 because S is a pointer and that would be trying to give a pointer a
 string type value - a compiler "type mismatch" would occur. So every
 time we wish to access that dynamic variable we dereference it.
 To delete the dynamic variable once it is of no further use is just a
 matter of -
 dispose(S);
 What this statement does is release the memory previously used by S^
 and make it available to be used for other purposes by the program.
 Depending on the version of Pascal you are using it may not erase or
 alter the contents of that memory and it may not give S a new value.
 However any attempt to dereference S is an error as the integrity of
 that memory location has been lost - it may have been allocated to
 other data.
 Pointers do not *have* to point to a memory location in the heap or
 even have their value always allocated by using the New procedure. Any
 valid memory address can be assigned to them and then they can be
 dereferenced as shown above. As a simple example of this lets say you
 want to examine the contents of the 16 byte area at 40ドル:$f0 (the ICA
 area). You could - (TP specific)
 type
 ICA_Ptr = ^array[0..15] of byte;
 var
 B : byte;
 ICA : ICA_Ptr;
 ICA := ptr(40,ドル$f0);
 Now ICA points to the address specified and you can dereference it -
 B := ICA^[10];
 Hope that helps get you started into the complex world of memory
 management and manipulation using pointers. There are countless
 permutations and methods that can be used.
 18.
 Q. How do I do word wrap?
 A. The demo program WRAP.PAS in this archive demonstrates both word wrap
 and the justifying of text.
 

AltStyle によって変換されたページ (->オリジナル) /