💾 Archived View for gemini.spam.works › mirrors › textfiles › internet › FAQ › faqpas3.txt captured on 2022-03-01 at 15:51:48.
-=-=-=-=-=-=-
From ts@uwasa.fi Thu Mar 7 00:00:00 1996 Subject: FAQPAS3.TXT contents Copyright (c) 1993-1996 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 -------------------------------------------------------------------- 51) I am running out of memory when compiling my large program. 52) How do I avoid scrolling in the last column of the last row? 53) How can one hide (or unhide) a directory using a TP program? 54) How do I test whether a file is already open in a TP program? 55) How can I test and convert a numerical string into a real? 56) How can I reverse a TP .EXE or .TPU back into source code? 57) How can I calculate the difference between two points of time? 58) Is a program running stand-alone or from within the IDE? 59) Please explain Turbo Pascal memory addressing to me. 60) How do I obtain a bit or bits from a byte, a word or a longint? 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? -------------------------------------------------------------------- From ts@uwasa.fi Thu Mar 7 00:00:51 1996 Subject: Out of memory in compiling 51. ***** Q: I am running out of memory when compiling my large program. What can I do? A: If you are compiling your program from within the IDE (the Integrated Development Environment) then select the Option from the main menu, choose the Compiler item and set the Link buffer to Disk. (Also make the Compile option Destination to be Disk). If this is not sufficient, next resort to using the TPC command line version of the Turbo Pascal compiler instead of the IDE. Use the "Link buffer on disk" option. Divide your program into units. It is advisable anyway for modularity when your program size grows. If you have extended memory, instead of TURBO.EXE use TPX.EXE, if you have TP 7.0. If you are into protected mode programming then use Borland Pascal BP 7.0. A2: If you would prefer compiling your program from within the IDE but cannot do it for the above reason (or if you would prefer to compile your program from within your favorite editor instead of the TP IDE) you can use the following trick. If your editor has a macro language like most good editors do, the assign a hotkey macro that compiles the current file with the TPC. If you are using SemWare's QEdit editor you'll find such macros in ("Macros and configurations for QEdit text-editor") ftp://garbo.uwasa.fi/pc/ts/tsqed17.zip and in ftp://garbo.uwasa.fi/ts/tstse16.zip ("SAL macro sources to extend The SemWare Editor"). Also your editor must be swapped to disk during the compilation if memory is critical. There is a very good program for doing that: ftp://garbo.uwasa.fi/pc/sysutil/shrom24b.zip ("Shell Room, Swap to disk when shelling to application"). For example I invoke the QEdit editor with using the following batch: c:\tools\shroom -s r:\cmand -z 1024 c:\qedit\q %1 %2 %3 %4 %5 %6 %7 You'll find more about the switches in the Shell Room documentation. The -s switch designates the swap destination (my r:\cmand directory is on my ramdisk). The -z switch sets the shell environment size. An unfortunate part is that the TP 5.0 Turbo Pascal IDE is about the only program I know that is not amenable the to Shell Room utility, so you cannot utilize Shell Room to swap the TP IDE to disk. Blessfully, at least TP 7.0 no more has this problem. -------------------------------------------------------------------- From ts@uwasa.fi Thu Mar 7 00:00:52 1996 Subject: Last position write woes 52. ***** Q: How do I avoid scrolling in the last column of the last row? A: If you use write or writeln at the last column of the last row (usually 80,25) the screen will scroll. If you wish to avoid the scrolling you'll have to use an alternative write that does not move the cursor. Here is a procedure to write without moving the cursor uses Dos; procedure WriteChar (Character : char; fgColor, bgColor : byte); var r : registers; begin FillChar (r, SizeOf(r), 0); r.ah := $09; r.al := ord(Character); r.bl := (bgColor shl 4) or fgColor; r.cx := 1; { Number of repeats } Intr ($10, r); end; (* writechar *) Thus, if you wish to write to the last column of the last row, you must first move the cursor to that position. That can be done in alternative ways. One might get there by having written previously on the screen (with writeln and write routines) until one is in that position. Another alternative is using GoToXY(80,20), but then you have to use the Crt unit. If you don't want to use it, then you can move the cursor by employing "GOATXY As the ordinary GoToXY but no Crt unit required" from ftp://garbo.uwasa.fi/pc/ts/tspa3470.zip. There is an alternative interrupt service ($0A) which does the same as service $09, but uses the default colors instead. Just substitute $0A for $09, and leave the r.bl assignment out of the WriteChar routine. Another option for writing anyhere on the screen without affecting the cursor is using direct screen writes: uses Dos; procedure WriteChar (c : char; x, y : byte; fg, bg : byte); var vidstart : word; regs : registers; begin FillChar (regs, SizeOf(regs), 0); regs.ah := $0F; Intr ($10, regs); { Color or MonoChrome video adapter } if regs.al = 7 then vidstart := $B000 else vidstart := $B800; mem[vidstart:((y-1)*80+x-1)*2] := ord(c); mem[vidstart:((y-1)*80+x-1)*2+1] := (bg shl 4) or fg; end; To write to the last position simply apply e.g. WriteChar ('X', 80, 25, 14, 0); { Yellow on black } The foreground (fg) and the background (bg) color codes are Black = 0 Blue = 1 Green = 2 Cyan = 3 Red = 4 Magenta = 5 Brown = 6 LightGray = 7 DarkGray = 8 LightBlue = 9 LightGreen = 10 LightCyan = 11 LightRed = 12 LightMagenta = 13 Yellow = 14 White = 15 Blink = 128 Yet another option is the following, but it needs the Crt unit. On the other hand, it uses the default color. This is quite a good and easy solution. I captured this fairly frequent idea from a posting by Robert Buergi (nbuero@hslrswi.hasler.ascom.ch). uses Crt; procedure WriteToCorner (c : char); begin Inc (WindMax); GotoXY (80, 25); write (c); Dec (WindMax); end; (* writeToCorner *) -------------------------------------------------------------------- From ts@uwasa.fi Thu Mar 7 00:00:53 1996 Subject: Hiding a directory 53. ***** Q: How can one hide (or unhide) a directory using a TP program? A: Here is the code using interrupt programming. Incidentally, since MS-DOS 5.0 the attrib command can be used to hide and unhide directories. (* Hide a directory. Before using it would be prudent to check that the directory exists, and that it is a directory. With a contribution from Jan Nielsen jak@hdc.hha.dk Based on information from Duncan (1986), p. 410 *) procedure HIDE (dirname : string); var regs : registers; begin FillChar (regs, SizeOf(regs), 0); { standard precaution } dirname := dirname + #0; { requires ASCIIZ strings } regs.ah := $43; { function } regs.al := $01; { subfunction } regs.ds := Seg(dirname[1]); { point to the name } regs.dx := Ofs(dirname[1]); regs.cx := 2; { set bit 1 on } { to unhide set regs.cx := 0 } Intr ($21, regs); { call the interrupt } if regs.Flags and FCarry <> 0 then { were we successful } writeln ('Failed to hide'); end; (* hide *) A2: An alternative method by Dr. Abimbola Olowofoyeku laa12@seq1.keele.ac.uk. No paths. procedure HIDE (dirname : string); var FileInfo : searchRec; f : file; begin FindFirst (dirname, Directory, FileInfo); while DosError = 0 do begin assign (f, FileInfo.Name); SetFAttr (f, Hidden); FindNext (FileInfo); end; end; (* hide *) {} procedure UNHIDE (dirname : string); var FileInfo : searchRec; f : file; begin FindFirst (dirname, AnyFile, FileInfo); while DosError = 0 do begin assign (f, FileInfo.Name); SetFAttr (f, Archive); FindNext (FileInfo); end; end; (* unhide *) -------------------------------------------------------------------- From ts@uwasa.fi Thu Mar 7 00:00:54 1996 Subject: Testing file opened status 54. ***** Q: How do I test whether a file is already open in a TP program? A: This question is best answered by providing the code: uses Dos; {... for non-text files ...} function ISFOPEN (var filePointer : file) : boolean; begin isfopen := FileRec(filePointer).mode <> FmClosed; end; {} {... for text files ...} function ISTOPEN (var filePointer : text) : boolean; begin istopen := TextRec(filePointer).mode <> FmClosed; end; {} procedure TEST; { Testing a non-text file } const name = 'R:\TMP'; var f : file; begin Assign (f, name); writeln ('File ', name, ' is open is ', ISFOPEN(f)); {$I-} rewrite (f); {$I+} if IOResult <> 0 then begin writeln ('Failed to open ', name); exit; end; writeln ('File ', name, ' is open is ', ISFOPEN(f)); close(f); writeln ('File ', name, ' is open is ', ISFOPEN(f)); end; -------------------------------------------------------------------- From ts@uwasa.fi Thu Mar 7 00:00:55 1996 Subject: From string to real 55. ***** Q: How can I test and convert a numerical string into a real? A1: An easy task in Turbo Pascal but in standard Pascal this frequent task is much trickier. Here are both the Turbo Pascal and Standard Pascal versions for general edification :-). (* Convert and test a numerical string with Turbo Pascal *) function DIGVALFN (mj : string; var ok : boolean) : real; var k : integer; x : real; begin Val (mj, x, k); ok := k = 0; if ok then digvalfn := x else digvalfn := 0; end; (* digvalfn *) {} (* Convert and test a numerical string with standard Pascal routines only *) procedure DIGVAL (mj : string; var number : real; var ok : boolean); label 1; var il, lenl, pl, kl1, kl2 : integer; nrol : boolean; numberdl : real; begin ok := true; lenl := Length (mj); nrol := false; pl := 0; number := 0.0; if lenl = 0 then ok := false; for il:=2 to lenl do if (mj[il]='-') or (mj[il]='+') then ok := false; for il:=1 to lenl do case mj[il] of '0'..'9','+','-','.' : ; else ok := false; end; for il:=1 to lenl do case mj[il] of '0'..'9' : begin nrol := true; goto 1; end; end; 1: if nrol = false then ok := false; for il:=1 to lenl do if mj[il] = '.' then pl := pl + 1; if pl > 1 then ok := false; kl1:=1; kl2:=lenl+1; if (mj[1]='-') or (mj[1]='+') then kl1 := 2; for il:=1 to lenl do if mj[il] = '.' then kl2 := il; if kl2-kl1 > 38 then ok := false; if ok then begin number:=0; numberdl:=0; for il:=kl1 to kl2-1 do number := (ord(mj[il])-48)+10*number; if kl2 < lenl+1 then for il:=lenl downto kl2+1 do numberdl := (ord(mj[il])-48)/10+numberdl/10; number := number + numberdl; if mj[1]='-' then number := -number; end; {if ok} end; (* digval *) {} procedure TEST; var s : string; r : real; ok : boolean; begin s := '123.41'; r := DIGVALFN (s, ok); if ok then writeln (r) else writeln ('Error in ', s); DIGVAL (s, r, ok); if ok then writeln (r) else writeln ('Error in ', s); end; A2: The conversion can be in the other directorion as well. Here is how to convert an integer into a string with standard Pascal routines only. function CHRIVLFN (number : integer) : string; var il, pl, al : integer; cl, mj : string; isNeg : boolean; begin if number < 0 then begin isNeg := true; number := -number; end else isNeg := false; pl := 0; mj := ''; cl := ''; repeat pl := pl + 1; al := number mod 10; cl := cl + chr(al+48); number := number div 10; until number = 0; if isNeg then begin pl := pl + 1; cl[pl] := '-'; end; for il := 1 to pl do mj := mj + cl[pl+1-il]; chrivlfn := mj; end; (* chrivlfn *) {} procedure TEST; var s : string; j : integer; begin j := 12341; s := CHRIVLFN (j); writeln (s); end; -------------------------------------------------------------------- From ts@uwasa.fi Thu Mar 7 00:00:56 1996 Subject: Decompiling a TP .EXE 56. ***** Q: How can I reverse a TP .EXE or .TPU back into source code? A: This is simply asking too much. You cannot decompile a TP program in a manner that would give you back the original source. This method of reverse engineering is not on in actual practice. Quoting Jeroen Pluimers (jeroenp@dragons.nest.nl) "During the compilation, important information get's lost about variables, types, identifiers etc. Writing a Pascal Decompiler is impossible. The best you can achieve is a disassembler that can help you recognize some Pascal statements." You might note that this question somewhat resembles another frequent question "How can I convert a TPU unit of one TP version to another?" which cannot be solved without the original source code. -------------------------------------------------------------------- From ts@uwasa.fi Thu Mar 7 00:00:57 1996 Subject: Calculating date/time differences 57. ***** Q: How can I calculate the difference between two points of time? A: This is an unconfirmed answer so be a little careful with it. But at the very least it shows some interesting information about Turbo Pascal date/time conventions and how to declare and initialize typed constants if they are records. program TimDifTest; uses Dos; const a : DateTime = (year:1992; month:10; day:24; hour:5; min:29; sec:38); b : DateTime = (year:1993; month:11; day:23; hour:6; min:30; sec:51); var aLong, bLong, cLong : longint; c : DateTime; begin PackTime (a, aLong); PackTime (b, bLong); cLong := bLong - aLong; UnpackTime (cLong, c); writeln (c.year-1980, ' ', c.month, ' ', c.day, ' ', c.hour, ' ', c.min, ' ', c.sec); end. More generally than for dates between 1980 and 2079, or for more accurate results, the difference between two date/times can be calculated using Zeller's congruence (see the item "I want code that gives the weekday of the given date"). First calculate Zeller's for both the dates, convert them, and the hour, min, and sec into seconds, subtract, and convert back. -------------------------------------------------------------------- From ts@uwasa.fi Thu Mar 7 00:00:58 1996 Subject: Stand-alone or from IDE 58. ***** Q: Is a program running stand-alone or from within the IDE? A: Not all questions have an answer yet. I posed this question to the the late UseNet newsgroup comp.lang.pascal, but we have not found an answer that would be general for all MS-DOS versions. The closest we have comes from dmurdoch@mast.queensu.ca Duncan Murdoch (naturally :-). I have done some slight editing of Duncan's solution. uses Dos; type Pchar = ^Char; function Asciiz2Str (p : Pchar) : string; var result : string; len : byte; begin len := 0; while (p^ <> #0) and (len < 255) do begin inc(len); result[len] := p^; inc(longint(p)); end; result[0] := chr(len); Asciiz2Str := result; end; {} var parentSeg : ^word; p : pchar; begin if swap(DosVersion) < $0400 then writeln ('Requires Dos 4.0+') else begin parentSeg := ptr (prefixSeg, $16); p := ptr (ParentSeg^-1, 8); writeln ('I was launched by ', Asciiz2Str(p)); end; end. Another suggestion has been that the contents of ParamStr(0) would show the launching program. I tested this on several configurations and TP versions and found no evidence that the contention would hold. -------------------------------------------------------------------- From ts@uwasa.fi Thu Mar 7 00:00:59 1996 Subject: Memory Addressing 59. ***** Q: Please explain Turbo Pascal memory addressing to me. A: This is far from an easy question, but let's see what we can do. The origins of the difficulties are in the design of the 8086 chip which still restricts all Turbo Pascal applications (which contrary to Borland Pascal use the original real mode). The 8086 (aka real mode) addressing is based on 16-bit registers. As you probably know 2^16 is 65536 which means that you cannot directly point to all addresses of the lower and upper memory, which ranges from 0 to 1048575 (2^20-1). Thus all the memory addresses are pointed to into two parts in TP programs, the segment and the offset. The following example of the PC's memory illustrates. Decimal Hexa- address decimal Segment Offset What 0 $00000 $0000 $0000 Conventional memory starts 1043 $00413 $0040 $0013 Base memory in Kb, a word 655359 $9FFFF $9000 $FFFF Conventional memory ends 655360 $A0000 $A000 $0000 Upper memory begins 1048575 $FFFFF $F000 $FFFF Upper memory ends To exemplify, let's look at some alternative ways we could access the information about the amount of the base memory. It is very straight-forward, since in a PC that information is at the fixed memory location show by the above table. We know this in advance. Using direct memory accessing we could write var memsize : word; memsize := MemW [$0040:$0013]; writeln (memsize); {.. or ..} var memsize : word absolute $0040:$0013; writeln (memsize); If you are not familiar with the true meaning of pointers, they may feel confusing, but what they basically are is just what the name indicates, pointers to memory locations. Study the following example. var memsizePtr : ^word; { A pointer to a word } begin memsizePtr := ptr ($40, $13); { Assign the pointer a value } writeln (memsizePtr^); { Write what is in the address } end. { that was pointed to } This was relatively simple, since we knew in advance the location of the information. Lets look at a case where we do not know that. Consider var x : word; begin x := 1223; writeln (x); end. We have a variable x somewhere in the memory, and we can refer to it without ever needing to know where the variable actually is in the memory. But how does one find out if one for some reason wants or needs to know? var x : word; Segment : word; Offset : word; y : ^word; begin x := 1223; writeln (x); Segment := Seg(x); Offset := Ofs(x); writeln ('Variable x is at