Skip to main content
« Dr Simon Price

Simon Price - Computer Games Programmer (reformed)

games magazine awards

Welcome to a fun bit of memorabilia from my former life as a computer games developer during the early days of home computing. I worked as a contract developer for Melbourne House, Mastertronic, Virgin Games and Electronics Arts from 1984 through to 1992 and then, as my dad likes to say, I got a proper job.

old computers

Eight Years in Computer Games

Having spent my Computer Science honours degree industrial year doing research into adventure games at the Army Personnel Research Establishment in Farnborough, I decided that this was the life for me. So, in 1985 when I graduated from North Staffordshire Polytechnic, now Staffordshire University, I set up a computer games company called Silhouette Software in partnership with fellow graduate Mike Lewis. Together with graphic artist Carl Cropley and the occasional musician, we wrote several 8-bit games, a book (pdf) about writing games and once, to fill a spare week, a role playing game scenario. After a few years Mike moved up to London to get closer to the city buzz and I moved down to the West Country to get further away from the city buzz. More time passed, more 8-bit games for the Commodore 64, Spectrum and Amstrad and, eventually, a move to 16-bit games for the sexy Commodore Amiga, the clunky Atari ST, the dark age CGA/EGA/VGA generation IBM PCs and the USA's hideously limiting Tandy PC.

Running a small software company was great fun. Our games won a few awards, we made a comfortable living and met some amazingly talented (if sometimes eccentric) people. However, eventually I realised that I was far more interested in the authoring tools and working with users than I was in the games themselves. So, in 1992 I took a one-year contract as a senior programmer at the University of Bristol to lead technical development of the WinEcon introductory economics e-Learning project and enjoyed it so much that I never left.

Gameography

screen shots and game covers

The various games that I developed or co-developed are listed below.

Technology

Most of our games were written entirely in assembly language (6502 for BBC and C64, Z80 for Spectrum and Amstrad CPC, 8086 for the PC and Tandy, 68000 for ST and Amiga). The exceptions were Demon's Tomb and Catalina which were both a mix of C and assembly, and the first Adventure Kernel System which was written in LISP. Later on this page you will find further details of the Adventure Kernel System and an answer to the modern student's question, "What does assembly language look like?".

The earlier games were written on the target machines with pretty minimalist development tools. The later ones used cross-platform development hosted by the BBC Micro or the IBM PC. Where available, we also used connecting electronics akin to in-circuit emulators to debug remotely.

Commodore 64 and/or Spectrum emulator versions of our games can be found around the web but Demon's Tomb (below) can still be run natively on a Windows PC.

Demon's Tomb

Demon's Tomb, published in 1989 for the PC, Tandy, Atari ST and Commodore Amiga, was a traditional text adventure with a parser that surpassed the Infocom and Level 9 ones and, I like to think, with an original twist on the genre.

Demon's Tomb box front

The game was developed in a custom-written authoring language that compiled to a machine independent pseudo code that in turn ran under a custom-written virtual machine. The authoring tools were PC-based and only the virtual machine had to be developed in order to port the game to new platforms. This had obvious advantages in terms of not having to significantly recode or retest for each new platform. Most of the programming was in C with speed critical parts, such as graphics rendering and text decompression, being written in the assembly language of the target machine. Space was a major consideration and completely dominated the architecture, with the entire game having to fit in the 128K of the Tandy and a distribution constraint of 720K disk space for graphics.

The project took 18 months and I enjoyed every moment of it. I was, and remain, very happy with the parser but graphically the game suffered from having to share the same 8-colour PC/Tandy graphics across all target machines due to budgetary constraints. Also, I was disappointed that the publisher's producer insisted on spelling out the opening plot like a bad voice over to make it easier. Even so, Video and Computer Games awarded it a "Golden Scroll" and a review score of 92%, which was pretty good for what is essentiall a text-only game in an arcade games magazine.

What is an adventure game?

An adventure game is an interactive story in which you control the main character. Each turn the game gives you a description of your character's current situation and you respond by typing in what you want your character to do next. If the game understands your input, the character will attempt to follow your command and the consequences of his action will be reported; otherwise, the game displays a message saying what it doesn't understand and waits for you to enter another command. As an example of this interaction consider the following dialogue with a traditional adventure game. Lines starting ">" are player input.

You are standing at the end of a road before a small, brick building. Around you is forest. A small stream flows out of the building and down a gully.
> enter the building
You are inside a building. A wellhouse for a large spring. You can see some keys, a shiny brass lamp, some food and a bottle.
> take the keys
Okay.
> take the lap
I don't know the word "lap".
> take the lamp
Okay.
> rub the lamp
Rubbing an electric lamp is not particularly rewarding.
Anyway, nothing happens.
> leave
You are at the end of the road again.
> go south
You are in a valley in the forest beside a stream tumbling along a rocky bed.
> go south
You are in a 20-ft. depression floored with bare dirt. Set into the dirt is a strong steel grate mounted in concrete. The grate is locked.
> unlock the grate with the keys
The grate is now unlocked.

As you play an adventure game you will encounter problems which will need to be solved using the available objects and your own native cunning and intellect. Unlocking the grate in the above example was fairly straight forward. A different adventure game plot might not provide such an obvious way of opening the grate; you might need to oil the hinges or pick the lock with a piece of wire, or even open the grate with a crowbar. Alternatively, examining the grate may reveal that it is welded shut; in which case dynamite might prove more effective! Solving the problem of opening the grate may require you to solve other puzzles first; you may need to get the dynamite out of a locked box, stop it getting wet in the rain and then find some way to light the fuse.

Adventure Kernel System

The adventure game plot and interactions for Demon's Tomb were written in a bespoke domain-specific language for my own Adventure Kernel System. I wrote a prototype of AKS in LISP during my undergraduate year in industry and a minimalist sketch of a second version in Basic for a book published by Melbourne House. The work was inspired by articles published by the MIT-based authors of Zork and founders of Infocom - in particular Lebling et al., "Zork: A Computerized Fantasy Simulation Game", IEEE Computer, pp.51-59, 12(4), April 1979. pdf). The system design and implementation also benefited greatly from having spent an entire year researching the genre and unpicking all the leading games of the day while working as a research assistant for the MOD in 1983-4.

Below is an extract from the in-house documentation of AKS. It describes part of the vocabulary definition file and rules for matching verb patterns and supplying default objects for incomplete sentences.

Let's start with a simple example of a verb definition:

	open
	OPEN = .O:visible(x)
	OPEN = .O:visible(x), WITH, .O:visible(x)

This defines a single verb, OPEN, with two different <verb_format>s. Respectively these are:

	<search_pattern>
	<search_pattern>, <preposition>, <search_pattern>

Or, in more grammatical terminology:

	direct-object
	direct-object, preposition, indirect-object

where "object" refers to the object of the sentence not to a game object. The direct and indirect objects can be location, actor, object, direction, quoted string, number, time or filename.

Examples of sentences with this format are:

	open door
	open door with key

Remember that the parser takes care of noise words like "the" and so you don't need to bother with them in your game definition.

Now, somewhere in the VERBS.STR file you must have a routine called OPEN which will handle any input the player enters which uses the word "open". To keep things as simple as possible that routine does not have to ensure that the player didn't enter incomplete sentences like, "open" or "open door with" because the parser will use the <search_pattern> to complete the sentence. Of course in Menu Input Mode the player can only select things from a menu produced by the <search_pattern> so his input is known to be complete!

Don't confuse <search_pattern>s with the validation of what the player actually typed in from Typed Text Mode. Just because you specified a search pattern of ".O:visible(x)" doesn't mean that the player can't type in "open box" when the box isn't visible. It is up to the OPEN routine in VERBS.STR to check that the box is visible. The search pattern is only there to make menus and defaults reasonably sensible NOT to validate anything the player types in.

The other use of these <search_pattern>s is to enable the parser to decide what you mean by "all". If you type "open all" the parser will use the <search_pattern> to build a list of things. You may want "open all" to only try and open objects which have the 'haslid' property set (see "/object/" section) and which are not not already open. The direct-object search pattern would therefore look like this:

	!O:visible(x).haslid(x).~open(x)

Notice the '!' which allows plurals. On the other hand you might decide that this makes the game too easy because the game will be virtually giving away some of your puzzles. Changing the '!' for a '.' would stop "open all" from working but if played from Menu Mode the player will still get a menu of all the openable items. This is why many verbs use a very broad search expression like ".O:visible(x)". There is a fine balance between making the player do tedious tasks and making the tasks too easy.

The final point to notice from the OPEN example is that there is more than one form of OPEN so the parser can not complete a sentence as short as "Open." because it does not know which form to use. However, "Open with." would enable the parser to prompt for first a direct-object and then an indirect object. The rule to derive from this is that the parser can only complete a sentence when it can uniquely identify the form of the verb.

The final feature of verb definitions is perhaps one of AKS's strongest language features. It allows a single word to have more than meaning depending on its context. Here is a definition which illustrates this feature:

	get,take
	STAND = UP
	BOOGIE = DOWN
	TAKE = !AO:reachable(x).~carried(x)
	TAKEFROM = .AO:reachable(x).~carried(x), FROM, ?A:visible(x)

This defines four different verbs which would respectively correspond to sentences like:

	get up
	get down
	get book
	get book from sam

Of course "take" is synonymous with "get" although the parser would say "Get book from who?" rather than "Take book from who?" in response to the incomplete sentence "Take book from.". This is because "get" is the first word on the vocabulary definition line. Identical synonym rules apply to the preposition FROM; the player can use any of the synonyms of "from" as defined in the "/preposition/" section.

Synonyms aside, the real multiple meanings are the ways in which the verb actually resolves to one of four forms: STAND, BOOGIE, TAKE and TAKEFROM. Each of these has a separate routine in the VERBS.STR file.

You could then go to define further verbs which would use different words to get to the same meanings (you could substitute the word "identifier" for "meaning"). For example:

	dance
	BOOGIE

	stand
	STAND
	STAND = UP

These define further ways in which the player may say the equivalent of "get down" and "get up". These are "dance" and "stand" or "stand up" respectively.

The vocabulary definition file only specifies the first stage in responding to user input and has to be accompanied by procedural definitions of the verbs that operate on objects in the game world but, hopefully, it gives a glimpse into the workings of AKS.

AKS was mainly written in C augmented with a small amount of assembly language for speed critical functions like text decompression and the graphics library. Although C is (correctly) considered to be low-level language, compared to assembly language, which we were forced to use for most of our coding, it is positively high-level. The following section aims to give a sense as to why this is the case.

What does assembly language look like?

To give a flavour of the amount of work required to do the simplest things in assembly language, below are two subroutines to read the colour of a pixel from the screen on a PC (in 80x86) and an Amiga (in 68000). Such direct access to the hardware is reserved for the operating system these days, but when writing games we often chose to switch off the operating system and use our own low-level interrupt, keyboard, joystick, mouse, disk, sound, and display handling to improve performance and increase the available working memory.

Intel 80x86 Assembly (PC)Motorola 68000 Assembly (Amiga)
PROC  _host_readpixel
;
; short host_readpixel(Icon *icon, short x,y)
;
;   returns colour no. of pixel at x,y 
;   or -1 if bounds error or bad icon
;
  ARG arg_icon:PTR Icon,arg_x:WORD,arg_y:WORD
  entry
  ;
  lds si,[arg_icon]
  mov ax,ds
  or  ax,si
  je  @@fail
  ;
  mov ax,[arg_y]
  cmp ax,[(Icon ds:si).height]
  jae @@fail
  ;
  mov bx,[arg_x]
  cmp bx,[(Icon ds:si).width]
  jae @@fail
  ;
  ;y component = y * bytesperrow
  mov dl,[(Icon ds:si).bytesperrow]
  xor dh,dh
  mul dx
  ;x component = x div 8
  mov cl,bl     ;keep copy of arg_x lo
  shr bx,1
  shr bx,1
  shr bx,1
  add bx,ax     ;merge x and y components
  ;prepare byte mask
  and cl,7
  mov ah,80h
  shr ah,cl
  ;for each plane
  mov cl,[(Icon ds:si).planes]
  xor ch,ch
  xor al,al ;build colour info in al
  mov dl,01h
@@nxtplane:
  les di,[(Icon ds:si).image]   ;get plane bitmap adrs
  add si,4        ;fudge icon to next plane ptr
  add di,bx       ;add offset
  ;is pixel set?
  test  [BYTE es:di],ah
  jz  @@notset
  or      al,dl
@@notset:
  shl dl,1
  loop  @@nxtplane
  ;
  xor ah,ah
@@done:
  ;return colour no. in ax
  exit

@@fail:
  mov ax,0FFFFh
  ;return -1 if failed
  jmp @@done
ENDP  _host_readpixel
    
_host_readpixel
;
; short host_readpixel(Icon *icon, short x,y);
;
;   returns colour no. of pixel at x,y 
;   or -1 if bounds error or bad icon
;
  move.l  4(sp),a0
  move.l  8(sp),d0
  move.l  12(sp),d1
  movem.l LC_REGS,-(sp)
  move.l  a0,a4
  move.l  a0,d2
  beq   .badicon
  ;bound check x,y
  cmp.w ic_width(a4),d0
  beq   .badicon
  bhi   .badicon
  cmp.w ic_height(a4),d1
  beq   .badicon
  bhi   .badicon
  ;bytesperrow = ((icon->width + 15) >> 4) << 1
  moveq #0,d2
  move.w  ic_width(a4),d2
  add.w #15,d2
  lsr.w #4,d2
  asl.w #1,d2
  ;planesize = icon->height * bytesperrow
  move.l  d2,d3
  mulu  ic_height(a4),d3
  ;calc adrs of plane#0 byte
  move.l  ic_image(a4),a0
  mulu  d2,d1       ;y * bytesperrow
  add.l d1,a0
  move.l  d0,d1
  lsr.w #3,d1       ;x div 8
  add.l d1,a0
  ;pixelnum = x mod 8
  and.w #7,d0
 eor.b  #7,d0
  ;take a pixel from each plane to build colour byte
  moveq #0,d1
  moveq #0,d4
  move.b  ic_planes(a4),d4
  subq.w  #1,d4 ;for dbf loop
.nxtplane
  moveq #0,d5
  btst.b  d0,(a0)
  beq.s .wasclr
  not.b d5
.wasclr
  lsr.b d5
  roxr.b  d1
  ;advance to next plane
  add.l d3,a0
  dbf   d4,.nxtplane
  ;rgt align the bit sequence
  move.w  d1,d0
  moveq #8,d1
  sub.b ic_planes(a4),d1
  lsr.b d1,d0
  movem.l (sp)+,LC_REGS
  rts
  ;
.badicon
  move.l  #-1,d0
  movem.l (sp)+,LC_REGS
  rts
    

Reflection

I count myself lucky to have been an undergraduate student of Computing Science between 1981-1985 at the height of one of the most exciting periods in computing: home microcomputers, colour graphics, sound, joysticks, mice, PC, Apple Mac, Inmos Transputer (Bristol based), the Fifth Generation project, Prolog (Bristol again), Unix and C, and, of course, the birth of the computer games industry. Although I have long since left the games industry I frequently bump into other reformed games programmers around the world - most recently in Silicon Valley at the IEEE Big Data Conference. This leads me to suspect that, apart from the more obvious legacy of those times - a booming UK-based games industry, a booming UK CG/animation industry (Bristol again), degrees in computer games development, etc., there remains a more subtle legacy in the form of people with "a very particular set of skills", fellow alumni, from the (now) retro games industry.

Dedication

This page is dedicated to the memory of our good friends Kevin Oliver, the namesake of Redhawk's alter ego, and Carl Cropley, our graphic artist.