A BBC Micro in the 21st Century
Once upon a time, in a very different century, I was working in my first job, as a software developer in a tiny company in a provincial city in the UK. In those days people, and indeed businesses, generally didn’t have computers. The company I was working for was a cash-register dealer that had seen an opportunity in this new-fangled personal computer technology and was selling the Commodore Pet to local businesses. They had heard a rumour that I knew something about computers, tracked me down, and hired me to help them out by creating software for their clients, there being nothing available off the shelf. At this point there was no literature on how to do this, no internet, and nobody else in that city (or county, come to that) doing any of this so we had to make everything up from scratch, using little more than the handbooks for the computers, which told us how the built-in BASIC interpreter worked, and an indispensable book by Raeto West. This was what they call a “learning experience” and I loved it.
I, on my meagre salary, could not afford to own a Commodore Pet myself but I could, eventually, and with enough credit card abuse, afford a BBC Micro. This was much more fun than a Commodore Pet anyway, having better hardware, a better BASIC, a better OS, and fancy things like colour graphics and sound. So when I was not writing Commodore Pet software at work I was writing BBC Micro software at home and I loved that even more. I did quite a bit of it, playing with all sorts of new ideas, and indeed eventually moved on to doing it for a living.
The foregoing will, I hope, serve as useful context for the story that follows. Recently I was searching my loft for something and stumbled on a trove of cassette tapes containing some BBC Micro software from those early days, audio tape having been the normal way to store software when, like me, you could not afford something as sophisticated as a 5.25” disk drive. I was immediately overcome with nostalgia and wondered what long-forgotten gems might be on these tapes, how they might compare to the software I write today, and whether there was any way to recover them. I no longer possess a working BBC Micro, a cassette deck, a suitable monitor (or PAL TV!) that would serve as a display, nor anything else relevant. Thus a challenge presented itself: what could I do with these cassettes without spending a fortune on obsolete collector’s pieces on eBay?
The first step was to convert the cassettes into something more manageable. This turned out to be fairly easy - my kids still had a cassette player, bought for them when they were much younger and wanted to be able to listen to the story tapes we kept in the car. A 3.5mm stereo jack-to-jack lead made for an easy connection from the headphone output to the mic input on my computer and a few minutes’ experimentation with Audacity yielded a configuration that allowed me to reliably capture the contents of a cassette as a WAV file. At the very least this would be secure from any further degradation of the tape - an archivable file that could potentially be replayed into the cassette port of an actual BBC Micro, if nothing else. But it doesn’t seem beyond the potential capabilities of modern DSP software to be able to decode this file into the byte stream it represents, does it?
The next step - of course there’s a way to do that! A little bit of googling and I discovered Unified Emulator Format, a de-facto standard file format used to store representations of all the various media types used by Acorn computers, including BBC Micro cassettes. It didn’t take much longer to discover that these files can be created from audio samples of actual cassettes using a freely available utility called MakeUEF. A quick download (there’s well under a megabyte of software, as we might expect for a utility with this heritage), a little bit of fiddling and I had a .uef
file, 7KB (compressed) of precious data recovered from a cassette labelled “chardef (c) Merlyn Kline 1984”. So now, what could I do with this? The clue is in the name of the file format of course - presumably there must be a BBC Micro Emulator that can make use of this.
Back to Google and here it is: BeebEm, an emulator for Windows that seems to cover all the bases - every variation of the BBC Micro, and a whole range of peripherals and add-ons, can be emulated with or without tweaks to take advantage of the fact that the hardware platform is so far advanced from the original (e.g. loading cassettes faster than real time). Another very quick download, start it up, load the file off the cassette, work out that this actually needs a BBC Master variation of the BBC Micro (with 128KB of RAM!), and we’re off - the program runs, and does everything it ever did, exactly as I (now) remember it. I’m not sure why it seems to need the BBC Master emulator as I now clearly remember writing it on a BBC Model B, but that seems a minor wrinkle. So what does it do and what is the code like?
Before looking at the program, a bit more background:
- First, in 1984 I had never seen a computer mouse. I had heard that such things existed, and that there was an idea that they might be a better way to interact with a computer than just a keyboard, and that this would work by moving the mouse to move a pointer on the screen and then using a button to “click” on parts of the screen. I was interested in this idea and wanted to experiment but the BBC Micro, of course, had no mouse and no support for such a thing. It did, however, have an analogue joystick for playing games and I realised I could use this to create an experimental UI that was at least conceptually similar to a mouse.
- Second, the BBC Micro drew text on the screen using a font of 8x8 pixel characters - fixed spacing, two colours only, so each character was defined by 8 bytes. We sometimes created new characters, or even fonts, but this was a tedious, error-prone process of drawing the characters on graph paper and working out the bit patterns to create the 8 bytes of data needed for each character. I kid ye not. We did this because a carefully designed specialised character set provided a fast way of rendering graphics on the screen; faster than the OS drawing routines, if used with care.
The program on the tape then, it turned out, was my experiment to see what using a mouse might be like. An attempt to improve the process of defining new characters by offering a graphical UI allowing you to edit the definitions of the 32 characters with codes 224 to 256, a chunk of the ASCII space unused by the BBC Micro OS.
On running the program, I saw this (after I’d clicked around a bit):
I should note here that BeebEm, with its “Analogue Mousestick” option for analogue joystick emulation turned on, worked so well that when I moved the mouse over the emulator’s window, the pointer drawn by my program tracked the Windows mouse pointer with complete precision so I was immediately able to click on things on the emulator Window and it all just worked. This was so intuitive that it was a short while before I realised it was happening and it occurred to me just how extraordinary it was! This software is 35 years old, running in an emulation of the hardware on a platform so advanced it would have been beyond my wildest dreams when I wrote the original, with a now quotidian input device that was almost mythical at the time, and everything was working exactly as intended!
Anyway, some features of this screenshot:
- That is the whole screen. It’s probably tiny when you look at it on your screen but on a BBC Micro this was everything and it occupied the entire monitor, which was probably a 14 inch CRT and had the 5:4 ratio you see here. The resolution of this screen mode was 320 x 256 pixels, yielding 32 rows of 40 characters.
- The system had no mouse pointer, nor any idea of a mouse, so I had to draw my own pointer. You can see it here, half way down on the left hand side. As you can probably tell, it was drawn on by an XOR process so it could be quickly removed and redrawn in a new location without the need to maintain a buffer with a copy of whatever was under it on the screen. That would have been too slow and would have used too much memory.
- In the top left part of the screen is an 8 by 8 grid showing the character currently being edited, here α. These pixels could be clicked to invert them.
- To the right of this is a scratchpad. You could click in here to drop a copy of the currently edited character and you could thus build up strings to see what they looked like in context. There are six of these scratchpads (for some reason!), selected using controls in the bottom left and right corners of the screen.
- Below this is a space that was used for various purposes. Here it shows the command you would need to put into a BBC BASIC program to create the edited character in its current state, i.e.
VDU23,225,
followed by the 8 bytes representing the current shape of the character. This updated as the character changed. On exit, this program would replace itself in memory with a BASIC program consisting of all the commands needed to generate the complete set of characters and you could then merge whichever bits you needed into whatever program you were writing that needed the newly defined characters. - At the bottom of the screen is a grid of characters from which you could select the character to be defined. Here we see the special characters beyond the basic set that were used for most purposes. These were the most commonly redefined because you could add new characters without interfering normal text printing. There is also a picker for the six scratchpads to the right of this, and a button to the left labelled NXT which cycles through the active scratchpads.
- To the right of the screen is a menu of commands that could be selected by clicking on them:
- ROTATE - Rotate the character right 90 degrees.
- INVERT - Flip every pixel in the character.
- MIRROR - Mirror the character.
- COLOUR - Change the foreground colour of the screen. Just cycled through the seven available colours (skipping the eighth, which would be whatever the current background was and thus pointless).
- BKGRND - Change the background colour of the screen, again by cycling and skipping the foreground.
- ENDRUN - Exit the program and create a new one with the character defining commands in it.
- FOUNTS - This was an accepted if somewhat unusual spelling of “fonts” at the time, used here to make the menu a consistent width I think! (Fonts were another new concept to me.) It displays a set of italic, bold and shadow options, with left- and right-biased versions of each, which were generated algorithmically. Selecting one applies it to the current character definition.
- SHDOWN - Shift the current character definition down one row of pixels, moving the bottom row to the top so it all wraps round.
- SHLEFT - Shift the character left one pixel, again wrapping round.
- SOUNDS - Turn the (very annoying!) sound effects on or off.
- COPYER - Present a display of the entire character set so one character could be clicked on to copy it to the currently edited character.
- CLEAR! - Clear the current character definition or scratchpad (depending on a second click) completely.
Modesty being the last refuge of fools and incompetents, I will say that this program achieves its purpose very well: it makes defining characters quick and easy, multiple orders of magnitude easier than the process it replaced, and it provides a good selection of tools given the extremely limited computing resources. It also demonstrates what is now common knowledge, that a mouse based UI can be very effective for this purpose; doubly so now that I am able to repeat the experiment with an actual mouse, rather than an analogue joystick as a second-rate substitute (and do that without changing a single line of the code!). This was a revelation at the time. There were certainly other interactive programs around but they would have involved moving some sort of focus indicator around with the cursor keys and pressing command keys, or typing text, to make the software work. Performing this task in that way would have been tedious in the extreme. (In fact this program does support moving the pointer with the arrow keys and clicking with the spacebar, and doing this is indeed tedious!) In addition to all this, I will also say that I think the design of the code is pretty good, again given the limitations of the system. I can quickly and easily understand how the program works despite it being well over 30 years since I last saw it or anything like it. I confess that the same cannot always be said of code I wrote last week! Much the same applies to the UI - it seems reasonably intuitive and is not unfamiliar to the user of a modern mouse & pointer based UI. Despite a few places in the code which give the appearance of not being quite complete (perhaps this is not the latest version of the code), the program is functionally complete and seems polished enough to have been suitable for commercial release, with a considerable level detail in the features and implementation that goes well beyond the needs of a simple mouse experiment or a personal utility. Despite that, I have no recollection of ever having sold it.
With that closing self-aggrandisement, I’ve finished telling the story I set out to tell and I’ve written a lot more than I intended to write in doing so. Now would be a good time for readers and writers alike to close the window and move on. However I myself am not going to do that quite yet so, if you’re interested enough, do read on for a quick look at the code itself.
Merlyn Kline
March 2020
The code
I’ve appended the actual code at the end of this article for reference and if you really want to you can spin up a BBC Master 128K emulator and paste this code into the terminal - type RUN and you will see the program running. I’ll take a quick look at some of the features of the code without trying to go into too much detail. If you really want to know more about any aspect of it you can find a BBC Micro manual on line. This manual, by the way, was pretty much the sum total of the information available when this program was written and it was only available as a printed book which came with the system, the character count alone being about 20 times the size of the RAM in a BBC Micro!
Before diving into the code I’ll offer a little more background: The program is written in BBC BASIC, which was very advanced for its time and has unusual features such as being able to define named functions and procedures, and having a built-in 6502 assembler which allowed it to interact directly with machine code - a feature that is exploited here. The memory available was extremely limited and the code was stored and interpreted as written (although language keywords were stored in single bytes) so there is little in the way of decorative whitespace, and identifiers are very short, which can make the code hard to read when you’re not familiar with the language. This is made worse by the fact that everything is uppercase. There is a simple type system, indicated by suffixes on the variable names (%
for integers, $
for strings, everything else being floating point). Operating system commands could be included in the code by prefixing them with a *
or using the OSCLI
command.
There were no dev tools as we know them today. The screen was tiny, with even the biggest mode having only 32 rows of 80 characters, and there wasn’t really an editor. You could list chunks of the code using the LIST
command, and you could “edit” a line by a process of copying it character by character, skipping unwanted ones and inserting new ones as you went. This was at least as tedious as it sounds! There was no way to search the code, or indeed do anything else at all really. Saving progress would have been many minutes of writing to a cassette tape so no source control and no reasonable way to revert to earlier versions. All of this certainly didn’t encourage refactoring and even a simple task like changing the name of a variable would be near impossible if there were more than two or three references to it.
Looking at the code itself I do think, as mentioned before, that it is not too bad, all things considered. There are some oddities, like commented-out lines and lines with GOTO
s jumping over them, which suggest that this is not a final version of the program. The names of some things reflect the difficulty of refactoring; it’s evident that some parts of the code have evolved away from their original functions. The names and formatting also reflect the extreme memory restrictions. There are no comments in this code and while I would normally consider that to be a good thing in the modern world, this is an example of a language where expression is limited enough that some comments might potentially be useful; on the other hand, memory is so restricted that they would be an impossible luxury. Despite all this the code seems reasonably well structured for the most part. Certainly I would feel comfortable enough maintaining it.
The system, of course, had no support for any kind of mouse input, and handling events at the high level of BASIC was unheard of, so for main the structure of the program I came up with the idea of a polling loop. This goes round and round, checking the joystick position and button states to decide what to do. Most functions that can be triggered execute quite quickly so they are completed immediately and control is returned to the main polling loop. Some functions present further UI to be interacted with and these draw the UI (e.g. the font picker) and then call the polling loop locally, remove their UI and return control to the main loop. The procedure PROC CURS
implements the core polling loop and simply draws the pointer on the screen, then re-draws it to remove it (as it’s an XOR process), checks for a button press, and exits if it detects one or loops until it does. It sets the global variables X% and Y% to the position of the pointer and other code decides what to do on the basis of the values in these.
The program starts with some basic initialisation, mostly in PROC INIT
, to draw the screen and set up the sound. It also assembles some machine code (in PROC A
) for processor-intensive activities, of which more later. It then calls the main polling loop on line 70 and takes action depending on the pointer position, on the following lines:
- 80: a click in the right-hand menu is detected and
PROC FUNCTION
is called to handle that, then control is returned to line 70. - 90: a click in the scratch pad is detected and handled.
- 100: A click on the character picker at the bottom of the screen is detected and
PROC NEWCHAR
is called to handle it. - 110: A click on one of the pixels in the large 8 x 8 grid is detected and handled via
PROC CHBIT
- 120: A click on the program name is detected and handled by calling
PROC NAME
- 130: If we get to here then the click was in a place that has no function so a sound is made (if sound is turned on, as indicated by a flag in the variable
S%
) and we go back to line 70 to await the next click.
That is the main program flow complete. The remaining code consists of subroutines, as 25 named functions and procedures, which implement the various functions. A quick look at these reveals some interesting features (and a great deal of banality!):
PROC SCREEN
- Draws the whole screen. This exploits various special control characters, sent to the screen using VDU
commands, as well as some simple graphics drawing.
PROC INIT
- Initialises the system and storage etc. Some odd features here, e.g. on line 490 the 200 byte space for the scratch pad (reserved on line 270 at an address stored in TB%
) is initialised to space characters by looping through writing a byte 32 to each byte address (this is an 8-bit system, remember!), and this is followed by a similar initialisation of the 1200 byte space at STR%
(used for the six scratchpads) but this time we do it much faster by writing the 4 byte word 0x202020 at every 4th byte address. Not only does there seem to be no reason why the first space shouldn’t be initialised using the faster algorithm, there also appears to be a classic off-by-one error which would result in a spurious extra byte being written after the end of the TB%
space (the loop runs from 0 to 200)! Fortunately I think that the way the space is reserved, this just gets written into the first byte of STR%
, which is initialised the same way anyway. How embarrassing! I guess we can at least say that this supports the oft-made observation that the two hard problems in computing are naming things, cache invalidation and off-by-one errors. Of note in here is the ?
operator, which reads or writes a byte at a specified address and offset, and the corresponding !
operator which does the same for a four byte word; these are more useful equivalents of the PEEK
and POKE
functions found in Microsoft BASIC, and were complemented by a $
operator which would read or write a string at a specified address.
PROC READ(C%)
- Reads the current definition of the character with the ASCII code given in C%
. This makes a call to an OS routine at the address 0xFFF1. Again, this is an 8-bit system with a 16 bit address bus - this address is right at the top of memory, where the OS kept a dispatch table. I don’t know why I didn’t have this address in a named variable (e.g. OSWORD
) as I did OSWRCH
! Note that the variables A%
, X%
and Y%
are loaded into the 6502 processor’s A
, X
and Y
registers before the call is made, in this case giving an address for a parameter block and a function code telling the OS what we want it to do, here function 10 which reads a character definition. After reading, the definition is copied to our workspace at the address in V%
and then we call PROC VCS
to update the display of the character definition command.
PROC SET
- Updates the definition of the currently edited character by issuing the VDU 23
command as displayed. For some reason there are no blank lines separating this from the next PROC, unlike most other cases.
PROC VCS
- Updates the display of the VDU
command that would define the current character to reflect the current state of the editor.
PROC WRITE(N%)
- Updates the display of which character we are currently defining.
PROC FILL
- Updates the grid in the top left which shows the current character definition. Just a veneer for the machine code defined later.
FN CLR
- Handler for a confirmation click after a click on the CLEAR! button. Highlights the button and then waits for another click and resets the button, returning a value indicating whether the confirmation click was in the character grid, the scratchpad or elsewhere. Note that BBC BASIC boolean expressions represent true and false as -1 and 0. This turned out to be quite useful for doing bitwise operations with the results in all sorts of horrible hacks needed to achieve useful performance!
PROC COPYER
- Handler for a confirmation click after a click on the COPYER button. Highlights the button, draws the full character set, and then waits for another click and resets the button. If the confirmation click was on the character set, works out which character and reads the definition. Finally updates all the various parts of the display that need it.
PROC TYPEFACE
- Handler for a confirmation click after a click on the FOUNTS button. Highlights the button, draws the font menu, and then waits for another click and resets the button. If the confirmation click was on the font menu, applies the chosen effect. Finally updates all the various parts of the display that need it.
PROC SPRN(A$,AD%)
- Print a string with one of the font effects applied. This calls machine code at SPR
for each character in the string, having first updated the code by writing the address of the appropriate code to apply the selected effect at the address SPRVEC
. It’s interesting that there are named constants for selecting the different effects even though they are not really used much. At the time this was written I think that being able to display things in different fonts was an innovation - any software that might have had occasional such effects on fixed screen titles or similar would have used hand-edited characters or graphics. I have a vague recollection of building a library that made this feature more widely available as an OS extension and this is probably a part of it; a precursor or an extract. I must dig further through my hoard of cassettes and see if I can find more evidence of that.
PROC ADLINE(A$)
- Add a line to a BASIC program in memory. We can see here that programs are represented as two bytes containing the line number followed a single byte containing the length of the line, and then the contents of the line itself. This routine is used on exit, when building the program which will create the newly defined character set on demand.
PROC MPRG
- Handler for the ENDRUN button. Constructs a new BASIC program in memory, in a separate area from the running program (i.e. just above HIMEM
), which is a simple program to redefine a set of characters as created by this editor. We can see that the code 0xF4 is the code used for the keyword REM
and 0xEF for the keyword VDU
. This process is quite slow and can be interrupted by another click if it is started accidentally. Once complete, the F9 key is programmed to switch to the memory space in which the newly program has been created, and the F0 key is programmed to switch back to the memory space the editor is in and run it. Finally, an F9 keypress is inserted into the keyboard input buffer and the program quits. This trick allows the editor to effectively vanish and be replaced by the newly created program, allowing the user to save it for use as required and easily get back to the editor by pressing F0. This was the best mechanism I could come up with for making the created code easily available in wold where simply saving it to a file was not a sensible option.
PROC TABLE(C%)
- Updates the screen with the passed-in character, first updating the right cell in the table at the bottom of the screen, then the redrawing the whole scratchpad by calling the machine code defined at the label .TABLE
in PROC A
. The name and structure of this suggest that the scratchpad was added to the design late in the day. This is called whenever the a character definition changes.
PROC DR
- Draws the pointer on the screen, including the currently defined character if the pointer is over the scratchpad. Note that this uses GCOL 3
mode, meaning that the plot uses exclusive-or to apply the pointer colour to the screen. This is a 1-bit-per-pixel screen mode, so each pixel under the pointer is flipped. Using this technique means that the pointer can be removed by simply drawing it again in the same place so there is no need to maintain a copy of what is under the pointer pointer for this purpose. Also of note is the check on line 1830 for a Ctrl-@ keypress, which causes a call to a non-standard OS command that dumps the screen to a specific printer that I had at the time (an Epson FX-80).
PROC CURS
- The main event loop. Draws the pointer, following the joystick position, and waits for a button click. Also handles detecting a space bar keypress and switching to keyboard mode if this happens, in which arrow key presses move the cursor and the space bar may be used as a button click. The INKEY
function checks whether a specific key is down, e.g. INKEY -99
tests the space bar.
FN CTRL
- Checks for and handles switching between joystick and keyboard modes.
PROC NAME
- Handler a click on the name of the program, switching between that and a copyright message.
PROC CALLCODE(ADDRESS%)
- Calls machine code at a given address and then updates the current character definition and the parts of the screen where this is shown.
PROC FUNCTION(F%)
- Handler for a click on one of the buttons. Line 2420 uses the BBC BASIC ON GOTO
command to go to a different line depending on which button was pressed. Simple functions such as rotating the screen colours or turning sound on and of are handled immediately while more complex ones are delegated to their own procedures.
PROC NEWCHAR
- Handler for a click on the character selector at the bottom. It looks as though the multiple scratchpad feature was a late addition because that’s handled by a couple of inserted GOTO
s at the beginning, one of which jumps over the next two PROC
definitions!
PROC SVT(A%)
- Saves a scratchpad, mostly by calling the machine code at SVT
.
PROC RDT(A%)
- Reads a scratchpad, mostly by calling the machine code at RDT
, and updates the display.
PROC CHBIT(BY%,BT%)
- Flips a bit in a byte in the current character definition and updates the display.
PROC A
- Assembles the machine code used in various other places, using BBC BASIC’s built in 6502 assembler. Note that this is done twice, in a loop, so that forward references can be resolved. To do this, the OPT
pseudo-instruction is set to 0
which suppresses errors so that on the first pass the forward-references to missing labels won’t be complained about and on the second pass they will work because the labels were defined in the first. During debugging you would use OPT I%
to enable error reporting, assembly listing and bounds checking on the second pass. The labels on the entry points are:
ROTATE
- Rotates current character definition by 90 degrees into a buffer atBLK%
before copying back to the current definition atV%
. Note how this allows for easy communication between the BASIC and assembler code. The algorithm takes each byte (i,e, row of 8 pixels) of the character definition and the performs 8 rotations of that byte, rotating the top bit into the bottom bit of each of the 8 bytes of the target. After repeating this 8 times the target contains the rotated character.INVERT
- Flips every bit in the current character definition by simply XORing each byte with 255.FILL
- Fills the character definition grid with the current character definition by writing space characters with the background and foreground colours set according to the status of each bit.MIRROR
- Mirrors the current character definition around a vertical axis, using the buffer atBLK%
, by rotating the bytes left so that each bit is rotated into the carry bit and then rotated right into the buffer.SHDOWN
- Shifts the current character definition down one pixel, moving the bottom row of pixels to the top. This is done in place, using theY
register to store the bottom row while the rest are shifted.SHLEFT
- Shifts the current character definition left one pixel, moving the left column of pixels to the right. Each byte is shifted left one bit and then 0 is added so that what was previously the top bit, now in the carry bit, becomes the bottom bit.TABLE
- Writes the current scratchpad on the screen.BLANK
- Blanks the current character definition. I’m not convinced there was much speed gain by doing this in machine code!ITALIC
- Applies an algorithmic italic effect to the current character definition. This uses theSHLEFT
code, above, to shift the definition left until there is a bit set in the left hand column, then shifts the rows by different amounts to the right - more at the top than the bottom.BOLD
- Applies an algorithmic bold effect to the current character definition. Takes each byte, shifts it one bit to the right, then ors it with the original.SHADOW
- Applies an algorithmic shadow effect to the current character definition by setting pixels below and to the right, and removing the originals by means of an xor.LITAL
- Applies an algorithmic italic effect but this time leaning to the left.LBOLD
- Applies an algorithmic bold effect but this time setting extra pixels to the left.LSHADOW
- Applies an algorithmic shadow effect but this time with the shadow below and to the left.SPR
- Prints a string with one of the above effects applied. This works by loading the definition of each character in the string, applying the effect, using the result to redefine character 255 and then writing that to the screen. Note that the subroutine call (JSR
) atSPRVEC
is to 0x0000 - this is overwritten with the address of one of the font-effect subroutines by the calling code beforeSPR
is called. Also note that the last thing this routine does is jump (JMP
) into the OS write character routine at 0xFFEE, rather than calling it as a subroutine - a tail-call optimisation! I don’t know why this code doesn’t use the labelOSWRCH
to call write character routine, given that we’ve gone to the effort to create that on line 480.SVT
- Saves the current scratchpad to one of the six scratchpad spaces.
So, finally, here is the code pretty much as I found it on the cassette. I’ve removed some comments at the top and bottom which contained control characters that were there to make it difficult to get a listing of the code. Otherwise, this is it:
30*FX200,0
40*FX4,1
50REM ON ERROR RUN
60MODE4:HIMEM=&5000:PROCINIT
70SOUND&11,0,0,1:SOUND&10,0,0,1:PROCCURS
80IFX%>1040ANDY%>175ANDY%<931THENPROCFUNCTION((Y%-175)DIV63+1):GOTO70
90IFX%>544ANDX%<1024ANDY%>524ANDY%<940THENSOUND1,10*S%,175,1:XT%=X%:YT%=Y%:X%=(X%-544)DIV32:Y%=(416-(Y%-512))DIV32:X%=TB%+Y%*15+X%:?X%=CHAR%+(CHAR%-32)*(?X%=CHAR%):X%=XT%:Y%=YT%:CALLTABLE:GOTO70
100IFY%>0ANDY%<160THENPROCNEWCHAR:GOTO70
110IFX%<528ANDY%>495ANDX%>20THENPROCCHBIT(((1007-Y%)DIV64)+1,7-(X%-20)DIV66):GOTO70
120IFX%>500ANDY%>940THENPROCNAME:GOTO70
130IFS%SOUND0,-10,4,1:TIME=0:REPEATUNTILTIME
140GOTO70
150END
160
170
180
190DEFPROCSCREEN
200
210CLS
220GOTO240
230VDU25,64,16;0;25,5,16;1007;25,5,1279;1007;25,5,1279;0;25,5,20;0;25,5,20;1007;25,64,1275;0;25,5,1275;1007;
240FORI%=0TO8:MOVEI%*64+20,1007:DRAWI%*64+20,495:MOVEI%*64+16,1007:DRAWI%*64+16,495:MOVE16,1007-I%*64:DRAW528,1007-I%*64
250NEXT:RESTORE320:FORI%=1TO12:READA$:PRINTTAB(33,I%*2+1)A$;:MOVE1039,1007-I%*64:DRAW1279,1007-I%*64:NEXT:MOVE1039,1007:DRAW1039,165:MOVE1035,1007:DRAW1035,165
260MOVE16,495:DRAW1279,495
270COLOUR129:COLOUR0:PRINTTAB(17,1)" BBC Character Definer "
280MOVE528,1007:MOVE1279,1007:PLOT85,528,992:PLOT85,1279,992:MOVE528,992:MOVE528,956:PLOT85,576,992:PLOT85,576,956
290MOVE528,960:MOVE1279,960:PLOT85,528,940:PLOT85,1279,940
300MOVE16,160:DRAW1279,160:MOVE16,80:DRAW1279,80:FORI%=0TO16:MOVEI%*64+112,160:DRAWI%*64+112,0:MOVEI%*64+116,160:DRAWI%*64+116,0:NEXT
310MOVE16,0:MOVE16,160:PLOT85,112,0:PLOT85,112,160:MOVE1140,0:MOVE1140,160:PLOT85,1279,0:PLOT85,1279,160
320DATAROTATE,INVERT,MIRROR,COLOUR,BKGRND,ENDRUN,FOUNTS,SHDOWN,SHLEFT,SOUNDS,COPYER,CLEAR!
321PRINTTAB(36,28)"123"TAB(36,30)"456"TAB(2,28)"N"TAB(2,29)"X"TAB(2,30)"T";:COLOUR128:COLOUR1:PRINTTAB(36,28)"1";:OTB%=1
330GCOL0,1:FORI%=0TO20STEP4:MOVEI%,I%:DRAWI%,1023-I%:DRAW1279-I%,1023-I%:DRAW1279-I%,I%:DRAWI%,I%:NEXT
340GCOL0,0:FORI%=8TO12STEP4:MOVEI%,I%:DRAWI%,1023-I%:DRAW1279-I%,1023-I%:DRAW1279-I%,I%:DRAWI%,I%:NEXT:GCOL0,1
350FORI%=224TO255:PROCTABLE(I%):NEXT
360ENDPROC
370
380
390
400DEFPROCINIT
410
420VDU23,1,0;0;0;0;:*KEY10O.|M
430ENVELOPE1,2,17,17,17,10,10,10,20,20,20,20,100,200:ENVELOPE2,2,-17,-17,-17,10,10,10,20,20,20,20,100,200:ENVELOPE3,0,17,17,-17,1,0,1,20,20,20,20,100,200:ENVELOPE4,2,-7,-7,-7,10,10,10,0,0,0,0,1,1
440ENVELOPE5,1,-26,-36,-45,255,255,255,127,0,0,-127,90,0:SOUND&11,5,255,255
450ENVELOPE6,1,-13,11,-8,14,11,10,126,-1,0,-1,126,63
460ENVELOPE7,1,-16,-26,-35,20,20,20,127,0,0,-127,90,0
470DIMCODE% 700,V% 10,BLK% 10,SV% 10,TB% 200,STR% 1200
480OSWRCH=&FFEE
490FORI%=0TO200:TB%?I%=32:NEXT
495FORI%=0TO1198STEP4:STR%!I%=&20202020:NEXT
500PROCA
510BKG%=3:FGR%=4:CHAR%=224:VDU19,1,FGR%;0;19,0,BKG%;0;
520X%=656:Y%=528:LX%=X%:LY%=Y%
530PROCSCREEN
540PROCREAD(CHAR%)
550PROCWRITE(CHAR%)
560PROCTABLE(CHAR%)
570PROCFILL
580S%=TRUE
590NAME%=TRUE
600CCH%=0
610JOY%=TRUE:STP%=32
620*FX20,0
630VDU19,1,FGR%;0;19,0,BKG%;0;
640ENDPROC
650
660
670
680DEFPROCREAD(C%)
690
700LOCALX%,Y%,A%
710X%=BLK%MOD256:Y%=BLK%DIV256:?BLK%=C%:A%=10:CALL&FFF1:FORI%=0TO8:V%?I%=BLK%?I%:NEXT:PROCVCS:ENDPROC
720
730
740
750DEFPROCSET
760
770VDU23:FORI%=0TO8:VDUV%?I%:NEXT:PROCVCS:ENDPROC
780DEFPROCVCS
790PRINTTAB(1,18)"VDU23,";V%?0",";SPC(32-POS);:PRINTTAB(1,19);:FORI%=1TO7:PRINT;V%?I%",";:NEXT:PRINT;V%?8;SPC(32-POS)
800ENDPROC
810
820
830
840DEFPROCWRITE(N%)
850
860PRINTTAB(1,22)"Defining character CHR$(";CHAR%;") ";
870ENDPROC
880
890
900
910DEFPROCFILL
920
930CALLFILL:COLOUR1:COLOUR128:ENDPROC
940ENDPROC
950
960
970
980DEFFNCLR
990
1000IFS%SOUND1,6,200,255
1010COLOUR129:COLOUR0:PRINTTAB(33,25)"CLEAR!":COLOUR128:COLOUR1:PROCCURS:PRINTTAB(33,25)"CLEAR!"
1020IFX%>544ANDX%<1024ANDY%>524ANDY%<940THEN=2
1030=-(X%<528ANDY%>495ANDX%>20)
1040
1050
1060
1070DEFPROCCOPYER
1080
1090CT%=CHAR%:PRINTTAB(33,23);:COLOUR0:COLOUR129:PRINT"COPYER";:COLOUR1:COLOUR128:IFS%SOUND1,-10,200,3:SOUND1,0,0,1:SOUND1,-10,200,3:SOUND1,0,0,1:REPEATUNTILADVAL(-6)=15
1100VDU28,1,25,31,17,12:FORI%=32TO255:VDUI%+95*(I%=127):NEXT
1110PROCCURS:IFX%>1023ORX%<32ORY%>480ORY%<191GOTO1150
1120IFS%SOUND0,-10,2,3
1130CHAR%=(X%DIV32+(289-(Y%-191))DIV32*31)MOD225+31
1140VDU28,1,17,1,17:PROCREAD(CHAR%):V%?0=CT%:CHAR%=CT%:PROCSET
1150CCH%=0:VDU28,1,25,31,17,12,26,31,33,23:PRINT"COPYER";:IFS%SOUND1,-10,150,3:SOUND1,0,0,1:SOUND1,-10,150,3:SOUND1,0,0,1:REPEATUNTILADVAL(-6)=15
1160CHAR%=CT%:PROCREAD(CHAR%):PROCWRITE(CHAR%):PROCFILL:PROCTABLE(CHAR%):ENDPROC
1170
1180
1190
1200DEFPROCTYPEFACE
1210
1220IFS%SOUND1,6,100,5:SOUND1,0,0,0
1230PRINTTAB(33,15);:COLOUR0:COLOUR129:PRINT"FOUNTS";:COLOUR1:COLOUR128
1240GCOL0,1:VDU28,1,25,31,17,12,29,0;0;26:FORI%=9TO12:MOVE100,1007-I%*64:DRAW750,1007-I%*64:NEXT:MOVE100,431:DRAW100,239:MOVE104,239:DRAW104,431:MOVE750,431:DRAW750,239:MOVE754,239:DRAW754,431
1250MOVE425,239:DRAW425,431:MOVE429,239:DRAW429,431
1260PRINTTAB(4,19);:PROCSPRN("Italic",LITAL):PRINTTAB(4,21);:PROCSPRN("Boldface",LBOLD):PRINTTAB(4,23);:PROCSPRN("Shadow",LSHADOW)
1270PRINTTAB(17,19);:PROCSPRN("Italic",ITALIC):PRINTTAB(15,21);:PROCSPRN("Boldface",BOLD):PRINTTAB(17,23);:PROCSPRN("Shadow",SHADOW)
1280REPEATUNTILADVAL-6=15:SOUND&11,0,0,0
1290VDU26:PROCCURS:VDU28,1,25,31,17,12,26:IFX%<100ORX%>750ORY%<239ORY%>431SOUND1,10*S%,100,1:GOTO1330
1300LH%=-(X%<427):RH%=-(X%>426):IFY%<304SOUND0,2,4,-10*S%:SOUND1,S%*10,150,10:PROCCALLCODE(SHADOW*RH%+LSHADOW*LH%)
1310IFY%>303ANDY%<368SOUND0,2,4,-10*S%:SOUND1,S%*10,100,10:PROCCALLCODE(BOLD*RH%+LBOLD*LH%)
1320IFY%>367SOUND0,2,4,-10*S%:SOUND1,S%*10,50,10:PROCCALLCODE(ITALIC*RH%+LITAL*LH%)
1330PRINTTAB(33,15)"FOUNTS";:PROCREAD(CHAR%):PROCWRITE(CHAR%):ENDPROC
1340
1350
1360
1370DEFPROCSPRN(A$,AD%)
1380
1390LOCALX%,Y%,A%:X%=SV%MOD256:Y%=SV%DIV256:?SV%=255:A%=10:CALL&FFF1:?(SPRVEC+1)=AD%MOD256:?(SPRVEC+2)=AD%DIV256
1400FORJ%=1TOLENA$:A%=ASCMID$(A$,J%):CALL SPR:NEXT
1410VDU23,255:FORI%=1TO8:VDUSV%?I%:NEXT:?V%=CHAR%:X%=V%MOD256:Y%=V%DIV256:A%=10:CALL&FFF1:ENDPROC
1420
1430
1440
1450DEFPROCADLINE(A$)
1460
1470?ADD%=LINN%DIV256:ADD%?1=LINN%MOD256:ADD%?2=LENA$+4:$(ADD%+3)=A$:ADD%=ADD%+4+LENA$
1480LINN%=LINN%+10
1490ENDPROC
1500
1510
1520
1530DEFPROCMPRG
1540
1550PRINTTAB(33,13);:COLOUR0:COLOUR129:PRINT"ENDRUN";:COLOUR1:COLOUR128
1560CT%=CHAR%:COLOUR129:COLOUR0:PRINTTAB(1,22)" Please wait, creating program "TAB(1,21)SPC31TAB(1,23)SPC31;:COLOUR1:COLOUR128
1570LINN%=10:ADD%=HIMEM+1:?HIMEM=13
1580PROCADLINE("basecode%=224:"+CHR$&F4+" Start code for definitions")
1590FORCHAR%=224TO255:PROCREAD(CHAR%):A$=CHR$&EF+"23,basecode%+"+STR$(CHAR%-224):FORJ%=1TO8:A$=A$+","+STR$(V%?J%):NEXT:PROCADLINE(A$)
1600IFINKEY-99OR(ADVAL(0)AND3)>0THENCHAR%=300:NEXT:CHAR%=CT%:VDU28,1,25,31,17,12,26:PROCREAD(CHAR%):PROCWRITE(CHAR%):PROCFILL:PRINTTAB(33,13)"ENDRUN";:ENDPROC
1610NEXT:PROCADLINE(" "):PROCADLINE(" ")
1620PROCADLINE(CHR$&F4+" You may save and load this program as usual and the variable basecode may be altered to change the range defined. Merge with other software to use (renumber if necessary).")
1630PROCADLINE(CHR$&F4+" By creating several of these programs and altering basecode, you may define the entire range of characters.")
1640PROCADLINE(" "):PROCADLINE(CHR$&F4+" USE RED KEY F0 TO RESTART AFTER SAVING THIS PROGRAM")
1650?ADD%=255:ADD%?1=255
1660A$=STR$(PAGE):A$="|U|V7PA.="+A$+"|MO.|MRUN|M":OSCLI("KEY0"+A$)
1670*KEY9|U|LPA.=&5000|MHIM.=&5800|MO.|M*FX200,0|ML.|L|M
1680*FX15,0
1690*FX138,0,137
1700VDU20,23,1,1;0;0;0;
1710END
1720
1730
1740
1750DEFPROCTABLE(C%):VDU26:COLOUR1:COLOUR128:PRINTTAB(((C%-224)MOD16)*2+4,((C%-224)DIV16)*2+28)CHR$C%;
1760CALLTABLE
1770ENDPROC
1780
1790
1800
1810DEFPROCDR
1820
1830IFINKEY(0)=0THENOSCLI"GWINDOW":OSCLI"GIMAGE EPSON T R1":VDU26
1840GCOL3,1:VDU29,X%;Y%;:MOVE0,0:MOVE40,-20:PLOT85,20,-40:IFX%<545ORX%>1023ORY%>939ORY%<525ENDPROC
1850MOVE-16,16:VDU5,CHAR%,4,26,10,8,23,1,0;0;0;0;:ENDPROC
1860
1870
1880
1890DEFPROCCURS
1900
1910IFFNCTRL GOTO1990
1920PROCDR:REPEATOSCLI"FX19":PROCDR:X%=(65536-ADVAL(1))/53+22:Y%=ADVAL(2)/66+16
1930IFINKEY-99THENUNTILTRUE:X%=656:Y%=528:GOTO1910
1940PROCDR:UNTIL(ADVAL(0)AND3)=0
1950REPEATOSCLI"FX19":PROCDR:X%=(65536-ADVAL(1))/53+22:Y%=ADVAL(2)/66+16
1960IFINKEY-99THENUNTILTRUE:X%=656:Y%=528:GOTO1910
1970PROCDR:UNTIL(ADVAL(0)AND3)
1980PROCDR:ENDPROC
1990PROCDR:REPEAT:OSCLI"FX19":PROCDR
2000Y%=Y%+STP%*INKEY-42-STP%*INKEY-58:IFY%>1023THENY%=16:ELSEIFY%<0Y%=1007
2010X%=X%+STP%*INKEY-26-STP%*INKEY-122:IFX%>1279THENX%=16:ELSEIFX%<0X%=1263
2020IF(ADVAL(0)AND3)>0THENUNTILTRUE:GOTO1910
2030PROCDR:UNTILNOTINKEY-99
2040REPEAT:OSCLI"FX19":PROCDR
2050Y%=Y%+STP%*INKEY-42-STP%*INKEY-58:IFY%>1023THENY%=16:ELSEIFY%<0Y%=1007
2060X%=X%+STP%*INKEY-26-STP%*INKEY-122:IFX%>1279THENX%=16:ELSEIFX%<0X%=1263
2070IF(ADVAL(0)AND3)>0THENUNTILTRUE:GOTO1910
2080PROCDR:UNTILINKEY-99:PROCDR:ENDPROC
2090
2100
2110
2120DEFFNCTRL
2130
2140IFJOY%JOY%=NOTINKEY-99ELSEJOY%=(ADVAL(0)AND3)>0
2150=NOTJOY%
2160IFJOY%GOTO2190
2170IF(ADVAL(0)AND3)>0JOY%=TRUE
2180=NOTJOY%
2190IF(INKEY-99)THENJOY%=FALSE
2200=NOTJOY%
2210
2220
2230
2240DEFPROCNAME
2250
2260IFS%SOUND0,-15,7,3:SOUND1,5,200,3:SOUND1,0,0,1:REPEATUNTILADVAL(-6)=15
2270NAME%=NOTNAME%:COLOUR129:COLOUR0:PRINTTAB(17,1);:IFNAME%THENPRINT" BBC Character Definer":ELSEPRINT" (c) Merlyn Kline 1984"
2280COLOUR1:COLOUR128
2290ENDPROC
2300
2310
2320
2330DEFPROCCALLCODE(ADDRESS%)
2340
2350CALL ADDRESS%:PROCSET:PROCTABLE(CHAR%):PROCFILL
2360ENDPROC
2370
2380
2390
2400DEFPROCFUNCTION(F%)
2410
2420ON(13-F%)GOTO2430,2470,2500,2530,2550,2570,2590,2610,2670,2730,2760,2770
2430REPEATIFS%SOUND&11,1,10,100
2440PROCCALLCODE(ROTATE)
2450UNTIL(ADVAL(0)AND3)=0ANDNOTINKEY-99
2460ENDPROC
2470IFS%SOUND1,2,200,3
2480PROCCALLCODE(INVERT)
2490ENDPROC
2500IFS%SOUND0,3,100,3:SOUND1,3,140,3
2510PROCCALLCODE(MIRROR)
2520ENDPROC
2530IFS%SOUND1,-10,250,100:TIME=0:REPEATUNTILTIME
2540REPEATFGR%=(FGR%+1)MOD8:UNTILBKG%<>FGR%:VDU19,1,FGR%,0,0,0:ENDPROC
2550IFS%SOUND1,-10,250,100:TIME=0:REPEATUNTILTIME
2560REPEATBKG%=(BKG%+1)MOD8:UNTILBKG%<>FGR%:VDU19,0,BKG%,0,0,0:ENDPROC
2570IFS%SOUND1,5,255,255
2580PROCMPRG:ENDPROC
2590PROCTYPEFACE
2600ENDPROC
2610REM SHDOWN
2620REPEAT
2630IFS%SOUND&11,4,200,30:SOUND&10,-15,7,30
2640PROCCALLCODE(SHDOWN)
2650UNTIL(ADVAL(0)AND3)=0ANDNOTINKEY-99
2660ENDPROC
2670REM SHLEFT
2680REPEAT
2690IFS%SOUND&10,0,0,1:SOUND0,-10,4,10
2700PROCCALLCODE(SHLEFT)
2710UNTIL(ADVAL(0)AND3)=0ANDNOTINKEY-99
2720ENDPROC
2730S%=NOTS%:IFS%FORI%=50TO250STEP25:SOUND1,-10,I%,1:NEXT:REPEATUNTILADVAL(-6)=15
2740PRINTTAB(33,21);:IFS%PRINT"SOUNDS"ELSEPRINT"SILENT"
2750ENDPROC
2760PROCCOPYER:ENDPROC
2770REM
2780ONFNCLR GOTO2800,2820:ELSEIFS%SOUND&11,-10,100,2:SOUND1,0,0,1:REPEATUNTILADVAL(-6)=15
2790ENDPROC
2800IFS%SOUND0,-10,4,100
2810PROCCALLCODE(BLANK):ENDPROC
2820IFS%SOUND0,-10,5,100
2830FORI%=0TO200:TB%?I%=32:NEXT:PROCTABLE(CHAR%)
2840ENDPROC
2850
2860
2870
2880DEFPROCNEWCHAR
2881REMX%>120ANDX%<1120
2890
2895IFX%>1119GOTO2911
2896IFX%<121GOTO2914
2900IFS%SOUND&11,7,1,255
2910PROCSET:PROCTABLE(CHAR%):CHAR%=(X%-120)DIV63+224+16*((160-Y%)DIV80):PROCREAD(CHAR%):PROCFILL:PROCTABLE(CHAR%):PROCWRITE(CHAR%):ENDPROC
2911NTB%=(X%-1119)DIV54-3*(Y%<80)+1:PROCSVT(OTB%):PROCRDT(NTB%):OTB%=NTB%:ENDPROC
2912DEFPROCSVT(A%):COLOUR129:COLOUR0:PRINTTAB(36+((A%-1)MOD3),28-2*(A%>3));A%;:A%=(A%-1)*200+STR%:?(SVA+1)=A%MOD256:?(SVA+2)=A%DIV 256:CALLSVT:ENDPROC
2913DEFPROCRDT(A%):COLOUR128:COLOUR1:PRINTTAB(36+((A%-1)MOD3),28-2*(A%>3));A%;:A%=(A%-1)*200+STR%:?(RDA+1)=A%MOD256:?(RDA+2)=A%DIV256:CALLRDT:CALLTABLE:ENDPROC
2914REPEAT:PROCSVT(OTB%):REPEATOTB%=OTB%+1:IFOTB%>6 OTB%=1
2915A%=(OTB%-1)*200+STR%:?(RDA+1)=A%MOD256:?(RDA+2)=A%DIV256:CALLRDT:UNTIL?TF1<>32OR OTB%=1:PROCRDT(OTB%):TIME=0:REPEATUNTILTIME>Y%DIV5
2916UNTIL(ADVAL(0)AND3)=0ANDNOTINKEY-99:ENDPROC
2920
2930
2940
2950DEFPROCCHBIT(BY%,BT%)
2960
2970IFS%SOUND1,-10,200+100*((V%?BY%AND2^BT%)>0),1
2980V%?BY%=V%?BY%EOR2^BT%:PROCSET:PROCTABLE(CHAR%):PROCFILL
2990ENDPROC
3000
3010
3020
3030DEFPROCA
3040
3050FORI%=0TO3STEP3:P%=CODE%:[OPT0
3060.ROTATE:LDX#7:LDA#0:.RCLR:STA BLK%+1,X:DEX:BPL RCLR:LDY#7:.RBYT:LDX#0:LDA V%+1,Y:.RBIT:ASL A:ROL BLK%+1,X:INX:CPX#8:BNE RBIT:DEY:BPL RBYT:LDX#7:.RMOV:LDA BLK%+1,X:STA V%+1,X:DEX:BPL RMOV:RTS
3070.INVERT:LDX#7:.IBYT:LDA V%+1,X:EOR#255:STA V%+1,X:DEX:BPL IBYT:RTS
3080.FILL:LDX#7:.FBYT:LDA#31:JSR OSWRCH:LDA#15:JSR OSWRCH:TXA:ASL A:CLC:ADC#1:JSR OSWRCH:LDY#7:LDA V%+1,X:.FBIT:LSR A:PHA:PHP:LDA#17:JSR OSWRCH:LDA#0:PLP:PHP:ROL A:ORA#128:JSR OSWRCH:LDA#17:JSR OSWRCH:LDA#0:PLP:ROLA:EOR#1:JSR OSWRCH
3090LDA#32:JSR OSWRCH:LDA #8:JSR OSWRCH::JSR OSWRCH:JSR OSWRCH:PLA:DEY:BPL FBIT
3100DEX:BPL FBYT:RTS
3110.MIRROR:LDX#7:LDA#0:.MCLR:STA BLK%+1,X:DEX:BPL MCLR:LDX#7:.MBYT:LDY#7:LDA V%+1,X:.MBIT:ASL A:ROR BLK%+1,X:DEY:BPL MBIT:DEX:BPL MBYT:LDX#7:.MMOV:LDA BLK%+1,X:STA V%+1,X:DEX:BPL MMOV:RTS
3120.SHDOWN:LDY V%+8:LDX#6:.SHLOOP:LDA V%+1,X:STA V%+2,X:DEX:BPL SHLOOP:STY V%+1:RTS
3130.SHLEFT:LDX#7:.SHLEF1:LDA V%+1,X:ASL A:ADC #0:STA V%+1,X:DEX:BPL SHLEF1:RTS
3140.TABLE:LDX#0:LDA#3:PHA:.TLINE:LDA#31:JSROSWRCH:LDA#17:JSROSWRCH:PLA:JSROSWRCH:CLC:ADC#1:PHA:LDY#15:.TCOL:LDA TB%,X:JSROSWRCH:INX:CPX#195:BEQTROUT:DEY:BNETCOL:BEQTLINE:.TROUT:PLA:RTS
3150.BLANK:LDX#8:LDA#0:.B1:STAV%,X:DEX:BNEB1:RTS
3160.ITALIC:LDA#8:STA BLK%+1:LDA#6:STA BLK%+2:.NXCHNK:LDX#7:LDA#0:.CHKLFT:ORA V%+1,X:DEX:BPL CHKLFT:AND#128:BNE DOCHNK:JSR SHLEFT:DEC BLK%+1:BNE NXCHNK:RTS
3170.DOCHNK:LDX#0:.NXLIN:LDA V%+1,X:CLC:ROR A:STA V%+1,X:INX:CPX BLK%+2:BNE NXLIN:TXA:SEC:SBC#2:STA BLK%+2:BNE NXCHNK:RTS
3180.BOLD:LDX#7:.BOLDL1:LDA V%+1,X:LSR A:ORA V%+1,X:STA V%+1,X:DEX:BPL BOLDL1:RTS
3190.SHADOW:LDX#7:.SHDL1:LDA V%,X:LSR A:ORA V%,X:ORA V%+1,X:EOR V%+1,X:STA V%+1,X:DEX:BNE SHDL1:LDA V%+1:LSR A:ORA V%+1:EOR V%+1:STA V%+1:RTS
3200.LITAL:LDA#8:STA BLK%+1:LDA#6:STA BLK%+2:.LINXCHNK:LDX#7:LDA#0:.CHKRHT:ORA V%+1,X:DEX:BPL CHKRHT:AND#1:BNE LDOCHNK:JSR SHRHT:DEC BLK%+1:BNE LINXCHNK:RTS
3210.LDOCHNK:LDX#0:.LINXLIN:LDA V%+1,X:ASL A:STA V%+1,X:INX:CPX BLK%+2:BNE LINXLIN:TXA:SEC:SBC#2:STA BLK%+2:BNE LINXCHNK:RTS
3220.SHRHT:LDX#7:.SHR1:LDA V%+1,X:LSR A:STA V%+1,X:DEX:BNE SHR1:RTS
3230.LBOLD:LDX#7:.LBOLDL1:LDA V%+1,X:ASL A:ORA V%+1,X:STA V%+1,X:DEX:BPL LBOLDL1:RTS
3240.LSHADOW:LDX#7:.LSHDL1:LDA V%,X:ASL A:ORA V%,X:ORA V%+1,X:EOR V%+1,X:STA V%+1,X:DEX:BNE LSHDL1:LDA V%+1:ASL A:ORA V%+1:EOR V%+1:STA V%+1:RTS
3250.SPR:STA V%:LDA#10:LDX#V%MOD256:LDY#V%DIV256:JSR &FFF1:.SPRVEC:JSR &0000:LDA #23:JSR &FFEE:LDA#255:JSR &FFEE:LDX#0:.SPR1:LDA V%+1,X:JSR &FFEE:INX:CPX#8:BNE SPR1:LDA #255:JMP &FFEE
3251.SVT:LDY#199:.SVL1:LDA TB%,Y:.SVA:STA &FFFF,Y:DEY:CPY#255:BNE SVL1:RTS:.RDT:LDA#0:STA TF1:LDY#199:.RDA:LDA&FFFF,Y:STA TB%,Y:ORA TF1:STA TF1:DEY:CPY#255:BNE RDA:RTS:.TF1:EQUB0
3260]:NEXT:ENDPROC