Advanced BASIC Programming For The C64 and Other Commodore Computers
Advanced BASIC Programming For The C64 and Other Commodore Computers
Advanced BASIC Programming For The C64 and Other Commodore Computers
Michael Richter
Includes index.
1. Commodore 64 (Computer)-Programming.
2. Commodore computers-Programming. 3. Basic
(Computer program language) I. Title.
QA76.8.C64R52 1983 001.64'2 83-15604
ISBN 0-89303-302-2
84 85 86 87 88 89 90 91 92 93 10 9 8 7 6 5 4 3 2 1
CONTENTS
1 Introduction 1
1.1 Prerequisites and purposes 2
1.2 Principles of BASIC 4
1. 3 Interoperabili ty 5
3 Mechanics of a Program 25
3.1 Line numbers 26
3.2 Commands 28
3.3 Variables 29
3.4 Arrays 31
3.5 Bits, bytes, characters, and numbers 33
3.6 Keyboard input 35
3.7 FOR ... NEXT loops 38
3.8 Machine-language interface 40
3.9 Odds and endings 41
3.10 Coding tricks-DEF and ON 43
4 Devi.ces 47
4.1 Un-conventions 49
4.2 Printers 51
4.3 Disks and drives 53
4.4 Disk files 55
4.5 Disk commands 57
4.6 Tokens 59
4.7 Using files 61
4.8 Sorts and searches 63
v
5.5 Prin ting the references 74
5.6 What doesn't work 75
5.7 SUPERLISTing 76
5.8 SYS CHECKSUM 81
5.9 TURTLEW ALK 85
5.10 DOMINOES 90
6 Pursuing a Project 97
6.1 The first call 98
6.2 First meeting 100
6.3 Structuring the system 102
6.4 Inventory control (customer description) 104
6.5 Design 105
6.6 Using the warehouse program 107
6.7 Design review meeting 109
6.8 Coding 110
6.9 Maintenance 111
6.10 Bigger and better 114
Index 117
vi
LIMITS OF LIABILITY AND
DISCLAIMER OF WARRANTY
The author and publisher of this book have used their best efforts in preparing
this book and the programs contained in it. These efforts include the develop-
ment, research, and testing of the programs to determine their effectiveness. The
author and the publisher make no warranty of any kind, expressed or implied,
with regard to these programs, the text, or the documentation contained in this
book. The author and the publisher shall not be liable in any event for claims of
incidental or consequential damages in connection with, or arising out of, the fur-
nishing, performance, or use of the text or the programs. The programs contained
in this book and on any diskettes are intended for use of the original purchaser-
user. The diskettes may be copied by the original purchaser-user for backup pur-
poses without requiring express permission of the copyright holder.
TRADEMARKS OF MATERIAL
MENTIONED IN THIS TEXT
Apple machines: Apple Computer, Inc.
C-BASIC: Compiler Systems, Inc.
CBM, Commodore 64, Name Machine, Pet, SuperPet, Vic-20,
Word Machine: Commodore Business Machines, Inc.
CP/M: Digital Research Corporation
M-BASIC, Microsoft: Microsoft Corporation
Petspeed: Small Systems Engineering
Tandy machines: Tandy Corporation
WordPro: Professional Software, Inc.
vii
I
1 Introduction
Advanced BASIC Programming is not a contradiction in terms. Tradi-
tionally, advanced software has been developed in other languages: FOR-
TRAN, COBOL, and, more recently, PASCAL. BASIC was designed to be
an introductory language, not one for high-powered needs. Yet, BASIC can
be used for those needs, often as well as or better than the traditional
languages.
This text has two interrelated functions: helping you develop advanced
software and teaching you concepts of programming that will carry over
into professional levels. In general, software quality is independent of the
language used, so learning to write good programs will serve you well if
you later move into the conventional languages. The great advantage of
using BASIC for learning is that the computer you need for it is inexpen-
sive and readily available.
This text is aimed at Commodore BASIC, and most of it can be applied
on the VIC 20. This means that you can learn advanced BASIC on a
machine selling for less than $200. By comparison, even a terminal to a
computer capable of running FORTRAN will cost several times that
amount, typically a few thousand dollars instead of a few hundred.
Another advantage of basing this text on the Commodore version of
BASIC is that it is applicable to all of their machines, from the VIC 20 and
64 through the business machines which, with peripherals, may cost
$5-10,000 and offer substantial business capabilities. So, you may learn
on an inexpensive starter system, then transfer that knowledge to ad-
vanced applications.
You don't have to use a Commodore computer to learn advanced pro-
gramming in BASIC-in fact, most of the material in this book is relevant
to Apple or Tandy machines as well. In addition, the philosophy and prin-
ciples of programming apply to all interpretive languages. Most of the
1
2 1: Introduction
material on BASIC itself is common to all dialects, even those most dis-
tant from Commodore's. But the versions of BASIC from Microsoft (Ap-
ple, Tandy, M- and C-BASIC for CP/M) are close enough to Commo-
dore's for most operations not dealing with peripherals to apply directly.
Finally, all the ideas and most of the examples should be valuable to you
regardless of the hardware you use.
You need not own a computer to learn advanced software or to use this
book. You will want access to one for enough time to convert book
knowledge to practice. Each of the sections of the book takes only a few
minutes to read, but it should give you enough ideas to spend an hour or
more in practice.
While you can write good code for a tape-only system, professional
software is normally aimed at disk. First, the disk saves so much time in
programming that tape alone is impractical. Even more important is the
fact that a serious program will use the disk to extend the computer's
built-in memory. A printer is almost a necessity, too; it lets you read list-
ings that would be too complex for a screen and correlate routines from
one part of the program with those they call in another. Finally, if you are
using a Commodore 64, get a monochrome monitor. You will spend
hours reading text on the screen; fighting hash from the modulator, poor
resolution from color dots, and blur from a poor television set costs more
than the monitor's price of $100 or so.
Even with all due diligence in reading this book and practicing, you
won't become an instant programmer. You will learn how good pro-
grams are written, how to read and use them, and how to know them
when you see them. You will learn what can be taught about writing ad-
vanced software; what can't be taught you will have to acquire with ex-
perience. Neither this nor any other book can substitute for getting your
hands dirty writing programs.
One message is repeated over and over in the pages to follow: there is
no "best" way to write a program. The objective of good software is to
develop programs that do their jobs well. Programming that is optimal
for one application may not even be adequate for another. Users differ in
needs, experience, and taste; your programs have to fit all three. Remem-
ber your market as you write your program, and don't try to make it be all
things to all users. For example, the Word Machine was written to com-
plement conventional word processors in a different market. WordPro,
Word craft and the rest are superb programs with capabilities the Word
Machine does not duplicate. However, using those capabilities requires
study, practice, and commitment. They are fine for an office, for an oper-
ator who will use them hours per day. But Word Machine was aimed at
the home user, someone who will want to use the computer to help write
a letter, a book report, or even a book like this one. It was designed for
simplicity, especially for the occasional user. It cannot replace WordPro
in the office, but WordPro does not fit the needs of the amateur computer
user. When you build your software, it should be aimed at your user.
This book should help you hit that mark.
Professional software is written for both users and customers. The cus-
tomer is the person who needs the program's products; the user is the
4 1: Introduction
and offer some of the virtues of each. Since the principal difference be-
tween BASIC and the "professional" languages is cancelled by the avail-
ability of alternative compilers and interpreters, the operational choice
of language must come from other considerations.
Fundamentally, almost any problem can be programmed in any lan-
guage. However, some problems are better suited to one language than to
another. Spoken languages have similar properties; for example, the
sound of a poem in French may make its translation into German diffi-
cult. Similarly, FORTRAN is designed for mathematical computation; it
is less than ideal for creating business reports. COBOL is intended for
formatting files and print but is a poor choice for complex analysis.
BASIC trades off among the features of specialized languages. It's good
for almost all uses, but perfect for none. BASIC is a general-purpose lan-
guage and can be used easily for almost all needs. It can be used for con-
ventional and fast Fourier transformations which are almost impossible
for COBOL, and for banking systems that would tax FORTRAN to or past
its limits. The mathematical program might have been better in FOR-
TRAN, the banking system would have been easier in COBOL, but both
are possible-and operational-in BASIC.
In practice, the greatest drawback to coding in BASIC is that it has no
prestige. After all, it is basic, easy to learn, and at least partially compre-
hensible to the untrained programmer. A FORTH programmer has cre-
dentials established by the simple fact that she can use the language.
BASIC provides no such cachet of ability. If you want to write a program,
the difference doesn't matter; if you want to be recognized by the drop of
a buzzword, you need another language.
1.3 Interoperability
In general, a package of advanced software may be written for a specific
target machine or for any computer in a broad class. If you write for one
computer model, you may exploit all of its special features and may in-
corporate machine-language elements freely. There are two drawbacks
to that specificity: the code cannot easily be moved to another machine
for another customer, and you require a backup machine Ito handle hard-
ware failures) with exactly the same properties. On the other hand, if
your programs can be used on any machine in a family, you have a
broader market for them and easier backup than if you design for only one
target. When requirements permit it, interoperability has a high payoff.
There are many dimensions of variation among the Commodore comput-
ers: monochrome/color, 22/40/S0-column screen, 3/SI16/32164/96K
memory, BASIC 2.0/4.0, etc. Commodore has provided enough com-
6 1: Introduction
mode and top left of the window. Now we give 25 "q"s and SPC(39) to
get to the bottom right of the window, then "0" to set it, and "e" to print
in white on the 64. Finally, POKE59468,12 and the program will run in
40-column graphics on any Commodore host except a VIC.
Getting into literals mode is slightly more complex. The problem is
the extra spacing provided between lines on the windowed machines;
on a 4032, that hides the top and bottom lines. So, we need the com-
pressed screen that comes from graphics mode, combined with upper
and lower case. By using POKE59468,14 (which we need for the 2001 in
any case), we accomplish the desired effect on the windowed machines
as well. However, we haven't handled the 64. For that, we need to
POKE53272,PEEK(53272)OR2. (The nominal value in 53272 is 21 for
graphics and 23 for literals, but Commodore doesn't guarantee that those
numbers will work on all future hardware. They have assured us that the
OR operation will work, so the extra nine bytes are needed.) Lines 9020
and 9025 of SUPERLIST (Section 5.7) provide just that code.
On one expansion system for the VIC, giving 40 columns and extra
RAM, the logic works without modification. However, the VIC 20 locks
up on POKEs that are safe on other machines, and you will have to play
with any adapter you might want to use. The reason for not writing inter-
operable software for the VIC in the first place is that its limited screen
(22 characters) and small memory as supplied are too restrictive for other
uses. There are many adapters to get around the problems, but each is
likely to need unique code. In practice, cutting an 80-column screen to
40 will win few friends, but there are so many 40's around that writing
code for that mode makes sense. Advanced software can and should be
written for the VIC 20, and everything except interoperability applies to
that machine; in fact, the limited capabilities of the VIC increase the
payoff for top-quality programming. But a good program for an off-the-
shelf VIC will be poor for an expanded one or for one with 40 mono-
chrome columns; it will be even worse for a 4032 or a CBM. Therefore)
regard the VIC market as separate from that for the other machines, and
don't strive for interoperability between them.
writing for
2 the user
I
Advanced software is written for use-and for a user. Usually, your pro-
gram is intended for a class of user-novice, frequent user, occasional pro-
grammer, or serious programmer. A program that gives the first-time com-
puter user simple capability must be self-explanatory, must use simple
language, and will trade away sophisticated capabilities for clarity and
flashy display. One written for the advanced programmer may do without
in-line instructions, may use computer jargon, and may provide extra
capabilities that are hard to reach. If you are writing for the data-entry pro-
fessional, you may use elaborate special functions to provide fancy opera-
tions-your user will remember them because they are frequently used.
In writing for someone who will use the program only occasionally, it is
better to run a menu than to use special keys for special functions. A menu
is simply a screen of options and a request for the user to select among
them. One way to select is by a code numberj the menu assigns a number
to each option, and that's the way the operator reaches the function. That
method is easy to code, and it's necessary when you don't know the op-
tions until the program is run. For example, if the user will define the
categories into which a data item will be placed, selection of categories re-
quires numbering. The category names go into an array, and the index is
both the array pointer and the value used in categorizing the information.
Single-character input (see 3.6 Keyboard input) gives you an easy al-
ternative when the programmer defines the options. Use the initials of the
options for selection. Pick wording for them that is easy to recognize and
to remember. Pick an order for display that is easy for the user to scan.
Then put the initials into your working string in an order that's easy for
the program. When you're dealing with a nonprofessional user, always
adapt the program to your customer. Layout the screen, take inputs, and
provide outputs for easiest usej protect as much as possible against simple
9
10 2: writing for the User
errors. For the true novice, don't let anyone keystroke cause a catastro-
phe. There is no better indicator of poor thinking by the programmer than
code which bombs when an extra RETURN is pressed.
A general rule for all users is to protect against losing information in-
advertently. Before you let the user throwaway his carefully written file,
verify that that's what he wants to do. That means setting flags when
information has been created or changed and when it has been saved. If
he asks to input something new when information hasn't been saved,
provide a warning and ask for confirmation. Do the same when exiting
the program.
It is always worth memory to simplify the user's job. For example, the
logical order in which information is entered into a program may not be
the best order when the program runs. Even though you have to "waste"
variables to take the input out of order, do it. A common example occurs
when a program must do a lot of processing and substantial printing. If
you take the user's input for both operations at once, when the first begins
he can go off to do useful work, leaving the computer to do its part unat-
tended. An occasional check of the screen or the printer should indicate
that progress is being made and give an indication of how much longer it
will be before he needs to return. If you organize the program for your con-
venience, the user will have to sit around for minu tes or hours, just to hit a
key or two every so often. It may save you a few bytes of program, but it
won't win you friends-or repeat customers.
It is difficult to remember the user as you program. Take the time to
write down what the program will do before you start to code. Offer it to
your customer for review, comments, and approval. The most important
thing to tell her is what she will see and what she must do to get what
results. Talk through the special needs and special features of the prob-
lem, and make sure that you know what she needs before you start to
code.
Murphy's laws could have been written for computer users. The pro-
grammer must expect the unexpected and provide workarounds for
everything that can be anticipated. Among the unexpected events that
happen with regularity are: hitting the key next to the one wanted, power
transient, and disk fault. Requiring confirmation of critical inputs is part
of the solution to the keystroke problem. In other cases, you may require
confirmation of a set of data before saving, facilitate deletion of individual
characters, and provide easy editing of data already filed. You can't protect
against wrong-headed users, people who confirm what they mean to deny
or those who won't type an II a" to II Add" information, but insist on using
a lie" to "Create" it-regardless of the menu. But put in thought and code
to keep from penalizing the user for hitting the wrong key. There are pro-
grams pretending to teach children that feed back II shame on you" and
2.1 Program planning 11
puter itself. But even more critical to the long-term success of the program
is your anticipation of requirements not well defined when you start.
Changes are normal after programming begins, and painfully common
even after the system is working. If your file structures won't expand, you
won't be able to handle growing requirements. Your product will be
limited by your failure to think before you code.
A program consists of collections of lines of code. Good programming
practice breaks the total job into major blocks or modules, each of which
does just one part of the job. One reason for modularizing is that it
simplifies changing the design. Another is that it shortens checkout and
debugging. For example, one module may read files, another write them, a
third supply all formatted print. Then if the system grows to include 'a
second disk drive or if you change to a letter-quality printer, all corre-
sponding changes are in one place, and they can all be made as a unit.
Frequently, the major functions seen by the user correspond exactly to
the software modules. However, there may be logically different opera-
tions that are functionally similar and fit within a single module. For ex-
ample, creating a new record is logically identical with editing an old
one-the "old" values are simply nulls. To the user, creation and editing
are logically differentj to the program, they are the same. The converse can
also be true: logically, one might edit a record out of existencej function-
ally, deletion may be significantly different (e.g., requiring repositioning
of pointers, editing of correlative files).
Modularization is the process of blocking out the functional code to cor-
respond to what the program must do. A high-level flowchart is one way
to do it, showing the relationship between the user's logical operations
and the program's modules. Typically, there will be about six high-level
modules in a program, and they can be entered at line numbers which are
multiples of 1000. There should be no more than six routines within a
module, and each can usually be started at a line numbered as a multiple
of 100. Except where speed of execution dictates, all code used by a
module should lie within it. It may take a few more bytes of storage to col-
locate the code, but it is usually worth it in legibility of listings. One ex-
ception that can be made safely is exit from a function to wrap up opera-
tions or utilities. For example, exiting from a subroutine to the logic for
"Hit a key" can be done as a GOTO, rather than a GOSUB followed by
RETURN. Similarly, it may be simpler to use a common routine to close
files opened for different functions and to return control to the calling
function from the close-file logic.
Utilities are packages of code used in many different programs, perform-
ing functions common to many parts of each, and with few or no varia-
tions among applications. They are standardized subroutines for your
needs, and give you a library of programming tools almost as convenient
2.1 Program planning 13
When you standardize information, you can look at an old listing and
recognize immediately what you were doing (or trying to do) without hav-
ing to refer to your documentation. If you remember not to use your own
"reserved" words for other purposes in programs that don't need their
normal ones, you will find that a year-old program is almost as easy to
read as the one you finished yesterday.
yourself and to your user what that line is doing. There are only two cases
that justify a line that has only a REM:
entry to a major module
an option that doesn't justify a run-time question
One function that runs almost as slowly as user interface is the printer.
Typically, a dot-matrix printer will put out about one line per second. Pro-
gram delays from putting print logic in the 7000' s will hardly matter on
that scale. If you want the code to be interoperable (even with run-time
option on which printer is used), printing becomes a common routine
which is almost a utility.
Sticking to the idea of starting a module at a line number divisible by
1000, we still have six entries available without crowding. Typically, one
will be used for input from the disk, another for output to it. That leaves
four to do the program's work. You can start a menu at 1000 or less; put-
ting it at 100 is sometimes awkward, but it has worked well in a lot of
applications. It leaves space before 1000 for many quick options, for
checking flags (e.g., loss of data), or for updating standard information
like Subject or Title. In a word, the space between the menu and the first
application module is used for routines common to the start of several
user (menu) functions.
Once modules and special routines have been located in the program,
design within the module determines both clarity of code and speed of ex-
ecution. The code is easiest to read if it runs in a straight logical line. Un-
fortunately, the requirement for speed translates into optimizing the most
commonly used path. For example, we might want to check a disk input
for end-of-file. The clearest code might be:
10 GETH1,C$: IFST=oGOT03o
20 X$=X$+C$: GOT010o: REM END OF FILE
30 X$=X$+C$: GOT01o: REM ATTACH THE CHARACTER
That version does the job, but every character except the last takes two
GOTOs. If that routine is located at 10, the wasted time may not matter.
But if you put it at 6010, it will slow down disk access-even on a floppy.
Recode it to speed things up a little with:
10 GETH1,C$: IFST=oTHENX$=X$+C$: GOT01o
20 X$=X$+C$: GOT010o
It's shorter and faster-one GOTO instead of two. If you want still more
speed (and are willing to be obscure to get it), try:
Now there are no significant GOTO'sj the code will run as fast at 6010 as
at 10, and a novice programmer will have quite a struggle to figure out why
and how the routine works. The "best" organization of this part of the
program depends on:
2.3 Structured programmIng 17
by the call, can provide a question mark or not, and can get the answer to
a yes/no question-all by entering it at three different points. Structured,
the same job would take a collection of GOSUB's or GOTO's. A true be-
liever in structuring would ask for three GOSUB's to answer "y" or "n."
Another structuring rule that doesn't fit an interpretive language is to
avoid GOTO's. To do that, we would have to put initialization at the
front of the code, and pay a substantial speed penalty for the luxury.
Common, high-speed subroutines would have to be put at large line
numbers. The slowdown from this rule would make BASIC useless for a
lot of otherwise reasonable applications. Interestingly, the kind of
"train-of-thought" programming that goes with this rule is the sort that
weak BASIC programmers fall into. If you sit down at the computer
before you think, your first cut at the program is likely to have no
GOTO's at all. Of course, before you get it to crawl (it will probably
never run), you're likely to have a lot of them. Typically, a major omis-
sion resulting from failure to plan will take at least two GOTO's or their
equivalent GOSUB's for its patches.
The remaining fundamental rule of structuring deals with "computed
GOTO's." In BASIC, we use ON ... GOTO instead. The equivalent struc-
turing "rule" would be an explicit test before each ON ... GOTO (or
ON ... GOSUB) that the variable we branched on was in the legal range.
As long as the variable cannot be negative, the BASIC construct doesn't
need the test. ON ... GOTO has a built-in safety feature that we can of-
ten afford to use: if the variable is out of range, processing falls through
to the next command. So, all you would need to follow the rule is:
10 IFC> OTHENONCGOT0100,200,300
20 GOTObOOOO: REM ERROR TRAP
Strictly speaking, you should also check that lines 100, 200, and 300 all
exist. But you would have done that anyway, wouldn't you?
Structured programming started out as a structured process for devel-
oping good programs. The rules were originally illustrative and represen-
tative. It has degenerated into a set of absolute rules; the ideas have been
lost. In this text, the ideas have been imbedded because they are both log-
1cal and constructive to our purpose: writing good code. If you know what
to look for, you'll find the philosophy of structure development in Pro-
gram planning, Program documentation, and half a dozen other sections.
One topic not covered in this book is flowcharts-structured or not. If
you find flowcharting convenient, by all means do it. If you think it will
help you design, draw the charts before you code. If you think a flowchart
helps maintenance, draw them after. But the author's experience is that
flowcharts are optional. Data structuring is mandatory. Blocking your
code into modules and routines is usually enough to replace flowcharts.
2.4 Program documentation 19
each option and its results. For simple products, that kind of documenta-
tion may be sufficient. It is always necessary. However, the User Manual
written that way serves primarily for training. Its other function, as a ref-
erence, requires a different point of view. For training, you tell the user
what happens at each step; for reference, you tell him what steps are
needed for an objective. The User Manual must provide a map of the
functions of the program; when the terrain is complicated enough, rec-
ommended routes are needed to get the user from place to place.
Since the User Manual serves as your specification, its acceptance by
the customer (with input from the intended operator) constitutes your
approval to start coding. It is written to pin down exactly what the cus-
tomer will get when the job is finished. Since it is comprehensive, it will
necessarily include some technical depth. The draft approved at the start
of coding evolves in depth and detail as the program is built, but nothing
in its initial version should be changed except through coordination with
the customer.
Programmer documentation is usually identified as a Maintenance
Manual. It contains everything required for a programmer competent at
the required level to modify the code to serve new purposes or to correct
errors. This document is required for all operational code and for any
product sold, even if you intend to do all maintenance yourself. There are
two pressing reasons for thorough documentation of your own code:
1) Your customer must be able to maintain the product even if you be-
come unavailable.
2) You cannot expect to remember enough about your code a year or
five after writing it to modify it easily.
Maintenance documentation is written after programming is complete,
but comes from notes made throughout development. It includes a de-
scription of each variable used, detailed file concepts and realization, and
nearly line-by-line analysis of each program module. Use of REMarks in
the program is not a substitute for the Maintenance Manual; they serve as
markers for major blocks, but a substantive, interpreted program cannot af-
ford enough remarks to support maintenance requirements.
The one-page description for the customer should be polished prose
that you will be proud to send to potential customers. The User Manual
must be legible to the operator of the system and be in presentable form.
The Maintenance Manual may be hand scribbled if no one is to read it
except you, the author, but that approach carries a lot of risk. If your pro-
gram is written under contract, the Maintenance Manual really belongs
to your customer, whether anyone else will work with it or not. If you
sell a copy of what you have developed to someone else, either you must
make the Maintenance Manual available (for a fee, of course), or plan to
2.5 Testing 21
maintain the program yourself throughout its useful life. Otherwise, you
will hang your customer out to dry. That will cost you at least one cus-
tomer, and more if she makes her displeasure known. Don't count on
your customer to know enough to ask for the maintenance material, or
even to know what to do with it when you turn it over. Tell the customer
what it's for, and try to persuade her that keeping it is the only protection
against catastrophe.
The example of the Maintenance Manual is a good one to keep in mind
when you deal with your customers. In general, they are not computer
experts and certainly not as proficient as you are. They should look to you
for information on your products, and they will rely on your input-often
more than you do yourself. Make sure that each one understands every
step. Take time and care to be sure that her expectations and your plans
agree. Review the User Manual in draft with your user so that he knows
just what you're doing for and to him.
If you don't have a customer, documentation is even harder. You have
to play both customer and user yourself. When writing the one-page de-
scription, put yourself into the place of the businessperson or home-
owner who will buy the product. For the User Manual, try to be the com-
puter operator. In both, you must pretend that you know nothing about
programming. Without a real customer and user, you will have to act out
both roles if your documentation is to do what it must for your continued
success.
Maintenance documentation is the information required to correct er-
rors and to add capability after the program is finished. If maintenance is
someone else's responsibility, documentation should be at the level you
would want if you had to pick up someone else's program to do the equiv-
alent job. You may want the kind of detailed description provided for
SUPERLIST in Chapter 5, flowcharts, and data definitions. If you are to
maintain the software yourself, organize and complete your notes and re-
view them to be sure that they are sufficient for you to pick up where you
left off-even if it's a year or more later. Adequate documentation is bor-
ing, time consuming, and essential for a viable program. (Note that the
job isn't finished until the paperwork is done but that the program can
be used while you're completing maintenance documentation.) Finally,
and at the risk of stating the obvious, keep a copy of everything you de-
liver. You cannot count on your customer or your user to remember
where-or whether-your documentation is stored.
2.5 Testing
There is a truism in professional software that no programmer can test his
or her own code. You know what the program is supposed to do, and what
22 2: writing for the user
note on the same page everything you observe. Even if you pass the test,
you may notice something that strikes you as odd. It may be as simple as •
a job that runs slower than you thought it would. Every anomaly is a sig-
nal that something may be off the track you laid. Check it out before
your program is derailed. A problem your customer finds will cost you
ten times as much as one you catch yourself.
In addition to your formal tests, play around with the program. Don't
follow a set procedure, but hit keys at random to do something unplanned
-and to check out how the program handles errors. When an error is
found and fixed, take the time to repeat any test which used that code.
Leave time at the end of the process for a final step: a complete run-
through of every test case on your final product. That's the only way to
prove to your customer and to yourself that you're finished.
I Mechanics of
3 a program
I
An advanced program seldom stands alone. It is usually part of a package
of software that performs a set of functions. Typically, there will be a
program used to set up information for the system, another to perform
the regular operations, and at least one more for analysis of errors or
anomalies. The first and last categories require more operator skill than
that for day-to-day operationsj as the programmer, you may be their only
user. Those support programs need enough documentation for you or
your successor to operate them, but not the full package that goes to your
customer. Even after the system is sold off, you need to retain the sup-
port software for maintenance and extension.
Divide the system into programs based on the user's needs, not the
programmer's. If part of the system requires only reading data and should
be accessible to a true novice, put it into a separate program which never
writes to a critical file. Don't let the hacker foul up your data base. Daily
operations may require a second program, used by a professional who is
familiar with, but not expert in, the system's details. Exceptional cases,
especially writing key files, should be isolated in another program. Keep
dangerous weapons out of inexperienced hands.
A typical application program will take 8-10K of the 30-40K available
in the machine. If it's much bigger, execution is likely to be too slow for
good results-especially on the 64 with BASIC 2.0 garbage collection.
The rest of RAM will fill with data soon enough. Utilities and initializa-
tion will take about 3K, so each of the programs in the package will have
to fit in less than 8K. Since most of RAM is data, most effort to save
memory should be concentrated on the data structures. Saving memory
in your code is likely to have less payoff than the same amount of effort
optimizing data structures. Modularization will already have eliminated
most of the flab in coding.
25
26 3: Mechanics of a program
a line "2 A = B." And that is not what you meant at all.
BASIC knows where its program begins and has a marker for where it
ends. In the early generations of Commodore machines (PET and CBM),
BASIC always begins at decimal 1025 ($0401, using the conventional
"$" to indicate hexadecimal). Later machines have a pointer to a variable
start of BASIC; the interpreter reads it to find out where to begin. The
first two bytes of a line of BASIC are the address of the next line. The next
two are the current line number. To get the line number from the stored
value, first convert the hex to decimal (if necessary), then multiply the
second by 256 and add it to the first. For example, if your first line num-
ber were 300, peeking at 1027 would show 44 ($2C) and 1028 would show
1 ($01). When you type in a new line nnn, the interpreter scans all line
numbers to find the last one less than nnn and the first one greater than
nnn. If nnn is already there, it is replaced by the new one. If there is no
command on the line you enter (numbers only), then the line is null and
it is deleted from the program. If BASIC cannot interpret the line number
(< 0 or > 63999), a 1/ syntax error" is reported. When a line is inserted,
3.1 LIne numbers 27
changed, or deleted, all lines with higher numbers are moved to make
room without leaving empty space.
The first pair of bytes in a BASIC instruction is the absolute address in
memory of the next larger line number of the program. When the pro-
gram looks for a line number, it jumps from one line to the next by using
that pointer. The computer knows three important program addresses:
start of BASIC, start of the next line, and where it is currently executing
(within the current line). When it sees a GOTO command, the inter-
preter checks whether the high-order byte of the line number is greater
than that of the current line. If it's greater, then the interpreter starts
looking for the destination at the next instruction. Otherwise, it begins
at the start of BASIC. When it finds the line number you commanded, it
makes that the current line and continues execution at its first com-
mand. (If it can't find the number, you get the error message.)
The GOSUB command is similar to GOTO in its operation, except
that the computer remembers where it came from: the address of the next
command after the GOSUB is saved in a reserved area of memory called
the "stack." Since the stack is limited in size, it can have more informa-
tion supplied than it can hold. When that happens, you receive an "out of
memory error." In this case, that message doesn't mean that you have
used up all of the memory, only that you have exceeded the space allot-
ted to the stack. The limit on how deeply subroutines may "nest" (call
other subroutines) is established by the size of the stack. When a sub-
routine RETURNs, the address to which it goes is the one at the top of
the stack. In that process the return address is "popped" (removed) from
the stack. The space is then available for another subroutine call. If you
don't RETURN from the call (for example, if you GOTO an error routine
or directly to a menu), the addresses of the current call and all its un-
popped predecessors are left behind, wasting space. Do this a few times,
and the stack fills up-for an "out of memory error."
It turns out, then, that we know a lot about good programming in
BASIC just from looking carefully at line numbers and what they mean.
First, we know that we need to RETURN from each GOSUB and why we
may get "out of memory error" when there's still a lot of memory free.
Second, we have insight into making programs run faster. A substantial
program spends a lot of its time going to other locations. If we have it go
either to very early lines or to lines with larger high-order bytes, it will
get there more quickly. In a large program, from line 2540 (high-order
byte $09) you may GOTO 20 (high-order $00) or GOTO 2600 (high-order
$Oa) very much faster than you can GOTO 2550 or GOTO 2530 (both
with high-order address byte $09). As a side note, you can also see why
you may not want to renumber a finished program to save space: it could
slow things down!
28 3: Mechanics of a program
3.2 Commands
A BASIC command is a reserved word which is interpreted by the com-
puter to cause specific actions. Like a line number, a command is not
stored the way you type it in (or the way it is displayed when you LIST
the program). When you hit RETURN after typing in the line, the com-
puter scans the line for recognizable commands. Each is converted to a
"token" (a number between 128 and 255) for storage. Everything else is
tucked away in the form the interpreter finds: as unreversed characters
IASC < 128). Since commands cannot be in quotes, the meaning of a
token in quotes is different, and reserved command words in quotes are
not tokenized.
The exact word transformed into a token is important and is often a
source of confusion. Logic was not the controlling factor in defining
keywords (or BASIC commands). So, the command to provide a number
of spaces includes its left parenthesis (" SPC("), while most others that
need the parentheses (e.g., STR$, SIN) do not include them. In some
cases (e.g., FRE), reasons for seeming illogic may show up with time.
One anomaly in Commodore BASIC gives insight into the tokenizing
process. Within a REMark, tokens are not created. But the stored infor-
mation is displayed as though it had been tokenized. A shifted letter (a
capital in literals mode) is stored as a character whose value happens to
lie in the range of command tokens. For example, "A" is character
65 + 128 = 193. If you put that letter into a REMark, the 193 is stored
(one byte). When you LIST the line, BASIC translates character 193 into
its command, ATN. Now, if you put the cursor on that line and key
RETURN, the line will be reinterpreted. The three characters that form
"ATN" will be stored separately as three bytes. There are few applica-
tions where this process is useful, but it is worth exploring just to be sure
that you understand the token concept.
Another form of shorthand commands is useful in programming. One
case is well documented, the use of "?" for PRINT. The general case
stems from the fact that the BASIC interpreter recognizes a shifted
character as ending a command. Therefore, a shorthand form of most
commands can be entered by typing the first couple of letters with the
last capitalized. For example, the PRINT# command may be spelled out,
but its shorthand form is short and easy: pRo To get a disk directory with
two characters, use CATALOG in its shorthand form: cA. (DIRECTORY
takes three: diR. Shorthand for DIM is dI.)
Note that a line number used in a GOTO or GOSUB is not a command.
It is stored as a sequence of ASCII characters, one for each digit. If you're
trying to squeeze out the last few bytes of storage, remember that
RETURN takes only one byte, where GOTOlOOO takes five (six if you
3.3 Variables 29
put a space before the number). Colons and spaces used to increase
readability cost memoryj so do REMarks. They may be so important to
your purposes that you use them extensively, or you may minimize
them if you're running out of space. Of course, a compiled program
removes all nonexecutable material, including spaces and REMarks; if
you plan to compile, use those features freely to simplify maintenance.
To summarize, a line of BASIC begins with two bytes of the line
number followed by the two-byte address of the next instruction. Then
comes a sequence of commands (values> 127) and characters. The line
ends with a byte set to zero. The overhead to put aline into a program is five
bytes; the overhead to add the command on the same line (with a colon)
is just one byte. Figure 1 illustrates the storage of a line of SUPERLIST.
At the top is BASIC line 1060 as LISTed. By entering the monitor, we
find that it stored at $0739. The first two bytes ($52 $07) point to the
start of the next line at $0752. The next pair ($24 $04) are the line num-
ber, 1060 = 4*256 + 36. The next byte ($8b) is the IF command. $51 cor-
responds to "q," $46 to "f," the name of the variable "qf." $89 is GOTO
and the next four bytes are the target line number (1400) in ASCII. $3a is
the colon and $8f the REM command. The remaining bytes are the
REMark itself, including $20's for the spaces. The last character (at
$0751) is the $00 that marks the end of the line.
o 0741 31 34 30 30 3a 8f 20 49
0
o . 0749 4e 20 51 55 4f 54 45 53
o 0751 00 6e 07 38 04 8b 43 bl
0
0
3.3 Variables
A variable is a thing you want the computer to remember. It has three key
elements: its name, its address (location in memory), and its value. Your
30 3: Mechanics of a Program
the value is not stored twice. The same thing applies if you READ a value
from a DATA statement. Note that a string variable has the same naming
rules as the other types (ending with a "$") and that it is initialized as a
null string (zero length) when first encountered.
When the value of a string variable is assigned through an operator
(e.g., X$ = X$ + "A"L a place must be found to put it. Within the mem-
ory of the computer, space is taken first for BASIC's work space, then for
the program itself. In some machines, screen memory comes into the
picture as wellj in the older models, it is up above the rest of RAM. (The
screen never enters BASIC's "map " of memory significantly.) At the end
of the BASIC program, variable names and values are allotted (or names
and string addresses). When you modify a line of BASIC, you may move
the place where the variables startj that's why all variable definitions are
cancelled when you change the program. When a string is assigned
through an operation, its value is stored away in the space between the
top of variable definitions and the top of available RAM. In fact, the
strings are put in from the top down. When the next assignment would
overlap variable definitions, it is necessary to free up more space if possi-
ble. Since each assignment takes a fresh chunk of memory, most of the
top space is filled with outdated information. Those old values are II gar-
bage" to the programj the process of reassigning string memory to discard
them is called II garbage collection."
Under BASIC 2.0 (the version in VIC and the 64), that process takes a
noticeable timej BASIC 4.0 uses more memory to save that time. In ex-
treme cases (which you are unlikely to find in practice), 2.0 can take an
hour and a half to do a job handled in a fraction of a second by 4.0. How
frequently garbage collection occurs depends on how much memory is
used by the program and variable definitions and on how frequently
strings are assigned j how long it takes depends on how many strings have
to be collected. If garbage collection is a problem in your code, one method
for speeding it up may be to fool the computer. For example, first read in
all fixed data from a disk file, then POKE the value BASIC uses for top of
memory down under the space they use. When garbage is collected, the
space used by the external inputs will not be included, and substantial
time may be saved. Many other methods can be used (depending on the
computer you haveL but try first to live with the delays.
3.4 Arrays
An array is a collection of variables of any type which are distinguished
by number (ordinal) instead of by name. The principal reason for using an
array is to operate on its elements by calling them with a number, usu-
32 3: Mechanics of a Program
ally in a FOR ... NEXT loop. The rules for storing information in arrays
parallel those for simple variables except that values in an integer array
are stored as two bytes each instead of seven. That memory saving is the
principal reason for having integer variables and the other reason for us-
ing arrays at all.
The first time that an array name (a variable name followed by a left
parenthesis) is encountered, a dimension is assigned to it and memory is
allocated. If the first use is in a DIM statement, the dimensionality is
given by that statement; otherwise, it is given a default assignment of
eleven entries (0 through 10). Since memory is assigned to the array
when it is first encountered, redimensioning during execution would re-
quire moving all subsequent assignments. Such an operation would be
slow and would take substantial code, so it is not permitted (redimen-
sioned array error). Normal procedure is to use DIM statements in the
early part of initialization. Arrays are stored right after the program and
before simple variables.
If you define an array after defining most of your variables, all of the
variables have to be moved and time is wasted. That's usually not much
of a problem and neither is the ordering of the variables themselves. When
BASIC runs, each reference to a variable causes the list to be scanned for
that name. If the most-used variables were named early in execution, that
scan is faster than if they were named late. However, programs rarely use
enough different variables for the delay to be significant in practice; as-
signing variables early seldom saves enough time to be worth the effort.
Arrays may have up to four dimensions. BASIC st<:>res values and ad-
dresses in an order computed from the values of those dimensions. It ac-
cesses them by finding the start of the array, then computing the address
of the entry you want from the values in your reference. The amount of
storage required for an array is three bytes for its name, then two bytes
per entry for string or integer, and seven bytes for numeric. Remember
that there is always a 0 element to the array, so DIM X(5) means 6 ele-
ments of 7 bytes each; X% (4,2) means 5 x 3 = 15 elements of 2 bytes
each. Two-dimensional arrays are common in advanced programs, while
three and four dimensions are needed rarely. For example, a mailing-list
program might have one dimension for the fields within a record (last
name, first, street address, etc.)' and the other for the individuals whose
information is on file. Some mathematical problems do require four or
more dimensions; if you have one that exceeds what BASIC will handle,
it will probably be too big to store in a microcomputer anyway.
One special case that gives a bonus "dimension" occurs often and can
save memory. Suppose the information you need to access is always
representable as a positive integer less than 256. It is then also represent-
able as a single, legal character in BASIC, and you can map between the
3.5 Bits, bytes, characters, and numbers 33
number and the character by using the STR$ and ASC functions. Now
you can make one "dimension" of a complex array be position in a string
(maximum value, 255), and can put that string into an array which may
have up to four dimensions. Access the appropriate string in the usual
way, then access the extra dimension with the MID$ function (remem-
ber that ASC returns the ASCII of the first character but that it bombs on
a null string). You may want to use the string even when you have
enough dimensions available since it takes only one byte per entry (in-
stead of two or seven). But it is not recommended if you need to do much
arithmetic on the elements or if garbage collection delay is a problem.
This "trick" is of real but limited use. It has a side benefit since it is not
predimensioned. The size of the dimension is simply the current length
of that string, and may vary as the program runs. In some cases, that
memory saving alone may be worth the trouble of using string length as a
dummy dimension.
You will usually be asking a question when you are using the GET
package. So, just add
8090 PRINT"?";
and enter at 8090 to get the question mark and space. The most common
answers you will want are "y" or "n" j it's probably worth a line:
8050 X$="yn
Then GOSUB80S0 without having to define x$ for every call.
Another utility program of value is one which GETs a string of max-
imum length mj that takes about four lines using both 20-27 and
8100-8130. However, if you need to restrict characters in the string (for
example, eliminate delimiters such as comma and colon), you probably
can't afford the time to search x$ for each character. One solution is to use
an array of acceptable characters and a replacement for 20-27 that only
returns when the character fits. Note that the whole process is a variation
on the theme of using a string as an extra array dimension (see 3.4 Arrays).
Finally, it's worth noting here the very special properties of the shifted
space CHR$(160). It always prints as a space, but is not eliminated when
leading blanks are dropped (e.g., on INPUT and INPUT#). Therefore, it
can be the only character in a diskfile record, where you can still INPUT#
and yet have' 'nothing" in the field when printed. However, a shifted
space must not be used in a diskfile namej it does nasty things to the direc-
tory and may hide the file out of reach. Whenever you specify acceptable
characters, give an extra thought to 160.
uses the conditional IF and GOTO in 20. Of course, the second example
takes more code and an extra line. It also needs a GOTO on each itera-
tion. Late in a large program, that will run very slowly. The FOR ...
NEXT loop saves the location of the command after FOR (and STEP if
used) on the same stack that remembers where subroutine calls come
from. So, when the NEXT test passes, the loop jumps directly to the ap-
propriate command-no searching for line numbers, no wasted time.
When the loop completes (i > 9), the calling information is popped from
the stack and execution proceeds.
Any of three mechanisms will keep the stack clean:
complete the loop
reuse the index as an index
RETURN from a subroutine that included the loop
the NEXT really doesn't clarify things very much and does take signifi-
cant time-BASIC has to look up the index to verify that you named the
right one instead of just incrementing the one that's there. When the loop
takes several lines, the extra legibility provided by the name of the index
is usually worth the time. In addition, having the computer check that
you are incrementing the right index is worthwhile during checkout of
the program-it can help you avoid very weird problems that you might
be unable to recognize otherwise. If you are using nested loops, remem-
ber that NEXTj, i is convenient, takes one less byte than NEXTj:NEXTi,
and does exactly the same thing.
characters. From 11 bytes after start-of-BASIC you are free to have a REM
of, say, 60 bytes. Replace them with your machine code by POKEs at any
point in the program. That's ample room to do a lot of good machine-
language work-exclusive OR, case conversion to true ASCII, or other
functions that are faster enough than BASIC to be worth the trouble. For an
example of embedded machine language in practice, see Section 5.8 SYS
CHECKSUM.
Two commands interface BASIC to the machine-language routine: SYS
and USR. SYS is, effectively, a subroutine call to its argument. Any vari-
ables to be input to that routine must have been POKEd into appropriate
(and safe) locations before the SYS; those returned must be PEEKed from
safe spots. Normally, you would use SYS when there are no arguments
required. For example, a SYS is used to extend BASIC with machine
language in programs like WEDGE and BASIC AID.
USR is a subroutine jump to a reserved area of RAM ($00 on PET and
CBM), where you have three bytes to jump to your routine. Its argument
is a value sent to and returned from the floating-point accumulator, not
the address. USR may be convenient when you want to call a single rou-
tine frequently with a variable argument, but its use of the floating-point
accumulator makes the machine code awkward for simple applications.
The lack of a true RESET on Commodore machines makes machine
language more of a risk than on other computers. If you POKE around in
memory, you may lock up the whole system to the point where you
must shut off power and start over. (On some machines, you can do
physical damage as well; video circuitry may not survive the speedup
that can be POKEd into the logic.) Within a BASIC program, a small
piece of machine code can save time and confound meddlers.
ble unless you unlink them. (What the LOAD causes is not a RUN, but a
jump to the start of BASIC-in effect, a GOTO the first line of the new
program. If the load failed for any reason, it will cause reexecution of the
old program without a CLR.) If the new program is larger than the old or
has an early CLR, variables will be cancelled before it begins to execute.
Chaining programs by LOADing the second from the first was a fre-
quently used device in smaller machines (e.g., 2001-S), especially when
you could not assume a disk. On larger modem systems, it is better to put
more into each program. If chaining is still needed, holding the linking
data on disk is preferable to relying on their staying accessible in memory.
For example, a bad program file could lose the work of its predecessors in
the chain. Putting data on disk and executing a CLR when the next pro-
gram begins is safer. An extra benefit is that bringing in a smaller pro-
gram without a CLR does not release the memory between the end of the
new program and the end of the old. A CLR before the LOAD or at the
start of initialization makes all of memory accessible.
code and effort. They are often relatively hard to learn and may be beyond
the skill of the novice programmer. Often, that's just as well since they
can be hard to understand from a listing. This section deals with two com-
mands, DEF and ON, which are worth the trouble to learn and use.
DEF is a means by which you can extend BASIC using BASIC itself. It
allows you to DEFine a unary numeric function which you can then call
from any point in the program. In other words, you can replace a subrou-
tine that computes one number from another with a user-defined function.
An example of the use of DEF is in converting a number to a character.
The ASCII function does the job, but with two disadvantages: small
numbers are unprintable characters, so are hard to verify, and some num-
bers correspond to delimiters which cannot be read with INPUT# com-
mands. Many applications need to pack integers from 0 through, say, 200
into single characters to save storage. If we remember how to count in
hex, we can extend the sequence: 0, I, ... ,9, A, B, C, ... , chr$(255).
The algorithm is simply:
x$=chr$(4S+x) ifx<lO
x$ = chr$(55 + x) if x>9
We cannot put an IF test into a DEF statement, but we can use another valu-
able "trick"-evaluation of Boolean expressions. Every logical expression
is evaluated to "true" or "false/, and those are given numeric values.
44 3: Mechanics of a Program
47
48 4: Devices
does the PRINT#'s and CLOSEs the file will do so to whatever device you
are using. Using this feature will demonstrate graphically that PRINT and
PRINT# are functionally the same operationi output to the screen will be
independent of which command is used to talk to it.
On the PET and CBM, hitting RETURN in response to an INPUT
prompt drops the system out of BASIC. In the newer machines, it leaves
the value of the INPUT variable unchanged. In either case, a program
might be better off with other results. One way to change the operation of
INPUT on a null string is to use ~NPUT# instead. Open a file to the key-
board and INPUT# from that file. As with screen operations, the keyboard
is a device, and file operations may be performed on it in any logical way.
Of course, an attempt to print to the keyboard [or to input from the screen)
can give you a file error ("not an input file" or "not an output file") if you
opened it properly and accessed it wrong. And you can expect a long wait
for input from the screen or for anything to show up that was PRINTed to
the keyboard, but the system will do what you command, whether it is
logical or not. Note that devices 0 through 3 are preassigned and usually
cannot be reassigned.
When you send the same information to different devices (or receive it
from different devices), there may be differences because of the nature of
the device. That's a complicated way of saying that getting literals on a
printer takes a different protocol from printing them to the screen-and
that you can't print them to the disk at all. On the disk, CHR$(193) is
just CHR$( 193). On the printer, it is either a spade or a capital A, depend-
ing on what went before it. On the screen, it is either a spade or a capital
A, depending on the value in one memory location-or, on the VIC or 64,
depending on the values in an area of memory.
While the Commodore peripherals are smart, your program has to
complement their intelligence by following the protocols they require.
For example, if you want to see literals instead of graphics, you must use
the appropriate POKE for the screen. You may either use SA7 or cursor-
down on every line to the printer. The question is meaningless on disk or
tape. The code that opens the file to the device should set up all condi-
tions required for its protocoli thereafter, the routine that communicates
neither knows nor cares which one you were using. For example, if you
are sending output to a dot-matrix printer or the screen, you may define a
paging string consisting of the CLR character and the HOME character.
By defining it as CHR$(12) for the 8300, you may also run with a letter-
quality printer. Preset the paging string when output is begun, and let the
print routine use it without caring which it is. [It would be nice to use all
three characters in the string and be independent of the choice of printer,
but you can't. The 8023 printer will accept either paging command i given
both, it puts out an extra page. And the 8300 prints CLR as "3"!)
4.1 Un-conventions 49
4.1 Un-conventions
To bring order out of computer chaos, international conventions have
been established. They represent protocols accepted over many years,
some dating from the most primitive teletype machines, and are accepted
by all manufacturers of computer hardware and peripherals. Except
Commodore.
When Hewlett-Packard started making computers, they wanted an ef-
50 4: Devices
fici ent , high-speed interface with their peripherals. They devised a buss
system which came to be known as HPIB. Other manufacturers devel-
oped peripherals to work with the HPIB, and to reduce publicity for
Hewlett-Packard they called it the GPIB. The Institute of Electrical and
Electronic Engineers embodied the GPIB in its standard, IEEE 488. Key
properties of the buss include its requirement for intelligence on both
ends (to manage the protocol), its high speed (suitable for even hard
disks), and its implementation by many vendors.
Given the low cost of microprocessors, Commodore was wise in se-
lecting the CPIB for its computers. Unfortunately, they chose to econo-
mize by omitting some parts of ~he protocol that were not needed in the
earliest machines. As a result, Commodore peripherals will not simply
plug into IEEE 488 busses, and Commodore computers will not run all
GPIB peripherals. Correction of the problem of interoperability is well
documented, and requires both hardware and software-for you to buy
and to build, respectively.
The situation with the RS-232C serial buss is somewhat different. In
the VIC 20, Commodore has implemented all of the required logic, but
saved substantial cost by not putting in the extra power supply for its
plus-and-minus twelve volts. Since the adapter to change signal level can
be as simple as an inverter, they provided inverted logic as well. The 64
behaves the same way, except for its inherent timing problem. The Com-
modore 64 looks into memory before refreshing the screen. It must do
that in order to handle relocation of screen memory. Unfortunately,
stealing that glance takes CPU time that is given the highest priority in
the interrupt structure. As a result, clocks are given lower priority and
do not run with their customary regularity. That affects all busses in the
system. The intelligence of an IEEE interface unit can handle the prob-
lem, but the serial busses have more difficulty. Since the RS-232C buss
demands precise timing, designers are hard at work trying to build inter-
face hardware that will work consistently in the face of the 64's inconsis-
tency. (That same problem may be part of the reason for difficulties with
the serial peripherals from Commodore.)
One standard that no one but Commodore violates is ASCII-the code
converting between characters and numbers. In computer terms, it's an-
cient, venerable, and respected by (almost) all. Commodore's logic for
changing it stemmed from their incorporation of literals mode in all ma-
chines. Since they expected users to want upper- and lowercase charac-
ters in their programs, they recognized that it was easier to use the key-
board as a typewriter.
In ASCII, the unshifted characters are uppercase; in Commodore, they
are lowercase. The ASCII standard is not complete, and there are varia-
tions in its special characters among manufacturers. If your external de-
4.2 Printers 51
vices want true ASCII, you must convert to their requirements from
those that Commodore supplies. In some cases, you may simply map
characters 65-90 into 193-218 and vice versa. The logic is a little easier if
you mimic the printers by putting out the wrong case for characters
91-95 too, but in either case all you need to do is XOR the character from
the computer with 128 to get the ASCII value. If your peripherals need
special characters, check their translation against Commodore's docu-
mentation and implement what you need.
Whatever reasons Commodore has had for varying from international
standards, your software and systems may have to correct for their re-
sults. Commodore stays consistent within its own lines and really hasn't
been concerned with whether you have problems tying in to others'.
Other manufacturers use other approaches. For example, Apple expected
you to plug in boards and chips from other vendors for what you need, and
even to use an outside source for lowercase characters. No one fully im-
plements all the protocols for all the different lines of printers, but some
manufacturers come closer than Commodore. Like Apple, Commodore
has chosen a path that supports a substantial cottage industry in periph-
eral adapters. Unfortunately, each such adapter is likely to require its
own software. Interoperability of your programs will suffer from that
variability. One routine you are almost certain to need for telecommuni-
cations is ASCII conversion for upper- and lowercase letters. Otherwise,
plan to adapt your software to each installation, with its unique hardware
and interfaces.
4.2 printers
Commodore distributes a wide range of printers manufactured by other
companies. The two designed for the serial buss of the VIC and the 64 are
the 1515 and the 1525; they differ in paper size (the 1525 takes standard
width), in noise level, and in that the 1515 will not run correctly with
the 64.
The range of printers on the IEEE buss is substantial. The original 2022
and 2023 required an upgrade ROM for best performance. Commodore
provided the ROM without charge, and embedded only one time bomb
for the programmer: paging logic was changed without warning and with-
out documentation. The later dot-matrix printers are quieter, faster, and
more costly. Their firmware is superior in that they more reliably per-
form the functions associated with secondary addresses than the earlier
machines could manage. The 8300 letter-quality machine is a Diablo
printer interfaced with a CMC adapter. Both are competent, but the
adapter requires support for its edge connector. The combination is effec-
52 4: Devices
has to format the disk, it will take several minutes at each initialization.
(You can detect whether the disk is already formatted and avoid wasting
the time. The easy way is to try to open the directory to read. If you suc-
ceed, you need not reformat the disk. That step will also detect the
wrong format, except that it won't catch a 2040 disk as an error; to do
that, you'll have to read the format code at the end of the first directory
line. Since the 2040 format is obsolete, you may be able to ignore the
problem.)
Considering their price and the quality of the competition, Commo-
dore's drives are quite good. The single drives for both IEEE and serial
busses are generally less reliable and less sturdily built than the duals.
The IEEE version performs well when properly set up, but its setup is dif-
ficult and it has a disturbing tendency to lose adjustment. Upgrade
boards are technically available, but whether they cure the problems is
not yet proved. The serial buss machines have firmware problems that
have not been isolated at this writing. The 4040 dual drive is highly reli-
able and not too demanding on the disk itself. The 8050 comes from one
of two manufacturers. The one with a 4040-style door is relatively slow,
but has proved reliable; the one with a 1541-type door is quite fast but
has had a painful history of failure. Both 8050's use higher density than
the 4040, so demand even more of the disk itself. The 8250 double-sided
drive offers little advantage for its higher cost; its performance is much
the same as that of the 8050. The problem with the 8250 is that its cost
approaches that of a hard disk, while its performance and capacity are still
in the range of the floppies. There may be applications where the 8250 is
the right answer, but more often you will either need to use a hard disk or
be able to get by with the readily available, better-known 8050 or 4040.
Commodore's hard disks are just becoming available as this text is be-
ing written. They should be given at least six months in the field for de-
bugging before you design advanced software around them. They are to
be fully compatible with the logic of the floppies (which is common to all
current units), so you should be able to design your programs for an 8050
and upgrade to hard disk without change.
The major advantage of hard disk is not its increased storage-although
that is significant to many applications. The big payoff is in speed of ac-
cess to information. The disk spins faster and has higher bit density than
a floppy, so data are pumped in more rapidly. In addition, the time to find
the data is decreased by the head arrangement. The hard disk is fast
enough to allow the luxury of storing a directory to your files on the disk
itself; you can make an indirect access to information faster on the hard
disk than you can a direct one on a floppy. (And if you aren't accessing the
disk continuously, you get a side benefit since the hard disk doesn't have
to spin up to speed before occasional reads.) While your floppy-based pro-
4.4 Disk flies 55
gram will run better and faster on the hard disk, you won't get all of its
advantages until you design your programs to exploit it.
Ideally, it is used for records which are all of constant (or nearly constant)
length, and which are to be accessed by ordinal. They are perfect comple-
ments to sequential files, since they are ideal when they are very large
and require access to individual records for read and write. Unfortu-
nately, it often happens that the file you need fits neither extreme; it may
be of variable-size records which need individual replacement. Then you
must choose between the evils, and spend either a lot of disk or a lot of
time using either relative or sequential types, respectively.
A little creativity can save a lot of grief. Suppose that you need to keep
a lot of information about a number of people. We might use a sequential
file of names, reading it in (and writing it out if necessary) as a whole.
The order of the name in the file (its ordinal) is the number of the relative
record of information about that person. Suppose the people are stu-
dents and that we need a full school year's record for each one, with one
character for each of 200 days. The longest name may be 30 bytes, but
they may average 14. So the memory-resident file for 500 students is
only about 7K. The disk-resident data file is 100,000 characters long, but
that's okay; we only look at one student at a time, so we only need 200
bytes of memory. We can manipulate the names in memory, then fetch
the data record we need, manipulate it, and put it back where it belongs.
The only time we would have to write the name file out again is when we
have changed it-a rare event for this type of application.
Note that we can work around many of the limitations of the file types
by combining their virtues and cancelling their faults. Sequential and
relative files are complementary, and most advanced software will need
both. Where the trade-off in algorithms is usually as simple as speed ver-
sus memory, in disk files it is usually among speed, computer memory,
and disk memory. And even the speed question is complex since there
are designs that take more time to initialize and less to run than alterna-
tives. (A simple instance is between reading the whole file in once and
manipulating it in memory, compared with modifying it on the disk.
Each modification will take multiple disk accesses and a lot of time com-
pared with shuffling memory, but you don't have the long delay to load
the file in the first time. There is no general rule for designing the file
structure. Pick the architecture that fits your application.)
rest of the data system. Since the Log is the first file on a blank disk, it is
likely to survive any anomalous disk behavior. Your backup procedures
should allow repetition of the merge process; any failure is unlikely to re-
peat. The disk with historical data should not be written at all until suc-
cessful merger has been verified. Verification may be as complex as
checking the block count by reading the directory; if you're going to that
much trouble, take the time for a COLLECT first.
Except for a bug, the BASIC 4.0 command HEADER is equivalent to
sending a NEW to the command channel. The undocumented error is
that a variable cannot be used for the disk id in HEADER; if you are going
to format the disk within your program, you must revert to BASIC 2.0.
One factor to consider in promoting both program speed and drive reli-
ability is head movement. When one drive is accessing two files concur-
rently, the head must be moved between them. That takes substantial
time and wears the mechanism. If you are running on a dual drive, COpy
files between sides, CONCAT from one side to the other, and generally
avoid performing concurrent operations (e.g., read and write) on one
drive when you can use both.
Secure processing on disk is tedious, but the user may not be troubled
by the process. Merging is the last operation of the day, and requires no
manual input when it succeeds. Even if the process takes hours to com-
plete, it can be effected by running the system overnight. On the rare oc-
casions when it fails, recovery can be the first job in the morning. Since
computer time is essentially free, good program design will yield a suc-
cessful product even when disk performance is marginal.
Directory confusion may be eliminated thanks to a suggestion by Mike
Louder. The method is to validate the disk (COLLECT in 4.0) before any
scratch, rename, or other erasure from the directory. The drawback of
validation is that it is very slow, taking several minutes on a full disk.
However, you may be forced to take the time penalty if your code has
trouble with disk errors. Since the Commodore drives have enough intel-
ligence to validate without computer intervention, you may send the
command early and have it implemented in the drive while the com-
puter is doing useful work. For example, a program which reads in a file
for modification expects to scratch and rewrite it later. Therefore, you
may want to COLLECT the disk it came from after reading the file, rather
than waiting until it's time to scratch it.
4.6 Tokens
A token is a single item that replaces a collection of others-in the way
that a subway token replaces a handful of change. In the BASIC inter-
preter, the nine characters of DIRECTORY are replaced by chr$(218). In-
60 4: Devices
terpreted as ASCII, that token would be a liZ," but as a BASIC 4.0 com-
mand it means DIRECTORY. The reason that the shorthand form "diR"
works is that finding a capital letter causes the interpreter to seek a suit-
able token for the part of the command already input. If you had entered
"dI," the list of commands would be searched, and the first one that fit,
DIM, chr$(134), would be selected.
A data-intensive program may also benefit from tokenizing under your
control. Suppose you use a system for converting the 200 most common
entries in a field into single-character tokens. One way is with the nu-
meric packing and unpacking functions (see 3.5 Bits, bytes, characters,
and numbers) that create characters you can INPUT. If you have ten
thousand classical recordings to file, the 200 composers most commonly
found probably account for 9,990 of them. To handle the other 10, save
one token to mean: this one is spelled out. If the average composer's
name takes seven characters, you replace seven with one in 9990 cases,
seven with eight in the other 10. Your primary file's composer informa-
tion then shrinks from 70,000 characters to 10,080. However, you need a
secondary file, a "directory," of the names to which the tokens point;
that one is 1400 characters long (7*200). Since it is a separate file, its size
may not be a problem; in any event, the net saving in storage is about a
factor of six.
A little more storage is saved in a real system. The composer field of
the untokenized file requires a character to identify its end, a "delimiter. II
It marks the end of a field which may be anywhere from two to, say,
twenty characters long. Considering the delimiter, the file would then
have 90,000 characters. But for the 9,990 items that are tokenized, the
field is fixed at one character, and needs no delimiter; only the 10 excep-
tions need to specify their length. Then tokenizing reduces that field's
size from 90,000 to 10,090, or total storage to 11,490.
Accessing the tokenized file requires packing, unpacking, and conver-
sion between numbers and characters. Therefore, using it will take
longer than using the simple one. As usual, saving memory costs time.
Since disk access is usually slower than computer operations, you can
limit the slowdown to acceptable levels. Count on thinking about your
design for several extra hours if you're going to tokenize effectively.
One thing you must resolve at the start of planning a tokenized file is
where to keep the directory. For maximum savings, it can be held as a se-
quential file on disk and read into memory at initialization. That will
take a chunk of precious RAM. However, using the file will be a lot faster
than if you access the directory on disk. If you simply can't afford to
spend the RAM, then try making the directory a relative file. With a
floppy, the time for the extra access can become painful; your program
will have to avoid reading any directory entries it can, and still may not
4.7 Using files 61
be fast enough. Of course, if you're using a hard disk you have enough
speed to keep the directory out there. One solution is to design the sys-
tem with the directory on disk, build it with the floppy, then invest in
the hard disk after everything is running as you wish. On the other hand,
with all the extra storage on the hard disk you may not need the space-
saving that tokens offer.
Tokenizing a hard disk file pays off in other ways, depending on how
you plan to extract records. For rapid access to information, you will need
pointers for many fields to lead you from record to record. To get that ca-
pability, you will need a directory for each chained file. Tokenizing them
costs little and saves much. (Notice that in many ways a tokenized field
is easier to maintain. For example, spelling can be modified in one place
for all records. Without tokens, each record with the old spelling would
have to be found, corrected, and replaced.)
ber, which is what we had in mind. And the routine is its own reciprocal
-since it just swaps two digits, it recovers the original by swapping
them back again. Just by using the swapping function, we cut storage
from 900 records to 200.
The next thing to look at is the names of the entries themselves. On
average, there are about 3 area codes per state. Therefore, we store each
state name three times. There are about 60 states, provinces, and other
areas to which codes are assigned. Suppose we build a file of those
names, hold it in memory, and then keep in our file of area codes only the
number of that entry. When we initialize the program, we read the state
names into an array from either DATA statements in the program or a se-
quential file on the disk. (We could use a second relative file on the disk,
but that would slow things down. On a 1541 drive, we would have to
keep opening and closing the two files, which would be particularly slow
and would stress the drive.) We now save the 60 names once, then have
200 pointers to them in the relative file. If we kept those pointers on
disk, we might pack them with fnp and fnu, giving us a one-char,acter
record j in memory, we might prefer to use them as integers (two bytes
each) and save the packing and unpacking time, Depending on the appli-
cation, we'll be dealing with about 700 bytes of storage instead of 7 -12K.
There is yet a final step we could use in packing the data into the
system. We could put the pointers (the number of the state or province in
our list) into a single-character string. Since the string is less than 256
characters long, it is legal. However, some of its characters cannot simply
be typed from the keyboard, so the string may have to be pieced together.
By now, the logic has gotten a lot more complicated, timing is a little
slower, and storage is a lot less. In the system with a 900-record relative
file, the logic was:
input the area code
send the code as the RECORD#
INPUT# the name
trouble. But in many cases, the time to input the three digits is likely to be
longer than all that logic, so it won't be noticed. And there is a sort of law
in programming that the problem will always expand to fill memory, so it
is usually worth at least some of your effort to save space. Even in this
simple problem, we have taken a program that would require a disk or at
least 16K of computer and rewritten it to run in an unexpanded VIC 20
from cassette. At the other extreme, if we had a hard disk, we wouldn't
worry so much about storage space, access time, or opening several
relative files. If we were trying this simple problem on such a big system,
brute force would be a logical way to do the job.
through the CPU to the other. Merging into a large catalogue is the sort of
job one gives the computer to run overnight.
It is necessary to be able to search any file if you ever need to look inside
a field. To retrieve a record by anything on which you have not sorted, you
must search the whole file-whether it is sorted on other fields or not. The
less you know about how a file will be used, the less likely you are to set it
up correctly. Then you will have to build it all over again when you know
what sorting your customer really needs. Make sure that your file package
will support special searches. The odds are good that you will need one
someday on even the most completely sorted file.
Terminology in data management is more confused than in any other
aspect of microcomputing. Consider a collection of data files and their
associated lists (files) of sort "keys." It is reasonable to consider a "file
manager" to be a package of code which supports the maintenance of a
single data file and its correlated lists. Whether it is highly sophisticated or
simplistic, easy to use or demanding on the programmer, as long as it
works one data file at a time, it is a file manager.
A data base is a collection of data files with their individual and collec-
tive lists. A II data base manager" will keep the multiple files within the
data base coordinated with each other and keep their lists linked as re-
quired. Whatever the package of commercial software may be, the chance
that it is a data base manager is small. Software to coordinate multiple
data files is complex and demands more of the computer and disk systems
than most micros can manage. Even as simple a task as correlating a
mailing-list file with data on the accounts it contains is beyond the
capability of the commercial packages examined to date.
Sort keys may be kept on disk or in memory. On disk, access is slow but
storage is cheap. In memory, you need RAM to save time. Note that order-
ing may be on a hierarchy of fields. H the primary field has multiple en-
tries with the same value, they may be ordered by a secondary field, then
by a third, fourth, etc.
H a file has the following characteristics, you should sort it.
It is to be accessed by several fields.
You know from the beginning how it will be used.
It will fit comfortably on the disk, even with:
relative field overhead;
sort-key overhead.
It is to be updated on line.
How the file is to be updated is a key to designing it right. A sorted file is
best written one record at a time. The operator will intersperse entries
with retrievals, and will tend the system as it does its work. Each addition
to a sorted file takes many key accesses and significant time-usually
4.8 Sorts and searches 65
enough to be noticed, but not enough to take a coffee break. If there are
fifty updates at a time, the sort system is hard on the operator. A catalogue
is updated in batch, with plenty of time for coffee-or lunch. But the
operator need not watch the computerj all entries are made at once and the
machine will digest them as a group. So part of the choice of system
should be based on how it will be run.
The most important single factor in choosing the file structure is the
software. For a catalogue, you must build your own, start to finish. That
will take time, but give you complete control. For a sorted structure, you
can start from any commercial package. Your selection among them
should be based on your ability to interface the programs you are writing
with the files it supports. In practical terms, your program(s) must be able
to pull information from the files that their software maintains. Ideally,
the file manager should come with routines that tie your program to its
files and services. If it doesn't have the routines, you'll have to invent
them. Since the package seldom comes with enough information to do the
job easily, you may spend more time tying in to a purchased product than
you would building your own.
Before setting out to build a file manager, plan on spending substantial
time learni.ng the techniques. Commodore's Name Machine uses a very
simple catalogue ordered by a single field. After you understand how it
works, you might modify it for a secondary sort key of first name. Then
two entries with the same last name will be alphabetized by first name.
The next step is to discard cassette capability and to convert the file the
program now manipulates in memory into a relative file on disk with a
single ordering key. If you get through that stage, try a second key with ZIP
code first (that's necessary for the large files that a business requires). After
those exercises, you should be ready to design and build your very own file
manager. If you did a good job, that last exercise may be marketable in its
own right, especially if you tie it in to other useful software the way that the
Word and Name Machines are linked.
•
I SUPERLIST an
5 Tool Example & a
The first item on the wish list is clear enough: those who use Com-
modore listings all the time know what reverse q's and s's mean. But
they are hard to read from a dot-matrix printer, require reverse printing
(impossible on letter-quality), a:nd communicate little or nothing to
those unfamiliar with the specialized jargon. So, let's convert reverse q
into something like c-d, reverse e into wht, and so on. In the process,
we'll get a listing that can be put out on a letter-quality printer. In a
word, it will be suitable for publication.
The second wish is a little less obvious. When you know who calls a
line, you get several useful data. First, if you want to discard a line, you
had better be sure that nothing is trying to use it (GOSUB or GOTO).
Next, if there are many calls to a line, you may want to make it con-
spicuous-say by numbering it x 500. Third, routines that are used a lot
are worth extra effort to optimize. When memory is very tight, you may
even want to save some by moving a small routine that's called often
into the two-digit area; it will save a couple of bytes each call. But the
most important use is when the program goes wrong. What you often
67
68 5: SUPERLIST-an Example & a Tool
need to know is: how did it ever get here? The line-number cross refer-
ence is often the only way to answer that question.
Variables need cross reference even more than line numbers. Do you
use a variable thirty times? Then make sure it's only one character long.
What working variables are available to add a feature in a routine? Check
the cross reference. And when your program goes sour, you can figure out
what code could have clobbered your pointer by looking at all of its
references.
The jargon for a number which characterizes a program is "checksum."
Its essential property is that two programs with identical checksums are
very likely to be the same. To say almost the same thing in another way,
it is unlikely that a change to the program will leave the number the
same. Then assume that you have a simple program which computes
the checksum. Run it against two programs to find out if they are
(probably) the same. The more complex the checksum, the less likely it
is that it will miss a program change. On the other hand, the more com-
plex it is, the slower it runs and the less likely it is to be used. A practical
compromise for microcomputer software is a simple, exclusive OR of the
bytes-as long as sabotage is not suspected. Nothing less than byte-for-
byte comparison will guarantee that two programs are identical, but bar-
ring a deliberate effort to make undetectable changes, eight bits should
do the job well.
The need for SUPERLIST became apparent as soon as advanced soft-
ware was attempted in BASIC. Most of its functions are handled by the
compiler for FORTRAN or COBOL; they are much more difficult in
languages such as FORTH. In the late 70's, the need for SUPERLIST
emerged when coding the 8K PET. The version presented here has been
recoded completely to include BASIC 4.0 and color commands. It il-
lustrates many of the features of advanced software as developed in the
earlier chapters. The program is both an example of advanced software
and a tool to help you develop advanced software for yourself. One way
to demonstrate what it does is in Section 5.7 SUPERLISTing-it has been
run on itself.
page), r$ (chr$( 13)), a convenient base number (bs = 2 t 15 - 1), and a user-
defined function. The function is called "fnx" and provides an exclusive
OR (XOR), a function omitted from BASIC but needed for our checksum.
Note that the XOR function itself is binary (two arguments), but can be
used here as unary (one argument), since one (s) is constant in all applica-
tions. That argument will be the eight-bit checksum that characterizes the
program.
To set format at 40 columns and literals mode, 9020 and the start of
9030 should be familiar (see 1.3 Interoperability). The rest of 9030 clears
the screen for input of initialization data. A string of 39 spaces (b$) is
generated in 9050, which also opens the disk command file. A dateline
(dt$ 9060) is input using the line-in utility at 8200.
Program planning recognized that we had to translate stored bytes from
the program into special characters (within quotes) and commands. Two
string arrays are dimensioned for the purpose: q$ within quotes, k$ for
commands (9070). The ninety commands available through BASIC 4.0
are contained in DATA statements (10000-10060) and read into k$ at
9070. To translate nonprintable characters, data are read from 10070-
10100 into the appropriate parts of q$ at 9080; since the //pi" character
prints differently on different printers, chr$(255) is entered separately.
Other characters will print as their ASCII values when needed. Note that
the DATA statements include values for all machines; only two have dif-
ferent meanings among the machines, and they are entered in their
VIC/64 incarnations, not those for CBM.
Now that all predefined data are established, we set a working variable
(x 9100) to the amount of FREe memory. We save about lK for working
space, and dynamically allocate the rest to arrays for storing the results of
our analysis. The information we want to save is the name of a variable or
a line number, and the lines at which it is referenced. For printout, we
need up to 5 characters for each field. Therefore, we could squeeze 13 col-
umns of information onto an 80-column page (leaving a blank space be-
tween fields), but that would be very crowded. For a clean display, we'll
use 11 columns per page-the name of the referenced item and up to ten
references per line of printout. We had to solve the print question first in
order to design the arrays. The problem is to have enough elements to han-
dle even a very large program in a computer of moderate size. What we
will use is one string array (z$) for the names of the referenced items, and
an integer array (z%) dimensioned ten wide for the references themselves.
Each reference then has up to 6 bytes for its name plus 2 for its address plus
twenty for the references to it. To provide a small cushion for garbage col-
lection (expanded VIC and, especially, 64), we model the array as needing
30 bytes; strictly, 28 is enough, but the few extra elements are not likely
to be worth the push. Subtracting 31 elements from the arrays assures us
70 5: SUPERLIST-an Example & a Tool
of 930 bytes for working storage. (It looks like 32 elements, but remember
the zero index.) Finally, we exit to the main entry point of the program,
line 100.
It is arguable whether we should name the target program in initializa-
tion, carrying the logic in the 9000's through line 170 or even 190. The
question is only a matter of taste and judgement, and is of little practical
importance. Breaking it here puts most of the one-time operations at high
line numbers where they belong. If you wanted to modify the program to
permit superlisting more than one program per initialization, the entry is
at the right place. If you try that, you will discover that the delay to
reinitialize the arrays is so great that it won't be worth the effort.
Two routines are assigned low line numbers for speed of execution.
Lines 20-27 GET a single character from the keyboard. Entered at 20, the
buffer is emptied (20) before the character (c$) is awaited (25), then re-
turned with its ASCII value (c). Entry at 25 allows the buffer to hold
characters typed in while earlier ones are being digested.
The users of the character-input logic are "Hit a key" (8010), yes/no
logic entered at 8050, single-character input (8100-not needed in
SUPERLIST), and line-in logic (8200). The logic from 8080 through 8095
is one way to require a RETURN to accept single-character input. It allows
the option of deleting the character, but accepts no keys except RETURN
and DEL.
Line input (8200-8290) uses character input to permit commas and co-
lons (precluded by INPUT). A line must be ended with a RETURN which
is tested at 8220. A deletion from a non-null string is handled in 8230.
Along with all other nonprintable characters, DELete of a null string is
rejected in 8250. The quote mark is rejected at 8260. The longest input
string is 18 characters: a drive number, a colon, and up to 16 characters of
program name. (The dateline is arbitrarily limited to the same length.) If
the string is less than 18 characters long (8270), an acceptable character is
printed and tacked onto the working string (x$). If the string is already its
full size, the program continues to wait for a RETURN or DEL (8280).
5.3 starting a SUPERLIST 71
Two other routines approach the status of utilities. The one that out-
puts a line and ejects a page (7900-7960) keeps count of lines, then pages
as required or on a call. A prefix string (s$) is attached to the print string
(p$) to set case and (if you code it that way) to set the left margin. The
string is printed and nulled (7920), the line counter (1) is incremented, and
the routine returns if the page is not yet full. If all allotted lines have been
printed, enough null lines are printed to provide bottom and top margins
(7950). That is also the entry for a forced page eject; by testing for I
(implicitly, > 0), we avoid ejecting a blank page if we have just finished a
page of text when the call comes.
The other semi-utility is the routine (30-34) that reads in two bytes,
XORs them into the checksum, and creates a number from them for
memory address or line number. Note that the logic is complicated by the
fact that the ASC function bombs on a null string, while the disk returns a
null string for a character zero. This routine really doesn't need the high
speed it's given, but it is as logical to put it here as anywhere else-and the
speed doesn't hurt. Along the same lines, if you didn't use buffering of in-
put in the line-in logic (8200), you would have to give it smaller line
numbers to avoid typing too fast for processing.
quote is one of many cases where syntax errors could be caught but aren't.
A line:
20 a=b"
will be a syntax error since the variable (b") is illegal. But it can be stored,
and SUPERLIST will not flag it. If you wish, you can expand SUPERLIST
to check for all sorts of syntax errors instead of having to run test cases-in
that way, you could accomplish many of the verification functions of a
compiler.
Information in quotes is processed separately (1060), as are commands
(1080). The code in the 1100's processes unshifted characters not in
quotes or remarks. If the line flag (If) is set and the current character is
numeric, the character is tacked onto the string (z$ 1100). If the character
is not numeric, line-reference is ended by calling 1600 (1110); other than
for a syntax error, this handles the cases of comma (ON ... eOTO) and a
variable after THEN. If a variable is currently flagged (1130) and the cur-
rent character is alphameric, it is tacked on. Although a variable usually
ends with a command or end of line, it can be terminated by a comma or
right parenthesis; they are handled by calling 1500 (1140). The variable
flag (vf) is set (1150) if the character is alpha and no flag is set. Finally
(1160), a colon resets the data flag (df), since that is one way to end a
DATA statement. (Note that only the end of a line terminates a REMark.)
Within quotes (1400-1420), the string from the quote array (q$) is
assigned to c$ for printing. Ending a variable (1500-1520) begins by chop-
ping off extra characters (1510) if there were more than two, then tacks on
the current character if it is part of the variable ($, %, or left parenthesis).
If it was $ or %, then a left parenthesis is also added if it is next on the line
(1520). The rest of the processing matches that for variables (1600-1690).
Since the line flag (If) was set when the leading command was given, in-
tervening spaces may occur on a line number. They are handled in
1600-essentially by ignoring them. A null line number is possible after
THEN; in that case (1610), the flag is reset before exiting. This would also
be a good point at which to check for legality of line number (val(z$) <
64000) if you wish. Now a counter (q) is used instead of a flag to identify
one of three conditions: a new line of ten entries is found for the reference
(q = 0); an old line has room for it (q = 9); or there is no place leftto put it
(q = 5). The counter is initialized (1620) and a loop initiated (index j)
among the references already encountered (z$ array). If we get through the
used entries without success, we use anew one (1620). If we find the name
in the list (1630) with space for the entry, we use that. If there is no room
(1640), we report the problem and continue with the rest of the work. In
1650, the program finds the first available place to put the entry. The
reference that is stored (z% 1660) is complex. An integer array can count
74 5: SUPERLIST-an Example & a Tool
from about -32K to +32K. Line numbers range from 0 through 63999, so
they need to be massaged before insertion. The easy way would be simply
to subtract a suitable number, like 32767, from the line number and store
the result. The problem is that that would let you store a 0 (for a true line
32767). That's the value when the array is initialized, so it could not be
detected easily. Therefore, we bias the value by adding 1 (subtracting -1)
if the stored value is not negative.
The last major operation to end reference processing is to reset the line-
number flag (1680) if the current character is not a comma (remember
ON ... GOTO and ON ... GOSUB). Finally, the variable flag is always
reset and the reference string cleared before the return (1690).
Processing a command is surprisingly simple. If the variable flag was
set, the variable is wrapped UPi if the line flag was set, the line number is
wrapped up. Then the current character is replaced by the string in the
command array (k$). Note that using ON ... GOSUB allows all this and
more to be done in one line (171 0). If the command is GOTO, RUN,
GOSUB, or THEN, the line flag (1£) is set. If the command is DATA, the
data flag (df) is set. Finally, if the command is a remark, we set the re-
mark flag (rf 1740).
At this point (1800), we have completed processing that byte, and c$
has the information to be added to the print string. If there's no room to at-
tach c$ and keep an 80-character print line, the existing line is printed (and
nulled) by calling 7900. The contribution from the current byte is at-
tached (1810) before the next byte is processed i when the line is finished,
variables and lines are wrapped up if needed, the remaining print line is
output, and the next line is called for by moving back to 1000.
that led to the bug in formatting a disk with a variable id.) When a file is
OPENed under 2.0, all the special information needed is put in quotes, as
though it were part of the name. DOPEN the same file (BASIC 4.0), and
you put that information into the line itself. As a result, one can find
spurious "variables" such as "d1," "w," and "1123" in a SUPERLIST. In a
HEADER command, you can generate a spurious "ix4" if you format the
disk within the program. The solution is straightforward: a BASIC 4.0
flag, set on any appropriate command, suppressing variables except
within parentheses, and cleared at the next command.
LIST presents an anomaly in its use of line numbers. LIST 123-456 is
not SUPERLISTed as having line references at all. There are two reasons
for that: neither 123 nor 456 is really a line number, and LIST does not
belong in finished code. The numbers following LIST and the dash are
bounds for line numbers; if the numbers don't exist, no error is generated.
Consequently, omitting the logic for the LIST command is a good design
decision, not simply a convenience.
Commodore offers a set of special characters which are not
SUPERLISTed. They are the graphics characters which do not convert to
uppercase letters; on a PET, they include shifted numerals and punctua-
tion. On CBM, they are not accessible from the keyboard at all. On a 64,
they are reached with the Commodore key. Since there is no obvious nota-
tion for those characters, the version of SUPERLIST shown here will print
question marks for them. If you have a preferred notation, just enter it in
the appropriate DATA statements.
Since SUPERLIST is in BASIC, you have the option to supply the miss-
ing logic for any or all of the above. If your code is not interoperable, you
will probably want to use BASIC 4.0. Then add the flag and the logic to
do the job you want. Trap GO TO if it's worth the effort; figure a solution
for LIST if you think that that would be worthwhile. But recognize that
those changes will slow the program further and will enlarge it too.
When you have finished with your own version of SUPERLIST, you
may want to compile it for your primary computer. In BASIC, it runs
slowly. Most of the time is spent finding places to put variables and line
numbers. A large program may take 30 minutes or more to SUPERLIST
in BASIC but be limited by the printer after compilation-to 5 minutes
or so. If you plan to compile, then the extra time required to handle the
special cases above won't be significant.
5.7 SUPERLISTing
SUPERLIST was modified before it was mn to make the listing in this
section. The changes indicate what you may want to do on rare occasion,
5.7 SUPERLISTing 77
or an option you might want to build in for yourself. The difference be-
tween this listing and an "ordinary" one is that this was intended for
publication.
Getting wide margins is simple enough. Left margin is set by adding
spaces to s$ in 130. The right margin is set indirectly-by changing the
number of characters per line (set at 80 in 1800). To get 6-character
margins, add six blanks to s$ and set the line limit at 68 in 1800. Of
course, references can still take 66 characters. The 8300 printer has a
number of features that will surprise you if you're familiar with the dot-
matrix variety. The one of immediate interest is that its underscore
character is not the shifted" $" but the left arrow. The shifted $" prints
/I
as "$." We can't just put the correction into the q$ array since ,it would
appear in brackets. This listing was generated by adding the line:
1415IFC=164THENC$="
followed by a left arrow. Note that the arrow simply isn't on the print
wheel; you can't get there from here. Since it can only appear legally in
quotes, you could add it to the q$ array. The same thing could be done for
other special characters, and you could build a version of SUPERLIST
just for formal printing on the 8300.
Some problems with printers have no practical solutions. The easiest
demonstration is the character" pi," which can appear on a program line.
It looks like a command and in some senses behaves like one. If you put a
special trap in the command logic (1700' s), you will slow down the pro-
gram significantly. For most purposes, the slowdown isn't worth the
small problem of interpolating a pi. If you disagree, write your own
version.
There are some obvious variations on the theme of SUPERLIST that are
included on the disk you will get on returning the card. CHECKSUM is a
fast and simple program that computes an 8-digit checksum matching the
one from SUPERLIST. It will run on a VIC if you remove the formatting.
PROGRAM COMPARE reports the line numbers that differ between two
programs; it is useful in identifying different versions when they have
different checksums. SUPERLIST takes about 20 minutes to SUPERLIST
itself. It is CHECKSUMmed in about 2, and can be compared with
another version in about 6. If you want to compile CHECKSUM or
SUPERLIST, note that some compilers won't handle the dynamic dimen-
sioning used in those programs. A compiled SUPERLIST can be limited
in speed by the printer-it should run about five times as fast as the inter-
preted version.
78 5: SUPERLIST-an Example & a Tool
superlist Operational
10 goto9000:rem (c) 1982 by m richter 90064
20 getc$:ifc$goto20:rem empty buffer
25 getc$: ifc$thenprint" <c-1>";: c=asc (c$) : return
27 print" <c-l>";:goto25
30 get#l, c$: x=O: 1£c$>" "thenx=asc (c$) : s=fnx (x)
32 get#l, c$: c=O: ifc$> ''''thenc=asc (c$) : x=x+256*c: s=fnx (c)
34 return
100 closel:print"<clr>","<rvs> SUPERLIST ":rem main entry
110 s=O:print"<c-d>Program name?":gosub8200:pr$=x$
120 openl ,8,0,pr$:input#8,x,x$:ifxthenprintx$:gosub8000:gotol00
130 print"<c-d>Graphics printout";:gosub8050:s$="":ifithens$="
<c-d>
160 ifmid$(pr$,2,1 )=":"thenpr$=mid$(pr$;3)
170 ifpr$=""thenprint"<c-d>The program needs a name":gosub8000:
gotol00
180 open4,4:print#4,s$pr$left$(b$,40-len(pr$»"<c-d>"dt$:print#4:
1=2
190 print"<c-d><rvs>current line<c-d>":gosub30:m=x:rem start of b
asic
1000 rem process a line
1010 gosub30:ifx<1024goto2000:rem end
1020 n=m:m=x: gosub30: In=x: qf=O: df=O: rf=O: vf=O: If=O: p$=str$ (In)+"
":z$="
1030 print"<c-u>"ln:x$="":fori=n+4tom-2:get#1 ,c$:x$=x$+c$:next:
get#l ,c$:rem line in
1040 fori=ltolen(x$):c=asc(mid$(x$,i»:s=fnx(c):c$=chr$(c):ifrf
goto1800:rem rem
1050 ifc=34thenqf=1-qf:goto1800:rem quote mark
1060 ifqfgoto1400:rem in quotes
1080 ifc>127goto1700:rem command
1100 iflfthenifc>47andc<58thenz$=z$+c$:goto1800:rem add digit
1110 iflfthengosub1600:rem end line
1130 ifvfthenif(c>47andc<58)or(c>64andc<91 )thenz$=z$+c$:goto1800:
l"em add char.
1140 ifvfthengosub1500:rem end variable
1150 ifc>64andc<91thenvf=1-df:z$=c$
1160 ifc=58thendf=0:rem colon
1170 goto1800
1400 rem within quotes
1410 ifq$(c»""thenc$="<"+q$(c)+">
1420 goto1800
1500 rem end of variable
1510 z$=left$(z$,2):ifc=36orc=37orc=40thenz$=z$+c$:rem $ ~ (
1520 if(c=36orc=37)andmid$(x$,i+l ,1)="("thenz$=z$+"(
1600 ifc=32andz$= .... thenreturn:rem leading space in line reference
1610 ifz$=""goto1680
1620 q=5:forj=Otov:ifz$(j)=""thenz$(j)=z$:q=0:k=j:j=v:rem new slo
t
1630 ifz$(j)=z$andz~(j,9)=Othenq=9:k=j:j=v:l"em old slot
1640 nextj: ifq=5thenprint"<rvs> File full <c-u>": goto1680
1650 forj=Oto9:ifz~(k,j)=Othenq=j:j=9
1660 nextj:z~(k,q)=ln-bs-(ln>=bs)
1680 ifc<>44thenlf=0:rem not comma
1690 vf=O:z$="":return
1700 rem command
1710 onvfgosub1500:onlfgosub1600:c$=k$(c-128)
1720 ifc=137orc=138orc=1416rc=167thenlf=1 :z$="
1730 ifc=131thendf=1 :rem data
5.7 SUPERLISTlng 79
9020 print"<clr><grph><home><home><tset><c-d><c-d><c-d><c-d><c-d>
<c-d><c-d><c-d><c-d><c-d><c-d><c-d><c-d><c-d><c-d><c-d><c-d><c-d>
<0-d><c-d><c-d><c-d><c-d><c-d><c-d>"spc(39) "<bset><wht >":poke59468
,14: rem format
9030 poke53272,2orpeek(53272):pr1nt"<clr>","<rvs> SUPERLIST "
9050 bS-" ":b$=bS+bS+bS:open8,8,15
9060 pr1nt"<c-d>Date11ne?":gosub8200:dtS=x$
9070 dimk$(127),q$(255):for1=Oto90:readk$(i):next
80 5: SUPERLIST-an Example & a Tool
Line-number references
100 120 170 9190
1000 1810
1400 1060
1500 1140 1710 1810
1600 1110 1710 1810
1680 1610 1640
1700 1080
1800 1040 1050 1100 1130 1170 1420
20 20 8010 8070
2000 1010
2040 2090
25 27 8220
2500 2050
2900 2010 2060
30 190 1010 1020
7900 1800 1810 2010 2010 2090 2900 2900
7950 2510 2510 2900
8000 120 170
8050 1;0 2510
8070 8070 8090
8080 8080 8095
8200 110 9060
8210 8210
8220 8250 8260 8280
9000 10
Variable references
b$ 180 2005 2005 2070 2080 9050 9050 9050 9050 9050
bs 1660 1660 2080 9010
c 25 32 32 32 32 1040 1040 1040 1050 1080
c 1100 1100 1130 1130 1130 1130 1150 1150 1160 1410
c 1410 1510 1510 1510 1520 1520 1600 1680 1710 1720
5.8 SYS CHECKSUM 81
The logic for providing the XOR function is trivial in machine code. The
6502 family, like most microprocessors, offers the needed instruction
(EOR) in its set of primitives, with several addressing modes. The pro-
gram POKEs the value to be XOR'd into the byte following the EOR-
immediate instruction ($49). The rest of the "routine" is simply loading
the checksum into the accumulator before executing the EOR, then stor-
ing the accumulator into the checksum's location. By putting the
checksum into a blank cell of the screen, we have a visible display of the
program's progress. We could put that display wherever we wished. Plac-
ing it one pixel right of the top left corner of the screen is convenient. Put-
ting it into the corner would be a problem: BASIC line 10 would have two
bytes = 0 (the low-order of the checksum address). If you LISTed the pro-
gram after RUNning it, you would find strange line numbers, which
would be confusing. BASIC would still run correctly, since it uses the
next-instruction address instead of looking for the end-of-line marker.
Still, the O's would be inconvenient.
For the 64, the machine code is:
LDA $0401
EOR #($1016)
STA $0401
RTS
We can identify the type Gf computer by noting that start of BASIC on the
PET or CBM coincides with the start of the normal screen position on the
64. Our programs control both what's in BASIC and what's on the screen.
In initialization, we have cleared the screen (9020) before we look at
1035 (9040). On the 64, we'll find 160, a reversed space. On PET or CBM,
we'll find 143, the first REM in line 10. Next (9050), we read into the nine
bytes following that REM the machine-language XOR program, then open
5.8 SYS CHECKSUM 83
the command channel to the disk and set s to the checksum site for 64,
and offset 1 to the location of the routine. If 1 is < 2000 (actually, 1040L
we reset the checksum site and POKE for literals on PET/CBM (9060).
Note that initialization may POKE any value after the EOR; the program
puts in the ASCII of each character as we get it. However, if we initialize to
0, the line would not list correctly until reading began. (We only POKE in
values for non-null characters, so we will never have a 0 after processing
starts.)
Since it only takes two lines to process the whole file, they're put right
up at the top of the program (12-14). Of course, we don't have to XOR a 0
into the checksum. On the other hand, we want to look for three con-
secutive O,s to find the end of the program. Line 12 handles both jobs
quickly. In 14, the ASCII value of the current character is POKEd into its
location (lL then the routine is called with SYS(s). It really is that easy.
We could have used the USR function, but that requires moving the
argument from the floating-point accumulator in memory to the pro-
cessor,s accumulator register. That means extra code and another location
to select depending on the computer in use. And it offers no advantage
over SYS in this case. There are more alternative designs in a typical
machine-language routine than in its BASIC equivalent, and selecting
among them is seldom as simple as choosing SYS over USR in this pro-
gram. The 20% speedup that machine language gives in CHECKSUM
would probably not be worth the time to write the extra code if the pro-
gram's purpose were simply to compute a checksum. The effort is
justified by its other function-as an example of machine code embedded
in a BASIC program.
eys checksum Operational
10 got09000:rem :rem (c) 19S2 by m richter 90064
12 get#1 ,c$:ifc$=""thenget#1 ,c$:ifc$=""thenget#1 ,c$:ifc$=""then
close1 :return
14 pokel,asc(c$):sys(x):goto12
20 getcS:ifc$goto20:rem empty buffer
25 getcS:ifc$thenprint" (c-l>";:c=asc(cS):return
27 print" (c-l>";:goto25
100 closeT:print"(clr>","(rvs> CHECKSUM ":rem entry
110 print"(c-d>Program name?":gosubS200:pr$=x$
120 openl ,8,0,pr$:input#8,x,x$:ifxthenprintx$:gosub8000:gotol00
160 ifmid$(pr$,2,1 )=":"thenpr$=midS(pr$,3)
170 ifprS=""thenprint"<c-d>The program needs a name":gosubSOOO:
goto100
1000 rem the works
1010 x=1-4:pokes,0:gosub12:print"<c-d>Checksum="peek(s)
1030 print"<c-d>Another program";:gosubS050:ifithenprint"<c-d>Goo
cbye!":end
1040 goto100
8000 rem utilities
8010 print"<c-d><rvs> Hit a key to continue";:gosub20:print:print
"<clr>":return
84 5: SUPERLIST-an Example & a Tool
8050 x$="yn
8060 print"? ";
8070 gosub20:fori=1tolen(x$):ifc$<>mid$(x$,i,1 )thennext:goto8070
8075 i=i-1 :printc$;
8080 getw$:ifw$=""thenprint" <c-l>"; :goto8080
8085 print" <c-l>"; :ifw$=chr"f(13)thenprint:return
8090 ifw$=chr$(20)thenprintw$;:goto8070
8095 goto8080
8200 x$="":rem input a string
8210 getc$:ifc$goto8210
8220 gosub25:ifc=13thenprint:return
8230 ifc=20andx$>""thenprintc$;:x$=left$(x$,len(x$)-1 ):rem delete
8250 if(127andc)<32goto8220:rem cursor control characters
8260 ifc=34goto8220:rem ~uote
8270 iflen(x$)<18thenprintc$;:x$=x$+c$
8280 goto8220
9000 rem initialize
9020 print"<clr><grph><home><home><tset><c-d><c-d><c-d><c-d><c-d>
<c-d><c-d><c-d><c-d><c-d><c-d><c-d><c-d><c-d><c-d><c-d><c-d><c-d>
<c-d><c-d><c-d><c-d><c-d><c-d><c-d>"spc(39) "<bset><wht >":poke53272
,2orpeek(53272)
9040 1=1024:ifpeek(1+11 )<>143thenl=4096:rem 1035 in basic/screen
on pet/64
9050 fori=1+12tol+20:readc:pokei,c:next:open8,8,15:s=1025:1=1+16
9060 ifl<2000thenpokel-2,128:pokel+3,128:poke59468,14:s=32769:rem
for 64
9090 goto100
10000 data173,1,4,73,1,141,1,4,96:rem Ida & eor # sta & rts (&)
CHECKSUM: 167
Line-number references
100 120 170 1040 9090
12 14 1010
20 20 8010 8070
25 27 8220
8000 120 170
8050 1030
8070 8070 8090
8080 8080 8095
8200 110
8210 8210
8220 8250 8260 8280
9000 10
Variable references
c 25 8220 8230 8250 8260 9050 9050
c$ 12 12 12 12 12 12 14 20 20 25
c$ 25 25 8070 8075 8210 8210 8230 8270 8270
i 1030 8070 8070 8075 8075 9050 9050
1 14 1010 9040 9040 9040 9050 9050 9050 9050 9060
1 9060 9060
pr$ 110 120 160 160 160 170
s 1010 1010 9050 9060
",$ 8080 8080 8085 8090 8090
x 14 120 120 1010
x$ 110 120 120 8050 8070 8070 8200 8230 8230 8230
x$ 8230 8270 8270 8270
5.9 TURTLEWALK 85
5.9 TURTLEWALK
Advanced software in BASIC is used for many purposes: business, games,
education-any programming application. Where SUPERLIST is an ex-
ample for software development and the following chapter deals with a
business application, a few pages are worthwhile for an educational pro-
gram that borders on a game.
The concept of a program is difficult to teach. The idea is an abstraction,
like a "function" in algebra, and is not self-evident as apples, lines of
BASIC, or other tangibles are. Most modem languages are ill suited to
teaching the concept, however easy they are to use when the idea is
understood. PILOT is one language in which the idea comes through easi-
ly, but it has few applications. Let's construct a "language" in BASIC that
teaches the idea in a PILOT-like way. We'll introduce the concept and
make it useful and entertaining. We don't want a language that resembles
BASIC or FORTRAN, since we want to talk about only the ideas of pro-
gramming, not a particular implementation. We need a few simple
"instructions" that will be understandable to anyone. We'll use a
minimum of jargon to build a language and to give the user some exercise
with it.
TURTLEW ALK uses a figure for a "turtle" that has a recognizable direc-
tion. It has "instructions" to point it in the cardinal directions, to move it
forward, and to locate it on a screen which may be cleared. Of the many
types of programming language, we'll use macros. Like a user-defined
function, a macro is an expression in the language which can be used as a
command.
Implementing TURTLEWALK is simple enough. The instructions are
extensive, and just about fill the 9000's after brief initialization. Since we
are trying only to teach the concepts, we don't need any real computing
power. As a result, we can limit the system to ten "programs" and can ex-
pand each one to its primitives. If we wanted to build a language for use,
we would allow more macros and would save memory by deferring their
expansion to execution time. That program would run slower, but would
still be acceptable to the user. The program might interpret the macro as a
number when the assignment was recognized, then execute the numbered
routine when it was run. Since BASIC does not offer direct access to the
stack, nesting would have to be limited. More to the point, special code
would be needed to prevent infinite loops. The language might be "better"
that way, but not in terms of its purpose.
The TURTLEWALK language has elements of many standard ones, in-
cluding BASIC, PILOT, FORTRAN, and FORTH. It is artificial and has no
other application. The only reason for its existence is TURTLEW ALK, so
there is no point to extending or expanding it. Consequently, the language
86 5: SUPERLIST-an Example & a Tool
and the program are built for the one purpose, and should be evaluated
only in terms of how well they accomplish it. TURTLEWALK might be
the first program of a series teaching BASIC, FORTRAN, or any other
language, or even teaching the role of the computer in society.
turtlewalk Operational
10 goto9QOO:rem m richter
20 getc$:ifc$goto20
22 getc$:lfc$=""thenprint" <c-1>"; :goto22
24 c=asc(c$):print" <c-l>"T:return
30 x$="neswfchq":ifp$="1/thenreturn:rem execute p$
32 forkzltolen(p$):c$=mld$(p$.k.l):gosub8110:ifj>6thenk=256
34 ifj=50rj=6thenxs l :y=l :ifj=5thenprint"<clr><c-d> I/;:rem clear h
ome
36 ifj=4thengosub8200
38 ifj<4thend~j
39 printe$t$(d);:nextk:return
40 return
50 getc$:lfc$goto50
52 getc$:ifc$=1/"goto52:rem no cursor
54 return
100 print"<clr><c-d> <rvs> turtle"
110 print"<c-d>type in your command or your program
120 print"<c-d>ending with <c/r>.<c-d>
130 a=-1:tl$="":gosub8300
140 ifasc(x$)=39gotol000:rem ? assign?
150 ifx$=""goto2000:rem wrapup
160 c=asc(x$):ifc=39goto300:rem macro
165 ifc=32thenprlnt" ";:x$=mid$(x$,2):goto150:rem space is ok
170 ifk%(c)=Ogoto500:rem invalid
180 iflen(p$)<255thenc$=chr$(c):printc$;:p$=p$+c$:x$=mid$(x$.2):
goto150
190 print"<rvs>A":print"<c-d>too much to remember! ":gosub8000:
gotol00
300 rem program name
310 j=0:forl=2tolen(x$):ifasc(mid$(x$.i))=39thenj=i:i=256
320 next:ifj<3goto500
330 y$=mid$(x$.2.j-2):x$=mid$(x$,j+l)
340 j=-l :fori=Oto9:ifp$(0,i)=y$thenj=i:i=10
350 next:ifj<Ogoto500
360 iflen(p$)+len(p$(l,j))>255goto190
370 print"'"y$"''';:p$=p$+p$(l .j):goto150
500 rem invalid character
510 print"<rvs>A":print"<c-d>try again":gosub8000:gotol00
1000 rem test assignment
1010 jsO:forls2tolen(x$):ifasc(mld$(x$.i))=39thenj=1:i=256
1020 next:ifj=Ogoto500:rem no next'
1030 ifmld$(x$.j+l .1)<>I/-"goto330:rem in-line
1100 rem effect assignment
1110 y$=mid$(x$,2,j-2):1fy$ .. ""thenprint"<c-d>it needs a name":
goto1190
1120 x$amid$(x$,j+2):ifx$.""thenprlnt"<c-d>1 can't remember nothl
ng!":gotol190
1130 j=-l :fori=Oto9:ifp$(0,i)~""orp$(0,1)=y$thenp$(0,1)=y$:j=i:1~
9
1140 next:ifj<Othenprlnt"<c-d>l can't remember any IIlore!":gotol19
o
1150 aaj :print"' "y$"'=";: goto150
5.9 TURTLEWALK 87
1190 gosub8000:goto100
2000 rem wrapup
2010 ifa--1thenp$-"h"+p$:gosub8040:got0100:rem direot
2020 p$(1 ,a)-p$:goto100
2500 rem effeot assignment
8000 rem utilities
8010 pr1nt"<c-d><rvs> please touch a key to cont1nue";:got020
8040 gosub30:gosub9990:print:print"<c-d><c-d>do it again";:gosub8
050:1!j=Ogot08040
8045 return
8050 print"? "j:x$="yn
8100 gosub20
8110 fori=1 tolen (x$) : ifc$=mid$ (x$, i, 1 ) thenj = i-1 : i=256
8120 next:i!i<257got08100
8130 return
8200 printe$:ifd=Otheny=y+(y>1)
8210 ifd=1thenx=x-(x<38)
8220 ifd=2theny=y-(y<2;)
8230 ifd=3thenx=x+(x>1)
8240 printleft$(d$,y+1 )spc(x)j:return
8300 x$="~:rem input a l1ne
8310 getcS:ifcSgot08310:rem olear buffer
8320 gosub22:1fc=13thenprint:return
8330 1fc=20andx$>""thenxS=left$(x$,len(x$)-1 ):printc$j:got08320
8340 ifc<>34thenif(cand127»31thenprintc$j:x$=x$+c$
8350 goto8320
8400 gosub50:fori=1to1en(xS):ifc$=mid$(x$,i,1 )thenj=i-1 :i=256
8410 next:ifi<257got08400
8420 return
9000 rem initialization
9010 print"<c1r><grph><ho~e><home><tset><c-d><c-d><c-d><c-d><c-d>
<c-d><c-d><c-d><c-d><c-d><c-d><c-d><c-d><c-d><c-d><c-d><c-d><c-d>
<c-d><c-d><c-d><c-d><c-d><c-d><c-d>"spc(39)"<bset><wht>":poke59468
, 1 2: rem format
9020 dimtS(3):fori=Ot03:readtS(i):next
9030 eS="<c-u> <c-d> <c-1><c-1><c-1> <c-d><c-1> <c-u><c-1>":d$="
<home><o-d><c-d><c-d><c-d><c-d><c-d><c-d><c-d><c-d><c-d><c-d><c-d>
<c-d><c-d><c-d><c-d><c-d><c-d><c-d><c-d><c-d><c-d><c-d><c-d>
9040 dimp$(1 ,9):fori=Ot02:readpS(0,i),p$(1 ,i):next
9050 dimk~(255): x$= "cefhnqsw": fori=1 tolen( x$) : ~(asc (mid$ (x$, i) ) )
=1 :next
9080 print"<clr><c-d> <rvs> turtle ":print"<c-d>do you want i
nstructions"j
9090 gosub8050:ifjgot0100
9100 print"<c1r><c-d>h1! 1'm a turtle nt$(O)
9110 print"<c-d>1 can face in four d1rections:
9120 gosub9990:pr1nt"<c-d><c-d> north "tS(O)
9130 gosub9990:print"<c-u> south neStS(2)
9140 gosub9990:print"<c-u> east "eStS(1)
9150 gosub9990:print"<c-u>or west neSt$(3)
9160 gosub9990:print"<c-d><c-d>to tell me to go north, type n.
9170 printn<c-d>for south, s, for east, e, for west, w.<c-d>
9180 gosub8000
9200 printn(clr><c-d>let'a try 1t now. just type n, a, e, w
9210 printn<c-d>or q for quit when you want to go on.
9220 pr1nt n<c-d><c-d> n;:j=O:x$="neawq
9230 print n(c-l><c-l><c-l>"mid$(xS,j+1 ,1)" "eSt$(j);:gosub8400:
ifj<4goto9230
9300 printn<clr><c-d>i can also go forward if you type f.
9310 print"<c-d>when i reach the edge of the screen,
9320 print"(c-d>i just atop. as i move around the
88 5: SUPERLIST-an Example &a Tool
10030 data"Q<c-l><c-l>W<c-u>U<c-d><c-d><c-l>J<c-u><c-l>
10050 data"square",effffsffffwffffnffff,"step",efsf,"2 step
10060 dataeffffsffffwffffnffffefsfeffffsffffwffffnffffefsfeffffsf
fffwtftfnffff
CHECKSUM: 21
Line-number references
100 190 510 1190 2010 2020 9090 9960
1000, 140
1190 1110 1120 1140
150 165 180 370 1150
190 360
20 20 8010 8100
2000 150
22 22 8320
30 8040 9500
300 160
330 1030
50 50 8400
500 170 320 350 1020
52 52
8000 190 510 1190 9180 9470 9690 9790 9890 9960
8040 2010 8040 9800 9890
8050 8040 9090 9560
8100 8120
8110 32
8200 36 9350
8300 130
8310 8310
8320 8330 8350
8400 8410 9230 9350
9000 10
9230 9230
9350 9350 9360
9500 9560
9990 8040 9120 9130 9140 9150 9160
Variable references
a 130 1150 2010 2020
c 24 160 160 165 170 180 8320 8330 8340 8340
c$ 20 20 22 22 24 32 50 50 52 52
c$ 180 180 180 8110 8310 8310 8330 8340 8340 8400
d 38 39 8200 8210 8220 8230 9340 9350 9360
d$ 8240 9030
e$ 39 8200 9030 9130 9140 9150 9230 9350
i 310 310 310 310 340 340 340 340 1010 1010
i 1010 1010 1130 1130 1130 1130 1130 1130 8110 8110
i 8110 8110 8120 8400 8400 8400 8400 8410 9020 9020
i 9040 9040 9040 9050 9050 9990 9990
j 32 34 34 34 36 38 38 310 310 320
j 330 330 340 340 350 360 370 1010 1010 1020
j 1030 1110 1120 1130 1130 1140 1150 8040 8110 8400
j 9090 9220 9230 9230 9230 9340 9340 9350 9360 9360
j 9560
k 32 32 32 39
k:'( 170 9050 9050
p$ 30 32 32 130 180 180 180 360 370 370
p$ 2010 2010 2020 9470 9470 9510 9800 9870
p$( 340 360 370 1130 1130 1130 2020 9040 9040 9040
p$( 9470 9750 9750 9870
90 5: SUPERLIST-an Example & a Tool
sq$ 9750
st$ 9750 9800 9800 9800 9800 9800 9800 9800 9800 9800
st$ 9800
t$( 39 9020 9020 9100 9120 9130 9140 9150 9230 9350
x 34 8210 8210 8210 8230 8230 8230 8240 9340
x$ 30 140 150 160 165 165 180 180 310 310
xS 330 330 330 1010 1010 1030 1110 1120 1120 1120
x$ 8050 8110 8110 8300 8330 8330 8330 8330 8340 8340
x$ 8400 8400 9050 9050 9050 9220 9230 9340
y 34 8200 8200 8200 8220 8220 8220 8240 9340
y$ 330 340 370 1110 1110 1130 1130 1150
5.10 DOMINOES
Some years ago, it was surprising to note that no one had put the game of
dominoes on a computer. A little analysis showed why it hadn't been
done, some thinking showed a solution to the problem, and a little work
put a predecessor of the program on the sample disk on the market.
Dominoes is played with "bones" that show two patterns of dots. Each
bone is unique, and each combination of zero through six dots on each
side is on one bone. The total number of bones is therefore 28. To display
a bone takes at least 5 x 8 pixels. Theoretically, all plays could go on one
side (or even one quadrant) of the screen-there just isn't room to show
them decently with only 1000 pixels.
In practice, the only important things about the bones already played
in the block game are the two active ends. Thus, we can make a display
with those ends shown in the center, and use just numbers to represent
those in the player's hand and those already played by both sides. Each
bone displayed takes two pixels (width) by four (height) for legibility. We
can put up to 20 bones on a row, so we need two rows to display those
already played. Each player starts with 7 bones, leaving 14 in the bone
yard. In the worst case, the computer could have all 7 bones with 6' s on
them in its starting hand. Then the player would have to draw all 14
before passing, making a total of 21, too many to show on one row. For
that very improbable case or the one requiring 20 bones in the player's
hand, we might want to have an extra row on the display. On the other
hand, the chance that that condition will ever arise is so low that it could
be ignored.
The DOMINOES program is fully operational and plays a pretty good
game. Its best play has three levels of logic: playa doublet if possible;
playa tile that has a successor if possible; pick at random from those that
can be played. Doublets are handicaps since they are less easily played
than bones with different numbers on the two sides. Therefore, it's
harder to play your last bone if it's a doublet, and your chance of winning
is reduced. (Double zero is an exception, but the program doesn't recog-
5.10 DOMINOES 91
nize that.) There are many other strategies that can be added to the sys-
tem, usually taking extra time.
A quick review of the listing will show that DOMINOES falls short of
"advanced software" as developed in this text. It isn't interoperable and
its speed could be improved. Before you make changes, make sure that
you really know the rules by checking a copy of Hoyle. You can get fancy
by using one of the variants of the game, but just improving the basic
block game will be useful.
dominoes Non-Professional
5 gosub15000:rem m.richter 90064 12/7S
10 pw=O:tw=O:ty=O
20 poke5946S, 12: l=rnd(-tl): dimd$ (27) ,h$( 1,20) ,pl(6) ,h(20)
40 lu$="<home><c-d><c-d><c-d><c-d><c-d><c-d><c-d><c-d><c-d><c-d>
<c-d><c-d><c-d><c-d><c-d><c-d><c-d>":lo$=lu$+"
<c-u>"
100 pl=0:print"<clr>"spc(15)"dominoes<c-d><c-d><c-d><c-d><c~d>
<c-d><c-d><c-d><c-d><c-d><c-d>
110 j=0:k=0:fori=Oto27:d$(i)=mid$(str$(j),2)+mid$(str$(k),2):
goeub9500
120 k=k+1 :ifk>jthenk=O:j=j+l
130 nextl:forl=Oto26:j=i+t2S-1)*rnd(1 ):x$=d$(i):d$(i)=d$(j):d$(j)
=x$:gosub9500
140 nexti:forj=Oto6:fori=Otol :h$(i,j)=d$(7*i+j):nexti:gosub9500:
nextj:sp=14
150 forj=7t020:fori=Otol :h$(i,j)="":nexti:gosub9500:nextj
160 kO$="d":k1$="<rvs>d<off>raw":k2$="draws a bone
200 1$="":forj=Oto1 :fori=Ot06:x$=left$(h$(j,i),1 ):gosub9500
210 ifx$=rlght$(h$(j,i),l )andx$>l$thenl$=x$:p=j:k=i
220 nexti:nextj:ifl$=""goto100
230 print"<home><c-d><c-d>p<c-d><c-l>l<c-d><c-l>a<c-d><c-l>y<c-d>
<c-l>e<c-d><c-l>d<c-d><c-d>
240 printspc(S)"U::::"""::::I
250 printsPC(S)"! <rvs>
260 printspc(S)" V <rvs>
270 printspc(S)" <rvs>
<off>
<off> V
<off>
!
bone count
DDDDDDDDDD
yard
you
2S0 printspc(S)"J::::<rvs>999<off>::::K pet
290 gosub2000:goeub7000
295 printlo$:print"the largest doublet is ";1$1$:r$=1$:gosub19S0:
gotol000
300 p=O:prlntlo$:print"which bone (or "k1$")? ";
302 gosub4000:x$=c$:lfx$=kOSthenprintxS:gotoSOOO
305 IfxS<"0"orx$>"6"goto302
307 prlntxS;:
310 gosub4010:y$=c$:lfy$<"0"ory$>"6"goto3 10
315 prlnty$:xS=x$+y$:y$=y$+leftS(xS,l)
320 fork=Oto20:ifh$(0,k)<>x$andh$(0,k)<>ySthennextk:goto9000
330 rf=O:Ifpl=Othenl$=risht$(x$,1 ):r$=left$(x$,l ):If=l :goto1000
340 If=O:ifl$=left$(x$,l )orl$=right$(x$,l )thenlf=l
350 Ifr$=left$(x$,l )orr$=right$(x$,l )thenrf=l
360 iflf+rf=Ogoto9000
370 iflf*rf=Othenonlf+lgoto500,550
375 ifl$=r$orh$(O,l )=""goto450
380 printlo$:printx$" - left or right? ";
390 getc$:ifc$<>"1"andc$<>"r"goto390
400 prlntc$:ifc$="1"Soto450
410 ifr$=right$(x$,l)thenr$=leftS(x$,l ):gotol000
92 5: SUPERLIST-an Example & a Tool
15025 printchr$(34)"return"chr$(34)".
15030 print"<c-d><c-d><c-d>press a key to continue
15040 gett$:ift$-""goto15040
16000 print"<clr><c-d><c-d><c-d>":print"pet has three levels of p
lay:
16010 print"<c-d>1. easy: you can win two games of three
16020 print"<c-d>2. slightly harder: maybe 60/40 for you
16030 print"<c-d>3. tough: nearly equals best play
16040 print"<c-d><c-d>which do you want?
16050 gosub4000:ifc$<"1 "orc$>"3"goto16050
16060 a1=val(c$):return
20100 printtab (9) "V"; : printtab (29) "V": return,
CHECKSUM: 60
Line-number references
1 00 220 8610
1000 295 330 410 420 450 460 500 510 550 560
1000 1550 1560 1570 1580
1200 8040 8200
1250 1230
1300 1260
1350 1210 1310
1400 1360
1450 1210
1500 1270 1460
15000 5
15040 15040
16050 16050
1980 295 1470 1510 8600
1990 1990
2000 290 1010 8040 8570
300 1150 8040 8200
302 305 9000
310 310
390 390
4000 302 4000 8610 16050
4010 310 4010
450 375 400
500 370
5000 1010 7100
550 370
5900 1100 1100
6000 5990
6010 5990
6020 5990
6030 5990
6040 5990
6050 5990
6060 5990
7000 290 1005 8040
7100 7010
8000 302 1470
8100 8010
8500 1110 8110
8610 8620
9000 320 360
9500 110 130 140 150 200
9900 1240 1270 1320 1370 1460
5.10 DOMINOES 95
Variable references
al 1210 16060
0$ 302 310 390 390 390 400 400 4000 4000 4010
0$ 4010 8610 8620 16050 16050 16060
d$~ 20 110 130 130 130 130 140 8040
h$ 20 140 150 200 210 320 320 375 1005 1005
h$( 1005 1005 1110 1110 1230 1260 1310 1360 2130 2150
h$( 7010 7100 8020 8040 8500 8520 9900
h( 20
i 20 110 110 130 130 130 130 130 130 140
i 140 140 140 140 150 150 150 200 200 210
i 210 220 1005 1005 1005 1005 1005 1220 1220 1220
i 2130 2130 2130 2150 2150 2150 2150 2170 7010 7010
i 7030 7040 7100 7100 7110 8020 8020 8020 8040 8500
i 8500 8510 8520 8520 8530
j 110 110 120 120 120 130 130 130 140 140
j 140 140 150 150 150 200 200 210 210 220
k 110 110 120 120 120 120 210 320 320 320
k 320 1005 1005 1230 1230 1240 1260 1260 1280 1310
k 1310 1340 1360 1360 1380 1460 1470 9900
kO$ 160 302 8030
k1$ 160 300 8030
k2$ 160 1470 8030
1$ 200 210 210 220 295 295 295 330 340 340
1$ 375 450 450 460 550 550 560 1100 1270 1320
1$ 1330 1370 1460 1460 1550 1550 1560 1560
l~ 1005 8110 8120
lf 330 340 340 360 370 370
10$ 40 295 300 380 1100 1470 1510 2120 8600 9000
1u$ 40 40 7010 8025
p 210 300 1005 1005 1005 1005 1150 1210 8020 8040
p 8040 8200
pl 100 330 1010 1010 1020
1240 1240 1270 1320 1330 1520
Pl~
pl
20 1220
1520 1520
1240
1520
1240
pv 10 8540 8540 8820
r$ 295 330 350 350 375 410 410 420 500 500
r$ 510 1100 1270 1320 1330 1370 1460 1460 1570 1570
r$ 1580
rf 330 350 360 370
sO 8500 8510 8510 8540 8550 8560 8570 8600
sl 8520 8530 8530 8540 8550 8560 8570
140 2120 8010 8030 8040 8040 8040
~i 15040 15040
to 8570 8570 8840
tl 8570 8570
t1 20 1990
tiS 1980
tv 10 8570 8570 8820
ty 10 8560 8560 8820 8830 8830
u 9500 9510 9510
x 1020 1020 1020 1020 1030 1240 1240 1240 1240 1520
x 1520 1520 1520 1520 1520
x$ 130 130 200 210 210 210 302 302 302 305
x$ 305 307 315 315 315 320 330 330 340 340
x$ 350 350 380 410 410 420 450 450 460 500
x$ 500 510 550 550 560 1010 1030 1100 1100 1100
x$ 1110 1240 1270 1270 1270 1270 1320 1320 1320 1370
x$ 1370 1370 1460 1460 1510 1520 1550 1560 1570 1580
96 5: SUPERLIST-an Example & a Tool
x$ 5000 5000 5000 5990 6000 6010 6020 6030 6040 6050
x$ 6060 7100 7100 8500 8510 8510 8510 8520 8530 8530
x$ 8530 8540 8550 8560 8600 9900 9900 9900 9900
y 1240 1240
y$ 310 310 310 315 315 315 315 320 1005 1010
y$ 1240 1270 1330 1330 1330 1370 1460 1460 1510 1520
y$ 1550 1560 1570 9900
I pursuing a
6 project
I
There is no formula for writing advanced software. There is no pattern to
the way projects come to you. This chapter is devoted to one scenario,
where you are designing and building an inventory system for a specific
customer. We will walk all the way through the process, from the first
phone call to the first request for expansion. Notice that we're taking the
easy path-a project requested by a customer. We're even simplifying
that, detailing only the easiest of the three programs in the system. In par-
tial atonement, the rest of this section looks briefly at a project that you
start on your own.
You're an amateur fly-tier, with a passion for the classic patterns and
their modern variations. (Not that kind of fly-these are fishing lures, and
can represent high art in the opinion of a devotee.) Your collection in-
cludes samples and pictures. You want to catalogue the collection, to
develop a taxonomy based on size, shape, material, color, and other
features. Then you will add measures of performance, and eventually
develop a science of fly-tying to complement the art. With sudden insight,
you decide to put your computer on the job with you. You'll write a
catalogue system, FLYTIE, and revolutionize the field. For this, you will
be customer, user, designer, coder, tester, and everything else.
There are two interesting possibilities here: you will be the only user of
your product; or you will be the pioneer in a new field of thousands
(dozens?). If no one else will ever use your system, then write what you
will and as you will. If it works for you, good enough! But maybe there
will be a market; maybe the fly-tying field will be revolutionized by your
product. How do you handle that prospect?
For fly-tying alone, there is little to add to the material in the rest of this
chapter. You will have to play all the roles and pretend to be a computer
novice when acting as the customer or user. The larger problem comes
97
98 6: pursuing a project
when you think about what else the system should do. At the least,
remember that other fly-tiers will want features that you don't. How do
you extend your ideas to address the broadest potential market? Concen-
trate on the most general statement of the problem that satisfies all of your
needs. (Don't lose sight of your basic requirement to catalogue your own
collection; just try to build on it.)
Can the system be extended still further? Can the same techniques be
applied in biology or geology? Is there a still more general concept of
cataloguing that opens the market still further? If the answers suggest a
broader kind of system than you planned, there are several ways to get
there from your original idea. One of the best approaches is gradual; trying
for the ultimate system first can leave you dissatisfied-even disgusted-
with beautiful parts of a package that doesn't work.
Start by building something that directly addresses your own fly-tying
needs. Get it up and working first to put your house in order. Then take
what you learned and what you have working to investigate extensions for
other applications. Can you add more fields? Can you tokenize more in-
formation to save space? Would a relative file be better than sequential or
vice versa? Sketch alternative designs until you find something that looks
to be marketable. Now, follow the methods of advanced software to build
the new system. How do you test it? Use your FLYTIE catalogue. Move
the files that work into the new system, and verify that it does everything
that the specialized program did. And don't forget to document. If anyone
else is ever to use your product, the rules must be legible.
and requires no change of file structure. So, we'll leave that for add-on
after the system is running.
The central system will take the logs in by disk and add fields for date
and station ID before recording them. It needs a dual drive for that tran-
scription. Daily activity is to integrate yesterday's transactions into the
history, flag special conditions (e.g., account near limit, parts requiring
manufacture or reorder), and create new files for the stations. Problems:
two trips to each station per day, one to take out the morning's disk, the
other to pick up the one with the transactions. Solution may be to use
telephone (modem) communications after the system is set up. Recognize
the problem now, make sure there is a solution that won't break the
system, then continue with primary design.
The central system needs dual disk and a quality printer, which means
an IEEE system at $3,000-5,000. Each station can run with a 64 (maybe
even a VIC) with a 1541 and a 1525 (for on-the-spot invoicing). Say $1500
for each of the 30 stations. So, the customer is looking at $50,000 in hard-
ware all told. She'll probably balk at that total and start with fewer sta-
tions. The five busiest stations will get the full treatment; the others will
work on paper at first. The bill will then be around $12,500. If the soft-
ware runs another $2,500, we mean about a 100-hour job. Allowing half
that time for testing and data entry, we need to be sure that $1,250 or so is
a fair price for your design and coding.
Two other items need to be considered at this stage, both dealing with
interfacing the system. Firing up the first time will require entering a com-
plete inventory. Thereafter, occasional inventories will verify the history,
check for pilferage, and keep the accountants happy. Inventories require
facilities for data entry and reports of discrepancies. The initial system
will require manual entry for warehouses without stations; that capability
will fit in with file editing and transaction correction. Until clerks become
perfect, errors will occur; the system has to be able to tolerate and to cor-
rect them. We'll need a system to identify transactions that are current
and to purge those that are not. Someday we'll want programs to analyze
trends and aid long-range planning, so purged logs shouldn't be thrown
away. We'll keep the old disks with their logs, and we'll maintain a se-
quential file of purges. Between them, we can construct a complete audit
trail.
The other set of functions of the central system we can think of as being
done monthly. (The actual interval is whenever the user wants to do
them.) That run reports activity by station and by customer; its primary
purpose is to generate bills for the current accounts. A later generation of
system may well provide a cover letter, but for now we'll just summarize
monthly activity. One line per transaction per account. Then part of the
104 6: pursuing a project
daily activity has to be logging payments that come in. (Check need for
distinct status flags: received, cleared. When should we credit the checks?
Need we compute interest on outstanding balance?) Put in a mechanism
for interest on daily balance after a fixed delay. Initial interest rate will be
set to 0; to set a rate, change the line and save the program again. It makes
no sense to ask the question repeatedly. More pressing is the need to flag
each billed transaction so it is not recomputed.
We'll log for each customer the date billed and amount due (for next
time) and tick off everything we billed. We can purge the old transactions
from the system (put them into the historical file) when the bills are
prepared or when they're paid. Nominally, let's do it at preparation, and
turn back to paper copies (or historical search) if questions arise after bill-
ing. Those functions could be in the same program as the daily central
functions, but they are really different and probably need a bookkeeper to
run them instead of the central clerk. Both programs end with long print
runs, so have different kinds of wrapup.
The system will need three distinct programs: warehouse (interoperable
64/central), daily (central), and monthly (central). For backup, we need a
second IEEE system (we can do without a second letter-quality printer). It
can back up a warehouse station as well as the central one since it can run
station software. Total hardware cost is about $lSK. We'll go for $3K for
the baseline software. If that's too big a bite at one time, we can back off to
a single, central station without warehouse software at $5K hardware,
$2K software on a two-month development schedule. Knowing our file
structures, we can add the warehouse hardware and software at any time.
(Most of the $lK saved in software comes from not having to test the in-
terface, not from skipping the warehouse program.)
The central station will serve as a warehouse station during the day; it
will run the same program that goes into their 64's. (It should probably
have a dot-matrix printer in addition to its 8300; otherwise, the programs
might not be identical.) A new customer will be added on second shift,
when the disks are updated and the item .information (prices, etc.) is
modified. With that, we have pretty much pinned down the architecture
we're proposing and are ready to write the customer description.
6.5 Design
Let's suppose that the customer has accepted the Inventory Control Sys-
tem as described, and has agreed to your terms for development (includ-
ing payment schedule). Since most of the eventual system cost will be
hardware, you should establish a link with a local dealer; a dealer's coop-
106 6: pursuing a Project
eration will be easy to obtain and may simplify some of your prob-
lems-including getting information and hardware. Your next task is to
layout the files and programs of the system in detail, to schedule your
work so you'll know whether you're in trouble or not, and, finally, to
start coding.
Transactions are collected in the daily log, which carries the coded date
in its name. Each transaction has a type [cash sale, credit sale, return, de-
livery), account, part number, quantity, line-item cost/ credit. Someday
the system may expand to include salesman commission, so leave space
for an ID field.
Inventory on disk is a relative file with two records per warehouse.
[200 items at 2 bytes each won't fit into a 255-character maximum rec-
ord.) The item names and numbers are.in a sequential file read in at the
start of the day; one each string and integer arrays required, at about 25
characters litem for 5K storage. We'll keep the local inventory in RAM as
well, so we can update it as the day's transactions proceed. Brute force
storage (200 items x 63 warehouses x 7 bytes) takes about half the disk.
We should be conservative and pack inventory into two bytes per entry
[use the 0-200 counter twice for a maximum of about 40,000 items). We
can afford to keep that file on disk and to spend time to unpack it since it
is accessed only rarely-when the customer needs more widgets than
this warehouse has in stock.
In contrast, the list of accounts is searched for every purchase. Figuring
the average account name as 15 characters, we could handle 200 of them
in 3K. Note: Add the 200-account limit to the system constraints. We'll
pack the number in the usual way. If the number of accounts goes over
200, we'd need two characters for the id-no problem. If it goes over 500,
we'd need more than 7.5K of RAM-that'll be a problem. If they can live
with 200, that's the design. The other account data can be relative and
fetched when needed (once per invoice).
Program architecture for the warehouse is almost trivial. After initial-
ization, enter the account [or a dummy for a delivery). Create a display
with account information, and start the invoice page. Select transaction
type, accept input data, print the line and log the transaction. Then
return for the next transaction. If the transaction type is "total," present
final display for confirmation, then wrap up the invoice on the printer.
[Check invoice formattingj special paging may be needed to get the form
out of the printer without too much wastage. How many transactions on
a typical order?) A dummy account can be used for "inventory"-to trig-
ger an inventory check. Its logic is to report first how many are in stock
here. If more are needed, the warehouses with at least the required [in-
put) quantity are on hand.
One module handles the inventory check. Otherwise, all transactions
G.G Using the warehouse program 107
to type in the number of the one you want. A number 0 tells the com-
puter that none was the one you wanted, and you'll reenter the account.
Once the account is specified, the screen will show you the full name,
discount and tax rates, and credit data. The printer will start to mn, put-
ting out the top part of the invoice. Then you type in what item is being
purchased or returned and how many pieces. The items may be specified
by part number, full name, or part of the name, just like the Accoupt.
The screen will show you the price per item and the total price for that
quantity, and ask you to "Accept" the entry. If you enter "y," it's ready
for the next. Answer "n," and that line item is cancelled. When you have
entered all items, call the next one "total," and the computer will figure
the total with tax and discount and show it to you for you to "Approve."
If you do, you tell whether the sale or return is for "Cash" (otherwise, it
is handled by crediting or debiting the account). The invoice is completed
and filed automatically. If the sale was not accepted, the printer voids
and ejects the page before asking for the next "Account. 1/
Notice that you have to treat an exchange as two separate operations-
one for return, the other for sale-and you have to enter the Account
twice. If the order is for more than ten items, after every ten the com-
puter will ask you to confirm the work so far and will automatically sub-
total and complete that invoice page. When you finally mn a total, the
subtotal for the last page will be computed and printed as well as the grand
total. Remember that each page is accepted separately, so you have no
way to correct an earlier page of a large order except by creating a return
invoice for items your customer decides not to take.
For walk-in customers, the Account is "walk-in," number O. Of course,
there is no walk-in account as such, so it cannot be credited or debited,
and all transactions are automatically for cash.
If you call the Account "inventory," you will be asked "What part?"
Enter its number or its full or partial name. The screen will tell you how
many are in stock. If you need more than you have on hand, answer the
question: "Check other warehouses?" with "y." Then enter the number
of pieces your customer needs. The program will list the numbers of the
warehouses that have enough to cover the order, so you can either send
the customer to one of them or arrange to pick them up for him.
When you're through for the day, remove the disk from the drive and
pack it for shipment to the central office. Take the spare disk from the
file and insert it in the drive. Then enter "quit" for the Account, and the
spare disk will be updated with your current inventory. Put the spare
away, shut off the power, and call it a night. The next day, a new disk
should be ready and you repeat the process. If the new disk has not ar-
rived, use the spare instead. You will have to tell it today's date, and the
inventories for the other warehouses won't be up to date, but it will do
everything you need and central can sort out the problems.
6.7 Design review meeting 109
you can and give them what you can afford. Try to postpone even minor
extensions until the system is mnning. Diverting your attention from
the fundamental problem threatens to delay delivery and to degrade the
product. Make notes, but no commitments, on each change that they re-
quest. Promise nothing that you didn't plan to do when you designed the
system. Be as friendly as possible, but don't give in.
Hand out copies of the test plan during the meeting, and walk through
it with your customer. Point out that you're testing everything the User
Manual says, both positive and negative. Invite them to suggest addi-
tional testing, and ask the user to participate. If he's willing, let him join
you for the testing itself. Again, the customer will have adopted your pro-
gram as her own even before delivery.
Finally, pin down some dates at the design review. Schedule delivery of
the documentation with the changes you have agreed to. Get a commit-
ment for their review, confirmation, and signoff by a fixed date. Make sure
that they understand that you cannot proceed without those signatures.
When the revised User Manual and Test Plan are ready, deliver them
with a cover letter that includes a list of the changes you made due to the
review, any changes in cost and schedule that they have caused, and the
date on which you require signoff. Resist the temptation to put in a nega-
tive option (" ... will be regarded as accepted unless ... "). It's much
weaker than a signature, and the User Manual is your specification (hence
your contract) while the Test Plan embodies the acceptance criteria (the
way you know you're done).
6.8 Coding
Now that we've come to the enjoyable part of the process, there isn't
much to say. Coding is a highly personal process; each programmer has
his or her own approach. Some prefer to write a routine of a dozen lines or
so and check it out thoroughly before they proceed. Others insist on get-
ting the whole thing down before the first RUN. If you prefer, work the
modules one at a time; an alternative is to label all of them, then write one
and all that GOTO it, then all that it can reach, and so on. All that matters
is that you use some system that you find comfortable and efficient.
Common sense suggests that you start with a nucleus of initialization
and utility routines from earlier programs. If there are utilities you
haven't tried before (say, RECORD in BASIC 2.0), code them separately
and mn some tests. Since part of the system will mn on a 64, figure on
BASIC 2.0 for all of the warehouse program; while you're at it, stick to
2.0 throughout for consistency and ease of maintenance. You'll probably
develop the program on another host (403218032) to take advantage of
6.9 Maintenance 111
your progTamming tools and the IEEE buss, and you could fall into BASIC
4.0 without thinking. Try to drill BASIC 2.0 into your head so you don't
slip accidentally.
An experienced programmer doing a variation on an established theme
may spend half the time coding, half in debugging and testing. The less
experience you have with that type of problem, the longer testing will
take. No customer will be upset if you deliver ahead of schedule. If coding
will take four weeks, schedule six for checkout and test. Maybe you'll
finish in four, or even three-great! But schedule for four and run into
one extra bug, and you'll lose your schedule or even your contract.
Remember your objective: deliver a quality product. Don't compro-
mise in testing to meet the schedule, and don't deliver code you haven't
fully checked out. If the user joins you in testing, you'll save no time; you
. will have an in-house supporter to attest that you fulfilled your contract.
If you have to test alone, invite your customer to witness the final run-
through of the plan. At least, ask her to select some parts of the test to
witness. Throughout testing, and particularly during the final pass, doc-
ument every result and its results compared with those you predicted in
the test cases. Once you have your customer's signoff on the test cases,
the system is finished. Unfortunately, your job is not.
While coding, you will find improvements over your original design.
Many will simply be better ways to do the original job. Some will modify
the procedures in the User Manual-or even the capabilities in the Cus-
tomer Description. Within the latitude provided by the approved docu-
mentation, the design is entirely under your control. Change it to save
memory, disk space, running time, or difficulty in development. If your
improvements will change the documentation, you cannot proceed with-
out customer approval. Most changes will give a better product, and she
will buy off quickly. Some may be necessary to meet schedule (budget is
your problem, not hers) even though they will cost performance; those
will be harder to sell. As soon as you see a design change that affects doc-
umentation, talk it over with the customer to get verbal approval. Before
you start to test, get her signature on the revised documentation-
including the Test Plan. You must have final documentation in hand be-
fore you start acceptance tests and demonstrations, for your protection
and hers.
6.9 Maintenance
Software cannot wear out or break; if it was correct when it was written,
it cannot go wrong. In hardware, maintenance means cleaning, inspec-
112 6: Pursuing a project
tion, and minor repair. In software, it means repair and expansion. Once
the program is delivered, you can expect calls of three varieties:
"It doesn't work. Come fix it."
"It doesn't work the way we want."
"Can't we make it do this, too?"
explain that he is going beyond its design limits. Widget's system will
handle 200 items, and they just got their 201st. Now what? Are all 201
really active now? If not, remind them that they can replace outdated
ones with current products. But if they really need 201, there's a prob-
lem. You used a coding scheme that won't expand, and made it clear from
the beginning that the limit was solid. The problem cannot be "fixed" by
changing a few lines or modifying procedures. It has just escalated be-
yond what the user can handle; call in the customer.
It's surprisingly difficult for a customer to recognize that a system that
works well for 200 items just won't handle 201. You explain that both
the disk files and the program have to change, and the reaction is, approx-
imately, "Change 'em! II She should recognize that there is no instant
fix, so the problem will last for some weeks until you can make some
minimum modifications. She has to pay for a week or so of your time to
patch in emergency measures, then may have to go to a whole new sys-
tem to handle the growth. Explain the process that you have to go through
so she understands the fact that it's complex and time consuming.
What will you do? To begin, you need a two-byte item ID instead of
one. The extra code is easy, but you need a program to transcribe and ex-
pand each file, inserting a "0" first digit. You can't afford to require a
new inventory and manual reentry just because you've changed the file
structure. Finally, you impose a new limit and implement it by changing
array dimensions. The new numbering system would handle up to 40,000
items-but the computer can't. With luck, the running programs will
have room for expansion to, say, 250 items. That will only handle growth
for a few months. For the Widget Company to stay in business, more ex-
tensive changes are needed-in a hurry. Get customer agreement, re-
work your schedule, and patch up the code. However great their panic,
retest the system before you deliver it. They will have worked around the
problem while you patched, so they can live with it for another day. They
cannot live with a patch that breaks down a week later.
When you deliver the modified system, stress the need for redesign to
handle the growing load. The customer must understand that you cannot
stretch the hardware any further, and that there's barely time to adapt be-
fore the system saturates again. If she isn't ready to face the problem, tell
her that she hasn't much time, then go home and write a letter. Formally
identify the facts: the patch is temporary, but functional; the system will
not handle further growth without substantial redesign; you cannot be
responsible for the effects of delaying that redesign. Make sure that you
are off both the legal and the moral hooks. Put the problem on paper and
be certain that your customer receives it.
If the customer is ready to face reality, take a contract to find practical,
economical solutions. Suppose that the 64's have space, but the 8032 just
114 6: pursuing a project
can't handle more items. Maybe there's a new machine with full com-
patibility and more RAM. Perhaps the critical 8032 code could keep the
files on a hard disk and access them there, saving RAM. More likely, the
64's have filled to the point where garbage collection becomes prohibi-
tive. Switching computers is expensive-it means new disk drives, too,
and there are at least 30 stations to resupply. A week should be time
enough for you to construct a maximum system for the hardware you
know, to estimate cost and schedule for several alternatives, and to pre-
pare the sad message for your customer. With customer approval, con-
tact a supplier of minicomputer systems. If you exceed the capacity of a
micro, maybe the Widget Company has to move up in capacity and price.
When you started the process, you had to guard against contagious
panic. That rule applies here as well. There is no greater trap than promis-
ing more than the system can deliver. There are few worse things to do to
your customer than make her believe that an unsolvable problem is under
control. Even when a problem has outgrown your programming, a cus-
tomer will still need your expert consultation. She'll still be a customer.
stuff the system knows. Suppose you start with the Word Machine. It's
inexpensive and written in BASIC. Rip it apart and interface it to the ICS
files. A little imagination will let you adapt the program so it cites bal-
ance past due, late-payment charges, and other items as they are relevant
to that account.
The same sort of thing applies to a general ledger or accounting pack-
age. Half of the accounting is already implied in the ICS files-items sold
and payments received. Look for a package that you can link into those
files so that bookkeeping will require entering information only once. Be
willing to sacrifice elaborate features with low payoff to Widget Com-
pany for easy interfacing. When you have finished ICS, you will be in a
unique position. You will know more about how Widget Company could
use computers and more about computers and software that apply to Wid-
get than anyone else connected with the company. Your consultation is
valuable to management, and they'll probably be well aware of it-and
willing to pay for you to use it.
Don't confuse yourself by working the second problem first. Your pri-
mary job is ICS. Get it working without concern about WordPro or Gen-
eral Ledger. Only after you have a satisfied customer should you spend
time or thought on what else can be done. After your first few projects,
you will automatically organize your systems for easy interfacing to your
favorite purchased products. But don't be tempted by the glitter in the
distance to leave the development vein you're paid to mine. The time to
look for building onto the system is after both you and your customer are
sure that it's working.
programs Supplied on the Diskette
Seven programs are provided on the eHdJ '5@G disk. All are referenced or
contained in the book Advanced BASIC Programming for the Commo-
dore 64 and Other Commodore Computers. All programs are self-docu-
mented, so user instructions are not required.
SUPERLIST is the principal program developed in Chapter 5 to pro-
vide cross-referenced listings.
DIABLO SUPERLIST is the variant of SUPERLIST for letter-quality
publication copy. It is described in 5.7 SUPERLISTing and was used for
all listings in the book.
SYS CHECKSUM is developed in 5.8 to show the use of embedded
machine-language code in a BASIC program.
CHECKSUM is a BASIC-only checksum program intermediate be-
tween SUPERLIST and SYS CHECKSUM. It is referenced in 5.8 and in-
cluded on the disk to clarify the relationship between its parent and its
descendant.
PROGRAM COMPARE reports line by line on the differences be-
tween two BASIC programs. It assists the advanced programmer in
tracking down differences in code which have been flagged by different
checksums.
TURTLEWALK is developed in 5.9 as an example of an educational
program for microcomputers.
DOMINOES is discussed in 5.10 as a less-advanced program. It is pro-
vided on the disk as a working program for the student to improve, ap-
plying the principles developed in the book to create a better product.
116
Index
Absolute address, 27 ATN command, 28
Accountlsl Automatic line feed, 49
entering of, 107-108
list of, 106
Accounts receivable, 104-105 Backup, ll, 114
Addition, 34 BASIC, 1,85
Addresslesl, 26, 32, 41, 72 information on, sources, 2
absolute, 27 principles, 4-5
return, 27 start of, 27
secondary, 49 use,S
of variable, 29, 30 BASIC AID program, 41
Addresser, for SYS CHECKSUM BASIC commands, see Commands
program, 82 BASIC programming, advanced, see
Advanced BASIC programming, 1-2 Advanced BASIC programming
prerequisites and purposes, 2-4 BASIC 2.0, 111
Advanced programmer, writing a garbage collection, 25
program for, 9 Beginner programmer, writing a
Advanced software, 1 program for, 9
interoperability, 5-7 Bells, 37
writing of, 97 Binary, 33
ADVENTURE, 2 Bitls), 33
Alternative designs, 13 assigning of, 33
AND, 41, 44 "Bones," dominoes and, 90
Anomalies, 25 Bookkeeping, 115
APPEND command, 58 Brackets, 77
use, 11 Branching, 37
Apple machines, I, 2 Brute force storage, 106
Application program, bytes, 25 Buffer
Arithmetic, 33 cassette, 40
Arrayls),31-32 keyboard, emptying of, 36
defining of, 32 Bug, see Error
dimensioning of, 6, 32-33 Buss system, 50
multidimensioned, 13 Byte(s), 25, 33-34
storage required, 32 high-order, 27
structure, 11, 12 of a line, in SUPERLIST program, 72
Array name, 32 losing of, 14
Array "variable," 75 meanings, 34
ASC function, 28, 33, 34, 37 saving of, 28, 29
ASCIL 33, 43, 53, 60, 75
changes in, 50-51
characters, 28 cA command, 28
conversion to, 41 Carriage return, 52, 56
in SYS CHECKSUM program, 83 automatic, 55
values, 34, 36, 39, 72 Cartridges, 52-53
117
118 Index
Resolution, 3 quality, 1
RESTORE,14 utility, 14
Retrieval of information, see Search Software packages, standard, 114
operation Sort key(s), 64
Return address, removal, 27 Sort operation, 63-65
RETURN command, 12,27,37,39,45, characteristics for, 64
48,70 designing of file for, 64-65
bytes used, 28 Spacers), 27, 29
use, 28 saving of, 27
Reverse characters, typing of, 6 wasting of, 27
Reverse video, 6, 37 "SPCi," 28
RND command, use, 41, 42 Special characters, spelling out of, 67
ROM, 51 Speed, 14, 27
Routine(sl, 18 disk files and, 57
layout, 14 execution of, 16
RS-232C port, 47, 53 loss of, 16, 38
RS-232C serial buss, 50 requirements, utility, 13
Rules, set of, see Protocols see also Time
RUN command, 14, 42, 43, 73, 74, 82 Spreadsheet(sl, 114
"Run-time" question, 15 Spurious "variable," 75-76
Stack
cleaning of, 39
s$ string, 71 popping of, 39
SA,49 STEP defaults, size of, 38
SAVE command, 58, 71 STOP command, use, 41, 42
SCRATCH command, 58 Storage
Screen brute force, 106
checking of, 10 saving of, 29
device number, 47 STR$ function, 28
dimensions, 5, 6 use, 35
display on, 72 String(sl
output to, 48 conversion of floating-point number
Screen memory, 31 to, 35
Search operations, 63-65 operation on, 35
Secondary addresses, 49 String names, scanning of, 74
Sequential files, 55, 57, 98 String variable, 30-31
appending to, 57 value, assigning of, 31
features, 55-56 Structured programming, 17-19
for inventory control, 106 Structuring of system, 102-104
use, 56, 57 Subroutine(s I
Serial busses, disk drive for, 54 exiting from, 12
Shifted space, use, 38 high-speed, 18
Shorthand commands, 28 Subroutine call, 27
Single-character input, 9 SUPERLIST program, 2, 7, 21, 67-68
Single disk drive(sl, 54 anomalies, 75-76
Single entry, 17-18 DOMINOES program, 90-96
Single exit, 17 initialization, 68-70
Software, 2, 53 printing the reference, 74-75
advanced, see Advanced software processing a line, 72-74
for catalogue, 65 starting of, 71-72
costs, 104 SUPERLISTing, 76-81
maintenance, Ill, 112 SYS CHECKSUM, 81-85
for multiple data file coordination, 64 TURTLEWALK program, 85-90
objective of, 3 utilities, 70-71
professional, 3 SUPERLISTing, 52, 76-81
Index 125