Skip to navigation

BBC Micro Elite

The split-screen mode

Elite's famous split-screen mode, dissected and explained in detail

References: IRQ1
Elite uses a unique split-screen mode that enables a high-resolution
black-and-white space view to coexist with a lower resolution, colour ship
dashboard. There are two parts to this screen mode: the custom mode, and the
split-screen aspect.

Elite's screen mode is a custom mode, based on mode 4 but with fewer pixels.
This mode is set up in elite-loader.asm by reprogramming the registers of the
6845 CRTC - see the section on VDU command data in that file for more
details, but the salient part is the screen size, which is 32 columns by 31
rows rather than the 40 x 32 of standard mode 4. Screen sizes are given in
terms of characters, which are 8 x 8 pixels, so this means Elite's custom
screen mode is 256 x 248 pixels, in monochrome.

The split-screen aspect is implemented using a timer. The timer is set when
the vertical sync occurs, which happens once every screen refresh. While the
screen is redrawn, the timer runs down, and it is set up to run out just as
the computer starts to redraw the dashboard section. When the timer hits zero
it generates an interrupt, which runs the code below to reprogram the Video
ULA to switch the number of colours per pixel from 2 (black and white) to 4,
so the dashboard can be shown in colour. The trick is setting up the timer so
that the interrupt happens at the right place during the screen refresh.

Looking at the code, you can see the SHEILA+&44 and &45 commands in LINSCN
start the 6522 System VIA T1 timer counting down from 14622 (the high byte is
57, the low byte is 30). The authors almost certainly arrived at this exact
figure by getting close and then tweaking the result, as the vertical sync
doesn't quite happen when you would expect, but here's how they would have
got an initial figure to start working from.

First, we need to know more about the screen structure and exactly where the
vertical sync occurs. Looking at the 6845 registers for screen mode 4, we get
the following:

  * The horizontal total register (R0) is set to 63, which means the total
    number of character columns is 64, the same as the default for mode 4
    (the number stored in R0 is the number of columns minus 1)

  * The vertical total register (R4) is set to 38, which means the total
    number of character rows is 39, the same as the default for mode 4 (the
    number stored in R4 is the number of rows minus 1)

  * The vertical displayed register (R6), which gives us the number of
    character rows, is set to 31 in elite-loader.asm, a change from the
    default value of 32 for mode 4

  * The vertical sync position register (R7) is 34, which again is the
    default for mode 4

For the countdown itself, we use the 6522 System VIA T1 timer, which ticks
away at 1 MHz, or 1 million times a second. Each screen row contains 64
characters, or 64 * 8 = 512 pixels, and in mode 4 pixels are written to the
screen at a rate of 1MHz, so that's 512 ticks of the timer per character row.

This means for every screen refresh, all 39 lines of it, the timer will tick
down from 39 * 512 = 19968 ticks. If we can work out how many ticks there are
between the vertical sync firing and the screen redraw reaching the
dashboard, we can use the T1 timer to switch the colour depth at the right
moment.

Register R7 determines the position of the vertical sync, and it's set to 34
for mode 4. In theory, this means that the vertical sync is fired when the
screen redraw hits row 34, though in practice the sync actually fires quite a
bit later, at around line 34.5.

It's probably easiest to visualise the screen layout in terms of rows, with
row 1 being the top of the screen:

  1     First row of space view
  .
  .     ... 24 rows of space view = 192 pixel rows ...
  .
  24    Last row of space view
  25    First row of dashboard
  .
  .     ... 7 rows of dashboard = 56 pixel rows ...
  .
  31    Last row of dashboard
  .
  .     ... vertical retrace period ...
  .
  34.5  Vertical sync fires
  .
  .     ... 4.5 rows between vertical sync and end of screen ...
  .
  39    Last row of screen

So starting at the vertical sync, we have 4.5 rows before the end of the
screen, and then 24 rows from the top of the screen down to the start of the
dashboard, so that's a total of 28.5 rows. So given that we have 512 ticks
per row, we get:

  28.5 * 512 = 14592

So if we started our timer from 14592 at the vertical sync and let it tick
down to zero, then it should get there just as we reach the dashboard.

However, because of the way the interrupt system works, this needs a little
tweaking, which is where the low byte of the timer comes in. In the code
below, the low byte is set to 30, to give a total timer count of 14622.

(Interestingly, in the loading screen in elite-loader.asm, the T1 timer for
the split screen has 56 in the high byte, and 50 in the low byte, so it counts
down from 14386 instead of 14622. As a result the screen does flicker quite a
bit more at the top of the dashboard, and there are quite a few red and yellow
pixels above the split, as it switches a bit too early. Perhaps the authors
didn't think it worth spending time perfecting the loader's split screen? Who
knows...)