Emulating a BBC Micro in Javascript - Part Two

Following on from my previous post, I’m going to talk a bit about emulating the video hardware of a BBC Micro. Firstly, a big credit to Tom Walker for his b-em emulator upon which much of the jsbeeb video code is based. Thanks Tom!

The video hardware

When most people think of a BBC Micro, they think of the iconic “Teletext” display, referred to as MODE 7. It was the default screen mode and had some great benefits: it had high-resolution, clear text, many colours, and some cool effects. The fact it only took up 1KB of memory was nice too, especially when that may be 1/16th of your whole RAM.

Picture of a typical MODE 7 display
MODE 7 in all its glory

MODE 7 was supplied by a separate chip from the rest of the screen modes, and although it’s very interesting and complex, it’s not what I’m going to talk about today. I’m going to talk about the rest of the video circuitry; the graphics modes provided by a Motorola 6845CRTC, in combination with a custom video ULA which provided timings and palette configuration.

The 6502 CPU communicated with the 6845 by writing to its memory mapped area at 0xfe00 to 0xfe07. The 6845 has 18 internal registers and to access them one would write the address number to 0xfe00 and then the value required to 0xfe01. (The other addresses in the range mapped to the same two functions).

The registers control things like where the television sync pulse would be generated, when and where horizontal lines would start and end, and what memory address the bitmapped screen was stored at. By judicious programming of the start of screen memory, games could perform hardware scrolling, albeit at a somewhat coarse level of 8 pixels in the X direction.

jsbeeb operates in a physical PAL TV space of 1280x768 pixels. This allows it to account for things like the vertical sync pulse being configurable. At the end of each frame, the area of the 1280x768 that actually contained pixels is then blitted to a 698x571 canvas element. The unusual size is to purposefully blur the pixels a little to simulate an authentic TV experience.

Some of the screen timings and registers
Some of the relationships between register values, courtesy of the Advanced User Guide

Every clock cycle (of the 2MHz clock), then 6845 processes one byte of screen memory, generating anywhere from two to eight logical pixels, depending on the number of colours per pixel and other register settings. In jsbeeb’s terms, each tick generates between eight and sixteen physical pixels in PAL space.

On each clock cycle, jsbeeb looks at all the hardware registers, determines which settings are in effect, and renders either 8 or 16 32-bpp pixels into its buffer. The video code runs every time the CPU emulator calls its polltime(), making it a kind of co-routine with the CPU (along with the other peripheral emulators). (Code is in video.js)

The reason the state is re-checked each cycle is because it was very common for games to alter the settings mid-frame by clever use of well-timed interrupts to, for example, scroll part of the screen, or change the palette of colours available halfway down the screen.

Checking each time is a little inefficient – as is ticking the screen code every time the CPU counter ticks. In future updates I plan on accumulating screen clock cycles and running the screen for longer batches if and only if I know for certain none of the registers have changed. At this point I can also consider better code arrangements to take advantage of rendering multiple screen pixels with the same settings.

The framebuffer object written to is a 32-bpp overlay on the 8-bpp underlying canvas’s framebuffer. This lets me write a single 32-bpp value to set a whole pixel. The mappings between the BBC’s colour format and the 32-bpp values required are all done when the palette registers are written to, instead of each time a colour is needed.

The code for a single screen byte dat looks something like:

var tblOff = dat * 16;
for (i = 0; i < charWidth; ++i) { // is 8 or 16
    fb32[cur + i] = ulapal[table4bpp[tblOff + i]];
}

The table4bpp is a table mapping the contributions of each display byte’s bits to the 8 or 16 physical pixels of a character (created here). It maps to a 4-bit value which indexes into the palette, ulapal.

Conceivably it might be worth regenerating the table each time the palette is modified rather than doubly-indexing into tables for every physical pixel: I haven’t done the profile so I can’t be sure. In reality the table is also indexed by some other of the video registers, but these also could be folded into the table regeneration. I’m intrigued now and will have to take a look!

In spite of the complexity, the performance of the screen code is suprisingly good. It does appear near the top of the profiles but upon further investigation a lot of the time is spent at the end of each frame in the blitting code that takes the area of the PAL physical screen that was rendered to and stretch blits it into the actual canvas object displayed on the web page. Blitting code is currently in the big blob of main.js. Suggestions on how to improve its speed welcomed!

Returning briefly to MODE 7 support; this rather complex mode is implemented only partially at the moment. Code is in teletext.js, and there’s a lot of allusions to interlacing that I don’t currently do. Tom Walker’s b-em is much better at this part and it’s one of the reasons his emulator’s MODE 7 display looks so much nicer: emulating the TV’s higher-resolution interlacing makes everything smoother. I’m hoping to find time to return to this soon.

Filed under: Coding Emulation
Posted at 12:10:00 BST on 16th May 2014.

About Matt Godbolt

Matt Godbolt is a C++ developer working in Chicago for Aquatic. Follow him on Mastodon.