×   Main Menu ALL The 8BS News Manuals (New menu) Links Worth a Look Tools Disc and Basic Webring Site Map 8BS Guestbook Old Guest Book Me The Barnsley Rovers   
8-Bit Software

The BBC and Master Computer Public Domain Library

206 Hints and Tips From BBC PD 147


Go to Numerical Order

Go To Topical Order Index

1. BASIC editing


If you are editing a BASIC program, you can find out what is the last line number in the program as follows:
Hold down <SHIFT> and <CTRL> together, press <ESCAPE> twice in succession, and finally release <SHIFT> and <CTRL>. This even works on an Archimedes/A3000!

2. Listings

To stop listings and any other screen output* whizzing past too quickly, hold <SHIFT> and <CTRL> down together. Alternatively, from the BASIC prompt, press <CTRL> <N> to engage paged mode, and press <SHIFT> to continue scrolling. Cancel with <CTRL> <O>. This also works fine on an Archimedes.

* eg packet messages!

3. Filling up strings

It is sometimes necessary to fill-up or 'pad' out strings to a fixed length. For example, you may want numbers to print out as 0045, 0001 etc., or you may want to add leading or trailing spaces onto character strings. The first example gives the obvious method, whereas the second shows a more elegant method which avoids the use of an IF statement. The third example shows a similar method in the form of a Function, with the addition that you can specify the amount of padding and the padding character to be used - it doesn't have to be a number.

30 IF LEN(X$)=4 THEN X$="0"+X$:GOTO 30

30 X$=STRING$(4-LEN(X$),"0")+X$

10 INPUT"Enter up to 5 digits "A$
20 A$=FNpad(A$,5,"0"):PRINT A$:END
30 :
100 DEFFNpad(st$,len%,char$):=STRING$(len%-LEN(st$),char$)+st$

4. Randomize

To 'scramble' the Beeb's random number generator properly, you can simulate the RANDOMIZE function of some other BASICs. Incorporate the dummy expression randomize=RND(-TIME) at the beginning of your program. This also works on an Arc/A3000.

5. Self-validating input

Try incorporating this sort of input routine into your own program. There is nothing special about the particular line numbers used.

1000 PRINT"Do you want another game ? ";
1010 ON INSTR("YyNn",GET$) GOTO 1020,1020,1030,1030 ELSE 1010
1020 REM Back to start of program.
1030 END

6. Shortened 'IF' statement

In most cases, the statement IF A=0 THEN etc. can be shortened to just IF A etc. The "=0" is implied, provided A can only be zero, or +1 or more, or -1 or less. In the 'before' and 'after' example below, note the space after the variable "A" in the second version. This space would be unnecessary if the variable were "A%" instead.

100 IF A=0 THEN G=5:GOTO 70

100 IF A G=5:GOTO 70 (Note the space after the A!)

7. Simple bleep

If you want a short 'bleep' in your program, you can use VDU7 or PRINT CHR$(7) instead of a SOUND statement. VDU7,7 etc would give a longer bleep.

8. VDU7 bleep

You can alter the nature of the VDU7 'bleep' as follows. *FX210,1 turns it off, and *FX210 turns it on again. You can alter the pitch with *FX213,P where "P" is a number from 0 to 255, (default value is about 100). Similarly, you can alter the duration with *FX214,D (default value about 7). You can change sound channel or 'voice' with *FX211,N where "N" is 0 to 3, (normally 3). Thus, if you try *FX211,0 you will get a sort of explosion noise, and *FX211,3 will restore it to a bleep. You can test the bleep by pressing <CTRL>+<G> ; quicker than typing VDU7.

9. Key-pressed check

You can check to see if a specific key is being pressed at a particular instant with a negative INKEY statement. For example, you can test the space-bar with IF INKEY(-99) THEN... However, if you want to see if ANY key is being pressed, (though not the red keys or <CTRL> or <SHIFT>), then use this command. It works in the opposite sense to the other negative INKEY commands, hence the inclusion of the word"NOT". IF NOT INKEY(-129) THEN ...

10. Input line

An ordinary INPUT statement will strip any leading spaces off strings, and will not accept commas within a string. The latter is because you can use commas to separate your replies, instead of pressing <RETURN> each time, eg BLOGGS,45,MALE <RETURN>. You are reminded that the alternative command INPUTLINE will accept commas and leading spaces. You can easily strip the leading spaces off afterwards, if they are a problem.

11. String input function

This Function is an alternative to using a conventional INPUT statement for strings. You specify the minimum and maximum number of characters in the string, and the Function does the rest. The example given is for 2 and 6 characters respectively, whereas an INPUT statement is effectively for 0 and 255. You will find that you cannot press <RETURN> until you have typed in the minimum number of characters, and that you cannot type more than the maximum. Full use of the 'Delete' and 'Copy' keys is allowed.

This Function is quite tricky to type in without errors, so take care. I've only put spaces in for clarity, and you can omit them all except immediately before the word "ELSE". If you want the cursor to remain on the same line after <RETURN> is pressed on a valid string, then omit the final PRINT: in line 140, so that you have just ELSE =I$.

10 PRINT"Enter Name ";:name$=FNinp(2,6):GOTO 10
100 DEFFNinp(min%,max%):LOCALG%,G$,I$:*FX21,0
110 PRINT G$;
120 G$=GET$:G%=ASC(G$)
130 IF(LEN(I$)=max% AND G%<>13 AND G%<>127)OR(((LEN(I$)=0
AND(G%<33 OR G%>126))OR(LEN(I$)<min% AND G%=13))AND(min%>0
OR G%<>13))THEN VDU7:GOTO 120
140 IF G%>31 AND G%<127 THEN I$=I$+G$:GOTO 110 ELSE IF G%=127
THEN I$=LEFT$(I$,LEN(I$)-1):GOTO 110 ELSE IF G%<>13 THEN
VDU7:GOTO 120 ELSE PRINT:=I$

12. Number input function

This is a similar sort of Function for validating numbers which may be more than just a single digit. If the number entered doesn't fall within the required limits, the incorrect entry is erased ready for another try. If you are only interested in integers, then alter the variables figure, min and max by adding a "%" sign on the end. Wierd things happen if you muck about with the cursor editing keys, but you can easily temporarily disable them with *FX4,1 or *FX4,2. Like the previous Function, it really pays off in larger programs, as it saves you having to write separate validation routines for each and every input. You could save the Function with the *SPOOL facility, and *EXEC it into new programs as required. Again, you can omit spaces if you want.

10 CLS:PRINTTAB(7,10)"Enter Width ";:width=FNnumber(18.2,65.7)
20 END
100 DEFFNnumber(min,max):LOCAL P%,V%,figure
110 P%=POS:V%=VPOS:PRINT:REPEAT REPEAT VDU127:UNTIL POS=P% AND
VPOS=V%:INPUT""figure:UNTIL figure>=min AND figure<=max:
=figure

13. Status functions

The first is a useful Function which can be used to test the status of various aspects of the VDU drivers. For example, to check if the screen is in paged mode, then use IF FNstatus(2)=TRUE THEN... To check whether there is a text window defined, then use IF FNstatus(3)=TRUE THEN..., and so on. You can achieve the same result, though not in a proper Tube-compatible way, by Peeking location &D0. Indeed, the only way I know of disabling scrolling, (bit 1), is to directly Poke &D0 with ?&D0=?&D0 OR 2. (Enable it again with ?&D0=?&D0 AND 253.) The second Function returns the current graphic Mode number, and the third Function returns the ASCII code of the character at the present cursor position. You can move the cursor to the required position with PRINTTAB(x,y); or VDU31,x,y.

100 REM ** Returns TRUE if Bit is Set **
110 :
120 REM 0 - Printer enabled by VDU2
130 REM 1 - Scrolling disabled
140 REM 2 - Page Mode enabled by VDU14
150 REM 3 - Text Window defined by VDU28
160 REM 4 - Not used
170 REM 5 - Text/Graphics cursors joined by VDU5
180 REM 6 - Edit cursor in use
190 REM 7 - VDU drivers disabled by VDU21
200 :
210 DEF FNstatus(bit%):LOCAL A%
220 A%=&75:=-(((USR&FFF4 AND &FF00)DIV&100)AND2^bit%)DIV2^bit%

100 REM ** Returns Graphic Mode **
110 :
120 DEF FNmode:LOCAL A%
130 A%=&87:=(USR&FFF4 AND &FF0000)DIV &10000

100 REM ** Returns Character at
110 REM flashing cursor position **
120 :
130 DEF FNchar:LOCAL A%
140 A%=&87:=(USR&FFF4 AND &FF00)DIV &100

14. Star commands in a program

It is sometimes useful to be able to use the 'Star' Machine Operating System's commands from inside a program; eg *KEY, *DISC, *CAT etc. These routines, written as Procedures, will allow you to do just that. Line 10 is only to demonstrate the Procedure, which you would call from your main program. (The best way to do this to test for the '*' key being pressed. The Procedure itself does not require this, as "CAT" is assumed to be "*CAT".) If you want to *CAT a cassette, then you would have to press <Escape> to finish, so you need to use ON ERROR GOTO to avoid coming out of the program altogether. The LOCAL statement is so that you can use the variables X%, Y% and Z% in the rest of your program, without any clash. However, it might be wise to avoid them anyway, as Escaping from the Procedure could cause odd things to happen. You could substitute any integer variable for Z%, but you must use X% and Y%. The Procedure as it stands works on all BASIC versions, except BAS128 *, and with the alterations shown to the right, it works on all BASIC versions except BASIC-1.
* NB: BAS128 is for the B+/Master/Compact only.

10 PROCcommand:PRINT"Done it!":END
20 :
1000 DEFPROCcommand:LOCAL X%,Y%,Z% DEFPROCcommand:LOCAL C$
1010 DIM Z%-1:PRINT"Enter MOS command" PRINT"Enter MOS command"
1020 INPUT"*"$Z% INPUT"*"C$
1030 X%=Z%:Y%=Z%DIV256:CALL &FFF7 OSCLI(C$)
1040 ENDPROC ENDPROC

15. Alphabetic sort

This is a very fast BASIC Sort Procedure, known as a Shell-Metzner Sort. It is far faster than a simple Bubble Sort on long lists, and can sort a several hundred names in only a few seconds. It can just be beaten by the Quicksort published in the November 1982 Beebug. However, the Quicksort uses recursion, which means you can't easily add any bits of your own in the Procedure without the routine hanging up, so I prefer to stick with this one. It assumes that you have a list of names, or whatever, in an array called file$, (or A$ etc. if you prefer).

If you have DIMensioned the array as file$(300), and it's always full up, then you would use PROCsort(300), otherwise substitute a variable giving the last element with anything in it. If you want the list in descending alphabetical order, then change the "<=" in line 160 to ">=". As usual, all the variables except file$ itself are LOCAL, so can be used elsewhere without any clash.

Unless you 'fill up' the whole array initially with strings of the maximum length likely to be encountered, then the Sort is bound to gobble up memory in use. This isn't the fault of the Procedure, but is a quirk of BBC BASIC, known as poor 'Garbage Collecting'. The Procedure can easily be adapted to sort numbers instead of strings, in which case this problem doesn't arise.

100 DEFPROCsort(extent%)
110 LOCAL I%,J%,K%,L%,M%,swap$:M%=extent%
120 M%=M%DIV2:IF M%=0 THEN 200
130 K%=extent%-M%:J%=1
140 I%=J%
150 L%=I%+M%
160 IF file$(I%)<=file$(L%) THEN 190
170 swap$=file$(I%):file$(I%)=file$(L%):file$(L%)=swap$
180 I%=I%-M%:IF I%>0 THEN 150
190 J%=J%+1:IF J%>K% THEN 120 ELSE 140
200 ENDPROC

16. Program size

To see how long your BASIC program, excluding variable space etc., type PRINT TOP-PAGE <RETURN>, or PRINT~TOP-PAGE <RETURN> if you want it in Hex. This also works on an Arc, with the addition that you can use END-PAGE if you want to include any variable space used by the program after it has been run.

17. Program string search

This is a very useful routine to assist in writing and debugging programs. When you need to search for a character or string of characters in your main program, just press a red user key, enter the string you are looking for, and the routine will immediately tell you all the line numbers where that string occurs, taking only 3 seconds to search an 11k program. (It is very similar to the *FIND facility in Disc Doctor.) It is a greatly improved version of a program originally published in Beebug magazine. This version occupies only 256 bytes of memory, hides away out of sight, and will even survive pressing of the <BREAK> key. You can just forget all about it while you use your Micro for BASIC programs. The only limitation is that you cannot use it with relocated programs, but it works with or without a DFS.

First, press <Break> and then type in the program exactly as shown, even if it does look rather weird. However, you absolutely MUST omit ALL spaces, with the sole exception of the space inside the quotes in line 40. I have only inserted spaces after the line numbers for clarity, so don't put them in yourself. When you've done that, type PRINT~TOP-PAGE, and check that the answer is "FF". It it isn't, then look again!

If all is well, you can SAVE it first, and then type RUN. As soon as the"Enter String" prompt appears, press <Escape> and then type NEW again. You can then LOAD or type in another BASIC program, and use the micro in the usual way. The only thing is that you should make sure that the red user keys 'f9' and 'f10' are not redefined. When you need to search, press 'f9' and enter the search string. You can search for any character, variable name, Procedure or Function name, punctuation etc., (hence the use of INPUTLINE instead of INPUT, to permit commas). You may get the occasional spurious find for a single character, due to it being confused with tokens.

The only thing you cannot search for is a BASIC keyword such as PROC, FOR, IF etc., but you can search for *FX, *OPT etc.. If you do need to search for a keyword, then look up the appropriate Hex token on page 483 of the User Guide. Instead of typing in the keyword itself in response the the prompt, press <SHIFT> and <f9>, which will move the cursor one space to the right, and then type in the token exactly as given in the User Guide. For example,"COLOUR" has the token FB, so this is what you type in; no more, no less. If you have Teletext Mode 7 colour codes inside PRINT or REM statements, then these can occasionally be mistaken for tokens, but this isn't a big problem.

The program works in all modes, and is unaffected by <BREAK>. After pressing <BREAK>, both SEARCH and your main program will still be present; just type NEW if you want to type in another program. If you press <CTRL+BREAK>, then you will need to type OLD, and RUN again as if you had just started.

10 ONERRORGOTO100
20 *KEY9PAGE=PAGE-&100|MRUN|M
30 *KEY10OLD|MPAGE=PAGE+&100|M
40 B%=PAGE+&101:T%=&A00:INPUTLINE"Enter String",$T%
50 IF?T%=&89THEN$T%=CHR$EVAL("&"+$(T%+1))
60 REPEATN%=&100*?B%+B%?1:P%=B%+3:B%=P%+P%?-1-3
70 IFLEN$T%>LEN$P%THEN90
80 IFINSTR($P%,$T%)THENPRINTN%;
90 UNTIL?B%=&FF
100 PRINT:PAGE=PAGE+&100

18. Wacky error message

For an error message straight out of Monty Python, try to renumber a program by typing RENUMBER 1000,1000 <RETURN> .

Another good one happens in VIEW, if you type the command...

=>FIELD 27 <RETURN>

19. Lock out lower-case

On occasions, it can be useful for lower-case letters to be treated the same as upper-case in GET$ statements, to avoid having to test for "Y" and "y" for"YES", etc.. This Function will always return upper-case letters, even if the 'Caps Lock' isn't on. It is written as a Function, which you use:

1000 DEFFNget:=CHR$(GET AND &DF)

20. Pressing user keys in a program

You can effectively 'press' a red key on exit from a program, by inserting the right code into the keyboard buffer. In this example, 128 corresponds to key 'f0', 129 would be 'f1' etc.. The <BREAK> key is code 138, and note that this method does not actually cause a Break; this would require the key to be physically pressed by hand.

10 *KEY0 LIST|M
20 *FX138,0,128
30 PRINT"Here is a Listing..."
40 END

21. Press space-bar to continue

There are various ways of doing this. The first method looks at the keyboard buffer, so if the Space-bar has been pressed just before the message, it will carry straight on. If this is a problem, then either use the second variant with the *FX21,0 buffer clear command, or use the third version. This will still cause a Space to enter the keyboard buffer, so this is cleared with
*FX21,0 to avoid problems later.

Although written as Procedures, they don't have to be. Call them with PROCspace.

1000 DEFPROCspace:REPEAT UNTIL GET=32:ENDPROC

1000 DEFPROCspace:*FX21,0
1010 REPEAT UNTIL GET=32:ENDPROC

1000 DEFPROCspace:REPEAT UNTIL INKEY(-99):*FX21,0
1010 ENDPROC

22. Time delays

Apart from using a dummy FOR-NEXT loop, there are two main ways to put a time delay in programs. The first is to use the TIME facility, and the other is to use INKEY. The first TIME routine resets the TIME to zero on each occasion. If you wish to avoid this, then the second routine will overcome the problem. The INKEY routine will give a similar delay, except that the delay terminates on pressing any key. The example provides a 2 second delay, (200 centiseconds). Call them with PROCdelay.

1000 DEFPROCdelay:TIME=0:REPEAT UNTIL TIME=200:ENDPROC

1000 DEFPROCdelay:LOCAL now%: REPEAT UNTIL TIME=now%+200: ENDPROC

1000 DEFPROCdelay:LOCAL pause%:pause%=INKEY(200):ENDPROC

Another way is to call up whatever length delay you want, but still only using one Procedure. You can call up delays with PROCdelay(100), or PROCdelay(200) etc. as follows:-

1000 DEF PROCdelay(wait%):TIME=0:REPEAT UNTIL TIME=wait%:ENDPROC


TIME=now%+wait%:ENDPROC

1000 DEF PROCdelay(wait%):LOCAL pause%:pause%=INKEY(wait%):ENDPROC

Note the use of Integer variables for accuracy. The variable pause% is a dummy, to satisfy the INKEY syntax. The variable wait% is an implied LOCAL variable, so can be used elsewhere in the program quite independently, without any clash.

23. Printer output only

Sometimes it is handy to be able to send text to the printer only, without it appearing on the VDU screen at the same time. This can be done by using VDU2,21 to activate the printer, and VDU6,3 to turn it off again and to restore the VDU drivers. This can cause problems if you try to send anything other than normal text or figures. Control codes or graphic data may be corrupted due to the filtering of the "printer ignore character" specified by *FX6. *FX3,10 and *FX3,0 have a similar effect to these VDU commands.

24. Simulated duff TV

This routine gives the fairly convincing impression that your TV or monitor has gone duff, especially if you turn the brightness up a bit. If you have a Master or Compact, then the ",0;0;0;" bit can be replaced by the single character '|'. Someone suggested hiding it somewhere in a program, as a booby trap for your mates....

10 VDU23,0,RND(255),RND(255),0;0;0;: GOTO 10

25. Cube roots

A reminder for those of us who aren't too hot at Maths. In BBC BASIC, a square root can be obtained with A=SRQ(B), but how to get the cube root? The answer is that SQR is unnecessary, as you could use A=B^(1/2) instead. Thus the cube root is given by A=B^(1/3) etc.

26. Integer variables

Programs can be speeded up by using Integer variables where possible. Try running this program first using X, and then using X% instead. Using NEXT rather than NEXT X or NEXT X% makes it even faster; try it.

10 TIME=0:FOR X=1 TO 5000:NEXT X:PRINT TIME

27. Disc relocate

This routine, sometimes referred to rather confusingly as a 'downloader', enables programs to be run which are normally too large for use with a DFS fitted. It switches off the DFS with *TAPE, and shifts the whole program down to &E00 before running it. The screen output is temporarily inhibited with VDU21 during relocation, and reactivated afterwards with |F (VDU6), to give a tidy appearance. For debugging, the VDU21 may be omitted initially, as it also inhibits useful error messages! The routine will relocate BASIC programs, including those containing assembler. If there is also 'hidden' data and/or machine code, then it is easiest to alter the TOP in line 1 to HIMEM, although this makes the routine rather slower. Spaces are shown for clarity only, line 1 is split at a convenient point for the same reason.

0 IF PAGE=&E00 THEN 10
1 VDU21:*KEY9 *TAPE|M FOR I%=0 TO TOP-PAGE STEP 4: I%!&E00=I%!PAGE:NEXT|M PAGE=&E00|M NEW|M OLD|M RUN|F|M
2 *FX138,0,137
3 END : REM Most Important!
10 REM Start of your Program.

28. User port experimenting (1)

The User Port is controlled by a very versatile little chip called a 6522 VIA, (Versatile Interface Adapter), which also looks after the Centronics printer port. You have 20 connector pins available, consisting of 8 connected to 0 Volts, 2 connected to +5 Volts, 2 handshake lines, (CB1 and CB2), and the 8 Input/Output lines PB0 to PB7. These lines default as inputs fairly similar to the 74L series low-power TTL gates. If you check them with a multimeter, you will find that they float high to about +3 Volts, just like a
TTL input.

The status of these 8 lines is read into location &FE60, so if you interrogate it with PRINT ?&FE60, you will get 255 Decimal, which is FF in Hex and 11111111 in Binary. If you were to short-circuit PB0 to 0 Volts, then you would get 254 Decimal, or 11111110 Binary. Similarly, if you short PB1 instead, you get 253 or 11111101, and if you short the lot together, you get 0, or 00000000 in Binary. It will be obvious by now that it is convenient to 'look' at &FE60 in Binary, as it gives a direct representation of what is happening on the pins.

If you wish to test the status of one particular pin, ie one binary bit, then use the logical AND to 'mask' out the other bits, and then divide it by itself to turn it into a 1 or 0. Thus, to test PB1, use PRINT (?&FE60 AND 2)DIV2. More generally, to test pin PBn%, where n% is 0 to 7, use p%=2^n%:PRINT (?&FE60 AND p%)DIV p%. If you stick a minus sign in front of the expression (?&FE60 etc...), then it will return the value TRUE for logic high, and FALSE for 0. Always use integer variables to avoid silly rounding errors, and to increase speed.

This short program displays the status of the pins in Decimal, Hex and Binary, enabling you to watch what happens when you pull various inputs low. You can also drive them up to +5 Volts like any TTL gate, and they source only 0.8 mA when low, so you can drive them from +5 Volt CMOS logic if you want. If you do have an accident, a new 6522 VIA isn't too expensive!

10 MODE 7:VDU23,1,0;0;0;0;:REM * Cursor off *
20 PRINT TAB(5,8)"Decimal"TAB(16,8)"Hex"TAB(25,8)"Binary"
30 REPEAT port%=?&FE60:REM * Read Port *
40 PRINT TAB(7,10);port%" "TAB(16,10);~port%" "TAB(24,10);
50 FOR pb%=7 TO 0 STEP-1
60 power%=2^pb%:REM * Read each bit *
70 PRINT;(port% AND power%)DIV power%;
80 NEXT:UNTIL FALSE

29. User port experimenting (2)

The previous program examines the User Port inputs via location &FE60. However, by writing to &FE62 you can nominate pins as outputs instead, and they will default to logic LO, which you can check with the previous program, and confirm with a multimeter. This program asks you which bits are to be outputs, so enter 0237 or 134 or whichever bits you want, in any order. If you just press <RETURN> without entering anything, all pins will be reset as inputs. Strangely, so it might seem, you alter the output states by writing to &FE60; the pins set as inputs will ignore this, so no need to worry on that score. In other words, ?&FE60=255 would set all outputs to logic HI without affecting the inputs, and so if you PRINT ?&FE60 afterwards, you will see the effect. Try combining the two programs, and add a few bits of your own to make a more complete experimenting program.

10 PRINT ?&FE62:INPUT"Which ones for output? " output$
20 FOR pb%=0 TO 7:power%=2^pb%
30 IF INSTR(output$,STR$(pb%))=0 THEN ?&FE62=?&FE62 OR power%
ELSE ?&FE62=?&FE62 AND (255-power%)
40 NEXT:PRINT ?&FE62

30. Program verify

To see if a BASIC program has been successfully SAVEd to tape, without losing the current program from memory, type *LOAD "" 8000 <RETURN>, (note the empty quotes). This works well for programs of not more than 16k in length, as it is simply trying to 'write' into the BASIC ROM, which it cannot do. However, all the normal error-checking of the tape data takes place, accompanied by the usual "Searching" and "Loading" messages. If you have sideways RAM, then you should operate the write-protect switch to avoid garbage being written into the RAM.

31. Tape reset

If you have a DFS fitted, then pressing <Tab+Break> will cause a "warm" reset to the cassette filing system. This is quicker than pressing <Break> followed by typing *T. <RETURN>. The cursor will step a few spaces to the right, but this is harmless; press <RETURN> if it bothers you.

32. Unwanted spaces

It is all too easy to leave unwanted spaces in listings, especially by "overshooting" the ends of lines when using the 'Copy' key. If you are short of memory, it is worthwhile editing out those spaces.

You can spot them by redefining them as white blobs, and then using any Mode except 7, (6 is ideal). When you wish to remove spaces, type the following in directly in Mode 7, and then change mode. When you've edited out any unwanted spaces, change back to Mode 7 and SAVE the program. To restore normal spaces, type *FX20 <RETURN>.

VDU23,32,126,126,126,126,126,126,126,0

33. Basic-1 and Basic-2

In order to be able to check compatibility of programs, you may wish to retain the BASIC-1 ROM in your machine after obtaining BASIC-2. Simply put the old ROM in a lower-priority ROM socket, and switch to it when required with *FX142,n where 'n' is the socket number, (0 to 15). If you have a program in memory, then type OLD to recover it. Pressing <Break> will not cancel the change, but switching the machine off, pressing <Ctrl+Break>, or typing *BASIC will return to BASIC-2. Alternatively, you can use *FX142 to switch back to BASIC-2.

34. Lower-case commands

You will know that whereas VDU and PRINT are BASIC keywords, vdu and print are not. However, with the MOS 'Star' commands, *FX, *SAVE, *DELETE etc., lower-case is treated the same as upper-case. Thus *fx0 and oPt1,2 are valid commands. Further to this, disc filenames, though not cassette filenames, are treated in a similar fashion. Thus if you save a program as "sneaky", it appears in lower-case in the catalogue, but you can reload it as "SNEAKY" if you wish, and any attempt to save a program called "SNEAKY" would overwrite the lower-case one.

35. Saving memory

When you are writing very large programs, or ones that need a lot of variable space, every byte can count to squeeze as much as possible into memory. All BASIC keywords are 'tokenised' into just one byte each, so "RESTORE" only occupies the same amount of memory as "TO", ie one byte. However, this is not true of the MOS 'star' commands. Thus *O. occupies 3 bytes, whereas *OPT occupies 4, and so on. You can abbreviate words like *KEY, *DELETE etc also. However, it is wise not to abbreviate *DISC or *TAPE, as this does not not work on all versions of Operating System.

36. Simplified commands

In the case of operations starting with a '*', you can omit the quotes round a filename. Thus you must have LOAD "name", but *LOAD name is OK. In the same way, you must have CHAIN "name", but *RUN name or just *RUN is OK. You can use the abbeviation */ for *RUN, eg */ Myprog.

37. Tidy menus

A tidy way of producing a program 'menu', without a lot of repetition, is to use DATA lists. In the example below, the GOTOs in line 130 are just for demonstration purposes. You would obviously put in the line numbers appropriate to your own program. The line number after RESTORE is the one where the DATA list is stored. You can actually put the DATA list anywhere you like in the program, even in the first line, as long as the RESTORE statement points accordingly. Don't be tempted to write this in the form of a Procedure, as the GOTOs will cause nasty things to happen!

100 CLS:PRINT'TAB(18)"MENU"'TAB(18)"----"'
110 RESTORE 2000:FOR A%=1 TO 5:READ A$:PRINT
TAB(4);A%"........."A$:NEXT
120 PRINT'"Select desired option No. ";
130 ON VAL(GET$) GOTO 200,300,350,400,480 ELSE 130
2000 DATA Start Again,Exit Program,Change Details,Display Details,
Action Replay

38. Double-height input

This is a Function which enables you to INPUT strings in two-tone double-height lettering. You have to pass the following variables into the Function: The prompt or question word(s), the first letters of the top and bottom colours, the X and Y tab positions you want it to start from, and the maximum length. You must enter at least one character before pressing <RETURN>, but if you don't like this restriction, then omit the "OR g%=13" from line 160. The space available for the input is shown by a dotted line. If you don't want this, then omit both STRING$ statements from line 130, and change the 46 to 32 in line 170. The example in lines 10 and 20 gives a good demonstration of this nice little routine, which was adapted from an original passed on by Andrew G8YHI. You may omit all spaces from the listing to save memory space, if you wish.

10 MODE7:N$=FNdhin("Name ? ","G","M",5,10,15)
20 PRINT''''N$:END
30 :
100 DEF FNdhin(prompt$,top$,bot$,x%,y%,len%)
110 LOCAL a$,g%,top%,bot%
120 top%=128+INSTR("RGYBMCW",top$): bot%=128+INSTR("RGYBMCW",bot$)
130 VDU31,x%,y%,top%,141:PRINT prompt$;STRING$(len%,"."):
VDU31,x%,y%+1,bot%,141:PRINT prompt$;STRING$(len%,".")
TAB(x%+LEN(prompt$)+2,y%);
140 REPEAT
150 g%=GET
160 IF (g%=32 OR g%=13 OR g%=127) AND LEN(a$)=0 THEN VDU7:GOTO 150
170 IF g%=127 THEN a$=LEFT$(a$,LEN(a$)-1):VDU127,10,46,8,11:GOTO 200

IF g%=13 THEN VDU7:GOTO 150
190 VDUg%,10,8,g%,11
200 UNTIL g%=13: =a$

39. Input with timeout

In some applications, especially Educational programs, it is useful to have an input routine which "times-out" if no key is pressed for a certain number of seconds. This is possible for single-character routines like INKEY, but not with INPUT. This Function solves the problem, and also enables you to set limits on the string size. You must pass the minimum and maximum lengths allowed, and also the number of seconds for timeout after the last key was pressed. If the routine times-out before you press <RETURN>, then it will act as if it had pressed <RETURN> for you. If, however, you would rather that it returned the null, (or empty), string instead, then see the REM statement. The example in line 10 allows a minimum of 1 character, and maximum of 12, with a 5-second timeout. You may omit all spaces in the listing, if you wish, except those inside quotes. Thanks to Brian Parker G6JPZ for suggesting the idea, which has been incorporated into the routine given in the Golden Oldie Tip #11.

10 PRINT"Enter name ? ";:A$=FNinp(1,12,5):PRINT A$':GOTO 10
20 :
100 DEFFNinp(min%,max%,secs%):LOCAL G%,G$,GI$:*FX15,1
110 TIME=0:PRINT G$;
120 G$=INKEY$(0):G%=ASC(G$)
130 IF TIME>secs%*100 THEN PRINT:VDU7:=GI$ ELSE IF G%=-1 THEN 120
140 REM Alter the =GI$ in line 130 to ="" if string is to be null on
timeout
150 IF (LEN(GI$)=max% AND G%<>13 AND G%<>127) OR (((LEN(GI$)=0 AND
(G%<33 OR G%>126)) OR (LEN(GI$)<min% AND G%=13)) AND (min%>0 OR
G%<>13)) THEN VDU7:GOTO 120
160 IF G%>31 AND G%<127 THEN GI$=GI$+G$:GOTO 110 ELSE IF G%=127 THEN
GI$=LEFT$(GI$,LEN(GI$)-1):GOTO 110 ELSE IF G%<>13 THEN VDU7:
GOTO 120 ELSE PRINT:=GI$

40. Date input routine

This is a reasonably 'intelligent' Procedure for entering the date in a program. It checks for sensible days, months and years, (1984 to 1999), takes account of the different number of days in each month, and allows for Leap Years. The original was written by a chap called Tim Davies, but I've altered it a bit! You can enter the date in many forms, eg. 27.08.84 or 27-8-84 or 27/8/1984 or 27 8 84 or 27,08,84 and so on. The result is a string, date$, which holds the date in the form 27-AUG-1984, and also three integer variables, d%, m% and y%, which represent the day, month and year. If you do not want the string date$, then change line 1070 to ENDPROC. If you do not want to use the integer variables, then add them to the list of LOCAL variables in line 1000. If you enter an invalid date, a beep will sound, and you will be invited to try again. Note the use of INPUTLINE rather than INPUT, in order to permit commas in the date.

10 PROCdate:PRINT'"Todays Date is "date$'d%,m%,y%'
20 GOTO10
1000 DEFPROCdate: LOCALmx%,I%,d$,m$,y$:*FX21,0
1010 PRINT:REPEAT: REPEATVDU127: UNTILPOS=0:INPUTLINE"Please submit
date as DD.MM.YY "date$
1020 d$=LEFT$(date$,2) :IFASC(RIGHT$(d$,1))<48THENm$=MID$(date$,3,2)
ELSEm$=MID$(date$,4,2)
1030 y$=RIGHT$(date$,2): d%=VAL(d$): m%=VAL(m$):y%=VAL(y$)+1900
1040 RESTORE1080: FORI%=1TOm%MOD13 :READm$,mx%:NEXT
1050 IFy%MOD4=0 AND m%=2 THENmx%=29
1060 VDU7:UNTILd%>0 ANDd%<=mx% AND m%>0 AND m%<13AND y%>1983:*FX21,7
1070 date$=STR$(d%)+"-"+m$+"-"+STR$(y%): ENDPROC
1080 DATA JAN,31,FEB,28,MAR,31 ,APR,30 ,MAY,31,JUN ,30,JUL,31,
AUG,31,SEP,30,OCT, 31,NOV, 30,DEC, 31

41. User key editor

This is a short program which lists all the current user-key definitions, and not just the red ones. You can then edit them, if you wish, using the cursor and 'Copy' keys as usual. The number of bytes used up by the definitions is displayed; 239 is the maximum available. This was adapted from an original which I think was published in "Acorn User". You can omit all spaces in the listing, if you wish, except those inside quotes. This applies to the BBC B (& B+ ?) but not the Master or Compact.

100 MODE7:@%=&902:M%=0: PRINT'"Keylist":FOR K%=0 TO 15
110 PRINT'" *KEY"K%" ";: A%=K%?&B00:B%=256 :FOR T%=0 TO 15
120 S%=T%?&B00:IF S%>A% AND S%<=B% THEN B%=S%
130 NEXT:IF B%=256 THEN 170
140 FOR G%=A% TO B%-1:H%=G%?&B01:M%=M%+1
150 IF H%>31 THEN VDU H% ELSE VDU 124,H%+64
160 NEXT
170 NEXT:@%=&90A: PRINT'';M%;" bytes used." :REM max is 239


42. User key definitions

Some members may not know that, having programmed the red 'soft' keys, you can SAVE the definitions directly with the command *SAVE KEYS B00 C00 or *SAVE KEYS B00+100. The definitions can then be reLOADed at any time, (even if you have a BASIC program in memory, or if you are using View or Wordwise), with *LOAD KEYS. Disc users can conveniently incorporate this command in the !BOOT file. This applies to the BBC B (& B+ ?) but not the Master or Compact.

43. Wordwise and the user keys

When you exit from Wordwise with *BASIC, you will find that the red keys do not work properly. You can cure this by pressing <BREAK>, but you can also restore normal key operation with *FX225,1 followed by *FX227,144. <SHIFT> plus red keys seems to work normally on current versions, but just as a precaution, you could also use *FX226,128 to ensure that everything is back to normal.

44. Multiple menu technique

If you have a program involving lots of different menus, it's very tedious writing them all. Here is a fiendishly clever way of making the job easier, courtesy of Tim Davies. Just put the heading and contents of each menu in DATA statements, ending each menu with a "#". You can then call up the first menu with FNmenu(1), the second with FNmenu(2) and so on. The Function returns the number of choice you made from the menu. You can use this in various ways, such as ON FNmenu(2) GOTO 100,110,120 etc.. , or CHOICE%=FNmenu(3):IF CHOICE%=1 THEN ... ELSE .... The number in line 5910 must point to the first of the DATA statements, which must be numbered in steps of 10. If you renumber the program, remember to alter line 5910, (or whatever it becomes), as it won't be done automatically. You can of course pretty the menus up with nice colours etc., but this should at least inspire your ideas. The easiest way to test this Function without having to write a short program is to simply type PRINT FNmenu(2) <RETURN> etc..

5900 DEF FNmenu(num%):LOCAL text$,get%:num%=(num%-1)*10
5910 RESTORE num%+6000:CLS :REM 6000 is 1st DATA statement
5920 item%=1:READ text$:PRINT'TAB(12)text$''
5930 READ text$:IF text$<>"#" THEN PRINT TAB(4);item%" .....
..... "text$:item%=item%+1:GOTO 5930
5940 PRINT'"Select desired Option No. ";:REPEAT get%=GET-48:UNTIL
get%>0 AND get%<item%
5950 =get%
6000 DATA MENU ONE,Do This,Do That,Do the Other,Do Nothing,#
6010 DATA MENU TWO,First Item,Second Item,Third Item,#
6020 DATA MENU THREE,Item One,Item Two,Item Three,#
6030 DATA MENU FOUR,Choice One,Choice Two,Choice Three,
Choice Four,#

45. ULTRA-SIMPLE DISC MENU

This is a very simple but effective program for CHAINing your programs off a disc. The TAB(40) may look a little eccentric, but in combination with the VDU11, it wipes the previous line. Miss them out, and you'll see what I mean. You should set the disc to CHAIN "MENU" when BOOTed up, and put your program names into the DATA list, finishing with "END". You could of course add pretty colours and so on to make it more interesting.

10 CLS:PRINT'':A%=0:REPEAT:READ N$:A%=A%+1:PRINT;A%" "N$:UNTIL N$="END"
20 RESTORE:VDU11:A%=0:INPUTTAB(40)"Enter Choice "B%:REPEAT:READ N$:A%=A%+1:UNTIL A%=B% OR N$="END"
30 IF N$="END" THEN CHAIN N$ ELSE RUN
40 DATA MEMDUMP,SHACK3,TERMIN,ROMPEEP,END


46. Extra-large printing

This is a demonstration program which creates attractive enlarged characters very simply. It makes use of a look-up table in the 1.20 Operating System ROM, which the machine normally uses in Modes 0 to 6. This particular example uses the table to create attractive block characters in teletext Mode 7. You could modify this for inclusion into your own programs, to create special headings. This is based on ideas from "Acorn User" and "Micro Programmer".

10 MODE7:E%=144:REPEAT:PRINT"Press a Key...":A%=&C000+8*(GET-32)
20 E%=145+(E%-144)MOD7:FORB%=A%TOA%+7:C%=?B%
30 PRINTCHR$(E%)CHR$(154);:FORD%=7TO0STEP-1
40 IF(2^D%ANDC%)THENPRINTCHR$(255);ELSEPRINT" ";
50 NEXT:PRINT:NEXT:UNTILFALSE


47. Mode 7 box

This is a very handy little Procedure for drawing a box around text in Mode 7. You can print the text before or after drawing the box. You call the Procedure by giving the 4 co-ordinates in the same order as in the VDU28 command, (see User Guide page 387). You also give the first letter of the colour required, such as "G" for green. In order to leave room for control codes, you must not print any text within one space to the left or right of each vertical line, but you can print text immediately above or below the horizontal lines. For the same reasons, X1% must be at least 1, and X2% must be less than 38, but Y1% can be as low as 0, and Y2% can be as high as 24. Do not try to make the box very small, or strange things may happen! In this example, I have defined the entire usable space inside the box as a text window, so try LISTing the program as soon as you have run it. Thanks to Andrew G8YHI for bringing the original of this Procedure to my attention.

10 MODE7:PROCbox(5,12,34,6,"G")
20 PRINTTAB(13,9)"THIS IS A BOX"
30 VDU28,7,11,32,7:END
100 DEF PROCbox(X1%,Y1%,X2%,Y2%,C$):LOCAL C%,Y%:X1%=X1%-1
110 C%=144+INSTR("RGYBMCW",C$):VDU31,X1%,Y2%,C%,55
120 REPEAT VDU96:UNTIL POS=X2%:VDU107,135
130 FOR Y%=Y2%+1 TO Y1%-1
140 VDU31,X1%,Y%,C%,53,135,31,X2%-1,Y%,C%,106,135:NEXT
150 VDU31,X1%,Y1%,C%,117:REPEAT VDU112:UNTIL POS=X2%
160 VDU122,135:ENDPROC

48. Centred text

This is a Procedure for automatically centering a short piece of text on a line, without having to figure out what value TAB to use. You call it with PROCcentre(40,"HELLO THERE") or PROCcentre(80,A$) and so on, where the figure is the width of the screen in the current Mode; 20, 40 or 80.

100 DEFPROCcentre(width%,text$)
110 PRINTTAB((width%-LEN(text$))DIV2)text$
120 ENDPROC

49. Screen border

As an alternative to using teletext Mode 7, try the following at the start of your program. Using Mode 1 or 4, it selects yellow text on a blue background, draws a neat border round the edge of the screen, and protects it with text and graphics windows. This still leaves you almost the full screen free to use. Of course, there won't be as much free user memory as in Mode 7, so you can't use this for very large programs. In Mode 1, you could in fact have a red border, blue background, yellow text and white graphics if you wanted to be very flash.

10 MODE 1 :REM or Mode 4
20 VDU19,0,4,0,0;19,3,3,0,0; :REM select yellow/blue
30 MOVE 0,0:DRAW 0,1023:DRAW 1279,1023 :REM draw border
40 DRAW 1279,0:DRAW 0,0 :REM draw border
50 VDU 28,1,30,38,1 :REM set up text window
60 VDU 24,8;8;1271;1015; :REM set up graphics window

50. Novel screen clearing

These two procedures give different ways of clearing the screen of text and graphics in Modes 0,1,2,4 & 5. The first wipes from the borders inwards, rather like a fadeout on TV or films. The second clears the screen in a way similar to curtains closing. You must alter line 110 slightly for Mode 0, which will clear more slowly than the others. You can speed up the second procedure, but not the first, in Modes 2 & 5 if you wish; see the REM statement. Simply use PROCclearscreen or PROCclosescreen instead of CLS or
CLG.

100 DEFPROCclearscreen:LOCAL t%:GCOL0,0
110 FOR t%=0 TO 512 STEP 4:REM Use STEP 2 in Mode 0
120 MOVE t%,t%:DRAW t%,1023-t%:DRAW 1279-t%,1023-t%
130 DRAW 1279-t%,t%:DRAW t%,t%:NEXT:GCOL0,7:ENDPROC

100 DEFPROCclosescreen:LOCAL t%:GCOL0,0
110 FOR t%=0 TO 640 STEP 4:REM Use STEP 2 in Mode 0, and STEP 8 in Modes 2 & 5
120 MOVE t%,0:DRAW t%,1023:MOVE 1279-t%,0:DRAW 1279-t%,1023
130 NEXT:GCOL0,7:ENDPROC

51. Reverse colour text

It is useful to be able to print certain words in reverse colours, ie. black on white. This is very easy on a Commodore or Sinclair, but not so easy on the BBC micro. Here are two sets of Functions to achieve this; the first pair is for Mode 7 only, and the second pair is for all other Modes. Note that in Mode 7 there are unavoidable spaces just before and after the lettering, which is actually blue on white. Also, you must use FNrevr again on each new line, but these problems do not arise in the other Modes. The second pair of Functions is used like Procedures, hence the rather odd looking :="" purely to satisfy the syntax.

10 PRINT "NORMAL" FNrevr "REVERSE" FNnorm "NORMAL":END
20 DEF FNrevr:=CHR$(135)+CHR$(157)+CHR$(132):REM Mode 7 only
30 DEF FNnorm:=CHR$(135)+CHR$(32)+CHR$(156)

20 DEF FNrevr:COLOUR 0:COLOUR 135:="":REM Modes 0 to 6
30 DEF FNnorm:COLOUR 7:COLOUR 128:=""

52. Drawing circles and discs

This is a Procedure for drawing circles or solid discs of any size, anywhere on the screen. You call the Procedure by specifying the X co-ordinate, (0-1279), and the Y co-ordinate, (0-1023), of the centre, plus the radius, and lastly a "C" for circle or "D" for disc. The graphics origin is set back to 0,0 after the circle is drawn, but you can stop this by omitting the VDU29 statement in line 150, if you wish. The example uses Mode 4, but any graphic Mode is suitable.

10 MODE4:PROCcircle(639,511,400,"C"):PROCcircle(639,511,300,"D")
20 END
100 DEFPROCcircle(X%,Y%,R%,T$):LOCAL Q
110 VDU29,X%;Y%;:FORQ=0 TO PI*41/20 STEP PI/20
120 X%=R%*COS(Q):Y%=R%*SIN(Q)
130 IF Q=0 THEN MOVE X%,Y% ELSE IF T$="C" THEN DRAW X%,Y% ELSE MOVE 0,0:PLOT 85,X%,Y%
140 NEXT:VDU29,0;0;:ENDPROC

(NB: The Master/Compact have ready-made circle/disc routines)

53. String formatting

A statement such as ' PRINT A$, ' doesn't do what you would expect, and that ' PRINT A$,; ' is needed to prevent an unwanted linefeed. If you try the first program below, you will see that this only works for 27 passes of a FOR-NEXT loop, after which odd things happen. The simplest cure is probably to pad the string out with spaces, and not use commas at all. Note that two line 100's are given, and they each give a slightly different effect, depending on whether you want to pad the string at the beginning or the end. You can change the figure 10 in line 100 to 4,5,10,20 etc., ie. something that will divide into the screen width of 20,40 or 80.

10 A$="ABCD":FOR S%=1 TO 60:PRINT A$,;:NEXT:PRINT

10 A$="ABCD":FOR S%=1 TO 60:PRINT FNpad(A$);:NEXT:PRINT:END
100 DEF FNpad(x$):=x$+STRING$(10-LEN(x$)," ")

100 DEF FNpad(x$):=STRING$(10-LEN(x$)," ")+x$

54. Capitals conversion

This is a Function for converting a string to all upper-case letters; figures and punctuation are not affected. This saves you having to test for, say,"YES" and "yes", in response to a keyboard input. To make sure a string is all capitals, just use A$=FNcaps(A$) . By making the alterations suggested in the REM statement, you can make it change to lower-case instead.

100 DEF FNcaps(text$):LOCAL letter%,char%,result$
110 FOR letter%=1 TO LEN(text$)
120 char%=ASC(MID$(text$,letter%,1))
130 IF char%>96 AND char%<123 THEN char%=char%-32
140 REM change 96 to 64, 123 to 91, and -32 to +32
150 result$=result$+CHR$(char%):NEXT:=result$

55. Tab command

When you are using the simple TAB(X) command, you will find that items already printed to the left are liable to be blanked out. This is a mixed blessing, and can be avoided by using the full TAB(X,Y) syntax. However, this can be inconvenient at times, but you will find that using TAB(X,VPOS) can be used instead in most cases. Try using TAB(20,VPOS) instead of TAB(20) in the example.

10 PRINT"HELLO":pause=INKEY(200):VDU11
20 PRINT TAB(20)"THERE"

56. Altering the cursor

It is possible to alter the size and flash rate of the cursor in all screen Modes, and to turn it off, with the command:

VDU23;10,N,0;0;0;

where N is made up by adding together two numbers. The first controls the flashrate, and you must pick one of the following...

0=steady 32=invisible 64=fast_flash 96=slow_flash (default is 96)

The second controls the size, and you may have anything from a thick block, which is 0 in all Modes, to a thin line, which is 7 in Modes 0,1,2,4 & 5, 9 in Modes 3 & 6, and 18 in Mode 7, (default is 7 in Modes 0-6, and 18 in Mode 7). ie.

0=thick block in all modes
7=thin line in modes 0,1,2,4,5 (default 7)
9=thin line in modes 3,6 (default 7)
8=thin line in mode 7 (default 18)

Thus N=105 (96+9) would give you a thinner cursor in Modes 3 & 6, and N=0 would give you a non-flashing solid block in all Modes. A Mode change will reset the cursor, and it can still be turned on and off with the usual commands.

57. Reversing flags

It is often necessary to use "flags" in a program. Eg.

IF male%=TRUE THEN PRINT "Hello Sir" ELSE PRINT "Hello Madam"

where male% is a flag to indicate the sex of the user. If you need to reverse the polarity of the flag, (ie change it from TRUE to FALSE and vice-versa), you could use:

IF flag%=TRUE THEN flag%=FALSE ELSE flag%=TRUE

However, this can be done much more simply, with:

flag%=NOT(flag%)

58. While...do structure

You may have missed the presence of a WHILE-DO or WHILE-ENDWHILE in BBC Basic (it's in version5 as per the Arc). Eg.

WHILE X%<100 DO X%=X%+1:A%=A%*B%
or
WHILE X%<100:X%=X%+1:A%=A%*B%:ENDWHILE (as on BBC BASIC V)

REPEAT-UNTIL and FOR-NEXT loops have a disadvantage compared to WHILE-DO or WHILE-ENDWHILE loops in that they always execute at least once, whether they need to or not. In the examples above, the loop would not execute at all if X% was >=100.

Well, you can imitate WHILE-DO or WHILE-ENDWHILE with the following:

REPEAT IF X%<100 THEN X%=X%+1:A%=A%*B%:UNTIL FALSE ELSE UNTIL TRUE

It may not look very elegant, but it avoids GOTOs, so that makes it structured in my book! (Source ... Beebug).

59. Variable names

When people start programming in Basic, there is a tendency for them to stick to variable names like A$, B%, FRED etc.. This has three drawbacks. First, it is not very easy to pick out variables from keywords like FOR, TO, PRINT etc., as everything is in capitals. Second, it is easy to accidentally use a reserved word as part of a variable; TODAY is the classic mistake, as it starts with TO. Third, when you return to the program after a few days or weeks break, you will find it hard to remember just what A$ meant. The solution is to use lower-case variables whenever possible, and use long, meaningful variable names like "name$", year%","degrees" etc.. If you are really pushed for memory space, then using the single-letter integer variables A% to Z% can help, as can shorter variable names such as "nd$" instead of "newdate$", but by and large you will not need to resort to that.

60. Opening disc files

When you open files on a disc for input, using the commands OPENIN or OPENUP, the syntax is basically chan=OPENIN("myfile"). The first file to be opened is allocated a channel number like 1, (ie. the variable 'chan' becomes 1), and if you open another file without closing the first, it is allocated the next channel number, (eg 2), and so on. The numbers don't necessarily start at 1; that's just an example.

On most DFSs and ADFS, an interesting thing happens when using OPENIN or OPENUP, where the filename should already exist on disc, but cannot be found, either because you have put the wrong disc in, or maybe because you have mistyped the filename. Instead of causing an error, as you might expect, it simply allocates channel number zero. Thus, you have a very useful way of trapping the mistake before it actually causes an error. Eg. you could add something like:

IF chan=0 THEN PRINT "Oops!":GOTO 200

There is no need to use CLOSE#, as the file hasn't been opened in the first place. Only when you attempt to use INPUT#/PRINT#/BGET#/BPUT# on this non-existent file, does the DFS return the error "Channel", (ERR=222).

61. Restore statement

It is wise always to use the full RESTORE (+ line number) syntax in programs containing DATA statements, even if they theoretically don't need them. If you don't, then rather strange things can happen if you change screen Mode in between reading DATA statements. This is because the Basic stack is stored immediately below HIMEM, and is therefore lost when you change Mode. For the same reason, you must not use MODE or CLEAR within Functions and Procedures, as the program will 'forget' where it has come from! Using MODE will give an error message, but using CLEAR will not, and it can cause all sorts of infuriating and unpredictable errors!

62. Abbreviations

You can replace PRINT:PRINT:PRINT with PRINT'' . In fact, an apostrophe will force a new line at any time; try typing PRINT"HELLO"'"FRED" and see what happens. (See User Guide, bottom of page 324.) You can also replace NEXT A:NEXT B:NEXT C with NEXT A,B,C and NEXT:NEXT:NEXT with NEXT,, .

63. Underlining in Wordwise

Owners of many types of printer may experience problems when centering underlined text in WORDWISE. The underlining tends to extend all the way to the left margin, instead of starting at the beginning of the heading. This effect also be seen if you are using the 'ds' and 'de' codes in Wordwise Plus, and are previewing in 80 column mode. The cure for this is to insert one space after the centering code, but before the underline code. If exact centering is vital, then an additional space after the final white control code would "balance" the first space. Better still, use the pad character, ("|" by default), instead of a space, so that it is clearly visible in Edit Mode. The {G} and {W} represent the green and white control codes respectively, and the printer codes used are for Epson-type printers. (You can substitute 'ds' and 'de' if you have Wordwise-Plus.)

Before: {G}ce{G}oc27,45,1{W}THIS IS A HEADING{G}oc27,45,0{W}
After : {G}ce{W}|{G}oc27,45,1{W}THIS IS A HEADING{G}oc27,45,0{W}|

^ pad character or space ^

64. Error reporting

This is a little routine which not only reports errors in your programs, but also lists the offending line ready for you to edit. The first listing given only works on Basic version 2 and later, including BAS128, so if you aren't sure which you've got, press <BREAK>, and then type REPORT <RETURN>. Version 1 is copyright 1981, and version 2 is copyright 1982, etc.. If you have version 1, or you want the program to be compatible with versions 1 as well as later ones, then alter the two lines shown underneath, (10025 is an extra one), and it will then work OK on them all, though not on BAS128. All you have to do is add the routine before debugging your program, and make the very first line ON ERROR GOTO 10000. Of course, there is nothing magic about line 10000, so you can renumber the routine higher if you wish.

In the example below, you should get the error "No such variable at line 100", as A$ hasn't been defined. Be very careful not to make mistakes when typing the routine; the listing should be copied exactly as shown, even if it looks rather odd, but you may omit all spaces if you wish. If you are debugging a listing from a magazine, miss out any other ON ERROR statements until you have finished. If you get into trouble, then press <BREAK>, type OLD <RETURN>, and you should get your program back. If you don't want to exit from the program when <ESCAPE> is pressed, then alter the number 10050 in line 10000 to the line where you do want the program to got to. You will find that this routine can save you a great deal of time in the long run.

10 ON ERROR GOTO 10000
100 PRINT A$:END:REM (Spot the deliberate mistake!)
10000 IF ERR=17 THEN 10050:REM (or other line number)
10010 MODE7:REPORT:PRINT" at line ";ERL':*FX15,0
10020 err$="L."+STR$(ERL)+CHR$(11)+CHR$(13)
10030 FOR bit%=1 TO LEN(err$)
10040 OSCLI("FX138,0,"+STR$(ASC(MID$(err$,bit%,1)))):NEXT
10050 END

Make the following changes only if compatibility with Basic-I is important.

10025 DIM X% 12:Y%=X% DIV 256:REM For Basic-1
10040 $X%="FX138,0,"+STR$(ASC(MID$(err$,bit%,1))):CALL &FFF7:NEXT

65. Error calls

Here is a selection of interesting calls into ROM, suggested by Dave Woodhead. Most of them differ between Basic-1 and Basic-2, so Basic-1 addresses are given first, then Basic-2 addresses are in brackets. You can use them to deliberately generate errors inside a program, and they can be trapped in the usual way with ON ERROR GOTO, except for "fatal" ones such as <BREAK>, and 'Silly'. Note that the calls for <BREAK> and 'Locked' are into the OS ROM, and apply to version 1.20 only. There must be all sorts of devious uses for these calls, especially the ones for <ESCAPE>, <BREAK> and 'Locked'. The syntax is simply CALL &D9CD, CALL &F1F9, and so on.

D9CD=Soft Break D9DA=Hard Break
F1F9=Locked 9839(982A)=Syntax error,
9848(9838)=Escape 97F0(9821)=Mistake
A677(A66C)=Too big AA45(AA38)=Accuracy lost
AE72(AE43)=No such variable B1B9(B18A)=Bad call,
8F2F(8FDF)=Silly 99A6(99A7)=Division by zero
98DD(9C03)=String too long AED9(AEAA)=Bad HEX.

66. Storing mode 7 screen

It can sometimes be useful to be able to store a screenful of information within a program, and to be able to recall it at will, without having to store all the appropriate variables. This is especially easy and quick with Mode 7 screens, although it can be done on others. The example below stores just one screenful at a time, but you could DIM extra space to save several screens at once, eg. DIM screen1% 1000,screen2% 1000,......

If you only want to save part of the screen, then reduce the upper and lower limits of the FOR-NEXT loop. The cursor is also returned to its original position on the screen. To prevent corruptions, it is a good idea to disable the Escape key while the screen is being loaded or saved, using the appropriate *FX command*. This will only correctly save a screen which hasn't been scrolled since the last MODE or CLS command. For this reason, it may also be necessary to use CLS before reloading a screen. Note that unlike true variables such as mem%, it is essential for HIMEM to be in brackets, due to a Basic interpreter quirk. PAGE and TOP, which are similar pseudo-variables, do not suffer from this problem.

NB:
*FX229,1 inhibits the Escape action
*FX229 enables the Escape action

10 DIM screen% 1000:REM have this early in program
1000 DEF PROCsavescreen:LOCAL mem%
1010 FOR mem%=0 TO 996 STEP 4:mem%!screen%=mem%!(HIMEM)
1020 NEXT:vpos%=VPOS:pos%=POS:ENDPROC
2000 DEF PROCloadscreen:LOCAL mem%
2010 FOR mem%=0 TO 996 STEP 4:mem%!(HIMEM)=mem%!screen%
2020 NEXT:VDU31,pos%,vpos%:ENDPROC

67. Sideways ROM search

This is an interesting little program, adapted from one supplied by Dave Woodhead. It searches sideways ROMs, looking for words of more than one letter, and it then prints them on the screen. You get a lot of garbage also, but it's fun looking at all the commands and error messages. The ROM number will be 0 to 3 on a standard machine, and up to 15 if you have an expansion board. The Basic ROM is a good starting point, and this is usually in socket 0 on a standard machine. The program expects a 16k ROM, eg Basic or Watford DFS, but if the ROM is only 8k, as many are, then the search will run through twice. You can limit the search to 8k by altering the &BFFF to &9FFF in line 20.

Please note that the use of Y% in line 10 is essential, even though it doesn't seem to do anything! If nothing appears on the screen within a few seconds, then the socket is empty. The Operating System ROM is NOT a sideways ROM, and so cannot be searched with this program, unless you have, say, an old OS 0.10 chip to plug into a spare sideways socket.

10 INPUT "ROM Number ",Y%
20 FOR !&F6=&8000 TO &BFFF
30 C%=USR(&FFB9) AND &FF
40 IF (C%>64 AND C%<91) OR (C%>96 AND C%<123) THEN VDU C%
ELSE IF POS=1 THEN VDU 13 ELSE IF POS>1 THEN PRINT
50 NEXT:PRINT'"End of Search"

68. Secure cipher code

One of the most fascinating stories of World War II concerns how British Intelligence were able to crack the codes generated by the German "Enigma" cipher machines. Thanks to a simple but elegant idea from James Slater, you too can produce very secure code using your Beeb. The method relies on the fact that you can "seed" the random number generator to start at different points in the pseudo-random sequence, and the message can't be decoded unless you know the "key" integer number used to seed the generator in the first place. The ASCII code is shifted by a random amount each time, and if you type a continuous string of A's, you will see the result!

Note that the random number generator on the Master/Compact is coded differently from that on a BBC B/B+, so there is no compatibility between them when using this technique.

The more digits you use in your key number, the harder it will be to crack the code by chance. The message may contain any keyboard character, including <SPACE>, but not <RETURN>. I am sure this cipher would not be considered secure in the context of giant business computers, which can try umpteen millions of combinations in a short time, but I reckon it could be a useful way of protecting private data on micros.

If you make the alterations shown underneath, only capitals will be allowed, and the test is forced into 5-letter groups for extra security.

Try it, it's great fun, but NOT over the air - it's strictly against the rules!

100 MODE6:INPUT''"Enter CODE KEY... "n%
110 randomize=RND(-(ABS(n%)))
120 PRINT'"Decipher or Encipher (D/E) ? ";
130 REPEATg$=CHR$(GETAND&DF):c%=INSTR("DE",g$)*2-3:UNTILc%<>-3
140 PRINTg$''"Please type your MESSAGE"'
150 REPEAT:REPEATg%=GET:UNTILg%>31ANDg%<127
160 IFPOS>38THENPRINT''
170 k%=g%-32+c%*(RND(95)-1):VDUg%,10,8(k%+95)MOD95+32,11
180 UNTILFALSE

Alternative lines for 5-letter groups, capitals only:

150 REPEAT:REPEATg%=GET:UNTILg%>64ANDg%<91
160 IFPOS>35THENPRINT'''" ";ELSEIFPOS MOD6=0 THEN PRINT" ";
170 k%=g%-65+c%*(RND(25)-1):VDUg%,10,8(k%+25)MOD25+65,11



69. Ignoring shift/caps lock

Simple routines which GET a character from the keyboard can be defeated by the CAPS and SHIFT LOCK settings. For example, if you are supposed to be answering "Y" or "N" to a question, then a "y" or "n" may confuse matters. Alternatively, if you are selecting from a menu, and should be entering a number from "1" to "5", then "!" or "%" will not do. If you have Wordwise, try using the main menu with the SHIFT LOCK set, and you'll see what I mean. You could overcome this by setting the required shift, from inside the program, with *FX202. A better way is to use AND and OR to ignore the effect of any binary bits which alter with the shift settings.

This exploits the fact that "A" in binary ASCII is 01000001 and "a" is 01100001, and so on. Only the 3rd most significant bit is different, (bit 5), and we need to make sure it is always 0. The binary number 11011111 is DF in Hex, so, instead of A$=GET$ to get a "Y" or "N" answer, use A$=CHR$(GET AND &DF), which will always return capital letters. This can conveniently be used
in a Function; ie A$=FNget, where the Function is defined as DEFFNget:=CHR$(GET AND &DF).

In a similar fashion, a "1" in binary ASCII is 00110001, and a "!" is 00100001. Only bit 4 differs, and this time we want to make sure it is always a 1, and 00010000 is 10 in Hex. So, for numbers, instead of using A=VAL(GET$) or A=GET-48, you can use A=(GET OR &10)-48. Again, it is handy to use a Function; ie A=FNnum, which is defined as DEFFNnum:=(GET OR &10)-48.

Letters (capitals) - DEF FNget:=CHR$(GET AND &DF)

Numbers (digits) - DEF FNnum:=(GET OR &10)-48

70. Caps lock and shift lock

If you use the keyboard with the <CAPS LOCK> off, then you have to press <SHIFT> to get upper-case letters. As an alternative, you can hold down <SHIFT>, press and release <CAPS LOCK>, and then release <SHIFT>. You will now find that you will get upper-case normally, but lower-case if you hold <SHIFT>. The effect is cancelled by pressing <CAPS LOCK> or <SHIFT LOCK>. This can also be achieved from within a program with *FX202,160 , normal <CAPS LOCK> is *FX202,32 , normal Shift Lock is *FX202,16 , and no lock at all is *FX202,48 . You should really follow these calls with *FX118 , to make sure the keyboard LEDs change, but it doesn't seem to be necessary.

*FX202,160 - Reverse Caps Lock
*FX202,48 - No Lock at all
*FX202,32 - Normal Caps Lock
*FX202,16 - Normal Shift Lock

71. Acorn DFS 0.90 and 1.20 (DNFS) workspace


** GENERAL ALLOCATION **

Memory Locations Function
~~~~~~~~~~~~~~~~ ~~~~~~~~
&D00- DFF General Workspace
E00- EFF Copy of Track-0 Sector-0 (with variations)
F00- FFF Copy of Track-0 Sector-1
1000-10FF General Workspace
1100-11FF Parameter Blocks for Files
1200-12FF 1st File Buffer + SPOOL/EXEC
1300-13FF 2nd File Buffer
1400-14FF 3rd File Buffer
1500-15FF 4th File Buffer
1600-16FF 5th File Buffer
1700-18FF Further Workspace

** USEFUL LOCATIONS **

{A} means ascii code {N} means literal number

&E00-E07 {A} First 8 bytes of disc Title, (see &F00-&F03).

E08-E0E {A} First Filename, (7 bytes, padded with spaces).

E0F {A} First Directory, (00 if same as current directory).
NB: MSB of directory will be set if file is locked. You should AND this byte with 127 to get directory, and AND it with 128 to
detect if it is locked.

E10-E16 {A} Second Filename
E17 {A} Second Directory

E18-E1E {A} Third Filename
E1F {A} Third Directory

E20-E26 {A} Fourth Filename
E27 {A} Fourth Directory

etc..

EF8-EFE {A} Thirty-First Filename
EFF {A} Thirty-First Directory

F00-F03 {A} Last 4 bytes of disc Title
(If less than 12 characterss, title is terminated with 00 on
0.90, but padded with spaces on 1.20.)

F04 {N} Number of writes to the Catalogue. (Hex number which appears
after disc Title)

F05 {N} Number of files on disc, multiplied by 8.

F06 {N} !BOOT file *OPT4 Option number. (eg 3 for EXEC).

0.90 1.20

&10CA &10C9 {A} Current Directory
10CB 10CA {N} Current Drive number

10CC 10CB {A} Library Directory
10CD 10CC {N} Library Drive number

72. Top 10 speed techniques

Here is a list of techniques to increase the speed of your programs, courtesy of Bob Horne. Some are fairly well known, others may be new to you. The amount of improvement can vary from dramatic to marginal, but small improvements here and there can add up to substantial increases in speed. You should use the TIME facility to measure which parts of the program are slowest, and to assess the effect of using these techniques. You should initially concentrate your attentions on statements inside loops, whether they be FOR-NEXT loops, REPEAT-UNTIL or IF-THEN-GOTO loops, as this is where small delays tend to add up. ie. pay special attention to lines which are executed most often. By all means do break these rules if it improves clarity without slowing things down, so use your common sense!

1. Use integer variables, (especially the resident A%-Z%), and integer arrays where possible, including loop-counter variables. (ie. FOR A%=1 TO 10.)

2. When graphics are drawn with a FOR-NEXT loop, use the largest STEP size the Mode resolution will allow. (ie. 4 vertically, and 2, 4 or 8 horizontally.)

3. Miss off the loop-counter variables in NEXT statements. (ie. use NEXT rather than NEXT A%.)

4. Ensure that all unnecessary calculations are done outside loops. (eg. X*3*PI/2 inside a loop is slower than X*p, where p=3*PI/2 before the loop is entered.) Use REMs where they will be executed, then put them on the end of an existing line, rather than separately. eg b=RAD(b):REM convert to radians.

5. Don't put REMs or blank lines where they will be executed often; even though they don't actually do anything, they will slow things down. They are harmless if the program jumps round them, but don't go putting in GOTOs specially, as that's just as bad. If you must use REMs where they will be executed, then put them on the end of an existing line rather than separately. Eg: 10 b=RAD(b):REM convert to radians

6. Use multistatement lines; the fewer line numbers that are involved, the faster the program works.

7. Procedures/Functions may sometimes be faster than GOSUBs, (but you never use GOSUBS anyway, do you?)

8. Use short Procedure/Function names, try to have them all start with a different letter, and put the DEF of the Procedures called most often before the others, (ie. at a lower line number).

9. Use short variable names, and try to start them with different letters, as for Procedures.

10.FOR-NEXT loops are faster than REPEAT-UNTIL or IF-THEN-GOTO loops.


73. Interlacing in modes 0-6

Turning the interlacing off can produce a much steadier picture. This is done using the *TV command, which is only partly explained on page 23 of the User Guide. The full syntax is *TV A,B , where A determines how many lines up or down the display is moved, and B determines whether the i/l is turned on or off.

Thus, to leave the picture as it is, but to turn the i/l off, you use *TV0,1. To move the picture down one line as well, you use *TV255,1. To move the display, but keep the i/l on, you would use *TV255,0 , which can be shortened to the familiar *TV255. To restore the display to normal, ie. i/l on and no shift, you use *TV0,0, which can be shortened to just *TV. Don't forget that the commands will have no effect until you change Mode, and that the interlacing always stays on in Mode 7.

74. Interlacing in mode 7

Having just told you that you cannot turn the interlacing off in Mode 7, here is a way of doing just that! This idea was passed on by Dave G4IAU, who thinks that it may have originated from Belfast University. It eliminates the classic Mode 7 'shake', which can be especially bad on some samples of Microvitec monitors.

It is a three-part VDU23 statement. The first part turns the i/l off, and the second halves the number of scan lines per character to correct for the lack of interlacing. I stuck the final part on to halve the number of scan lines before the cursor starts, otherwise it gets lost. The commands alter registers 8,9 and 10 of the 6845 CRT controller, and this is explained on pages 364-367 of the Advanced User Guide. The command takes effect immediately, and is reset by a Mode change. It is very effective on block graphics and double-height characters, but you lose the rounding on normal-height characters, with interesting results!

I/L off:- VDU23;8,144,0;0;0;23;9,9,0;0;0;23;10,105,0;0;0;
I/L on :- VDU23;8,147,0;0;0;23;9,18,0;0;0;23;10,114,0;0;0;

75. Random numbers not including zero

There are times when you may want to generate a random number, which can be positive or negative, but which must not include zero. Alternatively, you may wish to multiply something by -1 or +1 at random, perhaps to determine which way a monster will turn in a maze etc..

An easy way to generate -1 or +1 is with Z=SGN(RND) , (the brackets are optional). RND on its own generates a number between -2147483648 and +2147483647, so its SGN can only be -1, 0 or +1. Since the likelihood of RND generating zero is about one in 4000 million, the result for all practical purposes is always -1 or +1! To generate a random number between say, -9 and +9 , but not including zero, you could use Z=RND(9)*SGN(RND). Thanks to James Slater for passing this idea on.

76. Jumping out of loops, procedures etc.

As you know, it is very risky to use GOTOs to jump out of FOR-NEXT and REPEAT-UNTIL loops, or out of Functions and Procedures. Sooner or later you will get an error message along the lines of "Too many FORs", or else various odd things will start to happen. However, if your program has error-trapping with ON ERROR GOTO, you may have noticed that it doesn't seem to matter how many times you jump out of whatever the program is doing, to go back to the main menu, nothing untoward happens. Thus, if you could generate an Escape from within the program, you could happily jump out of anything, provided that you didn't jump into the middle of something else.

This is possible in various "dirty" ways, such as CALL &9848 (Basic-1), CALL&9838 (Basic-2), and CALL &F9AB (OS 1.20). These are very naughty, as they vary with Basic or Operating System versions, and are not Tube compatible. The tidy way is with *FX153,0,27 and this works very well. *FX153 is very similar to *FX138, which some of you may know, but it accepts the Escape character as an error, and not just as an ASCII code like any other key. When jumping out of Procedures or Functions, the micro will not release LOCAL variables, so they may clash if used in the main body of the program, (ie. with Global variables). This call will not work if you have disabled the Escape key with *FX200 or *FX229, so if you really must do it like that, then CALL &F9AB is the best of the dirty methods.

77. *FX lookalike

You cannot use variables or Hex numbers in *FX calls, and they must go at the end of a line. This Procedure performs a *FX call; you can use variables or Hex numbers, and it can go anywhere on a line. Call it with PROCfx(a,x,y) where a,x & y are the three normal parameters, (use zeroes if there are less than three). Eg, *FX5,1 would be equivalent to PROCfx(5,1,0) . Note that A%,X% & Y% are implied LOCAL variables, so will not clash if used elsewhere in the program.

1000 DEFPROCfx(A%,X%,Y%):CALL&FFF4:ENDPROC

78. Reading *FX calls

*FX, (Osbyte), calls 156 and 165 upwards, are special read/write ones. (See the Advanced User Guide for full details.) You may write to them from BASIC with *FXa,x where a is the call number, and x is the new value to be written, eg *FX229,3 . You can also read the existing value, without altering it, using FNfx(a). Eg value=FNfx(229) . Note that strictly speaking, you should add X%=0 to line 2000, but defining LOCAL variables automatically sets them to zero, so you need not bother.

2000 DEFFNfx(A%):LOCALX%,Y%:Y%=&FF
2010 =(USR(&FFF4)AND&FF00)DIV&100

79. User port experimenting (3)

Here is a little program to enable you to use your micro as an AF signal generator. The tone is emitted as a 5 Volt p-p squarewave on pin PB7 of the User Port. I suggest you connect a 0.1uF capacitor and a 10k resistor in series with the output, to protect the 6522 VIA chip. The program initially generates 1000 Hz, and you then enter the frequency required. The timing increments available are fairly coarse, especially near the 25kHz end, so the frequency you actually get will not be exactly what you asked for. The maximum frequency possible is 250kHz, but the next increment down is 167kHz, which isn't very helpful! You can hear the tone by connecting a CRYSTAL earphone or microphone insert across the output, or by feeding it into an amplifier.

10 ON ERROR GOTO 80
20 ?&FE6B=&C0:?&FE64=&F2:?&FE65=1:freq%=1000
30 REPEAT:F%=10^6/freq%/2-1.5:?&FE66=F% MOD 256:?&FE67=F% DIV 256
40 actual%=10^6/((F%+2)*2)+0.5
50 PRINT"Generating: ";actual%" Hz"'
60 REPEAT:INPUT"Frequency ? "freq%:UNTIL freq%>49 AND freq%<25001
70 UNTIL FALSE
80 REPORT:PRINT" at line ";ERL:?&FE6B=0

80. Text screen dump

This Procedure enables you to dump the entire text from the screen onto your printer. It will work in any Mode from 7 up to 0, and is called by specifying the number of columns and lines; eg. PROCdump(40,25) for Modes 6 and 7. Any Mode 7 teletext control codes will be printed as blank spaces, and graphics in other Modes will be ignored. Only text which has been printed in normal character positions will be printed; any which was printed with the aid of VDU5 and MOVE will not show. If your printer has an enlarged-print facility, you might like to insert the appropriate control code in lines 1015 and 1055 for dumping Modes 2 and 5; the code is VDU14 for the Epsons.
If you want to be very posh, you can extend the Procedure so that it sorts out what screen Mode is in use automatically, with the aid of the Function given in an earlier tip. Note that you should omit line 1050 if your printer does auto linefeeds after a carriage return, and that the setting of the *FX6 option has no effect on the dump Procedure. The little FNpeep at the end may be used independently if required; it returns the ASCII code of the character at screen position X,Y. Eg. A$=CHR$(FNpeep(12,15)) would make A$ equal to whatever character was in column 12 of line 15, and would leave the cursor at that position.

1000 DEF PROCdump(width%,depth%):LOCAL X%,Y%,ascii%
1010 VDU6,26,4,2
1020 FOR Y%=0 TO depth%-1:FOR X%=0 TO width%-1
1030 ascii%=FNpeep(X%,Y%):IF ascii%<32 OR ascii%>126 THEN ascii%=32
1040 VDU1,ascii%:NEXT X%:VDU1,13
1050 VDU1,10:REM Omit if printer has auto linefeed option.
1060 NEXT Y%:VDU3,30
1070 ENDPROC
1080:
1090 DEF FNpeep(X%,Y%):LOCAL A%
1100 VDU31,X%,Y%:A%=&87:=(USR&FFF4 AND &FF00)DIV &100

81. Booting up secondary drives

It is very simple to Boot up disc drive 0, by pressing <SHIFT> <BREAK>, but you cannot usually do this with, say, drive 1. (Some independent DFSs do allow this, but not Acorn ones.) However, there is a way, which works as long as you have a disc in drive 0 as well. Just insert this routine at the start of whatever program the !BOOT file CHAINs - eg a menu utility. All you do to Boot up drive 1 is to Boot up drive 0 as normal, but hold onto the <SHIFT> key just a little longer than usual, until you see drive 1 start up. Do release the <SHIFT> key as soon as drive 1 starts up, or it may go into a harmless loop until you do let go. Needless to say, if you have a double-sided drive, then this works just as well for drive 2 instead of 1.

95 IF INKEY(-1)=0 THEN 100:REM Test for <SHIFT>.
96 *DR.1
97 *EXEC !BOOT
98 END:REM You need this!
100 REM Start of main program.

82. Building !BOOT file

Those of you with disc drives will know that it is sometimes not possible to use *BUILD without the "disc full" message being issued, even though the disc still has quite a bit of room on it. This is because the DFS, not being telepathic, has no advance knowledge of how long the file will be, (only you know that), and tends to assume that it will need lots of space on the disc. One way round this problem is to *BUILD the file on a spare disc, and then *COPY it across. A more convenient method is to save a dummy !BOOT file onto the disc first. The most convenient and safe way of doing this is to use *SAVE !BOOT 0 1. This saves a dummy file of length one byte onto the disc, so as to get the filename "!BOOT" onto the catalogue. You should then have no difficulty in *BUILDing your !BOOT file afterwards, in the normal way. Note that this method will not corrupt any files in memory, and it can safely be used from BASIC, View, Wordwise etc.. This technique works equally well when you wish to use the commands *SPOOL or OPENOUT/UP, but get "Disc full" messages.

83. Can't-extend errors

The annoying message "Can't extend" is sometimes issued by the DFS when replacing an existing file with a longer version of the same name. This is because the file is 'sandwiched' in by others on the disc, and can't 'grow' any further. You can often overcome this by using *DELETE to remove the existing file, and then Saving the new version. The reason for this is that the DFS now puts the file after all the others on the disc, instead of trying to put in back in the original 'slot'. However, if you get the message "Disc full", you will have to save the file on a spare disc, *COMPACT the original one, and then try and fit the file back on with *COPY. Yes, the Beeb DFS really is rather thick I'm afraid; intelligent DFS's take care of all this disc 'Housekeeping' automatically.

84. *SAVE bug

It is useful to be able to save very long dummy files when reserving disc space for random access files, (or for other purposes), as recommended in the Acorn DFS User Guide. The syntax is *SAVE 0 N or *SAVE 0+N, where N is the length in Hex. This is very fast, and works fine on the 1.20 Acorn DFS, (DNFS). However, on the 0.90 Acorn DFS, horrible things happen as soon as N
exceeds &FF00, (just under 64k), and the disc tends to be corrupted. The alternative is use OPENOUT, and advance PTR# to the required length. In the example below, an 88k file can be reserved, albeit very slowly. The DNFS is much faster than the 0.90 all round, but the difference is particularly noticeable when you are using Random Access Filing, and it's worth upgrading if you can get hold of the EPROM.

The Acorn 1770/1772 DFS does not suffer from this problem.

Remember that 1k is 1024 bytes, so PRINT &16000/1024 gives the answer 88k.

10 chan=OPENOUT("name"):PTR#chan=&16000:CLOSE#chan

85. Disc drive hints

*BACKUP produces a duplicate of the original disc, with all the files, (programs), in the same order on the disc. *COPY, on the other hand, reverses the order on the disc. The catalogue will not show this up, as it is always in alphabetical order, but you can tell if you use *COMPACT or *INFO.

If you use a '*' command which none of the ROMs recognize, then the Acorn DFS's will attempt to *RUN a file of that name. If the file is not a machine-code routine, then the attempt will fail. However, the Watford DFS's go one step further, because they will *EXEC the file if it is text, (ie if it has been SPOOLed onto disc).

This was written before the Master and the 1770 DFS came out. I think that they too are semi-intelligent in this situation.

86. Formatted disc speed

You can get faster response times from your disc just by using the right formatting program. Each floppy has a small index hole near the large centre hole. Some formatters put sector 0 after the index hole on every track. The effect of this is that each time the head steps from one track to the next, it misses the start of sector 0, causing a delay until the index hole comes round again. The better formatters provide an offset of several sectors between each track to allow the head to settle after stepping. The following program was used to compare different formatters.

10 *CAT Return Head to Track 0 and spin disc.
20 TIME=0
30 *VERIFY Must have Watford DFS or Disc Doctor.
40 PRINT TIME

The Watford DFS formatter is one of the slowest, somewhat surprisingly being beaten by a BASIC program available free on Micronet. Disc Doctor has a very fast formatter, and the Cumana program is also good.

Further improvements in speed can be achieved by moving frequently accessed small files to the outer edge of the disc, i.e. nearer to the catalogue. The prime candidates for this treatment are the !BOOT and MENU files. These files are usually added as an afterthought when the disc is complete, thus placing them near the centre of the disc. This results in much tracking noise, wasted time and head wear each time the disc is booted. Far better to put dummy !BOOT and MENU files on the disc immediately after formatting it, ensuring that these vital files reside (mostly) on track 0.

(One way to save a dummy file is to use *SAVE !BOOT 0 1, but this only reserves one sector of 256 bytes, but you can use *SAVE MENU 0 n00, where n is the number of sectors to be reserved.)

NB: This was written before the Acorn 1770 DFS was released; this does of course support the *VERIFY command.

87. Acorn DFS page setting

The normal setting of PAGE with the Acorn DFS 0.90 and 1.20, (DNFS), is&1900. It is possible to set it at a lower value, in order to gain valuable memory space, but only at a price. Here is a list of the possible PAGE values, together with a note of the facilities available. The limitation revolves around the number of files which can be simultaneously open, and whether you can use *SPOOL and *EXEC, which is a type of file. Note that LOAD/SAVE also refers to all the similar commands CHAIN, *LOAD, *RUN etc..

It is interesting to note that a PAGE value of &1700 would appear to be just a good as &1900, and that &1100 is just as good as &1200. &1100 is the absolute minimum value possible without nasty things happening if you access the disc drive(s), even just to do a *CAT. For those of you who are interested in making use of the information contained in the disc workspace, details were given in tip #71.

In order to get most of the information into the workspace, the disc must have been accessed at least once; just a *CAT will do. If you don't actually want the catalogue to come up on the screen, then precede the *CAT with VDU21, and follow it with VDU6 on the next line. This will temporarily inhibit the screen.

PAGE VALUE FACILITIES

&1900 5 Files + LOAD/SAVE + SPOOL/EXEC
1800 5 Files + LOAD/SAVE + SPOOL/EXEC
1700 5 Files + LOAD/SAVE + SPOOL/EXEC
1600 4 Files + LOAD/SAVE + SPOOL/EXEC
1500 3 Files + LOAD/SAVE + SPOOL/EXEC
1400 2 Files + LOAD/SAVE + SPOOL/EXEC
1300 1 File + LOAD/SAVE + SPOOL/EXEC
1200 0 Files + LOAD/SAVE
1100 0 Files + LOAD/SAVE
less than 1100 No disc operations possible

88. Auto-loading of disc files

When using programs that save and load data files frequently, care has to be taken to load the most recently saved file. If files are saved with numbers as filenames which are increased by one each time, the procedure will load the file with the highest number. The example below assumes that the highest possible number is 10, but the lowest can be 0 or 1.

LISTING-1:
100 file%=11:REPEAT:file%=file%-1:REM start with highest possible plus 1.
110 chan%=OPENIN(STR$(file%)):REM Use OPENUP on Basic 2.
120 UNTIL chan%=0 OR file%=0
130 IF chan% THEN 200
140 PRINT"No file found":*CAT
150 STOP:REM This bit is up to you.
200 PRINT"Found file number ";file%
210 REM INPUT#chan% can now be used in normal way.
220 CLOSE#chan%:REM Tidy up afterwards.
230 REM Rest of the program.

LISTING-2:
100 FOR file%=0 TO 9:REM Save dummy files to disc.
110 chan%=OPENOUT(STR$(file%))
120 CLOSE#chan%:NEXT

In listing 1, Line 110 converts file% to a string and attempts to open a file of that name on the disc for input. A number is allocated to chan% for this operation, but this is 0 if the file is not found. The program loops between lines 100 and 120 until either chan% is not 0 or file% has reached 0. To test this, write some dummy files to disc as shown in listing 2, and then try the example in listing 1. Rather than have just numbers as the filenames, you could save files as "data1", "data2" etc., by modifying the (STR$(file%)) to ("data"+STR$(file%)). Note that this technique can be adapted to automatically load a series of files in turn, performing a search or other operation on each one.

89. Moving text (1)

This is a very simple routine which 'assembles' text from the right-hand margin. It is an example of moving graphics at the very simplest level, and illustrates the use of *FX19 to create smooth movement. It is written for a 40-column mode, but can be modified for 80 if required. You can omit any spaces, which are shown only for clarity.

10 MODE 7:VDU23,1,0;0;0;0;
20 PROCmove("HELLO THERE!!",12,10)
30 PRINT'':VDU23,1,1;0;0;0;:END
40 :
100 DEFPROCmove(text$,pos%,vpos%)
110 FOR let%=1 TO LEN(text$)
120 FOR p%=37 TO pos%+let%-1 STEP-1
130 PRINT TAB(p%,vpos%) MID$(text$,let%,1)" ";:*FX19
140 NEXT:NEXT:ENDPROC

90. Moving text (2)

Here is another short demonstration of moving text. It uses MID$ in a rather cunning way to achieve the movement very simply. If you omit the CLS from line 10, then you get quite a different effect. The routine repeats until you press any key.

10 MODE2:VDU23,1,0;0;0;0;:COLOUR1:COLOUR131:CLS
20 ME$=STRING$(20," ")+"******* THIS PROGRAM HAS BEEN BROUGHT TO YOU BY COURTESY OF THE WAKEFIELD BBC MICRO USER GROUP *******"
30 REPEATa%=0:REPEATa%=a%+1:PRINTTAB(0,29)MID$(ME$,a%,20): b%=INKEY(15):UNTILa%=LEN(ME$)ORNOTb%:UNTILNOTb%
40 MODE7:END

91. Shadow printing

Text printing on the screen looks far bolder and nicer if it appears to cast a slight shadow. This is used to good effect in the titles to a number of commercial programs. Here is a very useful procedure to produce this in any mode with 4 or more colours; ie Modes 1,2 and 5. You must specify the two colours to be used, and these must not be the same as the current background colour. In the example below, colours 1 and 2 are used, and you can alter the MODE statement to either 1 or 5 to see how the function utomatically adjusts to cater for the different graphic resolution. If you are sticking to just one mode, then you can simplify the function by getting rid of line 110, removing all references to A%, mode% and fact%, and substituting 32 or 64 for the variable fact% in lines 130 and 140. (The expression fact%DIV32 will of course also simplify to just 1 or 2.) If you do not want to have to specify the colours in the FN brackets, then you can omit them, remove all reference to c1% and c2% in the program, and merely substitute the required numbers in line 130 and 140. This will simplify the procedure even further, at the expense of versatility.

A very pleasing effect can be produced by running the program below in Mode 2, after adding :COLOUR132:CLS: immediately after the MODE statement in line 10. You can alter the position of the shadow by trying different combinations of +4, 0 and -4 in place of the two -4's in line 130. Ie the shadow can fall above, below, to the right, left or centre of the text.

Do remember that the Function is only for Modes 1,2 and 5; it simply won't work in the others.

10 MODE2:PRINTTAB(0,9)"WAKEFIELD "FNshad(1,2,"BBC")" MICROS"
30 END
40 :
100 DEFFNshad(c1%,c2%,text$):LOCALA%,mode%,fact%,v%,p%
110 A%=&87:mode%=(USR(&FFF4)AND&FF0000)DIV&10000
120 p%=POS:v%=VPOS:IFmode%=1THENfact%=32ELSEfact%=64
130 VDU5:GCOL0,c2%:MOVEp%*fact%-4*(fact%DIV32),((32-v%)*32-1)-4
140 PRINTtext$:GCOL0,c1%:MOVEp%*fact%,(32-v%)*32-1:PRINTtext$
150 VDU4,31,p%+LEN(text$),v%:=""

92. Graphics demo

This is a short demonstration of an interesting undocumented feature of the BBC Operating System on the BBC B, B+, Master, Arc etc.. It's a weird graphic effect that is easier to demonstrate than describe, so get typing!

Make sure you type it in very carefully, and save it before you run it. If you make any mistakes, the machine might 'hang up'.

10 REPEAT:READ A$:A%=EVAL("&"+A$):CALL 65518:UNTIL A%=&15
20 DATA 16,7,1F,C,C,88,83,41,70,72,69,6C,86,46,6F,6F,6C,1E,15

93. Exploring fractals

Fractals is an odd branch of mathematics, only discovered within the last decade or so. If you keep 'zooming in' to a plot or graph produced by a mathematical equation, you usually expect to end up with a straight line, or something equally unexciting. With Fractals, yet more detail is resolved each time you zoom in on any part of the plot. It reminds me of the witty rhyme "Great Fleas have Little Fleas, upon their backs to bite 'em, and Little Fleas have Lesser Fleas, and so ad infinitum...".

In this equation, known as the Mandelbrot Set after its discoverer, the horizontal axis is effectively scaled from -2.25 on the left to +0.75 on the right, whilst the vertical axis is scaled from -1.5 at the bottom to +1.5 at the top. If you wish to examine any part of the plot in more detail, ie to 'zoom in', then just choose a narrower range of values, and edit line 10 appropriately. For example, you could use -1.5 to -1.0 instead of -2.25 to +0.75, and +0.9 to +1.4 instead of -1.5 to +1.5. Note that the second number of each pair should be more positive than the first, and that you should zoom in equally in both axes to keep the right proportions. In the first example, the total range was 3.0 for both axes, ie +0.75-(-2.25)=3.0 and +1.5-(-1.5)=3.0, whereas in the second example it was 0.5 for both axes, ie -1.0-(-1.5)=0.5 and +1.4-(+0.9)=0.5 .

The plots take a very, very long time indeed, but the results are rather beautiful. The first plot may start off fairly rapidly, as it is drawing large areas of solid colour, but once it gets onto the intricate bits the speed drops dramatically, and you may even think the program has 'hung-up' if it happens to be plotting black on a black background at the time. The current plotting position is marked with a small dot, which also flashes in Mode 2. You can save the screen afterwards, to avoid having to wait all that time again, and this is done in line 110, using the filename "Screen1". To reload the display at a later date, just use the one-line program underneath. Save subsequent plots under "Screen2" etc.. If you are using cassette, you will have to suppress screen messages with *OPT1,0 before each *LOAD or *SAVE, so see page 398 of the BBC User Guide.

The speed and resolution of the Beeb are barely good enough to even hint at the real detail and beauty of fractals; you need very large mainframe computers for that. (Or an Archimedes!) However, by using MODE1, or even MODE0 in line 20, more detail can be resolved at the expense of speed and colours. If you do try these modes, then alter the STEP 8 in line 40 to STEP 4 for Mode 1, and STEP 2 for Mode 0. The STEP4 in the second part of the line should not be changed.

The idea for this program came from an article "Frontiers of Chaos" in The Guardian newspaper, long before all the computing magazines jumped on the bandwagon!

10 limit%=48:Xmin=-2.25:Xmax=0.75:Ymin=-1.5:Ymax=1.5
20 MODE2:VDU5
30 Horiz=(Xmax-Xmin)/1279:Vert=(Ymax-Ymin)/1023
40 FOR Xcoord%=0 TO 1280 STEP 8:FOR Ycoord%=0 TO 1024 STEP 4
45 GCOL0,11:PLOT69,Xcoord%,Ycoord%
50 M=Xmin+Xcoord%*Horiz:N=Ymin+Ycoord%*Vert:colour%=0:X=0:Y=0
60 REPEAT:R%=X*X+Y*Y:T=X*X-Y*Y+M:Y=2*X*Y+N:X=T:colour%=colour%+1
70 UNTIL R%>100 OR colour%=limit%
80 colour%=colour% MOD limit%
90 GCOL0,colour% MOD 8:PLOT69,Xcoord%,Ycoord%
100 NEXT:NEXT
110 *SAVE Screen1 FFFF3000+5000

10 REM Program to reload screen display
20 MODE2:VDU5:*LOAD Screen1 3000


(This was written in early 1985 - fractals have come on since then!)

94. Blanking the screen

It might sometimes be useful if you could blank the screen with a command, but turn it back on again, revealing all the original text/graphics unchanged. You can do this with the command VDU23,0,R,V,0;0;0; This is a general command to write value V into register R of the 6845 CRT controller chip. If you make R=0, then V=0 will blank the screen, and V=127 will turn it back on again in modes 0 to 3, and V=63 should be used for modes 4 to 7. It would be interesting to write an Event-driven routine which blanked the screen if none of the keys have been touched for more than a minute or so, and restored the screen as soon as a key is pressed, checking to see which mode is in use.

95. Auto screen-off

Further to the previous tip, Adrian Botham has written a machine-code routine which turns the screen display off if no key has been pressed within the previous minute. As soon as a key is pressed, the screen display magically reappears with its contents preserved. This helps to lengthen the useful life of the cathode ray tube by preventing 'phosphor burns'. The code occupies 88 bytes, and I have used the cassette/RS423 input buffer area at &A00. If this interferes with any of your other software, then you can try putting it somewhere else.

After running the program, you can load other programs in, switch to Wordwise etc., and as long as you don't press <BREAK>, then the 'auto screen-off' will operate.

Any of you who want to figure out how it works should read chapter 12 of the Advanced User Guide for the BBC Micro, and also chapter 9.5 and 9.6.

The only spaces in the listing are absolutely essential, and note that the underline character is used in some variable names, and should not be confused with the minus sign, which looks very similar in mode 7. (The underline character is on the same key as the Pound sign.) After typing in the listing, save the program before attempting to RUN it.

100 osword=&FFF1:osbyte=&FFF4:evntv=&220
110 clock_pars=&A00:code%=clock_pars+5
120 FORpass%=0TO1:P%=code%
130 [OPTpass%*2
140 PHP:CMP#2:BEQkey_pressed
150 CMP#5:BEQminute_up:PLP:RTS
160 .key_pressed PHA:TXA:PHA:TYA:PHA
170 LDA#0:STA&FE00:LDA&FE01:BEQturn_screen_on
180 .set_time LDX#clock_pars MOD256:LDY#clock_pars DIV256
190 LDA#4:JSRosword:JMPexit
200 .turn_screen_on LDA#132:JSRosbyte
210 CPY#&41:BPLfour_seven
220 .zero_three LDA#127:STA&FE01:JMPset_time
230 .four_seven LDA#63:STA&FE01:JMPset_time
240 .minute_up PHA:TXA:PHA:TYA:PHA
250 LDA#0:STA&FE00:STA&FE01
260 .exit PLA:TAY:PLA:TAX:PLA:PLP:RTS
270 ]NEXT
280 ?evntv=code%MOD256: evntv?1=code%DIV256
290 !clock_pars=&FFFFE890:clock_pars?4=&FF
300 *FX14,2
310 *FX14,5
320 CALLcode%



96. Printer pound/hash sign fix

On all Epson-compatible printers, there is a discrepancy between the keyboard Pound currency sign, and the printer Pound sign. Character 96 on the keyboard is a Pound sign, but the printer prints a sort of ‘ sign - like a lefthanded apostrophe. The Pound sign on the printer is shared with character 35, the "#" (Hash or Sharp symbol), depending on whether you are using the American or English character set. However, those of you with an Epson FX80 and some other printers can use a more elegant solution. It is possible to redefine the printer character set, and thus turn character 96 to a proper Pound sign, (dipswitch 2-3 on the NLQ's and 1-4 on the FX80 must be
"off").

This little program must be run with the printer "ON LINE", and could conveniently be CHAINed as part of a disc !BOOT file. Those of you who are more cunning can spool the bytes off directly onto disc, and can then *EXEC them instead of CHAINing. As it stands, this program has no effect in the NLQ mode of a Canon/Kaga printer, as a PCG RAM expansion is needed. The DATA list has only been split for clarity, and to allow me to insert REM statements, so you can type it all in on one line if you like.

NB: Many newer Epson printers only allow a limited range of characters to be redefined, so this may not work.

100 VDU2:REPEAT:REM Send data to printer only.
110 READ data%:IF data%>=0 THEN VDU1,data%
120 UNTIL data%= -1:VDU3:END
130 DATA 27,82,0:REM Esc "R" - Select American characters.
140 DATA 27,58,0,0,0:REM Esc ":" - Download from ROM to RAM.
150 DATA 27,38,0,96,96:REM Esc "&" - Specify character 96.
160 DATA 139,18,0,126,128,18,128,2,128,66,0,0:REM Data for sign.
170 DATA 27,38,0,224,224:REM Esc "&" - Repeat for Italic version.
180 DATA 139,18,0,30,96,18,128,18,128,64,0,0:REM Data for Italic.
190 DATA 27,37,1,0:REM Esc "%" - Select RAM for printing.
200 DATA -1:REM Terminator for data.



97. Printing listings (1)

Here is a handy little routine, for using a red key to LIST your programs without fuss. It is particularly useful if you want to LIST a number of programs one after another. The routine selects LISTO7 to format the listing nicely, cancels paged mode, switches the printer on, LISTs the program, switches the printer off, re-engages paged mode, and then cancels the LISTO. Please note that the spaces are for clarity only, and should be omitted, even though it looks mighty odd!

*KEY1 LISTO7|M |O |B L.|M |C |N LISTO0|M

98. Printing listings (2)

Here is another handy little routine from Peter Osborn, using the Perforation Skipover facility in your printer. This leaves a few blank lines at the top and bottom of each fanfold sheet, thus avoiding listing lines being bisected by the perforations. It is shown as a string of VDU codes on KEY0, and the cancel code is on KEY1. I have assumed that the formlength has first been set to 11", (the norm), or 12", either on the printer dipswitches or by the Esc"C" command.

The procedure is to set your paper up so that the print head is approximately N/2 lines from the top of a sheet, where N is the total number of blank lines to be skipped. (Ie if you want 3 blank lines at the top and bottom of the pages, then N=6.) Now switch your printer on, press <f0>, and then LIST your programs as normal, using <Ctrl+B> and <Ctrl+C> to switch the printer on and off. You can cancel the skipover with <f1>, to avoid odd things happening with, say, Wordwise, which can set its own top and bottom spaces. This
routine apparently works on all Epson-compatible printers, and also on the Star Gemini 10-X.

*KEY0 VDU2,1,27,1,78,1,N,3|M (Enable P-S)
*KEY1 VDU2,1,27,1,79,3|M (Disable P-S)

99. GREEK CHARACTERS

Many printers do not have Greek characters available, and this can be a nuisance when printing physical, electrical or mathematical formulae. Peter Osborn and his colleague B.Frere sent in some useful Wordwise codes to print some of the Greek alphabet on printers using Epson commands. I've improved some of them a bit, and added a few more; you might find them handy now and again. If you have Wordwise Plus then you can replace the initial oc27,75 or oc27,76 with es"K" and es"L" respectively.

The character is sent to the printer only in bit-image mode, and is not allowed for by any justification or tabbing routine; you'll see what I mean if you try it. Either avoid these situations, or else try playing about with pad characters in the text. You can also print the characters from Basic, by replacing the initial oc with VDU, and preceding each byte with a 1, eg VDU1,27,1,75,1, etc., which is rather tedious. You could 'cheat' by using *FX3,10 , but there are snags, so I suggest you avoid it. Incidentally, these codes confuse my machine-code Pound/# sign fix, so avoid using that too!

Alpha oc27,75,6,0,28,34,34,28,34,0
Beta oc27,75,6,0,127,146,146,108,0,0
Gamma oc27,75,6,0,32,64,63,64,0,0
Delta oc27,75,6,0,108,82,82,12,0,0
Delta (cap) oc27,76,12,0,2,6,10,18,34,66,34,18,10,6,2,0,0
Epsilon oc27,75,6,0,28,42,42,34,0,0
Zeta oc27,75,6,0,184,68,69,67,0,0
Eta oc27,75,6,0,32,30,32,32,31,0
Theta oc27,75,6,0,124,146,146,124,0,0
Kappa oc27,75,6,0,62,8,20,34,0,0
Lambda oc27,75,6,0,2,4,72,60,2,0
Mu oc27,75,6,0,1,62,4,4,62,0
Xi oc27,75,6,0,84,42,43,35,0,0
Pi oc27,75,6,0,32,62,32,62,32,0
Rho oc27,75,6,0,62,73,72,48,0,0
Sigma oc27,75,6,0,24,36,255,36,24,0
Sigma (cap) oc27,75,6,0,130,198,170,146,198,0
Tau oc27,75,6,0,16,32,62,32,32,0
Phi oc27,75,6,0,28,34,34,60,32,0
Psi oc27,75,6,0,56,4,255,4,56,0
Omega oc27,75,6,0,28,34,12,34,28,0
Omega (cap) oc27,75,6,0,26,38,32,38,26,0

100. Printer routine snag

If you wish to send text to the printer only, (ie with nothing appearing on the screen), there are two simple ways of doing it. You can precede the text with VDU2,21 , and follow it with VDU6,3 . Alternatively, you can precede it with *FX3,10 , (or *FX3,9 in the case of RS423 printers), and follow it with *FX3,0 , (or simply *FX3). In the latter case no VDU2 or VDU3 is necessary, and in both cases the character set by *FX6,- is not sent to the printer. This character is ASCII code 10, (linefeed), by default, and is usually set to 0 with *FX6,0 if your printer doesn't do auto-linefeeds.

This is fine until you try sending a string of VDU codes, such as for the Greek characters shown earlier. Depending on the setting of *FX6,- , either 10's or 0's will not be sent to the printer, with all sorts of unpredictable and horrible results. Eg VDU27,10,3,4,0,0,3 would effectively be sent as VDU27,3,4,0,0,3 or VDU27,10,3,4,3 ! Now since 0's tend to appear much more often than 10's in VDU statements, those of you with printers doing auto-linefeeds are less likely to have trouble, but even so, this can be a considerable nuisance.

There is a further snag with using VDU2,21 , and that is that if a 6 should appear in the VDU codes, then the VDU drivers are turned back on again too early, nasty things will happen on the screen, and the whole thing will probably seize up. The safe solution is to use VDU2 and VDU3 as normal, and precede each byte by 1; eg VDU1,27,1,45,1,1 , even though it is rather tedious. What would be nice would be to tell the Beeb to send all characters to the printer, but unfortunately, *FX6,- does not allow this to be done.

101. Second processor hints

When you are using the 6502 2nd processor, in BASIC or with View, Wordwise etc., you can safely use the disc commands *BACKUP, *COMPACT and *COPY without overwriting a file in memory. This is because the DFS uses the Beeb RAM for these operations, whereas your file is safely tucked away out of reach in the 2nd processor's RAM.

102. Speech procedure

For those of you with an Acorn Speech system, here is a procedure to conveniently call up speech, without using a clumsy string of SOUND statements or DATA lists. Note that the word numbers must be passed as one long string, with separating commas if necessary. Actually, you can use this technique whenever you wish to pass varying length lists of parameters, to a procedure or function. You would simply use num% for something else other than SOUND in line 1030.

100 PROCspeak("241,246,252"):REPEATUNTILGET=13:REM Now press <Return>
110 PROCspeak("265,275,128,266,209,230,211"):END
120 :
1000 DEFPROCspeak(data$):LOCALcom%,num%:REPEAT:com%=INSTR(data$,",")
1020 num%=VAL(LEFT$(data$,com%-1)):data$=RIGHT$(data$,LEN(data$)-com%)
1030 SOUND-1,num%,0,0:UNTILcom%=0:ENDPROC

103. Super-fast relocate

We published a very simple and effective relocate for disc users back in September 1983. This was added to your Basic program, and automatically moved it down to a lower value of PAGE. For those who don't know, the Beeb DFS 'pinches' some user memory as workspace for itself, and these relocate programs are to claim it back again after the program is loaded. This overcomes the problem of some long programs only running on Cassette-based Beebs.

The only drawback is that the published relocate is a trifle slow, and can take up to 5 seconds on very long programs. My new routine below uses machine-code for the critical section, with a bit of Basic for economy of programming. It can relocate a 25k program in less than 0.4 seconds, which is fast enough for anyone! It is written in the form of a dummy Procedure, which
tacks onto the end of your program, and is called by line 0. The line numbers used have been chosen so as not to clash with line numbers in your own program. Your program can start anywhere after line 0, and must finish before line 32000. Be sure to add an END to stop the program running into the Procedure by accident. A convenient way to add this routine is to *SPOOL it off onto disc, and then *EXEC it back in again to merge with your own program. Those of you with utilities like *MERGE will find life made even easier. This routine automatically switches the DFS off if you try to relocate below &1100, which is the lowest value of PAGE where limited use of the DFS is possible. You simply set the variable D% in line 0 to the address you want PAGE resetting to; &E00 in this example.

Please note that the Procedure is a dummy one, and is terminated with END, rather than ENDPROC. This is quite deliberate, so don't change it back again. The routine uses integer variables D%, (for Destination), and P%, (for Program counter). If you want to avoid using D% for any reason, then change it to a different resident integer variable. You cannot substitute for P% in this way, but it is quite OK to use it in your own program as well, without any clash. Some workspace on Zero Page is used, from &70 up to &8C, as this area isn't used by Basic, and is in any case freed again as soon as the program has been relocated.

There are no spaces within the listing, and you can omit the ones immediately after the line numbers. To those of you not familiar with machine-code, this must look very weird indeed, but just type it in very carefully, exactly as it is, but note that the two blank lines with colons are only to make the listing more clear. If you want to relocate a program which has data or machine code stored in memory locations above TOP, then you can substitute the length of the whole file in place of TOP-PAGE on line 32010; eg. &4B53+256. This Hex number can be found by using *INFO on the file, after you have added this routine. (Put any 4-digit Hex number in at first, just to simulate the true length.)

0 D%=&E00:IFPAGE>D%THENPROCmove
10 :
20 REM Your program goes somewhere about here.
5000 END:REM Most important.
5010 :
32000 DEFPROCmove:IFD%<&1100THEN*TAPE
32010 !&70=D%:!&72=PAGE:!&74=TOP-PAGE+256
32020 P%=&76:[OPT2:LDX&74:LDY#0
32030 LDA(&72),Y:STA(&70),Y:INY:BNE&85:INC&71:INC&73
32040 DEX:BNE&7A:DEC&75:BNE&7A:RTS:]
32050 VDU21:*KEY9CALL&76|MPAGE=D%|MNEW|MOLD|MRUN|F|M
32060 *FX138,0,137
32070 END



104. Controlling shift/caps lock in a program

It can sometimes be useful to control the Shift or Caps Lock from within a program, to ensure that the machine is set correctly for the next INPUT or GET statement. This can be done with the command *FX202,n, where n is as follows:-

16 - Shift Lock On 144 - Reverse Shift Lock On
32 - Caps Lock On 160 - Reverse Caps Lock On
48 - Both Locks Off

105. GOTO/GOSUB line tokens

If you examine the structure of a BASIC program listing in memory, you will see that the line numbers are stored as two bytes; low followed by high. The low byte is given by linenumber MOD 256, and the high byte is given by linenumber DIV 256. However, the line numbers following a GOSUB, GOTO, or implied GOTO, (ie IF A=5 THEN 550), are tokenised in a weird way. They are stored as 4 bytes; the first byte always being &8D to indicate that a line number follows in the next three bytes, which I shall call b1, b2 and b3.

To decode this, you must shift b1 up 2 bits, (the same as multiplying by 4 in BASIC, or doing two ASL's in machine code), mask off the bottom 6 bits, (same as ANDing it with &C0), then EOR it with b2, and this gives you the low byte of the line number. Now shift the original b1 up 4 bits, mask off the bottom 6 bits, and EOR it with b3, which gives you the high byte.

Try running the program below, from line 200, (ie type GOTO200 rather than RUN), enter A4,68,43 (separate with commas; no need to prefix with '&'), and you should get "line 1000". Note how the Hex numbers are entered as strings, and turned into numeric variables with EVAL; you can't do it directly. Encoding the line numbers is a bit more hairy; the first part of the program below demonstrates this, and you can enter the results into the second part, to confirm that the encoding works OK.

100 REM Encode tokenised line numbers.
110 INPUT"Enter Decimal Line No.... "line%
120 lobyt%=line%MOD256:hibyt%=line%DIV256
130 b2%=&40+(lobyt%AND&3F):b3%=&40+(hibyt%AND&3F)
140 lobyt%=(lobyt%AND&C0)DIV4:b1%=(&80+lobyt%)EOR&10
150 hibyt%=(hibyt%AND&C0)DIV16:b1%=(b1%+hibyt%)EOR4
160 PRINT~&8D,~b1%,~b2%,~b3%
170 :
200 REM Decode tokenised line numbers.
210 INPUT"Enter three Hex bytes.... 8D,"b1$,b2$,b3$
220 b1%=EVAL("&"+b1$):b2%=EVAL("&"+b2$):b3%=EVAL("&"+b3$)
230 lobyt%=((b1%*4)AND&C0)EORb2%:hibyt%=((b1%*16)AND&C0)EORb3%
240 PRINT"Line Number = ";hibyt%*256+lobyt%



106. Pretty VDU7 bell

If you are tired of the boring VDU7 bleep, and fancy something more interesting, then Andrew G8YHI has the answer. The following commands redefine the bleep as a nice typewriter-style "Ting"! The effect is cancelled on <BREAK>, although you only need to execute the *FX calls again to restore it, as the envelope parameters are not lost. Once you've RUN the program, you can type NEW or whatever, as it isn't needed any more. You could, of course, simply tack it onto your own program as a Procedure.

10 ENVELOPE1,1,0,0,0,0,0,0,127,-4,-2,-1,126,90
20 *FX212,0
30 *FX213,170

107. Key-pressed bleep

This is a routine passed on by Andrew G8YHI, and causes a short bleep every time a key is pressed, rather like a supermarket till. It works by intercepting the current input stream vector, (the current input being keyboard). You'll either love or hate it!

10 A%=!&210 AND &FFFF:*FX213,200
20 *FX214,1
30 P%=&D50:[OPT2:PHA:LDA#7:JSR&FFEE:PLA:JMP A%:]
40 *KEY10 ?&210=&50:?&211=&D|M*FX213,200|M*FX214,1|M|LNEW|M
50 *FX138,0,138

108. Wordwise pad character snag

You cannot normally program the character "|" to appear on a red user-definable key. The "|" is taken to stand for <CTRL>, so "|L" is interpreted as <Ctrl+L>, which is ASCII code 12 decimal and clears the screen. What you cannot do is program the keys to actually hold "|" as a separate printable character. This can be a bit of a nuisance on Wordwise, where you might want to hold a useful document heading on a red key, (or on the cursor and <Copy> keys), and the "|" is needed as the default pad character to make sure a centred heading comes out correctly. You could get round this in several ways. First, you could redefine the pad character at
the beginning of the document or key definition, and I often use the backslash "\", which looks like "1/2" in mode 7. Ie, you could put pc\, (pc"\" on Wordwise Plus), and then use "\" as the pad character from then on. Alternatively, you could use something like "\" on your red keys, but before you print or save the document, use Search-and-Replace to change the "\"s to"|"s. Finally, as a more permanent solution, you can define the keys(s) using"\" as the temporary pad character. When you've finished, use Disc Doctor or ADT etc. to edit the memory to change the temporary character to "|", using *MZAP B00 (or MEDIT B00 or whatever command is appropriate), and then save the definitions with *SAVE Keys B00 C00. You can reload the keys at any time with *LOAD Keys, including from the Wordwise menu, or in your disc !BOOT file. If you haven't got Disc Doctor etc., then you can achieve the same effect with the routine below, which can be typed in direct mode if you wish, rather than RUN as a program.

NB: If you have a 6502 2nd Processor, then edit and save your key definitions with the Processor switched off, and put leading "F"s on the Hex addresses so that you can successfully reload them when the 2nd Processor is in use. ie.

*SAVE Keys FFFF0B00 FFFF0C00.

FOR m%=&B00 TO &BFF:IF ?m%=ASC("\") THEN ?m%=ASC("|"):NEXT ELSE NEXT

Addendum
~~~~~~~~
The above dealt with the problem of not being able to directly program the"|" character, (which looks like two vertical parallel bars in Mode 7), onto the red keys. Normally the "|" is used to represent "Ctrl", so "|M" is equivalent to Ctrl-M, ie a CR.

Dave G7FHJ has pointed out there is a very simple way round this, which I was blissfully unaware of back in 1985! All you have to do is double-up on the character. Eg if you wanted a red key to produce the actual characters"|M" instead of a CR, then try using "||M" instead.

109. Disabling ROMS

There are times when it is useful to suppress a resident ROM temporarily, even if <BREAK> or <CTRL+BREAK> is pressed. You can do this by Poking the ROM private workspace table with 64, ie setting bit 6. Eg., if your DFS ROM is in socket 3, then you can kill it by typing ?&DF3=64, and then pressing <BREAK>. If you want to kill the ROM in socket 5, then use ?&DF5, and in socket 15, (&F), you use ?&DFF.

To enable the ROM again, either switch off for a few seconds, or else Poke the original value back. For example, if PRINT ?&DF3 <RETURN> normally gives 151, then replacing 64 with this original value will restore the ROM after a <BREAK>. Many thanks to Nick Davison of Calverley, Leeds, for this information.

110. Slowing down programs

Brian Smith points out that if you type ?&FE45=1:?&FE46=0 then all subsequent programs will run super-slow. You can try this out most easily by loading in program, typing in the slowing-down command, and then typing LIST. To restore normality, do the same again but with the original values in place of 1 and 0. (Obviously, you will have to find them out with PRINT?&FE45,?&FE46 before you start.)

On my Beeb they are both 14, but I think it depends on what options are fitted. I'm not sure exactly how this works, but you are messing about with the internal 6522 VIA timer registers. This evidently fools the computer into spending too much time looking for interrupts, and not enough time getting on with processing.

111. Tube indicator

This short Function will return TRUE if a 6502 2nd processor is active, and FALSE if it is not. It is a useful way of either warning the user to switch the processor off, or of making other decisions within the program. Note that X% is effectively set to zero simply by being made LOCAL; do not omit it, even though it apparently does nothing.

10 IF FNtube=TRUE THEN PRINT "2nd Processor is active"
20 END
1000 DEFFNtube:LOCAL A%,X%,Y%
1010 A%=&EA:Y%=&FF:IF USR(&FFF4)AND&FF00 THEN =TRUE ELSE =FALSE

112. Use for EVAL

People often get confused between VAL and EVAL on the Beeb. VAL simply tries to treat a string as a number, so PRINT VAL("4*3+1") would give the answer 4. The asterisk is a non-numeric character, so it and anything after it are ignored. However, EVAL goes a step further, by attempting to evaluate the string as a valid numeric expression, and would give the answer 13. It is
therefore possible to write a program which accepts an equation as a string, thus opening up some interesting possibilities. Anyway, here is a more down-to-earth use for EVAL, for which I claim no originality. If you think about it, there isn't any obvious way to INPUT a number in Hex format; only in Decimal. You can overcome this with the technique below.

10 INPUT"Hex number :&" A$:A=EVAL("&"+A$)

You can also use this technique in conjunction with READ and DATA to enable you to put Hex numbers in data statements, without bothering with the ampersands, eg DATA 5B,3C,20,FF instead of DATA &5B,&3C,&20,&FF etc.

113. Altering the RS423 format

If you are experimenting with the RS423 serial port, and need to alter the data format, then you can do so with the command *FX156,X,227 . The value of X can be looked up in the table below, where X is 4*n. The default value on the Beeb is n=5, corresponding to *FX156,20,227 .

DATA BITS PARITY STOP BITS n X
--------- ------ --------- - --
7 even 2 0 0
7 odd 2 1 4
7 even 1 2 8
7 odd 1 3 12
8 none 2 4 16
8 none 1 5 20
8 even 1 6 24
8 odd 1 7 28

You can read the current value of n using the following function. If you want to read the value of X rather than of n, then change the &400 in line 1020 to&100. Note the comment in tip #111 about X% being LOCAL.

1000 DEF FNstatus:LOCAL A%,X%,Y%
1010 A%=156:Y%=255
1020 =(USR(&FFF4) AND &1C00) DIV &400

114. Another relocate program

Earlier, I gave a Procedure for tacking onto the end of a Basic program, so that long programs could be run off disc, with a PAGE value down to &E00. This works fine, but there are times when this is not convenient. If you are adapting a very long and complex program from cassette to disc, it may not be safe to go tacking procedures on the end. This is especially true if there is data after the visible Basic part of the program, as any additions are likely to overwrite it. A typical example would be a text adventure game like Castle of Riddles, which is 65 blocks long, over half of which is data. This is too long to even LOAD into memory with PAGE set at &1900.

The solution is to make the program a 2-part one, with the relocate routine in the 1st half, thus avoiding any need to tamper with the main program. If the program is short enough to run with PAGE at &1100, then the 1st program need consist only of one line, ie 10 PAGE=&1100:CHAIN "Dragon2" . If the program must run with PAGE lower than that, eg &E00, then the program below will do the trick. It is so fast that it doesn't bother to work out the length of the program, (tricky if you have to allow for any hidden data), but simply relocates all available memory in 1/3 second. In line 10 you specify the new value of PAGE, eg &E00, and the name of the main program. You could of course replace the final term with an INPUT N$ statement, in which case the one relocator will run any named program. PAGE is initially set to &1100 in line 10, in case the main program is too long to even LOAD with PAGE at &1900. This doesn't affect the relocator itself, which churns on merrily, blissfully unaware that PAGE has been altered, but it does define where the main program will LOAD. In many cases, this 1st term in line 10 can be omitted, but it doesn't do any harm to leave it in, (with the Acorn 8271 DFS's at any rate).

The program may already have a first part, which just sets up some of the character shapes, and displays a logo on the screen. You could easily add this routine in place of the CHAIN "Dragon2" type of command, followed by END if necessary. Ignore any corruption of the logo; if you're clever enough, you'll know how to preserve it!

This program looks rather weird, and it can be hard to spot mistakes. Try missing out the VDU21 in line 60 at first, otherwise useful error messages are suppressed.

10 PAGE=&1100:D%=&E00:N$="Dragon2"
20 !&70=D%:!&72=PAGE:!&74=HIMEM-PAGE
30 P%=&76:[OPT2:LDX&74:LDY#0
40 LDA(&72),Y:STA(&70),Y:INY:BNE&85:INC&71:INC&73
50 DEX:BNE&7A:DEC&75:BNE&7A:RTS:]
60 VDU21:*KEY9LOADN$|M*TAPE|MCALL&76|MPAGE=D%|MNEW|MOLD|MRUN|F|M
70 *FX138,0,137

115. Saving Files

If you are using a program which saves files onto disc, (sequential files that is, not random-access ones), then you can come up against a snag. If the file is 'sandwiched' in between other files on the disc, and you attempt to save an amended version of that file under the same name, then you may run into trouble if the new version is longer than the old. You can get the error message "Can't extend", which means that the new version won't fit in the space available.

If this happens, and you are able to enter "*" commands without quitting the program, then *DELETE the file on the disc, and then save the new version again. This time, the file will not be put back in the same place, but will be put after all the other files on the disc. If there isn't room there either, then you will get the message "Can't extend", or much more likely,"Disc full". If this is the case, then you will have no option but to save your file on a separate disc, and *COPY it back onto the original after using *COMPACT.

Note that you do not get "Can't extend" errors with Wordwise-Plus, which in effect uses *SAVE rather than OPENOUT, but I seem to remember that you can get it with some if not all versions of Wordwise.

116. Comparing disc files (B/B+/Master)

It is useful if you can compare different files to see if any of them are the same. I often seem to end up with different versions of a program or file on different discs, but under the same filename. At other times, I find the exact same program saved under two different names. It can help to use *INFO, but this will only tell you whether or not the files are of the same length.

This short program will compare any two named files, whether BASIC, machine-code, text files etc., and check that they are of the same length, and that they are byte-for-byte identical. The first filename prompt will repeat until a valid filename is found on the appropriate disc, and then the second filename prompt is repeated until a valid but different filename is found. Obviously, the two filenames must be different, and they can be different in actual name, directory, or in drive. If you have two drives, then the two names might be "FRED" and ":1.FRED". If the two files are both on the same disc, then the names might be "FRED" and "A.FRED", or "FRED" and"FRED1", or "FRED" and "JOE". If a filename prompt keeps reappearing after you type a name in, then either that filename cannot be found on the disc, or else you are are typing in two identical filenames. The routine is compatible with the 6502 2nd processor.

The length of the file is displayed in Hex after each filename is entered. If the two files are not of the same length, then the program only takes a few seconds to run, otherwise it may take quite a bit longer if the files are large ones, and if any differences are not found until near the end of the files, if at all. Many of the spaces in the listing, but by no means all, can be omitted. If in doubt, type them all in at first. Note that there is a tilde in lines 130 and 160, looking like "~", and this is easily overlooked.

100 CLOSE#0:same=FALSE:ON ERROR GOTO 230
110 REPEAT:INPUT "1st Filename : " file1$
120 ch1=OPENUP(file1$):UNTIL ch1>0
130 PRINT TAB(6) "Length : &";~EXT#ch1
140 REPEAT:INPUT "2nd Filename : " file2$
150 ch2=OPENUP(file2$):UNTIL ch2>0
160 PRINT TAB(6) "Length : &";~EXT#ch2
170 IF EXT#ch1<>EXT#ch2 THEN 200
180 REPEAT UNTIL BGET#ch1<>BGET#ch2 OR EOF#ch1
190 IF EOF#ch1 THEN same=TRUE
200 PRINT "These files are ";
210 IF same THEN PRINT "identical" ELSE PRINT "different"
220 PRINT:CLOSE#0:END
230 IF ERR=194 THEN 140 ELSE CLOSE#0:REPORT:PRINT " at line ";ERL



NB: I have also written a version for BASIC programs only, which actually checks line-by-line, and tells you the line number(s) where the first difference is found. The difference may be in the contents of the line, or the actual line numbers themselves. It is too long to print here, but is available as two UUcode files entitled "BASIC COMPARE UTILITY"

117. Comparing programs with memory

This is a routine along the same lines as the previous one, but is for comparing a purely BASIC program in memory with a named program on disc. (It does not work with cassette.) BASIC in this context also included programs containing assembler code.

Since it is for comparing a program in memory with one on disc, the routine cannot conveniently be in the the form of a program itself. So, it is stored on a red function key ready for calling at any time. Before programming the key, (I have used *KEY0 in this example), you should type *FX18 to clear all the keys, as the routine takes up most of the available space on a B/B+. The key definition can be saved on a B/B+ with *SAVE name FFFF0B00+100, and reloaded at any time with *LOAD name . On a Master/Compact, save the definition as a one-line program, and simply CHAIN it to redefine the key when required. The routine is compatible with the 6502 2nd processor.

When called, the routine first prints out the length of the program in memory, and then prompts for a filename. This will repeat until a valid filename is found, and then the length of the disc file is also printed before a byte-for-byte check is carried out. There is a similar routine in BEEBUG, but it doesn't check for different lengths before laboriously comparing byte-for-byte, and with BASIC-I it is liable to try and lengthen the file if it is shorter than the program in memory!

Note that in order to squeeze everything on to one key definition, I have had to use many abbreviations, and only vital spaces have been retained. I have split the lines at convenient colons, but the definition is actually typed in as one continuous line. It may help to omit the first term VDU21|M initially, otherwise it tends to suppress any syntax error messages if you make a typing mistake. When the routine is debugged, you can restore the first term to give a neater effect on the screen.

*K.0VDU21|MCLO.#0:P." Length : &";~TOP-PA.:REP.I."Filename : "f$: ch=OP.f$:U.ch:P." Length : &";~EXT#ch:M%=-1: REP.M%=M%+1:U.EXT#ch=TOP-PA. ORBGET#ch=M%?PA. OR EOF#ch: IF EOF#ch TH.P."Identical"ELSEP."Different":CLO.#0|F|M

118. !BOOT file hint

Editing a !BOOT or other file which is to be *EXECed is rather tedious. If you want to alter it, you have to retype the lot, or else *TYPE or *LIST the old version, and then use the <Copy> and cursor keys. It is much easier to simply load it into WORDWISE or VIEW, edit it as required, and then save it in the normal way. Do not insert any embedded codes, and do not attempt to add the line numbers which *BUILD gives you. If you load an existing !BOOT file first, you will see how simple the format is. With Wordwise, leave one blank line under the last command, but do not with VIEW, or you will get an extra carriage return which may cause problems.

119. *RUN hint

You may abbreviate the command *RUN to just */ , saving two bytes of memory in the process. For example, with a DFS fitted, the command *FORM40 would attempt to *RUN a program called FORM40 off disc. However, in all probability, a utility ROM such as Disc Doctor, or maybe even the DFS itself, may assume you are attempting to use the resident FORMat facility with the wrong syntax. However, using the command */FORM40 instead would ensure that this doesn't happen.

120. *EXEC file nesting

You cannot 'nest' *EXEC files. You can have an *EXEC file calling another, which in turn may call another, but you can never return. Ie if an *EXEC file *EXECs another, then it must be in the final statement.

121. Merging BASIC programs

There is no single command which will merge a program on tape or disc with one in memory. This is a shame, as it is useful to be able to tack Procedures from a 'library' onto new programs being written. Some utility ROMs such as Beebug Toolkit incorporate a merge facility, but there are ways round the problem.

Method 1 is considered 'dirty' by the purists, but is very effective. Assuming that the main program is in memory, and that the Procedure or routine which you wish to add is on tape or disc, under the filename "fname". Type PRINT~TOP-2 , and note the 3 or 4 digit Hex number which is printed; I shall represent this as 'nnnn'. Now type *LOAD fname nnnn , followed by OLD , (vital), and the merge is complete.

This method adds the Procedure onto the end of the program, regardless of the line numbers. For example, your program may use lines 100-2500, and your Procedure may appear as lines 1000-1050 after line 2500. This looks weird, but can be cured simply by RENUMBERing the program. This oddity is actually very useful, because you don't have to worry about the line numbers in the Procedure clashing with those in the main program but if you don't want to renumber the program, then try method 2.

Method 2 is more tedious, but may be better on some occasions. First, load your Procedure or routine into memory. It is a good idea to renumber it at this stage, so that the line numbers will not clash with those in the main program. Now save it as a straight ASCII file, ie character-by-character rather than in compressed BASIC keyword token form. TYPE *SPOOL fname followed by LIST . When the listing stops, type *SPOOL on its own to close the file. If you are saving only one part of a longer program, then type LIST 1000,1050 etc instead of just LIST .

Now load your main program into memory and add the Procedure with *EXEC name. You will actually see the lines being added on the screen, together with a couple of "Syntax error" messages, which you may ignore. The lines are being added as if typed very quickly on the keyboard, so any common line numbers will be overwritten and the new lines will appear in the correct order. There is no need to subsequently renumber the program, unless you particularly want to. You can keep a library of these Procedures already in ASCII format, so you don't need to use *SPOOL every time. You can then just *EXEC them into your programs as required, which is fairly convenient. You could store them, each with a different set of lines numbers, but this would be hard to keep track of. Alternatively, store them all with high line numbers, say 30000 upwards and every time you add one to a program, renumber the program so that the highest line number becomes relatively low. In that case, you may find method-1 more suitable.

122. REM alternative

The characters "*|" have the same effect as a REM statement, and can conveniently be incorporated in *EXEC files etc.

Eg. *| This is my !BOOT file

123. VDU code reminder

You may not be aware that VDU codes may be used in place of some PRINT statements. For example, the term VDU(n) is directly equivalent to the term PRINT CHR$(n); . Note that the brackets are optional in both cases and that there is a semicolon after the latter term. Also, VDU31,x,y is directly equivalent to PRINT TAB(x,y);

Thus, the two statements below are directly equivalent, but the second one is shorter and tidier. This will not necessarily save much memory space as the second line is only one byte shorter than the first but even if you don't save any memory space, a shorter listing is an advantage in itself.

10 PRINTTAB(10,12)CHR$136;CHR$131;:INPUT"Name ? "N$

10 VDU31,10,12,136,131:INPUT"Name ? "N$

124. VDU oddities

VDU19, which chooses foreground and background colours from the palette, is equivalent to pressing <Ctrl+S> . I shall represent it by |S, since this is how you would store it on a red key.

To select a foreground colour of Green in Mode 0, you could use VDU19,1,2,0,0,0 or press |S followed by the keys 1 2 0 0 0 in turn. To send a zero byte in this way properly involves pressing |@. By pressing the 0 key directly, you are actually sending ASCII code 48. Thus you have sent the equivalent of VDU19,49,50,48,48,48 and in fact it does work perfectly as a VDU command; try it and see! You can do it 'properly' by pressing |S |A |B |@ |@ |@

I'm not quite sure what practical use you can put this new-found knowledge to, but there it is for what it's worth!

125. DIM oddity

If you have already dimensioned a numeric or string array in a program, such as A$(100), FRED(55) or mem%(300), then any attempt to redimension it will result in the error message "Bad DIM". In some versions of BASIC, though sadly not BBC BASIC, you can legally redimension an array, or reclaim the memory it occupies, provided you 'kill' it first, eg KILL A$ , ERASE A$ etc, (the syntax varies).
However, if you dimension an area of memory with the syntax DIM mem% 100 , as one might do if reserving memory for some data or machine code, then it can be redimensioned without killing it first, and without generating any error message. If this sounds too good to be true, then you're dead right! Every time you redimension the area, even if you are reserving the same or less space than before, the old area is 'thrown away', and new space reserved. If you don't do this too often, then it may be useful to you, but try the short program below and see how it rapidly gobbles up all the memory. This applies equally to all versions of BBC BASIC that I have come across. 10 REPEAT:DIM A% 50:UNTIL FALSE 20 END

126. Alphabetic comparison

ALPHABETIC COMPARISON. Most of you will be familiar with the use of the < and > operators to compare numbers, but not everyone may be aware that it
can be used on strings, thus forming the basis of an alphabetic sort. It is quite legitimate to use an expression like IF A$>B$ THEN GOTO 240

This can be used to compare "SMITH" with "BROWN", and it will decide that"BROWN" is less than "SMITH", because it comes sooner in alphabetical order. Lower case comes after upper, so "SMITH" is less than "Smith" for example.

In other words, the comparison is made in ASCII code order. This does mean that when you compare numbers in the form of strings, you get odd results if they do not both contain the same number of digits in front of the decimal point. For example "12" is less than "3" because it starts with a "1" rather than a "3", but "03" will correctly be found to be less than "12". Similarly, ".2" will be less than "0.1", because a fullstop comes before "0" in ASCII code order, but "0.2" rather than ".2" will be compared correctly.

127. ON-ERROR tip

An ON-ERROR statement can take several forms other than just simply ON ERROR GOTO . Here are a few ideas:

10 ON ERROR GOTO 140

10 ON ERROR RUN (or END or STOP)

10 ON ERROR PROCboob:GOTO 550

10 ON ERROR IF ERR=17 THEN 140 ELSE REPORT:PRINT" at line ";ERL:END

128. Date compression

When large numbers of dates need to be stored in a database, file space can very quickly be used up unless some economies are made. The first Function below accepts a date as its input, in the form of a six-character string, (eg 230286 for 23rd Feb 1986), checks that the date is valid, and compresses it to an integer. The Function returns the value zero (or FALSE) if an impossible date is entered, (eg 310985 or 290286), and this should be tested by the program.

If the date is OK the resulting integer can be stored in just two bytes, (it ranges from 33 to 51103), and can easily be sorted chronologically. (NB This routine does not do this conversion for you.) The date as a string needs six bytes, and would not sort properly unless it had been entered backwards! (eg 860225.) The second Function converts the stored integer back to a
six-character string.

1000 DEFFNdate_to_number(Z$):LOCAL D%,M%,Y%,N%
1010 IF LEN(Z$)<>6 THEN=FALSE
1020 D%=VAL(LEFT$(Z$,2)):M%=VAL(MID$(Z$,3,2)):Y%=VAL(RIGHT$(Z$,2))
1030 IF M%>12 OR M%<1 THEN=FALSE
1040 N%=30-(M%<8)*(M%MOD2)-(M%>7)*((M%+1)MOD2)
1050 IF M%=2 THEN N%=28-(Y%MOD4=0)
1060 IF D%<1 OR D%>N% THEN=FALSE ELSE=D%+32*M%+512*Y%
1070 :
2000 DEF FNnumber_to_date(Z%):LOCAL D%,M%,Y%
2010 D%=Z%MOD32:Z%=Z%DIV32:M%=Z%MOD16:Y%=Z%DIV16:Z%=D%*10000+M%*100+Y%
2020 =RIGHT$("0"+STR$(Z%),6)



129. Number compression

Further to tip 128, here is a way of storing that compressed date as a 2-byte integer. You must dimension some memory space twice the size of the number of dates to be stored. In the example below, I have assumed that 1000 dates are to be stored.

The first Procedure places the already compressed date Z% in position N% out of the list of 1000. Eg PROCstore(44119,500) actually places 23rd Feb 1986 in position 500 out of 1000. To read a stored date, the Function will return the compressed date stored at position N%. Eg PRINT FNfind(450) will print the compressed date stored in position 450 out of 1000. You can then use the second Function to turn it into a sensible date string. Using these Functions and Procedures as building blocks, you can incorporate them into your own programs.

100 DIM array% 2000
200 REM Program somewhere here.
3000 DEFPROCstore(Z%,N%)
3010 array%?(N%*2-1)=Z%:array%?(N%*2)=Z%DIV256:ENDPROC
3020 :
4000 DEFFNfind(N%):=array%?(N%*2-1)+(array%?(N%*2))*256

130. Coloured text and backgrounds

Changing colours using BASIC is a simple operation; the statement VDU19,1,2,0,0,0 will change the text colour to green in all modes except Mode 7. The same effect can be achieved by holding down the <CTRL> key and typing S12000 . When using VIEW the VDU command is not available, although the <CTRL> sequence will work. Neither of these methods will work in THE Watford Electronics Disc Sector Editor, for example; the only way to change colours here is by the *FX155,X operating system command which is rather complex. *FX155,X writes to register 1 (palette) of the video ULA, but in two-colour modes the command must be used 8 times with different values of X, just to change the text colour, and a further 8 times to change the background colour. (See The Advanced User Guide sections 19 and 22.)

The program below calculates the values of X for each foreground and background colour and transfers them onto disc as files. Before running the program make sure there is space for 16 small files on the disc; 8 files will be named as in the DATA statement in line 30, and 8 with the same names prefixed with the letter B. You can then delete any files that you feel you will not need. When using VIEW for example, entering *EXEC BLUE followed by *EXEC WHITEB will give blue text on a white background.

If you are NOT using the old BASIC-I on a BBC B, you can omit lines 500-530 and replace the PROCoscli in lines 40 and 80 with the keyword OSCLI .

10 DIM colour$(7)
20 FORI%=0 TO 7: READ colour$(I%): NEXT
30 DATA BLACK,RED,GREEN,YELLOW,BLUE,PURPLE,CYAN,WHITE
40 FORI%=0 TO 7: PROCoscli("SPOOL "+colour$(I%))
50 FORJ%=8 TO 15: PRINT"*FX155,";16*J%+I%: NEXTJ%
60 *SPOOL
70 NEXT
80 FORI%=0 TO 7: PROCoscli("SPOOL "+colour$(I%)+"B")
90 FORJ%=0 TO 7: PRINT"*FX155,";16*J%+I%: NEXTJ%
100 *SPOOL
110 NEXT
120 END
500 DEFPROCoscli(Z$)
510 $&C00=Z$
520 X%=0:Y%=&C:CALL&FFF7
530 ENDPROC

131. Stripey background hint

Modes 3 and 6 are text-only modes, with 25 rather than 32 lines per screen. This is achieved by leaving gaps between the lines, and is clearly illustrated by VDU19,0,4,0;0;0; which sets a coloured background. This gives a stripey appearance, which can be used to good effect. However, you are stuck with the black gaps between lines, and any user-defined characters on
adjacent lines will never join up. In the case of Mode 3, you may as well use Mode 0 instead, as they both occupy 20k of memory. However, Mode 6 takes up 8k as against the 10k of Mode 4, so it might be better on some occasions, unless you have Shadow RAM.

A clever chap called Cad Delworth, writing to Acorn User, has found that you can close up the gaps completely with VDU23,0,9,7,0;0;0; and to prevent any tendency for the picture to 'roll', you should follow this with VDU23,0,5,8,0;0;0; On the Master/Compact, these can be shortened to VDU23,0,9,7|23,0,5,8| The resulting screen has bigger margins at the top and bottom than normal, but this may be a small price to pay for being able to use a Mode which needs 2k less memory.

132. SPEECH! hint

The Superior Software SPEECH! software has two main commands, *SAY and *SPEAK. The former tries to pronounce the words phonetically, whereas the latter enables you to manipulate more subtly the pronunciation and pitch by building up words from 'phonemes'.

You may be very interested to know that when you use *SAY, the program translates this into *SPEAK format, and stores it in the cassette input buffer at memory location &A00. Thus, if you type *SAY HELLO FRED and then type PRINT $&A00 you will see "/HEHLOW FREHD". Now try *SPEAK /HEHLOW FREHD to prove the point. Similarly, *SAY MASTER 128 gives the result "MAA6STER WO5NTUH4WAY5T". This should help you in making up your own words, by seeing how the computer makes up similar ones.

133. ROM/RAM board 'fix'

Ever had problems with wobbly expansion boards, and that sort of thing? The effective low-technology solution is to apply a bit of the magic BLU-TACK in strategic places. With a Solidisk RAM board, try using it to stick the rear end to the UHF modulator can. It's so simple it's brilliant!

134. Writing 'legal' programs

Now that the BBC Micro 'family' includes the Model B, the B+, Master and the new Compact, it is all the more important to use 'legal' programming techniques, in order to reduce compatibility problems.

PS: There is now the Archimedes in the family of course!

135. Wordwise cursor hint

When printing or previewing with Wordwise or Wordwise-Plus, odd things sometimes happen if the cursor is not 'homed' to the start of the text first. I don't know if this has been cured on later versions.

136. Wordwise margin tips

This applies equally to Wordwise-Plus, of course. When mixing different character sizes on a printer, you have a problem. You must decide on the necessary Line Length and Left Margin settings in order to achieve approximately the same apparent left and right margins. Here, therefore, is a list of the Line Lengths and Left Margin settings I use for each character size. I have an Epson-compatible printer, but I imagine most dot matrix printers will be much the same in this respect. You cannot do condensed enlarged text to match these margins, but you should be able to manage it with condensed Elite on an Epson LX80.

SIZE STYLE COLUMNS SETTINGS

Enlarged Pica/NLQ 40 LL36 LM2
Standard Pica/NLQ 80 LL72 LM4
Standard Elite 96 LL86 LM5
Condensed Pica 132 LL123 LM7

Further to this, when changing from one character size to another, I find it best to issue the printer command first, (terminated in a White code), and then change the Line Length etc on the next line. It doesn't matter if you start your text on the same line as the Line Length command, but if the printer command is on the same line, or if it is on the previous line but not terminated in a White code, then the first line of new text tends to have the wrong left margin.

Below are five examples of changing to Elite text using ordinary Wordwise commands, (if you have Wordwise-Plus then OC27,77 becomes ES"M"). The first three tend to give odd results, but the last two are OK. In both of these, the printer command is not on the same line as the start of the text, and the line that it is on is properly terminated in a White code. I usually use the last method, but everyone to their own.

This demonstrates the point that if you don't at first get the result you expect from Wordwise, then try a slightly different way. Certainly, I find that mixing printer codes which change character size, with text on the same line, is almost guaranteed to cause trouble. This doesn't apply to commands for emphasized, double-strike, italic etc, nor to most of the other embedded commands.

WRONG: {G}OC27,77{G}LL86{G}LM5{W}This is your text.

WRONG: {G}OC27,77{G}LL86{G}LM5
This is your text.

WRONG: {G}OC27,77
{G}LL86{G}LM5{W}This is your text.

RIGHT: {G}OC27,77{G}LL86{G}LM5{W}
This is your text.

RIGHT: {G}OC27,77{W}
{G}LL86{G}LM5{W}This is your text.

137. Wordwise-plus line tip

A very simple way of drawing a straight, unbroken horizontal line across the paper is to use the commands {G}us{G}fi{G}ue{W}, where the {G} and the {W} represent the Green and White embedded codes respectively. The line will conform to the current Line Length and Left Margin settings, and of course the printer must already be in a suitable print mode. Sadly, this does not work properly on all versions of Wordwise-Plus.

138. Wordwise-plus bug

After acquiring version 1.4E of this splendid program, I found that unfortunately a bug has crept into the Full Indent facility. This bug was not present on my earlier version, and unfortunately 1.4E is actually on a ROM rather than on EPROM. This means any corrections can be expensive for Computer Concepts, and slow to be implemented. The irony is that I was perfectly satisfied with the version I had before, but it wasn't compatible with my new Master 128!

The bug appears in two situations. First, I showed you in the previous tip how you can easily draw a horizontal line across the page. Well, this no longer works, so you have to remove the "fi" term, and stick in the appropriate number of spaces or Pad characters. The second problem occurs if you use Full Indent when in the Double Strike mode; ie you have issued a "ds" somewhere prior to the "fi", but haven't cancelled it. The indent simply doesn't work in this situation. The solution is to use "de" just before the "fi", and then use "ds" again just after. I'm a great fan of Computer Concepts, but they are definitely getting a raspberry from me this time! I've
since acquired InterWord, and happily it doesn't appear to have similar bugs, (so far).

NB: The bugs have been cleared in version 1.4F!

139. Wordwise-plus paging hints

Three things occurred to Richard Hare after the group meeting in July 1986, when Wordwise-Plus was discussed, and here they are:-

1.
"Somebody said he wasted the first sheet of tractor-feed paper every time he used his Epson printer. This can be avoided if you pull the scale-bar towards you and only let it go back onto the paper after the first few lines have been printed. Better still, if the individual texts are short enough to allow quite a large top space, you can start printing with the perforation line along the top of the scale-bar ready for tear-off; use a top space of zero (TS0) and the first line of printing will come at line 6 in the case of an Epson FX80. Use HP0 if you are using a header, and TS4; the text will then start four lines below the header. Use BS10 and that will give you an actual bottom space of 4. Footers should be at FP2 or FP3; if the FP number is greater than 4 the footer will be beyond the perforation, at the top of the next page."

2.
"Somebody else mentioned that to get the paper to the start of the next page, when one text has been printed, he uses the operating control code for form-feed, OC12. That is fine, but if you are using a footer it will not be printed on the last page of your text. If you have a footer you should finish with a green BP, with no white afterwards and no carriage return. This will effectively do a form-feed at the end of the text and print the footer. If you press do <RETURN> or <f2> after the green BP it will line-feed the number
of lines in your top-space for the next page as well, and the header will be printed at the top. This is not desirable."

3.
"If you are defining the character strings while using WORDWISE-PLUS remember that it is fatal to define F$. F$ is used in the program, and if you try to use it yourself you will screw the whole thing up!"

140. ADFS filename snag

If you are using an Acorn ADFS, you must avoid using fullstops anywhere in filenames. Whereas an 'ordinary' DFS will accept "VIEW2.1" as a valid filename, the ADFS will treat this as filename "1" in directory "VIEW2". Since it is unlikely that you have such a directory on the disc, you will get the message "Not found".

I ran foul of this when using the Copyfiles utility to transfer files from a DFS formatted disc to an ADFS formatted disc. I had to *RENAME any offending filenames to get rid of the fullstops. You should also avoid using hyphens, as these are used in the syntax of a filename/filing-system path on a Master/Compact.

141. Reset to ADFS

There are occasions when you may wish to enter the ADFS filing system, without causing the disc drive to start up and look for a directory. You may have an 'ordinary' DFS disc in drive 0, and wish to *MOUNT an ADFS disc in drive 1. The behaviour of the ADFS in this situation does depend on the *CONFIGURE DIR (or NODIR) option. However, regardless of this, you can select ADFS without the drive starting up, by either pressing <Ctrl+F+Break>, or by typing *FADFS . The ADFS is a bit hard going at first, but by jove it's worth persisting!

142. Language problems

Some language ROMs will not work on the Master, (eg Commstar), as they give a"This is not a language" error. You can cure this by editing an 'image' of the ROM. Look through the first few bytes, and see if the 7th along, (address&8006), is &82. If so, edit this to &C2 and save the image. This should then enable the ROM to work, though not necessarily in SRAM if write-protection is needed. It doesn't mean that the ROM will necessarily be compatible with the Master, but it will at least be correctly recognized. Commstar, for example, does work fine after editing.

143. *MOVE syntax

The syntax of this useful command can be a bit tricky. It is used for moving individual files between filing systems, eg transferring from DFS to ADFS discs. To transfer a file from a DFS disc in drive 0 to an ADFS disc in drive/mount 1, use the form *MOVE -DISC-:0.name -ADFS-:1.newname Note the rather unfamiliar use of hyphens in the syntax. Directory names can be included where appropriate, and the drive specification could be omitted if the currently selected ADFS drive was 1 and the currently selected DFS drive was 0.

This is easily overlooked, in which case "Disc error" messages occur, so use the full syntax. To copy several files, it is better to use the Copyfiles ADFS utility program on the Welcome disc. Incidentally, if you specify the same filing system and drive for both source and destination, but specify a different directory or filename, then this a convenient way of duplicating a file on the same disc, without deleting the original as would happen with *RENAME.

73 Rick G4BLT @ GB7WRG

144. ADFS *COPY Syntax

The *COPY syntax on the ADFS is different from that on the DFS, and is a bit obscure. You must specify the filename to be copied, including the directory and/or drive specification if they are not current, and then the directory and/or drive specification of the destination, (ie just the pathname).

For example, suppose I want to copy a file called "Fred" from the current drive (0) and directory to the root directory on the other drive (1). This would be achieved with *COPY Fred :1 If I needed to include full pathnames, and I was copying from a sub-directory "NAMES" on drive 0 to a sub-directory"PEOPLE" on drive 1, then it might look something like *COPY :0.$.NAMES.Fred:1.$.PEOPLE . You can of course include the wildcard characters "#" and "*" in the filename if you are copying a group of files.

You cannot change the filename when copying in this way, but you can by using the same syntax with *MOVE, and adding the new filename after the destination pathname. *MOVE also has the advantage that you can add -ADFS-, -DISC- etc at the beginning of the pathnames to copy between filing systems, but it has the disadvantage of being appallingly slow. For copying between filing systems, those of you with the Advanced Disc Toolkit are better off using *XFER instead, as it is far faster.

145. Plotting ellipses

By referring to the list of PLOT commands on page 232 of the Master Welcome guide, you can easily work out how to draw rectangles etc., using similar methods to drawing triangles. However, drawing ellipses is a bit more tricky.

You must first 'visit' the dead centre of the ellipse, as you might expect. You must then visit the point where the ellipse cuts a horizontal line drawn through the centre. Since the Y-coordinate of this point is by definition the same as that of the centre, it doesn't matter what Y-coordinate you put in the MOVE, DRAW or PLOT command you use to visit the point, as the true value
is assumed automatically. The horizontal line cuts the ellipse twice, but it doesn't matter which of the two points you choose.

Finally, you should use the ellipse PLOT command, eg 197, to specify the highest point of the ellipse, ie the point with greatest Y-coordinate. (Alternatively, it can be the lowest point of the ellipse.) Anyone with access to the Acorn GXR manual should find no difficulty with the Master's extra PLOT commands, otherwise you need to see the Master Reference Manual part 1, which is at last available.

146. VDU terminator

There is a new terminator for the VDU commands, which sends 9 zero bytes. Thus, VDU23,1,0;0;0;0; can be replaced with VDU23,1| . It doesn't matter if too many zeros are sent, as they will have no effect.

147. Character founts

If you insert the ADFS Welcome disc and type *thin, *Italic or *7by8 , the screen ASCII character set is modified with interesting results in Modes 0 to 6; Cancel with *FX20 . For a comparison of all 3 of these founts, enter Mode 3, (but not Mode 131 or SHADOW modes), and type *LOAD LIBRARY.Fonts 4000 You can also modify some of the ASCII characters from 128 to 255, so try typing FORc%=128TO255:VDUc%,32:NEXT both before and after typing *EXEC LIBRARY.Pfont

148. ROM sockets

In addition to the two external cartridge sockets, which can support 4 ROMs, there are 3 spare internal ROM sockets. The only one which works initially is the centre one, (labelled IC27), and this corresponds to ROM slot 8. The rearmost socket, labelled IC37, corresponds to ROM slot 7, but this is normally configured as sideways RAM, (SRAM). To use if for a ROM, shift the nearby link LK19 to the right. The remaining socket at the front is labelled IC41, and corresponds to ROM slot 5, which is also normally configured as SRAM. To bring this into use, shift the nearby link LK18 to the right. Note that in gaining slot 7 as ROM, you lose SRAM slots 6 and 7. Similarly, in gaining ROM slot 5, you lose SRAM slots 4 and 5. This because each slot supports up to 16k, but ROMs can be from 8k up to 32k, and might need to occupy two slots. This means that you won't be able to run BAS128, which
needs all 4 slots as SRAM. I've had Disc Doctor, Wordwise-Plus and Printmaster all happily installed and working. When the plug-in cartridges are used, slots 0 to 3 are available for ROMs, so there should be no need to sacrifice SRAM.

149. When is a 1770 FDC not a 1770?

When it's a 1772! My Master would not work properly with an old Teac full-height drive. I tried using both *CONFIGURE FDRIVE and *FX255 to select 30ms head-step time to suit the slow drive, but that made matters worse. On examining the FDC, (Floppy Disc Controller), chip in my Master, I discovered it was a 1772 and not a 1770. Page C.5-4 of Part 1 of the Reference Manual has a little table relating to the *CO. FDRIVE parameter:-

Value Head Step Time Precompensation
(WD1770) (WD1772) (ADFS only)
0 or 4 6ms 6ms yes
1 or 5 6ms 6ms no
2 or 6 30ms 3ms yes
3 or 7 30ms 3ms no

The normal values to use are 0 or 2, but instead of increasing the step time from 6ms to 30ms, I was actually decreasing it to 3ms! Most 80-track drives will work fine on 6ms, and many are OK on 3ms, though not when switched to 40-track mode, (due to the double-stepping), but many older 40-drives won't work on either. This is a bit naughty of Acorn, as it could force Master buyers to change their drives. Whilst there are considerable advantages in upgrading to double-sided 80-track drives, people ought to be able to do this when they are good and ready! If you want to check which FDC chip is fitted, take the lid off, and peer under the back of the keyboard, more-or-less under the red <f5> key. You'll need a torch, and the keyboard ribbon cable does get in the way a bit!

150. Incorrect capacitors

On the Master, the capacitors C85 and C86 are located either side of IC43, near the UHF modulator can. They should be 10nF, (same as 0.01uF or 10,000pF) and may be marked "103". Some machines were accidentally fitted with 100pF capacitors, which may be marked "101". These cause overheating problems when a Turbo 2nd processor board is fitted, and should be changed.

151. Interword tab hint

The manual tells you that you can delete TAB markers from a ruler, (as opposed to the TAB codes in the text), using <Delete>, <Copy> or <Ctrl+A>. What it does not tell you is that you can also add them with the <Tab> key, after positioning the cursor at the desired place on the ruler.

152. InterWord line hint

With Wordwise-Plus, (though not version 1.4E), you could draw an unbroken horizontal line across the page with {G}us{G}fi{G}ue{W}, which would automatically conform to any changes in margins and line length. NB: {G} & {W} represent Green and White codes.

You can achieve a similar result in InterWord as follows. Make sure the line to be used is blank, and that there is, (temporarily at least), a blank line immediately above and below it. (It helps to have the RETURN and TAB codes visible, ie Screen codes ON.) Press <f3> Insert Marker, <Tab>, <f6> Align Right, <f3> Insert Marker, Cursor Left one space, <Shift+f4> Underline. You can now remove the blank lines above and below if you wish.

153. View extended highlights

Within VIEW there are two commands known as Highlight-1 and Highlight-2, which are used to mark parts of the text to be printed in a special way. The printer is controlled by a printer driver routine within View, which interprets the Highlight commands and converts them into codes which the particular type of printer in use can understand.

Normally, Highlight-1 inserts an underline "_" character into the screen text and sends a code 128 to the printer driver, which in turn produces underlined text on the printer. Similarly, Highlight-2 inserts an asterisk, sends code 129 and produces bold printed text.

To improve on this limited situation, use can be made of what Acorn call Extended Highlights. By resetting Highlight-2 to produce code 130, and loading one of the Acorn Printer Driver routines from disc, (costs about #10-00), various printer effects can be called upon by using combinations of Highlights 1 and 2. For example, italics can be created using Highlights 2+1+2, which would appear as "*_*" on the screen. The Acorn Printer Driver disc contains routines for most common printers, and a program is available to create your own if you wish. Highlight 2 is reset by entering as the first line on the edit screen:- 'Edit Command' HT <RETURN> 2 <SPACE> 130 .

The function keys, in conjunction with <CTRL> and <SHIFT>, can be used to provide the appropriate codes, which can be installed using a !BOOT setup; *FX228,1 enables the <Ctrl+Shift> facility. For further explanation of the facilities, refer to the Printer Driver User Guide and your Printer User Guide. Remember to include all the spaces when typing in, as they are vital, and don't confuse "!" with "|". For clarity, spaces are shown as a "-", and note that some definitions have a space at the END, not just in the middle.

*WORD
*FX228,1
*KEY0|!|-
*KEY1|!!|!!|!!
*KEY2|!!
*KEY3|!!|!|-
*KEY4|!!|!!
*KEY5|!!|!!|!|-
*KEY6|!!|!|-|!|-
*KEY7|!!|!|-|!!
*KEY8|!!|!|-|!|-|!|-

The facilities created using <Shift+Ctrl> + Function keys are:-

f0 f1 f2 f3 f4 f5
Under- Bold Next character Begin Begin Cancel Sub line On/Off to extension Subscript Superscript or Super- set script

f6 f7 f8
Alternative Italics Reset
Font on/off on/off

154. !BOOT file for ViewSpell

During a demonstration of 3 spelling checker packages, ViewSpell by Acorn came out as the worst, mainly because there was a lot of work involved for the user with originally generating the text in View, calling up ViewSpell, doing a disc change, making the ROM work, another disc change, then back into View for the results of the check!

Yes, done this way it can be a nightmare! I have only a single 80 track double-sided Cumana drive, hooked up to an Electron with 1770 DFS, (you may snigger but PAGE is always E00), and have configured my !BOOT file, placed on the master dictionary disc, to do most of the work, including searching a number of user dictionaries. The disc is set up in the following way.

Place all the dictionaries on side 2 and the text file to be checked as"text" on side 0. You will tell the ViewSpell system where all these bits are by the use of the PREFIX statements in the software when setting up the !BOOT file. The 'line' numbers below are the ones which appear when you are *BUILDING the !BOOT file, so don't type them in!

NB: Lines 10 and 11 refer to user dictionaries; yours will no doubt differ. Make sure you are able to call both View and ViewSpell from the existing system.

Line 12 is a blank. This is where the system asks for a user dictionary, and if using the keyboard one would only press <RETURN>. The !BOOT file therefore does it for you. Also, the statement in line 16 sets View into Format and Insert modes; your version may differ and may need a small change.

After you have built your !BOOT file, save your text as described above, and then !BOOT the disc. When the system has finished you will be presented with the 1st item of your document which is spelt incorrectly. This is where you take over again after all the hard work has been done!

1 *BASIC
2 MODE 6 (or MODE 7, but then leave out line 3 for text colour)
3 VDU 19,1,3,0,0,0
4 *SPELL
5 PREFIX M :2.
6 PREFIX U :2.
7 PREFIX T :0.
8 LOAD TEXT
9 CHECK
10 OTHERS
11 BAD
12 (Just press <RETURN> here)
13 MARK MARKER
14 MODE 3
15 *W.
16 SET FI
17 LOAD MARKER
18 SEARCH #!

155. Splitting large files

This program was written in order to split a very large word-processing file that was too big to fit into memory. (Any type of file can be split; not just text files.) This may happen if the micro which saved the data had a 6502 2nd processor, Shadow RAM, or some other means of gaining memory, whereas yours may not. The program splits large files into smaller ones, whose length in
bytes is determined by the variable max% in line 10. You should substitute the name of the large file for "oldname", and the name of the smaller subfiles for "newname". The subfile names will be suffixed by "1", "2" etc, so you will be limited to 6 characters on an ordinary DFS.

This program will split a file in mid-word, so if you don't want it to do that, you can replace the expression PTR#c2=max% in line 40 with (PTR#c2>=max%ANDg%=32) ; the brackets are very important. This will split the file at the first available Space, but if you put 13 instead of 32, the file will be split at the first available Carriage Return, which would usually be at the end of a paragraph. Many refinements could be added, and the program is rather slow, but it does the job.

10 max%=15000:f%=1:c1=OPENIN("oldname")
20 REPEAT:c2=OPENOUT("newname"+STR$(f%))
30 REPEAT:g%=BGET#c1:BPUT#c2,g%
40 UNTIL PTR#c2=max% OR EOF#c1:CLOSE#c2:f%=f%+1
50 UNTIL EOF#c1:CLOSE#c1

156. INKEY(-256) command

This command does not appear to be documented in any of the official Acorn manuals. Instead of checking for a key being pressed, it returns a number which indicates the type of machine in use. This can be handy when writing programs which are to be used in several different types of machine. Eg, to avoid the *SHADOW command upsetting a standard Electron or BBC model B,
whilst executing it on a B+ or Master/Compact, you could use IF INKEY(-256)>1 THEN *SHADOW1 The numbers are:-

BBC B with new 1.00+ OS -1 (including 1.20)
BBC B with old 0.10 OS 0 (obsolete!)
Electron 1
BBC B+ 64/128 251
Master 128 253
Compact 245

and now:
Archimedes (Arthur OS) 160 (obsolete)
Archimedes/A3000 (RiscOS) 161
Acoen A5000 ???

157. Calendar functions

The first of these, FNnumdays at line 1040, is derived from a short Function I saw on Micronet, credited to Frank McAree. It returns the number of days in month m% in year y%; eg PRINT FNnumdays(7,1987) would print the number of days in July 1987. It takes account of all Leap Years, which the original didn't. Years divisible by 4 are Leap years, except for those divisible by 100 but not by 400. Eg the year 1900 wasn't a Leap year, but 2000 will be. The present Gregorian calendar system dates from 1752 in most countries; before that it was chaos!

The second, FNday at line 1070, is one refined from an Acorn User program by Robin Newman. It returns the day of the week on the date d%,m%,y%; eg PRINT FNday(22,3,1986) would print the day of the week on 22nd March 1986 as a string, "Sat". If however, you want the day as a complete word string, eg"Saturday" rather than "Sat", then put the full words in line 1050, padded
with trailing spaces to 9 characters each, ie"Saturday-Sunday---Monday---Tuesday--WednesdayThursday-Friday---", ("-"
represents a space), and alter both the figures "3" to "9". If you would rather it returned the day of the week as a number, then alter line 1050 to =day% . This gives 0 for Saturday, 1 for Sunday and so forth, so if you would prefer 7 for Saturday instead of 0, then alter line 1050 to =day%-(day%MOD7=0)*7 . (This is more elegant and structured than using IFday%=0THEN=7 ELSE=day% .)

These two Functions can be used quite independently, but the Procedure PROCcalendar at line 1000 shows just one way in which they might be used together. It displays a crude calendar for month m% in year y%, eg PROCcalendar(7,1987) for July 1987.

1000 DEFPROCcalendar(m%,y%):LOCALd%
1010 FORd%=1TOFNnumdays(m%,y%):PRINTFNday(d%,m%,y%)" ";d%:NEXT
1020 ENDPROC
1030 :

1040 DEFFNnumdays(m%,y%)
1050 =30+ABS((m%>7)+(m%MOD2))+(m%=2)*
(2+(((y%MOD4)=0AND(y%MOD100)>0)OR((y%MOD400)=0)))
1060 :
1070 DEFFNday(d%,m%,y%):LOCALday%
1080 IFm%<3THENm%=m%+12:y%=y%-1
1090 day%=d%+2*m%+INT(0.61*(m%+1))+y%+y%DIV4-y%DIV100+y%DIV400+2
1100 day%=day%MOD7
1110 =MID$("SatSunMonTueWedThuFri",day%*3+1,3)



158. Multiple windows

It is possible to give the illusion that there are several independent text windows defined on the screen simultaneously. As soon as you define a new text window, the previous one is 'forgotten'. However, as long as the program 'remembers' where the cursor was, the same window can be redefined later, and the cursor put back in its original position. The short demonstration program below displays two independently scrolling windows on the screen. VDU31,x,y is equivalent to PRINTTAB(x,y); and the term FSGNRND=1THEN is used to make the two windows scroll at different, varying rates. Omit this term, leaving just VDURND(95)+31 to see what I mean.

100 p1%=0:v1%=0:p2%=0:v2%=0:MODE7:VDU23,1,0;0;0;0;
110 REPEAT:PROCwindow1:PROCvdu:PROCwindow2:PROCvdu:UNTILFALSE
120 :
1000 DEFPROCwindow1:p2%=POS:v2%=VPOS
1010 VDU28,0,11,18,0:VDU31,p1%,v1%:ENDPROC
1020 :
1030 DEFPROCwindow2:p1%=POS:v1%=VPOS
1040 VDU28,21,24,39,13:VDU31,p2%,v2%:ENDPROC
1050 :
1060 DEFPROCvdu:IFSGNRND=1THENVDURND(95)+31
1070 ENDPROC

159. Accelerating step rate

This is a simple program by Andrew G8YHI, to demonstrate an idea. If you press and hold either the Up or Down cursor key, a number on the screen will be incremented or decremented accordingly. The longer you hold the key down, the faster the number changes. This would enable values to be altered by large amounts easily and quickly, whilst still allowing 'fine tuning'. Perhaps some enterprising person could tidy this up into a more general-purpose procedure?

10 CLS:X=33:VDU23,1,0;0;0;0;
20 REPEAT:PROCchange:UNTILFALSE
30 :
100 DEFPROCchange:TIME=0:REPEAT
110 IF INKEY(-58) THEN X=X+(TIME/2000) ELSE IF INKEY(-42) THEN X=X-(TIME/2000)
120 Y=INT(X):PRINTTAB(10,10)Y:UNTILINKEY(-129):ENDPROC

160. Killing ROMS

I suppose that most Beeb users who have fitted extra ROMs have had problems with clashing "*" commands. When I first got my Beeb, I bought Computer Concepts Disc Doctor. I still have it fitted and it is arguably still the best standard utility ROM available for a Beeb that has an 8271 disc controller.

One of its commands is *MENU, which presents a pleasant uncluttered menu screen, enabling both BASIC and Machine-Code programs to be run by pressing a single key. It will even automatically handle downloading of programs to a PAGE value of say, &E00. Since it was so easy to use, I built the *MENU command into the !BOOT file on many of my discs. You can imagine how annoyed I was when later having fitted another utility ROM, Enigma, I found that it clashed with the *MENU command in Disc Doctor. All my discs wouldn't boot up, despite the fact that Enigma didn't even have a *MENU command among its own list of routines!

The time had come to temporarily immobilise the offending ROM. But how best? There is a command within Enigma that will turn off any ROM including itself, but when the <BREAK> key is pressed, as with <Shift+Break> to boot up a disc, the ROMs are reactivated. The switch-off command can be built into the !BOOT file on the disc, but you get a messy screen as the routine is implemented, and of course it only works if you happen to have Enigma. What is required is a simple method of switching off any offending ROM to allow the "*" Command to be acted upon by the required ROM.

Now on start up, <BREAK> or <CTRL+BREAK>, the OS copies the ROM type code for each of the ROMs that are fitted, down to page 2 for its own use. The table starts at &2A1; the 1st location corresponds to socket 0, the second to socket 1 etc.. If the value zero is placed in the table, then the OS will not recognise the ROM in the associated socket and will not offer it any "*" commands. Thus the solution is to 'poke' a zero into the correct address from the !BOOT file. The address in hex can be calculated by entering:-
PRINT~&2A1+(n) , where (n) = the socket number.
So to turn off any ROM, first find the socket number (n) of the ROM to be disabled, 2nd Calculate the address to be poked and then build up the !BOOT file. Since Enigma in my machine is in socket 7, the !BOOT file is built up as follows.

*BUILD !BOOT <RETURN>
1 ?&2A8=0 <RETURN>
2 *MENU <RETURN>
3 <ESCAPE>
*OPT 4,3 <RETURN>

Pressing <Shift+Break> should now boot up the disc with no problems. If you don't have a routine to find out which socket your ROMs are in, then the following program will show you what you have fitted and where. For a shortened printout which will fit across a Mode 7 screen, omit line 1020 and alter the MODE statement in line 100 from 3 to 7. Note that ROM images in SRAM will not be shown until initialised, ie <CTRL+BREAK>.
10 REM ROM Identifier by R.Sterry 09.06.85 modified 14.09.87
20 :
100 MODE3:@%=&902:FORY%=0TO15:PRINTY%,~Y%" "FNrom:NEXT:@%=&90A:END
110 :
1000 DEFFNrom:LOCALstring$,off%,off1%,byte%
1010 off%=FNpeep(7):IFY%?&2A1=0ORoff%=&80THEN="EMPTY"
1020 REPEAT:off%=off%+1:byte%=FNpeep(off%):UNTILbyte%<32ORbyte%>126
1030 FORoff1%=9TOoff%-1:byte%=FNpeep(off1%):IFbyte%<32THENbyte%=32
1040 string$=string$+CHR$(byte%):NEXT
1050 =string$
1060 :
2000 DEFFNpeep(mem%):!&F6=&8000+mem%:=USR(&FFB9)AND&FF

161. Instant migraine

A rather sadistic Marcus Wilson from Harrogate sent in the following one-line program, which is guaranteed to make you feel queasy very quickly! Master& Compact users can substitute VDU19| for the last statement (ie the vertical bar symbol).

10 MODE2:?&360=255:VDU19,0,0,0;0;0;

162. High-quality audio output

If you want to feed audio from a BBC B into an external amplifier, you can use the loudspeaker output, but there is a better-quality output available. Locate the point where the purple -5v supply wire from the front right of the PSU connects to the circuit board, and then look slightly nearer the front, just underneath the rear edge of the keyboard. You should find two solder pads marked "PL16"; the rear (North) pad is Ground (0v), and the front (South) pad is the output. This output is connected to the input of the internal volume control "VR1", and is at a level suitable for feeding into a hi-fi amplifier. There is a similar output marked on the BBC B+ circuit diagram as "S21", and apart from surmising that it will be close to the internal volume control "VR2", I have no other details.

163. BASIC program format

This is how a BASIC program is actually stored in memory. The program starts at the current value of PAGE, which denotes the lower edge of available memory. If you PRINT~PAGE you should get something like E00 or 1900, depending on whether or not you have a DFS etc fitted. The value ('byte') stored in this 1st location, is &D (Hex) or 13 (Dec), which is the code for a Carriage Return (CR). Whenever a program is present, then PRINT~?(PAGE) should indeed give "D". (If you have a utility ROM like Disc Doctor, then you have a much easier way of examining memory.) The next byte is the Most Significant Byte (MSB) of the 1st linenumber, and the one after is the LSB. You can check this as follows: Suppose the 1st linenumber is 100. The MSB is given by PRINT~100DIV256 , and the LSB is given by PRINT~100MOD256 . This should give you 00 and 64 respectively, (note the convention of expressing Hex bytes as 2 digits). Thus PRINT~?(PAGE+1),~?(PAGE+2) should indeed give you 00 and 64. Note that the MSB is always 00 for a linenumber of 255 or less; more about that later.

The next byte, ie at (PAGE+3), is the total length of the 1st line of the program, including the two linenumber bytes, including the length byte itself, and including the Carriage Return (0D) byte at the end of the line. Note that BASIC keywords such as REM and PRINT are stored as just 1 byte each, and that some also include the opening bracket, eg MID$( . You can look up these single-byte 'tokens' in your User Guide, indexed under"Tokens" or "Basic Tokens". For example, the keyword NEXT is tokenised as
ED. As a simple example, the basic line 110NEXT would be coded as 00 6E 05 ED 0D. Note that there would be another 0D immediately before the 00 in the unlikely event of this being the 1st line in the program, but the length byte would remain as 5.

The following lines are coded in exactly the same way, and the end of the program is indicated by an FF immediately following the 0D at the end of the final program line. (Do not confuse this FF with the keyword token for the BASIC word END, which happens to be E0.) The very next location after this, which is the 1st 'free' one after the program, is denoted by TOP . Thus if you PRINT~?(TOP-1) you always get FF. As a practical example, here is a simple 2-line program fully tokenised, with a 'translation'
underneath. You will notice that the code for a Space is 20 in Hex, and this is very commonly found in programs!

                         10REM HI
PAGE 20PRINT"LO" TOP-1
v v
0D 00 0A 08 F4 20 48 49 0D 00 14 09 F1 22 4C 4F 22 0D FF
^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
R Line Line R S H I R Line Line P " L O " R End
e 10 Len E p e 20 Len R e
t 8 M a t 9 I t
u c u N u
r e r T r
n n n

Cynics among you will point out that there doesn't seem to be any memory saving as a result of tokenising. However, you must remember that each line takes up at least 4 bytes before you've started, ie the linenumber bytes, the length byte, and the 0D at the end. Thus, for very short lines such as I have used in the example, there is little or no saving in memory space, particularly as I have only used 2 keywords. The more you cram onto one line, and hence the fewer linenumbers you use in the program, the better the
economy. Typically, a tokenised program contains about 70% of the characters counted in a listing.

There is just one little complication, and that is when a GOTO or GOSUB or THEN (implied GOTO) destination linenumber is encountered. You might expect to find the appropriate token, E5 E4 or 8C, followed by 2 bytes for the destination. In fact, the linenumber appears as 4 bytes; the 1st is always 8D as an indicator, followed by 3 bytes which are encoded in a really weird way.
One final little quirk. When you press <BREAK> or type NEW, the 2nd byte of the program at PAGE+1 is changed to FF. This tells the computer to ignore the program, which is still intact, and pretend there is nothing there. When you type OLD, the byte is changed back to 00, and the program 'reappears'. Now, it so happens that this byte is also the MSB of the 1st linenumber, and since it is reset to 00 rather than to whatever it was before, odd things happen if the 1st linenumber is greater than 255. Try typing in a two-line program with 1000 as the 1st line, and 1010 as the 2nd. Now type NEW followed by OLD, and then LIST, and see what happens to line 1000! The linenumber has been changed to 1000MOD256, which is 232. In other words, the linenumber bytes have been changed from 03 E8 to 00 E8. Although this is quite harmless, it is more than a little disconcerting to the uninitiated!

164. "HARD" BREAK/RESET

This can be achieved in software with the statement ?&FE4E=&7F:CALL !-4 (or CALL !&FFFC). Without the ?&FE4E=&7F, it is only a soft break.

165. Date-stamping discs

It is easy to overlook the important task of regularly backing-up your discs. This is all the more important when using double-density DFSs, as so much information can be carried on just one disc, (up to 640k with Acorn ADFS). As a record of when a disc was last backed-up, you could save a dummy file of zero length, with the date as the filename, eg *SAVE !7thDec 0 0 . The form of the date doesn't really matter, but the use of a "!" prefix followed by a number as the next character, will make the file appear first in the catalogue, even before !BOOT. Users of the Master 128 can automate this procedure to some extent, as shown in the next hint. Another way of subsequently altering this dummy date file is to simply *RENAME it.

166. Date-stamping discs (for Master)

The routine below, shown as a red key definition, will automatically save the current date as a dummy file of zero length, eg "!28Nov86". The use of a red key is handy if you are going to back up several discs in one session. If using the ordinary 1770 DFS, you are limited to 7 characters per filename, so you will have to omit the "!", or perhaps the "8" from "86".

The idea is to *MOUNT the source disc, *DELETE the old date file, *SAVE the new date with this routine, and then *BACKUP the disc. Alternatively, you can *RENAME an existing dummy date file. Thus, you know exactly when the disc was last backed-up, so there is no excuse for forgetting!

If you have a Utility ROM with *BACKUP on it, such as the Advanced Disc Toolkit, then you can make this whole operation very simple using the red keys. If not, then you could perhaps incorporate this routine on a line of BASIC in the BACKUP program provided on the Welcome disc. Abbreviations have been used, which is why some brackets appear to be missing. When typed as a
line of BASIC, (without the *K.0) the keywords will appear in full when LISTED, and all will be clear.

*K.0 OS.("SA. !"+M.TI.$,5,2)+M.TI.$,8,3)+M.TI.$,14,2)+" 0 0")|M

167. Auto-date function (for Master/Archimedes)

This Function is for adding to programs which expect a date entry of the form dd.mm.yy , eg 25.05.86 . The date is automatically extracted from the internal TIME$ function, thus saving the bother of manually entering it. Eg D$=FNdate . This Function occupies much less memory, and uses far fewer variables, than a version published in a recent issue of Beebug. If you want to save even
more memory, then add lines 2010 and 2020 onto the end of line 2000. Note that in line 2030, m% is the month number 1-12, and the RIGHT$ statement is to format it as a string, with a leading zero if it is less than 10.

2000 DEFFNdate:LOCALm%
2010 m%=INSTR("JanFebMarAprMayJunJulAugSepOctNovDec",MID$(TIME$,8,3))
2020 m%=(m%-1)DIV3+1
2030 =MID$(TIME$,5,2)+"."+RIGHT$("0"+STR$(m%),2)+"."+MID$(TIME$,14,2)

168. Drawing text boxes (for Master/Compact)

This Procedure makes use of the extra characters built-in to the Master. It enables you to draw a neat box round text, without resorting to graphics commands such as MOVE and DRAW. You specify the Left, Bottom, Right and Top TAB X-Y co-ordinates of the box, just as if defining a text window. The VDU28 command in line 1040 defines a text window inside the box, and can be omitted if not required. The example shown is in Mode 0, but it works in any 32-line Mode, ie 0/1/2/4/5, but will not look very nice in Modes 3 & 6 unless you make use of the "STRIPEY BACKGROUND" hint given in the 1986 tips compilation.

10 MODE0:PROCbox(20,20,60,10):END
20 :
1000 DEFPROCbox(L%,B%,R%,T%):LOCALV%
1010 PRINTTAB(L%,T%)CHR$(163)STRING$(R%-L%-1,CHR$(166))CHR$(165)
1020 FORV%=T%+1TOB%-1:VDU31,L%,V%,169,31,R%,V%,169:NEXT
1030 PRINTTAB(L%,B%)CHR$(170)STRING$(R%-L%-1,CHR$(166))CHR$(172);
1040 VDU28,L%+1,B%-1,R%-1,T%+1:REM Omit if not required
1050 ENDPROC

169. !BOOTing convert (for Master)

If you have some programs which will only run after you have used the CONVERT program off the Welcome disc, you can put them together on one disc, with the following !BOOT file:- IF INKEY(-256)=-1 THEN CHAIN "CONVERT" ELSE CHAIN "MENU" . This will run the CONVERT program if necessary, so that next time you boot the disc, the main menu or other selected program will be run instead. When transferring CONVERT from the Welcome disc, use *COPY or *MOVE rather than LOAD and SAVE, as it consists of a few lines of BASIC, followed by some 'hidden' machine code to be loaded into SRAM.

170. Composite video colour (for Master)

To get a colour signal from the CV output socket, I hear that you can solder a 470pF ceramic capacitor between the emitter (right leg) of transistor Q12 and the base (centre leg) of Q13. These are located between the CV and RGB sockets, and although it is a slightly delicate soldering job, it is nowhere near as tricky as the equivalent modification on an early BBC B. 73 Rick G4BLT @ GB7WRG

171. Negative INKEY code (for Master)

The code numbers for negative INKEY commands are given on pages D.2-39 and D.2-40 of the Master Reference Manual Part I. The list given omits to tell you that the code for the decimal point is -77 on the keypad, (-104 on the keyboard).

172. Another calendar function

This Function will calculate the Julian Day Number which starts at midday on the Gregorian date specified, eg PRINT Njulian(25,12,1987). Keen astronomers will know the significance of the Julian Day Number, but it has a more mundane use too. It provides a very simple way of calculating the exact number of days between two dates, taking full account of all Leap Years; you simply subtract the two Julian Day Numbers. eg:
PRINT FNjulian(25,12,1987)-FNjulian(28,5,1951) .
One small point; 1987 had a 'Leap Second' added to it to allow for the fact the Earth's rotation has slowed very slightly. Do not throw your Quartz watches away!
Note that I have split line 1010 to make it look tidier; it is actually all one continuous line.

1000 DEFFNjulian(d%,m%,y%)
1010 =367*y%-7*(y%+(m%+9)DIV12)DIV4-3*((y%+(m%-9)DIV7)DIV100+1)DIV4 +275*m%DIV9+d%+1721029

173. ROM problems

This is a fairly typical tale, and illustrates how careful you have to be in solving strange compatibility problems, and how careful you have to be in fitting upgrades. Ray Chand upgraded his BBC B with a Watford solderless ROM Socket Board, (very similar to the ATPL Sidewise board). First, the connections on the underside of the board contacted the top of the metal crystal sticking up near the rear of the BBC main board. PVC tape didn't help too much, as the sharp connections soon punctured it. I suggested using a double-sided sticky pad on the top of the crystal, with the protective strip left on the upper surface. Secondly, the leads to the S21 link pins stuck up too high, preventing the ROM board from seating properly; no choice here but to bend them out of the way. Finally, when the OS ROM was transferred to the ROM board, it prevented the keyboard from seating properly; spacing washers under the keyboard can help here. I have had similar problems with various makes of ROM boards on certain versions of the BBC - component heights and layouts do vary between production sub-contractors and between board version numbers.

After overcoming the physical problems, Ray found that several discs, including Mini-Office II wouldn't behave unless either Wordwise-Plus or SpellMaster was removed. The reason is that SpellMaster 'grabs' one page of memory, when it detects WordWise or View - ie PAGE goes up by &100 (256) bytes. The solution was to type *WORKOFF followed by <BREAK> to disable SpellMaster. An ATS ROM was causing similar problems for much the same reason. All was then well until he swapped the Acorn 1.20 DFS ROM for a Watford 1.42 DFS. *WORKOFF produced an error message, and so did *CWORKOFF, (you can prefix Computer Concepts' commands with a C, and Beebug's with a B, to avoid clashes with other ROMs). This still didn't work, but putting the DFS ROM in a lower priority socket to SpellMaster did cure the problem. It appears that the Watford DFS *WORK command was clashing, though it strictly speaking shouldn't have done. The lesson to be learned from this tale must be that patience and a methodical approach will usually overcome such problems in the end, and that it is worth persisting.

174. Decimal-Hex-Binary converter

This is a simple program which enables you to enter a decimal, hexadecimal or binary number, and it then prints out the same number in all three formats. It is intended for decimal numbers from 0 to 255, hex numbers from &00 to&FF, (just prefix the number with "&"), and binary numbers from 00000000 to 11111111, (you must type 8 digits). Thus, typing in "70", "&46" and "01000110" should all produce exactly the same results, but note that there is little trapping against silly inputs.

A nice refinement of the program would be for it to also print out the equivalent ASCII character, and to allow a single ASCII character input as an alternative to the ASCII code. There isn't enough space here, and anyway you can do that for yourself, CAN'T YOU?


10 REPEAT
20 INPUT"Number : "A$:IFLENA$<4THENA%=EVAL(A$)ELSEA%=FNbindec(A$)
30 PRINT;A%" &";~A%" ";FNdecbin(A%)'
40 UNTILFALSE
50 :
100 DEFFNbindec(A$):LOCALA%:IFLEN(A$)<8THENRUN
110 FORB%=7TO0STEP-1:A%=A%+VAL(MID$(A$,8-B%,1))*2^B%:NEXT
120 =A%
130 :
200 DEFFNdecbin(A%):LOCALA$
210 FORB%=7TO0STEP-1:A$=A$+STR$((A%AND2^B%)DIV2^B%):NEXT
220 =A$


175. TRACE

An associate had cause to use this rarely-needed command. Having checked down the screen, a point was reached at which he was sure his program worked, so he knew the error was further down. Looking at the TRACE command he observed
that:

TRACE 'line num.'

caused the computer to report only line numbers below 'line num.'.

However, on entering the command he found that only lines ABOVE 'line num.' were reported. He had interpreted "BELOW" as meaning BELOW in the listing; ie HIGHER line numbers. Evidently the message intended was that TRACE 'line num.' would cause the computer to report only line numbers LESS than 'line num.'. - Work this one out!!

176. Simple menu selection

When selecting one item from a screen menu, there is no need for each menu option to be tagged with a number; it could just as easily be a letter. Eg L = LOAD, S = SAVE, X = EXIT, etc.. You can then easily translate this into a number, so that you can use ON ... GOTO/GOSUB . A useful refinement would be to replace the GET$ with CHR$(GETAND&DF) , so that the Caps/Shift Lock state wouldn't matter.

10 REPEAT:G%=INSTR$("LSX...etc.",GET$):UNTILG%
20 ON G% GOTO 110,180,250 etc.

177. Remarks & all that

Have you tried putting remarks in your programs without REM....; it can be done if you slot them in between ENDPROC and DEFPROCabc after the END of your program, e.g.

Lines 10 to 240 Program
Line 250 END
Line 500 DEFROCxyz
Line 620 ENDPROC
Line 700 This is a sample of what you can
Line 710 do, with no REM's required.
Line 800 DEFPROCefg, etc, etc

178. Discy flopps

Virtually all floppy discs come with glowing reassurances like 'Life-Time Guarantee' or 'Fully Certified', so why do some discs cost many times the price of others? Floppy discs have a surface noise factor, rather like the hiss on an ordinary audio tape which can, under some circumstances create errors and make the disc unreadable as the computer cannot distinguish between noise and data.

Data pulses are inevitably recorded at different levels and the clipping level is the lowest level of a data pulse compared to the level of the average pulse; the normal standard is 40% which should work well provided the read/write head is exactly centred on the recorded track. This is because a data pulse whose strength is 40% of the average pulse will be well above the disc noise or 'hiss'. However, when a different drive is used, which may have different head alignment, the head may veer off the track. When this happens the pulse strength falls but the noise remains constant; some of the lowest recorded pulses may become dangerously close to the strength of the noise and become indistinguishable and even lost, causing an error. The best discs use a clipping level of 75% which keeps the lower pulses well away from the noise even when the head is off track.

When manufacturers talk of certification they ought to point out that there are two ways of certifying a disc; full surface and track only. The American National Standards Institute (ANSI) specification only calls for track certification but ideally the disc should be certified on and between the tracks to prevent an unrevealed flaw growing into the track and ruining the data. The best discs are full surface certified. Finally, discs are coated with millions of tiny particles of metal oxide and ideally that is where they should stay; on the disc. Disc manufacturers coat the discs with a protective layer but with wear and ageing this can break up leading to the oxide depositing itself onto the drive heads. On some of the best discs the manufacturers try to coat even individual particles which leads to a more durable and reliable disc.

When discs can be bought so cheaply it's easy to think "if it fails just replace it", but a failed disc can mean an awful lot of lost data. Lifetime Guarantees should and can mean just that, otherwise they are just about as useful as a warranty on a parachute!

179. Reading/writing registers using OSBYTE

There are many Osbyte calls, (*FX calls if you like), which can read from and write to various registers. The format given is a bit daunting to the unitiated:-

<New Value>=(<Old Value> AND Y) EOR X

What it means is that the new value you are putting into the register is made up of the original value, after logical ANDing it with the contents of Y, and then Exclusive ORing the result with the contents of X. Why bother with all this complication; why not just stick the new value in and have done? Well, you can, but there's more to it than that.

If you want to READ the register without altering the contents, then simply make the appropriate call with X=0 and Y=255, and the current value in the register will usually be in X on exit. The contents will be ANDed with 255, which is 11111111 binary, and will thus be unchanged. Then, they will be Exclusive ORed with 0, which is 00000000 in binary, which also leaves them unchanged. So, nothing is altered. You can do this from BASIC with the command:-

10 A%=156:X%=0:Y%=255:PRINT~(USR&FFF4 AND&FF00)DIV&100

If you want to write a specific value to the register, then you can make Y=0 and X=<New Value>. Thus, ANDing with 0 clears all the bits, and EORing them with X writes X into the register. Eg:- *FX4,2,0 (the ",0" may be omitted), writes 2 into the register; in this case it affects the action of the cursor keys. Using 4 as the osbyte number in the USR call above should return 0 to 2 depending on what setting of *FX4 is current.

It may well be that you want to alter only certain bits in a register, but you don't know exactly what the contents of the other bits will be at the time. For example, with *FX156, bits 5 and 6 affect the status of the RTS line on the RS423 port. I may wish to alter these bits, without knowing the contents of, and without wishing to alter, bits 7 and 0 to 4. This is where the apparent complicated way of doing things comes into its own. The method is:-

1. Set only the bits in Y which you do NOT wish to be affected in any way.
2. Set only the bits in X that you wish to be set to 1 in the register, and leave the rest as 0.
3. Make the appropriate osbyte call.

In the example above, if I wished to set bits 5 and 6 to 1, without altering the other bits, then X=&60 and Y=&9F. If I wished to set bit 5 to 0 and bit 6 to 1, then X=&40 and Y=&9F.

180. Printer snag

Several people have come up against an unexpected snag with some of the newer Epson printers such as the LX80 and LX800, as well as the Citizen 120D when in Epson-compatible mode. After having problems trying to redefine the draft character set, they read the small print in the manuals to find that you are only able to redefine the 6 characters ":;<=>?". Some earlier Epsons, and printers like the Canon PW-1080A and Taxan-Kaga KP810, allow you to redefine ALL the characters if you wish. This seems a curiously retrograde step by Epson, and you should take care when choosing a printer, if this is a facility you particularly need.

181. Friangle

The inspiration for this program came to Bob G3WWF whilst watching a programme on Channel 4. The theme was concerned with how simple rules are able to define quite complex structures, such as a fern leaf. To illustrate this, a simple method of plotting a sort of Fractal Triangle, (hence"Friangle"), was briefly described. In Bob's words:-

"Three points of an equilateral triangle are stated, and then a random initial point. A point is then plotted which is half way from the initial point to one of the three triangle tips, chosen at random. From this new point another point is plotted, again halfway to one of the triangle tips at random, and so on. One's first assumption is surely that the points will fill the triangle randomly, but not so!"

The program is very simple indeed, and has been further simplified and speeded up by Rick G4BLT. An additional 20% increase in speed can be achieved by using Integer arithmetic; change X to X%, Y to Y% and replace "/" with "DIV". The result is surprising and pleasing; a recursive pattern in fact. A few apparently 'stray' points will sometimes appear; see if you can work out why this happens!

10 MODE0:X=RND(1182)-1:Y=RND(1024)-1:MOVEX,Y
20 REPEAT:ONRND(3)GOSUB100,110,120:PLOT69,X,Y:UNTILFALSE
30 :
100 X=X/2:Y=Y/2:RETURN
110 X=(X+1181)/2:Y=Y/2:RETURN
120 X=(X+590)/2:Y=(Y+1023)/2:RETURN

182. Stripes in Wordwise Plus

A simple OS command will produce vertical stripes in preview mode so that along with the LNS command, it would be quite easy to align characters either vertically or horizontally.

There are two ways in which vertical stripes can be displayed; either projected in between characters or running through the middle of a character. An added bonus is that stripes could be in any of the BBC colours including flashing ones! The green embedded command is: OS"FX155,n" where the value of n=1 for red stripes between characters or n=17 for red stripes through the middle of characters. Obviously values n=2 & n=18 would give green stripes, 3 & 19 yellow and so on. Curious effects can be achieved if for example two OS commands are issued with say n=1 & 18 respectively. Stripes in two colours viz red (between characters) and green (through characters) are obtained. The possibilities are endless! Have fun!

NB: It is said that these effects can be generated in the later versions of WORDWISE but it does not work on mine. (Ed: I have tried it on version 1.4E and it works. TRY flashing colours - you go cross eyed!)

183. View text ruler

One of the nice things about Interword is the text ruler which shows the column numbers, and therefore lets you know exactly where your text will be printed on the paper. An easy way to recreate this in View is to include a dummy ruler on a Comment line showing text positions, with the actual Ruler 'floating' underneath. If markers 1 and 2 are placed at each end of the comment line the 'dummy' ruler can be copied anywhere into the document by pressing the COPY key with the current ruler being copied using SHIFT and
COPY.

CO |....:..10....:...20....:...30.. etc ... 110....:..120....:..130.|
.. >...........*...............*..............<

184. Master ADFS compacting routine

If you try a *MAP command after *COMPACT, you will often find that only partial compaction has taken place. If fuller compaction is required, you have to try again. The short program below can be kept in the root "$" directory of all your discs, and when CHAINed will carry out several 'passes' until there is only one gap when *MAP is issued. Each pass takes less time than the previous one, even if the same number of gaps result. Occasionally, a disc can be very reluctant to compact below 2 gaps, though it will make it eventually. The program therefore prompts you after 10 passes, in case you don't want to bother to go the whole way. Typically, I found that between 1 and 4 passes were needed with most discs.

You can alter the maximum number of passes via the variable T% in line 10, and you can alter the number of gaps it will attempt to compact down to, via the variable N% in line 10. (You will save a lot of time by increasing this to 2, as long as this is sufficient for your purposes.) Do not alter the variable A%. The program keeps you informed about how many passes it has made, and shows the free space on the disc when it has finished. Note the *COMPACT start page of 0F in line 20 to avoid corrupting the program itself, which should reside at the normal PAGE setting of &E00. Therefore, if you do add anything to the listing, even just a few extra spaces, then just check that PRINT~TOP gives a Hex number starting with an E and not an F. The spaces after the line numbers are shown for clarity only, and should be omitted.

10 T%=10:N%=1:A%=0:REPEAT:MODE135:*MAP
20 V%=VPOS-1:A%=A%+1:IFV%>N%ANDA%<=T%THENPRINT'"Pass ";A%:*COMPACT 0F 71
30 UNTILV%<=N%ORA%>T%
40 IFV%>N%THENPRINT'"Another try? ";:IFCHR$(GETAND&DF)="Y"THENRUN
50 PRINT''"Compacted"':*FREE

185. Master battery pack + internal modem

The early type of Lithium 'incendiary device' was replaced by a rather bulky pack of 3 'AA' size 1.5v Duracell batteries, which occupied the space intended for the internal modem. Despite the warning on the pack, I see no reason why the owner should not replace the individual cells when necessary. The positive lead has an 'idiot diode' in series, to prevent damage to the computer if the batteries are inserted wrongly, and to prevent the computer from attempting to charge 'flat' batteries. There is also a 100 Ohm resistor in series with the negative lead, which prevents damage to the batteries if the output is accidentally short-circuited. When plugging the pack back into the main PCB, note that the black lead goes to the North pin, (nearest the rear), the red lead goes to the central pin, and the South pin is unconnected. Hold down the "R" key whilst turning the power back on, so as to reset the RAM ready for you to enter the *CONFIGURE options. (The latter are best held on disc as a *SPOOLed file, ready to *EXEC back in when required.) Finally, you should reset the internal real-time clock/calendar.

The current type of battery pack still uses 3 Duracells, but they are in a shrink-wrap plastic covering, which makes it much more compact. It fits neatly down the small slot immediately to the left of the keyboard PCB, and immediately in front of the PSU. This frees the internal modem space for whatever you want to put there, including a modem of course. Replacing the individual cells oneself is not as easy as with the older pack, but it is by no means impossible. The pack costs 3-68 from Beebug, (3-50 to Beebug members), plus 1 pp, and the part number is "0807B - Master Battery Pack". Beebug also do an internal modem, which frees the RS423 port for other uses, including software on ROM, for 119, (113-05 to Beebug members), plus 3-75 pp. The modem is multi-standard, with auto-dial, auto-answer, and various other bells and whistles. The part number is "0759E - Master Internal Modem".

186. Master 128 power supply tip

The power supply unit is marked "No user serviceable parts inside" - but there are two fuses within this unit, and replacements of a similar type can be purchased from TANDY. If you get intermittent faults, it may indicate a dry joint on the main power transistor. (You must use your own discretion with regard to these two hints - liability cannot be accepted for damage to your computer.).

187. Hashing around

There are occasions, in programming, when you need to need to scan down a list and find if there is a match with a particular string. The string might be a name, a radio callsign in my case, or a number of some sort, or a mixed string of numbers letters and punctuation. If your program has to scan down a long list, then this can be very slow indeed. This is because the string might not have any obvious location in the list, so the lot has to be searched.

There is a way round this, called 'hashing', whereby the program calculates a definite location in the list for a particular string, which is ALMOST unique. If the location in question is found, as happens occasionally, to already contain a different string, then the next free location is used for the new string, which hardly slows things down at all. The theory behind the technique is too involved to be covered in a short article like this, but here is a ready-to-use Function to try it out.

The variable as% at the beginning of line 10 should be set to a size larger than the maximum expected length of the list. 50% larger is fine, but you can get away with as low as 20% with a slight reduction is speed, but smaller margins will cause a drastic reduction in speed as the list fills up. The string array list$ contains the list of strings, and the integer array list% is used to keep track of how many times each string has been searched for. The latter can be useful for statistical purposes, but if you aren't interested, then simply omit all terms involving list%.

Lines 20 to 30 form a simple demonstration program, using a list of length 100. (Well you shouldn't fill it up completely, as I implied above, so the real maximum is in the region of 65-80.) The Function FNlist() does two things. First, it checks the list to see if the string passed in brackets is already on the list, and adds it if necessary. It then increments the count by one for that string, and returns this number. So, if a string was not already on the list, then "1" is returned. The second time you search for that string, "2" would be returned, as so on. Thus "1" implies "not already on list", "2" and above implies it was found, the number being the total number of times it was searched for, including the very first time.
Obviously, a list as short as 100 doesn't take long to scan from top to bottom anyway, but with much larger lists it's a different story. This Function takes no more time to search in a list 1000 long as it does one 100 long, and a 10-character string takes only about 6/100 of a second to check for on a Master.

10as%=100:DIMlist$(as%),list%(as%)
20REPEAT:INPUT"Type in a string "s$
30PRINT"Searched ";FNlist(s$);" times"
40UNTILFALSE
50:
100DEFFNlist(n$):LOCALh%,j%
110FORj%=1TOLEN(n$):h%=h%+j%*ASC(MID$(n$,j%,1)):NEXT
120h%=RND(-h%):h%=RND(2):h%=RND(as%)-1
130REPEAT:h%=(h%MODas%)+1:UNTILlist$(h%)=""ORlist$(h%)=n$
140list$(h%)=n$:list%(h%)=list%(h%)+1
150=list%(h%)

188. Master/Arc scroll quirk

This tip applies to a Master/Compact and the Archimedes, but it has a nasty 'twist' on the Arc in particular.

If you set *CONFIGURE NOSCROLL, ie enable the scroll-protect option, and print a character on the last position (column) of a line on the screen, a LineFeed is not generated in the usual way. Instead, the cursor sits under the last character on the line until you print another character, in which case it moves down to the next line as normal. When the cursor is 'hanging' in this way, it returns a POS of 20/40/80 etc. depending on the screenmode, rather than 0 or 19/39/79 etc.. As soon as another character is printed, POS becomes 1 (not 0) as the cursor has moved down onto the next line. This can produce some strange results on programs written under the assumption that the cursor behaves as on a BBC B and B+. The cure is to set *CONFIGURE SCROLL, ie scroll protect is disabled.

OK, now the booby-trap for Arc-lovers. You may have disabled the scroll protect on your Arc, but the desktop enables it again when called up, and it stays on. Thus when you run a program after using the desktop, the scroll protect is enabled whether you like it or not!

On both machines, using VDU23,16| once in a program will disable the scroll protect, and VDU23,16,1| will enable it; problem solved. (Note the "|".)

Just for fun, load any BASIC program, and then try LISTing it after first typing VDU23,16,n| , where n is 2/3/4 etc.. The results are MOST entertaining!

189. Master/Compact/ARC printer ignore character

On the Master/Compact, if you configure the machine to send LFs to the printer with the command *CONFIGURE IGNORE, the previous ignore character is stored, though not active. If this previous character is 10 for example, some applications like InterWord will fail to send LFs because they read the character value with OSBYTE &F6, but don't check to see it the character is active with OSBYTE &B6. I had to use the command *FX6,0 in the !BOOT file to overcome this, until I realised what the root cause was.

You can avoid this problem by typing the following commands, followed by <CTRL+BREAK>...

*CO. IGNORE 0 (Set ignore character to zero)
*CO. IGNORE (Deactivate ignore character)

On the Archimedes, typing *CO. IGNORE also sets the character to zero, which is sensible, but the !Configure utility on the Applications-1 disc does allow you to leave the character set to 10, though deactivated. If using this utility, I advise setting the ignore character value to zero as well as toggling the little asterisk off.

The PC emulator (and 512 board) sends LFs anyway, so I advise against setting auto linefeeds on the printer. I curse the day Acorn chose *FX6,10 as the default on the model B; Think of all the hassle it has caused over the years!

190. Rescuing files with "E" attribute on ADFS

I recently made a serious error. I intended to set the file attributes of a whole directory of InterWord files to "RL", using *ACCESS RL as usual. In a moment of carelessness, my finger missed the "R" key and hit the "E" instead; what a calamity! Once a file has the "E" attribute set, you cannot change it back again using the *ACCESS command. The file can only be executed, not LOADed or changed in any way. I was unable even to load my files into InterWord, as I got a spurious "File too long!" error message when I tried.

I was more than a little dismayed, as although I had backup copies, they were not 100% up to date. I remembered reading in a magazine that it was possible to 'rescue' the files using a disc sector editor, but I couldn't find any trace of the article. I had no option but to figure it out for myself, and happily I was successful, after a bit of 'safe' fiddling about with a spare backup copy of the affected disc. Here's what to do:

First, you need a disc sector editor, and I thoroughly recommend the one contained in the Advanced Disc Toolkit (ADT). Make a backup of the affected disc, as you cannot copy individual "E" files, and work on this disc, NOT the original! Next, you need to know the sector number of the catalogue containing the affected file(s). If it is the Root ($) directory, then it is always sector 2 (&02), ie track 0 sector 2. If it not the root directory, then move to the parent of that directory, ie the directory containing the directory containing the affected file. Eg if an affected file is called $.MYPROGS.FUN.DUMMY, then move to the $.MYPROGS directory. Type *INFO <DirectoryName>, where <DirectoryName> is the name of the directory containing the affected files, eg FUN in the example above. The result will be the 2-digit Hex sector number you need.

Now enter the sector editor at the appropriate sector number, eg DEX 2 for the Root and DEX nn in the case of another directory if using the ADT. It is best to enter from an 80-column mode if possible, to avoid having to carry out scrolling to 'see' the whole sector. You will see the start of the catalogue for the directory containing the affected file. Now simply step through it and the next few sectors until you see the affected filename. Count along the filename to the 5th character from the start, eg "Y" if the filename was "DUMMY". If the filename is LESS than 5 characters, you must still look at whatever is in that 5th position. The first Hex digit of this
character should be in the range 0-7, ie the 8th bit is set to 0, but when the "E" attribute is set, the 8th bit is set to 1, so the first digit is in the range 8-F. For example, the "Y" of "DUMMY" should show as &59, but if the attribute is "E" then the "Y" will show as &D9 instead. In the case the ADT, the "Y" still looks normal on the screen, even though the Hex code is wrong, but it might look different on other editors. If the filename is shorter than 5 characters, you may often find the 5th character is "&8D", which is a CR (&0D) with the "E" set.

To get rid of the "E", simply alter the Hex code to the 'normal' value. Rather than calculate the code and enter in Hex, use the editor in ASCII mode, (the default with ADT), and overtype the appropriate character, eg "Y" in my example, or just <RETURN> to change &8D to &0D. If that looks ok, then exit from the editor, saying Yes to any Save option. If you catalogue the affected directory, and the "E" is still present, DO NOT PANIC, but simply *MOUNT the disc again, or type *DIR followed by the full directory pathname,
eg *DIR $.MYPROGS.FUN, so as to update the catalogue in memory, as this isn't necessarily done automatically by the editor. If the "E" has gone, the final thing to do is set the "WR" attributes, or "RL" etc. as necessary.

If you have to enter values in Hex, the correct code can be found from the expression PRINT ~'&oldhex' AND &7F, eg for the "Y" in my example type PRINT ~&D9 AND &7F, and the result is &59. If the character is a normal 'visible' character, such as "Y", then PRINT~ASC"Y" would suffice, but otherwise use the first expression.

In summary, locate the sector number of the catalogue of the directory containing the affected file(s). Enter the disc sector editor, locate the 5th character from the start of the filename(s), and overtype it with the correct character, or enter the correct Hex code, as convenient. The first digit in Hex should change from 8-F to 0-7. Exit from the editor, saving the changes, and if the "E" attribute hasn't gone, remount the disc or reselect the directory with the full pathname.

Although it sounds complicated, after a couple of practice goes on a spare disc and a dummy file, you can correct an "E" attribute in well under a minute!

191. Uses of *SRWRITE on Master

The *SRWRITE command is officially for the purpose of moving blocks of main memory into Sideways RAM slots. The complementary command is *SRREAD. Well, you can indeed use it in this way, but it can also be used for moving blocks
of data around in main memory. The real advantage of using *SRWRITE is that apart from being a very simple command, it is very fast indeed. The syntax takes either of the two forms:-

*SRWRITE <start> <finish> <sideways start> <slot number> or..

*SRWRITE <start> + <length> <sideways start> <slot number>

Note that in fact "finish" is actually the address of the first location AFTER the last byte to be moved, rather than of the last byte itself.

You could, for example, move up to 16k of screen memory into sideways RAM, and then move it back again to restore the original screen contents. If you were using Mode 4, (not Shadow mode), then the command *SRWRITE 5800 8000 8000 5 will save the screen to bank 5. The command could also be used in the form *SRWRITE 5800+2800 8000 5 and the result would be identical. Personally I generally prefer this latter syntax. To restore the screen contents later, you would use the command *SRREAD with the identical syntax. There is yet another syntax form for these commands, but I won't go into them now.

You couldn't save a 20k Mode 0 screen in one RAM bank, but you could save 16k of it, at the beginning, middle or end of the screen memory, or you could easily split it between two RAM banks, and use the spare 12k for something else.

If you wished to move the screen memory into a reserved area of main memory, then you substitute the appropriate address for the final "8000" in the command examples given, and then the RAM slot number would be a dummy simply to satisfy the syntax - zero would be as good as any. To move the memory bank into the screen memory area, you use *SRWRITE again, not *SRREAD, and interchange the addresses as appropriate. I couldn't move a 16k Mode 4 screen into main memory, as I would have less than 16k free to start with, so here is an example with an 8k Mode 6 screen, to be stored at address&3000.

*SRWRITE 6000+2000 3000 0 (copy screen memory to address &3000)
*SRWRITE 3000+2000 6000 0 (copy original contents back to screen memory)

alternatively:

*SRWRITE 6000 8000 3000 0 and:
*SRWRITE 3000 5000 6000 0 (See how the above syntax is simpler)

192. *EXEC files on Master

Just a reminder that if you try and *RUN files which have no sensible execution and/or load addresses, both DFS and ADFS on the Master will attempt to *EXEC them. Thus if you were running a DFS disc on drive 1, and wished to boot the disc, (you cannot use <Shift+Break> as it would boot drive 0), you could simply type *!BOOT instead of *EXEC !BOOT.

This applies to any EXEC files, including *SPOOLed listings. Oh, if the filename clashes with a ROM command, then you can use *RUN <filename> instead, but this can be abbreviated to */<filename>. So, these are equivalent, but the last 2 options avoid clashes with ROMs:-

*MYFILE
*/MYFILE
*RUN MYFILE


193. Making the most of View (1)

Expanding your Macros! by Andrew G8YHI If you need to print a block of text more than once in a document then View
offers you the facility to define it at the beginning and then recall it as and when needed. The idea of this is similar to using a procedure from within BASIC and the defined text is known as a macro. Each macro is given a two letter code which can be anything that is not already used by View, so AA would be fine but CE wouldn't.

To define a macro press <EDIT COMMAND> followed by DM and <RETURN> and then type in the two letter code; for example AA. Then type in your text making sure you start on the next line down and include any rulers or commands such as CE or RJ that you want. When completed move onto the next line down and press <EDIT COMMAND> followed by EM and <RETURN>. So the start of your document might look like this;

DM AA
This bit of useful text is a macro.
EM

Wherever you wish to recall the defined text move the cursor to the appropriate line in your document and press <EDIT COMMAND> AA and then <RETURN> so that AA now appears in the margin. When the document is printed wherever AA has been set the text will appear. You can define as many macros as you wish and might store some on disc to be added to other documents using the READ command. Scientific formulae using complex sub and super-scripts are one possibility or perhaps a set of different rulers.

Try out this simple macro technique for yourself and next I will show you how to use macros with parameters, which makes personalised mail-shots easy.

194. Making the most of VIEW (2) by Andrew G8YHI

Following on from the last tip we can use the macro to make the sending of standard but personalised letters a little easier. View allows up to 10 parameters to be passed to each macro and within these parameters we can introduce the words that make each document different. Suppose we wished to send a number of letters of thanks in which the different wording was the persons name, their address and the amount of money they had sent. We first have to define our standard letter as a macro but where each of the different words or groups of words appear we insert a symbol. Each of these parameters, as they are known are given symbols from @0, @1 etc ...up to @9 and so our letter might look like this.

DM AA
@0
@1
@2

Dear @3,

Thank you for your kind donation of `@4 in response to my recent begging letter. The villa in Marbella will do my sick grandmother
the world of good.
Yours sincerely,

EM

We now list the parameters as we wish them to be printed with each parameter separated by a comma, the macro name (in this case AA) being inserted in the margin using the EDIT COMMAND key.

AA 1 Sandal Lane,Wakefield,WF4 5AF,Mr Sperry,25.00
AA 24 Ferry Drive,Wakefield,WF5 2AZ,Mr Woolhead,2.00
AA 16 Sunset Boulevard,New York,NY 13245,Mr Lane,0.50

When the documents are printed the parameters will be inserted in the appropriate positions. All parameters must be entered on one line (which can be up to 132 chracters long) and if any parameter includes a comma, the parameter must be enclosed within angle brackets. This is a useful but limited facility in that although text is inserted, the document is not reformatted before printing so careful thought has to be given to how each parameter will affect the final layout. Try it out and next I'll show you how to make the most of the function keys. de Andrew G8YHI

195. Making the most of VIEW (3) by Andrew G8YHI

One of the nice things about View is that in common with Wordwise+ the most used editing commands are available from the function keys, either on their own or in conjunction with SHIFT or CTRL. But unlike Wordwise Plus there is no mention in the manual of how to program the function keys yourself and yet the facility is available, albeit after entering *FX228,1 and by using the function keys in conjunction with CTRL and SHIFT.

The facility is available from both command and edit mode and so might be used to insert often used phrases. eg *KEY0 COUNT |M or *KEY1 Wakefield BBC Micro User Group|M

The keys can also be programmed to insert View's stored commands, which means that some quite complex composite commands can be created. To do this we need to know the Ascii codes for the View function keys and the characters for the definitions that will produce them. Placing |! before a character adds 128 to its Ascii value and a '|' before a character reduces its Ascii value by 64. As an example the View stored command EDIT COMMAND has an Ascii code of 164 which corresponds to |!$. The |! adds 128 to the Ascii value of 36 for the $ character which adds up to 164. The codes for all the View stored commands are set out on the next page.

     f0           f1           f2           f3           f4
--------------------------------------------------------------
   Delete       Next       Formatting     Justify      Insert/
   Block        Match                                Overwrite     Ctrl
  172  |!,     173  |!-     174  |!.     175  |!/     176  |!0
____________ ____________ ____________ ____________ ____________
   Move          Swap       Margins     Delete up     Highlight
   Block         Case                    to char.         1       Shift
  156 |!|\     157 |!|}     158 |!|^     159 |!|_     160 |!
____________ ____________ ____________ ____________ ____________
   Format       Top Of      Bottom Of   Delete End    Beginning
                 Text         Text       Of Line       Of Line
  140 |!|L     141 |!|M     142 |!|N     143 |!|O     144 |!|P
____________ ____________ ____________ ____________ ____________

      f5           f6           f7           f8           f9
--------------------------------------------------------------
    Ruler        Split        Join        Mark As
                 Line         Lines        Ruler                   Ctrl
  177  |!1     178  |!2     179  |!3     180  |!4
____________ ____________ ____________ ____________ ____________
 Highlight      Go to         Set          Edit        Delete
     2          marker       marker      Command      Command     Shift
  161 |!!      162  |!"     163  |!#     164  |!$     165  |!%
____________ ____________ ____________ ____________ ____________
   End Of       Insert       Delete      Insert        Delete
    Line         Line         Line      Character     Character
  145 |!|Q     146 |!|R     147 |!|S     148 |!|T     149 |!|U
____________ ____________ ____________ ____________ ____________

Other useful commands are;
136  |!|H  ESCAPE         137  |!|I  RETURN        138  |!|J  DELETE
139  |!|K  TAB            152  |!|X  Left arrow    153  |!|Y  Right arrow
154  |!|Z  Down arrow     155  |!|[  Up arrow
167  |!'   Copy current ruler to cursor
168  |!(   SHIFT 
169  |!)   SHIFT 
170  |!*   SHIFT 
171  |!+   SHIFT < Up Arrow  >

Try and work out some useful definitions. Here are a few suggestions to get
you started.

*KEY 0 PO Box65 |!$ RJ |M |M Wakefield |!$ RJ |M |M WF2 6YZ |!$ RJ |M|
 ( Inserts address, EDIT COMMAND, Right Justify, Next Line etc)

*KEY 1 ...>.............*.......................*.....< |!4 |M|
 ( Insert ruler details, MARK AS RULER, Next Line )
NOTE:- For 160 (highlight one) the |! is actually |! followed by a space. Not easy to show!

196. Making the most of VIEW (4)

View has 26 number registers which are labelled A to Z. All of them can be set from within the document but two are used by View to hold the page number (P) and number of lines (L). To set a register press <EDIT COMMAND> and enter SR followed by <RETURN> and then the register and the number you wish to set it to.

For example

SR A 20 which would set register A to a value of 20

Printing out number register values is achieved by placing their symbols (A to Z) after either the DH DF CE RJ or LJ commands although they must be preceded by the symbol.

For example

<EDIT COMMAND> LJ L would print out the number of lines in the current page <EDIT COMMAND> DF <RETURN> //Page P//
would print the page number each time a footer is printed. Instead of a number you can use an expression such as A=A+2, which must be entered as

SR A A+2

Not the most exciting of the View facilities although the value L (number of lines) is useful when placed at the bottom of the document and in conjunction with SCREEN, used to show how many lines of the page are left. ie SR A 66- L
LJ A where 66 is the page length

Finally if you have a Master then D and T are not available as they are used for printing the date and time respectively. de Andrew G8YHI

197. Disc hint

If you are using discs that have previously been formatted on a 40-Track drive, you can sometimes have problems using them on an 80-track drive. Theoretically, this shouldn't happen, but apparently it does. The cure is to 'wipe' the disc using a bulk eraser electromagnet, or a powerful permanent magnet, and then reformat it.

This same technique is also useful when saving files onto 40T discs using a 40/80 switchable drive set to 40. If the disc has previously been formatted and used on a true 40T drive, wipe the disc with a magnet first and reformat it as 40T on the switchable drive. The files should be copied off this disc as soon as possible, and then the discs reformatted on a true 40T drive.

198. High-density floppy discs

From time to time, you may come across 5.25" discs referred to as "High Density". After "Single", "Double" and "Quad" density, you may be fooled into thinking that "High" density must be a good thing. Think again!

Single, and double density discs, whether single or double-sided, single or double-density, 40 or 80-Track, tend to be made on much the same production lines. After testing, the best discs can be certified as 80T (96 tpi) double-sided double-density, and lesser ones as perhaps single-sided, or single-density, and so on. Obviously, manufacturers cannot conveniently arrange exactly the right failure rates to achieve the exact mix of quality of discs required to satisfy the demand, so quite often discs are of better quality than officially certified.

Quad density discs tend to be made on separate production lines, and are of higher quality. Nonetheless, they are basically made of the same materials, and it would do no harm to use them if they were cheaply available.

High-density discs, on the other hand, are a very different thing altogether. They are intended for systems which pack 1.2 Mbytes onto one disc, (usually superior IBM compatibles), and the magnetic material is different. A crude comparison might be that between ordinary ferric audio cassettes and chrome cassettes. A much stronger magnetic field is required to magnetise the material properly, and only drives intended to do this are suitable. They sense whether the disc is 'ordinary' or high-density, via an extra notch, and alter the head current accordingly.

What this boils down to in practice, is that High Density discs can be very unreliable and unpredictable when used with 'ordinary' drives such as one has connected to a BBC Micro. Frequently, they do not even format properly, or if they do, funny things happen later. In other words, DO NOT USE THEM!!

The situation with 3.5" discs is similar, though apparently many modern drives normally used in a double-density mode will happily cope with high-density discs. This applies to later Arcs, though I am not sure about the earlier 440s amd 300 series. However since High Density discs are VERY expensive, there seems little point in finding out!

199. Disc backups

This is a sorry story but also a reminder. During the very hot weather, someone was working on a Master using the DFS and WORDWISE PLUS, they loaded a text file into Wordwise to amend and then to save the new version to a different disc. But unfortunately when they saved the file to the new disc it also saved the previous disc's catalogue information to the new disc
including the other disc's start of file info, resulting in two files being split into FOUR. The person concerned unfortunately had not been keeping a backup copy of the discs but had luckily got printed copies of his files. So the moral is ALWAYS make backups regularly of your files or the unwanted will happen one day. Oh, by the way, the apparent reason for this unusual event on a Master was the heat. The Machine should normally operate at a temp. between 75 and 90 degrees F but was working at well over 100 degrees F!

200. !BOOT & MOSPLUS hint

An interesting command provided by the MOS-PLUS ROM from Dabs Press, is *CONFIGURE START. If used in a !BOOT file, it can be used as part of a startup routine for loading one or more sideways ROM images, then doing a <CTRL+BREAK> followed by loading a menu. It can be done on the Master 128 only, by *BUILDing the following !BOOT file....

1 *CON. START CHAIN "MENU"|M
2 *SRLOAD ATSv31M 8000 4 Q
3 *FX200,2
4 CALL!-4

Note that you can of course use your own choice of ROM in line 2, (I have just used the new ATS ROM as an example), and your own filename in line 1. You should include in your menu program the command *CONFIGURE NOSTART in order to turn the command off when you exit from your menu program. (That's if you want to; otherwise every time you turn your machine on it will attempt to load your menu program.)

201. Configuring Escape key

Have you ever wanted to disable <ESCAPE>, or totally clear the BASIC program in memory if Break is pressed? If you have, then the *FX200 command is what you want. There are four variations on the command:

Command Action
------- ------
*FX200,0 Enable Escape, do not clear memory
contents on Break (default setting)
*FX200,1 Disable Escape
*FX200,2 Clear memory on pressing Break
*FX200,3 This combines the effects of *FX200,1
and *FX200,2

Don't forget to make sure your program works before you include these commands in it, otherwise you could lose valuable data, etc.. We hate to see grown men and women crying!

202. Beeb composite video output

The BBC Micro composite video output (BNC Socket) only gives out a monochrome signal. This is quite deliberate policy by Acorn, as the RGB output is best for high-resolution colour, and the absence of colour information on the composite signal ensures the best possible quality on a monochrome monitor. However, quite a few people do require a colour composite video output, and this can be achieved fairly easily. On early model Bs you need to solder a 470pF ceramic capacitor between the emitter of transistor Q9 (BC239) and the base of Q7 (BC309). These components are located a little to the left, and in front of, the ASTEC UHF modulator 'can'. This is a very cramped area of the circuit board, which makes soldering very tricky.

On later Bs, there is provision for soldering this capacitor onto the board at the position marked C58; if you're lucky the capacitor may already be fitted. To complete the circuit, you then only need to link the solder pads marked S39. If you have a circuit diagram, such as the one provided with the Advanced User Guide, then look at the bottom right corner, just to the left of the UHF Modulator. The link S39 may be marked as LK39 on the diagram. On the BBC B+, all you have to do is 'make' link 26 on the main board. I imagine the link position will be somewhere fairly close to the UHF modulator can.

On the Master 128, you can solder a 470pF ceramic capacitor between the emitter (right leg) of transistor Q12 and the base (centre leg) of Q13. These are located between the CV and RGB sockets, and although it is a slightly delicate soldering job, it is nowhere near as tricky as the equivalent modification on an early BBC B.

Note:
~~~~~
Since producing this tip, I have been told that the circuit diagram, supplied with the Watford Master reference Manual, shows that you can apparently simply 'make' link LK11 on the PCB to get colour on the CV socket.

Whether this is correct, and whether this has always been so right from the start of the production run of the Master, I cannot say. I don't even know where this link is, or whether any pins are fitted, or whether it's just a pair of solder pads on the PCB. However, it's certainly worth looking into before you start splashing solder all over the PCB!


203. Master/BBC 1772 DFS chip snag

There are two possible FDC (Floppy Disc Controller) chips which may be fitted to a Master, or to later Acorn DFS upgrade kits for a BBC B. (BBC B DFS upgrade kits originally used an 8271 chip.) There is the 1770, and the similar 1772. With the 1772, you can have problems using older drives (mainly 40T ones), or 80T drives when used in 40T mode.

The configuration command *CON. FDRIVE 0 gives 6 milliseconds track-to-track head-step time on a Master with a 1770 or 1772, whereas *CON. FDRIVE 2 gives 30ms with a 1770 and only 3ms with a 1772! Many older 40T drives are too slow to step the head at 6ms, so cannot be used at all with a Master fitted with the 1772. If an older drive fails to work with your Master, no matter how you configure it, but works on another Master or BBC B/B+, it may be because your machine has a 1772 fitted. (The Master Compact has a 1772 fitted as standard I believe.) 80T drives used in 40T mode, (either by being switched or by fancy software), will not work at 3ms (or possibly not even at 6), as they have to step twice each time, thus effectively trying to step at 1.5ms! See the table on the next page for a summary of the timings. The new alternative MOS ROM for the Master now has additional software delays built-in, and so can effectively handle step times of 3ms right up to 32 ms with a 1772, and 6ms up to 50ms with a 1770. Better late than never I suppose!

Acorn now fit the 1772 to their official DFS upgrade kits for the BBC B. With the 8271 and 1770 chips, keeping keyboard links 3/4 'Open' sets the slowest head-step time, but this is not the case with the 1772. The slowest step time available with the 1772 is 12ms, and even this may be too fast for some old 40T drives. Here is a list of link settings for the various Acorn DFS chips; a '0' means 'Open' (OFF) whereas a '1' means 'Closed' (ON). Confusingly, Acorn appear to have transposed all the '0's and '1's in the 1770/2 DFS User Guide Addendum 1, and also in the new supplement sheet 0433,080 Issue 1, the latter kindly supplied to me by Arc Electronics in Wakefield. Trust Acorn!

BBC B MASTER 128

Link 3 Link 4 Step Time *CONFIGURE Step Time 8271 1770 1772 FDRIVE 1770 1772

1 1 4ms 6ms 6ms 0 or 1* 6ms 6ms
0 1 6* 12 12 2 or 3* 30 3
1 0 6 20 2
0 0 24 30 3

* without pre-compensation (ADFS) - seems to make little or no difference.

* Note that the two settings of the links which set 6ms with the 8271 are not actually identical; the one starred has much longer settle and head load times, and is therefore effectively slower. I can supply further detailed information about the practical aspects of setting these links on request.

In summary, a setting of 6ms should be suitable for most modern drives, including switchable ones. However, older drives may require 12ms or more, but modern 3.5" drives will operate happily at 3ms, as on the Arc/A3000. Some modern 80T 5.25" drives will also operate at 3ms, though not when used in 40T mode. In any case, there seems little point in trying to 'push' 5.25" drives beyond 6ms, nor 3.5" drives beyond 3ms.

204. Attractive menu boxes for the BBC Master

If, like me, you go green with envy at the attractive menus on the Archimedes, and would like to produce something similar on your BBC Master, then this simple routine may go some way towards helping you.

DEFPROCfill produces the patterned background, using VDU23,2 to define the pattern which GCOL16,1 constructs. After MOVEing to the screen origin, the whole screen is covered using PLOT 101 (Rectangle Fill). DEFPROCbox just draws two areas; one in the background colour (in this case black) and one in the foreground colour (white) with the latter offset a little to the left and upwards. The screen might be coloured suitably using VDU19,0,5,0,0,0 and text is best written at the graphics cursor, so a VDU5 and GCOL0,0 (black) will have to be included. OK not quite an Archimedes, but now at least it's the screen that's green, (or blue, or ....).

10 REM Smarten up your Master, by Andrew G8YHI
20 MODE128
30 PROCfill
40 PROCbox(40,700,240,1000)
50 END
100 DEFPROCfill
110 VDU23,2,170,85,170,85,170,85,170,85
120 GCOL16,1
130 MOVE0,0:PLOT101,1280,1024
140 ENDPROC
200 DEFPROCbox(left%,bottom%,right%,top%)
210 GCOL0,0
220 VDU25,4,left%;bottom%;:VDU25,101,right%;top%;
230 GCOL0,1
240 VDU25,4,left%10;bottom%+10;:VDU25,101,right%-10;top%+5;
250 ENDPROC


205. Master VIEW/VIEWsheet hint

This applies if you use View or Viewsheet, and your Master normally starts up in Mode 7, but you use View or Viewsheet in Mode 0 or 3. It is useful to enter Mode 0 or 3 before starting View or Viewsheet; this helps set them up correctly to use 80 columns. In Viewsheet you get the correct default Printer windows, while in View the ruler starts in 80 columns instead of 40. If you do it after entering View or Viewsheet, you have to correct the ruler in View, and edit the windows in Viewsheet.

206. Wordwise-Plus string bug

It appears that all versions of Wordwise Plus, up to and including version 1.4F, contain a bug. This prevents strings from being compared correctly under certain conditions. This affects segment programs, and can cause them to execute incorrectly for no apparent reason. To see this bug for yourself, assuming you have Wordwise Plus, try:

CLS
A$=""
B$="ANYTHING"
REPEAT
A$=A$+""
UNTIL B$=B$
PRINT "Bug! Press a key"
x%=GET

This bug affects a number of the Wordwise support ROMs from various people, so beware. A firm called IFEL of Plymouth on 0752-847286 have a version of Wordwise Plus called WW+2 for #56-35, which has not got the bug, but does
have a number of nice new features. You can also upgrade from earlier versions of Wordwise Plus at a reduced price. I do not yet know if this bug exists in the Arc versions of Wordwise Plus. The WW+2 from IFEL is fully approved by Computer Concepts.