COMMODORE C12S BOOK 2 ADVANCED PROGRAMMING

COMMODORE el28

ADVANCED

PROGRAMMING

by

Ian Sinclair

Glentop Publishers Ltd MARCH 1986

All programs in this book have been written expressly to illustrate specific teaching points. They are not warranted as being suitable for any particular application. Every care has been taken in the writing and presentation of this book but no responsibility is assumed by the author or publishers for any errors or omissions contained herein.

COPYRIGHT © Glentop Publishers Ltd 1986 World rights reserved

No part of this pUblication may be copied, transmitted or stored in a retrieval system or reproduced in any way including but not limited to photography, photocopy, magnetic or other recording means, without prior permission from the publishers, with the exception of material entered and executed on a computer system for the reader's own use

ISBN 1 85181 034 X

Published by: Glentop Publishers Ltd Standfast House Bath Place High Street Barnet Herts ENS SXE Tel: 01-441-4130

Printed in Great Britain by The Eastern Press Ltd., London and Reading Contents

PREFACE

CHAPTER 1 Reminders roundup • Storage space • Machine code • Principles of programming. Other languages

CHAPTER 2 Why use disks? • What is a disk system? • Tracks, sectors and density. Formatting disks. Storage space. The disk filing system • Loading and saving • More disk commands • Clearing, retitling and erasing. Backing up • Copying a named flle • Deleting flles • Wildcards and wiping. Protecting disks and programs. Renaming flles

CHAPTER 3 Text display. Screen clear and print location. Print fielding. Formatting numbers • Standard form • Money amounts • Titles and centering. Windows. Hard copy

CHAPTER 4 Working with numbers. Operators. Expressions. Translat­ ing formulae. Functions. Precision of numbers. Rounding • Number roundup

CHAPTERS Complex data • Number arrays. Lists. Number sorting. String arrays. Sorting string lists. Matrix operations

CHAPTER 6 Menus, subroutines and program design. Sectional program­ ming • A visual menu. Drive it yourself. Put it on paper. Foundation stones. Subroutine routine. Entry subroutine. The edit subroutine

CHAPTER 7 BASIC filing techniques. What is a flle? • Knowing the names • Disk filing • Serial flles • Opening the flle • Printing to the flle • Getting your own back. Updating the file. Relative flles • Relative flle facts • Relative rules • Reading back. More complications

CHAPTERS A database program - FILIT • First principles. The program in detail • The creation subroutine • Writing to the flle • Reading the flle • The entry subroutine. Reading

v CHAPTER 9 Graphics and sound. Data display. Graphs. Elaborations. Function graphs. Bar charts • Pie diagrams. Sounding off

CHAPTER 10 Printers. Printer types • Interfaces. The Centronics interface • The EPSON RX80 and LX80. TheJUKI 6100 daisywheel

APPENDIX A Tuning a TV receiver

APPENDIXB Standard form

APPENDIXC Boolean actions

APPENDIXD Suppliers

INDEX

vi Introduction

The Commodore 128 is a machine of astonishing versatility, which can be used in a number of modes. This book is concerned with more advanced aspects of using and programming the machine in its 128 mode. As such, this book is a follow on to Sean Gray's book Starting BASIC for this machine. The main theme of this second level book will be business use, and in particular how to design and write programs of the database type for your own use. Many of the more advanced features of Commodore BASIC in the Commodore 128 are very well suited for use in database programs, and these features are fully explained and illustrated here.

Since this is intended to be a second-level book, I shall assume that you already have a working knowledge of simple BASIC, as you might have obtained by programming a , or from the use of the Commodore 128 with the Levell book. Where a point might have been forgotten, I shall remind you of it, because I can't assume that every reader of this book will have had the same experience and background. The listings in this book have been printed out using 40 characters per line, so that if you are using the 40-character screen display, the listing should correspond to the screen display. Long listings have been numbered with line increments of 10 so that you can enter them using the AUTO command.

I must thank many people at Commodore, particularly Chris Kaday, for arranging the loan of a Commodore 128 machine around which this book was written. I am also grateful, as always, to Mick Bignall whose excellent combination of hardware and software, Printlink 128, made it possible to print out the listings on my EPSON printer. Finally, my most fervent thanks must go to Dr. P. Holmes of Glentop Publishers, who commissioned this book, and to his team who made my printouts and disks into a real book.

Ian Sinclair February 1986

vii

Chapter 1

Reminders roundup

The Commodore 128 provides for three sets of video output signals. These are the signals which are used by a monitor or a TV receiver to display characters, number, letter or graphical, on a screen so that you can see what the computer is doing. Throughout this book, the programming examples will be usable on any kind of display, but I shall assume that you are using either: 1. A TV receiver connected to the RF socket output of the Commodore 128 and correctly tuned (see Appendix A). or 2. A monitor with composite video input. Several manufacturers can now supply combined TV/Monitor units which are ideal for use with the Commodore 128 in its 128 mode.

The 40/80 column switch should be in its 4O-column position. The 80-column position and the use of the RGB type of monitor should be reserved for the CP/M mode of the C128. The use of CP/M with the C128, and writing in CP/M is beyond the scope of this book.

The types of video signals are not explained in the Commodore 128 manual, and it's important to know the differences, particularly if you want to use monitors that are not manufactured by Commodore. Video signals are electrical signals which will produce a picture on a cathode ray tube. The signals that the machine produces consist of a set of four: Red, Green, Blue and sync. The signals that are labelled with colour names control the colours on the screen and, when each of these signals can be fed separately to a monitor, the picture quality is as good as you can ever get with a colour monitor. The sync signal is used to ensure that the movement of the spot on the screen of the cathode ray tube, the spot that produces the picture, keeps in step with the colour signals. A monitor which has RGB inputs may not provide at all for any other type of signal input. For many purposes, it's preferable to combine the signals into one, called a composite video signal. This does not give such sharp, clear pictures, but it's certainly good enough for 4O-column work. Composite video signals are also used by video recorders, so that a monitor which uses these siJP.1als can also be used to obtain a better picture from your video recorder. Looking at it the other way round, if you have a monitor for your video recorder, it will accept the composite video signals from your Commodore 1281.

The third method of connecting video signals is by modulation. This starts with a composite video signal, and uses a miniature TV transmitter circuit to convert the signal

1-1 into the form that a TV receiver can use. This gives the poorest results of all, because TV receivers are not intended to give the quality of picture that you can get from a monitor. The advantage, however, is that practically everyone has a TV receiver. In the course of writing this book, I used both a TV receiver and a monitor, and all the programs are usable on either. One of the advantages of working in 128 mode with database programs is that it doesn't really matter too much whether your display is colour or monochrome, since it's being used mainly for words and numbers. In any case, a lot of business programs use the screen only for reminders, and the main output of the computer is to a paper printer, producing what is called 'hard copy'. The use of printers of various types with the Commodore 128 is dealt with in Chapter 10.

Storage space

When you write a program for a computer, that program has to be stored in the memory of the computer, and it will usually need more memory when it runs. Whatever may appear on the screen when you switch your Commodore 128 on (122 365 bytes free), the amount of memory that can be used for a BASIC program is, at most, 58 109 bytes. You can check this for yourself by using P R I NT F R E ( 0 ). This corresponds to around 56K of free memory. Remember, however, that the C64 allowed only about 28K free for BASIC, and you'll appreciate that the Commodore 128 does allow much more space. This is why we can make use of the machine for so many serious purposes, unlike machines of the past. The figure of 122 365 bytes is obtained by adding in the number of bytes that are free for storing BASIC variables. This is obtained from P R I NT F R E ( 1 ) , and gives 64 256 when no program is in memory. By separating these two parts of memory, it's possible to run much longer programs than can be used on other machines which supposedly have 128K of memory. Once again, this is absolutely ideal for the more serious type of program.

The storage space on a disk depends on which disk drive you are using. I tested the Commodore 128 with an old style 1541 disk drive, and also with the much later type 1570. The old type of drive will store up to 169 984 bytes on one side of a disk, more than enough to fill up your computer. The 1570 disk drive packs its disks with more bytes - a maximum of 339968 per disk. We'll look in more detail at disk storage and how it is achieved in the following chapter.

Machine code

In this section we will deal with saving and loading, particularly of machine code. If you fmd it a little heavy going, just skim through then return later.

The Commodore 128, unlike previous Commodore machines, provides for comparative­ ly easy writing, saving and loading of machine code programs. The topic of writing machine code progratps for the Commodore 128 is one that would fill a book by itself,

1·2 but saving and loading is a topic that we can deal with now. Machine code programs can be saved and loaded either from BASIC, or from the Monitor which is built into the C128. Dealing with BASIC fIrst, the machine code commands are B LOA 0 and B S AV E, and I shall assume that you are using a disk drive. To save machine code, or any set of numbers stored in the memory, you need to know the memory addresses of the numbers. As an example, take a very simple BASIC program (Figure 1.1) consisting of four lines of REMs. If you type in this program and LIS T it, you can use the monitor to fmd how much space this BASIC program takes up. To do this, type M0 NIT 0 R and press . The monitor announces its presence with a display of some numbers which are of no particular interest to us at the moment. We can then look at what is stored in the memory. When you type a BASIC program, and are not using high-resolution graphics, the bytes of BASIC are stored starting at address #OICOO, where the hash (#) sign means that these numbers are in hexadecimal. Typing M0 1 COO will make the monitor print out the contents of 96 bytes of memory, and the sample BASIC program is included among these 96. You can then escape from the monitor by typing X . As you can see from Figure 1.2, this part of the memory contains data, some of which is left over from the Printlink program which was used to interface the EPSON printer for me to print out the program. Your computer will, of course, contain something different but the principle is the important thing. The important part starts at address #OICOI, because that's the fIrst part of the BASIC. When a BASIC program is stored in memory, it uses four bytes at the start of each line to point to the next line and to store the line number. The fIrst two bytes of the fIrst line are in addresses OICOI and 01C02, and they are stored in reverse order. As they are shown, these bytes are 07 and IC, but they represent the number IC07 (the fIrst zero, the bank number, is omitted). This address of I C07 is where the next line of BASIC will be stored. The next two bytes are OA 00, making the number OOOA. This is 10 in denary, the line number of the fIrst line. The following byte is #8F, which is the one-byte code for REM, and the end of the line is marked by the 00 at address #01 C06. The next line starts at #OIC07, just as the fIrst two bytes showed. Now if you pick your way through this, you'll see that the program ends when there is no 'next line'. Line 40 ends with the usual 00 in address #OICI8, but following that is another pair of zero bytes, instead of a next-line address. This is how the end of the program is marked.

READYm 10 REM 20 REM 30 REM 40 REM READY.

A four-line program used to explore how a program is stored in the memory.

FlGUREl.l

1·3 READY.

MONITOR PC SR AC XR YR SP B000 00 00 00 00 Fa

>01C00 00 07 lC iliA 00 SF 00 mD: ...... )01C08 1C 14 00 8F 00 13 1C 1 E: •••••••• >01C10 00 8F 00 19 Ie 28 00 BF: ••• II.( •• )01C18 00 00 00 00 00 00 26 1C: •••••• '1<. }0:tC20 28 00 A0 :2:0 34 00 00 00: (. 4 ••• :>01C28 C7 28 32 .37 29 3B 22 45:G(27);"E :~01C30 22 00 3A lC 28 00 A0 2m: ... =• (. >01C38 34 00 00 00 34 00 00 00:4 ••• 4 ••• >01(:40 50 4F 4B 45 20 49 46 20: POKE IF c-r:; >1lI1C48 41 4C 54 45 ...... :.. 45 44 20: ALTERED >01C50 28 41 4E 44 20 50 52 4F: (AND PRO >0lC~j8 47 52 41 4D 20 52 45 2D:GRAM RE-

The monitor display that reveals memory contents.

FIGURE 1.2

If we want to save all this as machine code, then we have to save everything from OICOO to OICIA. Now in denary, these numbers are 7168 and 7194. To convert, you simply type:

PRINT DEC(1I1COO II ),DEC(1I1C1A II ) assuming, of course, that you have returned to BASIC by typing X before this stage. The monitor will not recognise any BASIC commands. Knowing these numbers, we can use B S A V E to save this chunk of bytes just as if it were a machine code program. To do so, we type: BSAVE II REMS II ,BO,P7168 TO 7195 and press

Now how can we prove that we have saved this block of code? It's fairly easy - you can type NEW, or switch off, or whatever you want to do to convince yourself that the machine has no program in it. When you are up and running again, check with a LIS T that there is nothing to be found, and then type BLOA DII REM S II. Note that you don't need to add any address numbers if the bytes are to be returned to the same addresses as

1-4 you saved them from. It's a different matter if you want them to be shifted, and we'll leave that alone in this book. The point that is important here and now is that the BLOAD puts the bytes back where they came from, so that you have the BASIC program sitting in the memory once again, as a LIS T will demonstrate.

Now using BS AV E and B LOA 0 just to save and load a BASIC program is completely unnecessary, since we have 0 S AV Eand 0 LOA 0 which do the job more simply. The point is that any batch of code can be saved with B S AV E and B LOA 0, and the manual suggests sprite data. Another use that is made of the BS AV E command, however, is to save a mixture of BASIC and machine code. Suppose you had a short BASIC program in the memory, and you placed the bytes of a machine code program following it. The BASIC program could then contain a SYS command with a number which made the machine code run immediately following the BASIC. You could then save all of this code as if it were one piece, using BS AV E with the starting address of 7168 and whatever ending address was needed by the machine code program. Having saved this, you could then load it and run it. What makes it all really useful is that you can load it with an ordinary 0 LOA 0 rather than a B LOA D, and the whole lot, including machine code, will be loaded in. You can also, of course, use RUN" F I LEN AME" to load and run the mixture of BASIC and machine code. A lot of commercial programs for the Commodore 128 are constructed in this way, and it's useful to be able to separate the pieces using the monitor. You might, for example, want to record the machine code portion separately and include it in one of your own programs.

You can also carry out this saving and loading with the help of the monitor. If you get into the monitor using M0 NIT 0 R

Principles of programming

Even if you have written some programs in BASIC you don't necessarily fully understand what programming is about. In the space of a book that is mainly concerned with the use of the Commodore 128, it's obviously impossible to deal with everything that is known about programming, because people have been programmjng computers for some time now, and there's a lot known. What we can do, however, is to outline a methodical approach which will be very useful to you, and to illustrate it in each section of this book. As far as possible then, each listing in this book is part of a process that leads up to a couple of useful programs, and also provides you with routines that you can use in your own programs.

1·5 To start with then, how do you know if a problem can be solved using a computer? Computers are machines which obey instructions and which work in a way that has been decided by their designer. Just to illustrate this, think of a pocket calculator. A pocket calculator is useful, but you cannot ask it to name the best-looking car in the world. There are two reasons for this. One is that the calculator is designed to carry out a limited range of actions with numbers. It can't 'name' anything, nor has it been programmed with any names of cars. The other reason is that items like 'good looking' are human value-judgements that only a human can make. If you want to use a computer to solve a problem, the problem must be one that can be reduced to numbers, and you need a program that will carry out the action. It's programs that solve problems, not computers. Having said that, you need a computer that is capable of running your program.

Every problem looks different, but the steps that you need to use in writing a program to solve a problem are similar for all problems. First of all you have to decide whether the problem can be solved with a program. That might seem obvious, but is isn't always so simple. A computer cannot make value judgements, so it can't decide who is the best applicant for a job, for example. If, on the other hand, you could draw up a checklist of skills and a set of points that could be awarded for answers to questions, you could reduce the choice to a number of points, and this the computer could cope with. 'Best' it doesn't understand, 'most points' it does. It's important to remember that when you fmd a computer used in any form of selection that the value-judgements have been made by humans at some stage, and the computer is doing little more than acting as a scoreboard.

If the problem is one that can be solved by using the computer then the next item is to decide what sequence of steps will lead to a solution. The important point here is 'sequence'. A computer does one thing at a time, and the program sets out what this sequence of steps will be. Once you have decided what sequence is needed, you are well on the way to creating a program. Typical steps would be typing data into the computer, making calculations, and printing data out (using the screen or the printer). For a longer program, you might have steps of typing data in, saving data on a disk, replaying data, rearranging or selecting data, and printing data. No matter how simple or how complicated the program is, the sequence has to be correct, or you can't design the program correctly.

1-6 Take a simple example. Suppose you have to produce sets of prices which shows item names, price before VAT, VAT amount, and price including VAT. This is certainly a problem that can be solved using the computer, because it deals with words and numbers, and all it has to do is make calculations. At its simplest, the program might require you to enter the name of the item and its pre-VAT price. The screen would show these items. The calculation part of the program would then swing into action, producing the amount of VAT, and the total price. The next step would be to print this extra data on the screen. This is a simple three-step program outline, and from it a program could be written. You could summarise the steps in a diagram, as in Figure 1.3., with the steps arranged in order from top to bottom.

START ENTER item name and price CALCULATE VAT and totaL PRINT VAT and totaL END

An outline plan for a three-step program.

FIGURE 1.3

Too simplistic? Perhaps, but the value of something like this is that you can build upon it. Suppose, for example, that you wanted to keep entering items and prices and getting the information until you entered X as the name of an item. This means using a loop, as you know, and it makes the plan look as in Figure 104. This time, the loop has been marked out with the words REPEAT and LOOP BACK at the start and the finish. For any loop, we need an ending condition, and this has been stated in the same place as the ENTER line, using a curly bracket to indicate that this is more detail added here. The main outline of the program has stayed very much the same, however. This type of program design is much easier than the use of flowcharts, because it's easier to go from a simple plan to a more complicated one. Always start your program design with a simple plan, and then add the complications later. In this way, you can design the program better, test it in stages and, if necessary, make changes that will not affect very much of your program. Design like this is called 'top-down', because you start with the most important items, at the top of your list, and work down to the detail later.

START REPEAT { i 5 name X? ENTER item name and price if 50, Leave Loop CALCULATE VAT and totaL PRINT VAT and totaL LOOP BACK END

An outline plan modified so as to include a loop. The start of the loop is marked in the plan by using the word REPEAT, and the end by using LOOP BACK.

FIGURE 1.4

1-7 Suppose now you wanted to complicate things considerably more. You want now to enter the list of items and prices, end it with item X, record the whole list on a disk, and when the disk is played back, produce a printout of item, price, VAT, and total on paper. This only means modifications to the plan, not a new plan, as Figure l.S shows. The plan keeps to its order of actions, but this time it consists of two sections. The first section is a loop that accepts entries and saves data on a disk. The ending condition is the usual 'X' , but this time it leads to a second loop, marked in this example by the NEXT in the plan. In this loop, the data is taken from the disk, tested, the calculations made, and the results printed on paper. The sequence in each loop is just as it was in the simple plan, but this is now a primitive database program. Any program that consists of entry of data, storage, particularly on disk or cassette, and then calculation, selection or rearrangement of data, is a database program.

START REPEAT ENTER item name and price { is name X? SAVE item name and price if so, Leave Loop LOOP BACK NEXT REPEAT . f is name X? PLAY BACK item name and prlce l if so, Leave Loop CALCULATE VAT and total PRINT item name and price, VAT, total LOOP BACK END The plan expanded to cover saving on disk and recovery of data from a disk.

FIGURE 1.5

As we go through this book, then, we shall be working to simple plans and looking at pieces of program that carry out the actions that are needed in the plans. When you design your own programs, you can draw up plans for yourself, and make use of the program sections in this book to carry out the functions that you want. You can also make use of routines from other programs that you see in print. Even if you do not fully understand how these routines work (and if they are machine c<;>de routines that's not unusual) you can always use them provided you know what they do. Knowing what they do means knowing what has to be put in, what happens to it, and what comes out, and it's on that basis that we'll be working with program sections in this book.

Other languages

BASIC is the language that is suppied with your Commodore 128, together with the Assembly Language of the monitor. BASIC was originally devised for teaching purposes, and though it has changed considerably over the years, it is not always the language that is best suited for all purposes. BASIC is a language that can do almost everything in computing reasonably well. There are a lot of other languages, however, that are better than BASIC for many purposes . There are two items in particular that

1-8 can be compared. One is speed of execution, and the other is structure. Of the two speed is easier to understand. The BASIC in your Commodore 128 is an interpreted language. All computers carry out instructions in machine code, a set of numbers stored in the memory. Unless you program direct in machine code, then, all you can do is to select a set of machine code routines to run, one after the other. When you program P R I NT A S in BASIC, you are asking the machine to fmd the location of a set of ASCII codes that make up AS, then to fmd the routine called P R I NT, and use this routine on the codes. Typing it as P R I NT A S is a lot simpler, but the machine has to do a lot of work to carry out the action. The trouble is that it takes time to look up to fmd where routines are stored. If you type a simple loop, such as: 100 FOR N=1 TO 20 110 PRINT AS 120 NEXT then when this runs, all the actions of looking up the routine for P R I NT and finding where AS is stored (in line 110) will have to be carried out 20 times. The machine cannot look up the routine and the variable once and then use these quantities twenty times. Interpretation means doing each action as it comes along, and that makes it slow. It does mean, however, that you can stop at any point, print values of variables and then resume the program action again.

The alternative to interpreting a program is compiling. You write your program, just as you normally do, and then store the result on a disk. You then load in a compiler program, and command it to get to work on the stored program that you have on the disk. The compiler does all the looking up actions, and stores the result as machine code, ready to run. This new version is a machine code program, which you can't stop in mid-action and can't easily edit. This way, all the looking up has been done before the program runs, and when the machine code program runs, it will run fast. You can buy compilers for BASIC, but in general, BASIC is not a language that compiles to really fast running code. It wasn't designed to be a compiled language, but as one that could be stopped at any point to see what was going on.

1-9 The other point about alternative languages is structure. Structure means that you can very easily design programs in sections which can be tested independently, and then joined together into a large program. To be structured, a language has to contain loops like the REPEAT .. UNTIL and WHILE .. WEND type, replacing the use of GOTO to make such loops. In addition, the language should use procedures, a type of subroutine which is called into action by typing its name rather than a line number. Now the old version of BASIC as supplied with the C64 was completely unstructured, and the BASIC 7.0 of the Commodore 128 is very much better. Since the Commodore 128 can lead you to better BASIC programming, it's not so difficult to take one step onwards to really structured languages.

The two main compiled and structured languages that are used in small computers nowadays are PASCAL and C. Both compile to fast-running machine code, and both languages are organised in a way that is very different to BASIC. At the time of writing, no compilers were announced for the Commodore 128 running in its 128 mode, but for the C64 mode, the well established Oxford Pascal is available. With the machine in its CP/M mode, a huge variety of languages can be run, with many versions of Pascal and C available. The languages from HiSoft are of particular interest, since they can be bought for a very wide range of machines. If advanced programming is likely to grip you, and if you want to write programs for professional use, then I suggest that you investigate these possibilities.

1·10 ~ ______C_h_a_p_te_r_2 ______~1 About disks and disk systems

Why use disks?

One of the questions that you have to face as you move out of the beginner stage is 'why use disks?'. The obvious reasons are not necessarily the most important ones. To start with, a disk offers much faster operation. If you use a machine to load one program, and then use that program (a game perhaps) for several hours, this speed advantage may be of little use. It certainly would not justify the cost of a disk system. On the other hand, if you are developing programs for yourself, you may want to load a program, make changes, and save it again before you try the new version out. This can be very tedious if you have to wait for cassettes to load and save. It's even more tedious because cassette operation is not automatic. You either have to store each version of the program on a new cassette, or use a long cassette (C60 or C90), with each program version noted as a starting point on the tape counter. If you use separate cassettes, you may fmd yourself holding a dozen of them by the time the program is complete. If you use C90's, you will need paper to note the tape counter readings of each version of the program. Either way, it's tedious. Another class of user who will benefit greatly from the use of disks is the text writer. If you use the Commodore 128, as many users do, to create and edit text, with the Easy Script or Viza Write text editor programs, then the time that is needed for cassettes to load or save the data is a defmite handicap. If you want to load a piece of text to change a few words, and then store the new version back, the loading and saving time is a significant part of the total.

The overwhelming advantage of using a disk system, however, is automatic operation. The Commodore 128 cassette system does, at least, permit the motor of the cassette recorder to be controlled, and it allows programs or data flIes to be referred to by name. If you try to load a program called T EXT I N DEX, however, without winding the cassette back to the beginning, you may fmd that the program cannot be loaded. This is because recording on tape is 'serial' - you start recording at the beginning of the tape, and wind it on to the end. If you then want to load something which is at the start of the tape, you have to rewind it for yourself. The computer does not control the fast forward and reverse actions, because the cassette recorder was never designed as a way of storing computer programs and data. The disk system, in contrast, is completely computer controlled. The only manual action is that of putting in the correct disk and making sure

2-1 that it is the right way round. On loading, the computer will then use its disk to fmd the program or other material that you want, from its title. Having located the start, it will then load the data into the computer in a time of a few seconds. Saving is just as automatic. The DS A VE command is followed by a fllename (and other information in some cases), and pressing carries out the actions of fmding unused space on the disk and saving the data. The automatic nature of this action also means that a 'catalogue' can be kept on the disk itself. This means that you can insert a disk and obtain information on what is stored on it without the need to play back the whole disk. Though you can also fmd the names of programs on a cassette you have to replay a whole cassette to see its catalogue.

In addition to these compelling reasons for using disks, we must add the extra commands that the permits. Some computers go much further in this respect, so that their disk system add;; a BASIC of its own. In the Commodore 128 disk system, the new commands are all closely tied to the use of the disk system itself, and we shall examine them in detail. Several of the extra commands, however, allow you to obtain a lot more information about how the data is stored on the disk. This will not be of immediate use to you if you haven't used disks before, but its usefulness will become apparent before long.

Finally, the use of disks can bring order and reliability to what can be a very haphazard business. When you use cassettes for filing programs and data, you inevitably end up with a very large number of cassettes, all of which have to be catalogued. I had over two hundred cassettes at one stage! It can take a considerable time to locate a program on a cassette. Although a disk cannot hold quite as much information as a C90 cassette, the information is much easier to get at. This encourages you to use the whole of a disk whereas you might use only the fIrst ten minutes of a C90 cassette. It's quite possible to fmd, for example, that you can keep all of the programs that you want to use on one single disk! This in itself is such a liberation that it almost justifIes the use of disks by itself. Disks are slim and compact to store, so that a box of ten disks, holding a huge number of programs, will take up little more space than a couple of cassettes. The reliability of disk recording means that you can make a backup copy of a valuable program, and be fairly certain that you will never need it. Unless you spill coffee all over a disk, demagnetise it or tear it apart, it's unlikely that you will lose a program. Cassettes are never so reliable.

2-2 What is a disk system?

Disk system is the name that is given to a complicated combination of hardware and software. Hardware means the equipment in boxes, software refers to the programs which can be on disk or in the form of chips that plug into the machine. A disk system comprises the disk drive (or drives), the disk controlling circuits, and the disk operating system. The unique feature of the Commodore disk system is that all of these parts of the disk system are contained in the one box, the disk drive box. The disk drive is, in fact, a miniature computer in its own right, complete with its own memory. The drive is linked to the Commodore 128 by means of the provided data cable. This terminates in six-pin plugs at each end. There are two sockets on the disk drive, and the data connection is made by plugging one end of the cable into the socket on the computer (the socket next to the place where the cassette unit plugs in). The other end of the cable can go into either of the sockets on the disk drive. If you then wlUlt to connect a Commodore printer, it can plug into the other socket on the disk drive.

Two types of disk drives can be connected to the Commodore 128. The older type 1541 driv.e is very slow in operation, but when the Commodore 128 was introduced it was the only disk drive that was available. The faster and much superior 1570 drive became available only several months later. Both types of drives, 1541 and 1570, obey the same disk commands, and the main difference as far as the user is concerned is that the 1570 is faster, stores more data on each disk, and can be used for CP/M. For all of the listings in this book, the older 1541 drive was used.

The controlling units for the disk system are in the form of circuits which are all contained within the disk drive unit, along with the Disk Filing System (DFS). A 'me' in this sense means any collection of data which can be stored on the disk. The DFS is a program, and most computers use a 'DOS-disk' to hold this program, DOS is short for Disk Operating System. When this is done, a lot of the RAM memory (the memory that is free for you to use) is needed to hold the DFS. The Commodore 128, however, uses another chip, the DFS chip, to hold this information, and this leaves all of the memory of the Commodore 128 free. Some memory has to be used, and this is also fitted to the disk drive unit, so that none of the 56K of the Commodore 128 that is normally available for BASIC programs need be taken up when the disk system is used.

Tracks, sectors and density

The language of disk recording is very different from that of cassette recording. If your sole concern is to save and load programs in BASIC, you may possibly never need to know much about these terms. A working knowledge of how disk storage operates, however, is useful. To start with, it can clear up the problem of which disks are suitable for yoUr drives. At a more advanced level, it can allow you to extract information from damaged disks, and to make changes to the information that is stored on disks.

2·3 Unlike tape, which is pulled past a recording/replay head, a disk spins around its centre. When you insert a disk into a drive, it locates itself so that, when the drive operates, a hub engages the central hole of the disk, clamps it, and starts to spin it at a speed of about 300 revolutions per minute.

The disk is a circular flat piece of plastic which has been coated with magnetic material. It is enclosed in a square plastic jacket to reduce the chances of damage to the surface. The hub part of the disk may also be reinforced to avoid damage when it is gripped by the drive. The surface of each disk is smooth and flat, and any physical damage, such as a fmgerprint or a scratch, can cause loss of recorded data, The jacket has slots and holes cut into it so that the read/write head and various other components can touch the disk at the correct places.

Through a slot that is cut in the jacket (Figure 2.1), the head of the disk drive can touch the surface of the disk. This head is a tiny electromagnet, and it is used both for writing data and reading. When the head writes data, electrical signals through the coils of wire in the head cause changes of magnetism. These in turn magnetise the disk surface. When the head is used for reading, the changing magnetism of the disk as it turns causes electrical signals to be generated in the coils of wire. This recording and replaying action is very similar to that of the cassette recorder, with one important difference. The cassette recorder was never designed to record digital signals from computers, but the disk head is. The reliability of recording on a disk is therefore very much better than you can ever hope for from a cassette. .:::::::: )\\ Head slot 1-{// (Do not touch surface)

Disk put into drive this way + Write-protect notch

A typical disk, showing the slot for the head of the disk drive, and also the index hole which the machine uses to locate the sector positions around the track.

FIGURE 2.1

2·4 Unlike the head of a cassette recorder, which does not move once it is in contact with the tape, the head of a disk drive moves quite a lot. If the head is held steady, the spinning disk will allow a circular strip of the magnetic material to be affected by the head. By moving the head in and out, to and from the centre of the disk, the drive can make contact with different circular strips of the disk. These strips are called 'tracks'. Unlike the groove of a conventional record, these are circular, not spiral, and they are not grooves cut into the disk. The track is invisible, just as the recording on a tape is invisible. What creates the tracks is the movement of the recording/replay head of the disk drive. A rather similar situation is the choice of twin-track or four-track on cassette tapes. The same tape can be recorded with two or four tracks depending on the heads that are used by the cassette recorder. There is nothing on the tape which guides the heads, or which indicates to you how many tracks exist.

The number of tracks therefore depends on your disk drives. The vast majority of drives for other machines use either 40 or 80 tracks. 40-track disks use 48 tracks per inch (tpi), and 80-track drives use 96 tracks per inch. The Commodore 1541 disk system, however, uses 35 tracks. This does not force you to fmd any special variety of disks, however, because the tracks are put in place by the recording head, not by anything on the disk itself. The 1570 disk drive can be used either as a 35-track or as a 40-track drive, depending on whether you are using the computer in its C64/C128 or its CP/M mode.

Once you have accepted the idea of invisible tracks, it's not quite so difficult to accept also that each track can be invisibly divided up. The reason for this is organisation - the data is divided into 'blocks' , or sectors, each of 256 bytes. A byte is the unit of computer data; it's the amount of memory that is needed for storing one character. Each track of the disk is divided into a number of 'sectors', and each of these sectors can store 256 bytes. Conventional 40 or 80 track disks use only ten sectors per track, but the

2-5 Commodore 1541 and 1570 use considerably more. On the outer tracks, which are longer, the disk can use 21 sectors, and on the shorter inner tracks, it uses 17 sectors. This allows the 1541 or 1570 disk system to store a lot more bytes per disk that a lot of other disk systems for other computers. The Commodore manual usually refers to 'blocks'; in this book, I prefer to use the word 'sector' as used by most other manufacturers.

The next thing that we have to consider is how the sectors are marked out. Once again, this is not a visible marking, but a magnetic one. The system is called 'soft-sectoring'. Each disk has a small hole punched into it at a distance of about 25 mm (I") from the. centre. There is also a hole cut through the disk jacket, so that when the disk is turned round, it possible to see right through the hole when it comes round. When the disk is held in the disk drive, and spun, this position can be detected using a beam of light. This is the 'index marker' , and the head uses this as a reference to its starting point, putting a signal on to the disk at this position, and at seventeen to twenty-one equally spaced others, so as to form sectors (Figure 2.2). This sector marking has to be carried out on each track of the disk, which is part of the operation called 'formatting'.

The inner and outer tracks of a Commodore formatted disk. This arrangement is unusual, and disks formatted in this way can't be read easily by other machines.

FIGURE 2.2

2-6 Formatting disks

Formatting disks, as we have seen, consists partly of the action of 'marking out' the sectors on a disk. The formatting action should also, however, test the disk. This is done by writing a pattern to each sector, and checking that an identical pattern is read back later. Failure to do so indicates a faulty sector, and a disk with such a fault should be thrown away - once you are sure that the fault is genuine. That last remark needs some explanation. When disks are manufactured, they are tested and the best ones can be used for the most demanding recording systems, using double density, both sides of the disk and 80 tracks. If only one side of the disk is good enough, the disk can be sold as single sided. If the disk will not record tightly packed data reliably, it may be good enough to work with single density and with 40 tracks. Never be tempted to try to operate a disk beyond its stated limits, because it has already been tested beyond these limits and has failed! You may sometimes read descriptions (mostly copied from an article in a U.S. magazine in 1978) of how to cut extra holes in a disk jacket and use the other side of the disk. This can work, but it is fraught with problems. One is, as we've seen, that the disk has already been judged as not being up to it. The other is that when you turn the disk round and use its other side, you are revolving the disk the other way, and this inevitably releases some dust that was quite comfortably trapped when the disk was revolving in its original direction. My advice is - don't do it! For your Commodore 1541 disk drive, you can use single-sided, single density disks, though for the 1570 running CP/M its better to use double density types. At the time of writing, these can be bought for as little as £14.50 for a box of ten if you shop around a bit, and the single density disks are even cheaper - 79 pence each at one supplier.

Formatting, then, consists of marking out sectors and testing them. This takes about a minute on the 1541 drive, but the computer can be used for other things while this is going on, as long as the other things do not call for disks to be used. The reason is that the computer only has to send the formatting command to the disk drive, and the disk drive will then get on with the job of formatting by itself. This leaves the computer free for other jobs. If there is anything wrong with the disk, then the red light on the disk drive will flash to bring the fault to your attention. If you fmd a disk fault at the formatting stage, then return the disk.

~...... 17-- Ii

2-7 The formatting action is carried out when a H E A D E R instruction is typed and entered. First of all, insert a new disk in the drive, making sure that it is the correct way round. This is illustrated in Figure 2.3. On the 1541 drive, the flap on the front of the drive unit is opened by pushing the bar. This allows the flap to lift, and if a disk is already in the drive, it will be ejected. On the 1570 drive, the lever is swung aside, any disk in the drive removed and the new disk inserted. Hold your new disk with the label facing up and the write-protect notch to your left and slide the disk into the slot. Don't use any force to do this, because you can jam the disk if you do so. Press the disk in fIrmly until it stays put. Now press the bar down (1541 drive) or swing the lever round (1570 drive) until it clicks into place. The disk is now ready for formatting.

DRIVE

Locating notches -L ~-

Write-protect notch "~~ __--,

I I I Your own label Maker's label

Inserting a disk into the drive.

FIGURE 2.3

The keyword for formatting is HE A DE R, and it can be followed by a set of letters and numbers, some of which are optional. Suppose, as is reasonable, that you have one drive only. You must supply, following HE A DE R, a name, which can be a word of up to sixteen characters and will become the name of the disk. This name has to be enclosed in quotes, and has to be followed by a comma and the letter I. This is followed in turn by two characters to be used as the identity code (ID). If you use more than 16 characters for the name of the disk, the machine will issue a S T R I NG TOO LON G error message, and the disk will not be formatted. The ID characters are placed on each directory entry to identify data that belongs on that disk. When the header name and ID are acceptable, the screen message will be ARE YOU SUR E, and typing Y and pressing

2-8 Finally, the formatting action writes on to some sectors on the eighteenth track which is reserved for storing information about the contents of the disk itself. To put it crudely, the disk system reads the first few sectors of this track to fmd if a program is stored on the disk, and then to fmd at which sector the program starts. With this information, the head can then be moved to the start of the program, and loading can start. This part of the track is known as the ''{BAM) and directory. The BAM keeps a record of what tracks and sectors have been used, and which of them are free for further use. The rest of track 18 is used for directory entries. These are numbers which indicate which track and sector is used for the start of each ftle that is stored on the disk. Along with this information is the ftlename of the program (up to sixteen characters), and information that is needed when the ftle is not a program but some other form of data. When you wipe a program or some data from the disk, all you do is the remove its directory entry - the data remains on the disk until it is replaced by new data. This can sometimes allow you to recover a program that you thought you had erased. When a disk has been formatted by the use of HE AD ERin the form shown above, it can have its directory wiped for re-use by a shorter header command which uses only a new disk name (or an old name again). If you have more than one disk drive, or if you have several programmers using disks on the drives, you can add other codes, the drive number and unit number, to the HE A D E R command as the manual shows. To check that your disk name is in place, type DIRE CTOR Yore ATAL 0 G, and press

Storage space

How much can you store on a disk? The Commodore 1541 system uses 21 sectors on each of tracks 1 to 17,19 sectors on tracks 18 to 24,18 sectors on tracks 25 to 30, and 17 sectors on tracks 31 to 35. This makes a total of 683 sectors, or 'blocks'. Of these, one set of 19 sectors, the whole of track 18, is used for the BAM and directory entries, and this leaves 664 sectors free for you to use. The same storage system will be used when you connect the 1570 disk drive to use with the machine in C64 or C128 modes.

Each of these sectors will store 256 bytes, which is a quarter of a kilobyte. Multiply 664 by a quarter, and you end up with a figure of 166K on a single sided 40 track drive. Not all of this will normally be usable, however, because data is not stored at every point on the disk. Suppose you have a program that is 1027 bytes long. The disk operating system will split this into groups of 256 bytes, because it can record 256 bytes on one sector. When you divide 1027 by 256, you get 4.01171875 - but the DFS does not deal with fractions of a sector. Five sectors will be used, even though the last sector has only three of its 256 bytes recorded. When the next program is saved, it will start at the next sector, so that the unused bytes are surrounded, and there is no simple way of making use of them. If you save a lot of short programs on the disk, you will find that a lot of space may be wasted in this way. As well as this wasted space, each program will have a separate directory entry, and when the directory track is full, no more entries can be accepted. The 1541, however, allows up to 144 directory entries on a disk, so you would have to be

2-9 very fond of short programs to run out of directory space! The same applies to the 1570 disk drive when used in its C64 or C128 modes.

The large amount of storage space, l66K, on a 1541 or 1570 disk contrasts with the 57K or so which you have available for BASIC programs on the Commodore 128. For long programs, then, a disk system can be used as a form of extra memory. If a long program is split into sections, the sections can be recorded on a disk, and a master program entered into the computer. This master program can then call up different program sections from the disk as needed, giving the impression that a very large program is operating. The use of a disk system therefore not only allows you to load programs more quickly and store a lot of data, it also allows you to use the computer as if it had a very much larger amount of memory.

Figure 2.4 lists some precautions on the care of disks. These may look rather restrictive, but remember that a disk is precious. It can contain a lot of data, perhaps all of your programs. An accident to one disk, then, can wipe out all your work at the keyboard! Always make a backup copy, and always take good care of your disks. If you leave a fingerprint on a piece of tape, you may cause some loading difficulties on that piece of tape, but it's unlikely that you will lose a whole program. A fmgerprint on the surface of a disk could make the directory impossible to read, so that the whole disk becomes useless. Similarly, a disk can be demagnetised by strong magnetic fields. You will fmd such fields in the vicinity of loudspeakers, TV receivers, monitors, headphones, and electric motors. All of these should be regarded as potential disk-killers.

Care of disks 1. Don't bend the disks. They may be called 'floppy' disks, but the magnetic coating is liable to be damaged if you bend them. 2. Always buy disks with hub reinforcing rings. The mechanism that clamps the disks in place will soon tear the centres of unreinforced disks. If you have any such disks with programs on them, make backup copies onto disks with reinforcements. 3. Avoid touching the magnetic surface where the head slot is placed. Never try to take a disk out of its casing. 4. Store your disks, in their envelopes, in a box. You can keep them in the boxes (for ten disks) in which they arrive, or in boxes made for the purpose. Keep them away from dust, smoke, liquids, heat and sunlight. 5. Avoid, at all costs, magnets, or objects that contain magnets. These include electric motors, shavers (not many people shave while they are computing, but you never know), TV receivers and monitors, telephones, tape erasers, electric typewriters, and a host of other things you might be tempted to place disks on. 6. Don't use a ball-point pen to write on to labels on the disk. Berol make a ' pen' which has a point that will break off if you exert too much force. A felt-tip is suitable, but you must not press too hard while writing. 7. Use disks that are suitable for the drive you have. Do not use double-sided disks on a single sided drive.

Taking care of your disks. Magnetism is the greatest enemy of disks, mainly because it's invisible. FIGURE 2.4 2-10 The disk filing system

The Disk Filing System (DFS) is, as we have seen, a program. This program is not written in BASIC, but in the form of direct commands in machine code to the microprocessor (the 8502) which operates the Commodore 128. The purpose of the DFS is to interpret the disk commands that you type, and to convert these into signals that can be used to control the disk system and to move data to and from it. Note that the name is Disk filing System, not simply Disk System. Filing implies the storage of data (such as string or number arrays) as well as BASIC or machine code programs. The DFS is therefore equipped to carry out the necessary organisation of data required to store it on disk and recover it later. That's something. that we'll come back to in Chapter 7. Meantime we'll keep to the more straightforward uses of the DFS. Rather than looking at the commands of the DFS in alphabetical order, we'll look at them in the order that is most likely to be of use to you, starting with the use of disks for storing programs. First, however, we need to look at how the use of a DFS modifies the Commodore 128 machine, and what problems this can create for you.

The fIrst thing that you have to get used to is the order of switching on and off. When the disk system is switched on, it needs a short time to prepare itself for use, and in this time it's important that it should get no signals from the computer. As you switch on the components of your system, then, you must always ensure that the disk drive is switched on before the computer. If you fmd that you have managed to reverse the order, then switch both off and start again. You will see on the front of the 1541 or 1570 disk drive a pair of lights. The one on the left-hand side is green, and it simply indicates that the power is switched on to the drive unit. The red light is a 'busy' warning, and it will be on while the disk unit is operating. You must never take a disk out of the drive or put another disk in while this light is on. An error in a disk operation is signalled by this red light flashing, and later in this book we'll deal with how to fmd out what the error is. Some programs will cause the red light to blink irregularly as they load, but you will hear the drive whirring round as well, indicating that this is not an error. When you switch the drive on, you will see the red light come on momentarily. Only when it has extinguished can you safely switch on the computer. When you do this, the red light will come on briefly again, then switch off. You do not have to have any disk in the drive while you switch it on, but you should put a disk in before you switch the computer on. One way of ensuring that the disk drive always comes on fIrst is to keep it switched on and plugged in to the same socket as the computer. When you switch the socket on, the disk drive will be switched on, and you can then wait a moment before switching on the computer.

Loading and saving

Once a disk has been formatted, you can use it for storage. The method that you follow for BASIC programs is very similar to the method that you use for cassette storage. If, for example, you have some BASIC programs on a cassette that you want to save on to a

2·11 disk, then the procedure is as follows. Connect up the cassette recorder if you have not already done so, and place the cassette that you want to use in to the recorder. Place a formatted disk into the disk drive. Load the program that you want to save into the computer. If this program is on cassette, you will have to start by typing LOA D " NA ME" (or just LOA D"" if you want the next program on the tape), and pressing

To load a program from a disk, you can type D LO AD" MY P ROG" , for the 1541 or DLOAD"MYPROG",O for the 1570, using whatever fllename you have chosen, and press the

PRINT DS$ or the shorter version ? DS $ and press

Loading is generally much faster than storing, because the DFS carries out a check on data when it records, but not to the same extent when it replays. If you get any sort of error message when you are saving a program, then it's wise to assume that the program has not been saved, and to save it again. When you have saved a program on a disk, take a look at the way the disk keeps track of your program. This is done by reading the directory of the disk. This is done by typing DIRE CTOR Y or CAT A LOG and pressing . You will see the disk name header followed by the list of programs stored in the disk, and the number of blocks or sectors that remain free for use.

2-12 f

Sometimes, when you try to save a program, you will fmd that the disk system will not accept the save. This is almost always because a program with the same filename has already been saved on the same disk. The disk drive will not replace one program with another of the same name unless you specially want it to. This is a very useful protection for your programs, because it is not always convenient to keep a note of the directory.

If you really want to replace a program, however, this can be done by a small addition to the D5 A V E command. Suppose, for example, that you have a program on the disk which is called I N D EX, you have just developed version 2 of this program, but you want only one version on the disk. You can type: DSAVE"iilINDEX" and press

More disk commands

Because the disk system for the Commodore 128 contains its own computing circuits, complete with memory, a lot of the actions that we use to control the disk system have to be carried out by sending command words to the disk system itself. The words D5 AV E and D LOA D are Commodore 128 BASIC command words for the disk system, which correspond to 5 AV E and LOA D for the cassette system. There is another set of commands, however, which applies to the disk system only, and which has to be sent to the disk drive. For example, the word CAT A LOG (or C SHIFT/A) produces the directory, just as D IRE C TOR Y itself does.

Clearing, retitling and erasing

One very important command that you should get used to at this stage is D C LEA R. Sometimes, when you are developing a program that uses disks, things will go haywire, and the program will stop with an error message and the error light winking on the disk drive. Even worse, you may fmd that the disk keeps spinning and the keyboard does not have any effect until you press STOP and RESTORE at the same time. Mter any 'crash' of this kind, you should send a cOIllllUlDd which restores the disk to normal in case any commands have been incompletely executed. This is achieved by typing DeL EAR

2·13 Another thing that is rather unusual about the Commodore 128 disk system is the error system. When you have a BASIC program, or a direct command, which has a fault in it, the machine will stop, and bring the fault to your attention by means of an error message on the screen. You can then change the program or the command and try again. There are, as you might expect, disk system errors which can also be reported, but the method is not quite so straightforward and obvious. If you put out a command to the disk system which is impossible, you don't always get an error message directly. We have already looked at the example of loading a fIle that does not exist on the disk; some other types of error produce no screen message. All that there is to warn you that something has gone wrong is the error light on the front of the disk drive blinking after the drive has stopped spinning. This light is a warning that an error has occurred in the disk drive, but has not been delivered to the computer. It does not cause either the computer or the disk system to stop operating, and you can send another command if you like. What is can mean, however, is that some action that you wanted to carry out has not been carried out. You may, for example, think that you have saved something on the disk - but you have not!

Sometimes the light may blink even when an error message appears on the screen, as happens when a non-existant fIle is asked for. For example, suppose you put a disk into the drive that had been recorded on a different make of machine, and typed D LOA D" F I LEN Aft1 E ". After a lot of disk spinning, the screen would have the message F I LEN 0 T F 0 U ND ERR 0 R. The disk drive, however, has its own error message system, And you can make this appear by typing? DS $ . You will then see: 74,DRIVE NOT READV,OO,OO Now if we forget about the numbers for the moment, the message DR I V E NOT REA DV is a much more revealing one than F I LEN 0 T F 0 UN D. It draws your attention to something really wrong with the drive or the disk, and since this is the usual message for an unformatted disk, it should lead you to check the disk.

The snag, however, is that these disk system messages do not get delivered automatically to you, just as the directory of the disk is not read automatically. To read the disk error messages, you have to get them delivered to the computer and printed on the screen with the ? DS $ command. Once this has been done, the error light stops blinking because when the error report has been read, the drive no longer stores the error. The flrst number that appears on the screen is the error number, the error message follows, and second and third hold numbers are the track and sector numbers if the error is caused by a fault in the disk itself. The last two numbers are zero unless something is wrong with the disk. In Chapter 7 we'll look at methods for reading the disk error messages and acting on them in a program.

Backing up

One feature of a disk storage system which is less pleasant is that an accident to a disk can result in the loss of a lot of information. If you break a cassette tape, it's possible to splice the tape, and with some juggling, lose only a part of one program. If you damage a disk,

2·14 it's likely that all of the information on the disk will be lost as far as conventional DLOA D commands are concerned. This does not mean that the information cannot be recovered from the disk, but this is a desperate measure, not to be undertaken lightly. It makes sense, then, if you have a disk full of valuable programs or data, to make a backup copy as soon as possible.

One sensible measure is to make a second copy of each program as you put it on disk. If you have boUght programs on disk, however, you will need to make a backup copy, or two copies if the disk is a valuable one. Unfortunately, the disk system of the Commodore 128 does not have a backup command for the single disk user. There is a BA CKU P command, but it is available only for twin drives. If you have a single drive, you can copy individual mes, but not the whole of a disk unless you use one of the commercial copying programs, like Disc Dissector. For many purposes, however, copying a me is 'enough, because you may only have one valuable program or data me on the disk.

Unfortunately, the operating system of the Commodore 128 does not allow you to copy a me from one disk to another. To do so, you will have to load the me into memory from one disk, and save it to another. This is straightforward enough when the mes are BASIC programs, but the task is a lot more difficult when the mes are machine code programs or data meso Fortunately, utility programs like Disc Dissector are available for carrying out this essential task. Backing u,p is easy only when you have twin drives. With two drives in use, you can use the B A CK U P command to cause everything on the disk in Drive 0 to be copied to the disk in Drive 1, or the other way round. The process is accompanied by a lot of clicking and whirring, as one disk is read and the other written, but at least you don't have to attend to the process. You can make yourself a cup of coffee while it is all happening. An alternative, if you still have your cassette recorder, is to keep backup copies on cassettes. It's much safer, however, to backup on to another disk, and to keep this backup disk in a cool safe place well away from all the hazards of disks, such as loudspeakers, TV receivers, electric motors and anything else that uses magnets of any kind. ~ \~•. ~~~~ ... "' ~,.. ?.~i;k~:"':.~ .... ''''-~-\'- ,<":..:.... :-"::...~'.:. ... ~' ..... ~:~~ ,I~ " .~ .. ~' ~~ "~>'" ~ 0,1..

....

2·15 Copying a named file

Very often, you don't need to backup a complete disk, just make a copy of one flIe that is on a disk. For some curious reason, the operating system of the Commodore 128 does not contain any command which can be used to copy a single flIe from one disk to another one. It does, however, allow you to make another copy of a flIe on the same disk but using another name. This is not quite so silly as it sounds, because if you update a flIe regularly, you will probably want to keep the older version around just in case. Since you can't have two flIes bearing the same flIename, it's convenient to change the name of one flIe before you record the next one. You might, for example, use the name NEW 0 N E for the up-to-date version, and 0 L 0 0 N E for the old one. The command which makes this copy-and-name-change operation possible is COP Y. This has a straightforward syntax with the 1541 drive: COpy "PROGONE"TO"PROGTWO" On the 1570 drive you need to add the drive number O. The result of the action will be to create a flIe called PRO GTWO which contains all the information that is also in the flIe PRO G0 N E. These 'flIes' , remember, can be anything that is recorded on the disk, whether BASIC programs, machine code programs or data. If your drive is not drive 0, then you will have to place the drive number following the flIenames, with a letter D. For example, COpy" FIRST" , 0 1 TO "s ECON 0" , 0 1 carries out the internal copying action with drive 1. For most cases, though, if you have twin drives, you will use DO and Dl in order to transfer the program from one disk to the other.

It's possible, however, when you copy a flIe in the same disc, that a flIe with the same flIename of PRO GTWO may already exist on the disk. If this is so, the copy action will not proceed, and the only warning you have is the error light blinking on the front of the disk drive. If you then read the error report from the disk, you will get the F I L E E X 1ST S message on the screen. This is a reminder that you are in danger of wiping out a flIe which you may have forgotten about. If you actually want to do this - as for example, when you are updating a flIe, and want to keep using the same name, you will have to delete or rename the old flIe before you use COP Y. We'll deal with both of these processes later in this chapter.

Deleting files

As well as copying and creating flIes on to disks, you may want to delete flIes. You may, for example, have developed a BASIC program through several versions, and wish now to delete all the old versions. You may, to take another example, have an accounts program which creates a flIe of inputs and outputs of money, and which needs to read a data flIe in, and write one out to update the data. This also may require you to delete an old flIe - we'll discuss data filing in more detail in Chapter 7. Whatever your need, deleting a single flIe is carried out using. the S C RAT CH command. The syntax is: SCRATCH "PROGNAME"«RETURN»

2-16 and this will be followed by the inessage ARE YOU SUR E, requiring you to type Y and press . Once again, the precautions are needed to ensure that you don't lightly remove a fIle you might need later. As usual, a drive number can be included as 001 etc, following the fIlename and separated by a comma, and this must be used with the 1570 drive. The name of the program to be deleted should be whatever name appears in the directory for that program. Even if you pick a name that does not exist, the drive will go through the motions of deleting the name, and there will be no error message. There will always, however, be a message on screen to help you. If you scratch a fIle (real or imaginary!), the message will be: 01,FILES SCRATCHED,01,OO to show where the fIle was on the disk. If the numbers following the message are 00,00, then the fIle did not exist, but if either number is not zero, then a real fIle was deleted. The fIrst number following the report is the number of fIles that have been deleted by the command. This is necessary because it's possible to delete more than one fIle by this action - we'll look at that point later.

This 'scratch' action, which can be abbreviated to S C shiftlR, does r.ot to' Jly remove the data of the fIle from the disk. What it does is to remove the catalogue entry, so that the space on the disk can be made use of by a later entry. This will happen only if the new fIle is shorter than the deleted fIle, or of the same length. If this is not so, the new fIle will use another part of the disk, and the space that was used by the deleted fIle will remain unused until something is done about it. It is possible to recover the contents of a deleted fIle by writing a new catalogue entry, but this comes into the realms of advanced programming, and is defInitely not the sort of thing you want to attempt while you are getting to know your way round the DFS! The scratch action will not work on a disk that is 'write-protected' - see later, this chapter.

Wildcards and wiping

So far, we have always worked with a single named fIle on each command. You can, however, amend the commands slightly so that more than one fIle can be affected, or so that you have to specify less information. The amendment involves the use of 'wildcards' in the fIlename. A 'wildcard' means a character that can take the place of a letter or a group of letters in a fIlename. The two wildcard characters are * and ? Of these, the ? sign can be used to substitute for any single letter, and the * can replace any group of letters.

Suppose, for example, that you entered: DLOAD "R*" This will load a1W program whose fIlename starts with a 'R'. If only one program answers that description, this will be the one that is loaded. If you have two programs which start with a 'R', then the rules are that you will get the last one you used, or you will get whichever comes fIrst in the directory. What is amounts to is that you will get the fIrst

2·17 program which starts with a 'R' that the head of the disk drive comes across when the disk spins. You might call this CBM Roulette!

Another useful feature of this 'wildcard' character is that you can load selected parts of a directory. If you have a disk which contains a large number of programs, perhaps the maximum number of 144, it can be very tedious looking through them all. A simpler option is to ask for a limited directory. Suppose, for example, that you want only the program names that start with 'T'. By entering: DIRECTORY"T*" you can load in only the directory entries which start with the letter 'T'. You will still get the directory header which shows the name of the disk and its ID characters, but the list of programs will be shorter. As always, you can use a drive number in this command if you have more than one drive.

We have earlier looked at the use of S C RAT CH as a way of deleting one single named flIe on a disk. You can make use of 'wildcard' characters to remove more than one flIe. Suppose, for example, that you wanted to remove all the flIes that started with the letter 'T'. This would be done with: SCRATCH"T*" because of the presence of the 'wildcard' character * in the title. A less drastic deletion might be of every flIe that starts with 'A', has three letters, and which ends in 'D'. For this, you would use the single-character wildcard, the ? mark. By typing: SCRATCH "A?D" you will remove all flIes of this specification, including AND, AS D, AR D, but leaving ANY, AD F, BED and any others that do not match the specification.

A more drastic way of wiping a disk, if you want to remove all of the flIes, is simply to format it again. Remember, however, that reformatting takes a lot of time, and it's much better simply to use HE A DE R to clear the directory and rename the disk, as we dealt with earlier. If you use S C RAT CH to remove flIes selectively, you will be left with a disk which bears a strong resemblance to a piece of Emmenthal cheese - full of holes, unwanted bytes of data not used by any flIe in the current catalogue. We can make more efficient use of the disk by re-allocating this space, so that all the flIes we actually have in the directory are put into the fll'st parts of the disk, rather than scattered all over it. This requires the use of COL L E CT.

Every now and again, you will get a DIS K F U L L message on a disk which you know should have plenty. space on it. This is because flIes have been deleted from the disk, but subsequently entered flIes have been too large to fill the gaps. The gaps therefore remain, preventing the addition of data. The disk is full, but not of wanted data! COL L E CT reallocates space on a disk by the simple method of reading flIes from the disk, and writing them on again, using all the disk space in the lower-numbered sectors. Using this command will cause a fair amount of activity from the disk, as it checks and rechecks the BAM and shuffles the data around. Wait until the disk has stopped spinning before you load the directory to see the result.

2·18 Protecting disks and programs

Just as we have a number of methods of deleting ftles from disks, we have also a number of methods of preventing this from happening. One method of doing this is universal to all disk systems on all machines. It makes use of the 'write-protect' notch on the disk. If you hold a disk as you would if you were inserting it into the drive, you will see a small rectangular slot cut from the left-hand side of the jacket. This is the 'write-protect notch'. When the disk is in the drive, the presence of this notch is detected, either mechanically or by a light beam. If the notch is unobstructed; the disk can be read from and written to. If the notch is covered, then the disk can only be read, not written to again. In each pack of disks you will fmd a set of small sticky tabs that can be used to cover this notch to make a complete disk 'read-only'. If you want to re-use such a disk, you only have to remove the tab. Remember that another protection also exists for programs and for serial ftles (see later, Chapter 7, for an explanation of serial ftles). The protection is simply that you can't replace a program by one of the same name unless you use the '@' in front of the ftlename.

Renaming files

Occasionally we want to give a new name to a ftle. We could, of course, load the ftle, save it under another name, and then delete the old ftlename. This is not necessary because all that has to be changed is the catalogue entry on the disk. This can be done using the R E NA ft1 E command. Renaming is particularly important for data ftles. Suppose, for example, you have a program which creates an index of names and numbers, and which saves its index to disk under the ftlename I ND X i If this program has been used before, there will be a ftle called I N DX on the disk, and the disk system will refuse to create another ftle of the same name

UNPROTECTED PROTECT IT!

2·19 There are three ways out. One is to delete the I N DX flle and, if necessary, make space by using COL LEe T on the disk. Another way is to add the'@' ahead of the fllename so as to force the disk system to replace the old flle with the new one. The third way is to rename the offending flle. This last approach is rather better, because it preserves the old flle, and the new one now fmds another place on the disk. RE N A ME has to be followed by the drive number if you have more than one drive (or if you are using the 1570), the new fllename, the word TO, and then the old fllename, in that order. The fllenames must be enclosed in quotes ("). Renaming will not occur if the disk is write-protected. Taking an example, suppose that you have a program which is called TEST and you want to rename it TRUE. The procedure is to type: RENAME "TEST" TO "TRUE" and the renaming operation will be carried out. If you are using more than one drive, you can use the syntax: RENAME D1,"TESi" TO "TRUE"

Finally, there's another useful twist to a lot of these commands. If you want to carry out disk operations like deleting and renaming flles from within a program, you can use variable names to represent the fllenames. The only condition is that the variable names have to be enclosed in brackets, so that a rename operation from X $ to Y $ would look like this: RENAME (X$) TO (Y$)

2-20 Chapter 3

Text display

Screen clear and print location

Unlike its ancestors, the Commodore 128 has a BASIC screen clearing statement, seN CL R, which corresponds to the C L S statement of other computers. This clears any kind of screen, text or graphics, and in this section we'll look at it as applied to text screens. The screen clear action is an important one in a lot of business-biased programs, because presentation on the screen should always be as simple as possible, with no confusing clutter left over from previous work. The normal use of the screen in business programs is for reminders and prompting messages only, with all of the important output going to the printer. For home use, however, if no printer is being used, the screen may have to carry all of the output, and so clearing, followed by neat paging, is an important technique.

Along with clearing goes the ability to print anywhere you want on the screen. Later in this chapter, we'll be looking at the use of windows, but for a lot of purposes, a subroutine that moves the cursor is more useful. Figure 3.1 shpws a useful cursor-moving subroutine along with a demonstration of its use. The subroutine starts at line 60000, and makes use of the integers L L% and C L %which are assigned by the main program. L L% is line position and C L% is column position, both measured from the HEADY.

5 SCNCLR 10 LL%=3:CL%=12:GOSUB60000:PRINT"TRAPPED;t 210 END 60000 FORQQ=0TOLL%: PRINTCHR$ (17) :: NEXT 6010210 PRINTSPC(CL%); 610030 RETURN

READY.

A simple subroutine to move the screen cursor to a specified position.

FIGURE 3.1

3·1 present cursor pOSltlOn. The subroutine is therefore best suited to being used immediately following a seN C L R statement, as the example in lines 5 to 20 shows. The subroutine uses the number L L % in a loop to shift down the screen, using the cursor down character C HR $ ( 1 7 ) , and the movement across the screen is accomplished by using S PC ( C L %) , avoiding the need for another loop. Note that the subroutine does not work like aPR I NT A T command in other machines, because it can't, in its present form, place a piece of text on one part of the screen and then place another piece of text anywhere else.

The listing in Figure 3.2 gets around the restrictions to some extent by making use of PO K Eing direct to the screen memory addresses. As the example shows, the words can be placed at any screen position and in any order, using L L %as line number and C L %as column number. In the subroutine, which starts at line 60000 again, no safeguards have been built in against the use of numbers outside the range 0 to 24 for L L %and 0 to 39 for C L %. The principle is to calculate L P %, which is the screen memory address that corresponds to the values of L L %and C L %. The loop then takes the characters one by one from the string T X$, fmds their ASCII codes, and subtracts 64 to get the correct Commodore 128 code number. If the character was a space, however, the subtraction of 64 will give a negative number, and this is detected and the figure of 32 substituted. No attempt has been made to cope with characters with ASCII codes of 33 to 64. The code is then poked to the appropriate address, and the subroutine returns. This is much closer to the P R I NT A T action, and provided that you are not concerned with using characters with ASCII codes 33 to 64, its quite efficient, but slow. The machine provides, however, for a genuine P R I N TAT type of cursor position control, using the CH A R statement, and we'll look at this in use later. The C HA R statement cannot be used exactly like a P R I NT statement, which is why it has not been dealt with at this point. READY. 10 SCNCLR 20 TX$="TOP LEFT" 30 LL%=0:CL%=0:GOSUB 60000 40 rX$="BOTTOM LEFT" 50 LL%=24:CLi.=0:GOSUB 60000 60 TX$="TOP RIGHT" 70 LL.i.=0:CL%=31:GOSUB60000 80 TX$="BOTTOM RIGHT" 90 LL%=24:CLi.=28:GOSUB60000 100 END 60000 LP%=1024+40*LL%+CL% 60010 FOR JJ=l TO LEN(TX$) 60020 QQ%=ASC(MID$(TX$~JJ,1»-64:1 F QQ%{0 THEN QQ%=32 60040 POKE LP%+JJ-1,QQ%:NEXT 60050 RETURN READY. Another cursor movement subroutine for the 40-column screen, using PO KEs to screen addresses.

FIGURE 3.2

3·2 Print fielding

The topic of print fielding will be completely new to you if your computing has been learned on the Commodore 64, and it's a topic that rightly belongs in the category of more advanced programming. Print fielding is concerned with how a quantity, string or number, is presented on the screen. Fielding is not so much concerned with the position of print on the screen, which is dealt with by TAB, S P C and the routines of Figures 3.1 and 3.2, but with the actual quantity itself. The field that is referred to is the space in which something is printed, and fielding means how the printed text or numbers will be arranged in this space. For example, you might want a heading centred, or hard against the right-hand edge of the screen or paper. You might want a number printed with only two places of decimals, with decimal point lining up, with dollar or pound signs and so on.

Figure 3.3 shows an example of fielding carried out with text. The screen is cleared, and line 20 then prints the name using the field string # # # # #. The number of hashmarks means that the field will be of this width, five characters. When the name is printed then, only five characters will appear, and they will be the first five as you'll see when you run this. If the name is of fewer characters than are needed to fIll the specified space, then it is left justified, with the first letter of the name at the left-hand side of the field. The next one is more complicated. The field string is assigned to F$, and it consists of an equality sign followed by eleven hashmarks, a total of twelve characters in aU. The equality sign forces the printing to be centred within the field. Not, you'll notice, in the line, only in a field that is 12 characters wide. For the word title, that means starting on the 5th space - when the centering cannot be perfect (because there are no half-spaces), it's always one character to the right of centre. When the > sign is used in place of the = sign, the effect is right justification, meaning that the word will be printed at the right of the field. What you have to remember (and it's easy to forget) is that centering and right justification apply only to the field, which in tum is defmed by the number of characters between the quotes in the field string that follows USIN G. If there are more characters to print than the field has room for, then characters will be chopped off as needed so that the field is filled. READY. 10 SCNCLR 20 PRINT USING ...... ; .. SINCLAIR" 30 PRINT 40 F$="=••••••••••• " 50 TT$="TITLE" 60 PRINT USING F$;TT$ 70 PRINT:F$=">••••••••••••••• " 80 PRINT USING F$;TT$ READY. A demonstration of fielding carried out with P R I NT US I NG .

FIGURE 3.3

3·3 P R I NT USIN G, as applied to strings, does not easily perform the task that we most often need; that of centering a string in a line. You could, of course, type a formatting string that consisted of an equality sign followed by 39 hashmarks, but that's too tedious, and there are easier ways as we shall see. Where P R I NT USIN G comes into its own with strings is when working with several columns and trying to follow a format of centering or justification in each column. Take a look at the result of running Figure 3.4, for example. In this listing, the fielding string is much more elaborate. It starts with the equality sign, and then puts in nine hashmarks, then five spaces, and this pattern is repeated twice. This specification sets up three fields across the width of the screen, each with ten characters and separated by five spaces. The effect of using this fielding to create a column of names is then illustrated. Note that, because of the rule that a name is moved right by one character if it cannot be perfectly centred, some of the examples look rather ragged. Once again, because you cannot space by half a character width, this is unavoidable. One name has had to be chopped by one letter, but is still recognisable. The great advantage of using fielding for this type of display, as distinct from TAB, is that it's so much easier to ensure that names do not overlap, and that the five spaces between the columns are rigidly maintained. One last point about this string fielding is that you can put other characters in besides the #, = and >, and these other characters will be printed. For example, in the listing of Figure 3.4, you could replace the blanks between the columns by vertical bars, using the SHIFT B graphics shape. This will show on the screen, but not necessarily on a printer unless you are using a Commodore printer which prints the standard Commodore graphics. READY.

10 SCNCLR 20 F$= .. = ••••••••• =••••••••• =••••••••• " 30 NM$="NAME" 40 PRINTUSING F$;NM$,NM$,NM$ 50 PRINTUSING F$;"ROBERT","JIM","SUE" 60 PRINTUSING F$;"TIM","MAXIMILIAN","JANIS" 70 PRINTUSING F$;"ANDY","TOM","QUENTIN" READY.

Using a more elaborate fielding so that text can be put into three columns.

FIGURE 3.4

Formatting numbers

Formatting control strings have some applications for printing text, but they really come into their own when you want to print numbers, particularly money amounts and items in columns. As before, the number of hashmarks and other characters specifies the total number of characters in a field, but you now have a much wider choice of characters that

3-4 you can use along with the hashmarks. For the most part, what this amounts to is that you can insert various characters into the fielding string, just as you can with the text fielding string. These characters would typically be the decimal point and the comma when you want to use numbers, and by using these, you can make your numbers appear the way you want to see them. You can use as many commas as you want (there must always be a hashmark preceding a comma), but you are limited to one decimal point per field.

The program in Figure 3.5 prints a set of numbers, each set using a different field specification. The loop that starts in line 20 will print five sets of numbers, and the correct field specification is obtained by lines 30 to SO. The statement RES TOR E 1 30 ensures that the data for the field specification is taken from line 130, and by using the loop FOR X= 1 TON, you will end up with F $ assigned to the Nth. item in the DA T A line. The RES TOR E 1 40 in line 60 then picks the number data, and the loop that starts in line 70 reads various numbers so that they can be printed with the required format. When each loop of number printing is completed, the S LEE P 5 statement in line 110 makes the program wait for five seconds so that you can take a look at the group of figures before the next fielding string is assigned. READY.

10 SCNCLR 20 FOR N=l TO 5 30 RESTORE 130 40 FOR X=l TO N 50 READ F$:NEXT 60 RESTORE 140 70 FOR J=l TO 5 80 READ D 90 PRINTUSIN6 F$;D 100 NEXT 110 SLEEP 5 120 NEXT 1:30 OATA tt •• , ...... , ...... , "., ...... , ...... , ...... " 140 DATA 1.57,11.236,10143.2,1071623,237.145 REAO ....

A program to demonstrate number fielding. Note the use of RES TOR E with a line number following it.

FIGURE 3.5

Figure 3.6 shows the results of all this as printed on paper. The first fielding string uses a total of nine characters, so all fields are nine characters long. The numbers are justified to the right of the field, and since two hashmarks have been shown following the decimal point, there will be two decimal figures, whether the number contains decimals or not. Numbers with three places of decimals are rounded down to two, numbers with one decimal place have a trailing zero added. This ensures that the decimal points line up

3·5 1.51 .11.24 UI,143.2f1 ********* 237.15

1.6 11.2 **** **** **** 1.6 11.2 ******* ******* 237.1

1.6' .***, *** ***. *** 2 11 111143

*****.237 How the numbers are printed from the program of Fig.3.S.

FIGURE 3.6

which is ideal for displaying tabular data. One number cannot be displayed in this field, because it has 7 figures and no decimal point. It would therefore consist often characters if we added a decimal point and two figures following the point. Since this amount of space is not available, the number is not printed, and the asterisks show where the omission has occured. Note the effect of rounding on 11.236, which is printed as 11.24. This automatic rounding can save you a lot of programming effort, but you need to be careful that it is doing what you expect it to. If, for example, you are working with such things as compound interest, you may be calculating to four decimal places, but displaying a rounded amount only. You will have to ensure then that the rounding does not work against you!

The other sets of figures follow this pattern, with rounding when the decimal places are not displayed, and asterisks if the total number of places is insufficient. Note in the last set that the effect of the formatting string # # # # # on 1.57 is to print this number as 2, rounding up to the nearest whole number, which is what the format calls for. The figure of 1.49 would be rounded to 1 in this position. In general, when you are working with numbers, it's better to allow for more places than you need preceding the decimal point,

3-6 and for as many as you want following. If you need to display with three decimal places, and you are expecting numbers up to 502614 for example, use # , # # # , # # # • # # # as your formatter so that if a larger number comes along, you will not be treated to the sight of a set of asterisks.

Standard form 130 DATA .. # ...... ,.."A .. , .. #.#,.." ...... , .. #.###,.."A A...... , .. #.##,..",.."AA .. , .. #.####""""..... ,.." ..

READY. 2E+00 1E+01 lE+04 1E+06 2E+02

1.6E+00 1. lE+01 1.0E+04 1.lE+06 2.4E+02

1.S70E+00 1. 124E+01 1. 014E+04 1. 072E+06 2. 371E+02

1.S7E+00 1. 12E+01 1.01E+04 1.1Z17E+06 2. 37E+02

1.S700E+00 1. 1236E+01 1.0143E+04 1. 0716E+06 2. 371SE+02

Using Standard form for printing numbers. This shows the changed DA T A line, and the effect on the printout.

FIGURE 3.7

3·7 READY. 130 DATA"-...... ""A.II, .. - ...... A.II,"-••••

• A...... II , II _ •• ...... " , II _ •••••_ ...... " ...... 140 DATA -1.57,11.236,-10143.2,.00 71623,237.145

READY.

-2E+00 1E+01 -1E+04 7E-03 2E+02

-1.6E+00 1.1E+01 -1.0E+04 7.2E-03 2.4E+02

-1.570E+00 1. 124E+01 -1.014E+04 7. 162E-03 2. 371E+02

-1.57E+00 1. 12E+01 -1. 01E+04 7. 16E-03 2. 37E+02

-1.5700E+00 1. 1236E+01 -1.0143E+04 7. 1623E-03 2. 3715E+02

Placing a negative sign - the changed lines and the printed results.

FIGURE 3.8

For a lot of purposes, particularly in science and engineering applications, you might need to print numbers in standard form. If you have never met standard form before, this is no place to start making its acquaintance, and Appendix B offers a brief introduction. A number is fielded into standard form by using four up-arrow symbols. Confusingly, these always show on a printed listing as 'carets', the little inverted V's,

3-8 rather than as vertical arrows. The key that you need for this is next to the RESTORE key. The number of these symbols is important - fewer than four and you just get an error message, more than four and your figures will include these marks. Since standard form can be used to print a number of any size in a very concise format, you should never get a display of asterisks when you specify numbers in this form. You will need to use the correct number of hashmarks, however, if you want to specify precision. The specifying field string will therefore contain one hashmark before the decimal point and as many following it as you need for precision. For the number 63120, for example, the formatter # • # iii i would give the result 6.3E4, and the formatter # • # # # # iii i would give 6.3120E4 for the same number. The second version is considerably more precise than the first, and it's up to you to specify what you need or want in this respect. Figure 3.7 shows a change to the 0 A T A line 130 of the previous listing, so that you can see the effect of printing the same numbers in standard form with various field specifiers; the results are shown also.

In standard form, a number that is only fractional is shown as having a negative exponent (the figure following the E). If the number is a negative number, then there should be a negative sign preceding the figure before the decimalJ!)oint, but this will not be correctly shown unless you make space for it. If you put negative quantities into the program above, you will fmd that some will produce asterisks with one format (the format with no decimal point), and that others are printed with only a negative sign before the decimal point. If you place a negative sign before each first hashmark in the format strings, then the sign will be correctly printed only where it is needed, and will not affect the positioning of the figures, as Figure 3.8 shows. This listing shows the changes to line 130 and 140, and the results as printed out.

Money amounts

Printing money amounts is likely to be a requirement for many applications. You can place into a field any money sign that is provided for on your keyboard, and since keyboard symbols are provided for most European languages, that makes the Commodore 128 useful for accounts programs in most languages. The simplest way of placing a money unit sign is to have it at the head of a formatting string, as for example: FS="£##,###.##" would put the British pound symbol at the head of a ten-character field. Obviously, you could place whatever symbol you needed in this position, or in a similar string. Using the money symbol at the head of the field is not always satisfactory in visual terms, however, as Figure 3.9 shows. In this listing, which has been produced by an Epson printer, the pound sign has been replaced by the backslash (\) because there is no agreed ASCII code number for the pound, and the Commodore 128 uses ASCII code 92 which is the code for the backslash in most printers. This is just one of the problems that faces the designers of programs that work with money! When the program runs, you will see that the £ sign has been placed at the start of the field, but it looks much too remote for most of the figures. What is needed is what is called a 'floating' pound symbol, one which will just precede the first figure of a money sum.

3·9 READY.

10 SCNCLR 20 F$="\###,###.##" 30 FOR N=l TO 5:READ D 40 PRINTUSING F$;D 50 NEXT 60 DATA .25,1.76,23.4,2176,31260.22

READY.

Printing money amounts with a pound sterling sign. This shows in line 20 as a backslash, because the Commodore 128 uses ASCII code 92 for the £ sign rather than the more usual backslash.

FIGURE 3.9

This ability to float the symbol is provided in P R I NT USIN G - but primarily for the dollar sign. To float the dollar (sounds like an economist's prescription) requires putting one hashmark in front of the dollar sign, then the usual set of hashmarks that mark out the field size for your money amounts. Figure 3.10 shows the change that is needed to line 20 of the listing in Figure 3.8, and the results of running the program. You can see that the dollar sign just precedes the money amount in each case. The effect looks rather curious on the first item, which has no whole number of dollars. This displays with two dollar signs rather than using a zero before the decimal point. Apart from this, the display is much more satisfactory than that of the fixed monetary sign. READY.

20 F$="#$###,###.##"

READY.

$$.25 $1.76 $23.40 $2,176.00 $31,260.22

Altering line 20 of the previous listing so as to make the dollar sign float, and the resulting printout.

FIGURE 3.10

If your currency is not the dollar, however, the ability to put a dollar into a string format in this way is not particularly useful. The Commodore 128 has a unique function, P U D E F, which rescues you. P U D E F allows you to derme four of the standard

3-10 characters of a number string format. Try adding to the program the line: 15 PUDEF",.£" which contains in order a space, the comma, the full stop and the pound sign, all enclosed in quotes. The space is important, and the·;order of characters is important. Line 20, which defmes the format string is unchanged, the formatting sign is still the dollar. When this runs, however, you will see the numbers prefIXed with the pound sign rather then the dollar.

P U DE F allows four characters to be replaced wherever they are used in a format string. The characters are the blank, the comma, the full stop and the dollar. In most of Europe, for example, the comma is used in place of the decimal point, and a space is used where we use the colDlDli. A redefming statement would therefore use: PUDEF" ,F" taking an example that replaces the dollar by the Franc. You can also opt to have any blanks in a format string replaced by whatever you like, which can include graphics. The P U DE F statement need not be written exactly as shown, because a string can be specified as a set of CH RS () characters, or as a string variable. Figure 3.11 shows a P U DE F in which the blank is redefmed as a vertical bar, character 98. Remember that when this is used, the vertical bar appears where blanks would be printed, not where blanks are put in the formatting string. In other words, you'll get the vertical bars where there is a blank ahead of the L sign in this example, but if you altered the format string so that it read: FS="#S###,###.## " with two spaces following the last character, these would still be shown as spaces, not as vertical bars. The blanks are the gaps that the floating L (lire) symbol creates. The combination of P R I NT USIN G and P U DE F makes it possible to write programs in general terms, using dollars, commas and decimal points, and to alter the program for another language system simply by adding a P U DE F line. This is a very valuable feature, and I haven't met it on any other well-known BASIC.

READY. 10 SCNCLR 20 PUDEFCHR$(9B)+",.L" 30 F$= ...$ ••• , •••••• •• 40 FOR N=l TO 5:READ D 50 PRINTUSING F$;D,D,D 60 NEXT 70 DATA .25,1.76,23.4,2176,31260.22 READY.

Using P U DE F to redefine the blank character as well as the monetary symbol.

FIGURE 3.11

3-11 Titles and centering

A frequent requirement in programs is to have a prominent title which is centred on the screen. This calls for a subroutine, so that you can use the same method to print any title that you like, and since we have no simple option for printing in double-size letters, our main choices will be for flashing or coloured titles. If we need large titles, then a subroutine is less useful and it's easier to have a custom-built routine which will make the title out of graphics characters. In addition, we have to look at methods of stopping a title from displaying any longer than we want it to - this applies in particular to the flashing titles.

Figure 3.12 shows a subroutine which centres and flashes a title. The only items that have to be assigned are the variable name for the title T T $ and the number of flashes, R P. Lines 10 to 40 illustrate the subroutine being used. The subroutine itself consists of lines 60000 to 60020, and starts by creating a string of backspace/delete characters (C HR $ ( 20) ) which is as long as the title. The idea here is that having printed the title, the cursor will be left at the end of the title and printing this string of backspace characters will then delete the title, which is how the flashing is achieved. With this string of backspaces created, the variable C L is assigned to give the TAB position for centering the title, using the normal formula 20 - L L I 2. The next two lines then control the flashing. Variable R is set to count the number of flashes up to the value of R P, and the flashing sequence consists of printing T T $ at TAB ( C L ) , waiting for a short time which, in this example, has been set by using a time delay loop. This is because S LEE P is not useful for short delays since it can accept only integer numbers in its argument. Line 60020 then prints the backspace string and waits again before looping round for another pass in the R P loop. When the required number of repetitions has been achieved, the subroutine returns.

READY.

10 SCNCLR 20 TT$="THE TITLE" :RP=10 30 GOSUB 60000 40 END 60000 LL=LEN(TT$):BP$="":FOR X=l TO LL:B P$=BP$+CHR$(20):NEXT:CL=20-LL/2 60010 FOR R=l TO RP:PRINTTAB(CL);TT$;:FO R J=l TO 200:NEXT 60020 PRINTBP$;:FOR J=l TO 200: NEXT: NEXT : RETURN

READY.

A subroutine for centering a title in a line and flashing the title. This is more universally applicable than a PR I NT US I NG format.

FIGURE 3.12

3-12 There are several ways in which this subroutine can be improved. One way is to avoid any need to fix the number of repetitions when the subroutine is called. Another is that you may not want to sit and watch the title flashing, because you would prefer to get to whatever follows. We can combine these actions in Figure 3.13, which uses the count as a time-out device, and combines this with a 'press-any-key' test. This time, the loop is a DO ..WHILE loop, and the WH I L E condition is A$ =" " . The variable A$ is assigned by aGE T A $ statement, which will return with a blank for A $ unless a key is pressed. Everything from DO to L0 0 P will therefore be performed until you press a key. In the loop, things are much as before except that the variable R P is used as a counter. This has been initialised at zero, and is incremented on each flash of the title. The test that is made at the end of line 60020, however, introduces another ending condition - that RP has reached 100. This condition, like the press-a-key condition, will take the program out of its loop, and into line 60030. This tests to fmd which condition ended the loop, and if it was caused by the value of R P, prints the timeout message and stops the program. If the loop was broken by pressing a key, the operator is still awake, and the subroutine returns so that the program can proceed. READY. 10 SCNCLR:PRINT 20 TT$=IITHE TITLE" 30 GOSUB 60000 40 END 60000 LL=LEN(TT$):BP$=IIII:FOR X=l TO LL:B P$=BP$+CHR$(20):NEXT:CL=20-LL/2 60010 RP=0: DO WHILE A$=" I.: GET A$: PRINTTA B(CL);TT$;:FOR J=1 TO 200.NEXT 60020 RP=RP+l:PRINTBP$;:FOR J=l TO 2001N EXT. IF RP)=100 THEN EXIT:ELSE LOOP 60030 IF RP>=100 THEN PRINT:PRINTttTIMED OUT - ENDtt:END:ELSE RETURN READY.

An amended subroutine which incorporates a time-out feature.

FIGURE 3.13

Windows

The trouble with using a flashing title subroutine is that the subroutine ties up the computer, and nothing else can be done while the title flashes. It would be possible to print some instructions and then return to flash the title by making use of the C HA R statement that we shall look at later, but there are other methods of creating a title space that we need to look at. One of these uses windows. As you probably know, we can defme a window, a rectangular part of the screen, in which we can print and display data

3·13 independently of the rest of the screen. We can set these windows quite apart, using separate clearing, and with different character colours as we please. As a way of making a title stand out, then, a window is rather useful, and the window statements of .the Commodore 128 distinguish it sharply from the C64, which had no such facility. The only snag is that Commodore 128 windows are not quite so versatile as those you see on other machines, as we'll demonstrate.

Figure 3.14 is a demonstration of windows and coloured titles. To start with, the window area is defmed in the WIN DOW statement by using at least four numbers which specify the top left and bottom right positions. These numbers are, for each position, the column number followed by the row number. For example, if you wanted your window to be the whole screen, the numbers would be 0,0 (top left) and 39,24 (bottom right). To these numbers, which are separated by commas, you can add a 1 if you want this window cleared independently of any other part of the screen. So far, so good. The good news now is that you can specify a text colour in this window, and use a quite different colour for your text on the rest of the screen. The bad news is that whatever colour you use as a background colour will affect the whole screen, and any change in the background colour of the rest of the screen affects the window. You can't, in other words, have a window with a different background colour. It's a shame, really, because this is what makes windows so useful in machines like the oldBBC, the Amstrads, and many others.

READY. 10 FOR J=1 TO 24:PRINT" COMMODORE 128 B ASIC RULES OK? II: NEXT 20 TT$="THE NEW TITLE":COLOR 4,1:SLEEP5 30 SOSUB 60000 40 SCNCLR 50 PRINT" THE NEXT LINE" 60 PRINT" AND THE NEXT" 70 PRINT" AND SO ON" 80 SLEEP2:LIST:LIST:SLEEP3 90 WINDOW O,O,39,24 100 END 60000 WINDOW 0,0,39,1,1 60010 CL=20-(LEN(TT$»/2 60020 COLOR 0,7:COLOR 5,8 60030 PRINTTAB(CL)TT$:SLEEP5 60040 WINDOW 0,2,39,24 60050 COLOR 0,l.COLOR 5,2 60060 RETURN

READY.

A demonstration of the use of a window and a coloured title.

FIGURE 3.14

3·14 Back to the program. This starts by filling the screen with some text, so that you can see what is happening as the subroutine runs. The title string is assigned, the border colour is changed to black, and the program sleeps for five seconds. The subroutine is then called. This makes a two line window, and clears this part by the use of a fmal 1. The TAB position for centering is then assigned, and then foreground and background colours. The background colour is set with COL 0 R 0, 7, which makes the background blue, and the foreground colour is set with COL 0 R 5, 8 giving yellow letters. The title is then printed. You can see now, as the program sleeps, that the whole screen background is blue and the title is in yellow. After the five-second sleep, a new WIN DOW statement allows you to use the rest of the screen. This is important, because once a window has been defmed, you can't print anywhere else. The machines that allow you to print in any window use numbered windows to allow this. The Commodore 128 only has one window operating at a time, the current window (which is the most recently defmed window). When the rest of the screen is defmed in line 60040, then, the change of background colour here affects the first window as well, but the change of text colour does not. When the subroutine returns, the screen is cleared - which means the rest of the screen, because the first window is unaffected by this. Some lines are then printed, followed by listings, so that you can see the scrolling taking place but not affecting the first window. Finally, the normal screen is restored. This is not strictly necessary, but it saves you the trouble of having to do so after the program has fmished.

Windows, then, offer some useful features in programs. They can be used to display a title which will persist for as long as a program runs, or until it is changed, because what happens in the rest of the screen has no effect on the window after a new window has been created. A window at the top or at the bottom of the screen can be a very useful way of displaying reminders. The only point here is that some care is needed to change the reminders. When a window is created, the cursor is automatically set to the top left-hand position in that window. This is ideal for the message window, but it can cause problems on the piece of the window that is defmed as the rest of the screen. Figure 3.15 illustrates this principle. The subroutine is almost as it was in the previous example, but the background changes have gone. The idea is that each time this subroutine is called, a new message can be printed. The problems come from the return to the main screen.

3-15 READY.

10 SCNCLR 20 TT$="PREPARE TEXT NOW" 30 GOSUB 60000 40 PRINT"HERE'S ANOTHER":SLEEP5 50 TT$= II DIFFERENT MESSAGE II 60 GOSUB 60000 70 PRINTIPRINT"END OF THAT ONE" 80 END 60000 WINDOW 0,0,39,1,1 60010 CL=20-(LEN(TT$»/2 60020 COLOR 5,8 60030 PRINTTAB(CL)TT$ 60040 WINDOW 0,2,39,24,0 60050 COLOR 5,2 60060 RETURN

READY.

Illustrating the problem of moving the main-screen cursor after returning from a window subroutine.

FIGURE 3.15

You have to redefme the main screen again each time, otherwise you stay in the top window. When you return to the top screen though, the cursor is placed at top left, and when another comment is printed, it wipes out the fIrst one. One simple way to get round it is to have an extra P R I NT statement for each previous line you want to preserve. In this example, the fIrst P R I NT in line 70 serves to move the cursor down to the correct position. There is, however, a more elegant method, as you may have guessed. If, each time the subroutine is called, it increments a row number, then the cursor can be placed in the correct row using the CH A R statement, with no need to have a set of P R I NT statements in front of each phrase to be printed. Figure 3.16 illustrates this technique in action. It also shows a more intelligent use of integer numbers, which should be used wherever possible to allow a program to run as fast as possible. In this example, the number V% is assigned with -1 at the start, and will be incremented each time the subroutine is called. This variable V% is used in line 60060 of the subroutine to set the cursor position, using CH A R , 0 , V% - the 0 is to set the column position to the left-hand side. The incrementing action means that when the subroutine is fIrst called, the printing in the main window will be at line 0, and will advance by one line each time the subroutine is called. If you want to put more lines on the main screen between calls, you can increment V% in the program accordingly. If you need to preserve a row position, incidentally, you can put in X% = PO S ( X) when the cursor is where Jrou want it in the main screen, and use X% as the horizontal cursor number in the CH A R statement. It's a pity that the Commodore 128 does not have a VP 0 S function, as this would allow the cursor column and row numbers to be saved as variables before the subroutine was called, so that the position could be restored by the CH A R statement.

3-16 READY. 10 SCNCLR 20 TT$="PREPARE TEXT NOW":V'Y.=-l 30 GOSUB 60000 40 PRINT"HERE'S ANOTHER":SLEEP2 50 TT$="DIFFERENT MESSAGE" 60 GOSUB 60000 70 PRINT"END OF THAT ONE":SLEEP2 80 TT$="THIRD TIME LUCKY" 90 GOSUB 60000 100 PRINT"FINAL EFFORT" 110 END 60000 WINDOW 0,0,39,1,1 60010 CL'Y.=20-(LEN(TT$»/2 60020 COLOR 5,8 60030 PRINTTAB(CL'Y.)TT$ 60040 WINDOW O,2,39,24,O 60050 COLOR 5,2:V'Y.=V'Y.+1 60060 CHAR,0,V'Y.:RETURN READY.

Using a row-counter number along with CH A R to position the cursor correcdy on the main screen.

FIGURE 3.16

Hardcopy

Hard copy means printing on paper, and is obviously possible only if you have a printer connected. Chapter 10 deals in detail with the use of printers with the Commodore 128, and the only poipt I want to make here is that all the text display techniques in this chapter can be used to produce copy on paper, given suitable changes. The main change is that the P R I NT statement must be altered to aPR I NT # 4, or something of this sort, depending on how you make use of the printer. In addition, you have to make sure that TAB numbers and 'cursor' positions are suited to the printed output. Most printers will print 80 characters across a sheet of paper, so that you will often have to work with larger TAB numbers. You will not, of course, have to specify windows on your printer, but the use of the fielding strings in P R I NT U SIN G all apply - several of the examples in this chapter were printed by altering all of the P R I NT U SIN G statement to P R I NT # 4 US I NG statements.

3-17

Chapter 4

Working with numbers

Operators

An operator is a symbol for a fundamental mathematical operation. If that looks intimidating, don't worry, because the main operators that you are likely to use are the familiar signs *, I, + and - which carry out the fundamental operations of multiply, divide, add and subtract. Each of these is an operator that requires two numbers to work on, and in some books you will fmd them called 'binary operators'. The numbers (or in some cases strings) that operators work on are called operands. The operators of the Commodore 128 are of four kinds, classed as arithmetic, string, relational and logical. The fIrst group is composed of the four symbols that we are familiar with, plus the exponentiation sign, which appears on paper as a caret but on the keyboard and on the screen appears as an up-arrow.

The only string operator is the +, which will concatenate strings (join them together). This is, incidentally, a way in which longer strings can be generated, getting round the limitations of input strings. A string that is entered using a statement such as I NPUT A $ cannot be of more than 160 characters, and any attempt to enter a longer string will cause a S T R I NG TOO LON G error message, which is fatal- meaning that it stops the program. The limit of string length is actually 255 characters, and by using concatenation, you can build up the length of a string to this limit, rather than being restricted to the lower limit of 160 characters.

The relational operators are the set of signs that show relationships, rather than producing some answer. The main three signs in the group are =, < and >, which compare the size of numbers, and the ASCII codes of string characters. These signs can be combined, so that· > =means equal to or greater than, <= means equal to or less than, and <> means not equal to. Note that you can write <> as > <, reversing the position of the symbols, and you can also use =< or =>, but it's not a good habit to get into, because some other versions of BASIC will not accept these combinations. You would, of course, use these operators in connection with variable names for numbers or strings rather than with constants.

4-1 Finally, the logic operators are the words AND, 0 R and NOT, sometimes called the Boolean operators in honour of the mathematician George Boole whose work laid the foundations of computing science. The action of a logic operator is to return true or false when it is used to test a relationship. The NOT operator is unary, and it gives a true result if what it precedes is not true. The machine expresses true as the number -1 andfalse as O. If you are not accustomed to this, it can look very confusing, and a few examples will help. The secret is to work in terms of true and false only, and to start with any term that is enclosed in brackets.

For example, what do you expect from the line: PRINT NOT (2)1) when this runs? The answer is worked out by looking first at 2> 1, which is true. Not true is false, so the answer must be the code for false, which is O.

Try another one: PRINT NOT("B">"A") (the quotes are important). When we compare strings, the ASCII codes are what count, and the ASCII code for "B" is 66, more than the code for "A" which is 65. "B" >" A" is therefore true, and not true gives false, O. By the same token, NOT (" A" > " B") gives -1, true. Using P R I NT NOT (0) will give -1, since not false must be true, and equally obviously, NOT ( - 1 ) gives O. If your nerves are up to it, try P R I NT NOT ( 7) and see if you can explain the result. If it's baffling, please turn to Appendix C.

4-2 The other logic operators, AND and 0 R need two quantities to work on. These quantities can be numbers or strings, and the important point once again is that each side of the AND or 0 R word should be something that can be resolved to true or false. For example, if we have: PRINT (7)3)AND(S>2) then we can expect the result -1. Why? Working out the items in the brackets we have 7> 3 is true and 5> 2 is true, so true AND true = true. The Law ofAND is that the result is true only if the items that are connected are also both true. If one item is false, the result is also false. On that basis, then you would expect the result of: PRINT (S>6)AND(7>4) to be 0, as it is because one term is false. The 0 R operator will give true if one item is true, no matter whether the other is true or false. Only if both items are false will the result of OR be false. Figure 4.1 summarises the actions of AND and OR. Remember that you would normally be using these operators with variable names.

AND TEST TEST 2 TEST 3 RESULT True True True True False True True False True False True False True True False False any other combination Fa lse

OR TEST 1 TEST 2 TEST 3 RESULT False False False False True False False True False True False True False False True True any other combination True

Form of use (TEST1) AND (TEST2) AND (TEST3) (TEST1) OR (TEST2) OR (TEST3).

A summary of the effect of using AND and OR in logic statements.

FIGURE 4.1

Expressions

An expression is a set of operators and operands that provides a number or string result. A very simple expression is 1+ 2, but we usually reserve the term for lines that make use of variable names, and in which more than one operation may be carried out. A familiar pair of expressions are the incrementing expression, X= X + 1 and the decrementing

4-3 expression X= X - 1 . The =sign in BASIC is used here to mean 'becomes' rather than 'equals', and this is one respect in which BASIC can be very confusing to the beginner - other languages use different signs to distinguish between equality and assignment.

In general, the use of expressions is something that often proves baffling to a computer user who has no experience of mathematics. This needn't be, because expressions, like anything else in computing, follow precise rules, and once you know what the rules are it's not difficult to apply them. The most important rules concern precedence of operators, and once you know about precedence, it's not difficult to work out what an expression does. Making up an expression for yourself is another matter, and only practice can help there. The table of precedences is shown in Figure 4.2. What this means is that if you have more than one operation in an expression, the operation(s) with higher precedence are carried out first. If there is no clear precedence, then the order in the expression is simply left to right. For example, if you have the expression: PRINT 5+4*3-6/2 what do you expect? If everything obeyed a left-to-right order only, the result would be got from 5 + 4 = 9, 9 * 3 = 2 7, 27 - 6 = 21 and 2 1 /2= 11 • 5. It's not like this, though. Because of precedence, the 4 * 3 =1 2 and the 6 / 2 =3 are worked out first. Having done that, all that is left is of equal precedence, and we get 5 + 1 2 = 1 7 and 1 7 - 3 =1 4, which is the answer that the computer will give you. Remember that in a program, all or most of the quantities would be variables, and to fmd the numerical answer you would have to fmd what numbers were assigned to the variables at the time of evaluating the expression.

Highest

( ) Brackets t Exponentiation *1 Multiply and Divide +- Add and Subtract = < > Relational NOT AND OR Logical

Lowest

Operator precedence. This shows which actions will be performed fU'St in any expression. Ifall actions are of equal precedence, then the order is from left to right.

FIGURE 4.2

One important point to remember is that brackets take precedence over everything else. For example, if you have an expression which boils down to 5 * ( 4 + 3 ) , then this is not the same as 5 * 4 + 3 (which is 23) but is 35, because whatever is inside the brackets is worked out first, giving 7 in this case. When there are several sets of nested brackets, then whatever is innermost has highest precedence. For example, 5 * ( 4 + ( 8 - 6 / 2 ) ) gives 45, because the innermost bracket gives 5, adding this to the 4 in the next layer of brackets gives 9, and the result is 5 * 9. For some reason, however, it all looks much

4-4 more fearsome when used with variables, particularly when there are actions like S T R$ involved as well. An expression like: A%*CVALCB$)-C%/VALCO$»+E% always looks worse than it is. Using the rules, evaluate the VA L expressions fIrst, then work outwards. Suppose, for example, that the variables have been assigned values of A% = 5, B$ =" 1 6 ", C% = 8, 0 $ =" 2 ", E% = 6. The VA L expressions give numbers for "16" and "2", so that we can rewrite the expression as: 5*C16-8/2)+6 and working out the expression within the brackets gives 12. The expression is now 5 * 1 2 + 6, and this is 66. It's always easy if you take it in stages like this, and write down what each stage gives you. If you insist on trying to work it out at a glance then you might possible make a mistake, even with some experience.

Translating formulae

Some of the earliest computer programming languages were for scientifIc and engineering use, and translating formulae so that the computer could deal with them was a very impOrtant feature. It was so important, in fact, that one of the main languages in the early days was called FORTRAN, an abbreviation of FORmula TRANslation. FORTRAN is still used, and the BASIC language which is used by all microcomputers is based very considerably on the ideas and methods of FORTRAN. For this reason, BASIC is a language not to be despised if your needs or interests are in programming of this type. A lot of languages that are more highly regarded either by academics or for business. use are inferior to BASIC when it comes to working with formulae. If you haven't had some practice, however, it's not always straightforward to convert a formula written in a reference book into the form of a BASIC expression. For one thing, you have to remember that the order in which the terms of the formula are written will not usually be the order in which you want them evaluated, so that you must either change the order or make use of brackets to obtain the correct expression.

Examples help here, but one person's example is another's confusion, so please bear with me if the formulae that you want to use are not shown here. Remember that you don't have to derive the formulae for yourself for most purposes, you simply take them from a reference book. You need to know, of course, what variable values have to be supplied, and what the formula does, and you also need to know any limitations, but in general this is aU. You get into a different league when you start to generate your own formulae! We'll take as a fIrst example the formula for the volume of a sector of a sphere, shown in Figure 4.3(a). Now, like many formulae, this uses no sign for multiplication. Quantities that are printed together are intended to be multiplied, so that the formula requires you to multiply 2 by pi by the value of r (squared) by h, and then divide the answer by 3. Note that this uses the constant pi, and this value is available on your keyboard, though it isn't marked. If you press SHIFT t (the SHIFT key and the up-arrow key), you will see the pi symbol appear, and PRINT SHIFT t will give the number value 3.14159265,

4·5 which is probably to more places of decimals than you need. The code that is used, however, does not operate most printers, so that the pi sign does not appear in listings! Now in the expression, there is one power. taken, and this action will have precedence no matter where we put it. It makes sense, in any case, to start with this item, getting the value of r squared. Suppose that we use variable names R and H, then the expression that is shown in Figure 4.3(b) is the BASIC expression for evaluating the formula. The variable V is used for volume, and the main point to note is that we have to insert the multiplication signs * that BASIC demands, and the division sign I. Because all the operations apart from fmding the square are of equal precedence, we can write the rest of the expression in left-to-right order, and be reasonably confident. In all cases, however, if you are in any doubt, try a few examples with simple numbers and check that you get what you expect. In this example, the answers will always have more places of decimals that you would expect, and we'll look at how to round them off later .

.~-==-==- ...... -=-=-=------h

Sector of sphere

r

Formula is: V =2~r2h (a) 3

in BASIC: V=Rt2*2*n *H/3 (b)

A typical formula, in this case for the volume of a sector of a sphere, and how it is converted into a BASIC expression.

FIGURE 4.3

The real problems come when the formula is not in the form that you want. If you have a . smattering of algebra (i.e., if you are over the age of 40) then you may be able to rearrange the formula to suit. We can't in this book, cover all the mathematical actions of formula rearrangement except to give one example of where it is needed, and how it is

4-6 done. Suppose that you want to draw out the shape of an ellipse on the screen. If you make use of X and Y co-ordinate numbers, then you can choose a set of X numbers, and fmd the Y numbers that will give positions on the ellipse. The formula for the ellipse, however, will be shown in books in the form of Figure 4.4a. To be useful, we need this in the form Y = . . . , and the steps for changing into this form are shown in Figure 4.4b. This isn't computing, it's algebra! Once we get to the fmal form, however, we can write the expression that we need, which is: Y=(Af2-Xf2)*B/A and this is the expression that we shall use to fmd each value of Y for a given value of X. Remember that a set of X values ranging from 0 to A will give only one quarter of the ellipse corresponding to both A and B positive. For the other parts, you need to consider the results of + A and - B, - A and + B and - A and - B. Remember also that if you just want to draw an ellipse or any other geometrical figure, the C IRe L E statement of BASIC will cope with it.

(a) Equation for ellipse x2 y2 -+-=1 a2 b2

x2 y2 (b) Transforming equation -+'-=1 a2 b2 y2 x2 1. Get y2 on left -=1- - b2 a2 y2 a2_x2 2. Find equivalent of rhs - =-- b2 a2

3. Multiply by b2

4. Findy y=b aVa2~x2 5. Put into BASIC terms: Y= (A t 2-xt 2) *81 A

Transforming an equation (a) to get it into the form that will be needed in an expression. The steps in the transformation are shown in (b), along with the fmal BASIC form.

FIGURE 4.4

A very common requirement if you are working with probability in statistics is the factorial of a number. This is obtained from a formula in which the number is multiplied by the number one less, and by the next number down, and so on to 1. For example factorial 5, written as 5! is 5*4*3*2* 1. Another way of writing this is that 5!=5*4!, and this leads to a method of fmding factorials by what is called recursion, repeating a formula. If we keep to simple BASIC, then the factorial of a number X% can be found from a routine such as is shown in Figure 4.5. The important point here is to ensure that

4-7 if the number X% is 1, then the factorial value is also 1, and this is done by the test and GOT 0 in the fIrst line. If it were not for this 'odd' value of 1, the whole thing could be much more elegantly solved by a defined function of the form: DE F F NRC ( N ) =N * F NRC ( N- 1 ) , but this cannot be done because the form of a defmed function in the Commodore 128 does not allow any either/or choice to be programmed. Note also that a defmed function, like a FOR ••• N EXT loop, cannot take an integer value as its variable, so that using N %in the brackets would produce an error message. If you try the defmed function as shown, the computer will lock up, because there is no end to the calculation since you can keep calculating N -1 indefmitely. As it is, the limit is the stack memory, which runs out when attempting to store intermediate values.

READY.

10 INPUT "NUMBER";X% 20 GOSUB 60000 30 PRINT J% 40 END 600012) J%=X%:IF JY.=1 THEN 60050 60010 FOR N=XY. TO 2 STEP -1 60020 J%=J%*(X%-l) 60030 XY.=X'Y.-l 60040 NEXT 60050 RETURN

READY.

A subroutine for flllding the factorial of a number. This requires the use of a loop except when the number is unity.

FIGURE 4.5

Finally in this section, we need to look at what has to be done if the formula is in the form of a series. A series means that the result is found by using a loop, adding up a set of terms that are obtained from an expression. A series of this type is used to evaluate many quantities that are not usually provided as functions in small computers. For example, Figure 4.6 shows the series for evaluating the inverse hyperbolic tangent. If you don't know what that is, you'll feel better for not asking. The point is that the quantity can be found by substituting a value for the variable X and fmding a value for the series. The trouble with any kind of series like this is knowing where to stop, because most series like this are infmite. A practical, though arbitrary, solution is to continue until the term that you are evaluating adds less then 1% to the total value. This will require a loop which cannot be of the FOR ••• N EXT type, since the number of repetitions cannot be fIxed in advance. Figure 4.6 shows a suitable subroutine for this particular series, but note that this series converges only when the value of X is 1 or less. If X> 1 , then each term is larger than the one before it, and the total keeps growing until it becomes too large for the computer to store. This is a common thing to look out for in series, particularly series dealing with trigonometric functions, and it's always a good idea to put a trap for an illegal number in the subroutine, as has been done here. The trap is not

4-8 very elegant, and its use will result in the value of 0 being printed out along with the warning, but at least it prevents a long wait from being ended by an 0 VE R FLO W e r ro r notice.

READY.

10 INPUT "NUMBER";X:GOSUB 60000 20 PRINT "INV. TANH IS ";W 30 END 60000 IF X>l THEN PRINT"IMPOSSIBLE":GOTO 60050:ELSE W=X:NX=3 60010 DO WHILE XANX/NX>W/100 60020 W=W+XANX/NX 60030 NX=NX+2 60040 LOOP 60050 RETURN

READY.

Evaluating a quantity by using a series. This is another application of a loop to an expression.

FIGURE 4.6

Functions

There are many quantities that we need to calculate which cannot be dealt with by an operator, or even by a reasonably simple expression. Quantities such as trigonometrical ratios, square roots, hexadecimal equivalents and so on are dealt with by the use of Junctions. A function of a number is a quantity that is obtained by performing various actions on the number. The number (or more likely, the variable) is called the argument of the function, and for many functions has to be enclosed in brackets. These functions can include operations in sequence and the summing of one or more series. In the computing sense, functions can also include actions on strings, and the main thing that they have in common is that the function uses a statement word (not a symbol) and that it needs an operand or argument, which can be a number or a string depending on the type of function. The main number functions are listed in Figure 4.7, along with their effects. Of this list, the trigonometric functions merit particular attention, because they cause a lot of trouble to programmers who are working with trigonometrical formulae. These foinlulae are often the simplest way of obtaining some graphics effects, and to draw graphs. The functions that are most used are SIN, COS and TAN, all of which are provided in Commodore 128 BASIC. What you need to watch, however, is that each of these functions needs an argument which is an angle in radians. Now if you are working with angles in degrees you will need to convert from degrees to radians, and there is no conversion function built into the BASIC of the computer. This calls for a defmed function, which is illustrated in Figure 4.8. The defmed function makes use of the relationship between radians and degrees, which is that pi radians are equal to 180

4-9 Each of the following functions needs to use an argument, which will be a quantity that follows the function name, enclosed in brackets. The argument will normally be a variable name of the appropriate type. The effect of a function can be returned by using it as part of aPR I NT statement, such as P R I NT LOG ( 5 ) , or in an assignment such as J = SIN ( • 8 >. Where a function requires an angle as its argument, the angle will be in units of radians. Where the function returns an angle, the units of angle will again be radians.

Function Argument Effect

ABS number strips negative sign ATN number angle whose tangent is the argument COS angle cosine of angle DEC hex string denary equivalent EXP number exponential to power of number PRE bank number amount of free memory in bank INT number integer version of number, fractions stripped LOG number natural logarithm of number RND number returns 'random' fraction SGN number signals sign of nunlber SIN angle sine of angle SQR positive number square root of positive number TAN angle tangent of angle XOR two numbers XOR of two numbers

The main number functions ofCl28 BASIC.

FIGURE 4.7 degrees. By using this function, then you can make the conversion that is necessary for the functions COS, SIN and TAN. The reverse problem occurs when you need to use the only inverse trigonometric function, AT N. This fmds the angle (in radians) whose tangent has the value which is used as the argument. This is called an inverse function because it fmds the angle rather than the function of an angle. Figure 4.9 shows a defmed function which is useful for this type of action. Note that defmed functions must always be placed early in a program. If an F N is found before its DE F F N has been read, it will cause the program to stop with an UN D E FIN E D F N error.

READY. 10 DEF FNRD(D)=D*/180 20 PRINTIICOS OF 30 DEGREES IS II COS (FNRD ( 30» 30 END READY.

A dermed function for converting degrees to radians. Note that the pi sign should be typed between the * and the 1180. The Cl28 uses a code for the pi sign which does not print on the Epson printer!.

FIGURE 4.8

4-10 READY. 10 DEF FNDG(N)=N*1801 20 INPUTltTANGENT SIZE";J 30 PRINTltANGLE IN DEGREES IS ItFNDG(ATN(J » READY.

A defined function for converting from radians to degrees. The pi sign should be typed following the I in line 10.

FIGURE 4.9

The AT N is the only inverse trigonometrical function which is provided. It is also one which you don't need all that often, and a lot of work with trigonometry calls for the inverse SIN (A ReS IN) and inverse COS (A Rceo S) rather than the ARC TAN. 'The Commodore 128 manual (in its Appendix G) shows the relationship between these quantities, and they are quite straightforward to put into DE F F N form, because they have been printed in a suitable format. The inverse cosine and sine can also be obtained by evaluating series, but the defmed functions in Figure 4.10 will be useful if you need these functions. These illustrate the use of the relationships in the manual, so that you should be able to make your own defmed functions easily if you require any others from the list. READY. 10 DEF FNAS(X)=ATN(X/SQR(-X*X+l» 20 DEF FNAC(X)=-ATN(X/SQR(-X*X+l»+/2 30 PRINTnANGLE WHOSE SINE IS .4 IS u;FNA S(.4); II RADIANS" 40 PRINT 50 PRINT"ANGLE WHOSE COS IS .5 IS n;FNAC (.5) II RADIANS READY.

Dermed functions for arccos and arcsin (inverse cos and sine), using the formulae in the manual.

FIGURE 4.10

The other function which can cause problems is the LOG function. When you use a statement such as A= LOG ( X) then what is assigned to A is the natural log of X. In textbooks, this type of logarithm is usually written as LN rather than as LOG, and the use of LOG in BASIC can be a source of considerable confusion. This is compounded by the fact that some varieties of BASIC use both L N and LOG, with their correct meanings. If you are using a function which requires the natural log, then all is well - and remember that the 'antilog' function is EX P. The problems arise when you need to

4·11 use a base-ten logarithm. One good example is in calculating decibel ratios in electronic engineering. A practical deftnition of the ordinary logarithm of a number is the natural logarithm of the number divided by the natural logarithm of ten, and this is expressed in the example of Figure 4.11. This assigns the LOG ( 1 0) quantity to a variable so that it has to be calculated only once. From then on, the defmed function will fmd the base-ten logarithm for any number that you enter, as long as it is positive. Fractional numbers give negative logarithms, but using 0 or any negative number will give an ILL EGA L QUA NTIT Y ERR 0 R. If accidental entry of such numbers would cause problems, then you should trap them, either in the calling routine, or generally by the use of T RAP. The use of T RAP is covered at the end of Chapter 6.

READY.

10 LG=LOG (10) 20 DEF FNLG(N)=LOG(N)/LG 30 INPUT "NUMBER";J 40 PRINT "LOGARITHM IS u;FNLG(J) 50 INPUT"POWER RATIO";J 60 PRINT"DB RATIO IS "; 10*FNLG(J)

READY.

Converting from natural to the more familiar base-lO logarithms. This illustrates the use in rmding a decibel ratio.

FIGURE 4.11

Precision of numbers

An integer number is always precise, but its range is a small one, from -32768 to + 32767. Because an integer requires only two bytes for storage, the use of integers will improve speed, but unfortunately, the Commodore 128 does not permit the use of integers where this would be a great advantage, which is in the FOR ••• NEXT loop. As we have seen, the Commodore 128 also does not allow integers to be passed as the argument of a deftned function. If you can perform all arithmetic with integers, it will be fast and, with one exception, precise. The exception is division, because an integer number cannot be assigned with a fraction. If, for example, you assign A% = B %/ C%, with B %= 5 and C% = 3, then A% will be 1, not 1.66666667, which is what you would get from P R I NT 5 / 3. Some computers have a remainder operator, which allows you to print the remainder after an integer division, but this is not included in the Commodore 128. The use of integers in this machine is therefore not so extensive as it is in many of its competitors. One type of use in which integers are very defmitely an advantage, however, is graphics, since all of the screen co-ordinate numbers must be integers (see Chapter 9).

4-12 When you need to work with real numbers, meaning numbers which can be positive or negative, whole or fractional, and with a much greater range of size, precision becomes a problem. In a lot of applications, we use real numbers in standard form, which means that a number such as 52400 would be written as 5.24E4. When this is done, numbers are often approximated, so that the number 52417 might also be written as 5 .24E4 on the grounds that the extra 27 represented only a small fraction ofthe whole number. To put it another way, the precision of the number is sacrificed so that it can be written with only three digits before the E sign. This part of the number is called the mantissa, and the part which follows the E is called the exponent. When a real number is stored in the computer, it is also stored in mantissa-exponent form, but with both in binary numbers. The mantissa is a binary fraction, stored in four bytes, and the exponent is a single byte integer. This is a compact way of storing numbers, but it does mean that there will always be some approximations. Figure 4.12 illustrates this, using assignments to two variables which should be identical. Because of the approximations that have to be made in storing the variables, the two are not identical, as you'll see if you print out the values of the variables. READY.

10 A=1/11:B=7/11 - 6/11 20 IF A=B THEN PRINT"TRUE":ELSE PRINT"FA LSE" READY.

mustrating the problem of precision of numbers when real-number variables are used.

FIGURE 4.12

Rounding

Because of the precision problems that are caused by the use of real numbers, rounding may be needed before any comparisons are made. If, for example, you are writing an accounts program in which two quantities are expected to balance, then rounding will be essential. This is because you will be working with real numbers which include two . places of decimals, and because of storage errors, there may be small fractional errors. If you are using correct fielding to display your results, these fractions are of no importance, but they can cause any comparison to be incorrect. Two columns of figures, for example, which should give the same amount, will not necessarily give a true answer in a line such as: IF T1=T2 THEN PRINT "BALANCES" unless some rounding has been done. Suppose, for example, that you have the variable T 1 used to hold a total. The way to deal with this is by using T 1 = FN R N ( T 1 ) , where the rounding function is as shown in Figure 4.13. This rounding function has been

4-13 designed for rounding to the nearest penny, and you can adapt it if you need to work to closer limits. The essence of it is to multiply by a power of ten that will make the desired number into a whole number. If you multiply a money amount by 100, for example, the result should be a whole number of pennies. You can then use the I NT function to remove any fractions, and then divide again to get the correct fraction. This is a standard method, but you still see letters in the computer magazines asking why two quantities that ought to be equal do not obey an equality test. Once numbers have been rounded in this way, there should be no trouble with equality tests. You can, if you like, work to a precision of one tenth of a penny, and round the displayed figures again by formatting.

READY. 10 DEF FNRN(X)=(INT(X*100»/100 20 Tl=153.0500005 30 PR 1 NTFNRN (T 1>

READY.

Using a rounding function to eliminate small precision errors. The example is intended for rounding money amounts.

FIGURE 4.13

Number roundup

Working with numbers implies the input of numbers from the keyboard, processing, and the display of numbers on the screen. As far as input is concerned, the conventional method is the statement of the form I NPUT A or IN PUT A%. This is not always ideal for a number of reasons. One point is that an incorrect entry will not give a consistent message. If you are asked for an integer, using I NPUT A%, then entering a real number which is not too large, like 45.3, will cause no error message, but the number will be chopped, not rounded. If the number lies outside the integer range, you will get the ILL EGA L QUA NTIT Y error message, and the program will stop. If the I NPUT line calls for a real number and you enter a string, you will get the RED 0 FRO M S TAR T message, but no indication of what was wrong. In some instances, you might not want to have to type the number followed by the key. All of these problems can be dealt with by using more elaborate entry methods.

One good principle is to use a string for entry, such as IN PUT A$. No entry, unless it's a string of ridiculous length, will be rejected, and it's then easy to check what has been entered and issue messages about errors.

Suppose, for example, that you want to enter an integer, which must not be zero, but in the range 1 to 100. A suitable subroutine is shown in Figure 4.14, with a calling program. In the subroutine, the entry is made as a string, and in line 60010, the string is converted to integer form. The integer is then tested for the limits 1 to 100, and for being

4-14 unequal to VA L ( A$ ) , which would indicate that a real number was entered. If anyone of the tests fail, the the error messages are printed, and the routine is called recursively. Note the use of the BEG IN ••• BEN D construction so as to allow several lines of statements after the I F test. The only problem with tests of this kind is that it's not so easy to distinguish between an entry like "A" and one which is "0", because VA L ( " A" ) =V A L ( " 0 " ) =O. In such cases, a test such as: IF VAL(A$)=O AND ASC(A$)<>48 will detect the difference. READY. 10 PRINT"PLEASE ENTER NUMBER (1-100)" 20 GOSUB 60000 30 PRINTAY. 40 END 60000 INPUT "NUMBER";A$ 60010 AY.=VAL(A$):IF AY.<1 OR AY.>100 OR AX <>VAL(A$)THEN BEGIN 60020 PRINT"INCORRECT ENTRY OF II;A$ 60030 PRINTIINEED A NUMBER BETWEEN 1 AND 100" 60040 GOSUB 60000:BEND 60050 RETURN READY.

Using a string for entry, with conversion carried out by VA L. This is a preferred method of using INPUT.

FIGURE 4.14

Another point about the use of I N PUT is that it always requires the (RETURN> key to be pressed to signify an entry. This is not always convenient, and it's useful to have other options. One is provided by GET KEY, in the form of a statement such as GET KEY A$. This will wait until a key is pressed, and then return that key as A$, so that the value of A$ can be tested as before. This serves for one character only, though, and if a variable number of characters has to be entered, then the method is not so useful. One way in which this can be used is for fielded input, in which the entry may have to be for a particular type, like a date in DD/MMIYV configuration. An entry like this can be usefully combined with a screen display which shows the items that are entered, and allows only the correct number of characters to be entered. This does not always allow full checking as the characters are entered, but checking can always be carried out later. An example is shown in Figure 4.15, in which the date is entered in the DD/MMlYY form at the top of the screen, using a window. The fmal data is returned to the program and in this case just printed again to show that the string is correct. In a real program, the error-testing would be carried out at this stage so that the subroutine could be called again if necessary.

4·15 READY. 10 SCNCLR:PRINT"ENTER DD/MM/YV" 20 NRX=8:GOSUB 60000: PRINT: PRINT 30 PRINTRP$ 40 END 60000 WINDOW0,0,39,1:SCNCLR:TBX=20-INT(N RX/2):F$=II1:B$="I:RP$="" 60010 FOR J=1 TO NRXIF$=F$+CHR$(166):B$= B$+CHR$(157):NEXT 60020 PRINTTAB(TBX)F$;B$;:FOR J=1 TO NRX 60030 GETKEY K$:PRINTK$;=RP$=RP$+K$:NEXT 60040 WINDOW 0,0,39,24:RETURN READY.

An example of a fielded entry, in which the space for the entry is printed on to the screen and filled as the characters are entered.

FIGURE 4.15

The subroutine creates a two-line window, and clears this space. Constants T B %, F S, BS and R P S are initialised, with NR % passed to the routine as the number of spaces required. This makes it unnecessary to have to change the subroutine when a different number of spaces is used. Line 60010 then fills a string with chequer pattern blocks. This line could be amended to show the strokes in the date if needed, but if you do so, you will need to ensure that they are not replaced by a number at the input stage. The string BS is filled with 'cursor-left' characters, so that the cursor can be returned to its original position. Line 60020 prints the pattern and returns the cursor, then line 60030 uses GET KEY KS to get each key that is pressed and place the character on the screen, replacing a chequer. The slashmark between numbers must also be typed. The FOR. • • NEXT loop ensures that only the correct total of characters can be input, following which the WIN DOW statement in line 60040 restores the full normal screen and the subroutine returns. The visual input routine in Chapter.6 can also be useful for number input.

4-16 I ChapterS Complex data

Number arrays

As far as BASIC is concerned, the primary data types are integers, floating point numbers and strings, and the only complex data type is the array. An array is a list of items which are stored in the memory as one block, and which you can access by using a common identifier name along with a subscript number. For example, if we create an array of numbers with name A X, then we can refer to numbers in this list as A X ( 0 ) , AX ( 1 ) , AX ( 2) and so on, These names are pronounced AX-of-zero, AX-of-one and so on. The Commodore 128 allows you to work with arrays of any of the primary data types, integer, floating point, or string. These arrays can be further classed by their number of dimensions, meaning the number of subscripts that has to be used to specify an item. The simplest arrays are one dimensional, with members such as A %( 2 ) , G ( X% ), D$ ( K- 3 ). A two dimensional array requires two sUbscript numbers separated by a comma, such as A% ( 4 , 2 ), C ( X% , Y% ), H$ ( J - 2 , K+ 6 >. Arrays of this type are extensively used in specifying positions on a flat surface, as in the use of screen graphics. Arrays with three or more dimensions are less common, but can have applications for some purposes. Three dimensional an-ays can be used to specify position in space (using X,Y and height coordinates), and four-dimensional arrays are sometimes found when a quantity is affected by four others, such as three space dimensions and one of time or temperature. We'll start looking at array techniques by considering one dimensional arrays.

Lists

A one dimensional array is often referred to by the shorter name of list. Any type of list (integer, float, string) can be filled by using a straightforward FOR. • • NEXT loop, remembering that an array which will use a subscript number greater than 10 needs to be declared in a DIM statement. Remember that a subscript number of up to 10 allows up to 11 array members, because the zeroth member (such as A (0») is allowed. Typically, then, an array will be filled in lines such as:

5-1 DIM A%(20) FOR N=O TO 20 INPUT A%(N):NEXT - assuming here that the numbers are to be entered from the keyboard. The numbers could, of course, be supplied from a set stored on a disk, and we shall look at that type of input in Chapter 7. The numbers could also be printed in a straightforward list, but this is wasteful, because printing one number on each line permits only 25 numbers per screen 'page'. A much better method is to force the printing into columns, as Figure 5.1 illustrates. This is not such an elementary example as you might suppose, because I have taken the opportunity to deal with two common problems of number display. One is that it's very easy, using TAB, to print in columns, but not so easy if you need the numbers right justified. The other point is that it can be quite difficult to make sure that all of the numbers of an array are printed in columns when the number of items does not fill all of the columns.

READY. 10 DIM A'X.(22):F$="######tI 20 FOR N=0 TO 22 30 READ A'X.(N):NEXT 40 TRAP1000:SCNCLR:J'X.=0:Y'X.=1:DO 50 FOR N=l TO 4 60 CHAR1,10*(N-1),Y'X.:PRINT USING F$;A'X.(J 'X.); 70 J'X.=J'X.+1:NEXT:PRINT:Y'X.=Y'X.+l:LOOP 80 PRINT:PRINT:PRINTtlENDtI:TRAP:END 1000 IF ER=18 THEN GOTO 80:ELSE PRINTERR $(ER);" IN LINE ";EL:END 2000 DATA 12,-2,5,7,8,9,-5,6,7,4,6,7,45, 56,-222,23476,12,43,6,78,98,45,67 READY.

Forcing printing into columns without using TAB. This uses CH A R and US I NG, and also demonstrates a use for T RAP.

FIGURES.l

The display format is solved by using P R I NT USIN G, as you might expect. You cannot, however, have both US I NG and TAB following aPR IN T statement (the manual doesn't tell you things like that!), so that the cursor has to be placed by using CH A R. The problem of printing a set of items that will not divide evenly into the number of columns is solved here by the use of T RAP. The principle is to print using an endless loop, and force a jump out of the loop by trapping the occurrence of an error message. It's not elegant, but it greatly simplifies the programming. The array is filled from aRE AD ••• DA T A statement to save effort. This is a useful dodge when you are

5·2 testing an array program, because if you use an I NPUT line, then any syntax error will force you to abort, and you will have to input another set of numbers on the next run. The display section starts in line 40 with T RAP 1 000. This statement will force BASIC to execute from line 1000 if any error is reported. This over-rules the normal error reports, apart from disk errors. The screen is then cleared, and the variables J %and Y% are initialised before the endless loop is started with DO. In each pass through the loop, four numbers will be printed in columns, so that line 50 starts a suitable inner loop. Line 60 contains the meat of the printing routine, with CH A R used to position the cursor at columns 0, 10, 20 or 30 according to the value of N, and the P R I NT US I NG F $ statement printing the formatted number. Line 70 contains J %= J %+ 1 to increment the array item, then a N EXT to increment the column loop, aPR I NT to force a new line, and then the incrementation of the row number Y%. This is the far end of the loop, which has no ending condition. Obviously, if we knew how many items were in the array (as we do in this example) we could make the loop end when J %exceeded the number of items. If we don't know this, and in many cases we might not, then the T RAP swings into action. When the loop tries to print an array item which doesn't exist, control passes to line 1000, which detects the error number 18 meaning 'bad subscript'. This then causes the program to end - or go to another line. Any error which is not a bad subscript error is printed out using ERR $ ( E R) as the error message, and E L as the error line number. This use of T RAP is rather artificial, because you would normally know the number of items to be printed, but it's a convenient way of introducing the use of the T RAP statement.

Number sorting

One very common requirement with lists of numbers is to sort the lists into some kind of order, either ascending or descending. Many textbooks offer the bubble sort for this purpose. The bubble sort is easy to understand, but requires a lot of time if you are working with long lists. A much better way of sorting long lists is the quicksort. It's certainly not an easy sort to understand, and it's even more difficult to explain without going through the action step by step. That's something you can do for yourself by putting suitable P R I NT and GET KEY lines into your program, so in this book, I'll leave out the explanations. The quicksort is shown as a subroutine in Figure 5.2, sorting a list of random numbers, and using a version of the display routine shown in Figure 5.1. Even this sort is not exactly breathtakingly fast for three hundred integers, and it can be noticeably slower than the bubble sort for small numbers of items (less than 50). In this example, the sorting has been speeded up by selecting the F A S T running speed of the microprocessor just before the subroutine and reverting to S LOW just after. This causes the screen to blank out while sorting is proceeding, and if this is too alarming to contemplate, then you can restore the more comforting message by removing the F A S T and S LOW statements in line 30. The numbers have been created in this example by using R N D ( 0 ) * 1 E 4, so that the random fraction produced by R N D ( 0) is multiplied by ten thousand. These numbers are not completely random, but they are good enough to give the sorting program plenty to do. In the printing routine, the T RAP has been left in, but the display is more closely controlled by detecting the value of Y% in line 70, so as

5-3 READY. 10 DIM A'X.(300):F$=u •••••• u:N'X.=300 20 PRINT"PLEASE WAIT-CREATING NUMBERS":F OR N=1 TO N'X 30 A'X(N)=RND(0)*lE4:NEXT:FAST:GOSUB 6000 0: SLOW 40 TRAP1000:SCNCLR:J'X=1:Y'X.=1:DO UNTIL J'X >N'X. 50 FOR N=1 TO 4 60 CHAR1,10*(N-1),Y'X.:PRINT USING F$;A'X.(J 'X); 70 J'X=J'X.+1:NEXT:PRINT:Y'X.=Y'X+1:IF Y'X>23 T HEN SLEEP5:SCNCLR:YX=1 80 LOOP 90 PRINT:PRINT:PRINT"END":TRAP:END 1000 IF ER=18 THEN GOTO 80:ELSE PRINTERR $(ERR);" IN LINE ";EL:END 1010 END 3000 END 60000 PRINT"PLEASE WAIT •••• SORTING .. 60010 PX=l:Q'X(l)=l:R'X(l)=N'X 60020 U'X=Q'X(P'X):D'X.=R'X(PX):P'X=P'X-l 60030 J'X=U'X:LX=DX:X'X=AX(INT«U'X+D'X)/2» 60040 DO WHILE A'X(J'X)(X'X:J'X=J'X+l:LOOP 60050 DO WHILE X'X(A'X(L'X):L'X.=L'X-l:LOOP 60060 IF J'X)L'X THEN 60100 60070 A'X=A'X(J'X):A'X(J'X)=A'X(L'X):A'X(L'X)=A'X 60080 J'X=J'X+l:L'X=L'X-l 60090 IF JX<=L% THEN 60040 60100 IF J'X>=D'X THEN 60120 60110 P'X=P'X+1:Q'X(P'X)=JX:R'X(PX)=D'X 60120 D'X=L'X:IF U'X.0 THEN 60020 60140 RETURN READY.

The quicksort subroutine, here shown in action sorting a list of 300 random numbers.

FIGURE 5.2 to 'page' the output. The S LEE P statement is used in this example, but you could substitute aGE T KEY statement if you wanted more time. The sorting routine occupies lines 60000 to 60140. This uses some GOT 0 statements which could be rewritten into a more structured form, but the advantage of GOT 0 in a routine like this is that it's fast, and speed is important in any sorting subroutine. The important line to look for is line 60070, in which the array values are swapped. This is the line which needs to be altered if

5-4 you are sorting any other type of variables. If, for example, you want to sort real numbers then each A% would be replaced by A, or by whatever variable name that you wanted to use. You would be well advised, however, to use this sort only for numbers, either integer or real, rather than strings, for reasons that will be discussed later. If you want to try a very simple sort, then one is listed in Figure 5.3, but for 300 random numbers this will take 16 minutes and 53 seconds at the S LOW rate, and 8 minutes 26 seconds at the FA STrate. I did say that the quicksort was noticeably quicker! The simple sort can, however, be useful for small arrays of numbers.

READY.

60000 PRINT"PLEASE WAIT •.•. SORTING" 60010 FOR J=l TO NI.-l 60020 FOR P=J+l TO NI. 60030 IF AX(P)(=AX(J) THEN BEGIN 60040 AI.=A%(P):AX(P)=A%(J):AX(J)=A% 60050 BEND 60060 NEXT:NEXT:RETURN

READY.

A very simple sort routine. This is suitable only for short lists, because it takes a very long time to sort 300 numbers.

FIGURES.3

The next topic as far as numbers are concerned, is locating a number in a list that has been put into order. For a list that is not in order, you need to check each item in turn, but for sorted lists a very much faster subroutine exists in the form of a binary chop. The principle is to break the list in two, and fmd which half contains the number that is being looked for. This part is then halved in turn, and the process is repeated until the number is found or the search is abandoned. The usefulness of the routine lies in the fact that it needs very few loops, because even a very large list can be reduced to one item with a surprisingly small number of repeated halvings. The technique is even more useful for string items, and the subroutine is illustrated, in its number form, in Figure 5.4.

The subroutine in line 61000 starts by asking for the required item. In this example, to save effort, the numbers consist of an array of even numbers ranging from 2 to 200. The required number is assigned to A%, and the next line initialises variables. The variable L 0 %is used to carry the lowest subscript number which in this array is 1. If you intend to use A% ( 0) in your array, then this assignment will have to be altered, or the A% ( 0) item will never be found. The highest subscript number, passed as N% from the main program, is assigned to HI %. The quantities F 0 % and MI % are 'flags', used respectively to report whether an item has been found or is missing. For most purposes, one or both of these can be passed back to the main program for use; both will be tested in the subroutine. The main loop of the subroutine starts in line 61020, and consists of a loop which will continue until the item is found or is reported missing. The search actions then lie in the:lines 61030 to 61110.

5-5 READY.

10 DIM AX(300):F$=" ••## •• ":NX=100 20 PRINT"PLEASE WAIT-CREATING NUMBERS":F OR N=1 TO NX 30 AX(N)=2*N:NEXT: 40 A$=IIVII:PRINT"LIST READV":DO UNTIL A$< >"V":GOSUB 6100121 50 IF FOX=ITHEN PRINT"ITEM IS NO. "MDX:E LSE PRINT liND SUCH ITEM" 6121 PRINT"PRESS Y TO REPEAT SEARCH" 70 GETKEVA$:LOOP 80 END 6100121 PRINT"PLEASE TYPE NUMBER REQUIRED" : INPUT AX 61010 LOX=I:HIX=NX:FOX=0:MIX=0 61020 DO UNTIL (FOX=1 OR MIX=I) 61030 MDX=(HIX+LOX)/2 61040 IF AX=AX (MDX) THEN FOX=I:GOTO 6112121 61050 IFAX

A binary chop subroutine for finding a Dumber in an ordered list. This is very fast, but the items must be in order.

FIGURE 5.4

These actions consist of tests and alterations to the variables. The fll"St action is the calculation of MD %, a subscript number that lies about mid-way between L 0 % and HI %. Line 61040 then tests to see if the wanted item is the array member A$ ( MD % ) • If it is, then F0 % is set to 1 (item found) and the GOT 0 directs to the end of the loop, so that the UN TIL action can be used to terminate the loop correctly. If, as is likely on the first pass, the items are not equal, then the test A% < A % ( MD %) is made. If this is true, then A% lies in the first half of the set of subscripts. We need to test at this point to fmd if MD % has been reduced to the value of L 0 %, because if this has happened, the whole range has been tested and the item is missing. This is the fust action in the BEG IN ••• BEN D structure. The next action applies if MD % has not been reduced to L 0 %- the value of HI %is made equal to MD % -1 . This will restrict searching from now on to the lower half of the group of subscripts.

5-6 If A % has not been less than A %( MD %) and is not equal, then the test for A% >A% (M D%) must succeed in line 61090. Here again, the BEG IN ••• BEN D encloses another test, for MD % = H I %, which means that the whole group has been searched with no success. The alternative is to alter the value of LO% so that the upper part of the group will be searched next time. The program then loops back until either the item is found or the variable MD % reaches either L 0 %or H I %without matching the item. The thing to note in testing this routine is how little difference in time there is between fmding an item in the middle of the list and one at either end. An item in the middle of the array will be found on the first pass through the loop, whereas an item at either end will require several passes. The maximum number of passes, however, is small even for very long lists. As the name 'binary search' indicates, the number of passes is n, where 2ft >= the number of items. For example, 128 items would take at most 7 loops, since 27=128. This is a very efficient search, but you must remember that the items must be in order before the search can be used.

String arrays

String arrays are different and special, and cause more problems than almost anything else in computing. For a lot of computing purposes, string arrays are the most imponant form of variable because such a great pan of data processing is concerned with strings. Most lists, other than computer lists, are of string items such as names and addresses, page references to magazines and indexes to things of interest to us. Because of the way that strings are stored in the computer, however, working with strings and string arrays is not quite so straightforward as working with the corresponding number variables. The imponant difference is storage space. An integer number is stored in two bytes, a floating point number in five. By contrast, a string can consist of anything from one byte (a single character) to 255 bytes (the longest allowable string). Some computers, and some programming languages (notably PASCAL) get round the problem by requiring you to specify the exact number of characters in each string you use. The Commodore 128 takes the other approach, of allowing you to use what you like as a string, and keeping a note of where it is stored. The 'note' consists of three bytes, two to store the address where the first character of a string is stored, and one for the length of the string. Instead of using the variable and an unpredictable number of bytes that is needed for storing a string then, the computer keeps track by using this block of three, sometimes called a 'dope block'.

5-7 All of this would be of purely academic interest, as it is on several other computers, if the Commodore 128 BASIC did not allow you to fmd where this dope block is stored. The key to this is the POI NT E R statement. When you use POI NT E R ( A$ ) , for example, the result is a number. This is the address of the BAN K 1 memory in which the fIrst byte of the dope block is stored. These addresses can shift around, incidentally, so you have to fmd the address afresh each time you need it. Having found the address, you can use it with PEE K to fmd the bytes in the block.

Figure 5.5 illustrates POI NT ERin use. One slight complication is that the Commodore 128 stores its variable values in the second bank of RAM: BAN K 1. This means that we have to switch to BAN K 1 when we want to make use of the address supplied by POI NT E R, or to see where that address leads to. In the listing, a string A$ is defmed, and its dope block address is found by using POI NT E R ( A$ ) . This will be an address in BAN K 1, and it's the fIrst of the three bytes in the dope block, representing the length of the string. Peeking this address should give the string length, and this is assigned to variable L. The next two addresses contain bytes which will make up the true address of the string. These bytes are in lo-hi order, so that to obtain the string address, the fIrst byte must be added to 256 times the second byte. This gives the new address P in the listing. These quantities are printed in line 40 so that you can see the values. We can then recover the string by peeking addresses that start at P, using L as the number ofbytes to peek. The fact that the loop in lines 50 and 60 print out the word T EST is proof that the string was indeed stored in this way.

READY.

10 A$="TEST" 20 A=POINTER(A$):PRINTA:BANK 1 30 L=PEEK(A):P=PEEKCA+1)+256*PEEK(A+2) 40 PRINTL,P 50 FOR J=0 TO L-l 60 PRINTCHR$CPEEK(P+J»;:NEXT 70 BANK 15

READY.

Using PO I NT E R to fmd the 'dope block' or variable list table entry for a string.

FIGURES.S

Things are altogether more diffIcult when we try to make use of POI NT E R with a string (or any other) array. The main problem is that we can't go about making use of variables that have not been defmed, because these force the addresses of the dope blocks and the string address data into higher parts of the memory. Suppose, for example, that we have the statement P R I NT POI NT E R (A $ ( 0) ) , and we fmd that it returns a number, say 2243. Now if we add the statements X=POINTER(A$(O» and P R I NT X to the program, we fmd that the number printed is, for example, 2250. This change of address occurs because number variables are stored below the string array variables in the memory, and the string variable quantities need to be moved in order to

5-8 make room for them. How, then, can we cope with printing values from the memory when they keep shifting? The answer is illustrated in Figure 5.6 - don't shift them! If each variable that will be used has been assigned with a dummy value before the program runs, then the position of the strings in the array won't change, because there are no new variable names being put into the memory. All that is happening is that variables T M, J and P have their values changed, but this doesn't involve taking up any more space, because the space that is needed for a number variable is fIxed. This space is 7 bytes, of which two are needed for the name of the variable, and fIve for the value (since we are using floating-point variables in this instance). The program will therefore correctly print the value of the string array A $ ( 1 ), and you can try substituting the other possible values as well . Hang on to all this, because we're going to come back to it. READY. 5 TM=12345:J=12345:P=12345 10 A$(1)="TEST"IA$(2)="TRY"IA$(3)="SURE" :A$(4)="END": 20 PRINTPOINTER(A$(l» 30 TM=POINTER(A$(l» 40 BANK1:P=PEEK(TM+l)+256*PEEKCTM+2) 50 FOR J=0 TO PEEK(TM)-l 60 PRINTCHR$(PEEK(P+J»;:NEXT 70 BANK 15 READY.

Using PO I NT ER with a string array. The addition ofa number variable will shift the dope blocks, so that variables have to be assigned before the subroutine runs.

FIGURE 5.6

Sorting string lists

String lists can be sorted in much the same way as number lists, except that a string array has to be declared and filled fIrst. The most popular sorting algorithm is the Shell-Metzner sort, and this is illustrated in Figure 5.7. It is rather simpler than the quicksort, and performs very well with large string arrays. The basis is the same as that for any of the faster sorting methods - to compare items which have subscripts that are about half the list number apart, and then to alter the gap until items next to each other are sorted. In Figure 5.7, the sort is shown as a subroutine, adapted for Commodore 128 BASIC and running as fast as you can expect of a string sort.

You'll gather from that faint praise that string sorts don't normally run quickly. Small string arrays can, in fact, be sorted quite quickly, and this listing gives you a chance to see why it is that large ones do not. Run the program, and then use

5·9 READV. 10 A$(1)=IIONE II :A$(2)="TWO":A$(3>=IITHREE" I A$ (4) =IIFOURI1 : A$ (5) =IIFIVE 11 : NX,=5 20 GOSU8 61000 30 FOR N=l TO 5:PRINTA$(N):NEXT 40 END 61000 PRINT II SORT ING- PLEASE WAIT":V7.=l 61010 DO WHILE V7.0 THEN 61040 61070 8END 61080 NEXT:LOOP 61090 RETURN READY.

Sorting a string array using a Shell-Metzner type of sort routine. The strings are interchanged in memory, which means that many temporary strings are created and left in the memory.

FIGURE 5.7

? POI NT E R ( A$ ( 1 ) ) to fmd the address of the first name in the sorted list. This will give an address in memory as usual, and if you convert this into hex and use the monitor, you will see addresses for the members of the array. These addresses are not, however, adjacent as Figure 5.8 illustrates. If you now use the monitor to look in memory at addresses from # FE9E upwards, you'll fmd that these addresses are full of string data. In fact, each time the sort routine exchanges the values of strings in its line 61050, new strings are created and placed in memory. This has to be done because a string does not have a fixed length, and each time a string variable name is assigned a different value, a new place is used in the memory to avoid the awkwardness of testing to see if the old position is large enough. This results in the memory filling up with unwanted old string values, particularly if a large array is being sorted. This has two consequences. One is that sorting is slow because of the work of creating new strings. The other is that if a lot if strings have been made in this way, the memory will fill up, and will have to be swept clear of unused string addresses. This process is called 'garbage collection', and when it happens, nothing else can happen until it has fmished. To the onlooker, the string sort appears to be taking even longer than it should.

This can be avoided by using machine code for the exchange action, or in the BASIC of any machine that possesses a POI NT E R type of statement. The principle is simple enough - instead of swapping strings when needed, we simply swap the pointers. Since the pointers are integer numbers, and only ever three of them, the swapping can be more rapid. The great advantage, however, is that the strings that were originally put into memory stay unchanged, and no additional strings have to be created. This means no

5-10 >108FE 04 AF FE 04 B5 FE 03 9E:.I...... S ...... >10906 FE 05 A3 FE 03 C7 FE 00:"'. t# .... G...... >1090E 00 00 00 00 00 00 00 0m: •••••••• >10916 00 00 00 00 00 00 00 mm: ••••.•.. >1091E 00 00 00 00 00 00 00 00: ......

The monitor's picture of the dope blocks after a string son, showing that the elements of the array are not now adjacent.

F1GURES.8 garbage, and no garbage collection time. For a small number of strings, of course, the difference is not noticeable, but for a large number it is defmitely an advantage to operate on the pointers rather than on the strings themselves.

Our experience so far, however, indicates that we have to be careful about this, to avoid shifting the position of the string pointers. Provided that no new variables are created between fmding a pointer address and using it, however, there should be no problems, and as we have seen, this can be done by making sure that all variables have had a value assigned to them before the swapping starts. Figure 5.9 shows the Rrevious string sorting program modified so as to swap the pointers rather than the strings. The lines have not been renumbered so that you can see how the new pointer exchange fits in. The variables L, Y 1 %, Y2 %, X 1 % and X2 % have been assigned with dummy values in line 61005, before the loop starts. Line 61040 is the point at which the array values are tested for order, and it's here that swapping, if needed, will start. The swap operations are contained, as before, between the BEG I N and BEN 0 statements, and the first statement is BAN K 1, to ensure that the correct bank of memory is being used. The two pointers are then found, and assigned to X 1 % and X 2 % respectively. We then have to interchange three bytes, starting at the pointer address, and this is done by a loop which starts in line 61042. The bytes are peeked from one address and poked into the other, and the addition of the loop variable L to the address ensures that all three bytes of each dope block are exchanged. When this has been done, we can switch back to the default bank number of 15, and then take a new value of J % as before.

5-11 READV. 10 A$(1)=IONEI :A$(2)=ITWOI :A$(3)="THREE" :A$(4)="FOUR":A$(5)="FIVE":NX=5 20 GOSUB 61000 30 FOR N=l TO 5:PRINTA$(N):NEXT 40 END 61000 PRINT"SORTING- PLEASE WAIT":VX=l 61005 L=12345:V1X=12345:V2X=12345:X1X=12 345:X2X=12345 61010 DO WHILE VX(NX:VX=2*VY.:LOOP 61020 DO:VY.=(VX-1) 12: IF VX=0 THEN 61090 61030 ITX=NX-VY.:FOR 1=1 TO ITX:JX=I 61040 Zy'=JX+VX:IF A$(ZX)(=A$(JX) THEN BE GIN: BANK 1 61041 X1X=POINTER(A$(ZX»:X2X=POINTER(A$ (JX) ) 61042 FOR L=0 TO 2 61044 V1X=PEEK(X1X+L):V2X=PEEK(X2X+L) 61046 POKE X1X+L,V2X:POKE X2X+L,V1Y. 61049 NEXT:BANK15 61060 JX=JY.-YX:IF JX>0 THEN 61040 61070 BEND 61080 NEXT:LOOP 61090 RETURN

READY.

A string array sort routine that exchanges pointers rather than string characters.

FIGURE 5.9

Matrix operations

A multi-dimensional array is called a matrix, and can have a variety of uses. In Adventure games, for example, a multidimensional string matrix can be used to store 'location' information, and two-dimensional number matrices can store screen image data. Arrays of this type are filled and used in very much the same way as a single dimensional list, so that there is not much point in going over the more elementary uses. The use of number matrices can get us into highly mathematical topics, and of these, only one is likely to be useful to more than a few readers. This is the use of a number matrix for solving simultaneous equations. If you don't know simultaneous equations from a Waldorf salad then you should skip this section, but if you fmd that you need to solve equations of the form: 3.5x + 2.7y = 99.27 17.4x + 9.8y = 465.98

5-12 READY. 10 K(1,1)=3.5:K(1,2)=2.7 20 K(2,1)=17.4:KC2,2)=9.8 30 G(1)=99.27:G(2)=465.98:NX=2 40 GOSUB 60000 50 PRINTUSING" ••••••• 11 ; L (1) ,L (2) 60 END 60000 PRINT"PLEASE WAIT ••• SOLVING MATRIX " 60010 XXX=TRUE:FOR J=1 TO NX-1:JX=J 60020 YX=JX:X=KCJX,JX) 60030 FOR H=JX+l TO NX:HX=H 60040 IF K(HX,JX)(=X THEN NEXT 60050 ELSE YX=HX:X=K(JX,HX):NEXT 60060 IF X=0 THEN PRINTIINO POSSIBLE SOLU TIONIt:XXX=FALSE:GOTO 60210 60070 IF YX=JX THEN 60110 60080 FOR F=1 TO NX:FX=F:Z=K(JX,FX) 60090 KCJX,FX)=K(YX,FX):KCYX,FX)=Z:NEXT 60100 Z=G(JX):G(JX)=G(YX):G(YX)=Z 60110 FOR H=JX+1 TO NX:HX=H 60120 X=K(HX,JX)/K(JX,JX) 60130 FOR F=JX TO NX:FX=F 60140 K(HX,FX)=K(HX,FX)-X*K(JX,FX) 60150 NEXT:G(HX)=GCHX)-X*GCJX) 60160 NEXT:NEXT:L(NX)=G(NX)/K(NX,NX) 60170 FOR J=NX-l TO 1 STEP -1:JX=J:Z=0 60180 FOR H=JX+1 TO NX:HX=H 60190 Z=Z+LCHX)*K(JX,HX):NEXT 60200 L(JX)=(G(JX)-Z)/K(JX,JX):NEXT 60210 RETURN READY. An equation-solving subroutine. along with a smaIl calling program to illustrate its use. It is not confined to just two simultaneous equations, but the time that is needed will be greatly lengthened if a 1arge number of equations are to be solved.

FIGURES.IO then this could be a very useful subroutine for you. Figure S.10 shows the subroutine with only a very brief calling program. If you want to use this as a general subroutine for solving equations that might use several more unknown quantities, then the calling program will have to be more elaborate. If, on the other hand, you always need to solve equations for the same form, then you could have a calling routine which contained a loop, and filled the matrix using program lines such as those on the next page:

5·13 INPUT"NUM8ER OF EQUATIONS";N% FOR 8=1 TO N% PRINT "EQUATION"; 8;" COEFFICIENT OF X "; INPUT K(1,1> and so on. The way that the numbers in the equations correspond to the matrix and the list values are shown in Figure 5.11, and the results are taken from another list, L. The subroutine uses a variable XX % to determine whether the results are valid or not, and this can be used to suppress the printing of results if the equations cannot be solved. In this example, XX %has not been used because the equation that is supplied is guaranteed to have solutions! Note the use of P RI NT USIN G to avoid rounding eJ,Tors from being printed. It would be unusual in equations of this type to need to work to more than three places of decimals.

Equations: 3.5x + 2.7y = 99.27 17.4x + 9.8y = 465.98 Matrix fonn: Column Column 2 Li s t

Line 1 3.5 2.7 99.27

Line 2 17.4 9.8 465.98

K(1,1>= 3.5 K(1,2)=2.7 G(1)= 99.27 K(2,1>=17.4 K(2,2)=9.8 G(2)=465.98

Solutions:

L(l)=22.5 L(2)= 7.6

How the matrix numbers are derived from the equations, and how the answers are obtained from a list.

FIGURE 5.11

5-14 Chapter 6

Menus, subroutines and program design

A menu is a list of choices, usually of program actions. By picking one of these choices, we can cause a section of the program to be run. This is a very useful way of keeping a form of central control over what a program does, and so it's not surprising to fmd that most business programs and other programs for 'serious' applications are based on a menu. A program which is designed around a menu is often described as being 'menu-driven'. We'll start this Chapter, then, by looking at the display of menus and the way in which choices can be made from them. The Commodore 128 allows you a considerable number of options in how you present your menus and make your choices.

One way of making the choice is by numbering the menu items, and typing the number of the one that you want to use. Figure 6.1 shows what a typical menu of this type would look like on the screen. The part of the listing that displayed this menu would then go into a GETKEY type of loop, awaiting the pressing of a number key. This number would be tested for the correct range, with an error message if the range did not suit, and then the choice of routine would be made. With a computer which was fitted with Stone-age BASIC, we could use a set of lines such as: IF K% =1 THEN 1000 IF K% =2 THEN 2000 and so on. There is a much simpler method, however, which uses the instruction words, ON ••• GOT 0 or 0 N••• GO SUB. These represent two different ways of carrying out the same task, and we'll look at both of them closely, because this type of command is not available in some other versions of BASIC that you might have used. MENU

1. Make a new list of names 2. Add names to list 3. Read names from list 4. Delete names from list 5. Select one name 6. End program PLEASE CHOOSE BY NUMBER A typical menu of the conventional type.

FIGURE 6.1 6·1 Looking at the detail of the menu system, suppose we want to pick from four items by typing numbers 1 to 4 on the keyboard. In the example of Figure 6.2, lines 10 to 50 print a title and a menu, using TABs to make the printing reasonably neat. This is then followed by advice on how to choose (it might be obvious to you, but not to any other user!). The action starts in line 70. This contains the start of an endless DO loop which extends until the end of line 80. In this case, though, there is a way out, the E X I T statement at the end of line 70. The keyboard is tested, and waits for a key to be assigned to K%. If this number is in the correct range, the E X I T statement operates, so that the loop is abandoned, and the next line to be executed is line 90. This is a beautifully straightforward method of testing and leaving a testing loop, and it is very rare in BASIC. If the number has not been in the correct range, the E X I T statement does not apply, and the loop will then print the error message, and return you to the GET KEY K% part of the loop until the correct number is typed. Note that you don't have to use the key to select, and that if you press a letter key, then the program stops with a T YP E MIS MAT CH error.

READY. 10 SCNCLR: PRINTTAB (19) "MENU" 20 PRINTTAB (2) "1. DAILY TAKINGS." 30 PRINTTAB(2) "2. STOCK SITUATION." 40 PRINTTAB(2) "3. PURCHASES." 50 PRINTTAB(2) "4. SUMMARY." 60 PRINT: PRINTTAB (1) "PLEASE SELECT BY NU MBER 1-4":PRINT 70 DO:GETKEY K%:IF K%)0 AND K%(5 THEN EX IT 80 PRINT"INCORRECT RANGE- 1 TO 4 ONLY- P EASE TRY AGAIN":PRINT: LOOP 90 ON K% GOTO 110,130,150,170 11210 END 11121 PRINT"ROUTINE 1" 120 END 13121 PRINT"ROUTINE 2" 14121 END 15121 PRINT"ROUTINE 3" 16121 END 170 PRINT"ROUTINE 4" 180 END READY.

A listing for a menu routine of the conventional type, using 0 N K % GOT O.

FIGURE 6.2

6·2 At line 90, with the value of K% in the correct range, the choice is made by the statement oN K% GOT 0 11 0 , 1 3 0 , 1 5 0 , 1 7 o. This list must be in the order that will serve the menu choices. In other words, the routine which starts at line llO should attend to the needs of menu item 1, the line which starts at 130 should attend to the needs of menu item 2, and so on. These lines need not be numbered llO, 130, 150, 170 of course. The numbering might have been 7000,600, 150,2010 or whatever you like provided that these were the correct starting lines for the routines. In this example, each 'routine' is just a printed message, and it is followed by an END line. This makes sure that the program ends after each routine. If we had used GOT 0 1 0, we would have made the program return to the menu. As it happens, when we use 0 N K% GOT 0 we normally want the program to end after a choice has been made and carried out. This is very often what is needed. If the program always returns to the menu, then one of the menu choices should be 'End this program.', so that you can stop without having to switch off the machine. If this is the case, though, it's normally preferable to use 0 N K% GO SUB rather than GOT 0, as we'll see.

The listing as it stands is usable, but has noticeable faults. One is that pressing any key other than a number key will cause a fatal error, one that stops the program. The first law of programming should be to avoid any such chance, and we can do this by making the entry to a string variable and then using VA L. The other point is that if you keep entering an incorrect item, the screen scrolls so that eventually you can't see the menu any longer. This can be dealt with by using windows. Figure 6.3 shows the improved menu listing. This time, the GET KEY statement will accept any key, because the input is to a string. This avoids stopping the program just because your fmger slips! In addition, the selection and error message parts have been put into a small window. This allows the menu to remain on the screen, and the window will scroll each time a mistaken entry is made. In this example, the window also shows the chosen item, but in a real working program, the window would be cancelled whenever a correct choice was made.

6-3 READY. 10 SCNCLR:PRINTTA8(19) "MENU" 20 PRINTTAB (2) "1. DAILY TAKINGS." 30 PRINTTAB(2) "2. STOCK SITUATION." 40 PRINTTAB (2) 113. PURCHASES." 50 PRINTTA8(2) "4. SUMMARY. II 60 PRINT:PRINTTAB(l)"PLEASE SELECT BY NU MBER 1-411 :PRINT 62 WINDOW 0,8,39,10ISCNCLR 70 DO:GETKEY K$:KX=VAL(K$):IF KX>0 AND K X<5 THEN EXIT 80 PRI NT-' INCORRECT RANGE- 1· TO 4 ONL Y- P EASE TRY AGAIN II : LOOP 90 SCNCLR:ON KY. GOTO 110,130,150,170 100 WINDOW 0,0,39,24:END 110 PRINT"ROUTINE It1 120 GOTO 100 130 PRINT"ROUTINE 211 140 GOTO 100 150 PRINTIIROUTINE 3 11 160 GOTO 100 170 PRINTIIROUTINE 4" 180 GOTO 100 READY.

An improved menu routine, using a window, string entry, and GO SUB.

FIGURE 6.3

Sectional programming

A lot of programs start out with a tide, some instructions, and then a menu. Depending on what menu choice you have made, some part of the program is run, and the program ends, or returns to the menu again. The 0 N K GOT 0 type of menu selection is useful, but an even better method uses subroutines. The advantage of using a subroutine for this purpose is that it offers an automatic return to the P.oint immediately following the GO SUB. The program of Figure 6.3 can be modified to this type simply by using GOSUB in place of GOTO in line 90, and using in lines 120, 140, 160,180 in place of the GOT 0 100. This is altogether tidier, and is gready preferable to the use of ON K% GOTO, because each subroutine now returns to the same place automatically. This is particularly important when you are making use of windows, because it ensures that the window size is changed when it needs to be after the end of each subroutine.

6-4 Figure 6.4 shows an elaboration on the GET KEY A $ subroutine. The trouble with GET KEY is that it doesn't remind you that it's in use, there's no question mark printed as there is when you use I N PUT. As you may have noticed, there's nothing but a blank piece of screen under the menu. As always, if the program is to be used by you, its maker, and no-one else, this might just be satisfactory, but it's much more pleasing if there is some reminder of what is happening. This is particularly useful if you are likely to leave a program running for a time and then return to it. The subroutine in Figure 6.4 is one that can be added into the menu routine which remedies that problem by causing an asterisk to flash while you are thinking about which key to press. The asterisk is flashed by alternately printing the asterisk and the delete step. C HR $ ( 20) causes the cursor to move one step back, and wipes out the character at the cursor position. To make the rate of flashing reasonably slow, I've put in a delay using a FOR ••• N EXT loop. This subroutine is used by removing the GET KEY and VA L statements from line 70 so that it reads: 70 DO:GOSUB 59000:IF K%>O AND K%<5 THEN EXIT so that the flashing asterisk routine takes the place of the ordinary GET KEY action. Note that the subroutine uses GET, which does not stop and wait for a key to be pressed, so making the loop in the subroutine run until a key is pressed. When a keypress is detected the asterisk will have been erased, so that the routine does not stop with an asterisk still showing. It's a small point, but one that can save a lot of bother. READY. 59000 DO:GET K$; IF K$<>'tI'THEN EXIT 59010 PRINT"*";=FOR J=l TO 300:NEXT 59020 PRINTCHR$(20);:FOR J=l TO 300:NEXT : LOOP 59030 KX=VAL(K$):RETURN READY.

A subroutine which improves on the GET KEY statement by flashing an asterisk.

FIGURE 6.4

A visual menu

The old-style choose-by-number menu is simple to program, and has a lot of merits, but can sometimes involve too many numbers. For example, if you have twenty menu choices, you can't use the simple form of the GET KEY K $ input, because GET KEY allows only one key to be pressed, and you can't put in the number 10 unless you work in hexadecimal and use A! This and other drawbacks of the straightforward number can be overcome by using a visual menu system, which also has the advantage of being in the form of a very adaptable subroutine. Figure 6.S shows an example of this type of menu in action, and we'll look at this listing in detail. Not only is the style of menu different, but

6-5 READY.

10 NR%=7:GOSUB 60000 20 PRINT"CHOICE IS ";J% 30 REM USE IN ON J% GOSUB 40 REM CHECK FOR ANY INCORRECT NUMBER 50 END 60000 SCNCLR:Y%=2:TRAP 60160 60010 DO UNTIL YX)INT«NRX+3)/2):READ D$ 60020 CHAR1,2,YX,D$,I:READ D$ 60030 CHAR1,21,Y%,D$,I:YX=Y%+1:LOOP 60040 FYX=Y%:Y%=2:XX=0 60050 DO UNTIL AX=32:CHAR1,XX,YX,CHR$(62 ) ,0 60060 XX%=X%:YY%=Y% 60070 GET A$:AX=ASC(A$) 60080 IF A%=17 THEN Y%=V%+l 60090 IF A%=145 THEN YX=Y%-l 60100 IF A%=29 THEN X%=20:ELSE IF AX=157 THEN XX=0 60110 IF YX)FY%THEN Y%=2 60120 IF Y%<2 THEN Y%=FYX 60130 CHAR1,XX%,YY%,CHR$(32),0 60140 LOOP:J%=(2*Y%-3)-(X%=20) 60150 TRAP:RETURN 60160 IF ER=13 THEN GOTO 60040:ELSE PRIN TERR$(ER);" IN LINE ";EL:END 60170 DATA TEST,PRINT,RUN,MON,EXAMINE,ST EP,SELECT

READY.

A more modem style of menu which features visual choice.

FIGURE 6.5 the listing introduces a lot of techniques that may be new to you, and which are certainly new to any Commodore user who was brought up with the C64.

The principle in this menu is to place a set of choices on the screen by reading words and phrases from DA T A lines. The words are put into definite positions, and a pointer can be made to move so as to indicate any of the words. This pointer can be moved by using the arrowed cursor keys, and its range of movement is confIned to the set of words/phrases that are displayed on the screen. When the arrow points to the action that you want, you press the space bar to make the subroutine run. The rest is up to you. As it's written, the subroutine is very flexible and easy to alter to your own needs.

6-6 The lines 10 to 50 simply illustrate the ca11ing of the subroutine and the fact that it returns a number, J %, which is the number of the item that has been chosen. This allows you to make a test for an incorrect number, which is possible if the items do not fill each possible position. In the example, there are seven items arranged in two columns, so that there is a blank space at the bottom of the right-hand column. The way that the subroutine has been written, this blank is a valid point to take the arrow to, and will return a nUJ;Ilber. Though this could be avoided by more elaborate programming, it would restrict the ease with which this routine can be adapted, so I have left it that the ultimate testing for the incorrect number can be done by the calling routine. If the items fIll the columns, no test is needed, because the arrow cannot be pointed anywhere but at the rows and columns of the menu items. For that reason, it's often useful to fill in an otherwise blank space with another END PRO G RAM option. The number of items is put into the ca11ing program in the form of the variable N R % in line 10.

The subroutine itself starts in line 60000 by clearing the screen. The assignment of Y% with 2 then ensures a start on line 2 of the screen, and the T RAP statement is put in so as to avoid problems in reading the data. No limit has been put on the number of items in the 0 A T A lines, so that to use different data, you need only type in what you want to use, and alter N R %to suit. It would have been neater to use a FOR ••• N EXT loop with N R% as its limit to read in the data, but I particularly wanted to illustrate the use of the T RAP statement. The main restriction is that the number of characters for each choice should not be too much for a column, about 16 characters is a good maximum to aim for. The reading of items starts in line 60010 with the DO loop, whose limit is specified by a value of Y%, the line number for the last pair of items. As each item is read, it is placed in its correct row and column by the C HA R statements. Each C HA R statement uses the column number of 2 or 21, and the Y% row number, along with the 0 $ for the item that has been read from the 0 A T A lines and which will be placed on the screen. The final 1 in the C HA R statement causes the items to be printed in inverse video, highlighting the choices. Mter each pair of C HA R statements, the Y% row number is incremented in line 60030 and the loop returns to its start.

6·7 This has placed the choices on the screen in inverse video, and the next step is to organise the pointing arrow and the selection. Line 60040 sets F Y% to hold the value of the maximum Y% number, with X %and Y% then arranged at a point to the left of the flrst item. Another loop then starts in line 60050, with the ending condition being the pressing of the space bar, code ASCII 32 .. In this line, the CH A R statement uses CH R$ (62) in place of a variable name so that the right arrowhead, code 62, will be printed at the position determined by the values of X% and Y %. Line 60060 then places these values for position of the arrow into variables XX % and Y Y %, so that the arrow can be deleted from one position when it is moved to another. This deleting action will also make the arrow flash at a fast rate when it is pointing to one item. The GET A $ in line 60070 then detects if a key has been pressed, and the following lines check for the pressing of the cursor keys or the space bar. The principle is that if a vertical cursor movement key has been pressed, the Y % number will be incremented or decremented. For the horizontal movements, the only two acceptable numbers for X% are 0 and 20, and this choice is in line 60100. Lines 60110 and 60120 then check that the value of Y% has not reached an unacceptable number, and wrap the arrow around if this has happened. Line 60130 then prints a blank space at position XX %, Y Y %, the old position of the arrowhead. This deletes the arrow at a former position, and causes the arrow to flash if it has not been moved. The flashing rate can, incidentally, be slowed down by using a delay in the loop, but this will aiso slow down the response of the program to a key being pressed.

When a choice is made by pressing the space bar, the program moves to line 60140, following the L 00 P statement. The expression here works out the value of item number J % from the X% and Y % values. Don't think that the (X %= 20) is a mistake - it's a very important part of the expression. By enclosing this in brackets, it becomes a T RUE / F AL S E expression. If X% is equal to 20, this bracket is T RUE, meaning that it has a value of -1. Now because of the negative sign in front of the bracket, this adds one to the previous value, which will be the odd number 2 * Y %- 3. This will make the number into the next even number, which is the number for the right-hand column. If you want to work with three columns, you will have to use a more elaborate formula than this! When X% is not 20, then the item in brackets is 0, and the number is the odd number for the flrst column. Finally, the T RAP in line 60150 turns off the trapping of errors which was being used in the subroutine, and restores normal operation before the subroutine returns. Line 60160 contains the error trap for error 13, the 0 UT 0 FDA T A error, and restores execution to line 60040 if such an error has occurred. The E L S E statement that follows deals with any other error, and will not be invoked unless you have made a syntax error in typing the subroutine. The remaining line is the DA T A line which contains the menu items, seven in this case.

Try this one out for yourself, and see how it looks. Since it's just a demonstration listing, no attempt has been made to do more than show the number of the item that it returns. You might, for example, want to place the whole display in a window and put whatever the menu choice leads to into another window. You might want to brighten up the whole thing by the use of colour. The advantage of a subroutine like this is that you can bend it to your own will, choosing for yourself what you make it do.

6·8 Drive it yourself

You can get a lot of enjoyment from your Commodore 128 computer when you use it to enter programs from disks that you have bought, or from plug-in cartridges. You can obtain even more enjoyment from typing in programs that you have seen printed in magazines. Even more rewarding is modifying one of these programs so that it behaves in a rather different way, making it do what suits you. The pinnacle of satisfaction, as far as computing is concerned, however, is achieved when you design your own programs. These don't have to be masterpieces. Just to have decided what you want, written it as a program, entered it and made it work is enough. It's 100% your own work, and you'll enjoy it all the more for that. After all, buying a computer and not programming it yourself is like buying a Ferrari and getting someone else to drive it for you - while you pump up the tyres now and again.

Now I can't tell in advance what your interests in programs might be. Some readers might want to design programs that will keep tabs on a stamp collection, a record collection, a set of notes on food preparation or the technical details of vintage steam locomotives. Programs of this type are called 'database' programs, because they need a lot of data items to be typed in and recorded. On the other hand, you might be interested in games, colour patterns, drawings, sound, or other programs that require shapes to move across the screen. These are specialised topics, and what we are going to look at in this section is the database type of program, because it's designed in a way that can be used for all types of programs.

Two points are important here. One is that experience counts in this design business. If you make your first efforts at design as simple as possible, you'll learn much more from them. That's because you're more likely to succeed with a simple program first time round. You'll learn more from designing a simple program that works after a bit of thought and modification, than from an elaborate program that never seems to do what it should. The second point is that program design has to start with the computer switched off, preferably in another room! The reason is that program design needs planning, and you can't plan properly when you have temptation in the shape of a keyboard in front of you. Get away from it!

Put it on paper

We start, then, with a pad of paper. For myself, I use an A4 'student's pad' which is punched so that I can put the sheets into a fIle. This way, I can keep the sheets tidy, and add to them as I need. I can also throwaway any sheets I don't need, which is just as important. Yes, I said sheets! Even a very simple program is probably going to need more than one sheet of paper for its design. If you then go in for more elaborate programs, you may easily fmd yourself with a couple of dozen sheets of planning and of listing before you get to the keyboard. Just to make the exercise more interesting, I'll take an example of a program which needs no menu, and design it as we go. This will be a very simple program, but it will illustrate all the skills that you need.

6-9 Start, then, by writing down what you expect the program to do. You might think that you don't need to do this, because you know what you want, but you'd be surprised. There's an old saying about not being able to see the wood for the trees, and it applies very forcefully to designing programs. If you don't write down what you expect a program to do, it's odds on that the program will never do it! The reason is that you get so involved in details when you start writing the lines of BASIC that it's astonishingly easy to forget what it's all for. If you write it down, you'll have a goal to aim for, and that's as important in program design as it is in life. Don't just dash down a few words. Take some time about it, and consider what you want the program to be able to do. If you don't know, you can't program it! What is even more important is that this action of writing down what you expect a program to do gives you a chance to design a properly structured program. Structured in this sense means that the program is put together in a way that is a logical sequence, so that it is easy to add to, change, or redesign. If you learn to program in this way, your programs will be easy to understand, take less time to get working, and will be easy to extend so that they do more than you intended at first.

As an example, take a look at Figure 6.6. This shows a program outline plan for a simple indexing program. The aim is to be able to type in words and page numbers, edit out any mistakes, and then display or print the list, sorted into alphabetical order. The program plan shows what I expect of the first attempt at this program. It must allow me to enter words, with suitable prompting, and page numbers. It should keep a tally of the number of words, and prevent the array from becoming overfllied. It must then allow me to edit each word or number in -case of mistakes. The lists should then be sorted into alphabetical order, and fmally presented either on the screen or to the printer. Now this is about as much detail as we need, unless we want to make the program more elaborate. For a first effort, this is quite enough. Very few programs emerge like an insect perfect from a chrysalis, and most are evolved from simple beginnings, even if these beginnings were another program, or a subroutine published in a magazine. How do we start the design from this point on?

START REPEAT INPUT item (out if blank item) LOOP BACK REPEAT EDIT list UNTIL DONE SORT list PRINT list END

A simple outline plan for an indexing program.

FIGURE 6.6

The answer is to design the program in the way that an artist paints a picture or an architect designs a house. That means designing the outlines first, and the details later. The outlines of this program are the steps that make up the sequence of actions. We

6-10 shall, for example, want to have a title displayed. Give the user time to read this, and then show instructions. There's little doubt that we shall want to do things like assign variable names, dimension arrays, and other such preparation. All of this forms an 'initialisation section', which is not part of the actual program itself. We then need to enter the words and page numbers. Mter this has been done, the user might want to edit the lists. Mter editing, the lists can be sorted, and then displayed. In a more elaborate effort, we might want to keep the items stored on a disk, either before or after sorting, but we haven't come to disk filing yet. No matter, with a program designed in this way, we can easily add disk storage afterwards.

Foundation stones

Now at last, we can start writing a chunk of program. This will just be a foundation, though. What you must avoid at all costs is filling pages with BASIC lines at this stage. As any builder will tell you, the foundation counts for a lot. Get it right, and you have decided how good the rest of the structure will be. The main thing you have to avoid now is building a wall before the foundation is complete!

Figure 6.7 shows what you should aim for at this stage. There are only eleven lines of program here, and that's as much as you want. This is a foundation, remember, not the Empire State Building. It's also a program that is being developed, so we've hung some 'danger - men at work' signs around. These take the form of the lines that start with REM. REM means REMark, and any line of a program that starts with REM will be ignored by the computer. This means that you can type whatever you like following REM, and the point of it all is to allow you to put notes in with the program. These notes will not be printed on the screen when you are using the program, and you will see them only when you LIS T. In Figure 6.7, all of the lines are REM lines, arranged in pairs. READY. 10 REM ANY INITIALIZING ACTIONS 20 REM 30 REM INPUT OF WORDS AND NUMBERS 40 REM 50 REM EDITING OF LIST 60 REM 70 REM SORTING OF LISTS 80 REM 90 REM DISPLAY OF LISTS 100 REM 110 END

The core program for the index. Note that this program doesn't do anything - yet.

FIGURE 6.7

6-11 The flrst REM line in each pair describes the action that will be required, and the second allows space for aGO SUB that will carry out this action. This way, the REM S act as a guide to the way that the program will be constructed, and will later show where each section begins and ends. Eventually, all of the REM lines can be removed when the program is complete, tested, and working perfectly. REMs are useful, but they make a program take up more space in memory, and run slightly slower. I always like to keep one copy of a program with the R E Ms in place, and another 'working' copy which has no R E Ms. That way I have a fast and effIcient program for everyday use, and a fully-detailed version that I can use if I want to make changes.

Let's get back to the program itself. As you can see, it consists of all these sets of R E Ms which will be replaced by GO SUB instructions that we haven't written yet. That's intentional. What we want at this point, remember, is foundations. The program follows the plan of Figure 6.6 exactly. Take a good long look at this eleven-line piece of program, because it's important. The use of the subroutines will mean that we can check this program easily - there isn't much to go wrong with it. We can now decide in what order we are going to write the subroutines. The wrong order, in practically every example, is the order in which they appear. Always write the title and instructions last, because they are the least important to you at this stage. In any case, if you write them too early, it's odds on that you will have some bright ideas about improving the program soon enough, and you will have to write the instructions all over again. A good idea at this stage is to write'a line such as: 9 GOTO 30 which will cause the program to skip over the title and instructions. This saves a lot of time when you are testing the program, because you don't have the delay of printing the title and instructions each time you run it.

The next step is to get to the keyboard (at last, at last) and enter this core program. If you use the GOT 0 step to skip round the title and instructions temporarily, you can then put in simple P R I NT lines at each subroutine line number.

The next step is to record this core program and then keep adding to the core. If you have the core recorded, then you can load this into your Commodore 128, add one of the subroutines, and then test. When you are satisfled that it works, you can record the whole lot as another disk fIle. Next time you want to add a subroutine, you start with this version, and so on. This way, you keep fIles of a steadily-growing program, with each stage tested and known to work. Again, this is important. Very often, testing takes longer than you expect, and it can be a very tedious job when you have a long program to work with. By testing each subroutine as you go, you know that you can have confldence in the earlier parts of the program, and you can concentrate on errors in the new sections.

Subroutine routines

The next thing we have to do is to design the subroutines. In fact, you don't have to learn anything new to do this. Each subroutine is designed in exactly the same way as we

6·12 designed the core program. That means we have to write down what we expect it to do, and then arrange the steps that will carry out the action. If there's anything that seems to need more thought, we can relegate it to yet another subroutine to be dealt with later.

As an example, take a look at Figure 6.8. This is a plan for the word/number input subroutine, which also includes information that we shall need for the setting-up steps. The plan starts by setting an item number at 1, then follows a loop in which the item number is printed so that you can keep a check on what is happening, then the name and number are input. The item number is then incremented, and the program checks for the limit - the loop will have to be left if this has been reached. If the subroutine does not end in this way, then it is terminated by entering a blank as the name. All of this hints that we shall be using arrays to hold names and numbers. At this point, we might think hard about whether we want to use one array to hold both, or separate arrays. We often want to print an index in a form like byte,38, using a comma to separate the two. If we want to do this, then we have to remember that we certainly can't type the comma if we choose to keep both name and number in one array. This is because the comma is taken to mean that more than one item is being entered. By using separate arrays, we can type the input using a comma, which seems more natural. The other point is that we might eventually want to merge items in which the same name appears with different page numbers. For example, if we have the items: memory,22 memory,58 then it's likely we'll want to combine these in the form: memory,22,58 and this is not so easy if the name and the number are contained in one array item. On balance, then, two arrays seem to be indicated, as we can use NM $ and NR $ for them. Why use a string array to holcj. a number? Simply because we can do more with strings, and since we aren't going to perform any arithmetic with the number there doesn't seem to be much point in keeping it as a number in the array.

START Item No. 1 REPEAT PRINT item number INPUT name, number (same line) INCREMENT item number CHECK: at end? OUT IF END ELSE LOOP BACK RETURN

A plan for the input subroutine. As you can see, a subroutine is planned in exactly the same way as the main program itself.

FIGURE 6.8

6-13 Entry subroutine

Figure 6.9 shows a fIrst attempt at a suitable entry subroutine. Line 1500 deals with some initialisation. The item count number N %is put to zero, and a number MX % to 5. MX % is put in for temporary testing, and represents the maximum number of entries that will be permitted. Later on, we'll put MX %in among the earlier initialising steps, but for the moment it's needed here. The entry loop then starts in line 1510, with the item number being incremented. This is the best place to increment the number, because it means that all the input and testing will be carried out with this same item number. The item number is printed, and line 1520 gets the inputs to the array. Line 1530 then tests for the last entry number having been reached, and prints a suitable message if it has, then leaves the loop. Finally, line 1540 contains the looping condition and the RET URN statement.

READY. 1500 N7.=0:MX7.=5 1510 DO:N7.=N7.+1:PRINTTAB(1) "ITEM ";N7.; 1520 INPUT NM$(N7.),NR$(N%) 1530 IF N%=MX7. THEN PRINT"LAST ITEM":EXI T 1540 LOOP UNTIL NM:$(N7.)="":RETURN READY.

A listing for an entry subroutine.

FIGURE 6.9

This can be typed into place, and tried out by making line 40 of the core program read GO SUB 1 500. It appears to do what we need when we test it, and that's another important point. The testing must check for three things in particular. One is that the entries are as you would expect, another is that the program ends as you expect (a blank entry or reaching maximum entry number). The other thing is that no 'silly' entry causes problems. In this case, entering the space bar by itself has the same effect as pressing the

6·14 The edit subroutine

We can leave sorting and display for the moment, because we already know how to tackle these points, and we'll tum now to the editing routine. The essentials here are to display items, amend them as needed, and ensure that they are corrected in the list. Unfortunately we can't easily capture the excellent screen editing of the Commodore 128 to do this, because that's designed for BASIC only. Perhaps in a Version 3 of this program, we might make our lists by using the AUT 0 1 command of BASIC, and then recording the results on disk, but that's beyond our scope at the moment. What we need must be reasonably simple, but capable of letting us see a reasonable number of entries and correcting the mistakes. READY.

2000 J%=1:SCNCLR:WINDOW0,0,39,10 2010 DO:SCNCLR:FOR X=J% TO J%+9:X%=X 2020 PRINTTAB(2);NM$(X%);",";NR$(X%):NEX T 2030 Y%=0:X%=J%:DO 2040 CHAR1,1,Y%,CHR$(62) ,0 2050 GET K$:K%=ASC(K$):YY%=Y% 2060 IF K%=17 THEN YX=Y%+l:XX=X%+l 2070 IF K%=145 THEN Y%=Y%-1:XX=X%-1 2080 IF Y%)9 THEN Y%=0:XX=JX 2090 IF Y%(0 THEN YX=9:X%=JX+9 2100 CHAR1,1,YY%,CHR$(32),0 2110 LOOP UNTIL Ki.=32 OR KX=13 2120 IF KX=32 THEN 90SUB 2150190T02140 2130 J%=Ji.+10:IF J%)Ni.THEN WINDOW O,0,39 ,24:EXIT 2140 LOOP:RETURN 2150 WINDOW 0, 12,39, 15:SCNCLR 2160 CHAR1,2,1,NM$(X%)+",t/+NR$(X%),1 2170 PRINT: INPUT NM$(XX),NR$(X%) 2180 WINDOW 0,0,39,10:RETURN

READY.

A suitable edit subroutine. This relies on typing a new entry to replace a faulty one.

FIGURE 6.10

The new lines of code are shown in Figure 6.10. We have used the system of visual selection to display and pick out entries ten at a time from the list. By moving the cursor to an incorrect entry and pressing the space bar, an entry can be edited by calling the subroutine at 2150. Pressing the key will fetch the next set of entries, because a set is repeated until all of the corrections have been made. The correction subroutine is a very simple one- displaying the entry and making another input. It's not

6·15 difficult to devise a more elaborate correction system, which copies the characters down and allows a single character to be corrected, but unfortunately such ambitious methods tend to cause problems, not least the fact that the corrected string contains the old characterS plus the delete character (ASCII 20), making the string longer than before.

The next step is display and printing. Display on the screen is straightforward, because it has been done already in the course of editing. You have to decide at this point whether to use a subroutine to serve both purposes, or to write a new one for fmal display. Since we want to allow the possibility of printed (hard-copy) output as well as screen display, we might as well make this a new routine. The choice of printer or screen will have to be made, using a window as shown. Figure 6.11 shows this part of the program. The messages are put into windows at the bottom of the screen, and the listing on the remainder of the screen. One curiosity here was that the first WIN DOW statement in line 3000 was ignored in the program unless preceded by the SeN CL R, though the command GOT 0 3000 from outside the program would activate the WIN DOW statement normally. This may be an oddity of WIN DOW that you will have to watch out for. The only novelty in this routine is that the choice of screen or printer is done by allocating the device number S%, using the standard numbers of 3 for the screen and 4 for a printer. If your printer uses another device number, then you will have to make the substitution here.

READY .. 3000 SCNCLR:WINDOW O,22,39,23 3010 PRINT"PRINTER OR SCREEN (P/S)";:INP UT RP$ 3020 IF RP$= II pOI THEN S'Y.=4:ELSE S'Y.=3 3030 OPEN SX,SX:JY.=l:SCNCLRIWINDOW O,O,3 9,22:00 3040 FOR J=JY. TO JY.+19 3050 PAINT.SY., TAB (2) ; NM$ (J) +" , "+NR$ (J) : N EXT: IF S'Y.::I:4" THEN 3090 3060 WINDOW0,22,39,24,1 3070 PRINT"PRESS ANY KEY TO PROCEED" 3080 GETKEY A$:SCNCLR:WINDOW0,0,39,22,1 3090 JX=J'Y.+20:IF J'Y.>NXTHEN EXIT 3100 LOOPICLOSE S'Y.:RETURN READY.

A display subroutine that offers the choice of screen or printer.

FIGURE 6.11

6-16 Now all that is left is to put in the sort routine, and that's something that we have covered before. Once again, on the basis that you keep it simple uptil you are sure of what you are doing, we'll use the straightforward string sort. We know that if this causes difficulties with garbage collection, we can substitute the more elaborate pointer sort later, because once again, that's a tried and tested subroutine. Figure 6.12 shows the fmallisting for this part of the routine. The only change froll) the earlier eXlPllple is the addition of a line for sorting the N R$ array as well as the NM $ one, in 4055. Once again, a quick test reveals that it works. All the testing so far will have been done with the aid of REA 0 ••• 0 A T A lines, and these will be kept until we can be absohgely sure that the program works perfectly. Once that has been accomplished, we can delete these lines and put in the initialisation that we need in order to make this a useful program. In this case, we'll use an array of 300 entries, enough for the indexing of a small book. Obviously, if your needs require more than this, you can extend the dimensioning, but you will by that time need to use the 'pointer' form of sort routine.

READY.

4000 SCNCLR: CHAR 1 ,9, 12, .. SORT I N.G~ PLEASE WAIT",1:YX=1 4010 DO WHILE YX0 THEN 4040 4070 BEND 4080 NEXT:LOOP 4090 RETURN

READY.

The sorting subroutine, using a conventional Shell-Metzner sort.

FIGURE 6.12

In all of this, you may be surprised to see no elaborate flowcharts or pther planning aids. The reason is that when you plan to make use of s~andard subroutines, the use of flowcharts is unnecessary. Providing that you draw up a pJan of what the program will do and in what sequence, you can build the program in stages as I have demonstrated, knowing that you can rely on each stage which has been used in other prQgrams, and testing each stage, old or new, as you go. This is one very great benefit ofprQgramming in this core and subroutine form, and another is the ease with which you can alter the program to suit yourself. There's a lot, in fact, that you can do to make this program into something more elaborate, or perhaps better suited for your purposes. The reason that I

6·17 have used it as an example is to show what you can design for yourself at this stage. Take this as a sort of BASIC 'construction set' to rebuild any way you like. It will give you some idea of the sense of achievement that you can get from mastering your Commodore 128. As your experience grows, you will then be able to design programs that are very much longer and more elaborate than this one by a long way.

10 REM INITIALIZING ACTIONS 20 MXX=300:DIM NM$(MXX),NR$(MXX) 30 REM INPUT OF WORDS AND NUMBERS 40 BOSUS 1500 50 REM EDITING OF LIST 60 BOSUB 2000 70 REM SORTING OF LISTS 80 BOSUB 4000 90 REM DISPLAY OF LISTS 100 80SUB 3000 110 END 1500 NX=0 1510 DOINX=NX+1IPRINTTAB(1) If ITEM II;NX; 1520 INPUT NM$(NX),NR$(NX) 1530 IF NX=MXX THEN PRINT"LAST ITEM":EXI T

1540 LOOP UNTIL NM$ (NX) =If ": NX=NX-1: RETUR N 2000 JX=1:SCNCLR:WINDOW0,0,39,10 2010 DO:SCNCLR:FOR X=JX TO JX+9:XX=X 2020 PRINTTAB(2);NM$(XX);I,";NR$(XX):NEX T 2030 YY.=0:XX=JX:DO 2040 CHAR1,1,YY.,CHR$(62),0 2050 8ET K$:KX=ASC(K$):YYX=YX 2060 IF KX=17 THEN YX=YX+1.XX=XX+1 2070 IF KX=145 THEN YX=YX-l:XX=XX-l 2080 IF YX>9 THEN VX=0:XX=JX 2090 IF YX<0 THEN YX=9:XX=JX+9 2100 CHAR1,l,VYX,CHR$(32),0 2110 LOOP UNTIL KX=32 OR KX=13 2120 IF KX=32 THEN 80SUB 2150:80T02140 2130 JX=JX+10:IF JX>NXTHEN WINDOW O,0,39 ,24:EXIT 2140 LOOP:RETURN 2150 WINDOW 0,12,39,15:SCNCLR 2160 CHAR1,2,1,NM$(XX)+","+NR$(XX),1 2170 PRINT: INPUT NM$(XX>,NR$(XX) 2180 WINDOW 0,0,39,10:RETURN 2200 END 3000 SCNCLR:WINDOW O,22,39,23

6-18 3010 PRINT"PRINTER OR SCREEN (P/S)U;:INP UT RP$ 3020 IF RP$=lIp uTHEN SY.=4:ELSE SY.=3 3030 OPEN SY.,SY.:JY.=l:SCNCLR:WINDOW O,O,3 9,22:00 3040 FOR J=JY. TO Jy'+19 3050 PRINT#SY.,TAB(2);NM$(J)+","+NR$(J):N EXT.IF Sy'=4 THEN 3090 3060 WINDOW0,22,39,24,1 3070 PRINT"PRESS ANY KEY TO PROCEED" 3080 GETKEY A$:SCNCLR:WINDOW0,0,39,22,1 3090 JY.=JY.+20:IF JY.)NY.THEN EXIT 3100 LOOP:CLOSE SY.:RETURN 4000 SCNCLR:CHAR1,9,12,uSORTING- PLEASE WAIT",1:YY.=1 4010 DO WHILE YY.

The complete indexing program, to show all the parts fitted together.

FIGURE 6.13

6·19

Chapter 7

BASIC filing techniques

What is a file?

The word file means a collection of information which we can record on a disk. Programs in BASIC are one type of fIle, and the only type, incidentally, which permits the use of DLOA D and DS AV E in a straightforward way. In this chapter, I shall use the word fIle in a narrower sense. I'll take it to mean a collection of data that is separate from a program. For example, if you have a program that deals with your household accounts, you would need a fIle of items and money amounts. This fIle is produced as a result of the action of the program, ~d it preserves these amounts for the next time that you use the program. Taking another example, suppose that you devised a program which was intended to keep a note of your collection of vintage 78 rpm recordings. The program would require you to enter lots of information about these recordings. This information is a me, and at some stage in the program, you would have to record this fIle. Why? Because when you load a BASIC program and RUN it, it starts from scratch. All the information that you fed into it the last time you used it has gone - unless you recorded that information separately. This is the topic that we're dealing with in this chapter, recording the information that a program uses, or filing the information.

Knowing the names

You can't discuss filing without coming across some words which are always used in connection with filing. The most important of these words are record and field. A record is a set of facts about one item in the fIle. For example, if you have a me about vintage steam locomotives, one of your records might be used for each locomotive type. Within that record, you might have wheel formation, designers name, fIrebox area, working steam pressure, tractive force and anything else that's relevant. Each of these items is a fIeld, an item of the group that makes up a record. Your record might, for example, be the SCOTT class 4-4-0 locomotives. Every different bit of information about the SCOTT class is a fIeld, the whole set of fIelds is a record, and the SCOTT class is just one record in a fIle that will include the Gresley PacifIes, the 4-6-0 general purpose locos, and so on. Take another example, the fIle 'British Motor-bikes'. In this fIle, BSA is one

7-1 record, AJS is another, Norton is another. In each record, you will have fields. These might each allow you to record capacity, number of cylinders, bore and stroke, suspension, top speed, acceleration and whatever else you want to take note of. Filing is fun - if you like arranging things in the right order. It need not, of course, be as elaborate as I have suggested. The indexing program of the previous chapter could have used a disk me simply to store the list of words either before or after sorting, and this would, with some modifications, allow the input of words to be interrupted rather than having to be done in one long session.

Disk filing

In this book, because we are dealing with the Commodore 128 which is normally sold along with a disk system, we'll ignore filing methods that are based on DA T A lines in a BASIC program, or on the use of cassettes. To start with, there are three types of mes that we can use with a disk system, serial (or sequential) mes, relative mes and random access meso The differences are simple, but important ones.

A serial me places all the information on a disk in the order in which the information is received, just as it would be placed on a cassette. If you want to get at one item, you have to read all of the items from the beginning of the me into the computer, and then select. There is no way in which you can command the system to read just one record or one field. More important, you can't change any part of a record unless you read the me, alter the record, and record the new version of the me.

A relative me is a kind that applies only to the Commodore disk system. A relative me is stored like a serial me, but each entry into the me creates a kind of directory entry (though not on the directory track of the disk). This allows any part of the me to be found much more quickly that would be possible if the me were just an ordinary serial type. In addition, it allows records to be replaced, and more records to be added.

A random access me allows you to get one selected record or field from the disk without reading every other one from the start of the file. You might imagine that, faced with this choice, no-one would want to use anything but random access meso It's not as simple as that, though, because the convenience of random access ming has to be paid for by a lot more complication. For one thing, because random access filing allows you to write data at any part of the disk, it would be very easy to wipe out valuable data, or even the directory, with a program that was badly designed.

Serial files

We'll start by supposing that we have a file to record, called CAM ERA S. On this me we have records (such as Nikon, Pentax, Canon, Yashica and so on). For each record we have fields like mm size, shutter speed range, aperture range, manual or automatic, and

7-2 so on. How do we write these records? First of all, we need to arrange the program that has created the records so that it can output them in some order. The usual order will be to take the records in some chosen order, and output the fields of the record in that same order on to disk, but how do we actually put it on the disk? There are several stages, and the first one is to allocate a 'channel number'. This is a code that the machine will use to distinguish fIles from each other. The Commodore 128 can deal with up to five serial fIles at one time. It's most unlikely that you will ever want to use more than two serial fIles at a time (probably one for reading and one for writing, for example), but it's better to be generous than not. Each time you want to make use of a fIle, then, you must have a channel number (or 'handle') allocated. This is done with the DO PEN statement which is followed by a hashmark, then the channel number. You can allocate a channel number of anything from 1 to 255, and unless you have special reasons for doing otherwise, it's wise to keep to the numbers 1 and 2, or 128 and 129. The difference here is that numbers below 128 send a carriage return character to the disk after each item is saved, but this can be suppressed by using a semicolon following the P R I NT # statement used to save data. If you use channel numbers of 128 or more, then both a carriage return and a line feed will be sent ..

The purpose of this channel number is to organise data. The disk stores all data in units of 256 bytes (actually a few bytes less, but that's not important at present). It wouldn't make sense to spin the disk and fmd a place on the disk just to record one byte at a time, so when you record or read a"disk, it's always one complete sector at a time. Some of the memory of the Commodore 128 has to be used to hold data which is being gathered up for recording, or which is being replayed. The channel number is a identifying number for the piece of memory that is being used, so that the machine fmds the correct data in the correct part of the memory. Using channel numbers like this avoids the need for you to have to allocate parts of the memory to use in this way as buffers.

Opening the fde

Mter that short diversion, back to our fIling program, and to more details of the DO PEN statement. Following the channel number there must be a comma, and then the fIlename The name can be like any other fIlename, up to 16 characters. This can then be followed by various specifier letters, of which the important one at the moment is W. By using only the channel number and a fIlename, you are specifying reading a sequential fIle. By using a W in addition, you specify that you want to write to a sequential fIle. The complete DO PEN statement, then, might look as follows: DOPEN #1,"AIRCRAFT",W to write a fIle called A I R C RAFT. If needed, you can use D1 to specify drive 1 - remember that if you are using the 1570 disk system, you must use DO here. When you read the directory of the disk, the fIle name will be shown, along with the typename of SEQ (sequential) so that you know that you can't read the fIle using DLOA D. If you open another fIle for reading, it will be allocated another channel number, so that the fIles can be kept separate. To avoid confusing yourself, though, try to keep as few fIles on the go at anyone time as possible.

7-3 The use of the DO PEN command opens a flle - which means that we can make use of the flle. It also means that the disk is prepared for the flle. Any flle that exists on the disk already and has the same name of A I R CR AFT will prevent you from opening this flle, however. To get round that problem, you can modify the name of the flle so that it will automatically delete any flle of the same name. This is done in the usual way by adding the '@' mark just ahead of the colon or the title. In addition to recording the fllename, sectors will be reserved for the flle.

Printing to the file

It's at this stage that we need to make use of the loops in the writing program. Within these loops, we need to have a line something like: 1100 PRINT#1,FDS(N) P R I NT # 1 means put the information out on channell. This, because of our previous DO PEN statement leads to the disk flle that is to be written, so that P R I N T # 1 will eventually put out to the disk system the data that follows. In this example, it's F DS ( N ) . N is the number in the FOR ••• N EXT loop, so that as the loop goes round, we will put on to the disk f ; e l d ( 1 ) , then fie l d ( 2 ) , then fie l d ( 3) and so on. We also need to write the record name, and this is done within the loop, by using a line such as: 1070 PRINT#1,RCS without using an array (because of the unknown amount of dimensioning).

Figure 7.1 shows an example of a very short and simple program of this type which follows these guidelines. You can enter anything you like into this, but it makes more sense to enter something that you can easily check. Since the flle is called AI R CR AFT, you could make each record name the name of an aircraft type, and each field some feature of the aircraft, like wingspan, engine details, number of crew, and so on. You can, of course, easily change this program so that it has another title that suits the information that you might want to use. Before we move on, though, consider what this has done. It has created a flle called A I R CR AFT, and allocated a channel number of 1 to this flle. It has then stored the data as it came along, in the sequence of R E COR D, then FIE L DS. Finally, the flle has been recorded and closed by using D C LOS E. This last step is very important. For one thing, you don't actually record on the disk any of the information in this short program until the DC LO S E statement is executed. That's because it would be a very time-consuming business to record each item of a flle at a time. What the DFS does, remember, is to gather the data together in memory. This is a buffer, and it will be written to the disk only under one of two possible circumstances. One is that the buffer is full, so that there is one sector full of data (256 bytes) to write. The other is that there is a D C LOS E type of statement in the program. For a large amount of data, the disk will spin and write data each time the buffer is full. The DC LOS E command then writes the last piece of data, the one which doesn't fill the buffer. It also writes a special code number, called the end-of-flle code (EOF). This can be used when the fIle is read, as we'll see later. If you forget the D CLOS E statement, you'll leave the buffer unwritten, with no EOF - and cause a lot of problems in your

7·4 READY. 1000 DOPEN#1,u@AIRCRAFTu,W 1010 DIM FD(5):XX=0 1020 SCNCLR:WINDOW O,22,39,23,1 1030 PRINT"TYPE X TO END ENTRY" 1040 WINDOW 0,0,39,21:DO 1050 INPUT"RECORD NAME ";RC$:IF RC$="X u THEN EXIT 1060 XX=XX+1 1070 PRINT#1,RC$ 1080 FOR N=1 TO 5 1090 PRINTuFIELD ITEM ";N;" u;:INPUT FD$ (N) 1100 PRINT#1,FD$(N):NEXT:LOOP 1110 PRINTuTHERE ARE ";XX;u RECORDS ON T HE FILEu 1120 DCLOSE:WINDOW O,O,39,24,1 READY.

A simple serial me writing program which writes a record with five fields.

FIGURE 7.1 programs. Forgetting the 0 C LOS E is called leaving your fIles open. The biggest danger is when you are testing a program. If there is an error, such as a syntax error, which stops the program from running, there will be no 0 C LOS E carried out, and the fIles will be left open. If you had typed a lot of data, you would lose it if you then went on to correct the program and run it again. The correct procedure is to close all of the open channels. In this example, it's easy-you only have to type 0 C LOS E and press

Getting your own back

Having created a fIle on disk, we need to prove that it has actually happened by reading the fIle back. A program which reads a fIle must contain, early on, a command which opens the fIle for reading. This is another DO PEN, and it makes sense to use another channel and fIle number, just in case you want to carry out both reading and writing in

7-5 quick succession. The name of the ftle must, of course, be the same. If we recorded a ftle using the name AIRe RAFT, then we must not expect to be able to read it if we use CAM ERA S - or any other name. Mis-spelling can haunt you here! Once the channel number has been allocated, we can read data with I N PUT #, which will be followed by the ftle number. This reads an item from the disk and allocates it to a permanent variable, or just prints the item, according to what we have programmed. The number of reads can be controlled by a FOR ••• N EXT loop, if the number is known, or it can make use of the EOF marker if the number is unknown. The operating system of the Commodore 128 uses a variable S T which changes from 0 when an end of ftle is found. By testing for S T changing value, then, we can make the program stop reading the ftle at the correct place. The example of Figure 7.2 shows both methods in use. The number of fields has been five, so that a FOR. • • N EXT loop can be used to control the input of the fields. The number of records, however, has not been defmed by a FOR ••• N EXT loop, so that we have to keep reading the ftle until the EOF byte is found. This is done in line 2040 by testing ST. If S T is not zero, then the ftle is closed, and the program ends. Note that the disk does not spin each time you press a key to get another record. This is because a complete sector is read into the buffer each time, and if the information that you want is all on the same sector, the disk need not be accessed.

READY. 2000 DIM FX$(5).X$=I>##" 20111 DOPENI:2, II AI RCRAFT II 2020 SCNCLR 211311 DO:INPUTI:2,NM$ 20411 IF ST<>0 THEN EXIT 211511 PRINT"TYPE IS "gNM$ 2060 FOR N=1 TO 5 2070 INPUT#2,FX$(N):NEXT 20811 PRINT"WINGSPAN ";=PRINTUSING X$;FX $(1) 2090 PRINT"LENGTH I.; I PRINTUSING X$; FX $(2) 2100 PRINT"CREW NO. ";=PRINTUSING X$;FX $(3) 2110 PRINT"ENGINECS) ";:PRINTUSING X$;FX $(4) 2120 PRINT"RANGE ";:PRINTUSING X$;FX $(5) 2130 WINDOW 0,22,39,23,1 2140 PRINTIIPRESS SPACEBAR TO CONTINUE" 2150 GETKEY A$:WINDOW 0,0,39,22,1 21611 LOOP:DCLOSE READY.

Reading back a serial file, in this case the file that was created by the program of Figure 7.1.

FJGURE7.2 7-6 Now this simple example shows a lot about serial filing that you need to know. When you use disks, then the name that is used with DO PEN is the me name for the me on the disk. Any other me that is later recorded with the same name will not overwrite this me, so the system provides for good me security. You have to be careful to watch for the winking light on the disk drive, however, because you might otherwise think that you have recorded a me when you have not in fact done so. This can be made more automatic by reading the disk error number which is available as the variable DS. If the me already exists, this variable will contain the number 63. If all is well, the number will be o. By testing this after opening for a write, you can print your own error message, and make sure that nothing else is done until the name for the new me is changed, or other suitable steps, like renaming or removing the existing me are taken. How, then, can you update a me, particularly if you want to add more items to the end of the me?

Updating the file

There are two answers, if we stick to serial mingo One possibility is to load the whole me into the memory of the computer, make the alterations (your BASIC program will have to be written to provide for this), and then write the me again, wiping out the earlier version. The other possibility for extending the me is much better, and it hinges on the use of the A P PEN D statement. A P PEN D is followed by a channel number (with hashmark) and the me name, rather like aDO PEN statement. There is, however, no W added following the mename, and there should be no @ sign placed before the mename. The effect of A P PEN D is to open the sequential me, but at the end of the file, deleting the EOF character. Whatever you type then will form a continuation of the me on the disk, so that extending becomes delightfully easy, as Figure 7.3 shows. READY. 1000 APPEND#l,"AIRCRAFT" 1010 DIM FD(5):XX=0 1020 SCNCLR:WINDOW 0,22,39,23,1 1030 PRINT"TYPE X TO END ENTRY" 1040 WINDOW 0,0,39,21:00 1050 INPUT"RECORD NAME ";RC$:IF RC$="X" THEN EXIT 1060 X'X=X'X+l 1070 PRINT#l,RC$ 1080 FOR N=l TO 5 1090 PRINT"FIELD ITEM u;N;" ";: INPUT FD$ (N) 1100 PRINT#l,FD$(N):NEXT:LOOP 1110 PRINT"THERE WERE ";XX;U RECORDS ADD ED TO THE FILE" 1120 DCLOSE:WINDOW 0,O,39,24,1 READY. A routine for appending more data to an existing serial me.

FIGURE 7.3 7·7 READY. 3000 DIM FX$(5) 3010 DOPEN#3,"AIRCRAFT":DOPEN#4,"@MORECR AFT",W 3020 SCNCLR:FOR N=l TO 5:READ LB$(N):NEX T 3030 DO:SCNCLR:INPUT#3,NM$ 3040 IF ST<>0 THEN EXIT 3050 PRINT:PRINTTAB(5);NM$ 3060 FOR N=1 TO 5 3070 INPUT#3,FX$(N) 3090 PRINTTAB(2);LB$(N);TAB(12);FX$(N) 3100 NEXT:FX$(0)=NM$:GOSUB 61000:NM$=FX$ (0) 3110 PRINT#4,NM$:FOR N=1 TO 5 3120 PRINT#4,FX$(N):NEXT 3130 LOOP:DCLOSE 3140 END 3200 DATA WINGSPAN, LENGTH, CREW NO.,ENGIN E(S),RANGE 61000 XY.=0:YY.=1:J7.=0:DO 61010 CHAR1,XY.,Y7.,CHR$(62) 61020 GET K$:K7.=ASC(K$):YYY.=Y% 61030 IF K%=13 THEN RETURN 61040 IF K%=17 THEN YY.=Y%+1:JY.=JY.+1 61050 IF K%=145 THEN Y7.=YX-1:J7.=J7.-1 61060 IF Y7.>6 THEN YX=1:JX=0 61070 IF YX<1 THEN Y7.=6:J7.=5 61080 CHAR1,X7.,YY%,CHR$(32) 61090 LOOP UNTIL K7.=32:WINDOW 0,15,39,17 61100 I NPUT "CORRECT I ON - "; FX$(JX) 61110 WINDOW0,0,39,24:RETURN READY.

items and then Changing items in a serial me. This involves reading the me into memory, changing and the A IRe RAFT rewriting the me. In this example, the rewriting is under a different mename, me would be scratched so that the M 0R E eRA F T me could be renamed.

FIGUItE 7.4

as distinct This, however, does not deal with the problem of altering an item in the fIle, fIles, one for from extending the fIle. The method that is needed here is to open two drives for this, reading and the other for writing. You don't need to have dual disk will maintain though it makes life much simpler if you do. This means that the computer want, display two buffers. You read one record from the reading fIle and you can, if you record has to be it. If it's all right, it's then written (to the write buffer, initially). If the

7-8 modified, you can do so, using a subroutine. If extra records have to be added, this is equally simple. Each time a buffer empties, the disk will spin and a read or write will take place. This simultaneous operation is possible because of the use of different channel numbers, which control different buffers. In practice, it's a matter of writing your program to suit. Figure 7.4 shows a simple program which allows you to change the items of the fIle that was created by the program of Figure 7.3. Note, however, that the fIles use different names, because I have assumed that both fIles will be on the same disk. We must therefore end the program by deleting the old fIle and changing the name of the newly created fIle (the extended or changed fIle) so that it has the same name as the old fIle. This can be done from BASIC by using the S eRA T CHand R E NA ME commands just as we would use them direct from the keyboard. One point we have to be very careful about, however, is closing fIles. The S eRA T CHand R E NA ME statements will not work if the fIles are open.

In the listing of Figure 7.4, one fIle is opened for reading on channel 3, and the other for writing using channel 4. The second fIle has been called @M 0 R E eRA F T to ensure that it will always be written to, and will have a different name from the A IRe RAFT fIle which is being read. The fIle is read in the usual way, and when each group of items is displyed, you can make an edit choice by moving the cursor and pressing the space bar, or move to the next record by pressing the key. This uses a version of the subroutine which should be familiar by this time. The corrected, or unaltered record is then written to the other fIle, and if the fIles are small, nothing happens until the DeL 0 S E in line 3130 is executed. The S eRA T CHand RE NA ME statements have not been used in this simple example, but they are not difficult to add following the DeL 0 S E statement. Remember also that you can use the CON CAT command or statement to add one fIle to another on the disk itself without reading either fIle into the computer.

7·9 Relative files

Serial flIes are very useful for a lot of purposes, but not if you want to be able to select a single record out of a very large number. With a long program in the memory of the Commodore 128, there would not be much memory left to store a great number of records at a time. This means that you would have to read in your records in short batches, test each one to fmd if it was the one you wanted, and then read another lot in if you couldn't fmd the one that you wanted. This could take a long time, and it would be much more satisfactory if you could pinpoint the record that you wanted, and just read that one (and possibly some others) direct from the disk. This implies some form of random access, which is provided on the Commodore 128 in the form of relative flIes which have a lot of the advantages of truly random flIing with few of the disadvantages. We'll spend the rest of this chapter, then, looking at relative flIes and how to use them. In the next chapter, I'll give an example of a much longer program which makes use of relative flIes to achieve a random access database. This will be a program that allows you to flIe facts on the disk and get them back whenever you want them.

Relative file facts

A relative flIe has to be made up from a number of records, like any other flIe. One very important distinguishing feature, however, is that the record length is fIxed, and this record length, the number of characters in each record, must be included as part of the DO PEN statement. With each and every record in a flIe being of the same length, it's comparatively easy for the disk system to locate a particular record and load it into a buffer. It's equally easy to go through the flIe character by character, so that you can place your fIelds at specifIc parts of the record knowing that they can be read by specifying a position number. This implies that each fIeld should have a fIxed maximum length and you have to choose for yourself how many characters will be needed in each fIeld. You may, for example, feel that a fIeld of 15 characters might be enough to store any name of a country for a flIe on world geography. You would then have to make sure that no name that you entered contained more than 15 characters (by a statement such as LE FT$ (NM$, 15) for example). This might mean that a country name like Papua New Guinea ends up shortened to Papua New Guine, but this would still be enough to recognise the country. When you have settled on a length number for a fIeld in your record, every one of these fIelds must be of that length. It doesn't matter if a name doesn't fIll a fIeld, because the computer will automatically fIll up the fIeld to the number of characters that you have chosen, using blanks which don't appear when the name is printed. When you have several fIelds in each record, as you normally do, then you don't have to use the same number of characters per fIeld. It's convenient, as you will see, if you can, but it saves a lot of splice on the disk if you keep each fIeld to the most useful size. For telephone numbers or post codes, for example, it would be very wasteful to use fIelds of 25 characters.

The most important feature of relative flIing is that the disk operating system keeps a track of the flIes for you. This is done on the Commodore 128 with one simple but

7-10 important statement word, R E COR D. R E COR 0 is followed by a channel number, such as # 2, then by a record number and a character number separated with commas. In this way, you can locate both a record and a field in that record and use an I N PUT # 2 or P R I NT # 2 to read or to write that field. All of this was also possible with the old 1541 disk system on the e64, but in a much more roundabout way.

Relative rules

On any computer, there is always a price to be paid for convenience, and in this case, the price is that you have to construct your filing program according to a set of rules. We have seen that you need to fix the total size of each record, and this will mean fixing the number of characters per field. You have to a special form of DO PEN command for the fIle, and you must use aRE COR 0 statement to fmd the correct record and the correct position in the record to read each field. Following that, you can use P R I NT # to write a field or I NPUT # to read a field. You must leave space in each field for a carriage return character, so that if you are working with 20-character fields, you should allow for 21 characters . One important difference between serial fIles and relative fIles is that a relative fIle is not protected in the way that programs or serial mes are protected. If you have a fIle called MY 0 A T A opened and with 20 items recorded, then it is possible to replace these 20 items with a different 20, to alter one of the 20, or to add more items. Your program then, must be constructed so that you will not accidentally zap out a me of data. There is nothing in the disk operating system that would prevent you from doing this, though you will get an error message if you try to print to a new me for the first time! READY. 100 OOPEN#1,IDATA",L111 110 NTr.=1:Nr.=1:DO:SCNCLR:RESTORE 120 FOR J=1 TO 5:READ TX$:PRINTTX$;" "; 130 INPUT NM$:NM$=LEFT$(NM$,20) 140 IF NM$="X" THEN EXIT 150 RECORD#1,NTX,Nr.:IF OS>20 AND DS<>50 THEN PRINTDS$:STOP 160 PRINT#1,NM$ 170 Nr.=Nr.+22:NEXT:NTr.=NTr.+1:Nr.=1:LOOP 180 DCLOSE:END 190 DATA NAME,ADDRESS 1,ADDRESS 2,ADDRES S 3,ADORESS 4

READY.

A routine that creates a relative file - in this case for name and address data.

FIGURE 7.5

7-11 It's time, now, to look at some examples. Figure 7.5 shows a simple relative me of name and address data being created. The relative me is created whenever the correct 00 PEN instruction is used. The 00 PEN statement follows the same pattern as for a serial me as far as channel number and mename is concerned, but this is then followed by L 111 . The L means length, and by specifying this number, we allow for a record length of 111 bytes. This is one more than is strictly needed, but it's always better to err slighty on the generous side. When the fields are created, you must add one character to allow for the carriage return, and another to allow for a space between fields, so that the size of the record is:

Sum of the field sizes + 2 x (number of fields)

This number must not exceed 255. It's because of this fixed length feature that the disk system can fmd each record so easily. In this example, we shall make each field of data of up to 20 character maximum, and allow 22 characters in the recorded fields so that the carriage return and the space can be correctly placed.

It looks reasonably straightforward, but there's one slight problem. Because of the way that the disk operating system works, attempting to write data to a me like this will cause an error message! The message is R E COR 0 NOT PRE 5 EN T, error number SO, and it appears because the 00 PEN command for a relative me allows both reading and writing. When you open the me for the first time and write to it, nothing has yet been recorded on the me, so the error message is technically correct - there is no record present. You don't want to let this error message stop the program, however, so you must arrange to test the error number (variable 05) each time you write to the me. If the error is number SO, the R E COR 0 NOT PRE 5 EN Terror, you can then ignore it and carry on. If there is any other error, though, you will have to print the error and stop. All of this is done in line 150. The reason for the test condition 05> 2 0 is that if you do not include this, you will get the message printed for any satisfactory disk operation, because in such a case 05=0.

7-12 Since this is not a serial me, we have to pass to the disk operating system some more information. We must, in particular, pass the record number. If the record number is 1, then the disk will write the record on the first available piece of track associated with the me. If the number is 20, the disk will have to count out 19 records, which is 19 times the number of characters per record, to fmd space. The point about this is that you can write record number 1, and then record number 20, without having written numbers 2 to 19 between them. When you do write these other records, there is space left for them. This is another important difference between relative mes and serial (sequential) meso This information on the record has to be passed on to the disk system using the R E COR 0 statement. As you can see from the listing, this requires the channel number, the record number NT %and the field position number N %to be supplied. This allows you to write individual fields of a record, not just the whole record, as is illustrated here. The field byte number must not start at 0, and its maximum value is 255. The record number must also never be zero, and its maximum is 65535, which allows for a generous number of records.

The program starts by opening the channel for a relative me - the statement is the same whether the me is to be read or written. The name that will be used for the me is 0 AT A, and the record length will be 111 characters. The position in the record is represented by the variable N%, and the record number is NT %, both of these start with a value of 1. In line 110, the main loop starts, the screen is cleared, and the 0 A T A list restored. If you wanted to use this routine to write any numbered record you would need a subroutine which allowed you to input a number here to use as the record number.

Another loop starts in line 120. The planning of this program has allowed for five fields in each record, so each field has to be entered and recorded. In line 120, the name of the field is read from the 0 A T A line, and this name is printed. We are using, for the sake of simplicity, twenty characters per field, and when you type the data NM $, each entry is chopped down to this size, if it is larger, by the L EFT $ routine. Line 140 allows you to leave the main loop by entering X for a name .

. Line 150 is important. This is the command to the disk system which places the pointer, and will ensure that the whole record goes in the correct place. The R E COR 0 # 1 has to be followed by the record number NT %and then by the field position N %. If you are not interested in recording individual fields, you can simply print to the record. This is the technique that you would use, for example, if you were recording a couple of strings in each record. The error variable 0 S then has to be checked to make sure that the error number 50 does not cause problems. If the error is any other valid type, then the program will print the error message and stop. Line 160 then prints the value of NM$ into the correct position in the buffer. It doesn't go to the disk, remember, until enough has been gathered. The disk is checked for errors, and the N% = N % + 2 2 advances the position of the pointer in the same record. We have to use 22, because the number of characters is 20, and we have to add one for the carriage return and one for a space. The loop then repeats, so that each field is input, and written to the buffer in the correct field position. The record number, NT %is then incremented, the byte position number N %is reset to 1, and the program loops back again. This allows you to input names and addresses until the disk is full, or you reach 65535 records, whichever happens first! The

7·13 entry is stopped by entering as a name the letter X. You could choose any other character that you liked to terminate entry.

When entry is terminated, line 180 closes the channel and ends the program. Now each time that you use this program, it will create a me called DA T A, and it will start from scratch. This may not be what you want, and you can provide for adding to the me very easily. If you know, for example, that you already have the me DA T A created, with 40 entries, then if you make NT %= 41 in line 110, you can continue adding to the me. The only change that this needs to the program is a subroutine. This should ask DO YOU WAN T TOE X TEN D THE F I L E, and if you do, ask for the record number which will be assigned to NT %, and then you return to the program. There are no safeguards here to ensure that you don't start at the wrong place, but that's a matter of how you want to construct your program. One good tip is to keep the current number of entries as the fIrst record in the me.

Reading back

Having created a relative me, we now have to look at the problem of reading it back. Once you have been through the process of writing, reading looks a lot simpler. For one thing, the me now exists on the disk, and opening the me to read it is a simpler operation. Figure 7.6 illustrates the processes for reading the me. Line 200 opens th~ channel, and line 210 asks you for a record number. This is assigned to variable N1.% as before, and we can make use of the fact that there cannot be a record number 0 to end the loop. The screen is cleared and the position in the record, variable N%, is set to 1. The loop which gets fIve fIelds then starts in line 240. Line 250 fmds the correct position in the buffer as before, using NT %and N% , .and the I NPUT # 2 statement carries out the reading action. If, incidentally, you do not have the me DA T A on the disk, you will get the error message S T R I NG TOO LON GIN 2 50. This is not what you might expect, and it can be very frustrating until you realise what it really means. The disk system has

7·14 200 DOPEN#2,"DATA",Llll 210 DO: INPUT"WHICH RECORD ";.NTX 220 IF NTX=0 THEN EXIT 230 SCNCLR:NX=l 240 FOR J=l TO 5 250 RECORD#2,NT7.,N7.:INPUT#2,NM$ 260 PRINTNM$:N7.=N7.+22:NEXT:LOOP 270 DCLOSE:END READY.

Reading back the relative fIle that was created by the previous listing.

FIGURE 7.6 found no fIle of this name to read, and has carried on, then read garbage from the disk, looking for an end of fIle character. The F I LED 0 E S NOT E X 1ST error is not detected on the 1541 drive when you are reading! If all is well, the title and the field item is then printed, and the loop continues to read in all of the fields.

In general, the routine is pretty much the same as before, and the main difference now is that no error detection has been included. There is nothing in the program which prevents you from asking for an impossible record number. If the number that you pick for the record number is impossible - higher than the largest record number on the disk - then the error number 50, R E COR 0 NOT PRE SEN T, will be signalled. This can simply be used to print a message and then run again! Once again, it's useful to carry the number of records as the first record on the fIle.

More complications

These examples work nicely, but they are only illustrations for learning purposes. For one thing, it's not very satisfactory that the maximum record number is not placed on the disk fIle. For another thing, you might not be able to remember record numbers. If you want to fmd a name that starts with SIN, for example, this doesn't help you know which record number it is. It would be a lot more useful if you could search the fIle for a given name rather than for a given record number.

This is not something that you can do rapidly with relative fIles unless they are sorted into alphabetical order. You would have to load in each record, test it, and keep repeating this until you found the one that you wanted. The manual hints that this can be done by keeping a list of names and record numbers on another fIle. It suggests a serial fIle for the purpose. This is fme if the fIle has a fixed length, but if you want to extend the main fIle, it's a bit of a pain trying to extend the serial fIle as well. We'll look at ways round these problems in the next chapter, which consists of a database program that uses relative and serial fIles.

7-15

Chapter 8

A database program - FILIT

This chapter consists mainly of one long listing (Figure 8.1) for a database type of program which demonstrates disk filing methods in action. The program is called 'FILIT', and it allows you to specify five titles for the fields of your records. These field names, and their lengths, are recorded on the disk, and will be used from then on. You can then enter information, add to information, read the data or select items as you please. These are the normal actions of a very simple database. Looking at the length of the program, you might wonder how long a complicated program would be, but this is a simple version. There is no facility, for example, for changing a single record, other than by altering all records. There is no facility for printing records in alphabetical order of any field. This is, you see, a skeleton database, which has been produced to illustrate the use of the two types of files for this type of program. Once you have this program up and running, and have completed reading this book, you should be able to add whatever extra trimmings you need. READY. 1121 SCNCLR 2121 Fl'X=12I 3121 COLORI2I,1:COLOR4,1:COLOR1,2 40 REM WHITE CHARACTERS, BLACK SCREEN 50 REM OPEN KEY FILE 60 DOPENtl:7,"KEY",L5 70 DCLOSEtl:7 80 X$="NO SUCH FILENAME" 90 TT$="FILIT":GOSUB230 100 WINDOW0,22,39,23:PRINT"DO YOU NEED I NSTRUCTIONS

8-1 180 DO:WINDOW 0,22,39,23,1:GETKEY KY$:VX =VALCKY$) 190 IF VX>0 AND VX<5 THEN EXIT:ELSE PRIN Till TO 4 ONLY, PLEASE II :LOOP:WINDOW0,0,39 ,24 200 ON VXGOSUB330,520,770,210 210 LOOP UNTIL VX=4 220 SCNCLR: PRINT: PRINTTAB (13) ; IIEND OF PR OGRAMII:DCLOSE:END 230 DX=(40-LEN(TT$»/2 240 PRINTTAB(DX)CHR$(158);TT$ 250 RETURN 260 SCNCLR:TT$=IIINSTRUCTIONS II :GOSUB230:P RINT:PRINT 270 PRINTIIREAD TEXT, THEN WRITE YOUR OWN I II 280 PRINTIIPRESS SPACEBAR TO CONTINUE II 290 GETKEY KY$:RETURN 300 Fl'Y.=l:PRINT:INPUT"FILE NAME, PLEASE ";FM$:GOSUB700 310 IF DS=62 OR DS=70 THEN PRINT X$:SLEE P3: F1'Y.=0: RETURN 320 RETURN 330 DO:SCNCLR:TT$=ItCREATE NEW FILE":GOSU 8230 340 PRINT=PRINT"PLEASE ENTER DATA AS REQ UESTED II 350 PRINT:PRINT" PRESS SPACEBAR TO START ..... :GETKEY KY$:PRINT 360 TLX=2:FORJ=lT05 370 PRINTIITITLE II;J;" II: INPUT TL$(J):PRI NTIIFIELD LENGTH- II;: INPUT LNX(J) 380 TLX=TLX+LNX(J)+2:IF TLX<255 THEN NEX T:EXIT 390 IF TLX>=255 THEN BEGIN 400 PRINTIITOO LONG- PLEASE TRY SHORTER F IELDS": BEND: LOOP 410 PRINT:PRINTIIWHAT FILENAME WOULD YOU LIKE":PRINT 420 INPUT FM$:FM$=LEFT$(FM$,16) 430 PRINT: PRINT"PLEASE WAIT FOR FILE PRE PARATION" 440 DOPENtt6,"I!TITLES II ,W 450 PRINT#6,TLX:FORJ=lT05 460 PRINT#6,TL$(J) IPRINT#6,LNX(J) INEXT:D CLOSE#6 470 DOPEN#5,1I0: II +FM$+",L,II+CHR$(TLX) 480 RECORD#5,255,1

8-2 490 PRINT*5,IIENDII:GOSUB1380:REM MAKE SPA CE FOR 256 500 DCLOSE 510 VX=0=FIX=1:RETURN 520 SCNCLR:TT$="WRITE TO FILE II :GOSUB23CZh GOSUB300 =IF F1X=0 THEN 600 530 PRINT:PRINTIIDO YOU WANT TO-" 540 PRINT:PRINTTAB(4) 111. START A FILE" 550 PRINT=PRINTTAB(4)1I2. CONTINUE A FILE II 560 PRINT:PRINTTAB(4);IIPLEASE SELECT BY NUMBER. II 570 DO:GETKEY KY$:VX=VAL(KY$) 580 IF VX>0 AND VX<3 THEN EXIT: ELSEPRIN T "1 OR 2 ONLY- PLEASE TRY AGAIN"IILOOP 590 ON VXGOSUB610,650 600 DCLOSE:V7.=0:RETURN 610 SCNCLR:GOSUB1340:REM GET TITLES 620 RNX=l 630 GOSUB1130:REM ENTRY 64.0 VX=0: RETURN 650 SCNCLR=GOSUB1340I1REM TITLES 660 GOSUB730:RECORD*7,1,1:GOSUB1380 670 INPUT*7,N$:RNX=1+VAL(N$) 680 GOSUB750=GOSUBl130:REM ENTRY 690 VX=0:RETURN 700 DOPEN*6, II TITLES II : GOSUB 1380 710 DOPEN*5, II 0: II +FM$ II GOSUB 138121 72121 RETURN 730 DCLOSE*5:DOPEN*7,IIKEY II 740 RETURN 750 DCLOSE*7I1DOPEN*5,"0: II +FM$ 76121 RETURN 770 SCNCLR:TT$=IIREAD FILE"IIGOSUB31210:IF F lX=0 THEN 88121 780 GOSUB1340:REM TITLES 790 DO: SCNCLR =GOSUB230: PR I NT II PR I NT II DO Y OU WANT TO-II ·800 PRINT: PRINTTAB (4) 111. READ ALL ITEMS II 810 PRINT: PRI.NTTAB (4) 112. READ SELECTED I TEM":PRINT 820 PRINTTAB(4) "3. RETURN TO MAIN MENU" 830 PR I NT I PR I NTTAB (8) II PLEASE SELECT BY NUMBER. II 84121 DO:GETKEY KY$:V7.=VAL(KY$) 85121 IF V7.>0 AND V7.<4 THEN EXIT:ELSE PRIN T"I-3 ONLY, PLEASE TRY AGAIN":LOOp· 860 ON V7. GOSUB 890,98121,88121

8-3 870 LOOP UNTIL VX=0 880 DCLOSE:VX=0:RETURN 890 SCNCLR:GOSUB730:RECORD#7,1~1 900 INPUT#7,A$ 910 RXX=VAL(A$):GOSUB750 920 FOR RN=1TORXX:RNX=RN 930 RECORD#5,RN7.,1 940 GOSUB1280:REM READ FILE#5 AND PRINT 950 PRINT:PRINT"PRESS SPACEBAR FOR NEXT, Q TO QUIT." 960 GETKEY KY$:IF KY$<>"Q"THEN NEXT 970 DCLOSE:RETURN 980 SCNCLR:PRINTuTVPE NAME PLEASE ..... 990 INPUT NM$:ID$=LEFT$(NM$,3):GOSUB730 1000 RECORD#7,1,1 1010 INPUT#7,A$:RXY.=VALCA$):RNY.=2:DO 1020 RECORD#7,RNX,1 1030 INPUT#7,A$:IF ID$=LEFT$(A$,3)THEN E XIT 1040 RNX=RNX+l:IF RNX>RXX+1 THEN EXIT 1050 LOOP: IF RN%>RX%+1 THEN BEGIN:GOSUB 750 1060 PRINT"NO SUCH NAME-PLEASE TRY AGAIN u:SLEEP3:GOSUB 980:BEND 1070 IF ID$=LEFT$(A$,3) THEN BEGIN: IF RN %=1 THEN RN%=2 1080 RNY.=RNX-1:GOSUB750:GOSUB1280:BEND 1090 PRINT:PRINTIJPRESS SPACEBAR FOR NEXT , Q TO QUIT u 1100 GET KEY KY$:IF KY$<>IJQuTHEN GOSUB980 1110 DCLOSE:Jy'~0:RETURN 1120 REM ENTRY SUBROUTINE 1130 DO: SCNCLR: PRINT: PRINT: N1Y.=1 1140 PRINT"ITEM u;RNY.:FORJ=lT05 1150 PRINTTL$(J),:INPUT NM$:GOSUB1220:IF LEFT$(NM$,1)=uX"THEN EXIT 1160 GOSUB 750 1170 RECORD#5,RNX,N1X 1180 PRINT#5,NM$:N1%=N17.+2+LN7.(J) I GOSUB1 380 1190 IF J=1THEN GOSUB 1230:REM UPDATE KE Y 1200 NEXT:RN%=RNX+1:LOOP 1210 RETURN 1220 NM$=LEFT$(NM$,LNX(J»:RETURN 1230 GOSUB730:RECORD#7,1,1 1240 PRINT#7,RNX:RN%=RNX+1 1250 RECORD#7,RNX,1

8-4 1260 PRINT#7,LEFT$(NM$,3):RN%=RN%-1:GOSU 81380 1270 GOSU8750:RETURN 1280 N1%=1:FORJ=lT05 1290 RECORD#5,RN%,N1% 1300 INPUT#5,NM$:PRINTNM$ 1310 Nl%=N1%+2+LN%(J) 1320 NEXT 1330 RETURN 1340 INPUT#6,TL%:FOR J=1T05 1350 INPUT#6,TL$(J) 1360 INPUT#6,LN%(J):NEXT 1370 OCLOSE#6:RETURN 1380 REM TEST OF DI 81( ERROR BYTE 1390 IF DS(20THEN 1440 1400 IF DS=50THEN 1440 1410 IF DS=62THEN 1440 1420 IF OS=70 THEN 1440 1430 PRINT OS$:PRINTTAB(S) "TYPE CaNT (RE TURN) TO CONTINUE":STOP:RETURN 1440 RETURN

READY.

The F I LIT program, an example of me use rather than a serious database example, but you can extend it as you will. Remember that if you use a database program for personal details of people for business purposes, you may have to register under the Data Protection Act (in the UK).

FIGURES.l

First principles

We shall start by looking at how the program works in outline. The listing has been renumbered starting at line 10 and with increments of 10, so that you can use the AUT 0 10 command to give you automatic line numbering. If you are using the 1570 disk drive, you will have to go over the listing and make sure that each fIlename that is used is of the form "0: NA ME", because otherwise you will get error messages. This is not needed if you are using the 1541 or 1571 drives. Three fIles are used, two relative fIles and one serial fIle. The serial fIle is used to keep a note of the names of the fields, and the length of each field. When you first use the program for a new variety of fIle, you will type these titles, and they stay with the fIle from then on unless you start another type of fIle on the same disk. This option, then, will be used just once for each file. If you want to use more than one F I LIT, you must keep them on separate disks, with a copy of the program on each disk. One of the relative files is used to keep the records themselves, the other is used to keep a key. The key fIle consists of the first three letters of the first field of each record. This allows you to pick a record by name, assuming that the names do

8-5 not share the same first three letters. You could use the first two and the last letter as another way of identification, or devise other combinations to suit yourself. As I said, this is a skeleton program, and it's yours to trim to shape and pad out as you please. The key ftle is the one that you could use if you wanted to sort, for example. One thing that we have to be very careful about is that the disk drive (1541,1570 or 1571) will not permit two relative ftles to be open at the same time. This causes NO CHAN NE L error reports, and other complications, and it's this point which makes the program rather slow to respond at times when one ftle has to be closed to allow another one to open.

When the program runs,a ftle (K E Y) is opened and closed again, and you are presented with the main menu. The first time that you use the program on a disk, you should go for the START NEW FILE option. This allows you to choose five titles for the fields of your record, and asks you for the length of each field. These names and figures will be recorded and used forever after, so you should plan them carefully. Figure 8.2 shows a typical display. After entering the fields and lengths, you are prompted for a ftlename. You can choose anything you like, as long as it has 16 characters or less, and is not KEY or TIT L E S. Perhaps you might like to add a line 425 which rejected these names as ftlenames, and asked again? The reason, of course, is that these ftlenames are used for other meso

Once the ftlename has been typed and pressed, the main ftle is opened with the name you have selected. The pointer is then moved to record number 255 and the word END placed there. This takes time, and a P LEA S E WA I T FOR F I L E PRE PAR AT ION message is printed to remind you. At several places in the program, the disk will be busy, and you may have to wait for it. This happens in particular when you have just entered the first field in each record. When the me has been created, the program returns to the menu.

TITLE 1 SURNAME FIELD LENGTH -- 20

TITLE 2 FORENAME FIELD LENGTH 15

TITLE 3 DATE OF BIRTH (DDMMYY) FIELD LENGTH 6

TITLE 4 OCCUPATION FIELD LENGTH -- 30 TITLE 5 PHONE NUMBER FIELD LENGTH -- 12 WHAT FILENAME WOULD YOU LIKE ?WORKFORCE

The appearance of a typical S TAR T NEW FI LE display just before storing the names on the disk.

FIGURES.2

8-6 You can now type data into your me by choosing the WR I T E TO F I L E option. You can do this right away, or any other time you switch on and load in the disk. You will then be asked for the mename. This is to ensure that the correct me is identified. If you choose the wrong name, you will be informed, and the menu reappears. When you use the correct mename, this brings up a new menu in which your choice is to S TAR T A F I L E or to CON TIN UE A F I L E. If you have only entered the field names so far, you will be starting a me, and you would also pick this option if you wanted to wipe out and replace a me of the same name. In this option, you will be prompted by the field names (such as NA ME, ADD RES S, etc) to type data. The data will be chopped to fit the field size that you have specified and recorded. There is a noticeable pause following the first name in each field. This is because the first three letters of this field are also recorded on the KEY me. This involves closing the main me, so recording the first field, and opening the key me, then closing the key me again. Don't rush your typing after you press on this first field. You will also fmd that the disk spins at other places while you enter data, and you have to wait until the screen cursor is visible again before you can continue typing. To end the entry, you type X. If this is inconvenient, change it (line 1150)! If you have a me already created, you can use the other write option to extend the me. You don't have to input the record number, because this is recorded with the key me, and the me will be extended with no gaps.

If you select the REA D option from the main menu, you will once again be presented with another menu, after checking for the mename. This time, the choice is to list all of the names, or pick a name. If you choose to list all names, the disk starts to work hard, and a complete record is printed on the screen. You can press the space bar to get the next record until the end of the me, or you can press Q to return to the start of this menu, with the F I LEN AM E prompt. If you choose to select a name, you are asked to type the name. This will be the name of the first field item. Only the first three letters are important in the program as it is organised at present. The program then opens the key me, and looks for these letters. Since this is a much shorter me than the main me, the search is quick. When the letters are found, the record number is calculated, and the main me is read to produce the record that you want. At present there is no facility to produce more than one record with the same identifying letters, and the program will always pick the first record that answers the description. You could, of course, redesign the program so that only the main me is used and you read the first three letters in each first field of the main me for identification. That would avoid the need to juggle with two relative mes when recording. Once again, it's your program, change it as you want. The aim here is to demonstrate techniques rather than to provide a piece of software for instant use. If you ask for a name that does not appear in the me, then you will be advised, and asked to try again. That's it!

The program in detail

Now for the hard work. There are a lot of points in this program which are important. If you try to design your own database programs, you will need to know what the disk drive does, and this listing reveals a lot that isn't made particularly clear by the manuals, and

8-7 which is not so easy to illustrate with short examples. No matter how much you may hate looking at other peoples programs, then, it will be useful to study this one, so that you can appreciate reasons. Unless you do so, you can waste a lot of time looking at the disk drive's inscrutable error messages and wondering why they arise.

The program is built round a core and a set of subroutines. A lot of the programming is straightforward BASIC, and I have made no use of fancy color or screen presentation effects - there's quite enough to type as it is. I'll concentrate on the explanations that relate to the use of the disk drive, rather than explain everything in detail. In other words, I'm assuming that you knew a reasonable amount of BASIC before you started reading this book.

Lines 10-90 are concerned with initial values of constants, and setting screen display. The relative me KEY is also opened. By using this opening statement at this point, we can then use DO PEN # 7 ," KEY" in future commands. The variable X$ is used to deliver an error message at a place in one of the subroutines, and the program starts in earnest in line 90. This prints a title, centred by the GOSUB 230, and asks if you need instructions. Any key apart from Y will cause the instructions to be skipped. I have not written detailed instructions, because these are just another load oftyping. You can type your own instructions when you have modified the program for your own use.

Line 120 starts a loop which fIrst clears the screen and then prints the menu. You are asked to select by number, using the GET KEY K Y $ and converting this input to a number form with V A L. This number is assigned to V% (an integer) and tested. If the range is acceptable, then line 200 carries out the choice. In the course of this choice, the value of V % can be changed. This can cause another subroutine to run when the is used, so each subroutine ends with V%=O to prevent this. Line 210 ensures that the menu is repeated unless you have picked the END PRO GRAM option. The subroutines then carry out the main actions. This is important, because it makes the program very easy to change. Practically all the subroutines that you might need for your own 'custom' version are listed, so if you know in detail what each subroutine does, . making your own version is relatively easy.

The creation subroutine

The subroutine that starts in line 330 creates a new me. This will wipe out any other me that has been created with this program on the same disk, which is why it's useful to have several copies of the program on different disks. The loop that starts in line 360 obtains title names for each fIeld, and also a length value. The length number is not tested, and it might be useful to reject lengths of less than 1 or more than, say, SO. Each title and length is assigned to an array, using T L$ for the title, and L N% for the length of fIeld. If the total fIeld length, allowing for spaces and carriage returns, is too long then you will have to start again. This test is made in line 390. If all is well, then you are asked for a menamein line 410. Once again, it might be useful to test this to make sure that the names KEY and TIT L E S were not used. Line 440 then opens a serial me for writing.

8-8 By using C iil' in the flIename of TIT L E S , any previous flIe of this name is deleted. The total length of record, each title name and each field length figure is then recorded in this serial flIe, and the flIe is closed. Line 470 then opens channelS for a relative flIe whose name is the one that you have selected, F M$. Note the way in which this has to be done. You are not permitted to use DO PEN # 5 , F M$, but as long as you have something in quotes, the string will be accepted. By using "0 : II + F M$, we solve two problems at once, because the 1570 drive insists on the 0: identification. The record length has already been calculated as T L%, and this is also included in the DO PEN statement. Once again, this is not completely straightforward, because the L and the number have to be presented as a string to make DO PEN accept a variable name. In line 480, the pointer is placed at a position for the 255th record, and END is printed to make room. This causes filing to operate faster in future, and it would have been an advantage to do the same with the key flIe early in the program. Try it!

It takes time to allocate space for 255 records, so a message to this effect is printed in line 430. Line 500 closes all flies, and line 510 sets flag variables and returns. Your titles and field lengths are now recorded, and the flies are set up and ready to use.

Writing to the file

Selecting the WR I T E option in the main menu leads to line 520. The flIename for the data flIe is checked, and if all is well another menu is presented. This gives the options of starting a flIe of data, or continuing a flIe. If the S TAR T F I L E option is selected, this will replace any existing flIe of the same name. The S TAR T option then leads to line 610. This uses GO SUB 1 340 to read the serial flIe, TIT L E S, so that the titles of each field are read into the array T L $ and the lengths into L N %. This is important because whatever you enter will have to be cut to the correct size if it exceeds the allocated field length. For a new flIe, the record number RN % will start with a value of 1 in line 620. The main entry is then carried out (line 630) by calling the entry subroutine at line 1130. When entry is complete (X is typed), line 640 sets V% to 0 and returns.

8-9 When the CON TIN U E A F I L E option is selected, the subroutine starts at 650. This reads the titles once again, but then has to fmd the correct value of record number R N% to extend the me. This process starts in line 680. The GOSUB 730 closes the main me channel (#5) and opens the KEY me channel #7. This has to be done, because you can't P R I NT # or I N PUT # with both relative mes open. The pointer is then put to the ftrst record of the key me. This is used, as we'll see later, to hold the maximum record number, so that this quantity can be read in line 670 and converted back to number form. We then add 1, to prepare for the next record. In line 680, the GO SUB 750 closes channel #7 and reopens channel #5, the main data channel. The program then makes use of the entry subroutine at line 1130 again to get another set of data. When this is complete, line 690 sets V % to zero and returns.

Reading the fde

The me reading option in the main menu once again causes a prompt for a mename, and the me is checked by the subroutine in line 300. This makes sure that the me exists, using the F 1 % flag to signal this to the routine. The new menu that is then presented gives the options of reading all mes or a selected me. If the ALL I T EMS option is picked, the subroutine which starts at line 890 is run. The ftrst three lines of this subroutine are used to read the maximum record number from the key me. As before, this involves closing #5, opening #7, reading the value, and then closing #7 and opening #5 again. The value of maximum record number is assigned to R X %, and this is used as the terminating value in a loop which starts in line 920. In each pass of the loop, R N% is assigned a number, and this is used to position the pointer in the main me. Line 940 then calls the subroutine that reads the me and prints the data. After each read, aPR E S SSP ACE BAR TOe 0 NTIN U E step is used to prevent the data scrolling out of sight before you have had time to read it. A 'quit' option is also included so that you can stop and return to the menu when you have had enough.

Using the S E LEe TED I T EM option leads to line 980. You are asked to type the name that you want, meaning the name of the ftrst fteld of the record. The variable I 0 $ is then used to hold the ftrst three letters of this name. Channel #7 is then selected by the subroutine at 730, and the pointer is placed at the start of the key me in line 1000. Line 1010 gets the ftrst item in this me, which is the maximum number of records. The rest of the me, starting at record number 2, consists of the ftrst three letters of names, and so this me can now be searched. The loop starts at the end of line 1010, and uses the maximum record number R X %+ 1 as its terminator. The' + l' is needed because the key me always contains one more item than the main me. This is because of the maximum record number which is stored at the start of the me. The me then goes from record number 2 to one more than the value of record number that is recorded. Lines 1020, 1030 set the pointer for each value of R N%, read the key me and compare the letters. If a match is found, the E X I T statement causes the loop to end and the test in line 1070 adjusts the value of R N% to 2 if it happens to be 1. This is because in line 1080 the statement R N% = R N% - 1 restores the correct value of R N% for the main me. If no match is found at the end ofthe loop in line 1050, the mes are swapped over (#7 closed,

8-10 #5 opened), a message is printed, and the subroutine starts again. If the name is found, the ftles also have to be swapped, and the reading subroutine at 1280 is used to read the full ftle entry. Lines 1090, 1100 then give you a chance to get another entry or to quit. The subroutine ends in 1110 by closing ftles, setting V% to zero, and returning to the menu.

The entry subroutine

The entry subroutine starts at line 1130. The correct channels must have been opened by the time this is called, and this is something you must ensure if you extend the program. In addition, the subroutine will use whatever value of R N% is passed to it by the main program. This value is printed as an item number in line 1140, and a loop starts to read in field values. In each pass of the loop, the title of the field is printed, and then you must input a value (name or number). The entry is chopped to size by the GOSUB 1220, and the program checks for .the entry of X, which terminates the procedure. Line 1160 ensures that channel #7 is closed, because you can't use two open relative ftles. Lines 1170 and 1180 then place the entry on disk (or, more correctly, in a buffer). The record pointer is set with N1 % as the position in the record. The number N 1 % is calculated from the field lengths, allowing for the added carriage return and a space. Line 1190 detects if this is the first field, because this is the key field. If so, the subroutine at 1230 is called to print the record number and the first three letters of the first field into the key ftle. We'll look at that later. The counting loop ends with the NEXT in line 1200, and the record number is incremented. The L 0 0 P then makes this action repeat until an X is entered. The entry of X causes the E X I T in line 1150 to return to the calling point.

The subroutine at 1230 is closely related to the writing routine, because it writes to the key ftle for each first field in the main ftle. Line 1230 selects the correct ftle (close #5, open #7), and places the pointer at record number 1, byte 1. Line 1240 then prints R N% in this position, and the number R N% is incremented. The incremented value is then used to place the pointer in the key ftle and print the first three letters of the key field (line 1260). The R N% value is then restored, the ftles are also restored (#7 closed, #5 opened), and the routine returns.

Reading

The general reading routine starts in line 1280. Once again, N 1 % is used to mark the position in the record, and a loop is used to read each field. Line 1290 sets the pointer, and line 1300 reads in the data and prints it. Line 1310 updates the value of N 1 %, using the same formula as was used when ftles were written. At the end of the loop, the routine returns. This is one of the most straightforward of the ftle-using routines.

The routine at line 1340 reads the serial ftle so as to obtain the names of the fields. In the program, this subroutine has been used rather too much. I have assumed that the

8·11 program is used once and then switched off, so that the titles have to be read for each use. In fact, once you have read the titles, they stay until you run the program again. You might want to put a test at every point where GO SUB 1 3 40 is used. A suitable test would be: IF NMS(1)="" THEN GOSUB1340 which would call the subroutine only if it were needed. This will save a lot of time and disk use.

The error routines are located at 1390. These follow the lines that we discussed in Chapter 7, but error 70 is also tested. This is the NO CHAN N E L error, and it normally occurs if there is no me or if the me is of the wrong name. The main use for this is in the subroutine at line 310. This uses a 'flag' variable F 1 %which is initially set at a value of 1. If the mename is wrong, there will be an error, which will be number 62 or 70 (I always get 70). This causes the program to print the string XS, and wait. The flag is set to zero, and the subroutine returns. This value of F 1 %is then used to prevent any me action in the subroutines, either for writing or for reading.

That's all there is to it. Taken as a whole, it looks rather intimidating, but when you split it into a core and its subroutines, as it was when it was written, it looks a lot simpler. It's yours now. Modify it as you wish, but please don't sell it or publish it as your own work!

8-12 Chapter 9

Graphics and sound

Data display

The ability of the Commodore 128 for producing excellent screen graphics, and remarkable sound effects, is exploited mainly by the writers of games who do not use BASIC as their programming language, and don't normally write the games on a Commodore 128 either! The full potential of the Commodore 128 for either sound or graphics, using BASIC, is so considerable that it would require a book to itself. In this chapter, then, we shall look only at a few aspects of graphics and sound that might be useful in more serious programs that you would write to support a business or hobby use for the machine. To start with, we'll look at how the graphics effects can be used in the display of data.

There aren't many serious applications for computers that don't at some stage include some need to display data, and for some purposes you might need to buy a spreadsheet type of program just for that purpose. It's more likely, however, that you will be writing a program to suit a purpose of your own, and you want a subroutine for displaying the data that the program works with or has generated. We have already, in Chapter 3, illustrated how data can be displayed on screen, and this applied also to the printer. What we need to look at now are the alternative visual ways in which data can be displayed, in particular at graphs, bar-charts and pie-diagrams.

Graphs

A graph is a set of points, each of which relates to a data item. A graph point is represented as a two-dimensional location, needing a V-number and an X-number to fIx its position. This means that each graph point represents two values, so that a graph is used to show the relationship between two sets of changing values. These values may • have some mathematical relationship, in which case what we are graphing is a function; or they may have a relationship which is purely an empirical one, derived from measurement rather than calculation. We'll look at function graphs later, and for the moment stick to the more empirical type of graph of which a typical example might be a

9-1 plot of daily operating profits. In such a graph plot, one quantity is time, measured in days, and the other is profit, measured in money. The standard way of displaying such quantities on a graph is to make the horizontal X axis of the graph represent the independent variable - an item such as time, over which you have no control. The other axis, the Y axis, then represents the dependent variable, the one that changes more erratically and whose changes you want to display.

The plotting of data can be very conveniently handled by the grap~cs statements of the Commodore 128, which are much more convenient that the few BASIC graphics statements of the old C64. The first set of statements to get used to are GRAP HIe and DRAW, which are enough to get started with graphs. The G RAP HIe statement sets the scene for drawing by specifying the screen mode. This can be any of the six modes that are listed in the manual, and for the kind of application that we have in mind at the moment, the most useful is mode 2, the standard bit-map, split-screen, graphics mode. This allows the top part of the screen to be used for graphics and the lower part for ordinary text. It is, in fact, the graphics equivalent of a WIN DOW statement. Following the mode number, you can use a clear number (1), or a 0 to mean that the screen is to be left uncleared. The third number in the G RAP HIe statement specifies the line at which the text will start. If you do not specify this, the start will be taken to be line 19, leaving six text lines.

The DRAW statement can use as its first number a background/foreground number. We will normally want to put graph points on to the screen in foreground colour, and this is achieved by using a 1 in this position. If you omit this number, you still need to put in the comma that normally follows it. The next two numbers are the X and Y coordinate numbers for the screen position. The X number is straightforward, using a range of 0 to 319, but the Y number is not quite so simple. If you use the normal graph convention that distances on the Y axis are measured from the bottom upwards, then you'll fmd that your display is upside down. This is because the Commodore 128, like many other computers, regards the top of the screen as the starting point for Y numbers, and make Y=199 the bottom of the screen. Furthermore, this number refers to the whole screen. The 0 to 199 range of the graphics screen corresponds to the 0 to 24 range of the text screen. In other words, eight graphics lines correspond to one text line. If you are using lines 20 to 24 for text, then these five lines correspond to 40 graphics lines, allowing the more restricted range of 0 to 159 to use. If 159 represents the bottom of the graph, then,

9-2 we can get our graph correct way up by subtracting the conventional Y number from 159.

Figure 9.1 illustrates this in use. As usual, I have put in REA D ••• DA T A lines in order to save the input of X% and Y % numbers each time you run the program. The GRAP HIe statement in line 10 specifies a bit-mapped screen, cleared before use, and with line 20 starting the text area. The background colour of the bit-mapped part of the screen will be whatever background colour was in use before the GRAPHIC statement, and changes in background color following G RAP HIe will affect only the text portion. The constant Y Y %= 1 59 will be used to convert the Y numbers into the form that we need. The DRAW statement is then used in a loop which will range over the whole set of X values. The DRAW statement uses the foreground number of 1 for the bit map screen, and the numbers X% and Y Y %- Y % for mapping the points. In line 50, aGE T KEY statement is followed by GRAP HIe 0 , 1. The reason for this is that the use of a bit-mapped screen locks you out of a normal program end. If you simply leave the program with no END statement, you will have to press RUN/STOP RESTORE to get back to normal. If you use an END line, then you'll fmd your text screen is still the window that was defmed in the GRAP HIe statement. Using GRAP HIe 0, 1 gets back to normal, but with no graphics displayed. The statements in line 50 are a useful compromise - you get back to normal service when you press any key. Note, by the way, that the text has been printed one line down from the start of the text screen. This is because there is always slight interference between the bit mapped screen and the text window. Any text that you print on the top line of the window always appears to have a slight flicker at the top of the letters. By avoiding this line, we get much better looking text. Try it, and try a few variations for yourself to get the feel of it.

READY.

10 GRAPHIC 2,1,20:YYY.=159 20 FOR N=1 TO 31:READ X%~y% 30 DRAW 1,X%,VYY.-YX:NEXT 40 PRINT:PRINTTAB(14); "SAMPLE GRAPH" 50 GETI

READY.

A simple graph-drawing program to illustrate the GRAP HIe and DRAW statements.

FIGURE9.l

9-3 Elaborations

The display of points that the program in Figure 9.1 gives is useful, but not as vivid a display as some prefer. Tastes will vary here, and for a lot of purposes in science and engineering, a point graph is preferred, unless a smooth straight line or curve can be fitted to the points. For other purposes, though, a set of joined points makes the change from one point to another more obvious, and this can be accomplished by using an extension of the DRAW statement. By adding TO and another set of X and Y values, we can make DRAW connect two points. This allows us to alter the graph program into one that will draw connected points, a process which makes the plotting action look much more impressive. Figure 9.2 shows this in action and can be edited from your listing 9.1. The main changes are in the DRAW statement, but line 10 also contains a new READ OX %, 0 Y %. In this program, 0 X% , 0 Y % will be the start of a line, and X% , Y % the end, so that we can draw between these points. When a line has been drawn, the values for X %and Y %will then be transferred to 0 X % and 0 Y %, so that the next line can start from where the previous one ended. The DRAW statement in line 30 is altered as shown, drawing from the 0 X %,0 Y % value to the X %, Y % one, using the value of Y Y % as before to correct the vertical coordinate values. The result is a rather gratifying line graph.

READY.

10 GRAPHIC 2,1,20:YY%=159:READ OX'l.,OY'l. 20 FOR N=l TO 30:READ X'l.,Yy. 30 DRAW 1,OXY..YV'l.-OVy. TO X'l.,YY'l.-Y'Y.:OX'l.=X 'Y.: OYY.=YY.: NEXT 40 PRINT: PRINTTAB (14) ; "SAMPLE GRAPH" 50 GETKEY A$:GRAPHIC0,1 100 DATA 10,10,20,30,30,50,40,80,50,60,6 0,40,70,60,80,65,90,70 110 DATA 100,75,110,80,120,75,130,80,140 ,90,150,100,160,120,170,130 120 DATA 180,140,190,130,200,120 130 DATA 210,110,220,115,230,100,240,90, 250,85,260,80,270,65,280,70,290,50,300,4 O,310,60

READY.

A graph which uses joined points, giving a better display for some purposes.

FIGURE 9.2

9·4 This can be extended by use of the P A I NT statement, provided that you ca~ exercise some patience. The purpose of P A I NTis to fill in an area with colour; the fllling is quite remarkably slow but the effect can be very good as far as the finished product is concerned. The listing of Figure 9.3 shows the graph drawn and painted, and also illustrates some odd defects in the text 'window' that accompanies graphics. The starting point is made at an X value of zero, and the starting Y value of 10, and the graph ends with the X value of 319 and the fmal Y value, put in as an addition to the DA T A lines. This has been done so that the graph line leaves no gaps at either side that would allow the 'paint' to escape. The graph is drawn in the usual way, and then line 50 prints a very necessary message. Line 60 contains the P A I NT statement, picking for its starting point a place somewhere around the middle of the area under the graph that we want to shade. Thus far, so good, but we then want to replace this message by another. Now this is where frustration comes in. If you use SeN C L R, the whole screen is cleared. If you use SeN C L R0, the text screen is cleared, but aPR I NT statement operates on the top of the part of the text screen that is hidden by the graphics screen. If you use CH A R, the text can appear only on the graphics screen in this layout. There are two ways out. One is to use a looping subroutine which will use P R I NT 20 times with blank lines, and then print the message. The other is illustrated here, to create a WIN DOW for the text, and control it in the usual way. This window will have to be reset to the whole screen when the program ends. READY.

1m GRAPHIC 2, 1 ,2121:YY~~=159:0X%=12I:0Y%=1121 2m COLOR1,3:COLOR5,8 3m FOR N=1 TO 32:READ X%,Y% 4121 DRAW 1,OX%,YY%-OY% TO X%,YY%-Y%:OXX=X %:OYX=Y%:NEXT 5121 PRINT:PRINTTAB(11);"PATIENCE, PLEASE! " 6121 PAINT 1,16121,6121,121 7121 WINDOW 121,2121,39,24,1 8m PRINT:PRINTTAB(14)"FILLED GRAPH" 9121 GETKEY A$:GRAPHICI2I,I2I:WINDOWI2I,12I,39,24 1121121 DATA 1121,1121,2121,3121,3121,5121,40,8121,5121,6121,6 121,4121,7121,6121,8121,65,9121,7121 11m DATA 1121121,75,11121,8121,12121,75,13121,8121,t4121 ,9121,15121,1121121,160,12121,170,13121 12m DATA 18121,14121,19121,13121,2121121,12121 13m DATA 21121,11121,22121,115.23121,1121121,24121,9121, 25121,a5,26121,al2l,270,65,280,70~29121,50,300,4 121,31121,6121,319,6121 READY.

A drawn and filled graph, to illustrate the use of P A I NT. Note that the ends of the graph line must be taken to the limits of the screen to stop paint spreading.

FIGURE 9.3

9-5 You can speed up the drawing and painting actions if you add FA S T to the end of line 20 and S LOW to the end of line 80. Doing this, however, deprives you of the sight of the pattern being drawn, and the sight of a blank screen, albeit for a shorter time, is not a good compensation. Another way out is to avoid the double act of drawing and then painting by making the graph out of close-spaced vertical lines. This is done in Figure 9.4, which uses a rather tricky expression in its DR AW statement. This can be done only when the X number advances in regular steps, as it does in this example, which uses steps of ten. The main outline of the program is very much as before, and the novelty is mainly in line 60. The start of each DRAW is now on the baseline, at the values OX %+ J , Y Y %. The value of J is selected in a loop so as to fill in the intermediate steps of the X value. If you were entering values of X % from an I NPUT line, the successive values would have to be subtracted, and decremented by 1 to get a suitable range for J in each loop. In this case, with uniform steps of ten, the problem doesn't arise. The other end of the line is the awkward one. The X value is the same, but the Y value is the fearsome expression shown, and Figure 9.5 attempts to show how this has been obtained. A complication here is that the values of Y %and 0 Y %that are being used are the corrected values, that is values which are obtained by subtracting each read value from YY%. When this runs, however, you will see the graph painted and drawn in one step, and at a rather faster rate. Once again, FA STand S LOW could be used to speed this up at the expense of watching it happen.

READY.

10 GRAPHIC 2,1,20:YY~=159:READ OX~,OY~ 20 OY~=YY~-OY~ 30 PRINT: PRINTTAB (14); "PLEASE WAIT" 40 COLOR1,3:COLOR5,a 50 FOR N=1 TO 30:READ X7.,Y%:Y~=YY~-Y~ 60 FOR J=0 TO 9:DRAW 1,OX~+J,YY~ TO OX~+ J,OY~+(J+1)*(Y~-OY~)/(X~-OX~):NEXT 70 OY~=Y~:OX~=X~:NEXT 80 WINDOW O,20,39,24,1 90 PRINT:PRINTTA8(14) "FILLED GRAPH" 100 GETKEY A$:GRAPHIC0,0:WINDOW0,0,39,24 110 DATA 10,10,20,30,30,50,40,80,50,60,6 O,40,70,60,80,65,90,70 120 DATA 100,75,110,80,120,75,130,80;140 ,90,150,100,160,120,170,130 130 DATA 180,140,190,130,200,120 140 DATA 210,110,220,115,230,100,240,90, 250,85,260,80,270,65,280,70,290,50,300,4 O,310,60 READY.

A faster way of producing a filled graph, without using P A I NT.

FIGURE 9.4

9-6 Y%

Y% - OY%

OY% X% - OX% -"'''~I

YY%~ ______~~-L~~ D __~~~~~~ ______OX% 0 1 2 3 4 5 6 7 8 X%

" Stepsoft./ J

Line AB is 3/10 of Y%-OY%

Length of AD=OY% + 3/10 (Y%-OY%)

=OY% + 3 (Y% - OY%) since X%-OX% = 10 steps X% - OX%)

= OY%+(J+1)*(Y%-OY%)/(X%-OX%)

How the expression for Y in Figure 9.4 is obtained.

FIGURE 9.5

Getting back to less exotic routines, we may want to have scale figures printed on our graph. This can be done by using C HA R, which will operate on the graphics screen. For a graph displayed on the screen, the less you add in this way the better, because it does get in the way of the graph. Part of this is due to the printing of numbers by C HA R as strings, so that there is always a space left on the left-hand side for a sign. The routine in Figure 9.6 avoids the worst of this by using a RIG HT $ function, but as you can see, it blots out part of the graph, and looks obtrusive. It's better to use C HA R only for putting in a few numbers, perhaps top and bottom, or one in the middle also. The C HA R action works inside the graphics screen, but it deals with the good old row and column numbers of the text screen. Line 42 has been inserted into a listing that is basically the one of l"igure 9.2, so that the Y range numbers appear at the left-hand side.

9·7 READY.

10 GRAPHIC 2,1,20:YYX=159:READ OXX,OYX 20 FOR N=l TO 30.READ X1.,Y1. 30 DRAW 1,OXX,YYX-OYX TO XX,YYX-YX:OXX=X X:OYX=YX:NEXT 40 PRINT:PRINTTAB(14);"SAMPLE GRAPH" 42 FOR Y=0 TO 19:5$=RIGHT$(STR$(160-8*Y) ,3):CHAR1,0,Y,S$:NEXT 50 GETKEY A$:GRAPHIC0,1 100 DATA 10,10,20,30,30,50,40,80,50,60,6 0,40,70,60,80,65,90,70 110 DATA 100,75,110,80,120,75,130,80,140 ,90,150,100,160,120,170,130 120 DATA 180,140,190,130,200,120 130 DATA 210,110,220,115,230,100,240,90, 250,85,260,80,270,65,280,70,290,50,300,4 O,310,60

READY.

Putting lettering into the graph, using CH A R. This is obtrusive because of the large size of the letters when the 4O-character screen is being used.

FIGURE 9.6

Another problem in graphing is that quantities seldom appear to be suited to graphs. We have a range of X values of 0 to 319 and a Y range of 0 to 199 available, but what if the quantities that we need are outside this range? Provided that it's the range and not just the number that makes this a problem, we can deal with it. If the numbers are all large, then they can be divided down, or have a constant subtracted. If the numbers are all very small, they can have a multiplying factor applied to them. The problem such as an X range of 10 to 5000 and a Y range of 5 to 3000 can be dealt with differently, using SeA L E. seA L E, to be effective, has to be used immediately following the GRAP HIe statement - if it is used before G RAP HIe, it will be ineffective. The word seA LEis followed by a 1 to tum the scaling action on, and then by the maximum values of X and Y that will be used. You have to be careful here that if you are inverting the Y values with y Y % again, then Y Y % must be the same as the number you have selected for Y scaling. You could in fact, use seA L E 1, MX %, Y Y% here, with MX % equal to the maximum X value. You can then use the values of X and Y which would be normally unacceptable, providing that they don't exceed the values in the seA L E statement. As the program of Figure 9.7 illustrates, this can be very effective and useful.

9·8 READY. 10 GRAPHIC 2,1,20:YY%=2000:READ OX%,OY% 15 SCALE 1,5000,2000 20 FOR N=1 TO 14:READ X%,Y% 30 DRAW 1,OX%,YY%-OY% TO X7.,YY%-Y7.:0X%=X I.: OY%=YI.: NEXT 40 PRINT:PRINTTAB(14);"SAMPLE GRAPH" 50 GETKEY A$:GRAPHIC0,1 100 DATA 100,200,400,300,600,700,1000,10 00,1200,900,1300,1500,1400,1600,2000,140 o 120 DATA 2200,1700,2400,1800,2600,1700,3 000,1200,3400,1000,4000,800,5000,300

READY.

The use of seA LEfor ranges of number which could not normally be used as coordinates.

FIGURE 9.7

Function graphs

For some specialised purposes, it's useful to be able to plot the graph of a function. In this respect, a function means an expression which can fmd a value for Y in terms of X. An example of such a function would be Y = SIN ( X ) + X t 3 / 2, and it's often useful to be able to plot a graph of such a function in terms of X and Y over a suitable range of X. This can be done by a fairly simple program, but the problem always arises that the formula that links X and Y has to be put into the program as a line, often a defmed function, before the program can run. It is much more useful if the function can be typed as the program runs, so that graphs can be drawn quickly, and scales adjusted if need be, and another amended function tried. This is by no means simple programming until more information on the Commodore 128 is available, but Figure 9.8 shows a useful compromise. In this program, the function is put into the action of the function key Fl, and a rerun command in the key F2. When the function is entered, the first part of the program ends, and you are asked to press the function keys. This will put in a new line 1000 which carries the function, and the command RUN 5 a which will start the graph drawing. The graph drawing part of the program is a simple one, with the minimum of complications, and it is up to the user to enter functions that will result in visible graphs. The SeA L E action can be used, but it is very difficult to adjust scaling in any automatic way to suit every possible function. You can't, for example, display a function like Y =X t 2 over more than a small part of the screen unless you incorporate suitable scaling factors such as Y =1 8 a * ( X / 3 aa ) t 2. The trigonometric functions also present

9-9 scaling problems, because a function such as SIN ( X) takes values that lie between + 1 and -1, with X values being in radians. A suitable formula here would be y - 90 + 9 0 * SIN ( X 130 ) , using a maximum value of 300 for X and step of 2. In general, if you know why you want this program, you will know what scaling is needed!

READY.

10 INPUT"EXPRESSION TO PLOT: Y=";XP$ 20 XP$="1000 y=u+XP$+I:RETURN" 30 KEY 1 , XP$+CHR$ ( 13) : KEY2, .. RUN50 II +CHR$ ( 1 3) 40 PRINT"PRESS KEYS Fl THEN F2":END 50 GRAPHIC 2,1,22:PRINT 60 INPUT"MAX. X RAN6E 0 TO -";MX 70 INPUT"STEPS OF X - u;SX 80 IF MX>319 THEN MX=319 90 MV=183 100 OX=1:X=OX:GOSUS1000:0Y=V 110 FOR N=l TO MX STEP SX:X=N 120 GOSUS 1000 130 DRAW 1,OX,MV-OV TO X,MY-Y 140 OX=X:OV=Y:NEXT:6ETKEV A$ 150 GRAPHIC 0,1:END

READY.

A program for displaying the graph of a function. This is a simplified version of a much longer listing which PO K Es values direct into memory.

FIGURE 9.8

Barcharts

A bar chart is a popular alternative to the use of a conventional graph, particularly when not too many items of data have to be shown. It's a particularly good way of showing items that really don't need to be graphed, like the monthly turnover of a business. In addition, bar charts don't need such elaborate preparation as a graph, and look more 'solid' and interesting. Scaling can still be a problem, because it's still difficult to represent a large range of values on the screen with any reasonable precision. Where bar charts are used, however, precision is normally less important than conveying how something has changed.

The fundamental BASIC statement for making bar charts is the BOX statement. BOX has to be followed normally by seven numbers, of which we shall omit one, the rotation number, for the purpose of drawing vertical bars. The first number is the colour source

9·10 number which for the ordinary bit mapped screen foreground colour is 1. This is followed by two sets of X,Y coordinates. The fIrst set is for the top left-hand corner of the box, and the second pair is for the bottom right-hand corner, like the WIN DOW numbers. The difference here is that these numbers are in graphics terms of 0 to 319 for X (for a simple bit-map) and 0 to 199 for a full height ofY. In the example of Figure 9.9, we are using once again the restricted height for Y due to the G RAP HIe statement, so that the Y range is 0 to 159. READY.

10 GRAPHIC 1,1,20:SC=1.6 20 COLOR0,1:COLOR1,6:COLOR5,2ISCNCLR 30 FOR N=2 TO 13 40 READ A$,Jr. 50 BOSUB 110 60 BOX 1,Xlr.,Ylr.,X2r.,Y2r.,,1 70 CHAR 1,3*N-3,21,A$ 80 NEXT 90 GETKEY A$:BRAPHIC0,1:END 100 DATA JAN,30,FEB,40,MAR,60,APR,90,MAY ,200,JUN,220,JUL,240,AUB,210,SEP, 180,OCT ,80,NOV,70,DEC,100 110 X1X=8*C3*N-3):X2X=8*(3*N):Ylr.=159-JX ISC:Y2X=159 120 RETURN

READY.

A simple bar-chart program, which produces joined boxes.

FIGURE 9.9

9-11 Line 10 incorporates a scaling factor as variable S c. This has been calculated from the range of figures that will be plotted. As usual for demonstration purposes, these are being read from a DA T A line, rather than from an array as would be more likely in a working program. The scaling factor is the maximum number (in the data) divided by 160, which is the maximum range of Y - the actual value has been rounded. This could be obtained in a real program by fmding the maximum value in the arrray and dividing by 160, then rounding down. Line 20 then sets colours. Note that the C HA R lines will write in the graphics foreground colour, not in the character colour. The loop then starts in line 30, using 2 to 13 rather than 1 to 12 because this leaves a margin on each side of the chart. The items are read, and line 50 calls the subroutine that calculates suitable values for the BOX statement. In this subroutine, the factor of 8 is used because the graphics dimensions are eight times the ordinary text dimensions. The other factors should by now be fairly familiar. The chart that is drawn in this way is a joined bar chart.

Now if a joined chart of this type is not to your taste, it's not difficult to alter the program so as to produce separated bars, and at the same time, centre the display to some extent. The revised version is shown in Figure 9.10, and the changes are in lines 30, 70 and 110. The range has been put at 1 to 12 again, and the CH A R statement has been changed so as to move the X number one place along, using 3 * N - 2 rather than 3 * N - 3. In the subroutine line, 12 has been added to the X1 % number and 4 to the X 2 % number, so that the bars are located fairly symmetrically around the month letters. This often looks much more effective for many purposes than the solid type of chart.

READY. 10 GRAPHIC 1,1,20:SC=1.6 20 COLOR0,1:COLOR1,6:COLOR5,2:SCNCLR 30 FOR N=1 TO 12 40 READ A$,J'I. 50 GOSUB 110 60 BOX 1,Xl'1.,Yl'1.,X2'1.,Y2'1.,,1 70 CHAR 1,3*N-2,21,A$ 80 NEXT 90 SETKEY A$:SRAPHIC0,1:END 100 DATA JAN,30,FEB,40,MAR,60,APR,90,MAY ,200,JUN,220,JUL,240,AUS,210,SEP,180,OCT ,80,NOV,70,DEC,100 110 Xli.=12+8*<3*N-3):X2X=4+8*<3*N):Yli.=1 59-Ji./SC:Y2X=159 120 RETURN READY.

An improved form of bar-chart, with the bars separated.

FIGURE 9.10

9-12 Pie diagrams

A pie diagram is an excellent way of displaying how anything is split up. You might use it, for example, to show how the spending of a business is allocated, or from what main sources profits are derived. The principle of the pie display is to show the whole as a complete circle, with the parts as sectors that are either slightly separated or distinguished by colour. Such graphs are not particularly difficult to draw, the main problem is that of getting the data into suitable form. Figure 9.11 shows a pie chart formed from a DA T A line for demonstration purposes, so that you see the general form of the command and how the sectors of the circle can be marked off with the D RAW TO statement. For many purposes, this might be completely adequate, but you may fmd that the shape of the circle that is drawn by the C IRe L E statement is not perfectly round. This depends a lot on the monitor or TV that you are using for display, and you can adjust the roundness of the circle by putting in the missing parameter following R %. Despite what the manual states, making this parameter equal to R% produced an ellipse with height less than width on my display, and a value of R %+ 2 0 was needed to produce a circle with an acceptable shape. It's likely that the manual sizes relate to American TV screens and to monitors. You may fmd, of course, that making the chart non-circular makes it more interesting to look at. Note that the multicolour graphics mode has been used, because using the simple bit-map mode causes each different colour to be accompanied by rectangles of black background. In the multi-colour mode, the X range is halved as compared to the ordinary bit-map. READY. 5 COLOR0,1 10 GRAPHIC 4~1,20 2121 X'l.=79:YI.=79:R'l.=30:SAY.=I2I:INY.=1 30 FOR J=1 TO 5 40 READ EA'l.,CL'l. 50 COLOR 1, CL'l. 60 CIRCLEl,X'l.,Y%,RY..~SA%.EAY."IN'l. 64 DRAW TO XY.,Y% 70 SAY.=EAY. 80 NEXT 90 DATA 40,6,120,7,230,8,290,15,360,3 100 GETKEY A$:GRAPHIC 121,0 READy.

A pie-chart obtained from dummy data read from a DA T A line. This is a 'skeleton' pie, with no paint filling.

FIGURE 9.11

The sectors of the pie chart can be filled in by using P A I NT, but the form of the filling is rather disappointing as Figure 9.12 shows. The problem here is that the P A I NT instruction follows the shapes of character blocks rather then the fine lines of the

9·13 READY.

:10 COLOR0,1 20 GRAPHIC 4,1,20 30 X%=79:Y%=79:R%=30:SA%=0:IN%=1:XR%=R%: YR%=R'X+20 . 35 DRAW1,X%,Y% TO XX,YX-YR% 40 FOR J=l TO 5 50 READ EA%,CLI. 60 COLOR 1, CL% 70 CIRCLE1,X%,Y%,XR%,YR%,SAI.,EA%"IN% 80 DRAW TO X%,Y% 81 AG=(SA%+EA%)*/360 82 NX%=XI.+YRI.*SIN(AG)/2 83 NYI.=Y%-YR%*COS(AG)/2 85 PAINT1,NXI.,NYI.,1 90 SAI.-=EA% 100 NEXT 110 DATA 40,6,120,7,230,8,29121,15,360,3 120 GETKEY A$:GRAPHIC 0,0

READY.

A simple colour-fLlled pie-chart. Note the missing pi in line 81 - it should appear between the * and / symbols. This produces colours - but the shape is very poor.

FIGURE 9.12 drawing, making the fmal shape show rough staircase patterns. Lines 81 to 83 obtain coordinates for a point within the boundaries of a sector, so that the P A I N T statement can operate correctly. Note that Mode 1 of the P A I NT statement is used, so that painting will stop at any colour of boundary. Note also that though the C IRe L E statement uses angles in degrees, the calculation of internal points for fIlling must use radians, hence the p i / 360 part of the line 81. In the listing, the pi does not print, and must be inserted between the * and / signs. You cannot get around the staircase problem by such dodges as using DRAW to create a set of radial lines, because these also will degenerate into blocks wherever different colours are adjacent. A fully-filled pie chart, then, is not a particularly useful device when drawn in this way. If you want to show the pie chart in filled colours and also with good outlines, you will have to resort to a separated segment chart, such as is illustrated in Figure 9.13.

The aim here is to draw each segment from a different point. By using the SIN and COS terms to calculate X C % and Y C %, these starting points can themselves be made to lie on a circle. The principle is to draw a point at the edge of an arc in line 100, and then a line to the 'centre' in line 110. The full arc is then drawn, followed by another line to the centre that is being used. The sector of circle can then be fIlled in, and C HA R can be used to place any reference letter or number. Note that C HA R, as always, needs text coordinates, and this means dividing the graphics X coordinates by 4 and the Y

9·14 READY. 10 COLOR0, 1. 20 GRAPHIC 4,1,20 30 XX=79:YX=79:RX=30:SAX=0:INX=I:XRX=RX: YRX=RX+20:0SX=20 40 FOR J=1 TO 5 50 READ EAX,CLX 60 AG=(SAX+EAX>*/360 70 XCX=XX+OSX*SIN(AG)/2 80 YCX=YX-OSX*COS(AG)/2 90 COLOR 1, CLX 100 CIRCLE1,XCX,YCX,XRX,YRX,SAX,SAX+l"I NX 110 DRAW TO XCX,YCX 120 CIRCLE1,XCX,YCX,XRX,YRX,SAX,EAX"INX 130 DRAW TO XCX,YCX 140 NXX=XCX+YRX*SIN(AG)/2 150 NYX=YCX-YRX*COS(AG)/2 160 PAINT1,NXX,NYX,1 170 CHAR1,NXX/4-1,NYX/8,STR$(J) 180 SAX=EAX 190 NEXT 200 PRINT: PRINTlll.LABOUR II ,1I2. MATERIALS 11,"3. RATES","4.TAX","5. OTHER" 210 DATA 40,6,120,7,230,8,290,15,360,3 220 GETKEY A$:GRAPHIC 0,0 READY.

A separated-segment pie chart. This is very much more satisfactory in appearance. Note the missing pi between * and I in line 60.

FIGURE 9.13 coordinates by 8. The reason for the difference is that multicolour bit mapping is being used, and this permits a smaller range of X numbers because each pixel is of double normal width as the manual explains. The use of CH A R is not entirely satisfactory because of the black background that is used in each case, but this can be simply overcome by inverting the video of C HA R by using a , 1 at the end of the C HA R statement. This last version gives a much better pie chart than any other version as far as visual appeal is concerned, and the listing is sufficiently adaptable to allow it to be converted to subroQtine form. The only remaining item is how to convert data into the angle numbers that would be needed.

This is illustrated in Figure 9.14, which shows the conversion routine only. The ending angles are produced as an array EN %( C% ) , and in this illustration, the amounts and the total are input into the program, though in a working subroutine you would probably

9·15 want to read these from a disk me. The routine uses no checking, though in a real-life situation it would be desirable to check that the sizes that were entered did add up to the total given. An alternative is to accept all but one of the items and make the other a default found by subtraction. Along with the pie chart routine, this will allow good presentation of data. The array of angles has deliberately not been dimensioned, because pie charts with ten sectors or more look too fussy, so that you should aim to stay within this limit. READY.

1121121121 INPUT "TOTAL u;TTX:INPUT "NUMBER OF SECTORS ";DX:ENX(0)=12I 11211121 PRINT"PLEASE TYPE EACH VALUE AS ASK ED" 102121 FOR C=1 TO OX: INPUT "VALUE ";KX:CX=C 11213121 ENX(CX)=KX*36121/TTX+ENX(CX-l) 11214121 NEXT: FOR N=l TO OX 105121 PRINTENX(N);" u;:NEXT READY.

A data-conversion routine for obtaining data in correct angle form for a pie chart.

FIGURE 9.14

Sounding off

The application of sound to a data-handling of program is rather more limited than the application of graphics. The BASIC of the Commodore 128 permits an excellent degree of control over the sound output, to such an extent that a complete book would be needed to do justice to the system. In this small section, then, we shall look at just one specialised use of sound - to provide a user warning. This might be to attract the attention of the user to the need for an input, or to warn that another input would exceed the limits of memory or of array size. As usual, if the sound statements are put into a subroutine, then that subroutine can be called by making a test of whatever is needed.

Figure 9.15 illustrates a useful warbling note subroutine. A note of this type can be much more attention-grabbing than a note of ftxed pitch. The rate of warble is determined by the sixth number, 500, in the SOU N0 statement, and the pitch range is determined by the second and fifth numbers. The third number, 60, is the time of the sound which is in units of sixtieths of a second (USA) or ftftieths (Europe). The fourth number, 2, is the one that specilles that the pitch must oscillate - other values of 0 or 1 will specify a change of pitch up or down without oscillation. The fmal number, 1, specilles the waveshape and you can experiment with values of 0 or 2 as alternatives. The ftrst number is the sound channel, permitting a choice of 0, 1 or 2 - since three separate sounds can be produced. Remember that the sound is reproduced through the loudspeaker of a TV

9-16 receiver or monitor, and if you are using a monitor other than a Commodore type there may be no loudspeaker in it. The sound output will then have to be connected to an amplifier and loudspeaker system. On the whole, this is rather too much effort to have to make if you only want to use sound as a warning. The subroutine sets volume at a maximum just before the SOU N D statement, and returns it to zero afterwards. The reason is that you need to have the volume control of the monitor or TV turned up to hear the sound, and if the volume in the subroutine is not returned to zero, the background noise is rather obtrusive. If you are using sound in a program, it's advisable to start with a VOL 0 statement, and put the programmed volume up only when needed. Another point concerning sound is that the statement executes with no delay, but the sound may take time to end. This is the reason for the S LEE P 1 statement in line 60020. If this is omitted, nothing will be heard, because the VOL 0 statement will turn off the sound before it has had time to start. The sound statement numbers are handed on to a separate sound chip, which then creates the sound, using its own timing. By using the S LEE P statement in this subroutine, enough time is allowed to hear the sound before it is turned off again. If you do not want SOU N D subroutines to hold up your programs, you will have to omit the VOL 0 and S LEE P statements, and use some other method of reducing volume - or put up with the background noise.

READY.

10 GOSUB 60000 20 END 60000 VOL15 60010 SQUND1,8500,60,2,8000,500,1 60020 SLEEP1:VQL0:RETURN READY.

A warbling note subroutine for drawing the user's attention to something.

FIGURE 9.15

9·17

Chapter 10

Printers

The reasons for needing a printer are obvious if you use your Commodore 128 for business purposes. You can hardly expect your accountants or your inc()me-tax inspector to look at accounts that can be shown only on the screen. It would be a total waste of time if you kept your stock records on a computer, and then had to write down each change on a piece of paper, copying from the display on the screen. For ~ of these purposes, and particularly for word processing, the printer is an essential part of the computer system. Output on paper is referred to as hard copy, and ~his hard copy is essential if the computer is be of any use in business applications. For word-processing uses, it's not enough just to have a printer, you need a printer with a high-quality output whose characters are as clear as those of a typewriter.

Even if your computer is never used for any kind of business purpose, however, you can run up against the need for a printer. If you use, modify or write programs, the printer can pay for itself in terms of saved time. Trying to trace what a program does from a listing that you can see only in lumps of a few screen lines at a time is totally frustrating. Even a very modest program may need a hundred lines of BASIC, and trying to check a program of a hundred lines when you can only see a dozen or so at at4ne is like bailing out a leaky boat with a teaspoon. With a printer attached to your Commodore 128 you can print out the whole listing, and then examine it at your leisure. If you design your programs the way you ought to, using a 'core' and ~ubroutines, then you qUl print each subroutine on a separate piece of paper. In this way, you can keep a note of each different subroutine, with variable names noted. On each sheet you can write what the subroutine does, what quantities are represented by the variable nallles, and how it is used. If you have a utility program that allows you to lllerge subroutines, You can then construct programs painlessly using your library of tested subroutines.

Printer types

Granted, then, that the use of a printer is a high priority for the serious cOInputer user, what sort of printers are available? The Commodore 128 in its natural state allows only Commodore printers to be attached, but thanks to the ingenuity of a number of independent suppliers, you can, for a small sum, now attach almost any printer you like to the Commodore 128. This opens up the way for the use of any of the printers which

10·1 are offered at such attractive prices in the magazines. In particular, the Printlink system allows the very popular range of printers that use the Centronics connection to be interfaced to your Commodore 128.

Printers that are used with small computers will use one of the mechanisms that are listed in Figure 10.1. Of these, the impact dot-matrix type is the most common. A dot matrix printer creates each character out of a set of dots, and when you look at the print closely, you can see the dot structure. The printhead of the dot-matrix printer consists of a set of tiny electromagnets, each of which acts on a set of needles that are arranged in a vertical line (Figure 10.2). By firing these needles at an inked ribbon which is placed between the head and the paper, dots can be marked on the paper. Each character is printed by firing certain needles, moving the head slightly, then firing another set of needles, and so on until the character shape is completely drawn (Figure 10.3). The most common pattern of dots for low-cost printers is 7 x 5, meaning that the characters are made up out of up to seven dots vertically and up to five horizontally. This implies that the head moves across the paper in five steps to print each character, and that up to seven needles can be fired. Using a 7xS structure gives characters which are readable, but not good-looking. The dots are very evident, and some of the letters are mis-shapen. You will fmd, for example, that lower-case letters lack 'descenders'. This means the tails on letters y, g, p, q will either be missing, or will be on the same level as the foot of other letters. When this print is used for listings which are in upper-case only, there is no problem. You would not, however, use a printer of this class to print letters or other documents that anyone else would have to read. Dot matrix impact thermal electrostatic

Type impact type stalk daisywheel

Plotters graphics plotters X-Y plotters

Ink-jet single colour multicolour

The main printer mechanisms. This excludes varieties such as the laser printer which are at present priced well beyond the reach of most computer owners.

FIGURE 10.1

Rather better results can be obtained if the number of needles in the printhead is increased. Using 9x9 (nine needles, nine steps across) or lSx9 heads can create much better-looking characters, both lower- and upper-case. Another advantage of these printheads is that the characters are not limited to the ordinary letters of the alphabet and numbers. Foreign characters can usually be printed, and it is possible to print Arabic

10·2 0-- ..

Yiewfrom the paper

A magnified view of a dot-matrix printhead with its vertica1line of independently controlled needles.

FIGURE 10.2 .

./1\\-- ...... • four steps offormatioB I I \ \ 6 2 2 3 - Beedles fired -

How the shape of a character can be drawn on paper by rlI'ing different needles as the head moves across the paper.

FIGURE 10.3 script, or even to make up your own character set. Most of the dot-matrix printers are impact types. This means that the paper is marked by the impact of a needle on an inked ribbon which hits the paper. There are also thermal and electrostatic dot-matrix printers. These use needles, but the needles do not move. Instead the needles are used to affect a special type of paper. In the electrostatic printer the needles are used to pass sparks to the paper, removing a thin coating of metal from the black backing paper. The thermal type of printer uses hot metallic pads to make marks on heat-sensitive paper. Both of these printers require expensive special paper, and are unsuitable for serious business purposes, so we won't spend any time on them here. If you want a cheap printer for listings, there are better methods.

The ultimate in print quality at the moment is provided by the daisy-wheel printer. This uses a typewriter approach, with the letters and other characters placed on stalks round a wheel. The principle is that the wheel spins to get the letter that you want at the top, and then a small hammer hits the back of the letter, pressing it against the ribbon and on to

10·3 the paper. Because this is exactly the same way as a typewriter produces text, the quality of print is very high. It's also possible now to buy a combination of typewriter and daisy-wheel printer. This looks like a typewriter, with a normal typewriter keyboard, but has an interface connection for a computer. You can use it as a typewriter, and then connect it to the computer and use it as a printer. Machines of this sort are made by leading typewriter manufacturers such as Silver Reed, Brother, Triumph-Adler, Smith­ Corona and others. If you need a typewriter as well as a printer, then this type of machine is an obvious choice.

Interfaces

The printer has to be connected by a cable to the computer, so that signals can be passed in each direction. The computer will pass to the printer the signals that make the printer produce characters on the paper, but the printer must also be able to pass signals to the computer. This is because the printer operates much more slowly than the computer. Unless the printer contains a large memory 'buffer', so that it can store all the signals from the computer and then get to work on them at its own pace, some sort of 'handshaking' is needed. This means that the printer will accept as many signals as its memory will take, and will then send out a signal to the computer which makes the computer wait before sending further characters. When the printer has completed a number of characters, (one line, one thousand, or possibly just one character), it changes the 'handshake' Signal, and the computer sends another batch. This continues until all of the text has been printed. This can mean that you don't have the use of the computer until the printer has finished. Printers can be very slow, particularly daisy-wheel and plotter types. Even the fastest dot-matrix printers can make you wait for a minute or more for a listing, and a printer-buffer is very useful.

10-4 Two types of interface are used by practically all printers. These are classed as serial and parallel.

A parallel printer is connected to the computer by a cable which uses a large number of separate wires. Since each character in ASCII code uses seven signals, the parallel printer sends these along seven separate wires - many printers can use an eighth signal and this is usually sent as well. In addition, there are wires for the handshake signals. The best-known, and most-used variety of parallel connection is called Centronics, after the printer manufacturer which fIrst used it. Practically all of the popular printers use this type of parallel interface.

The serial interface sends the signals out one at a time. This means that at least seven signals have to be sent for each character, and in practice this will be as many as ten or eleven, to allow for the 'start and stop' signals which are used to mark where the signals for each character start and stop. This system uses fewer wires because only two strands need to be used for signals, and the cables can be longer, because there's no risk of one signal interfering with another. The standard system is called 'RS-232'. Printers can be obtained with RS-232, but seldom as standard, and often only as an extra, costing up to £50 extra. The Commodore 128 uses a serial system, but it is not one of the standard forms, so only Commodore printers can be connected unless you attach an iflterface. The details of attaching and using such printers are available when you buy them, and there is no point in repeating them here. .

The range of Commodore printers is fairly large, and you may very well feel that there is no need for you to look at any other printer types. The non-standard interface is a disadvantage, however. Most computers can make use of printers which have the Centronics parallel interface, and it is a great advantage to have this. For one thing, it allows you to use the most popular printers that are best value for money. For another, if you ever change machines, you will not have to part with a printer which uses a Centronics interface, because it will work with practically all other computers. The Commodore printers are modilled versions of the popular printers which are designed to work with Commodore machines only. FortunatelY, it is comparatively easy to convert your Commodore 128 so that it can be connected to a Centronics style of interface.

10-5 The Centronics interface

Several suppliers advertise Centronics interfaces for the Commodore 128. These consist of a cable which plugs into the socket at the back left-hand side of the computer, and which has a standard Centronics printer connector at the other end. This is not enough for printing, however, since you need a program in machine code to send the correct signals to the printer. This can be supplied on cassette or on disk. At the time of writing, the best buy in such interfaces was from Microport, whose address is listed in Appendix D. If you want to fmd the current price for the disk and the cable, you should contact Mick Bignell at that address. When the program runs, machine code is placed into bank 1 memory, and the Commodore 128 will drive a printer with a Centronics interface. This does not necessarily mean that the graphics can be reproduced, because this depends on what printer you are using, so if you insist on using graphics in your programs, you should make them of the CH R$ ( n n) variety, rather than by using the graphics keys. The listings in this book were printed on an EPSON RX80 printer. This uses a 9x9 matrix for characters, so that the appearance of the characters is better. In addition, the EPSON can operate in 'emphasised' mode. In this printing mode, each dot is struck twice, but the head is shifted slightly between dots. This causes the dots to look almost joined up, and makes the appearance of the print much more acceptable.

A problem that you are bound to run up against when you use any non-Commodore printer is that of line feed and carriage return. A lot of computers send out only one code number, the carriage return code (13) at the end of a line. Other machines send both the line feed (code 10) and carriage return codes. Printers are arranged, therefore, so that either possibility can be catered for by altering the position of an internal switch. If you connect your printer and fmd that everything is printed on one line (as may happen when the Commodore 128 is driving the printer), then don't return the printer. Just look in the manual, and fmd the switch that alters the line-feed setting. If, on the other hand you are using another computer with your printer and you fmd that each line is double-spaced, then this switch will have to be set to the opposite position

The EPSON RX80 and LX80

The EPSON range of printers has for a long time been the most popular range of moderately-priced printers, offering good print quality at reasonable prices. The LX80 is one of the latest in this line, superseding the RX80, but if you are offered a second-hand MX80, then this is also likely to be a good buy. A particular feature of the EPSON range is that the print heads can easily be replaced when they wear out. Myoid EPSON MX80 is just beginning to show signs of head wear after printing half-a-million words, so it might not be a problem for you!

The standard version of the RX80 uses a pin-feed paper drive, but the RX80FIT can take any form or paper, including rolls. You have to pay extra for a paper roll holder, but if you are handy with wood, this is something that you could easily make for yourself. The advantage of using the FIT version is that plain unperforated paper rolls are very

10·6 much cheaper to buy, and it also means that you can use plain paper sheets if you want to. When you use a lot of paper for listings, this can be a great saving. Paper width of 4" to 10" in pin-feed can be used, so you can buy whatever paper size is on offer. If you use the FIT option, you can then buy teletype rolls, which are eight and a half inches wide.

The LX80 and RX80 offer a full set of upper or lower-case letters, and you don't have to go through any elaborate antics to select which one you want. The print speed of the normal text is very fast, and a most listings will be completed in under a minute, though the text is not good enough for reproduction in a book. The emphasised text can be obtained by entering: PRINT#3,CHR$(27)CHR$(69) before starting the listing. The emphasised print can be cancelled with: PRINT#3,CHR$(27)CHR$(70) These commands can be used in programs, so that you can print normal, condensed, emphasised, double width, and all of the other varieties, under program control. This makes it very easy to produce good headings, to produce words in bold type or italics, and to underline. For a lot of word-processing actions, the LX80 or RX80 can be a very satisfactory low-cost alternative to a daisy-wheel. International character sets (U.S.A., France, Germany, England, Denmark, Sweden, Italy, Spain, Japan, Norway) can be printed, and are selected under software control. This means that selection is done by printing CH R$ numbers rather than by altering switches on the printer itself. The only switches on the RX80 that you have to alter are for such items as are listed in Figure 10.4. For a lot of purposes, you would probably never need to alter the factory settings of these switches. Figure 10.5 shows the options that can be selected by sending CH R $ ( 27 ) C H R$ ( N) codes to the RX80 printer.

Switch 1 Position ON OFF 1 Condensed Pica (print size) 2 Graphics Control code 3 No buzzer Buzzer on (end of paper) 4 12 inch 11 inch (form length) 5 Not detected Detected (paper end) 6 Selects from international 7 character set of 8 eight languages

Switch 2 Position ON OFF 1 Slashed Non-slashed (zero) 2 Control pin Not fixed 3 Line feed No line feed (with CR) 4 Skip Don't skip (perforation)

The main switch settings of the Epson RX-80.

FIGURE 10.4

10-7 Each of the letter codes will be preceded by CH R $ ( 27 ) , the ESCape code. Some of the CH R $ ( n u mb e r) codes can be used alone - consult the manual for details.

Code Effect

J Adjust line spacing in 1/216 inch units M Elite size characters P Pica size characters CHRS(14) Enlarged print CHRS(20) Cancel enlarged print W Second enlarged print mode CHRS(1S) Condensed print CHRS(1S) Cancel condensed print Underline on/off switch E Set emphasised mode F Cancel emphasised mode G Double strike mode H Cancel double strike mode S Superscript/subscript mode T Cancel superscript/subscript CHRS(S) Backspace CHRS(4) Alternate character set CHRS(S) Cancel alternate character set m Choose graphics or control characters o 1/8 inch line spacing 1 7/72 inch line spacing 2 1/6 inch line spacing 3 Set spacing in 1/216 inch units A Set line spacing in 1172 inch units CHRS(9) Horizontal tab CHRS(11) Vertical tab e Tab unit setting f Skip position setting C Form length setting N Skip over perforation setting o Skip OVer perforation cancel Q Right margin set I Left margin set 8 Ignore paper end detector 9 Enable paper end detector < One line unidirectional printing Iil Restore normal settings U Unidirectional printing S Half speed (quiet!) printipg

Other codes can be used to control each pin in the head so that graphics can be printed. This allows 'screen dump' programs which place a copy of the screen graphics on to the paper to be written for this printer.

The selection options that use the ESC (ASCII 27) codes.

FIGURE 10.5

10-8 The Joki 6100 Daisywheel

TheJuki was one of the first low-cost daisywheel printers to become available. Like most printers, it comes with a Centronics parallel interface, although an RS-232 serial interface is available at extra cost. TheJuki is a very large and heavy machine which can accept paper up to 13" wide. The daisy-wheel is of the same type as is used on Triumph-Adler printers, and the ribbon cartridge is an IBM Selectric 82/C type. The ribbon that was supplied with my Juki was of the single-strike variety, and this had a very short life (about three chapters of this book!). A multistrike type of ribbon is much better. These ribbons are very easy to obtain from a lot of suppliers. The ribbons are carbon fIlm rather than inked nylon, and are thrown away after use.

The printhead of the Juki will print in either direction, and there is a 2K buffer. This means that short pieces of text can be transferred to the printer buffer almost instantaneously, and the computer can be used for other purposes while the printer gets on with the printing actions. Printing is much slower than the normal rate of the EPSON, but not so much slower than the double emphasised mode of the EPSON as to make the daisywheel seem irritatingly slow. Its enormous advantage is the quality of the type. This is exceptionally clear on the top copy, and even three carbons later it is still very legible. For any letter work, or for the manuscript of a book, the Juki is ideal.

As you would expect of any modem design of printer, the Juki permits a lot of character sets, but you need to have the appropriate daisy-wheels fitted for each language. You cannot, for example, have words in varying character sets without changing wheels in between. Changing wheels is simple, but this is something that you don't have to worry about with dot-matrix printers, because the same dot-matrix head can produce any character under software control. The Juki allows underlining, bold type, and shadow type in addition to the normal printing style, and you can select your print style from a range of at least fourteen daisy wheels. The daisy wheels were at one time expensive in comparison to others on the market, but ribbons are cheap. As a compensation for their price, the daisywheels appear to have a very long life, and their prices have recently dropped.

10-9 Each of these codes will be preceded by CH R $ ( 2 7 ) •

Code Effect

1 Set horizontal tab (HT) at present position 2 Clear all tabs 3 Graphics mode on (CIR clears) 4 Graphics mode off 5 Forward print on (CIR clears) 6 Backward print on (CIR clears) 7 Print suppress on (CIR clears) 8 Clear present HT stop 9 Set left margin at present position o Set right margin at present position CHR$(9) Set HT (tab number follows) CHR$(10) Set lines per page (number follows) CHR$(1n Vertical tab (VT) set (number follows) CHR$(12) Set lines per page (number follows) Sets VT at present position CHR$(13)P Remote reset CHR$(30) Sets line spacing (number follows) CHR$(31) Sets character spacing C Clears toplbottom margins D Reverse half-line feed U Normal half-line feed L Set bottom margin at present position T Set top margin at present position Y Special character Z Special character H Special character (new paragraph symbol) I English pound sign J Diaeresis mark K Spanish c with cedilla I Automatic backward print \ Disable backward print S Set character spacing CHR$(26)A Remote error reset CHR$(26)I Initia1ise printer CHR$(26)I Status (serial interface only) P Proportional spacing on Q Proportional spacing off CHR$(17) Offset selection E Underline on R Underline off o Bold print on (C/R clears) W Shadow print on (CIR clears) & Bold or shadow print off % Carriage settling time N Clear carriage settling time CHR$(8) 11120 inch backspace X Cancels all word processing modes except proportional spacing

The Juki daisywheel selection codes, each of which follows CH R $ ( 27 ) .

FIGURE 10.6

10-10 Like the EPSON, the Juki permits a number of changes to be made simply by sending control codes to the printer. These use the ESC character, CH R $ ( 27) followed by one more character, so that whatever immediately follows CH R $ ( 27) is never printed. The options include graphics mode, left and right margins, lines per page, half-line feeds in either direction (for printing subscripts and superscripts), top and bottom page margins, and some special characters, including the English pound sign (£). Even more usefully, the print can be changed to bold or shadow by sending such codes, and text can be underlined. Figure 10.6 lists these actions.

The same quality of print can now be obtained from a large number of daisywheel typewriters, and many of these can now be obtained with a Centronics parallel interface. This type of machine offers a lot of advantages, because it can be used as a typewriter for small items that do not justify the use of the computer, yet is available for word-processing use along with the Commodore 128 and such programs as Viza-write. These machines can now be bought in many high-street stores as well as from office supply shops. The only thing to watch is that replacement ribbons and daisywheels are obtainable from several different sources. There's nothing worse that being stuck with a machine for which you can get spares from only one supplier.

10·11

Appendix A

Tuning a TV receiver

For some purposes, the use of a TV receiver along with your Commodore 128 can be very useful. If, for example, you have bought the Commodore 128 for business purposes, or for running any CP/M software, you will fmd the green-screen monitor much easier on your eyes for the 80-column screen display than a colour monitor. If you sometimes want to make use of low-resolution colour displays, then a TV receiver is a low-cost alternative to having both a green-screen and a colour monitor. To make use of a TV receiver, however, you need to connect a TV cable from the TV outlet on the rear of the Commodore 128 into the aerial (antenna) socket of a TV receiver. Note, however, that though it's convenient to be able to use a TV receiver (and cheap as well), the picture that you see is of a poor standard. This is because a TV receiver was never designed to accept computer signals, and also because of the need to convert the computer's signals into the same form as transmitted TV signals. The result is that the letters and other shapes on the screen look fuzzy, and the colours look streaky. Unless you can keep a TV receiver specially for use with the 128, you will fmd that you have to keep plugging and unplugging the aerial cable and the 128 cable. This is never a good thing to have to do, because it loosens the contacts of the socket on the TV, and a useful alternative (in the UK) is to use the type of adaptor that is illustrated inFigure AI. This allows you to plug a

Lead from computer in here

Plugs into TV

A typical TV cable adaptor that allows you to keep both the aerial lead and the computer lead plugged in.

FIGURE Al

A-I lead into the aerial socket of the TV so that the TV can be used both for Commodore 128 and for Dynasty without having to pull plugs out. The two-way TV adaptor that I used is sold in TV stores under the name of Panda. Corresponding adaptor designs are available for the U.S. type of twin-lead TV cable. If the TV lead is too short for you, you can buy extension pieces of cable, and cable joiners. If you have used the adaptor, then all that you have to do to change between computing and TV watching is to change channels.

The next step now is to switch on the TV receiver and the Commodore 128. Unless you are exceptionally lucky you will probably see nothing appear on the screen. This is because a TV receiver has to be tuned to the signal from the Commodore 128 . Unless you have been using a video cassette recorder, and the TV has a tuning button that is marked 'VCR' it's unlikely that you will be able to get the Commodore 128 tuning signal to appear on the screen of the TV simply by pressing tuning buttons. The next step, then, is to tune the TV to the Commodore 128 signals.

Figure A2 shows the main method that is used for tuning modem TV receivers in Europe, using touch pads or very small push-buttons for selecting transmissions. These are used for selection only, not for l1111ing. The tuning is carried out by a set of miniature knobs or wheels located behind a panel which may be at the side or at the front of the receiver (Figure A2), or sometimes in a miniature drawer. The buttons or touch pads are usually numbered, and corresponding numbers are marked on the tuning wheels or knobs. Use the highest number available (usually 6 or 12), press the pad or button for this number, and then fmd the knob or wheel which also carries this number and tune in by turning it. What you are looking for is a clear picture on the screen and silence from the loudspeaker. On this type of receiver, the picture is usually 'fme-tuned' automatically when you put the cover back on the tuning panel, so don't leave it off. If you do, the receiver's circuits that keep it in tune can't operate, and you will fmd that the

Selector switch-press

Adjusting wheel (turn to tune)

The tuning method that is used for most modem TV receivers.

FIGUREA2

A·2 tuning alters, so that you have to keep retuning. The Commodore 128 should give a good picture on practically any TV receiver. If your TV exhibits faUlts like a shaking picture, or very blurred colours, then check the tuning carefully. If the faults persist, and the TV is correctly tuned, you will have to contact the service agents for the TV - or use a different model in future!

A·3

AppendixB

Standard form

Standard form is a method of writing and working with numbers that are either very large or very small. The principle is that only a limited number of digits in a number are ever really significant. For example, if a poll calls for 10,000 people to be questioned, it does not really matter very much if the number happens to be 9990 or 10,010. Taking another example, a number like 0.000000126842314 might just as well be written as 0.000000127 unless there is some special need to use more then three significant figures. To see why standard form is used, consider the number 1,000,000. If you wanted to multiply this by 1.5 you would not, I hope, go through the stages of saying: 'one point five times zero is zero, one point five times zero is zero . . .' six times before coming to 'one point five times one is one point five'. You would, of course, simply say: 'one point five times a million is one point five million'. This is what standard form is all about. Each number, when put into standard form, consists of two parts. One part is a number that is between 1 and 10, and which will use as many significant figures as needed. If you settled for three figures following the decimal point, for example, you might use numbers like 1.776, 5.204, 9.917 and so on. The second part of each number is a power of ten. This is just a way of writing the zeros of the number, and showing whether it is greater than or less than unity. The multiplier 10 would be written as El, (a single zero), 100 as E2 (two zeros) 1000 as E3 (three zeros) and so on. Fractions are written with a negative sign and a number that is one more than the number of zeros. This the multiplier 0.01 is written as B-2, 0.001 as B-3 and so on. Looking at an example, a complete number in standard form might be written as 1.617E3. This is the standard form of the number 1617. Similarly the number 4. 116B-3 is the standard form of the number 0.004116.

To put a number into standard form

1. Shift the decimal point until the number lies between 1 and 10. Count the number of decimal places, and note if the shift is left or right. 2. Write the number that you now have, and chop off, or round off, all digits beyond the limit that you have decided on. 3. Write down the E, and if you have had to shift the decimal point right, add a minus sign. 4. Follow this with the number of places that you had to shift the decimal point.

B·l Examples

1 814 519 371 402 The number of figures to be used is 5. Shift the point 12 places left, and write down five figures, getting 1.8145. Since the point was shifted 12 places, the number is 1.8145E12.

0.000 000 000 141 536 The number of places to be used is 4. Shift the point ten places right and chop to four figures, getting 1.415. Because the shift was often places right, the multiplier is E-I0, so that the number in standard form is 1.415E-I0.

Converting back from standard form

1. If the sign that follows the E is negative, then the point is moved left, otherwise it is moved to the right. 2. Move the point, fIlling out with zeros where there are no digits to use.

Examples

3. 124E8. Move eight places right to get 312 400 000 - the five zeros are used to fill in spaces.

4. 198E-4 Move four places left, inserting zeros to get .000419 8

B-2 AppendixC

Boolean actions

The logic actions of AND, OR and NOT appear at fIrst sight to have very odd effects when used on numbers. The effects can be understood only if you know how numbers are stored in binary form, and if this is a closed book to you, then I suggest that you look at a book on machine code programming relating to the Commodore 128 or the C64. The important point as far as we are concerned here is that the Commodore 128 stores all numbers in binary form, and the logic actions work on these binary numbers. If you type NOT ( 7) for example, then you are acting on the binary form of 7, which is 0000000000000111. The action of NOT is to change each 1 to 0 and each 0 to 1, so that the result is 1111111111111000. This is the number 65528 in denary, but the Commodore 128 follows the normal convention that any number above 32767 is a negative number, and its true value is obtained by subtracting 65536. This gives -8, which is the result of the command ? NOT ( 7 ) . Figure Cl shows how the actions of OR and AND can be interpreted for numbers.

(a) Bits Bit 1 Bit 2 Result

0 AND 0 0 0 . 1 0 1 0 0 1 1 1

Bit 1 Bit 2 Result

0 OR 0 0 0 1 1 1 0 1 1 1 1

The actions of AND, OR and XOR on numbers. The basic action is a bit action, and bytes are treated bit by bit, as the examples show. The XOR action is obtained with a different syntax, such as PRINT XOR(109,155>'

FIGURECI

C-l Bit 1 Bit 2 Result

XOR 0 0 0 0 1 1 1 0 1 1 1 0

(b) Bytes

Denary

AND

10110101 181 11110000 AND 240 gives 10110000 176

OR

11010011 211 10000101 ORl33 . gives 11010111 215

XOR

01101101 109 10011011 XOR ISS gives 11110110 246

FIGURE Cl (continued)

C-2 AppendixD

Suppliers

If you have not been a customer for disks, paper and other supplies before, you may have some difficulty in deciding where to look. The suppliers listed below have either supplied me regularly or with selected items used in the preparation of this book. I have no hesitation in recommending them to you if you are looking for a source of these items.

Mick Bignell Microport 7 Clydesdale Close Borehamwood Herts. WD62SD Tel: 01-953-8385 supplies the Centronics interface, PRINTLINK 128, on disk, along with other software of considerable interest to the more advanced user.

Pinner Wordpro 34 Cannonbury Avenue Pinner, Middx. HA51TS Tel: 01-868-9548 supply disks at very competitive prices, with fast delivery, along with disk accessories, printers, ribbons, paper etc.

Willis Computer Supplies Ltd. POBox 10 South Mill Road Bishop's Stonford Herts. CM23 3DN Tel: 0279-506491 supply a very wide range of paper, ribbons, printwhee1s, office furniture and accessories. Almost all computer supplies are catered for, and small orders are accepted - but please don't ask them to send you one ribbon!

D·l

Index

A Accuracy of numbers 4.12 AND CI AND/OR/NOT 4.2 Angles 4.10 APPEND 7.7 ARCCOS 4.11 ARCTAN 4.11 Arguments 4.9 Arrays - multi-dimension 5.12 string 5.7 of numbers 5.1 ASCII 1.9,4.1 ATN 4.10 AUTO 6.15

B Background colours 3.14 Backup copies 2.2,2.14 BANK 5.8 Bank numbers 4.10 Bar charts 9.9 BEGIN -- BEND 4.15,5.6 Binary chop 5.6 BLOAD and BSAVE 1.3 Blocks (disks) 2.5 Boolean actions CI BOX 9.9 Brackets and precedence 4.4 Buffer (print) 10.4 Buffer (me)· 7.3 c C language IA.I Calculators 1.6 Care of disks 2.10 Cassettes 2.0 I CATALOG 2.9,2.13

(ix) Centering strings 3.12 Centronics interface (printer) 10.5 - 10.6 CHAR 3.2,3.13,3.16 CIRCLE 9.13 C LS 3.1 COLLECT 2.18,2.20 COLOR 3.15 Colours 3.14,9.2 Comma as decimal point 3.11 Compiled programs 1.9 CONCAT 7.9 COpy 2.16 Copying fIles 2.16 COS 4.9 Crash (disk) 2.13 Creating a fIle 8.8 Cursor movement 3.1 D Daisy wheel printers 10.2, 10.9 DA T A with menus 6.8 Database 7.10 explanation 8.5 programs 1.8 Dates 4.15 DC LEAR 2.13 Decimal notation Bl DEF FN 4.8 Defmed functions 4.8 Deleting fIles 2.13,2.16 Deriving formulae 4.4 Descenders (printers) 10.2 Designing programs 6.1,6.9 DIRE CTORY 2.9,2.13 Disk - ID 2.8 backups 2.14 care 2.10 crashes 2.13 error messages 2.14 faults 2.6 - 2.7 filing system (DFS) 2.3, 2.8-2.9 formatting 2.7 hardware 2.3 lights 2.11 space 1.2, 2.9 structure 2.4

(x) tidying 2.18 types 2.3 utilities 2.15 Displaying data 9.1 oLOA 0 2.12, 7.1 DO ... - WH I LE 3.13 Dope block 5.7 DOPEN 7.3 -and printing to fIle 7.4 DOS disk 2.3 Dot matrix printers 10.2 DRAW 9.2,9.4 DSAVE 1.5,2.12,7.1 E Edit subroutine 6.15 End of fIle (EOF) 7.4,7.7 Entry subroutine 8.11 EO F see End of File Epson printers 10.6 Erasing fIles 2.13,2.16 Error handling (disks) 2.14 ESCape codes (printers) 10.8 EXI T 6.2 Exponents 4.13, Bl

F False and True 4.2 FAST 5.3 Faults on disks 2.7 Fields - print 3.3 F I LEE X 1ST S error 2.16 FILE NOT FOUND error 2.12,2.14 Files 7.1 buffers 7.3 creation 8.8 field sizes 7.12 key 8.5 multiple 8.5 names 2.12, 7.1 opening 7.3 pointers 7.13 printing to 7.4 read 7.5, 8.10 relative 7.10

(xi) searching 7.15 types 7.2 updating 7.7 using variables 2.20 wildcards 2.17 write to 8.9 Filled graph 9.5 Floating pound symbol 3.9 Flowcharts 1.7 Foreign languages 3.11 Formatting disks 2.6 - 2.7 Formatting money amounts 3.9 numbers 3.4 Formulae - deriving 4.4 Fortran language 4.4 Friction and tractor feed (printers) 10.6 Functions 4.8 - 4.9 graphs of 9.9

G GET 6.8 GOSUB 6.4 Graphs 9.1 GRAPHIC 9.3 Graphics 9.1 and text 9.8 Graph program 9.3

H Hard copy (printouts) 3.17 Hardware and Software 2.3 HEADER 2.8,2.18 Hexadecimal numbers 4.10

I ID (disks) 2.8 ILLEGAL QUANTITY error 4.12, 4.14 Impact printers 10.2 INDEX (disk) 2.13,2.20 Index marker (disks) 2.6 Ink-jet printers 10.2

(xii) Input range limits 6.2 Input subroutines 6.13 Interface - TV Al printer 10.4

L

Languages 1.8 Lights on disk drive 2.11 Limiting an input range 6.2 Line feed (printers) 10.6 Lists 5.1 LOAD 2.12 Logarithms 4.11 Logical operators C1 LOOP BACK 1.7

M Machine code 1.2 Magnetism and disks 2.4 Mantissa 4.13 Matrix operations 5.12 Memory usage 1.2 Menus 6.1 Message windows 3.15 Mixing text and graphics 9.8 Money - formatting 3.9 MONITOR 1.3 Monitor - leaving 1.3 Motor control (tape) 2.01 Moving the cursor 3.1 Multiple fIles 8.5

N Names - programs 2.2 fIles 7.1 Negative exponents 3.9 NOT 4.2 Numbers - arrays 5.1 formatting 3.4 in strings 4.14 operations on 4.1 precision 4.12

(xiii) rounding 4.13 - 4.14 standard form Bl o ON • • • GOTO 6.1 ON • • • GOSUB 6.1 Opening flIes 7.3 Operators 4.1 and precedence 4.4 logical Cl OR 4.2, Cl OUT 0 F DATA mor trap 6.8

p PAINT 9.5,9.13 Parallel and serial printers 10.5 Pascal language 5.7, A.l Pie diagrams 9.13 Planning a program 6.9 Point graph 9.4 POINTER 5.8 Pointers (flIes) 7.13 Pound symbol 3.9 Precedence and brackets 4.4 Precision of numbers 4.12 PRINT 1.9,3.2 with printer 3.17 PRINT# (flIes) 7.3 PRINTAT (LL% instead of) 3.2 Print buffer 10.4 fielding 3.3 Printer - daisywheel 10.9 ESCape codes 10.8 interfaces 10.4 switches 10.7 types 10.2 Printing to flIes 7.4 Printouts 3.17 PRINT USING 3.3,3.17 Program design 6.1,6.9 names 2.2 storage 1.3 Programming - in sections 6.4 principles 1.5 saving parts of 6.12 Protecting disks 2.19

(xi\') R Random access flles 7.2 Ranges of numbers (graphs 9.9 Reading flles 7.5, 8.10 RE CORD NOT PRE SENT error 7.12 Relationships (numeric) 4.1 Relative flles 7.2,7.10 Remainders (numeric) 4.12 RENAME 2.19-2.20,7.9 Renaming flles 2.13,2.19 REPEAT 1.7 RESTORE 3.5 RF socket 1.1 RGB signal 1.1 Rounding numbers 3.5,4.13 - 4.14 RS-232 interface 10.5 s SAVE 2.12 failure 2.13 SCALE 9.8 S CNC LR 3.1 SCNCLRO 9.5 SCRATCH 2.16,7.9 Screen - clearing 3.1 titles 3.12 Searching fues 7.15 Sectors (disks) 2.3 Sequence (in programming) 1.6 Serial and parallel printers 10.5 Serial flles 7.2 Series fonn.ulae 4.8 Shell-Metzner sort 6.17 SIN 4.9 Single and double sided disks 2.7 SLEEP 3.12,9.17 SLOW 5.3 Soft sectors 2.6 Sorting 6.17 numbers 5.3 strings 5.9 SOUND 9.16 SPC 3.3 Stack limits 4.8 Storage space (disks) 2.9 String arrays 5.7

(xv) Strings for numbers 4.14 STRING TOO LONG error 4.1 Subroutines 6.1,6.12, 8.8 Subroutine - editing 6.15 entry 8.11 input 6.13 warble 9.17 Switching on and off 2.11 Switches on printer 10.7 Sync signal 1.1 SYS 1.5

T TAB 3.3,3.12 (on printers) 3.17 TAN 4.9 Text and graphics 9.8 editing 2.01 Tidying a disk 2.18 Titles 3.12 TPI (Tracks Per Inch) 2.5 Tracks (disks) 2.3 number of 2.5 TRAP 4.12,5.3 Trigonometric functions 9.9 TRUE and FALSE 4.2 TV 1.1 cable adaptor Al or monitor for sound 9.16 tuning Al T Y P E ft1 I S ft1 ATe H error 6.2

u UNDEFINED FN error 4.10 Updating a fIle 7.7 Utilities (disk) 2.15 v Variable - storage 1.2 as fIlename 2.20 Video output Updating a fIle VOL 9.17 VPOS 3.16

(xvi) w Waldorf salad 5.12 Warble subroutine (sound) 9.17 WH I LE (with DO) 1.10,3.13 Wildcard characters 2.17 WIN DOW in graphics 9.2 Windows 3.13 not moving 3.15 Write to fIle 8.9 Write-protecting disks 2.19

(xvii)