SuperBASIC in Action

Free Samples - SuperBASIC meets the Real World!

Simon Goodwin senses the potential of new I2C analogue interfaces 
from TF Services.

Tony Firshman has gone to a great deal of trouble to ensure that 
his new I2C interfaces for the QL are programmable from 
SuperBasic. This column demonstrates several applications for the 
new Analogue interface, with working SuperBasic to play sampled 
sounds, convert them from other disk formats, graph sampled data 
and read a proportional 'analogue' joystick in two dimensions.

Up till now, QL computer signals have invariably been digital. 
This means they are considered to be at one of two levels, either 
on or off, true or false. An analogue interface allows 
intermediate values, like shades of grey. In this case, a byte of 
data coresponds to a linear scale of 256 voltages, from a preset 
minimum to a maximum of a few volts. Your programs can generate 
waves or read instruments that return varying signals.

This is not the place to review the I2C hardware; suffice it to 
say that it's a sturdy black box that connects to the Mark 2 
Minerva via two plugs and a ribbon cable. You can connect and use 
several parallel or analogue interfaces at once, although the 
software only communicates with one at a time. I tested the 
Analogue version which provides four eight-bit inputs and a pair 
of eight-bit outputs.

The I2C interface is controlled by a tiny machine-code extension, 
I2C_IO, which calls new code in the Minerva rom. The I2C vector 
appears in all the latest Minerva roms, but you need the extra 
chips in the Mark 2 version (with a real-time clock) to 
communicate with other I2C devices.

I2C_IO lets you use the small amount of non-volatile memory in 
the clock chip, but the protocol is complicated and error prone, 
with up to four parameters and an intricate message string. The 
Analogue and Parallel I2C interfaces come with extra commands 
that keep I2C_IO at arm's length. These extras are written in 
SuperBasic by Tony Firshman. You can either edit and interpret 
the original source, or use the equivalent QLiberated code, also 
supplied.

I2C_IO is tersely documented at the back of the latest (green) 
Minerva manual. The I2C interfaces come with a 12 page manual 
that explains the hardware and four new extensions. READ_PAR and 
WRITE_PAR transfer eight bit signals to or from any parallel 
port.

WRITE_ADC% and READ_ADC$ move strings of values to or from the 
Analogue/Digital Converters (ADCs). Characters in the string are 
transferred one by one over the I2C interface, at a rate of 
around 10,000 eight-bit bytes per second.

SuperBasic ADC

Simplified source for WRITE_ADC% and READ_ADC$ appears in the 
first listing. The originals used fiendishly clever SuperBasic to 
check their parameters, but space is short so I've replaced this 
with simpler stuff to catch most cases.

The function TEST_ADDR% checks that an I2C port address is valid, 
using interesting bitwise operations. The extensions are 
carefully designed to detect mistakes before they reach the I2C 
ports, and return meaningful error-codes.

The I2C address contains two parts in one byte. The bottom three 
bits are the unit number, while the other bits identify the type. 
Thus you can have up to eight interfaces of each type. When you 
call READ_ADC$ or WRITE_ADC% you can pass the full eight-bit 
address or a simple unit number 0 to 7, in which case  the code 
sets the other bits to suit the type.

The 'bitwise or' operator || (two vertical bars) combines the 
type, M%, with the address in A. a%=a&&-8 returns zero if a is 
less than 8; otherwise it sets A% to the value of A, with the 
bottom three bits cleared.

The value -8 corresponds to 11111000 in eight-bit binary. The 
SELect on A% ensures that the type must either be zero or match 
M%, which is 78 for Analogue (ADC) ports or 56 for PAR ones.

I put the range check values in quotes on line 630, so errors are 
unlikely even if parameter A is passed as a string, rather than a 
number. The quotes ensure that the parameter is coerced into a 
string before the comparison takes place. For yet more 
reliability, use UNSET from DIY Toolkit Volume P to ensure that 
the parameter has some sort of value.

Demonstrations

The JOYSTICK procedure contains a small loop that lets you move a 
block cursor around the screen with an analogue joystick. The 
cursor is drawn in OVER -1, so that it complements the background 
colour and vanishes when drawn a second time. Type OVER 0 to 
cancel this if subsequent PRINT commands give messy results.

The demonstration runs as a loop, reading the inputs on port one 
and converting the resultant characters into co-ordinates for 
BLOCK. Remove the REMark from the middle of line 220 if you want 
co-ordinates displayed in window #0 as the block moves. There's 
little risk of an 'out of range' error as new Minerva roms clip 
BLOCKs to fit any window.

I made up a simple lead to connect the two potentiometers of a 
five-pound Amstrad PC joystick to a pair of I2C analogue inputs. 
This could replace a mouse, as long as eight bit (MODE 8 pixel) 
resolution is good enough for your purposes. The advantage is 
that results are repeatable. Any particular position of the stick 
will correspond to a position on the screen.

As you move the stick the cursor jumps directly from place to 
place, in proportion to the movement. This is far more precise 
than a switched digital joystick, like the sort that plugs into 
QL CTRL ports, which can only signal 'up', 'down', 'left' or 
'right', with no indication of proportion or speed.

Depending on your graphics routines you may like to reverse the Y 
co-ordinate and scale the results from READ_ADC$ up or down. Most 
sticks have knobs which set the limits of travel.

Often sticks use a small part of the possible potentiomenter 
movement, so they do not return the whole voltage range. If you 
need maximum precision, with values from 0 to 255 rather than 
(say) 80 to 160, you might need three resistors or a couple of 
presets to set appropriate limits for the interface. If these are 
set too close together the stick will only make a difference for 
part of its travel, and will stick at 0 or 255 before it reaches 
the end-stop.

Smeghead

PROCedure REPLAY uses the function WRITE_ADC% to play a sampled 
sound. The graph shows the wave - a sampled recording of 'Rimmer' 
from the BBC space-ship 'Red Dwarf' saying 'Marvellous' with 
characteristic enthusiasm. NEWCHAN% is from DIY Toolkit Volume R. 
It returns a free channel number.

The 32K string length limit means that each utterance is limited 
in length to about 3 seconds, but you can fire them out in rapid 
succession. There's a short pause as SuperBasic hefts each new 
string into place, but you could create near-continuous speech by 
assembling and replaying 'phoneme' strings.

The sample rate is better than telephone specification but worse 
than AM radio. Speech is clear enough, while music sounds 
recognisable but a little distorted. It is important to use the 
full eight-bit range when recording samples, without overloading. 
To check this, display the sample as a graph.

The graph uses two dots (sample levels) in each column, which 
means each width of the MODE 4 screen corresponds to about one 
tenth of a second, at the fixed I2C rate of 10K per second.

Samples might not be recorded at this rate - possibilities range 
from around 2,000 per second for toys to 44,100 for CDs - so they 
may sound slow or fast when replayed via I2C. Once the wave is in 
digital form you can change the length and pitch by interpolating 
extra values to slow down a fast sample, or averaging them in a 
sample that plays too slowly.

Wave GRAPH

The two traces follow the 'M' sound at the beginning of the word. 
The middle trace was drawn first. The level is limited by the 
louder 'ah' sound that comes later. The whole sample uses about 
17K.

PROCedure GRAPH plots the wave. The PUT #3\0 in line 150 is only 
needed if you want to re-plot without re-opening the file. Lines 
170 and 180 use BLOCK efficiently to plot a fine millisecond 
grid. Change the limit of the FOR T loop if you want more of the 
sample overlaid on each screen. Lines 220 and 230 ensure that 
each trace appears in a new colour, starting with green, then 
white, then red, and back to green.

Pixels are lit by the DIY Toolkit PLOT function, from graphics 
Volume G. If you don't mind a slower, flickering display you 
could replace PLOT with a call to BLOCK, like this:

         270 BLOCK #4,1,1,j, CODE(k$(i)),colour

Sinclair's BLOCK routine is clumsy if you're trying to light 
individual pixels, but at least it's not as eccentric as POINT. 
In QL MODE 8 and Thor MODE 12 you need to change the width 
(second parameter) to 2 or the dot will not appear.

Flicker appears because each BLOCK is drawn as a short line and 
then clipped on either side to fit. The colour must be specified 
each time; move the IF test on line 230 to 295 if you want the 
same colours with BLOCK as PLOT.

Free Samples

Eight-bit samples are widely available from Public Domain 
libraries; the 'Rimmer' sample came from an Amiga cover-disk. 
Sample formats vary; some are signed values from -128 to 127, 
others (like the I2C ones) are unsigned bytes, and most are 
packed or padded in some way.

QL users have easy access to these files via the PD Amiga Qdos 
QL_HANDLER, DiscOver and similar utilities, so I've written 
programs to decode Amiga IFF (Interchange File Format) '8SVX' and 
some Microsoft .WAV files. Many utilities on Mac, PC and other 
computers support these formats.

The second listing is my IFF file Converter, written in 
SuperBasic. You need the IFF file on a QL drive first; the 
program converts that sample into the I2C format used by REPLAY 
and WRITE_ADC%.

The Turbo/DIY Toolkit extension INPUT$ is used in REPLAY and the 
IFF Converter to read a sequence of characters from a file in one 
go. If you lack INPUT$ and are in no hurry you could build up a 
string with successive calls to INKEY$. FLEN is in Toolkit 2, PD 
Toolkit, Thors and most disk roms.

IFF files consist of a sequence of 'hunks' with four character 
names and 32 bit sizes at the start of each. CONVERT_IFF looks 
for the 8SVX block that contains the sample, skipping or 
reporting others en route. IFF supports graphics, text and 
animations as well as sounds; Ergon's Open World utility converts 
IFF graphics to QL modes. 

PROCedure CONVERT changes each byte from signed to unsigned 
format. This conversion is accelerated amazingly much by Turbo, 
which is why I've supplied TF Services with a compiled version 
for I2C users. The initial DATA_AREA and IMPLICIT% directives 
instruct the Turbo compiler. Remove the REMs on lines 840, 880 
and 910 to see a rough graph as the sample is converted.

Extra possibilities

There's lots more to be done with the Analogue interface and a 
little SuperBasic. You could keep tabs on your your environment 
with temperature and light sensors, ranging from old-fashioned 
thermistors and light-dependent resistors to the latest active 
sensors. You could control a robot, record speech messages for 
digital replay, in a QL-controlled answer-phone, or synthesise 
waves of sound or patterns of light with simple formulae.

Alas, I2C is not for everyone. You need the Mark 2 Minerva, and a 
little knowledge of electricity, to make use of any I2C 
interface. The programming is simple and exciting, because these 
ports let your QL interact with the intricate world outside, 
where a little SuperBasic can go a long way.

DIY Toolkit will be back next month.


