home *** CD-ROM | disk | FTP | other *** search
- *********************************************************************
- 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
- <COMMODORE><SHIFT>.
-
- 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