First BBC Basic program: mission creep

It’s always the same. “I’ll just hack out this quick program,” you think. And you get it to the point where it works and does the job you intended it to do. But then: “Maybe it would be nice if it also did this…”

And even when you’ve added all the functionality the program will bear, and your wife is yelling up the stairs, “It’s time to make dinner” or the dog is nodding towards his lead while tapping on his watch or it’s suddenly 3am when only a moment ago it was tea-time, you find yourself rationalising, “I’ll stop now, but first I’ll just move that bit of text one character to the right because it will look so much better”. But, of course, we all know that’s not the end of it.

Coding is addictive. You get wired-in, locked into the world of your code and there’s always one more little adjustment to make. You swear it’s the last, but it never is.

It was like that from the very beginning, from the moment I got my first Sinclair Spectrum and then, even more so when I upgraded to a BBC Micro with a 5.25in floppy disk drive. (The lengthy and tedious process of saving programs to cassette tape would sometimes be enough to overcome my obsession and make me crawl off to bed.)

And it’s still that way now. It’s just that it’s been a long time since I experienced that kind of flow where BBC Basic was involved.

So, having posted my blogs about recreating the first-ever program I wrote in BBC Basic, and how I’m using a DataCentre device so I can hack away at programs on either my BBC Master or the BeebEm emulator on my iMac, it won’t surprise you to learn that work on the depth of field calculator program didn’t stop there. Oh no.

The program as we last saw it.

I knew that this new version of the program wasn’t the same as the incarnation I’d originally coded on the Sinclair Spectrum and then the BBC Micro. I had a vague recollection of there being a much bigger table.

It occurred to me that I’d used this program – at least, the Spectrum version of it – to produce data for the publication I was working on at the time.

This was my first job in journalism, as a section editor for a partwork called The Photo, published by Marshall Cavendish. Back in the 1970s and early 80s partworks were very popular. They were weekly publications, resembling magazines, that you would collect and put in binders so that they built up into a very expensive encyclopaedia. And one of the characteristics of a partwork is that it has a finite run – if memory serves, The Photo ran for 96 issues.

Click on the image to see it larger

Another characteristic of the partwork is that you can cover a specific subject only once. In the technical section that I ran, for example, once you’d published an article on, say, depth of field, you couldn’t revisit it later unless you could find a completely new angle and all new information. So by the end of the publication, we were running out of ideas. Then some genius had the brainwave that we would run ‘Data’ articles consisting of handy tables, hints and tips, glossaries and data sheets.

So my very first ‘proper’ program actually turned out to be useful. I could fill some space with depth of field tables.

I remember lugging my Spectrum, cassette recorder, tapes and associated cables into work and then going on a long search for a TV to plug into. Because these were the days when there wasn’t a screen to be seen anywhere, even in a publishing company’s offices. We hacked out our articles on manual typewriters. (Two or three years later I would work at Marshall Cavendish again; by this time I’d upgraded from Spectrum to BBC Micro Model B to a BBC Master Turbo and the office had a computer – a sole Sirius PC used by the secretary. ‘Desktop publishing’ was in the air but was yet to have significant uptake in periodical publishing.)

Finally, I tracked down a TV in the company board room – actually in another building – ran my program and dutifully noted down the output with pencil and paper. Yup, no printer.

It just so happens I still have a complete set of The Photo and so I dug into the last volume and, sure enough, there were the tables. But they weren’t as I remember.

We ran two tables: one shows depth of field figures but for close-up photography, with the axes being magnification ratios and apertures (below); and the other is a hyperfocal distance table with axes of focal lengths and apertures (above).

(If these terms are unknown to you, or you think you might enjoy the phrase “enter circle of confusion”, then see my original post.)

I have absolutely no memory of the close-up calculations. But the hyperfocal distance table makes sense. So I went back to my new program and started hacking.

Here’s the how the main screen looks now, with a little tarting up and an extra menu item:

And I’ve added that hyperfocal distance table:

And in case you’d like to follow along at home, here’s the code as it currently stands. Is this the final version? It seems unlikely, doesn’t it?

REM Depth of Field Calculator
REM Version 2 with hyperfocal distance table
MODE 128
flen% = 50: REM focal length, mm
fstop = 8.0 : REM aperture
circ = 0.03 : REM circle of confusion, mm
dist = 5.0: REM focus point in meters
DoFnear = 0.0
DoFfar = 0.0
REM --- Set up arrays for tables ---
distances% = 18 : REM max index for dists array
DIM dists(distances%)
FOR I% = 0 TO distances%
READ dists(I%)
stops% = 9 : REM max index for fstop array
DIM stops(stops%)
FOR I% = 0 TO stops%
READ stops(I%)
flengths% = 9
DIM focals%(flengths%)
FOR I% = 0 TO flengths%
READ focals%(I%)
REM --- Main Loop ---
done% = FALSE
sel$ = CHR$(FNmenu)
IF sel$ = "Q" THEN done% = TRUE
IF sel$ = "A" THEN fstop = FNsetparam(sel$)
IF sel$ = "C" THEN circ = FNsetparam(sel$)
IF sel$ = "D" THEN dist = FNsetparam(sel$)
IF sel$ = "F" THEN flen% = FNsetparam(sel$)
IF sel$ = "H" THEN PROChypertable
UNTIL done% = TRUE
REM distances for table
DATA 0.5,0.7,1,1.5,2,3,4,6,8,12,15,25,30,50,75,100,150,200,500
REM f-stops for table
DATA 1.4,2,2.8,4,5.6,8,11,16,22,32
REM focal lengths for hyperfocal distance table
DATA 20,24,28,35,50,85,105,135,200,300
DEF FNsetparam(ch$)
IF ch$="A" THEN prompt$ = "aperture":min = 1:max = 130:default=fstop
IF ch$="F" THEN prompt$="focal length":min=8:max=1000:default=flen%
IF ch$="C" THEN prompt$="circle of confusion":min=0.025:max=0.05:default=circ
IF ch$="D" THEN prompt$="subject distance":min=0.1:max=1000:default=dist
prompt$ = "Enter " + prompt$
PRINT TAB(5,6);prompt$
INPUT TAB(LEN(prompt$) + 6,6);">"param
result = param
IF param < min THEN result = min
IF param > max THEN result = max
IF param = 0 THEN result = default
DEF PROCprinthead(label$)
pad% = (80 - LEN(label$)) / 2
extra% = LEN(label$) MOD 2
PRINTTAB(0,1);SPC(pad%);label$;SPC(pad% + extra%)
DEF PROCdisplay
y% = 4 : REM Y position for text
@% = &01000006
PRINT TAB(1,y%);"Focal Length: "; flen%; "mm"
@% = &01000004
PRINT TAB(23,y%);"Aperture: f/"; fstop
@% = &01020105
PRINT TAB(41,y%);"Subj Dist: "; dist; "m"
@% = &01020305
PRINT TAB(67,y%);"CoC: "; circ; "mm"
y% = y% + 1
@% = &01020106
PRINT TAB(1,y%); "DoF Near: " ; DoFnear ; "m"
PRINT TAB(23,y%); "DoF Far: " ; FNfarstr(DoFfar)
PRINT TAB(41,y%); "Hyperfocal distance: "; FNhyper(flen%,fstop); "m"
y% = y% + 2
x% = 19
PRINT TAB(x%,y%); "dist"; TAB(x%+17,y%); "near"; TAB(x%+36,y%); "far"
PRINT TAB(x%,y%+1); "----"; TAB(x%+17,y%+1); "----"; TAB(x%+32,y%+1); "-------"
y% = y% + 2
FOR I% = 0 TO distances%
@% = &0100003
PRINTTAB(x%,y%+I%), dists(I%); "m"
@% = &01020108
fardist$ = FNfarstr(DoFfar)
PRINTTAB(x%+31,y%+I%); SPC(8-LEN(fardist$)); fardist$
DEF FNfarstr(far)
IF far > 0 THEN far$ = STR$(far)+"m" ELSE far$ = "-inf-"
DEF PROCcalc(dst)
fdist = dst * 1000 : REM work in mm
fact = fdist * circ * fstop
DoFnear = fdist / (1 + (fact / flen% ^ 2))
DoFnear = DoFnear / 1000
DoFfar = fdist / (1 - (fact / flen% ^ 2))
DoFfar = DoFfar / 1000
DEF FNhyper(focl%,ap)
= ((focl% ^ 2 / (circ * ap)) + focl%) / 1000
DEF PROChypertable
y% = 4 : REM Y position for text
FOR I% = 0 TO stops%
@% = &01000006
y% = y% + 1
FOR I% = 0 TO flengths%
@% = &01000004
y% = y% + 1
PRINT TAB(1,y%),focals%(I%)
@% = &01020106
FOR J% = 0 TO stops%
PRINT TAB(7+(J%*7),y%),FNhyper(focals%(I%), stops(J%))
@% = &01000004
@% = &01020106
FOR I% = 0 TO stops%
PRINTTAB(7+(I%*7),y%+2),FNhyper(flen%, stops(I%))
@% = &01020305
PRINTTAB(2,y%+5);"Circle of confusion: ";circ;"mm : Distances in metres"
PRINTTAB(2,y%+7);"Press <RETURN> for main screen"
DEF FNmenu
PRINT TAB(1,29);"(F)ocal length : (A)perture : (D)istance : (C)ircle of Confusion"
PRINT TAB(1,30);"(H)yperfocal distance table : (Q)uit"
opt% = FALSE
key% = INKEY(0)
REM convert to uppercase if necessary
key% = key% AND &DF
IF key%=65 OR key%=67 OR key%=68 OR key%=70 OR key%=81 OR key%=72 THEN opt%=TRUE


Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.