Monday, 26 January 2026

QBX: Border Fill Algorithm

In paint programs, a common operation is Flood Fill. You pick a colour, click on a region, and that region is "flooded" with that colour. Every adjacent pixel of the same colour is replaced by your new colour.

In QuickBASIC, there is a statement PAINT which provides a related function, but it is different in a subtle but very important way: It doesn't paint as long as it sees pixels of the same colour, it paints up until a specified border. It doesn't matter what pixels it is replacing. This is a related operation called a Border Fill.

It cannot be overstated: Border Fill is much trickier to implement than Flood Fill!

But, for QBX to faithfully execute QuickBASIC code, it needed a Border Fill algorithm that was reliable and fast.

The implementation of QBX's Border Fill uses an interesting data structure: It tracks a subset of a 2D area as a series of intervals using an Interval Set structure built on a B-Tree. The B-Tree can very quickly enumerate all elements whose keys are greater than or equal to a specified key. The intervals are placed into the tree using their right edges as the key. It is then possible to test whether a given coordinate is in the set or not by enumerating the intervals whose right edge is greater than or equal to the given coordinate. This immediately eliminates all sets that come before it, and the enumeration will either find an interval that contains the point, or it will find an interval that comes after the point -- in which case all future intervals also come after the point. Fast algorithms for intersecting intervals are also possible.

The initial implementation tried very hard to maintain a traditional queue of spans needing to be processed, but I just couldn't get it perfect. There was always some edge case that would either fail to paint or enter an infinite loop, processing areas it had already processed. I came to the conclusion that this was because:

  • Spans in the queue to be processed don't get merged together if they're adjacent, and
  • When advancing to a new scan and trying to expand left or right, the expansions are queued as independent entries (because they have different propagation flags)

But, it suddenly occurred to me that the merging problem could be solved with the existing interval set implementation I was already using to track which parts were processed, and that once I did that, there were no propagation flags any more, which meant that the extension could simply be processed as part of the span it came from, rather than being queued independently.

So, I reworked it to do exactly that, and that solved all the problems.

Saturday, 17 January 2026

QBX: PC Speaker Emulation

I'm excited by this. 🙂

Getting closer to running NIBBLES. The latest advancement: PC speaker sound emulation.

You might think, looking at this, that it's not _really_ PC speaker sound emulation, it's QBASIC PLAY statement emulation, right?

Wrong 🙂

Behind the scenes, there's a simulated 8253 timer chip. Its Timer 2 configuration is linked to a simulated 8042 keyboard controller chip (well, this last one is a bit of a stretch, because it only cares about the two speaker control bits). (For performance reasons, it simulates frequencies and ticks instead of communicating actual raw ticks.)

The function in charge of playing a note as part of a PLAY statement does so in a manner that reconfigures the 8253 to generate the target frequency. The sound you're hearing is the raw square wave resulting from the simulated output from the timer chip. Woohoo!

Wednesday, 7 January 2026

QBX: Now Runs Code

A small update: My QuickBASIC clone now runs code 🙂 At this point, only the specific, limited set of statements and functions actually used in FERN.BAS are supported. But, this proves the strategy.

Friday, 2 January 2026

Presenting: QBX

About 2 weeks ago, I got properly started on my QuickBASIC environment emulator. I call it QBX. It's now a bit over 21,000 lines of code, and it features:

  • A code model for the QuickBASIC programming language that can take a structured representation of a program and output it with canonical formatting.
  • A lexer and a parser that can read QuickBASIC code and build code models for it.
  • Automated testing of the lexer and parser.
  • A mostly-complete low-level emulator of the VGA chipset, the most popular graphics system for PCs in the late '80s.
  • Code Page 437 translation to match the emulated VGA fonts.
  • Keyboard handling that should do exactly what's needed to, eventually, be wired up to INKEY$, the QBASIC keyboard input function. This one is more involved that it looks on the surface. 🙂
  • Maybe 10% of the QuickBASIC IDE, showcased here.

The classic blue screen you see here is fully emulated. The VGA memory is, as in the real chipset, divided into 4 64KB planes. In the mode shown here, characters are read from plane 0 and attributes from plane 1, and within each character box, the font from which the actual dots are generated is read from plane 2. The emulation also supports CGA/EGA/VGA graphics modes, including the classic 640x480x16 and 320x200x256 VGA modes. The emulation is based on the actual VGA hardware registers and timing. The idea is that, eventually, when this thing can actually run programs, a program that directly accessed the VGA hardware should work in this too.

The incomplete IDE here doesn't yet know how to run code, but it does parse code to the abstract code model and format it back out. You can see keywords being capitalized and expressions being spaced out here. The formatting should be a very close match to actual QuickBASIC, if not identical to it. (For instance, if I run the well-known gaem NIBBLES.BAS through it, the output it produces is byte-for-byte identical to the input file.) The editing experience is also as close as I've been able to make it and, as I find mistakes, will get better. Microsoft's DOS TUIs had interesting mechanics around selection and the clipboard, and those are also shown here.

The menu doesn't actually do anything yet, but it is complete and fully behaves the way the menu should.

I feel really good about this project and the way it's coming along. And, it was really awesome, having done all the development so far on a Windows machine, to pull the code down on a Linux machine, type "dotnet run", and have it start up immediately on a completely different operating system and do exactly what it's supposed to do 🙂