Skip to navigation

Revs on the BBC Micro

Text tokens

How the game text in Revs is tokenised to save space

Memory is tight in the unexpanded BBC Micro, and Revs has a number of tricks up its sleeve to tackle this. One of the more interesting is the text token system, which takes all the text used in the game, and compresses it.

The compression system is relatively simple. Revs contains 55 text tokens, that are numbered from 0 to 54. Each of these tokens (apart from token 54, which is a special case) has an associated string of text, labelled token0 through token53 in the source code. This string contains not only the text for that token, but it can contain other tokens too, as well as arbitrary numbers of spaces. Given that most of the strings are printed in mode 7 and can contain teletext control codes, this system enables us to create some pretty sophisticated tokens with very few bytes.

Let's take a deeper look at how text tokenisation is implemented in Revs.

Text token strings
------------------

Each token's string is a sequence of bytes. They have the following meanings:

0-159Print character n (where n is ASCII or a teletext control code)
160-199Print n - 160 spaces (i.e. 0 to 39 spaces)
200-254Print token n - 200 (i.e. token 0 to 54)
255End of token

Let's look at a typical token definition to see how this works:

  .token30

   EQUB 31, 5, 24         \ Move text cursor to column 5, row 24

   EQUB 134               \ Set foreground colour to cyan alphanumeric

   EQUB 200 + 17          \ Print token 17 ("PRESS ")

   EQUS "SPACE BAR "      \ Print "SPACE BAR "

   EQUS "TO CONTINUE"     \ Print "TO CONTINUE"

   EQUB 255               \ End token

We can print this token by calling the PrintToken routine with X = 30. It is designed to be printed in screen mode 7, and is part of the menu system shown before the race starts. The first two parts write the standard VDU commands to move the text cursor to column 5, row 24, and then to set the foreground colour to cyan (see the BBC User Guide for details of how the mode 7 teletext control codes work).

Next up we have a byte in the token string that has a value in the range 200 to 254, which indicates we should print another token. In this case it's token 17, which just contains the string "PRESS ", but as all tokens can contain other tokens, it's possible to create tokens with pretty sophisticated recursive content (token 28, for example, clears the screen and prints an entire menu, along with header and prompt, all using embedded tokens).

Next, we have the text "SPACE BAR " and "TO CONTINUE", and finally we have a value of 255, which denotes the end of the token. So in all, token 30 prints "PRESS SPACE BAR TO CONTINUE" in cyan at column 5 on row 24.

Tokens 0 to 53 all work in this way, but token 54 doesn't have an associated text string. Instead, when printing token 54, the PrintToken routine calls the PrintHeader routine with X = 0, which prints out token 0 ("FORMULA 3 CHAMPIONSHIP") as a double-height header.

Note that not all tokens are designed for screen mode 7. Tokens 40-45, 48 and 53 are designed to be printed along the top of the driving screen, and are laid out at the exact width of the screen (though token 44 is wider, for some reason). However, as most of the game text is shown in mode 7 before or after the race, most of the tokens are built with teletext control codes in mind.

Text routines
-------------

Here's a list of all the related routines:

  • PrintToken prints the text token given in X. It looks up the address of the relevant token string in the (tokenHi tokenLo) table, and prints the token one byte at a time, either printing text with PrintCharacter (for bytes 0-159), printing spaces with PrintSpaces (for bytes 160-199), or recursively printing any embedded text tokens with PrintToken (for bytes 200-253) or PrintHeader (for byte 254).
  • PrintHeader prints a token as a double-height header, with the position and colours given in the header tables, and a specific number of spaces between the top and bottom parts of the double-height text (to ensure they line up). Seven tokens are supported (tokens 0 to 6). See the next section on configurable tokens for details of how this works.
  • PrintHeaderChecks prints a chequered line above and below the mode 7 header. This is only called once, at the start of the main game loop in MainLoop, just after PrintHeader is called with X = 4 to print "REVS" four times in multicoloured letters. The call to PrintHeaderChecks decorates the multicoloured REVS banner with two lines of checks.
  • PrintSpaces prints the specified number of spaces.
  • PrintCharacter either prints a character using the normal OSWRCH routine (when we are in mode 7), or pokes the character bitmap directly into screen memory (when we are in the custom screen mode).

Let's take a quick look now at the PrintHeader routine, before moving on to the tokens themselves.

Configurable text tokens
------------------------

Text tokens 31, 33 and 34 are special, in that they are configurable. They are configured by poking values directly into the strings at token31, token33 and token34.

The main example of this is PrintHeader, which looks up values from the following tables, depending on which header is being printed:

These determine the position, layout and colours of the double-height header that is printed, and the routine works by poking these values directly into tokens 33 and 34, and then printing token 33 to display the final result (which itself prints token 34, as token 33 contains token 34, twice).

Token 31 is also configured with colour information before it is printed, this time by direct poking into token31.

All the text tokens in Revs
---------------------------

The table below lists all the text tokens in Revs. The following syntax is used to denote each token's content:

  • (#) = change to the specified foreground colour, e.g. (cyan) = change to cyan text
  • (#/#) = change to the specified foreground/background colour, e.g. (yellow/red) = change to yellow on red
  • (/#) = change to the specified background colour, e.g. (/black) = change to a black background
  • [#] = print the specified embedded token, e.g. [54] = print token 54
  • (#, #) = move the cursor to specified screen location, e.g. (2, 10) = move to character column 2 on character row 10
  • ">" denotes the teletext right-arrow character
  • Tokens denoted as "race text" are only shown when driving
  • Tokens denoted as "menu" tokens (e.g. Menu [35-37]) denote menus that are only shown in mode 7, with the numbers denoting which tokens are used to show the menu options

The following syntax is used for tokens whose contents are set by modifying the token definitions before PrintToken is called:

  • {#/#} = configurable foreground/background colours
  • {#} = configurable embedded token
  • {# spaces} = configurable number of spaces

Click on the link in the first column to see the token definition in the source code.

#SummaryContents
0Text"FORMULA 3  CHAMPIONSHIP"
1Text"      POINTS      "
2Text"GRID POSITIONS"
3Text"ACCUMULATED POINTS"
4Text"REVS   REVS   REVS "

Each "REVS" is magenta/yellow/cyan/green [52]
5Text"THE  PITS"
6Text"  BEST LAP TIMES  "
7Text"Novice"
8Text"Amateur"
9Text"Professional"
10Text"SELECT "
11Text"ENTER "
12Text" DRIVER"
13Text" mins"
14Text" laps"
15Text" RACE"
16Text" > "
17Text"PRESS "
18Text" 5"
19Text"10"
20Text"20"
21F3 class menuClear screen

"FORMULA 3  CHAMPIONSHIP" header [54]

Menu [36-38]:

(flashing cyan) "PRESS "

"1  Novice"

"2  Amateur"

"3  Professional"

(2, 10) "     SELECT THE CLASS OF RACE"
22F3 duration menuClear screen

"FORMULA 3  CHAMPIONSHIP" header [54]

Menu [36-38]:

(flashing cyan) "PRESS "

"1   5 mins"

"2  10 mins"

"3  20 mins"

(2, 10) (cyan) "SELECT DURATION OF QUALIFYING LAPS"
23F3 driver nameClear screen

"FORMULA 3  CHAMPIONSHIP" header [54]

(2, 10) (cyan) "       ENTER NAME OF DRIVER"

(12, 17) (yellow) "------------"

(9, 16) (magenta) " > "
24Wing settings 1(2, 10) cyan "SELECT WING SETTINGS > range 0 to 40"

(14, 16) "rear  " (magenta) " > "
25Wing settings 2(13, 18) "front  " (magenta) " > "
26F3 race headerClear screen

"FORMULA 3  CHAMPIONSHIP" header [54]

(10, 12) (yellow) "STANDARD OF RACE"

(14, 14)
27F3 driver menuClear screen

"FORMULA 3  CHAMPIONSHIP" header [54]

Menu [36-37]:

"1  ENTER ANOTHER DRIVER"

"2  START RACE"
28F3 laps menuClear screen

"FORMULA 3  CHAMPIONSHIP" header [54]

(2, 10) (cyan) "      SELECT NUMBER OF LAPS"

Menu [36-38]:

(flashing cyan) "PRESS "

"1   5 laps"

"2  10 laps"

"3  20 laps"
29F3 driver promptClear screen

"FORMULA 3  CHAMPIONSHIP" header [54]

(2, 10) (cyan) "     " (green) " DRIVER > "
30Press space(5, 24) (cyan) "PRESS SPACE BAR TO CONTINUE"
31Configurable colours"  " (/black) {foreground/background}
32Spaces and backspaces"  " (/black) backspace backspace [31]
33Configurable header 1Clear screen

(4, 3) [34] {# spaces} [34]

(36, 2)
34Configurable header 2double-height {foreground/background} {token} "  " (/black)
35Cyan prompt(2, 10) (cyan)
36Menu option 1(4, 14) (flashing cyan) "PRESS "

(5, 16) (cyan/blue) "1  " (/black) "     " (yellow)
37Menu option 2(5, 18) (cyan/blue) "2  " (/black) "     " (yellow)
38Menu option 3(5, 20) (cyan/blue) "3  " (/black) "     " (yellow)
39Initial menuMenu [36-37]:

(flashing cyan) "PRESS "

"1  PRACTICE"

"2  COMPETITION"
40Race text"Lap Time   :         Best Time        "
41Race text"      Less than one minute to go      "
42Race text"           YOUR TIME IS UP!           "
43Race text"Position        In front:             "
44Race text"Laps to go        Behind:                  "
45Race text"                                      "
46Screen mode 7Screen mode 7

Disable cursor
47Unused-
48Race text"             PLEASE  WAIT             "
49Cursor to heading(9, 2)
50Configurable text for number of laps(24, 2) " 5 laps"
51Text" POINTS"
52Text(magenta) "R"

(yellow) "E"

(cyan) "V"

(green) "S"
53Race text"               FINISHED               "
54F3 headerCall PrintHeader with X = 0:

Clear screen

Double-height (yellow/red)

"FORMULA 3  CHAMPIONSHIP"