PASCAL MAGIC (c) Copyright 1992, Jeff Napier & Another Company Tutorial File What Pascal Magic can do for you: This disk consists of three parts, Magic.Tpu, Magic.Doc and Tutorial.Txt. Magic.Tpu is an add-in for Borland's Turbo Pascal version 6.0 which will make your job as a programmer much easier and your results much more polished. Magic.Doc is a text file which explains how to use all the features of Magic.Tpu, and Magic.Tut, the file you are now reading, is a tutorial which starts at a very beginning level and quickly, but effortlessly moves into advanced programming techniques. Specifically, Magic.Tpu offers the following: * Automatic pop-up text boxes, dialog boxes and light-bar menus. * Automatic handling of text files. * Built-in mouse support for the programs you create. * Support for graphics programs to make them as easy to write as text-based programs. * Incorporation of .BGI and .CHR files directly into finished .EXE files. * Sound effects for your programs. * Simplification of many Turbo Pascal operations. * Complete color control in text and graphics modes. * Build cursor control into your programs for laptop LCD screens. * Full, documented, easy-to-understand source code is available so you can learn from and customize the Magic framework to your own applications. NOTE: This is a shareware product. You have our specific permission to copy and distribute this package as long as all files remain intact and unchanged. If you benefit from this tutorial, or if you use the Magic.Tpu unit in your programs you are expected to register. To register, send $29.95 to: Another Company P.O. Box 298 Applegate, OR 97530 USA If you would like the full source code with your registration, send $59.90. Please specify disk size. QUESTIONS: If you have questions: 503-846-7884, 9-5 Weekdays, West Coast time. (We are not always available during those hours.) THE BEST WAY TO USE THIS TUTORIAL: Copy Magic.Tpu to the directory where you keep your Pascal *.tpu files, and copy Tutorial.txt to the directory which contains your *.pas files. Start your Turbo.Exe and load in this file, (type: TURBO TUTORIAL.TXT). Since the Turbo Pascal IDE (Integrated Development Environment) allows you to work on several files at once, you can read this tutorial in one window and work on practice programs in another window. If you don't know how yet, I'll show you. If you are an experienced programmer, you might want to skip through this tutorial and concentrate only on the areas that are new to you. A COUPLE OF OTHER NOTES: 1. It is assumed that you have legally purchased Turbo Pascal version 6.0 and therefore have the four valuable printed manuals that come with the program. If not, you are advised to go spend a little money and get what you need, because there are places within this tutorial which will refer you to those books to complete your knowledge. 2. This is not the last word on Turbo Pascal. It doesn't teach everything about programming - probably not even 10 percent of what you will eventually learn, but between this tutorial and the Borland manuals, you'll get pretty good at programming, and fairly quickly. One of the best ways to learn this is to experiment frequently. Tear the example programs apart and try to interject your own ideas into them. See what you can change or improve, then run the programs you have modified and see what happens. As you well know, you can't hurt your computer by typing an incorrect line of code. 3. Turbo Pascal is not sensitive to capitalization or white space. The exact placement of indentations is not important and every programmer develops a slightly different style. In this tutorial, I have tried to capitalize letters within procedure and function names where such capitalization may make the source code easier to read. This 'convention' occurs irregularly throughout this tutorial. 4. This is version 1.0 of what I believe is a wonderful product, but then, I wrote it. Let me know what you think. I'm pretty sure I caught all the major bugs, but there may be some minor ones. Most likely the registered and/or subsequent shareware versions will be even better! 5. To use Magic.tpu, Video.tpu and Fonts.tpu will also need to be available in the same sub-directory. Also, Graph.tpu, furnished with Turbo Pascal must be in the sub-directory. HERE WE GO: Here is the simplest program you can write: begin end. Let's try it out. Press [F3]. (First finish reading this paragraph so you know how to get back.) A window pops up asking for a file name. Call it Prog1 (or any name you choose) and type the preceding two-line program. Don't forget the period after end. In Pascal, there is no need to care about capitalization, Capitals, small case or mixed-case makes no difference. Press [F2] to save your valuable work against power failure, etc. While holding the [Ctrl] key, press [F9] and the program will run. Did you blink? Since, the program doesn't actually do anything, it runs very quickly, then returns to Turbo Pascal. To switch back to the first window, press [Alt] and [1]. This next program is just like the first, it doesn't do anything, but it is also a legal program. There is one more line starting with the word Program. Some versions of Pascal require this line, but with Turbo Pascal, it is optional. program prog2; begin end. Next, we'll add something to the program so it will actually have a purpose: program prog3; begin write('Hello, World!'); end. This program only does one thing, it "write"s the text between the single quote marks to the screen. Turbo Pascal, like all computer applications is sensitive to exact spelling and punctuation. Make sure you have a colon at the end of the write('Hello, World!'); line, and that all the other pieces are in their proper places. Go ahead and run this program. Press [Alt] and [2] to get back to the other window you opened, and add the lines to make the first program into this one. Or, if you wish, you can make a block around this prog3, using the mouse or with ^KB the arrow keys ^KK, (actually there are several ways to mark a block). Then press [Alt] and [E], then [C]. This puts a copy of the marked block in the Turbo Pascal clipboard. Switch to the other window (use [Alt]-[2], or [Alt]- [W], [L]). Then press [Alt]-[E], then [P] to paste the block into the second window. Erase the previous program, then compile and run the new prog3. It happened very quickly, didn't it? Lets slow it down in prog4: program prog4; begin write('Hello, World!'); readln; end. In prog4, the line readln causes the computer to wait for the user to press the [Enter] key. Now you can take time to contemplate your "Hello, World!" line. You may notice that the screen wasn't cleared of other junk when prog4 ran. In prog5, let's clear the screen, giving the "Hello, World!" line more clarity. We'll use the procedure clrscr, which Turbo Pascal provides to clear the screen. program prog5; begin clrscr; write('Hello, World!'); readln; end. What happened? If you tried to run this program, you'll have noticed that it didn't work. Turbo Pascal does not recognize "clrscr." Did I misspell it? No. I didn't tell you that clrscr is not part of Turbo Pascal itself. It is in a library of operations for screen handling called Crt. Turbo Pascal provides 5 special libraries of routines, called units. They are Crt, System, Printer, Printer, and Dos. You can also create your own custom units, or buy special units from programmers. These 5 are special because they are grouped together in a special file called Turbo.Tpl, but usually units are separate files with the extension of .Tpu. The corrected prog5a shows how to use a unit: program prog5a; uses crt; begin clrscr; write('Hello, World!'); readln; end. As the proper prog5 indicates, to use something within a unit, simply declare the unit's name in the "uses" line, which you must type just below the "program" line, or as the first line in the program. Let's get a bit fancier, take a look at prog6. program prog6; uses crt,magic; begin sent[1] := 'Hello, World!'; GetAnyKey(5,11); end. Prog6 uses the Magic.Tpu unit. Give this program a try. Kind of prettier, isn't it? Let's talk about prog6 in detail. You'll notice that instead of writing "Hello, World!" directly to the screen, we have assigned that string of characters to a variable called "sent[1]." In the magic unit, "sent" is an array of sentences called strings. An array is a group of something, all of which are the same type and size. The "sent" array is 46 strings, each of which can contain up to 76 characters. In prog6, we have used only one of them, the first one, number [1]. When you want to specify a certain item from an array of items, you put its number in square brackets. Until sent[1] was told to equal with ":=" (called "assignment equals") "Hello, World!," it was nothing. Until prog6 assigned "Hello, World!" to it, sent[1] was equal to nothing, like this: sent[1] := ''. In fact, all the other sent[]s in prog6 are still nothing. The magic unit will use only the sent[]s which contain data. The next line, "GetAnyKey(5,11)" calls a procedure that displays the sent[]s which contain data in a bordered, shadowed box, and waits for the user to press any key or click a mouse button. The two numbers following GetAnyKey are coordinates. These refer to the position on the screen where the box is to pop up. Specifically, they mark the upper left corner of the pop-up box. The first number is the horizontal position on the screen. If it were 1, then the box would be jammed up against the left hand edge of the screen. 80 is the right-hand edge of a standard text screen since it is 80 characters wide, but you can't put the left edge of a text box there if you want to see it. The second number is, of course, the vertical coordinate, with 1 being the top of the screen and 25 being the bottom. You'll notice, that clrscr is missing. It is not needed with the magic unit, since the unit automatically clears the screen when the program starts. But, I left "Crt" in the uses statement. This is easy programming practice, because if you don't use anything from the Crt unit, Turbo Pascal does not increase the size of the compiled program. Yet, if you need something from the unit, it is at your disposal. You'll see that many of my programs "use" the Crt unit, even though they don't end up using anything in the Crt unit. Prog7 illustrates a close relative to GetAnyKey, GetYN. While GetAnyKey waits for the user to press almost anything, GetYN stalls the program until the user presses [Y] or [N], or clicks the left or right mouse button. Also, you'll note that one of the coordinates is a negative number. In the preceding paragraphs, you'll notice I didn't allow for the possibility of a negative screen coordinate, and technically there is no such thing. When the magic unit comes across a negative coordinate, it ignores the number and automatically figures out where the center of the screen is and pops up its box there! In other words, if you pass a negative number for the vertical location, the box will be between the top and bottom of the screen. If you use a negative number for the horzontal location, the box will be centered right to left on the screen. program prog7; uses crt, magic; begin sent[1] := 'Do you want to quit? (Y/N)'; sent[2] := ''; sent[3] := 'This is just a test, actually, the program'; sent[4] := 'is gonna quit whether you answer Y or N'; GetYN(-5,3); end. Let's make a slight improvement in the next program. Prog8 changes a boolean variable, declared from the magic unit. A boolean is a variable which can have only two values, true or false. This particular variable is CenterJustify. Normally, it is false (the "default" condition) but if changed to true, then the strings in the sent array will be centered within any pop-up box. Run prog8 and you'll see what I mean. program prog8; uses crt, magic; begin sent[1] := 'Do you want to quit? (Y/N)'; sent[2] := ''; sent[3] := 'This is just a test, actually, the program'; sent[4] := 'is gonna quit whether you answer Y or N'; CenterJustify := true; GetYN(-5,3); end. Prog8 is a little prettier, n'est ce pas? Prog9 will walk its talk, it won't end until the user says "yes" by pressing [Y]. It uses a repeat loop, which simply repeats everything between the words "repeat" and "until" until the condition listed after "until" is true. GetAnyKey and GetYN wait for the user to press a key. The difference is that GetYN is specific, it waits for the key pressed to be 'Y' or 'N'. Both of these procedures assign the character of the key pressed to a global character variable called U. So, when GetAnyKey or GetYN are done, u becomes what the user pressed. In the special case of GetYN, if the user pressed a small case 'y' or 'n', it is automatically converted into a capital letter. Furthermore, GetYN makes u := 'Y' if the left mouse button was clicked, and u := 'N' if the right mouse button was clicked. program prog9; uses crt, magic; begin repeat sent[1] := 'Do you want to quit? (Y/N)'; GetYN(-5,3); until u = 'Y'; end. Prog10 introduces a new magic procedure called Dialog. With this, you can pose a question, and have the user type an answer. Try it. program prog10; uses crt, magic; begin sent[1] := 'How would you answer this question?'; Dialog(-1,-1); sent[1] := 'User typed:'; sent[2] := Answer; GetAnyKey(-1,-1); end. Prog10 allows the user to type the answer, then if your program were more sophisticated that prog10, you could process that answer. The answer is contained in the global string variable Answer which GetAnyKey uses to display the result. Notice that "User typed:" is in single quotes, telling GetAnyKey to type literally that, but Answer is not in quotes, telling it to print the string represented by the variable answer, and not type "Answer". In prog10, you could make the whole operation more evident if you used some color control. Try prog11. program prog11; uses crt, magic; begin sent[1] := 'How would you answer this question?'; Dialog(-1,-1); BoxBack := White; Border := LightBlue; BoxText := Green; Shadow := Magenta; sent[1] := 'User typed:'; sent[2] := Answer; GetAnyKey(-1,-1); end. Prog11 is ugly, but you can see how to change the colors within the text box. The colors seem to be used here as some sort of variables and they are. They are pre-defined constants from the Turbo Pascal Crt unit. The Box colors, Border, and Shadow are global variables from the magic unit. The Colors could just as easily have been represented by numbers, as in the following example: program prog11a; uses crt, magic; begin sent[1] := 'How would you answer this question?'; Dialog(-1,-1); BoxBack := 15; Border := 9; BoxText := 2; Shadow := 5; sent[1] := 'User typed:'; sent[2] := Answer; GetAnyKey(-1,-1); end. Lets find out what all the colors are: Here is a slightly more complex program than any we have made so far: program prog12; uses crt, magic; begin border := white; BoxBack := black; CenterJustify := true; sent[1] := 'Type a number from 0 to 15.'; dialog(-1,-1); repeat BoxText := number; if (BoxText < 1) or (BoxText > 15) then boxtext := 15; sent[1] := 'The current color is #' + answer; sent[2] := 'Type a number from 0 to 15'; sent[3] := 'or type 16 to exit'; dialog(-1,-1); until answer = '16'; end. In detail, prog12 does this: First, the Border color and the BoxBack color are changed. CenterJustify will center text in the pop up boxes. Then a one-line dialog box appears. As soon as the user answers the question in this dialog box, a repeat loop, starts. The repeat loop changes the BoxText color to the "number" corresponding to what the user typed in the dialog box. "Number" is a function from the magic unit. It takes the Answer string (as returned by Dialog) and converts it from a string into an integer variable. That way it can be assigned to BoxText and BoxText will be able to handle it. Since the only 'seeable' colors range from 1 to 15 (#0 is black), the line starting with 'if' repairs the boxtext number if it not suitable. BoxText is a byte variable (only values it can handle are 0 through 255), but Answer is a string. Number fixes that by becoming a integer compatible with BoxText. Within the repeat loop a new box pops up containing three lines of text written in the color corresponding to the number the user typed in the dialog box. This all repeats until the user types "16". Let's make that program a little cleaner: program prog13; uses crt, magic; begin border := white; BoxBack := black; CenterJustify := true; sent[1] := 'Type a number from 0 to 15.'; dialog(-1,-1); repeat BoxText := number; if (BoxText < 1) or (BoxText > 15) then boxtext := 15; sent[1] := 'The current color is #' + answer; sent[2] := 'Type a number from 0 to 15'; sent[3] := 'or type 16 to exit'; dialog(-1,-1); until (answer = '16') or (u = #27); end. There is only one small change. Can you spot it? The second-from last line now allows the user out of the loop by typing "16" or by pressing the [Esc] key which is Ascii #27. This is because Dialog uses the global character u to build the Answer string. U always contains the character value of the last character entered, which in the case of pressing the [Esc] key would be #27. Prog14 will be our first program that does something useful in the real world. It will produce the square of an integer. I know that's not much. You paid around $1000 for your computer, but your $5 calculator can do squares, and it can even do it with floating point numbers, which this example can't. The importang thing is, once you learn this, you can build on this program. program prog14; uses crt, magic; var thing : word; begin repeat sent[1] := 'Type a number to be squared'; sent[2] := 'or press [Esc] to end program'; dialog(3,3); if u <> #27 then begin thing := number * number; str(thing,answer); sent[1] := 'The square is ' + answer; GetAnyKey(40,15); XClear; end; until (u = #27); end. Prog14 introduces two new concepts. First we have a line that contains "var." This line is found before the first begin in any procedure, or before the body of a program but after the uses clause. The lines that follow "var" are "user-declared" variables. If they are declared before the main body of the program they are "global," usable throughout the program. If they are declared within a function or procedure, they are usable only by that function or procedure. In this case, we have declared, "thing : word." on the left of the colon (notice no = sign) is any combination of letters and numbers and "_" allowed by Turbo Pascal (see manual). On the right of the colon is the type of variable it is. A word is a integer number with a positive value from 0 to approximately 64000. Other common types appointed by Turbo Pascal are integer (approx value -32000 to +32000), real (floating point number), byte (0 to 255), char (Any ASCII character), string (an array, or group of characters), pointer (indicating the starting location of a variable of any type stored in memory) and an array (a group of any of these items). Additionally, you can make up your own types, composed of one or more of these basic types in a mixture. For more information about all this, watch the Var section of the programs that follow in this tutorial, and see your Borland manuals. Furthermore, we introduced "str" which is a Borland procedure to convert a numeric variable into a string representing that number in text. This program is easy to crash. All you have to do is pick a number that's square is more than the range of a "word" type variable. (64000) There are a couple more new things here, but if you are sharp, you probably already picked them out. One is the "*" which is computer programming talk for "multiply". The other is the "<>" which means "not equal to." This program will be quite confusing, it would have been confusing to me for the first year or so that I was using Turbo Pascal, so you might want to study it carefully. Once you understand what everything does, try making changes and see if the do what you would expect. Once you get it, you're well on your way to writing custom programs. Most of prog15 is similar to the last, but this one is for converting inches to millimeters. Try it and you'll see a surprise. program prog15; uses crt, magic; var millimeters : real; begin repeat sent[1] := 'Enter a number of inches'; sent[2] := 'or press [Esc] when done'; dialog(2,2); if u <> #27 then begin millimeters := number * 25.4; str(millimeters,answer); sent[1] := 'That would be ' + answer + ' millimeters'; GetAnyKey(30,20); xclear; end; until u = #27; end. The surprise is in the way Turbo Pascal handles real variables when converted to string variables. Unless you add specific formatting commands, you'll get scientific notation. So, prog15a has a small change in the str procedure parameters, specifically, "millimeters" is followed with ":1:0." See your Borland books for more information, but basically, if the first integer is anything less than 8, then up to 8 digits will be displayed. The second integer determines how many digits after the decimal point will be considered in the string. For monetary notations, your second integer would typically be 2, for cents. program prog15a; uses crt, magic; var millimeters : real; begin repeat sent[1] := 'Enter a number of inches'; sent[2] := 'or press [Esc] when done'; dialog(2,2); if u <> #27 then begin millimeters := number * 25.4; str(millimeters:1:0,answer); sent[1] := 'That would be ' + answer + ' millimeters'; GetAnyKey(30,20); xclear; end; until u = #27; end. Program 16 converts Dupers from the country Xanopieland to United States Dollars. program prog16; uses crt, magic; var dollars : real; begin centerjustify := true; sent[1] := 'How many Dupers?'; dialog(-1,-1); dollars := number * 0.7143; {current duper conversion rate} str(dollars:1:2,answer); sent[3] := '$' + answer; sent[2] := ''; sent[1] := 'You own this much Xanopieland money:'; (* notice that sent[3] is assigned above *) sent[4] := ''; sent[5] := 'Press any key to end program...'; GetAnyKey(-1,-1); end. Prog16 introduces comments. There are two ways you can put comments into your source code that won't affect the way your program is compiled. One is to place it between curly brackets {} and the other way is to use (* and *) as the borders of comments. Comments can extend over several lines as long as the beginning is marked with { or (* and the end is marked with the matching } or *). You can even nest one type within another: {comment (* within a comment *)} No matter how many comments you put in your source code, there will be no change in the compiled program's size or run time speed. Comments can be very important for you or for others that have to figure out your programs a long time after you have created them. Prog16 also illustrates that Turbo Pascal is not at all fussy about white space. The only place white space matters is within strings. The same program can be written this way: program prog16a; uses crt, magic; var dollars : real; begin centerjustify := true; sent[1] := 'How many Dupers?'; dialog(-1,1); dollars := number * 0.7143; {current duper} {conversion rate} str(dollars:1:2,answer); sent[3] := '$' + answer; sent[2] := ''; sent[1] := 'You own this much Xanopieland money:'; (* notice that sent[3] is assigned above *) sent[4] := ''; sent[5] := 'Press any key to end program...'; GetAnyKey(-1,1); end. Program 16a is just as easy for the compiler to read, but much harder for humans to read! Let's make one more improvement in prog16. In prog16b, we'll straighten out the way Dupers can be entered. The standard dialog box is as wide as the widest sent[] and therefore the answer can be that long. What if you want to limit the user to a 4-digit answer, for instance? The following technique works well for use with filling in database forms: program prog16b; uses crt, magic; var dollars : real; begin centerjustify := true; sent[1] := 'How many Dupers?'; sent[2] := ' '; {must be at least one char} pop(-1,-1); textcolor(BoxText); textbackground(BoxBack); answer := XReadLn(38,12,4); restore; dollars := number * 0.7143; str(dollars:1:2,answer); sent[3] := '$' + answer; sent[2] := ''; sent[1] := 'You own this much Xanopieland money:'; sent[4] := ''; sent[5] := 'Press any key to end program...'; GetAnyKey(-1,-1); end. Can you spot the differences? This program does not depend on Dialog. Instead, we pop up a box with "pop," which is a simple procedure that remembers what's on the screen, then puts a box on the screen. Since pop will use only as many sent[]s as have text, sent[2] is ' ', or one blank, so it will be included in pop's box. This will give the user a line within the box on which to type the answer. Then the current text color and background color are set to the same as the colors within the box, so that XReadLn, will work in the proper colors. XReadln is like the Turbo Pascal ReadLn procedure but with three differences. XReadLn accepts mouse control (left mouse button is like pressing [Enter] and right button is like [Esc]), XReadLn requires two integers to designate the screen coordinates at which it will echo the text typed by the user, and a third integer which limits how long a string the user is allowed to type. Dialog knows when the user is done, and restores the screen and disappears, but pop doesn't know what is wanted, so stays on the screen until a specific call to Restore, which wipes it out and replaces the previous screen image. Prog17 shows many new techniques. It is the Universal Money Converter. For simplicity, only the first menu entry has a procedure installed. Run this program to see what it does, then study the source code to see how it does it. From a practical point of view, you can see that with modifications, this could be used for real money from real countries. For that matter, the same program could be modified for many purposes. How about a universal metric converter that converts lengths, weights, temperatures, etc? (NOTE: When I first roughed out this tutorial, I got to thinking about the previous sentence, and an idea was born. It is now complete and being distributed as shareware. Perhaps you've seen it, THE UNIVERSAL CONVERTER. This new program performs over 600 useful calculations and it makes extensive use of Pascal Magic!) program prog17; uses crt, magic; var dollars : real; procedure xano; begin sent[1] := 'How many Xanopieland Dupers?'; sent[2] := ' '; {must be at least one char} pop(-1,-1); textcolor(BoxText); textbackground(BoxBack); answer := XReadLn(38,12,4); restore; dollars := number * 0.7143; str(dollars:1:2,answer); sent[3] := '$' + answer; sent[2] := ''; sent[1] := 'You own this much Xanopieland money:'; sent[4] := ''; sent[5] := 'Press any key to continue...'; GetAnyKey(-1,-1); end; begin {main} centerjustify := true; sent[1] := 'Universal Money Converter'; pop(25,2); mc := 1; repeat sent[1] := 'Xanopieland'; sent[2] := 'Braze Island'; sent[3] := 'Nomania'; sent[4] := 'Grazille'; sent[5] := 'Exit to Dos'; menu(-1,-1); if u = #13 then begin if mc = 1 then xano; (* if mc = 2 then braze; if mc = 3 then nomania; if mc = 4 then Grazille; *) end; until (u = #13) and (mc = 5); CleanUp; end. The most noticeable change is the use of a sub-routine, called a "procedure." And Pascal seems kind of backward at first - the main program is the bottom-most thing, and the procedure(s) are at the top. In Pascal, it must be this way. Any procedure or function that is called from another procedure or function, or from the main program, must be above it in the source code. Actually, there are ways around this (see Forward declaration in your Borland manuals), but for now, we'll keep it simple. The procedure Xano is called only when the user selects the first menu entry. Now, let's look at the main program to see exactly what it does. You know CenterJustify. It makes all the sent[]s centered within the pop-up boxes. Then a box pops near the top of the screen with the text, "Universal Money Converter." Then we have a funny line, "mc := 1." I'll explain that one in a minute. Within the repeat loop are 5 sent[]s and a call to the magic procedure "menu". Menu is like the other pop-up boxes you are familiar with, but it is a light-bar menu. It allows the user to highlight any one of the sent[]s by moving the mouse or using the arrow keys. When the user presses [Enter], [Esc] or a mouse button, the menu box disappears. A global integer variable, "mc" is assigned the number of the sent[] that was highlighted when the menu box disappears. So if the user has highlighted Nomania (sent[3]), then pressed [Enter], then mc will equal 3. And after the menu box disappears, the next line of code will be executed, which happens to be "if mc = 1 then xano." Well, if the user selected Nomania, then mc is not 1 and the procedure xano will not be run. mc = 3, but the line, "if mc = 3 then nomania" is commented out, so nothing will occur. However, if the user had selected Xanopieland, then mc would be 1 and the procedure xano would be executed. And you know what that does, it is the same as prog16. If the user should select sent[5] then mc will be 5 and the until clause will then allow the program to end. The last procedure in the program is CleanUp which restores everything to the way it was before the program started. It resets the screen colors, clears the screen and the program terminates gracefully. CleanUp should be used at the end of every program. It has a very specific purpose here. Remember the one-line pop at the beginning of the program? It held a picture of what's under that box in memory and is still holding it until calling CleanUp. We never "Restore"d the first "pop". Now about that line "mc := 1." Without this line, when menu is first started, it doesn't know which sent[] to highlight. You must tell it. If you forget to assign a value to mc before calling a menu, then a random item will be highlighted. Notice that "mc := 1" is before the repeat loop. This is because, once the repeat loop starts, the user may be most comfortable seeing the menu pop-up with the highlight where it was last located rather than on the top item every time. Prog18 is the same as the last one, except we use DoubleMenu instead of Menu. Everything is the same, but the menu is shorter and wider, allowing for situations where you may have lots of menu choices. You can use up to 36 sent[]s with DoubleMenu. program prog18; uses crt, magic; var dollars : real; procedure xano; begin sent[1] := 'How many Xanopieland Dupers?'; sent[2] := ' '; {must be at least one char} pop(-1,-1); textcolor(BoxText); textbackground(BoxBack); answer := XReadLn(38,12,4); restore; dollars := number * 0.7143; str(dollars:1:2,answer); sent[3] := '$' + answer; sent[2] := ''; sent[1] := 'You own this much Xanopieland money:'; sent[4] := ''; sent[5] := 'Press any key to continue...'; GetAnyKey(-1,-1); end; begin {main} centerjustify := true; sent[1] := 'Universal Money Converter'; pop(25,2); mc := 1; repeat sent[1] := 'Xanopieland'; sent[2] := 'Braze Island'; sent[3] := 'Nomania'; sent[4] := 'Grazille'; sent[5] := 'Exit to Dos'; doublemenu(-1,-1); if u = #13 then case mc of 1 : xano; (* 2 : brace; 3 : nomania; 4 : grazille; *) end; until (u = #13) and (mc = 5); end. In prog18 we have also cleaned up the multiple if statements by using a "case" statement. But, there are still no procedures written for 3 of the 5 menu choices, so these are commented out. Prog19 adds another case statement allowing the user to press a key corresponding to the first letter of a menu item and then that procedure will be run directly. program prog19; uses crt, magic; var dollars : real; procedure xano; begin sent[1] := 'How many Xanopieland Dupers?'; sent[2] := ' '; {must be at least one char} pop(-1,-1); textcolor(BoxText); textbackground(BoxBack); answer := XReadLn(38,12,4); restore; dollars := number * 0.7143; str(dollars:1:2,answer); sent[3] := '$' + answer; sent[2] := ''; sent[1] := 'You own this much Xanopieland money:'; sent[4] := ''; sent[5] := 'Press any key to continue...'; GetAnyKey(-1,-1); end; begin {main} centerjustify := true; sent[1] := 'Universal Money Converter'; pop(25,2); mc := 1; repeat sent[1] := 'Xanopieland'; sent[2] := 'Braze Island'; sent[3] := 'Nomania'; sent[4] := 'Grazille'; sent[5] := 'Exit to Dos'; doublemenu(-1,-1); if (u = #13) then case mc of 1 : xano; (* 2 : brace; 3 : nomania; 4 : grazille; *) end else case upcase(u) of 'X' : xano; (* 'B' : braze; 'N' : nomania; 'G' : grazille; *) 'E' : begin mc := 5; u := #13; end; end; until (u = #13) and (mc = 5); end. There are opportunities for bugs here. You want to make sure u = #13 (the [Enter] key) before allowing the first "case" to execute, because otherwise, a procedure may run twice in a row if the user presses a character key, first when mc = whatever, then then when u = 'whatever'. Also make sure to put the second case characters in single quote marks. And last, notice the "upcase(u)." Without this the user would have to press the shift key to get the desired result. Upcase converts u into a capital letter. Prog20 is going to be a big, but very special program because it will be something you'll use everyday. Run it and see what it does, then study the source code to understand it. program prog20; uses crt, magic; procedure extkey; (* 1 *) var tempstr : string[3]; ekey : boolean; begin XClear; (* 8 *) sent[1] := 'Press any key to see it''s extended code'; sent[2] := 'Press ''Q'' when done.'; pop(18,3); sent[1] := ' '; (* 9 *) sent[2] := ''; repeat ekey := false; GetAnyKey(-1,-1); if u = #0 then begin sent[1] := '#0 + '; u := readkey; ekey := true; end; case u of #13 : answer := ' '; (* 10 *) #8 : answer := ' '; #10 : answer := ' '; #7 : answer := ' '; else answer := u; end; {case} str(ord(u),tempstr); sent[1] := sent[1] + answer + ' (#' + tempstr + ')'; until (upcase(u) = 'Q') and (ekey = false); XClear; end; {procedure ExtKey} procedure asciichart; var x,y : byte; (* 3 *) temp : integer; begin textcolor(white); textbackground(black); x := 1; y := 1; clrscr; for temp := 0 to 255 do begin u := chr(temp); (* 4 *) gotoxy(x,y); (* 5 *) write(' '); if temp < 10 then write(' '); if temp < 100 then write(' '); if (temp <> 7) and (temp <> 8) and (temp <> 10) and (temp <> 13) and (temp <> 27) (* 11 *) then write(temp,'=',u,' ') else write(temp,'='); inc(y); (* 6 *) if y > 25 then begin y := 1; x := x + 7; end; end; {end of for 0 to 255 loop} WaitForUser; xclear; (* 7 *) end; {procedure Asciichart} begin {main} centerjustify := true; mc := 1; repeat BoxBack := red; sent[1] := 'Programmer''s Tools'; sent[2] := 'Copyright 1991, Freeware by Another Company'; pop(18,2); BoxBack := green; sent[1] := 'ASCII Chart'; sent[2] := 'Extended Keys'; sent[3] := 'Quit'; menu(-1,-1); if u = #13 then case mc of 1 : AsciiChart; (* 2 *) 2 : ExtKey; end; until (u = #13) and (mc = 3); CleanUp; end. I've tagged this file with note numbers surrounded by (* and *). Note 1) Don't look here to start reading a program, this is merely a sub-routine. Go way down to the main program block, where it says "{main}" to get a feel for what's going on. Looking at the overall program you'll see that there is the main program block and two procedures, which may be called from the case statement in the main block. Note 2) If the user selects the first menu item, "ASCII Chart" then MC will be 1, and the AsciiChart procedure will be called. Now let's go up and take a look at that procedure. Note 3) Here are some variables being declared within this procedure. They are not available to the main program. X and y are bytes, they can hold a value of from 0 to 255. Temp is an integer, with a possible value of from approx -32000 to +32000. If you look down a few lines, you'll see that x and y are specifically set to 1. In Pascal, as in most programming languages, the act of declaring a variable doesn't set it to 0 or any other value. Unless specifically set to something, it's value will be random. In this procedure, the values of x and y need to start with 1. Note 4) We are in a "for" loop which as it is set up in this program will start with temp defined as 0 and the contents of the loop (between the "begin" and "end" will repeat 255 times, and each time, the value of temp will be increased by 1. The first thing that happens in this loop is that u, a global char variable, is set to the ASCII character represented by the value of temp. This is done by the Turbo Pascal function "chr." Note 5) The next thing that happens in this loop that repeats 255 times is "gotoxy." Gotoxy moves the cursor to the coordinates in the parentheses. Then, with close study, you'll figure out what is written to the screen by the program in the several following lines of code. Following this, are a few lines to make columns on a single screen rather than simply scrolling down 255 lines. I'll let you figure out how that's accomplished by studying the code. Note 6) This note calls your attention to the line, "inc(y)." Inc adds 1 to an integer value. It is like saying, "y := y + 1. By doing this, when the next iteration of the loop occurs, and the gotoxy is encountered, the next character will be written one line below the last, because of y being one number higher. Note 7) After the 255 executions of the for loop, the program calls the magic procedure WaitForUser, which waits for the user to press a key or click a mouse button. The next line is XClear, which clears the screen in preparation to return to the main program. Note 8) is way up high in the code, in the top procedure. This procedure, ExtKey is the one which pops up the ASCII and Extended Key information in a little window for each key that is pressed. First thing this procedure does is clear the screen again. Then it makes a box with a couple of sent[]s explaining, "Press any key to see it''s extended code," and "Press ''Q'' when done." Look at all those single quote marks! There are two of them after in it''s, and two single quote marks in a row on either side of the ''Q''. No, they're not typographical errors. This is how Turbo Pascal differentiates between the beginning and end of a literal string, and a string containing a ' in it. When there are two ''s, Pascal prints it on the screen as a single '. When there is just one ' in the source code, Turbo Pascal thinks you are trying to mark the beginning or end of a literal string. Note 9) The first time the ExtKey procedure runs, as you may recall, a little box containing nothing appears, until you press a key to fill it. Sent[1] has to be something, or the program will think there are no sent[]s at all, so sent[1] is a one-character string containing a space. sent[2] must be specifically rewritten as a string containing nothing, because the preceding pop had used two sent[]s, and the information in sent[]s is not cleared after a call to pop, as it would be in pop-up boxes that automatically disappear after they are done. Notes 10 and 11) Some characters, when written to the "screen" do funny things. #8 is the backspace character which will actually back up the cursor and cause something else to be overwritten. #13 is a carriage return and #10 is a line feed, which would screw up a chart or anything you try to display normally. #7 goes "beep!" So such characters must specifically eliminated if you try to write them literally to the screen. This is an excellent program to study in detail. A really good way to understand it is to modify it. Experiment. Make changes, and see if when you run the program, your changes act as you had expected. See if you can improve on this program or add new features. If you can understand all of this, you are well on your way to being a GOOD programmer. Prog21 is derived from prog20. It is a demonstration of writing to a disk file. The object of this program is to put a column of ASCII equivalents in a text file, so you can use it with your word processor, etc. Run prog21, then go look at the file called ASCII.TXT with the Turbo Pascal IDE or your favorite word processor. Be aware, prog21 is very slow, requiring several seconds to run its course. program prog21; uses crt,magic; var temp : integer; tempstr : string; begin NameOutFile('Ascii.txt'); for temp := 0 to 255 do begin str(temp,tempstr); tempstr := tempstr + ' = ' + chr(temp); FileWriteLn(tempstr); end; end. There are two special items in the magic unit, "InFile" and "OutFile." InFile is used to get text in from the disk and use it within your program. OutFile is used to put information out to the disk. Essentially, these are pathways to disk files. You must give them names that will be used in the disk directory. As usual with disk files, these names must be DOS-legal, with up to 8 characters, optionally followed by a period and up to a three-character extension. Drive designator and sub-directory designations can precede file names. So we would use OutFile to convey information to the disk. The first operation in prog21 is to associate a disk filename with outfile, using "NameOutFile." Then a for loop creates a string, "tempstr" containing each ASCII number and the character represented by that number. The line in this loop, "FileWriteLn" writes this tempstr to the disk. There is another procedure called FileWrite which writes strings to disk, but does not put each one on a separate line. This follows the convention of Turbo Pascal's Write and Writeln. If you have named a file that does not yet exist on disk, it will be created automatically with the first call to FileWriteLn or FileWrite. If the file already exists, the string passed with FileWriteLn or FileWrite will be appended to the end of the file. You'll notice two things about FileWriteLn, one iis that it is very slow. FileWriteLn is nice, because it automatically does several things that you would normally have to write separately into your program, but that's why it is slow. The other thing you'll see when you look at the finished file. Some of the characters do funny things when written to a file, causing a sort of misalignment. These are things like line feed (#10) and Bell (#7), which beeps if read by the DOS type command. Prog22 is a variation of the previous program that handles writing to file more manually, resulting in a much faster program, and eliminates the funny characters. program prog22; uses crt,magic; var temp : integer; tempstr : string; outfile : text; begin assign(outfile,'Ascii.txt'); rewrite(outfile); for temp := 0 to 255 do begin str(temp,tempstr); tempstr := tempstr + ' = '; if (temp <> 7) and (temp <> 10) and (temp <> 13) and (temp <> 26) then tempstr := tempstr + chr(temp); writeln(outfile,tempstr); end; close(outfile); end. Cleaner and faster, eh? I won't go into the specifics of file handling here, it is all well documented in your Turbo Pascal manuals. There are three types of files you can work with, with text files being the most often used by new programmers, and there are many things you can do with files besides simple reading and writing of strings. For that matter, there's a whole bunch of things you can do with strings, but all that is well beyond the scope of this humble tutorial. The compliment of FileWriteLn is FileReadLn. Prog23 shows it in operation, using the previously created Ascii.txt. program prog23; uses crt,magic; var tempstr : string; begin clrscr; NameInFile('Ascii.txt'); repeat tempstr := FileReadLn; writeln(tempstr); until problem; WaitForUser; CleanUp; end. Not very elegant, since the stuff scrolls right off the screen, but you get the general idea. Notice the 4th line from the bottom: "until problem". This introduces something very excellent about the magic unit. It is always watching for mistakes. If you ask most of the functions and procedures in the magic unit to do something that can't be done, they do not crash the program. Instead, they handle the problem as gracefully as possible and set a global boolean variable to true. If there is no problem with your use of the function or routine problem is false. In this case, if you try to read beyond the end of the file, FileReadLn turns problem to true, and this particular program's repeat until loop falls through, and the program ends. If you forced the issue or wrote a program that does not regard "problem" then any subsequent attempts to use FileReadLn would simply assign #0 to tempstr. In Turbo Pascal, files that are read from or written to must first be opened, the operation(s) performed, then closed. FileWrite and FileWriteLn handle all this automatically, but FileRead and FileReadLn cannot, because if you were to make subsequent calls to these and they did open and close the file each time, each time you read, you would always get the first line in the file, since opening a file, resets it to the beginning. So from the first time you read the file with FileRead or FileReadLn, InFile remains open. It should be closed before the program ends, and the best way to do that is to call the very standard procedure, "CleanUp" which is specifically designed to be the last line in any program. It closes all open files, resets the video mode, clears the screen and frees up memory. It never hurts to have this line at the end of any program, and it is essential at the end of some. If you have read several lines in infile, then you want to start over at the top, do this: "reset(infile)." Prog24 explores graphics a bit. Compile it, run it, study it. program prog24; uses crt, video, magic; begin sent[1] := 'This is text mode'; GetAnyKey(-1,-1); BestVideo; sent[1] := 'This is a graphics mode'; GetAnyKey(-1,-1); CleanUp; end. As you can see, it is quite simple. There are only two new things in this program. Can you spot them? The first is the use of a new unit, "Video." The second is a procedure called "BestVideo." BestVideo is a procedure that figures out what type of monitor you have, then sets up the program to run on your graphics card. And there is a special surprise. Those of you who have done some graphics work in Turbo Pascal will recognize it's benefit right away. When you call BestVideo, it automatically incorporates the graphics drivers into your compiled .EXE file. Usually, the Borland Graphics Interface files (*.BGI) must be distributed on the disk along with your program. There are several video procedures within the magic unit. They are CGAVideo, HercVideo, EGAVideo, VGAVideo, BestVideo, and TextVideo. If you use HercVideo, in place of BestVideo, then the program will work only on computers equipped to display Hercules Graphics, but the compiled program will be much smaller, and you can use absolute coordinates to display graphics. BestVideo will link in all the *.BGI files and then automatically detect which graphics card is present in any computer, and therefore will run on practically any IBM-clone that has any graphics card. But BestVideo has to incorporate all the graphics drivers, and therefore the finished product is larger. Furthermore, when using BestVideo, you have to account for different resolutions on different machines. On a CGA machine, the coordinates 0,180 are near the bottom left edge of the screen, but on VGA 0,180 is on the left, but nearer to the top of the screen. With BestVideo, you have to make calculations based on GetMaxX, and GetMaxY, (see your Borland books) or use centered pop-up boxes. TextVideo is the way out of graphics. For instance, if you write a program that starts in graphics mode to display a drawing, then you want to switch back to text for faster handling of strings, you simply call the TextVideo procedure. Pop, GetYN, GetAnyKey, Dialog, Menu and DoubleMenu all work almost the same in any graphics mode as in text mode. program prog25; uses crt, video, magic; begin sent[1] := 'This is text mode'; GetAnyKey(-1,-1); CGAVideo; sent[1] := 'This is a graphics mode'; GetAnyKey(-1,-1); TextVideo; sent[1] := 'This is text again'; GetAnyKey(-1,-1); CleanUp; end. Prog25 switches to CGA graphics mode, then back to text mode. Prog26 shows some of the complexity in using BestVideo. program prog26; uses crt, video, graph, magic; begin sent[1] := 'This is text mode'; GetAnyKey(-1,-1); BestVideo; setcolor(yellow); circle(getmaxx div 2,GetMaxY div 2, GetMaxY div 4); sent[1] := 'This is a graphics mode'; GetAnyKey(-1,-1); TextVideo; CleanUp; end. Notice the new unit. We are now using a unit that comes with Turbo Pascal called Graph.tpu. It allows us to make circles, rectangles, lines, change colors, and many other things. You should use Graph in all your graphics programs because those which don't make use of it will not be much larger than those that do. Borland's compiler is smart and strips unused code out of units as the program is compiled. This one switches to a graphics when a key is pressed. If you have a VGA system, then you'll see a yellow circle around a pop-up box. But, then change the "BestVideo" line to "CGAVideo" and you'll see that the pop-up box partially covers the circle, even though the circle is supposedly the same size. It's the pop-up that changed. Notice that you can change color by simply passing a color to SetColor. Prog27 shows a few of the many other things you can do with the Turbo Pascal Graph unit. program prog27; uses crt, video, graph, magic; begin BestVideo; SetColor(white); circle(100,100,50); circle(100,100,40); SetFillStyle(solidfill,white); FloodFill(51,100,white); SetColor(lightgreen); SetLineStyle(0,0,3); rectangle(70,70,130,130); SetFillStyle(solidfill,lightmagenta); bar(90,90,110,110); WaitForUser; CleanUp; end. Prog28 shows how to work with text in a graphics mode. Turbo Pascal supplies a default font, which is an 8 x 8 pixel bitmapped font. It is called DefaultFont. program prog28; uses crt, video, graph, magic; begin BestVideo; OutTextXY(10,100,'This is the Default font'); WaitForUser; CleanUp; end. In Prog28 we see that the way to display a string is through OutTextXY, which is similar to GoToXY combined with Write in text mode. OutTextXY requires screen coordinates, then a string. Unlike write and writeln, you must pass only a string and only one string at a time. You must "str" numbers before using OutTextXY. Prog29 shows how to work with different fonts. program prog29; uses crt, video, graph, magic; begin BestVideo; Triplex; SetTextStyle(TriplexFont,HorizDir,4); OutTextXY(0,0,'This is the Triplex font'); WaitForUser; CleanUp; end. First, you'll see a call to "Triplex." This will cause the external Triplex.Chr file to be compiled directly into the .EXE file. You do not have to distribute finished programs with external *.chr files! But by itself, Triplex has no other effect on the program. If you want to actually use the Triplex font, you have to use "SetTextStyle". SetTextStyle takes three parameters. First is an integer for the font to use, and these integers also have pre-declared constants in the graph unit. Triplex can be referred to as "1" or TriplexFont. The second number or constant is the orientation of the text. HorizDir is the most common, of course. It can also be represented with a 0. And finally the size of the font can be controlled with another integer value from 1 to 10. As tempting as it may seem, there are two reasons not to use any fonts except DefaultFont with Pop, GetYN, GetAnyKey, Dialog, Menu or Doublemenu. Gothic, Triplex, SansSerif and Small fonts are drawn rather than bitmapped and therefore take longer to generate on the screen, slowing the program perceptably. Furthermore, because of the way they are generated, they don't line up right relative to the general x and y coordinates used by these procedures. If you run prog29 in CGA mode (you can change BestVideo to CGAVideo to see) you'll notice that the line, "This is Triplex font." is too long to fit on the screen. So, in prog30, lets switch to a different CGA graphics mode. program prog30; uses crt, video, graph, magic; begin CGAVideo; SetGraphMode(4); Triplex; SettextStyle(TriplexFont,HorizDir,4); outtextxy(0,0,'This is the Triplex font'); WaitForUser; CleanUp; end. The line, "SetGraphMode(4)" changes to the higher resolution, 2 color, 640 x 200 CGA mode. Prog31 shows off some basic mouse work, and the main principal of drawing to the screen. program prog31; uses crt, video, graph, magic; var x,y : word; {"word" is a positive integer value} begin BestVideo; WasteMouse; repeat poll(x,y,left,right); LineTo(x,y); until left; CleanUp; end. In prog31 a loop repeats until left becomes true. Left is a global boolean variable that is set to true when the user clicks the left mouse button. Poll is a function that asks the mouse to report it's current status. It tells you through poll, the current screen position, in this case using x and y as word type variables for coordinates, and the status of the left and right mouse buttons. Every time the mouse is polled, a line is drawn from where the last line ended, to where the mouse is currently. Notice the line, "WasteMouse." This is a procedure which stalls the program until both "left" and "right" are false. (no mouse buttons pressed). Without "WasteMouse" the program might end immediately. Prog32 shows you one way to move a rectangle around. This program will display the mouse pointer until the left mouse button is clicked, then it will turn it off and show a movable rectangle until the right mouse button is clicked. program prog32; uses crt, video, graph, magic; var x,y : word; begin BestVideo; WasteMouse; PointerToXY(GetMaxX div 2,GetMaxY div 2); PointerOn; repeat Poll(x,y,left,right) until left; PointerOff; SetWriteMode(XOrPut); rectangle(x,y, x + 40, y + 20); repeat rectangle(x,y, x + 40, y + 20); Poll(x,y,left,right); rectangle(x,y, x + 40, y + 20); until right; CleanUp; end. Prog32 shows you how to turn the mouse pointer on and off, and shows "SetWriteMode" so that you can eliminate images which have been created, by merely writing them over themselves. Prog33 shows a couple of small improvements: program prog33; uses crt, video, graph, magic; var x,y,oldx,oldy : word; begin BestVideo; WasteMouse; SetTextJustify(CenterText,BottomText); OutTextXY(GetMaxX div 2,GetMaxY - 4, 'Click right mouse button to end program'); SetWriteMode(XOrPut); rectangle(x,y, x + 40, y + 20); repeat repeat Poll(x,y,left,right); until (x <> oldx) or (y <> oldy) or right; rectangle(oldx,oldy, oldx + 40, oldy + 20); oldx := x; oldy := y; rectangle(x,y, x + 40, y + 20); until right; CleanUp; end. First, we took off the mouse pointer stuff to simplify the program. Then we use one of the fancy features of the Turbo Pascal Graph unit to cause text to be centered from left to right around the x coordinate and the bottom of the string will be on the y coordinate. And a notice is posted for the user. Then using the new variables, "oldx" and "oldy" within the repeat loop, we check to see if there have been any changes in position before moving the rectangle. This eliminates annoying flashing. In our last program I'll show you how to use the sound effects in the magic unit, and we'll fool around with text in graphics modes a bit more. program prog34; uses crt, video, graph, magic; var x,y,oldx,oldy : word; begin SoundUp; (* 1 *) MainBack := black; (* 2 *) BestVideo; Gothic; (* 3 *) Triplex; SetColor(LightMagenta); SetTextJustify(CenterText,TopText); (* 4 *) SetTextStyle(GothicFont,HorizDir,6); OutTextXy(GetMaxX div 2,0,'Type Anything'); SetTextStyle(TriplexFont,VertDir,1); SetTextJustify(LeftText,CenterText); OutTextXY(20,(GetMaxY div 2) + 30,'Press [Enter]'); OutTextXY(40,(GetMaxY div 2) + 30,'when done.'); SetTextStyle(DefaultFont,HorizDir,2); SetTextJustify(LeftText,TopText); SetFillStyle(SolidFill,black); (* 5 *) SetColor(LightCyan); Waste; (* 6 *) answer := ''; repeat u := ReadKey; pink; (* 7 *) if ord(u) > 31 then answer := answer + u; (* 8 *) if u = #8 then (* 9 *) begin bar(70,100,70 + TextWidth(answer), (* 10 *) 100 + TextHeight(answer)); delete(answer,length(answer),1); (* 11 *) end; OutTextXY(70,100,answer); until u = #13; SoundDown; delay(500); (* 12 *) Twinkle; CleanUp; end. Prog34 has notes within (* and *): Note 1) One of the four sound effects built into the magic unit. To use one of the effects, simply call it's procedure. Sometimes sounds don't behave as expected with machines faster than 33mhz or when heavily multitasked (running concurrently with other programs) under MS-Windows or Deskview. Note 2) By setting the main background color to black before a call to BestVideo, the video mode will start and run with a black background. Note 3) These procedures, Gothic and Triplex hook in the two fonts besides the default font that this program is going to use. There will be no need for *.chr files on the disk with a compiled program. Note 4) SetTextJustify will center a title on the screen when set this way. Note 5) SetFillStyle is used to establish parameters for "bar," used a few lines down in this program. Bar will make a solid rectangle in the color specified (black in this case) This is a good way to erase text in graphics modes. Note 6) Waste stalls the program until no mouse buttons are pressed and no characters have been entered from the keyboard. If a key had been pressed as this program was starting, it would be reflected as the first character in a string the user is going to type. Note 7) "Pink" is a sound effect, typically used to indicate a small action has occurred, such as a key has been pressed. Note 8) "Ord" returns the ordinal position in a set of the variable specified in parentheses. When used with ASCII characters, Ord returns their ASCII number. What this line does is eliminates adding characters to the string called "answer" if they might be unprintable characters. Note 9) u is ASCII #8 when the backspace key has been pressed. Note 10) "Bar" is being used to erase "answer" from the screen because the backspace key has been pressed and therefore the user wants to eliminate the last character in answer. The Bar will erase it, and a newer version of answer, one which will be one character shorter will be written in its place. TextWidth and TextHeight are excellent functions provided by Turbo Pascal to determine how many pixels wide and how many pixels high a string are, no matter what font and size are selected. Note 11) "Delete" is a way to shorten a string. Delete requires three parameters, the string on which to operate, the character position in the string at which to delete, and the number of characters to take out. There are several other string procedures available from Turbo Pascal including Pos (find the position of a matching sub-string within a string), Copy (make a sub-string from a string), and ConCat (add two strings together). See your Borland Turbo Pascal manuals for more information about string handling. Note 12) Delay is called between two sound effects to put some time between them. Delay requires one number, an integer representing the number of milliseconds to delay. There are 1000 milliseconds in one second, so this delay is 1/2 second. While Delay is running, no other actions can take place. - - - And that almost wraps up this tutorial! Hope you have had as much fun reading and experimenting as I did writing it. There is far more available in Turbo Pascal than this short tutorial can tell you about. To learn as much as you can, in a reasonable amount of time, I recommend getting several books on Turbo Pascal, and experimenting with every idea that crosses your mind. In fact, there's quite a bit more to the Magic unit itself. You can read Magic.Doc to learn more of its included procedures and functions, and get more technical information on the ones you already know. This is shareware. Feel free to copy and distribute Pascal Magic as long as all files remain intact and unchanged. If you have paid a professional shareware distributor a few dollars for this disk, you have paid for the service of providing a copy. If you use MAGIC.TPU in your programs, or if you have found this tutorial helpful, you must pay for the creation of the product. Send $29.95 to: Another Company P.O. Box 298 Applegate, OR 97530 Complete source code is available for the MAGIC.TPU. This is good for using as a framework for your own very customized applications, for learning more about Turbo Pascal Programming, for stripping down top efficiency, and for improving, since you will probably soon become (or might already be) a better programmer than I am. For registration AND source code, send $59.90 and please specify disk size. This is version 1.0 and you may find a bug or two. I make no guarantees about it's suitability to your programming needs. If we find any major bugs, they will probably be corrected by the time you get your registered version! If you require technical assistance, you can try to phone me at 503-846-7884. Best bet is 9-5 weekdays, but I'm not always available. Since you are going to write some wonderful programs, you might as well start making money with them! You won't have to work in an office doing programming for someone else. You can write exactly the kind of programs you want to write, and make money at home! The 'secret' is shareware. Does it work? You bet! You can easily have a small success, making some extra spending money each month, or with some practice, you can have a wild success, and make a fine living! There are some millionaires who make their ever-growing fortune entirely through shareware. ANOTHER COMPANY shows you everything. Besides our own shareware successes, including WRITER'S DREAM, BICYCLE TUNE-UP AND REPAIR, MONEY, BLACKBOARD, BETTER EYESIGHT, WHAT'S IN THAT BOX? and THE UNIVERSAL CONVERTER, we have researched the shareware market, we have interviewed other shareware authors and have learned about all sides of the rapidly expanding shareware business. We show you how to write programs that capture the public's interest, how to make sure your customers 'register', and how to get money by other means through your shareware. We show you more. We show you frequency charts indicating which types of programs sell best, we offer suggestions for programs as yet unwritten, we talk about your on-disk instruction manuals, we even show you how to write user-friendliness into your programs. Can you succeed without SUCCESS WITH SHAREWARE? Yes! But why do it the hard way? We have learned all the super-professional approaches, the pitfalls and the shortcuts. We'll tell you all about it, and then instead of stumbling around for the first couple of years, you'll be a professional from the start! To get your copy of SUCCESS WITH SHAREWARE! (which is not shareware, itself - only available from Another Company), send $19.95. Please specify if you need 3.5" disk size. Thanks, - Jeff Napier - January 9, 1992 You can order products by writing your order on plain paper, or by printing the included ASCII file called Order.frm, or by phoning 1-503-846-7884, generally weekdays between 9 and 5, west coast time.