My Photo
Location: Downham Market, Norfolk, United Kingdom

Sunday, June 18, 2006

Writing the previous message set me thinking: was there some way I could modify BBC BASIC for Windows to reduce its tendency to crash if a dialogue box wasn't properly closed before exit? Could the IDE perhaps close any dialogue boxes itself? The answer was surprisingly simple: periodically scan for all windows owned by the BASIC program and, after the program exits, check to see if any of those windows still exist. If they do, forcibly close them by sending a WM_QUIT message (deprecated by Microsoft, but effective in this case). It's not a totally foolproof method, because a dialogue box must exist long enough for the periodic scan to notice it, but will catch many examples. As an unexpected bonus it also closes any Windows message boxes left open when the BASIC program exits. Version 5.21a of BBC BASIC for Windows implements this system.

Adapting BASIC to work in a Windows environment poses a number of problems, not least how to deal with the fact that Windows is an event based operating system, i.e. a Windows program can receive, asynchronously, incoming messages informing it of events to which it needs to react. Such events may include key presses, mouse clicks, menu selections, scroll commands etc. Key presses are not really a problem, because they can be queued just as they would be with a more traditional implementation, but standard BASIC contains no built-in mechanisms to deal with other asynchronous events.

One approach, and the one adopted by Microsoft for Visual Basic, is to radically change the language to incorporate the features needed by Windows. However desirable this may be (and personally I feel the end result is too different from traditional BASIC) it wasn't an option open to me because I needed to maintain compatibility with earlier versions of BBC BASIC. A radically changed language wouldn't be BBC BASIC!

Liberty Basic adopts an interesting approach; it has a specific statement (SCAN) to trigger event processing. SCAN instructs Liberty Basic to pause what it is doing and check for any 'pending' keyboard or mouse events. In some ways this is a neat solution, because it avoids the issue of asynchronous events entirely (it forces them to be synchronous) but it is quite limiting. For example, you must include a SCAN statement in every part of your program that you might want to react to a mouse click. I'm also unsure what happens if two or more events have happened since the last SCAN statement - do events get lost?

For BBC BASIC for Windows I decided to incorporate genuine event interrupts using the ON syntax (ON CLOSE, ON MOUSE, ON MOVE, ON SYS, ON TIME). BASIC checks whether an event has occurred after every statement, and if so does an automatic GOSUB to the appropriate service routine (if any). This is powerful but must be used with care - many BASIC programs are not designed to be interrupted! For example if a variable read by the service routine is declared as LOCAL (or PRIVATE) anywhere in the program, and the interrupt happens to occur in one of those places, the local version of the variable will be accessed. This can cause subtle and non-reproducible errors.

So I tend to recommend that the interrupt service routine simply loads a global variable (or array) with the relevant information; the global data can then be polled where required elsewhere in the program. Effectively this reproduces the functionality of Liberty Basic's SCAN statement but, importantly, retains the option of a genuine interrupt when one is needed. The possibility of multiple events occurring between polls can also be catered for by using a queue.