//////// // // // // // /// // // // // //// // // //////// // // // // // // //// // // // /// // // // // /////// Pascal NewsLetter Issue #4 July, 1990 Editor: Pete Davis The Programmer's Forum BBS is the home of PNL. It can be reached in Washington, DC at (202)966-3647. Information is available through the following locations: FidoNet: Pete Davis@1:109/138 Genie: P.DAVIS5 BitNet: HJ647C@GWUVM & UE356C@GWUVM InterNet: HJ647C@GWUVM.GWU.EDU or UE356C@GWUVM.GWU.EDU or Pete.Davis@f138.n109.z1.fidonet.org Table of Contents Introduction .................................. 3 (P.D.) Letters to the Editor ......................... 5 (****) Binary Trees For Sorting ...................... 7 (R.M.) Putting the Spurs to Pascal ................... 15 (J.R.) For Beginners ................................. 22 (B.G.) Review of TechnoJock's Turbo Toolkit .......... 27 (J.A.) Review of Turbo Pascal-The Complete Reference . 29 (P.D.) Key to author names: P.D. -> Pete Davis - Senior editor and writer B.G. -> Bob Gowans - Staff writer J.R. -> Jan-Erik Rosinowski - Contributing writer R.M. -> Robert Mashlan - Contributing writer J.A. -> John A. Abele - Contributing writer 3 Introduction Well, PNL003 finally got off, so now I guess it's about time to get started on issue #4. I was so pleased with PNL003 that I was worried I might break my arm patting myself on the back. It was a good issue, though, and I was very pleased. I hope issue 4, and all the issues to follow for that matter, are just as good or better. I received a wonderful letter the other day from a reader who said he got a subscription to the Cobb Group newsletter on Turbo Pascal. He said that he and a friend compared it to PNL and said that they preferred mine. It's this kind of support that really makes me want to keep writing this. I hope that people will keep up the contributions. They've been a fantastic support for me. Without them, I don't know if I'd be able to go on with this. There have already been a lot sent that I couldn't have written myself and I think that makes it more interesting for the regular readers. Because of the time consuming nature of writing the NewsLetter, I'm afraid I might be stuck with only putting out one issue every two months or so. I doubt it would really be much longer than that. I am looking into getting some more permanent writers who I can count on to write an article for every issue and maybe even pitch in on the editing. Right now, Bob Gowans is the only person who has had the time to be able to contribute on a regular basis. PNL is still young, though, and growth can be expected, but it might slow down a bit. Since the first issue, the PNL has grown very quickly, but things seem to be steadying out a bit. Well, in this issue we've got an article on Binary Trees, a very powerful data structure, which once understood, can be very easy to implement. I had already planned on doing an article on Binary Trees, but got a submission from a reader on them. Since Robert's article covers the basics of Binary trees, I decided I would take a bit more complex route and discuss the AVL tree balancing algorithm in a future issue. I've also had a reader, John Abele, who sent in a terrific review of the TechnoJock's Turbo Toolkit. I am doing a review of Turbo Pascal - The Complete Reference by Stephen K. O'Brien. These will be only the start of reviews that will be done on books, toolkits, and software related to Pascal. I think the star of this issue is going to be the PROFILER and accompanying article that are in this issue. I've used to profiler a bit myself and have found it very useful. I hope you like it. Originally the article was written in German and thanks to the tremendous effort of one of my BBS users, Howard Sanner, I 4 was able to get the article translated to English. Howard suggested, and I agreed, that it was best to leave the article using some of the expressions that are distinctly German, in the article, instead of trying to write something in English that wouldn't convey the meaning the author was trying to get across. I hope this poses no problems. All-in-all, I think this issue came out very well, and again, let me thank all those who contributed and hopefully I can get other readers to contribute also. 5 Letters to the Editor From uunet!GWUVM.BITNET!HJ647C From: uunet!CUNYVM.CUNY.EDU!CDCKAB%EMUVM1.BITNET (Karl Brendel) To: Pete Davis Date: Fri, 27 Jul 1990 13:04 EDT Pete-- I've just read the second issue of PNL (the first one I've seen). Thanks for this work! Not wanting to be critical of article--being critical is so much easier than writing--but Craig Thurber's references to the $L compiler directive is completely wrong, to the point that you really need to issue a correction in your next issue. Reference _Turbo Pascal Reference Guide_ for 5.0 or 5.5, pp 434-5 and 437-8 in my copy. {$L filename} is the "link object file" directive. {$L- or {$L+ are the "local symbol information" directives. Craig indicates that {$L filename} is the local symbol information directive and that it is ignored if debug information is off ({$D-}) (PNL002, pg 17). He shows an external file linked as {$L+ HELLO.OBJ}. This will not work. It appears that Craig doesn't understand how to use the link directive. It appears to me that neither he nor you tried to compile or use his code. As printed, the program "Hello" will neither compile nor run. One of the compile errors is from a typo (a problem every publication has), but the other is from Craig's misuse of {$L+. I urge you to require your writers to compile and run every single line of code they send you, and to repeat the compilation and running yourself before publication. Best wishes. I'm looking forward to PNL003. Karl Brendel Centers for Disease Control Epidemiology Program Office Atlanta, GA Karl, You bring up a valid point. To be honest, not all of the code that goes into the articles themselves has really been checked by me to see if it works. I do read through the articles and try to get an idea of the competency of the writer and see if the ideas are valid. I must admit that from time to time things will slip by, but from now on, actual code going into the articles will be tested. It appears Craig had made a small mistake and I thank you for pointing it out to the readers. I am afraid that this will 6 probably happen again, at some point and hope that in the future readers will respond, as you have to make the corrections that slip by us here at PNL. _Pete Davis Editor 7 Binary Trees for Sorting By Robert Mashlan A binary tree is a useful sorting device in structured programming. It is a quick and easy way to maintain data in a sorted order. A binary tree is composed of units called 'nodes'. These nodes are linked together in a special way to form a tree. A tree requires the use of pointers, and each node of the tree must be allocated dynamically. If you don't quite understand the concept of a pointer, don't worry, just read on. A pointer is nothing more than a variable that holds the address of another variable. Pointers are used to access dynamic variables, that is, variables that you can create or destroy at will. Besides possessing this flexibility, you may hold more data with dynamic variables than you could by using global and/or local variables. In a program, the construction of pointers is straightforward. In the type declaration of a program consider the declaration Type stringptr = ^string; or in English, 'Let stringptr denote that all things of this type point to things of type string!'. In the declaration there is a caret '^' which simply means 'pointer to'. It takes on a slightly different meaning when used in the actual code. Now we need to declare an instance of a pointer so that it can be used: Var sp : stringptr; What we have done is to declare an instance of a variable that points to a string. This variable doesn't take much space; pointer in Turbo Pascal it is 4 bytes. Now we need to allocate storage for a string on the heap: begin New(sp); What we have done here is told a set of routines called the 'Heap Manager' that we need some space for a string. If there are 255 bytes in a row, the Heap manager complies and sets the value of sp to the address of the space that it gave us for the string. This space will be protected from being given to other 8 requests for space until the space is reclaimed with dispose. Now that we have allocated space for it, we can now use it. The caret '^' comes into play again as it is needed to dereference a pointer. This means 'refer to the thing I point at'. sp^ := 'I am a dynamic string!'; writeln(sp^); Now when we no longer need the string, we can get rid of it, possibly reusing the space that it occupied. This is done with the dispose function: dispose(sp); end. After using dispose, the value that sp had is now no longer valid. That means don't read it, don't change it, don't use it for anything unless you reallocate space for it with the new procedure. A pointer with an invalid value that is being used often causes really mean bugs. It may write to almost anywhere in memory, like the next instruction to be executed, the IDE, the operating system, etc. The problem is that these kind of problems may not show up until much later, such as when you exit your program and run another. You should also try not to lose track of your pointers, or you'll get a 'memory leak' that wastes space that could be used for other purposes. Whenever a pointer goes out of scope always dispose the space it points at. A pointer can also have the special value nil. This simply means that the pointer points at nothing. It can be assigned to any pointer. Ok, now on to the forest: First a graphical demonstration of creating a binary tree: Say we have these pieces of data and we want to sort them in alphabetical order: G, B, A, H, I, E, J, F, C, D The first letter G, we will denote as the root of our tree: G / \ The node that G is in points to other nodes, which are currently pointing at nothing. 9 Now we add the next letter B. To add it, we use the algorithm: 1) Start at the root node 2) if the node is empty then place the data here else if the data is greater than the data at the node then go to the right node else go to the left node go to step 2 By using this algorithm, we now have G / \ B ( Unused datum: A, H, I, E, J, F, C, D ) The next step would be: G / \ B / A to G / \ B H / \ A I to G / \ B H / \ \ A E I to G / \ B H / \ \ A E I \ J 10 to G / \ B H / \ \ A E I \ \ F J to G / \ B H / \ \ A E I \ \ F J to G / \ B H / \ \ A E I / \ \ C F J to G / \ B H / \ \ A E I / \ \ C F J \ D Now we have a strange looking upside down tree that seems to have no reason to it. To use this tree, we must transverse it. This is the name of a special algorithm that we use to 'travel' through the tree so that it comes out in sorted order. The traversal algorithm: 1) start at the root node 2) do procedure 2 3) end 4) if node is not empty then do this procedure with the left node process the data at this node do this procedure for the right node 11 Using this algorithm with the tree we start at the root node. The instructions tell us to go left, so we go to B. Next we go to A. Since there is no left node to A, we process A, or in this case, write it down. We now return to B and print it. The instructions say go right, so we end up at E. Now we go left to the C node and print it. Mentally traversing the rest of this tree is left as an exercise to the reader. The traversing algorithm is called recursive, that is, the algorithm calls itself. If you have a good mental picture of what a tree is, read on for a demonstration of implementing it in Pascal. ---------------------- Program TreeSort; Const MaxStr = 255; { The maximum length of strings to be used } Type Str = string[MaxStr]; { Our string type to be used } { We now declare a node record and pointer type: } nodeptr = ^node; node = record data : str; left, right : nodeptr; end; { record } { The node record type has three fields. The first field data, is used to store a string, the things we are to sort. The left and right fields contain pointers to other nodes. When these nodes aren't pointing at other nodes, they will be assigned the value nil. } { Next we must have a root to our tree: } Var root : nodeptr; { Root is not actually the root, but a pointer that will point to the root node of our tree. } InData : str; { This variable will be used by the main block of this program } 12 Function NewNode( var newdata : str ) : nodeptr; { The purpose of this function is to allocate and and initialize space for a node. The newdata parameter is the string that will be placed into the Node's data field. newdata is passed by reference to reduce stack usage. } var result : nodeptr; { the value to be returned } begin New(result); { allocate space for the new node } with result^ do begin left := nil; { set these pointers to point at nothing } right := nil; data := newdata; { store the data in the node } end; {with} NewNode := result; end; {Function NewNode} Procedure Add( var newdata : str ); { This procedure is an implementation of the 'word' algorithm used to add data to the graphical tree. The string that is passed as a parameter will be stored in the tree. } var chase : nodeptr; { this pointer is used for } { moving through the tree } done : boolean; { the done flag } Function Compare( var a,b : str ) : boolean; { This nested function tells the outer procedure the order that the data is to be sorted. It is to return TRUE is a is to go to the left of b. In this case we are comparing strings in alphabetical order with no regard to case } var i, limit : integer; begin if length(a) < length(b) then { limit is to be the maximum} limit := length(a) { number of times to loop, } else { In this case, the length } limit := length(b); { of the shortest string. } i := 1; while ( i <= limit ) and ( Upcase(a[i]) = UpCase(b[i]) ) do i := succ(i); { Increment i } { at this point either a mismatch occured or the strings 13 Matched up to the length of the shortest string } if UpCase(a[i])=UpCase(b[i]) then Compare := length(a) > length(b) { longer strings go after shorter ones } else Compare := UpCase(a[i]) > UpCase(b[i]); end; {Function Add.Compare} begin if root = nil then root := NewNode(newdata) { gotta start somewhere } else begin done := false; chase := root; { start at root } repeat with chase^ do begin if Compare( data, NewData ) then {go to the left} if left = nil then begin {put the data here} left := NewNode(newdata); done := true; end else chase := left { move on to the left node } else { go to the right } if right = nil then begin { put the data here } right := NewNode(newdata); done := true; end else chase := right; { move on to the right node } end; {with} until done; end; {else root=nil} end; {Procedure Add} Procedure Transverse( np : nodeptr ); { This is the transversal procedure to the tree. It calls the nested function Process in sorted order. Note that this procedure is recursive. } Procedure Process; begin writeln(Output, np^.data);{write data to standard output} end; {Procedure Transverse.Process} begin 14 if np <> nil then with np^ do begin Transverse(left); Process; Transverse(right); end; {with} end; {Procedure Transverse} {Pretty short, huh?} begin {Program TreeSort} { This program is a filter, that is, it takes input from standard input, sorts it, and send it to standard output. It works like the sort filter you get with MS-DOS } root := nil; { initialize Root } while( not eof(Input) ) do { read all the input datum } begin readln(Input,InData); Add(InData); end; Transverse(root); { transverse the tree and write out the datum } end. {Program TreeSort} ---------------------- To use this program, create an ascii text file of strings to sort. Type in the command: >treesort < data.dat and the sorted output will be displayed. Binary trees are useful for many sorting applications, and are fairly quick. There are a few disadvantages to binary trees, namely the recursive nature of the transversal algorithm. In a case where the incoming data is nearly or already sorted. The tree will appear to look like a linked list, or a very long chain. This will cause the transversal algorithm to call itself many times and possibly cause a stack overflow error. That's it. If you have in questions or comments, you may ask them in the FidoNet International Pascal Echo or via FidoNet NetMail to Robert Mashlan @ 1:147/34.33 15 PUTTING THE SPURS TO PASCAL by Jan-Erik Rosinowski Does it un-nerve you when, with increasing development time, your program runs progressively slower? Editors that cannot keep up with the cursor? Databases that seem to be stored on cassette tape? Though the complexity of the problem can cause bewildering frustration, it is constructive to use a profiler at this point. It will expose the time-consuming procedures, so that nothing will stand in the way of subsequent significant optimization of the program. Such a profiler for Turbo Pascal v. 4 and 5 programs is described below. There are two approaches to program optimization, the theoretical and the practical. They will both be briefly sketched. PROGRAM OPTIMIZATION IN THEORY AND PRACTICE The theorist uses the following method: using a simply canonical approach, he searches for the theoretically lower limits of the best algorithm. The time to formulate and verify an algorithm is limited only by the author's maximum writing speed--at the latest, after a midnight snack of pizza he is in the know. For him, the implementation of the algorithm is almost secondary: This would require him to write a small program, and in such a manner could many free hours and years pass! In contrast, the graceless practitioner: He writes his database program using a BASIC interpreter. Before thinking two minutes about the program, he has written 100 lines. After a hundred test runs that help him in the "verification" of his program, he has a bug free version. He often thinks the following: The program's done. Only he can no longer coordinate the long coffee break his computer's run time has forced upon him. After a compiler doesn't help him, he decides to re-write the program in assembler. For quick access to his data, he once again uses a linear, sequential search. AND HOW ONE DOES IT RIGHT Though both examples were exaggerated, it is clear that neither approach is workable. Clever optimization of a program comes after a careful analysis of the problem and costs. Then one must next analyze the kernel functions of the planned program. In a database program, these would be, for example, functions to insert, update, delete, search, and index data records. Along with the expected quantity and format of data and operations, one 16 also selects appropriate data structures. Thus one chooses here suitable algorithms, for example, linear searching, B-trees, or hashing. An estimation of the expected overall complexity, the provision of resources (time, money, tools), and taste/experience will quickly determine the high or low-level programming language to be used. After solving the general complexity of the problem, one should, in the first, analytical phase, in no case underestimate the time taken up by operating system functions. In many programs they claim a large part of the total run time. Bypassing DOS and BIOS calls will definitely yield a copious increase in speed, at the cost of many gray hairs when porting the program to another system. Routines with extreme requirements, for example, a screen handling package for a text program, will need to be purchased. AND THEN! After choosing appropriate algorithms, data structures, and programming languages, places ripe for optimization can be quickly identified. There are, of course, many sloppily-written small routines that are called unexpectedly often. If these are optimized first, relatively small optimizations will yield a big improvement in run time. Routines should be re-written in assembler or, for example, a bubble sort replaced with a quicksort. The amount of improvement to be gained from using a profiler is inversely proportional to the amount of thought put into the original design. In other words, the greater effort put into a statement, analysis, and evaluation of the problem, the fewer the opportunities for improvement. The true art of efficient programming thus lies in appropriate decisions in the analysis and coding phase. PRACTICE With the profiler's help, you can quickly do away with the worst time wasters in a Turbo Pascal program. The program package consists of a preprocessor (PROFILER.PAS) as well as the analysis unit, PROFILE.PAS. The work of the preprocessor is to imbed a call to "PBegin" and PEnd" at the beginning and end of each function, respectively. (See listings 4 and 5.) When the program runs, these procedures capture timing statistics. Both the format and use of upper and lower case in the source code are unimportant. Include files and source code of available units are both processed. In addition, the Pascal ugliness of "Record," "Case," and "End" (in contrast to Modula-2 or single logic only 17 "End") is digested without complaint. The programs were developed under Turbo Pascal 5.0. Only changes to a few CONST declarations and case selectors are needed to adapt it to the previous version. The assembler part, PROTIMER.ASM, was assembled with MASM. WILLY GO! The preprocessor "PROFILER" runs only from the DOS command line. The first parameter expected is that of the name of the module to be analyzed. This can be either one unit or the whole program. Modules linked in with "uses" or include directives are each processed. If one wants to omit a module (e.g., one that exclusively reads the keyboard via the BIOS), one can name the file to be excluded after the /X: option. Names without extension are interpreted as ".PAS," and this applies to all source files (thus units to be excluded with "/X: XYZ.TPU" will cause an error). In order not to inflate the profiler's size, it assumes that the program to be processed has no syntax errors. This isn't really a restriction, since the profiler is intended to be used to put the finishing touches on the program. The profiler will also not be of help to people who, as already mentioned above, plan their programs in the coding phase. The module to be profiled must always have either a "Program" or "Unit" identifier. THE BIG RENAMING So that one can't work with source code the profiler has processed (the time checking calls will not be found in the finished program), the files to be processed are renamed. Thus the first and last letters of the extension are exchanged (".PAS" becomes ".SAP"). This can nevertheless lead to problems should a program be run through the profiler again, as well as with files like, for example, "INCLUDE.010." The latter must be renamed to something reasonable. The PAS-SAP collision is best avoided with a batch file that will copy the identically named ".SAP" files over the ".PAS" files. The latter may then be deleted. INTERPRETING THE RUN TIME PROFILE After one has compiled the profiler-processed program and tested it adequately, the unit PROFILE will generate the run time analysis [PROGRAM/UNIT NAME].PRF from the file [PROGRAM/UNIT 18 NAME].PR$ produced by the profiler. In the first column is the procedure name, in the second the number of calls, and in the third column the run time in milliseconds. In contrast to the notion of run time in reference (1), here it means only the run time of the procedure itself. It does not include that of the subprocedures it calls. This considerably simplifies usage, so that one can directly see the true time a procedure consumes (figure 1). By means of the DOS SORT program one can generate a list by run time, so that one can immediately find the slow parts of a large program. The definition of run time in reference (1), in contrast to that given here, always shows 100% for the main program's run time, which is true in few cases. A marginal note: Anyone who tests the entire program being analyzed with the profiler and gets unexpected results must remember that the time the profiling unit itself takes can be compensated for by setting the variable "adjusttimer" to a more precise value. The true run time of the program is small compared to that of the stopped. Should one wish to test the program isolated in diverse situations, one can delete the uninteresting procedures from the ".PR$" file with a text editor. (Delete whole lines.) This will greatly reduce the confusion of paper, since the profile unit will only emit a run time profile for procedures in the ".PR$" file. Should the program terminate before the "End." statement, there will nevertheless be a run time analysis. In this case the profiler will issue a corresponding warning. This can happen, for example, if the profiler is used to analyze itself, since the main program terminates with a halt statement. The run time of the "open" procedure will be approximate. One small piece of bad news: External procedures (in assembler or C, for example) cannot be directly analyzed. If this is desired, one must enclose them in an outer procedure. This presupposes changes to the external procedures (public names); an extension of the profiler in this regard would be excessively complex. INTERNALS The profiler consists of two kernel procedures: one, the recursive function "prep_module" that inserts the calls to the profiler. It is recursive, since it calls itself in analyzing uses and include modules. This serves to simplify the file management, since thus Turbo Pascal gets stuck with the dirty 19 work. The second important procedure, "getsymbol," supplies "prep_module" with tokens and handles include modules. That the rest of allocated storage isn't released should not trouble you; DOS also takes no notice of it, since the management is handled internally by Turbo Pascal. The most important function of the unit PROFILE is "readtimer." It makes possible accurate timing at microsecond intervals; it thus has substantially greater resolution than the BIOS or DOS timers. In spite of what the manuals say, the DOS timer cannot resolve time finer than five milliseconds. The more precise results of the "readtimer" procedure are also not surprising, when we consider the fact that the BIOS timer, which is also used by DOS, is incremented whenever the timer used in the "readtimer" procedure counts down to zero. Timer zero of the 8253 chip acts as time keeper. It is critical in this connection to coordinate with the 8259 interrupt controller; under the worst circumstances it can falsify the measurements "catastrophically" (by five milliseconds). This can happen if the timer has been latched and an interrupt is triggered between the time the counter value is stored and the interrupt request register is read. An extremely low counter value indicates that request flag has been set. Since this is nevertheless influenced by the timer- independent CPU frequency, an adjustment of the constant "corr" in PROTIMER.ASM is needed. Also, the DRAM refresh and installed cache programs (DOS buffers, FASTOPEN, VCache, etc.) falsify the measurement not inconsiderably. For these reasons it is important to keep the timer resolution under 1/100000 seconds and not to put the unit PROFILE into an overlay. The constant "fracs" in PROFILE.PAS determines the number of decimal digits to which the run time in milliseconds will be displayed. The unit PROFILE will normally be the first unit entered once the program is started and the last left (via an Exitproc). The counter is installed, and the correction value needed to account for calls to "PBegin" and "Pend" calculated, via calls to the initialization blocks. The local stack as well as the storage space "procstore" are statically declared, and the constants "stacksize" and "maxprocedures" must be adjusted. Dynamic memory allocation in the profiler might cause errors, since the program to be analyzed could also manage its storage by means of MARK/RELEASE or also DISPOSE. Listing 5 is presented as an example of capturing timing information. Time measurement begins with PBegin (1). Since nothing is on the stack yet, PBegin stores only the number of the active procedure (1) as well as the system time. The call to PBegin (2) in the procedure "a" causes the time used in procedure 1 (stored time-elapsed time- correction value) to be saved in "procedure[1].time." Again the number of the active procedure (2) and the system time are pushed on the stack. PEnd performs the analog: it closes off the active procedure and then pops its 20 record off the stack. Since the system time is once again read, the second call to PEnd can determine the run time of the main program correctly. LIMITATIONS OF USE Use of the profiler can lead to problems in the following circumstances: 1. As mentioned above, extra steps are needed to analyze procedures written in other languages. They must be given different public names so that one can write enclosing procedures with their original names. If only a few modules are involved, it may be quicker to change the names in the Pascal source. 2. Programs or PCs that themselves make use of timer zero can cause minor irritations and influence the unit PROFILE, respectively. In this case, the procedure READTIMER (PROTIMER.ASM) must be changed. 3. The run time analysis is only procedural. The time taken by DOS and run time library calls must also be accounted for. One would be well advised, for example, to redefine the file functions in their own unit. "READ" and "WRITE" statements unfortunately often evade this method. In individual, truly critical cases, one can extend the ".PR$" file with a pseudo-procedure after the last line. It will then carry all the PBegin and PEnd calls associated with the "READ" and "WRITE" statements. 4. Time critical procedures, as, for example, a serial port handler, will take longer to run, and thus the profiler may interfere with their operation. For this reason, interrupt procedures are automatically excluded from analysis. One must either delete the PBegin and PEnd statements from time critical procedures or put them in units or include modules that are excluded with the /X directive from analysis. 5. "Inline" procedures cannot be analyzed with any tricks. This corresponds to their character. The shortest machine programs execute in a fraction of a microsecond. Anyone who would like to analyze such a time critical section should perhaps 21 consider coding it completely in assembler. With these hurdles crossed, nothing stands in the way of analyzing your critical source code. One should, however, always put readability and correctness of a program before its speed, otherwise one can program the same as in C or BASIC. References 1. Profilgewinn, c't 5/89. 2. Interrupts auf Reihe, 5/86. 3. Die PC-Referenz Figures 1. PROTEST.PRF: This is the result of the analysis of PROTEST with the unit PROFILE. Listings 1. PROFILER.PAS: This program links calls into Turbo Pascal 4/5 compatible source code that make possible an analysis accurate to the microsecond. 2. PROFILE.PAS: This unit measures the procedure run times and generates a run time profile from them. 3. PROTIMER.ASM: This procedure can read the timer with microsecond precision. 4. PROTEST.SAP: The program to be analyzed appeared thus before the application of the profiler. 5. PROTEST.PAS: The profiler has here performed all of its work. 22 For Beginners By Bob Gowans In the last issue of PNL the beginners column discussed the use of the Pascal assignment operator ( := ) to assign values to variables. We were concerned mainly with operations on the variable data type integer, a simple Pascal data type, and were made aware of other Pascal data types such as char, Boolean and real. In this issue we will further increase our knowledge of variable operations by designing a simple program and implementing our design as a Pascal program. In particular we will be paying close attention to correct initialization of variables and input and output. Later you will be presented with a simple problem and from this we will devise a design to solve this problem but first lets discuss the technique by which we may solve the problem. First we must make sure that we fully understand the problem. How?... By asking relevant questions so that we are made fully aware of all the details of the problem. For example, if we were asked to update a catalogue of books we might ask How many books are there? Are they to be ordered? How are they to be ordered, by title or by author? You can probably think of many more questions to ask until you are fully satisfied that you thoroughly understand the problem. Next we would start to devise a solution keeping in mind that our ultimate aim is to implement the solution to the problem by making use of a computer. Devising an efficient solution when using a computer is something that comes about as you gain experience of your computer system's capabilities, its advantages and disadvantages. One of the best methods of problem solving is by a process known as TOP-DOWN DESIGN. This involves simplifying the problem into a series of broad terms and then adding more detail as you look at each term. For example, the solution to the problem of how to update a bank account is given as follows: (a) Give a broad outline 1. read in transactions 2. bring the accounts up to date 3. print out a list of transactions (b) Start to refine the broad outline 1.1 read in credits 1.2 read in debits 2.1 calculate the total credits 2.2 calculate the total debits 2.3 calculate the new balance 3.1 print out credits 3.2 print out debits 23 3.3 print out new balance Each step would then be further refined until we are at a position to implement the solution. Implementing the solution would result in the construction of a Pascal program from our refined design. When the program is entered into the computer and successfully executed then the associated problem is solved. Whenever a solution is to be implemented there are three important stages to consider : 1) Data input or reading in information to the program. 2) Processing instructions - the main body of the program. 3) Results or output. For example, to the printer or screen or some other output device. After having completed the implementation of your design successfully, always go back over what you have done, ask yourself if you managed to solve the problem to your satisfaction and does the solution work for all cases that could arise? Finally it is vital that you make a written record of what you have done. Say for example several months later, you want to amend the program. If you have clear, concise documentation covering all aspects of your design and solution then the process of amendment should be made much less painful. Now lets take a simple problem and using the above, process solutions ready for implementation in Pascal. Problem 1 : We are going back to the bank again. This time the bank manager has asked us to devise a program that will assist his customers. He wants an interactive program that will prompt the user for the balance in his deposit account and then print out, to the computer screen, the new balance with interest at the end of the first interest period. First lets ask some questions : 1) What is the current rate of interest? 2) Over what period is the interest calculated? 3) How is the new balance to be calculated? 4) What will happen if there is a withdrawal from the account? Ask as many questions as you like until you are fully satisfied that you understand the problem. For the purpose of this problem the bank manager supplies you with the following answers : 1) Interest is paid annually at 13% 2) The period of interest is quarterly ie 3 months 3) The new balance is the current deposit plus the accumulated interest. 24 4) The account is new and no withdrawals are allowed We are now in a position to construct a broad design of the intended program. 1. set values of variables ( or initialize variables ) 2. calculate the new balance at the end of the quarter 3. write out the result The broad outline is easy and understandable and although this is a much simplified case the same principles apply when dealing with more complex design. Let us continue by refining the broad outline to a more specific list of instructions. 1.1 initialize interest rate 1.2 initialize interest period 1.3 initialize deposit 2.1 calculate interest 2.2 set balance to deposit + interest 3.1 write out 'New balance is ', balance The design is now at a stage where it could be implemented as a Pascal program, however there is nothing to stop you from further refining the design if you think it necessary. At this point it would be beneficial to stop and reflect over our design for a moment or two. In our design there are three statements asking us to initialize certain data, if a data table is constructed we can make clear the data structures and what we intend to do with them. Identifier Description Type ========== =========== ==== deposit amount of money in account real variable rate interest rate per year real variable interest calculated interest real variable quarter period over which the interest is calculated real variable balance total money in the account after first quarter real variable The design has introduced a new data type REAL. A real data type is defined as 'positive and negative numbers that include decimal points'. Our real variables must be initialized, or given a starting value before they can appear in an expression in the program. It is true to say that many Pascal systems will automatically initialize variables to zero, however it is not good programming practice to presume this and it is much better if you ensure that all variables used in the program are properly initialized. Using the data table we can now write the declarations part of the program ( the part where all the variables are declared ). 25 var deposit : real; rate : real; quarter : real; interest : real; balance : real; Note this could also be written as var deposit,rate,quarter,interest,balance : real; Initializing the variables in Pascal is as follows, using the statements from lines 1.1, 1.2 and 1.3 of our design rate := 0.13; { 13% as a decimal fraction } quarter := 0.25; { 1/4 of a year expressed as a decimal } readln(deposit); The first two lines make use of the Pascal assignment operator ( := ) which we discussed in the last issue of PNL. The readln statement is new to us and merits a brief discussion. The use of the readln statement results in assigning a value to a variable from an external source ie from outside the program. As our program specified an interactive design we expect the user to input some information, a passing of information must take place between the user and the computer. The program will temporarily halt when it encounters the readln statement, expecting some data to be input. It is vital that interactive programs communicate while they run so that the user will know when he is required to input the data. The way to do this is to use a program prompt. It is also important that interactive prompts ask make clear their purpose. Lets add an interactive prompt to our program ensuring it comes before the readln statement. write('Please enter your deposit here > '); Steps 2.1 and 2.2 of our design are fairly straight forward and are implemented as follows : interest := deposit*rate*quarter; balance := deposit + interest; Finally step1 requires the output to the screen for the information of the user. writeln('The new balance is ',balance:10:2); The writeln statement informs the user of his balance plus interest and provides a neat format. The :10 after the variable balance allows a field width of 10 characters for the output and the :2 ensures the output is rounded to two decimal places. Try 26 the program without these output controls and note the results. The whole program can now be put together; program new_balance; { calculates balance on deposit at a rate of 13 % over 3 months } var deposit : real; { entered by user } rate : real; { current rate of interest} quarter : real; { a period of three months } interest : real; { calculated interest on deposit } balance : real; { the new balance } begin rate := 0.13; quarter := 0.25; write('Please enter your deposit here > '); readln(deposit); interest := deposit*rate*quarter; balance := deposit + interest; writeln('New balance is ',balance:10:2) end. In conclusion top down design is a method for designing a solution to a problem that is going to be implemented on a computer. If you look at the design, it is understandable to a non programming person as it conveys a broad outline of the problem. With further levels of refinement and a data table you are ready to implement your design. Show your final coded program to the same non programming person and the chances are that he will have no idea what it is about. By designing your programs before coding them, you will have a much more clear understanding of exactly what your program does and will find that coding soon becomes routine and effortless. Error : In PNL # 3 the following line should be inserted in the program add_up after male := 100; :- total := male + female; I hope that this did not distract from the understanding of the program. 27 TechnoJock's Turbo Toolkit A Review by John A. Abele Those of us who are serious/professional programmers need to know about those rare individuals who have become programmer's programmers. These folks write the toolkits and other utilities that save the rest of us from a lot of tedious "grunt work" in our programming. TechnoJock's Turbo Toolkit is a well done series of 12 Turbo Pascal units that is worthy of your consideration. It is published by TechnoJock Software, Inc. of Houston, Texas. Briefly, the 12 units are as follows: 1) fast screen updating, 2) windows and boxes, 3) user input training, 4) directory display and retrieval, 5) keyboard manipulation and reading, 6) dynamic list management (this one is a real winner), 7) menu display, 8) nested menuing, 9) pull down (Lotus-like) menuing, 10) data type reading (like reading only integers from the keyboard), 11) string manipulating (some good functions found here), and 12) things that don't fit anywhere else such as date manipulating, file information, on-screen clock and some printer testers. You'll find some razzle-dazzle procedures in this toolkit, such as sliding part of a screen in from the left or windows that appear to explode from the center. What I appreciate about this toolkit is its overall concern with what the screen looks like. We are all aware that some of the best programs never get used because the screen is sloppy or un-inviting to the user. The programmer who uses this toolkit is constantly encouraged to make a nice looking screen. Definitely "home-brewed", these units bear the stamp of experience: one programmer knowing what another programmer is likely to need. This shows itself in the intuitive names given to procedures and functions. A procedure named "TempMessageBox" is fairly self-explanatory as to what it is likely to do. The Toolkit sells for $49.95 and includes full source code to all the units, a printed manual and a diskette full of short demo programs showing how to use each of the units. I have, on several occasions, written to Bob Ainsbury, the so-called "janitor" of TechnoJock Software, Inc., with questions or problems about the units. In EVERY CASE I have received either a personal letter explaining how to modify the source code to do what I want, or, a diskette with a fixed bug. This support alone is well worth the price of registration. In addition, there 28 are no royalty payments for using his units in compiled programs you distribute and he gives his customers price breaks on upgrades, etc. Are there any minuses? Yes, I've found two things that may stand in your way when using this toolkit. One, a minor complaint, is that the manual could be cleaned up. There are several inconsistent references to procedure calls and places where some information is missing. These errors are not serious enough to prohibit your effectively using the manual, but they may slow you down at times. A second and more serious consideration is getting used to how one uses all the functions and procedures in the toolkit. Fortunately, the disk comes with an ample supply of programs which demo how to use the various units. But be prepared to spend some time looking at these programs and studying the code in them. Doing so at the beginning will save frustration later. You probably won't hit the ground running with this toolkit. Beginner and advanced programmers who are looking for a workhorse toolbox that can do a lot and get the job done should consider TechnoJock's Turbo Toolkit. 29 Turbo Pascal - The Complete Reference A Review By Pete Davis I picked this book as my first to review because I feel that this book is invaluable for any Turbo Pascal Programmer. The book would be useful for everyone from beginner to the veteran Turbo Pascal Programmer. Stephen K. O'Brien, the Author is obviously very comfortable with Turbo Pascal and his book is official enough to have been published by Borland-Osbourne/McGraw-Hill. My copy is actually for Version 4 but it has been re- released for Version 5.5 of Turbo Pascal, which for the most part is almost identical in content with the addition of coverage of some 5.5 features. The first seven chapters are devoted mostly to the beginner, covering everything from basic data structures to program structure to the Turbo Pascal environment. Chapter eight then begins on more complex ideas like pointers and dynamic memory. The explanations of ideas are very clear and to the point and the most useful feature is the volume of examples included in the text. Almost every idea covered includes examples which drive home the point of every section. My personal favorite is chapter eighteen : 'Optimizing Turbo Pascal Programs', in which the author provides incredible insight into how to make you programs run faster and use less memory. This chapter alone nearly pays for the book, especially when you are involved in large projects where speed and code size are very important. Another nice feature is the Procedure and Function reference near the end of the book. This and other reference areas in the book make the Turbo Pascal Reference Guide, which comes with Turbo Pascal, almost worthless. (I shouldn't really say that, as the reference guide is quite useful, but many of the more important aspects are covered in The Complete Reference). I give Stephen K. O'Brien five stars for a fantastic book. If you have been using Turbo Pascal for a long time, or you are just learning to use it, I suggest that you grab a copy and read through it. It is by far the most useful book on Turbo Pascal that I have ever seen. _Pete Davis Editor 30 Conclusion Well, I would first like to thank everyone for being so patient in waiting for the release of this issue of the Pascal NewsLetter. I apologize for the delay, but there were a lot of things that held this issue up. Some of them, many of you already know about. Between my vacation and some computer problems, there was about a two week delay. When I was just getting ready to start finishing up the issue, I got a the Profiler article and program which I just had to include in this issue. I think you'll agree that it was worth the wait. Getting the article translated to English was the last thing holding this issue up, but it was worth the wait for me. I would like to thank everyone who contributed to this issue, in particular, I would like to thank Bob Gowans for his continued support and work in the For Beginners column. I would like to thank Jan-Erik Rosinowski for a fantastic program. His contributions are welcome here anytime. I would also like to thank Howard Sanner for putting in a lot of time in translating the article for me. I couldn't have done it without him. I have a few surprises that I hope I can spring in the next issue, but I can't be sure of whether or not things will go through by then. If not, I still plan on having a good issue out. Please send in your contributions. They're what keeps this newsletter coming out. Even if you don't write well, send in the best you can do, and if you're uncomfortable with it, I'll be happy to go through and edit it. Also, please keep the comments and suggestions coming. They're a great help. Until the next issue, happy computing. _Pete Davis 31 The Pascal NewsLetter is Copyrighted by Pete Davis. Articles submitted by others are the property of the authors and are used with permission. They may not be treated separately from this newsletter without the author's permission and thus maintain all distribution rules of the newsletter as a whole. It may be freely distributed in un-modified form, but no charge whatsoever may be incurred on the recipient. All code is provided 'as-is' with no guarantees whatsoever. The Pascal NewsLetter can be obtained from the following locations: GEnie : General Electric Network for Information Exchange. It is located in the IBMPC filelist. Simtel : Internet address: 26.2.0.74 or Simtel20.ARPA It is located in the directory. Programmer's Forum : This is the home of the PNL. Each issue can be found in the MAG directory from the main area. The number is on the title page. If you would like to receive back issues of PNL directly from me, send a diskette and $2.00 for shipping. Don't forget to include your address. Send your order to: Peter Davis 4851 Brandywine St. NW Washington, DC 20016 If you are a SysOp that will regularly carry PNL and would like to have your bulletin board listed as such, here, send me a message either by postal mail or at one of the electronic addresses given on the title page, with your bulletin board's name, phone number, and your name. 32 Distribution List The following is the phone numbers to bulletin boards known to carry PNL. If you would like your bulletin board's name and number added to or deleted from this list, please send me a message at one of my many addresses. I can not guarantee whether a listed board will have any particular issue, however. The Programmer's Forum ................. Phone: (202) 966-3647 The Programmer's Forum is the home of the PNL. The Bored .............................. Phone: (512) 303-0471 Classic City ........................... Phone: (404) 548-0726 Theive's World ......................... Phone: (713) 463-8053 Hippocampus ............................ Phone: (203) 484-4621 Rogers State College BBS ............... Phone: (918) 341-8982 The Foxtrot BBS ........................ Phone: (914) 567-1814 Turbo City BBS ......................... Phone: (209) 599-7435 Austin IEMUG/MIDI BBS .................. Phone: (512) 528-0626 Laser Publishers ....................... Phone: (918) 438-2749 Fargo RBBS-PC .......................... Phone: (701) 293-5973 Momentary Lapse of Reason .............. Phone: (704) 327-6361 The Demon's Den ........................ Phone: (508) 433-2702 The Cannibal Cafe ...................... Phone: (508) 840-6589 IBM Tech FIDO .......................... Phone: (508) 433-6491 The User Friendly BBS .................. Phone: (704) 323-8223 The Disseminator BBS (Australia)........ Phone: 61-7-368-1239 Beyound Frontiers BBS (Denmark)......... Phone: (+45)8694-1609 Collision Theory ....................... Phone: (703) 503-9441 Ed-Net 9600 ............................ Phone: (604) 732-8877