********************************************************************* This article is being presented through the *StarBoard* Journal of the FlagShip/StarShip, SIGS (Special Interest Groups) on the Delphi and GEnie telecommunications networks. Permission is hereby granted to non-profit organizations only to reprint this article or pass it along electronically as long as proper credit is given to both the author (when known) and the *StarBoard* Journal. ********************************************************************* BEGIN BASIC WITH BUGS by Tim Sickbert (MIDNIT) [This article is Copyrighted 1986, by Timothy B. Sickbert. The article and accompanying program first appeared in the April 1986 Champaign-Urbana Commodore User's Group Newsletter. Permission is hereby granted to non-profit organizations to re-print this article, to pass it along electronically, provided credit is given the author, or to throw it away, as long as the author is not mentioned. Permission is also granted to commercial electronic information services to include this article among the files which users may freely download, provided credit is given the author. Comments are invited. Please address to the Timothy B. Sickbert, P.O. Box 1747, Champaign, IL 61820.] One of the best ways to learn to program, aside from writing your own programs, is to take a program written by somebody else, debug, and rewrite it. So, to help get you started with BASIC, I am going to write an ongoing column on BASIC debugging. What I will do is give, in this and future columns, a listing of a BASIC program that is poorly written. It will have flaws in the overall design and the actual code. Some of the flaws will be obvious; some more subtle. I will try to have to different programs in each issue of the newsletter. The corrections for one of the programs will be included and the corrections for the other program will appear the following month. So, to start things off... The following article and program appeared some time ago in a Commodore User's Group newsletter. It is supposed to be a nice, simple, little utility for printing multiple mail labels with the same name and address, such as you could use for return address labels. The program is poorly designed and coded, having both major and minor errors. For this first program, I will comment on the problems and how to fix them. I will give you another program to work on yourself and then comment on that second program next month and give my revision. I will also give you another buggy program next month. Revised code, showing better use of BASIC, appears later in this issue. But before you look at the revisions, see if you can find the problems with this version of the program. Often, I have wanted to make my own address labels. I have several programs for mailing lists, but I wanted quantities of one specific label and none of my programs would do this. I finally decided to write my own program and was surprised at how simple it was. I have not dressed up the program, except for the FOR/NEXT loop and to make it for double wide labels. In the form presented, it will print labels two across. If you want single wide labels, simply omit the semi-colon (;) in all the even thousand lines (1000, 2000,...) and omit the next line after the semi-colon (1100,2100,...). Line 200 sets the quantity of labels. It is currently set at 20 for double wide (10 x 2 across) or 10 for single wide. Simply change the data in quotation marks to your data and run the program. I used a Commodore MPS 801 printer. I am not familiar with others but assume that they will work the same. I originally intended to add to the program, but have decided to let you alter the program as you see fit. 100 OPEN 4,4 200 FOR X = 1 TO 10 1000 PRINT#4,"NAME"; 1100 PRINT#4,TAB(40)"NAME" 2000 PRINT#4,"ADDRESS"; 2100 PRINT#4,TAB(40)"ADDRESS" 3000 PRINT#4,"CITY,TOWN,ZIP" 3100 PRINT#4,TAB(40)"CITY,TOWN,ZIP" 4000 PRINT#4,; 4100 PRINT#4, 5000 PRINT#4,"TELEPHONE" 5100 PRINT#4,TAB40"TELEPHONE" 6000 PRINT#4 6100 PRINT#4, 9000 NEXT 9500 CLOSE4 10000 END Don't look at this until you have tried to find the flaws in the mail label program listed with the first half of this article. First, we must give the original author credit for his disclaimer. He says that the program is not dressed up and that he is not too familiar with printers other than the MPS 801. He is really inviting us to do what we are doing here: Taking a simple design and expanding it. Still, the original program shows a poor understanding of BASIC in general, and of printers and printer commands in particular. A printer is not the same thing as a video display screen and must be treated differently from BASIC. The first mistake is in using the TAB command to space over to the second set of labels. The TAB command does not work correctly with many printers. It will often tab from the current position the print head, rather from the left margin or leftmost column - if it works with your printer at all. In fact, the Commodore 128 System Guide states that, "The TAB function can only be used with the PRINT statement, since it has no effect if used with the PRINT# to a logical file." Well, it happened to work for the author with his MPS 801. It will not work on most printers. The second error appears in lines 4000-4100 and 6000-6100. Actually, this is not so much a coding error as a style error. The author is bending the code to fit the documentation - the author says to delete lines for single wide labels. Well, it does work. But it could have been better handled. In the actual coding, these sets of two lines each provide a single line feed, i.e., the printer rolls the paper up one line. There is no need for lines 4100 or 6100 except to match the documentation. The third major error is in line 9500. The author closes the communications channel to the printer without first unlistening it. "Unlistening" means to tell the printer not to expect any more data, and is done by sending a "PRINT#4," without any other data, immediately before closing the channel. The author comes very close to accidental elegance - like doing a swan dive when falling off a bridge - in line 6100. There he has a "PRINT#4," statement. This might succeed in unlistening the printer if he had not put the comma at the end of the command. The final, minor, error, is in designing a program for double wide labels. If any of you have double wide labels, let me know; I have never seen them. Oh, sure, they are available. But they are rare. If anybody is going to bother looking for double wides, let THEM modify the program - they are in the minority. The author of the program apparently does not know much about users, or does not care much about making the program useful and easy to the majority of users. But, to show how to handle formatting simple printed output, I provided for double wide labels in my revisions. So, one example of amended code looks something like this: 10 REM 20 REM CHAMPAIGN-URBANA COMMODORE USERS GROUP 30 REM APRIL 1986 40 REM TIMOTHY B. SICKBERT 50 REM 100 CR$=CHR$(13):SP$=CHR$(32):REM CARRIAGE RETURN AND SPACE 110 DIM NM$(4) 120 FOR I = 1 TO 4: READ R$: NM$(I) = R$: NEXT 130 DATA "NAME","ADDRESS","CITY, STATE, ZIP","TELEPHONE" 140 N=40-LEN(NM$(1)):A=40-LEN(NM$(2)):C=40-LEN(NM$(3)): T=40-LEN(NM$(4)) 150 : 160 OPEN4,4,7 170 FORX=1TO10 180 PRINT#4,NM$(1); 185 FORY=1TON:PRINT#4,SP$;:NEXTY:PRINT#4,NM$(1) 190 PRINT#4,NM$(2); 195 FORY=1TOA:PRINT#4,SP$;:NEXTY:PRINT#4,NM$(2) 200 PRINT#4,NM$(3); 205 FORY=1TOC:PRINT#4,SP$;:NEXTY:PRINT#4,NM$(3) 210 PRINT#4,CR$; 220 PRINT#4,NM$(4); 225 FORY=1TOT:PRINT#4,SP$;:NEXTY:PRINT#4,NM$(4) 230 PRINT#4,CR$; 240 NEXTX 250 PRINT#4:CLOSE4:END In line 100 I set up string variables that I will want to use later, specifically, the ASCII codes for a carriage return and for space. The need for these will become apparent shortly. Line 110 DIMensions a string array. It is really not necessary to DIMension arrays of less than 10 elements, but it is often good practice. This lets somebody else know what you are doing. The reason I bother to put the name, address, etc., in string variables is so I can later format the printing more easily. It also makes the program easier to change for another person to use. Rather than entering everything twice, for double wide labels, it need be entered only once - and all in one line, at that. Line 140 is the key to setting up the double wide format. TAB and SPC (space) commands do not always work right for formatting on a printer. But I have to get it to print on the second column of labels. If I just inserted a certain number of spaces after each item the right column of labels would be a mess. And since everybody's name is of a different length, we have to make it handle that, too. So, what I did was set it up to space over 40 spaces minus the length of the name (,address, or whatever). Thus the FOR/NEXT loops in the odd numbered lines from 160 to 250. These loops add however many spaces are needed to get over to the fortieth column, regardless of the length of the name and address. Line 150 is there for one reason only: To separate two distinct parts of the program. Lines 100 to 140 set up variables; lines 160 to 250 do the actual printing. I could separated the two with REM statements, but a colon gives a virtually blank line which makes for a visual break where REMS can look cluttered. By using a secondary address of 7 in line 160, I allow the user to print the labels in lowercase/uppercase. With the implied secondary address of 0 in the original, the user could use only uppercase. When using this revision, it is helpful to have the screen in lowercase/uppercase mode as well. You can get this by . Line 170 just sets up the number of labels the program will print. The program is set up for double wide labels. It would have been alot easier, and probably more useful, to set it up only for single labels, but I wanted to compare with the original. The even numbered lines from 180 through 230 are necessary for both single and double wide labels; the odd numbered lines are needed only for double wide labels. Setting it up this way causes the same problem as the original: The user most actually play with the code to change it for single wide labels. Generally, this should be avoided. If you want to modify the program for single wide labels, delete the odd numbered lines and remove the semi-colon from even numbered lines 180 to 200 and 220. This brings up an interesting little point. In lines 210 and 230, I have "PRINT#4,CR$;". This looks like an odd little construction. It is. But this insures that it will work on virtually any printer. I had defined CR$ as a CHR$(13) in line 100. CHR$(13) is a carriage return and, for almost all printers, a line feed. Some printers will execute a carriage return and line feed if you simply "PRINT#4". Others won't. So sending the CHR$(13) insures that it will feed a line. Other printers, when sent a CHR$(13), will execute the line feed, and then another for the command strin! So, we put a semi-colon at the end of the statement to suppress a possible second line feed. Oh, by the way, the reason we want extra lines in the first place is because almost all mail labels are spaced at one inch. Most printers print six lines to an inch. So, we need to print a total of six lines. Line 240 closes the loop that started in line 170, and line 250 neatly wraps things up by unlistening the printer, closing the logical file (communications channel), and ending the program. The listing above is a decent type-in program. It will run on any Commodore system straight up; will work with almost any printer; will handle any name, address, or whatever, less than 40 characters long; and only one line needs be changed for somebody else to use it. It is a couple lines longer than the original but it doesn't have the problems. It would not, however, be a very good program for uploading to a bulletin board or giving to someone on a disk. It has no documentation in the listing and never tells the user what is going on. To make it good for passing around on BBSs or on disks, we should add INPUTs to get the name, address, etc., and how many copies the user wants. We should tell the user what is going on by printing messages to the screen. We should add REM statements to make the listing self-documenting so somebody else who wants to play with the program will be able to see what has already been done. Finally, for real class, we could make it ask the user if he wanted single or double columns: And make the print routine shorter at the same time! So, we finally end up with a program that looks like this: 10 rem"****************************** 20 rem" 30 rem" not so simple label maker 40 rem" 50 rem" Timothy B. Sickbert 60 rem" 70 rem"****************************** 80 : 90 cr$=chr$(13):sp$=chr$(32):rem chr$(13)=carriage return chr$(32)=space 95 rem "qQ][rR" = cursor down, up, right, left, reverse on, reverse off 100 : 110 print chr$(147);chr$(14):rem "clear/home:lowercase/uppercase 120 print "qqqq]]]]rLabel MakerR":rem cursor down, cursor right, reverse on/off 130 print " I will ask for your full name" 140 print " street address, apartment #," 150 print " city, state, zip code, and " 160 print " telephone #. Do not use any" 170 print " punctuation other than a period.qqq" 180 nm$="":ad$="":ap$="":ci$="":se$="":zi$="":tl$="" 181 input "What is your full name";nm$ 190 input "What is your street address";ad$ 200 input "What is your apartment #";ap$ 210 input "What is your city";ci$ 220 input "What is your state";se$ 230 input "What is your ZIP code";zi$ 240 input "What is your telephone number";tl$ 250 la=1 251 print"qq":input "Single or double labels <1 or 2>]]]1[[[";la 260 if (la<1) or (la>2) then 250 270 input "How many labels do you want to print";nu 280 print"Sqqq]]]I have the information as:qqq" 290 print "Name: ";nm$ 300 print "Address: ";ad$ 310 print "Apartment:";ap$ 320 print "City: ";ci$ 330 print "State: ";se$ 340 print "ZIP: ";zi$ 350 print "Telephone:";tl$ 360 print "q Your labels are "la" wide." 370 print "You want "nu" labels printed." 380 print:print:input"]]]Is this correct]]]y[[[";r$ 390 ifr$="y"then420 400 ifr$="n"then 110 410 goto 380 420 : 430 ifla=2thenla=0 440 if len(ap$)>0then ad$=ad$+", Apt. "+ap$:rem concat street and apartment so no comma 450 cz$=ci$+", "+se$+" "+zi$:rem concat city/state/zip 460 nm=40-len(nm$):ad=40-len(ad$):cz=40-len(cz$):tl=40-len(tl$) 470 : 480 rem print routine 490 open 4,4,7: rem lowercase/uppercase 500 for x = 1 to nu 510 dc=nm:dc$=nm$:print#4,nm$;:gosub700 520 dc=ad:dc$=ad$:print#4,ad$;:gosub700 530 dc=cz:dc$=cz$:print#4,cz$;:gosub700 540 print#4,cr$; 550 dc=tl:dc$=tl$:print#4,tl$;:gosub700 560 print#4,cr$; 570 next x 580 : 590 print#4:close4 600 : 610 input "Do you want more labels]]]n[[[";r$ 620 if r$="y"then640 630 end: 640 printchr$(147) : rem "clear home 650 input "Do you want the same name]]]y[[[";r$ 660 ifr$="y"then270 670 if r$ = "n" then 180 680 goto 610 690 rem double column routine 700 if la then print#4,cr$;:return 710 for i=0 to dc:print#4,sp$;: next i 720 print#4,dc$:return The above program has almost everything needed. It is not, however, idiot proof. It does not tell the user when it is printing. And it still has a bug. So I got lazy. But it should work, without modification, on any Commodore computer from the PET 2001 to the Commodore 128 PC and on almost any printer. A user can run it without ever looking at the program itself. The program is fairly well self documenting to both the user and to the programmer, so it can be passed around on disk or on a BBS without any accompanying documentation. A few final notes before I give you your next buggy program to work on. First, I will not ramble on this long in the future, I promise. This one is so long so I can show you basically where we are starting and where we want to go. In the future, I will give only the original and the short revisions in the newsletter, and will put the long revision with the bells and whistles on the monthly CUCUG public domain disk. I will try to concentrate on the elementary BASIC commands for the first several months. [To keep everything together, when I pass this around, I will include the listing of the long revision in the article as well as have the long revision as a second file.] The buggy program for you to work on appears below. Work on it, see if you can find the bugs, and by next month I will have some comments and an example revision. 10 rem "Simple adding machine 20 geta$:ifa$=""then20 30 print a$; 30 if val(a$)<0 or val[a$]>then goto200 40 b$=b$+a$:goto20 200 b=val(b$) 210 if a$="+"then c=c+b 220 if a$="-"then c=c-b 230 if a$="*"then c=c*b 240 if a$="/"thten c=c/b 250 print c 260 goto 20 This little adding machine program above does not work. It is supposed to GET a key, then IF 1) the key is a numberal key, then add the character to the string to any other numeral characters previously entered to make a multi-digit number; or 2) thte key is an operation key (+,-,*,/,), then perform the operation, print the total, and go back and get the next value. There are errors with the program all over the place. One of the biggest is the order in which it tries to do things. Also, look out or an insidious little error in line 30. This one will take some doing to get around, but be creative. And, good luck! Tim Sickbert