Blog

Creating a Custom Scripting Language For In-Game Events

pdp-10_1090
DEC PDP-10 mainframe

Let’s start by going back to the late 1970s, shortly after Chuck Jones himself was frozen. In 1977 a bunch of guys at MIT were working on the groundbreaking text adventure game ZORK. ZORK was written originally for the big mainframe computers at MIT such as the DEC PDP-10. Eventually these guys decided to form a company called Infocom in order to sell their games, but they had one big problem: There were very few mainframe computers in the world and the people who owned them were generally not interested in buying Video Games. If Infocom wanted to sell games, they would have to make them run on the small personal computers that were entering the consumer market at the time. However this presented an interesting challenge: the mainframe version of ZORK required 1MB of memory (1 MB = 1024 KB) but the personal computers of the day often had less than 32KB of memory, so how on earth were the going to get the giant world of ZORK on a personal computer!? The answer: A custom scripting language! The guys at Infocom were some seriously smart dudes and realized that most of the core logic of the game could be broken down into a series of game specific instructions such as move object to room, write message, etc. each one of these in game instructions could correspond to tens or even hundreds of machine code instructions making the game tiny. They essentially created a Virtual Machine, which they called the Z-Machine which was highly specialized for playing text adventures. The program to execute the Z-Machine instructions could be very small, and it being the only part of the game that was specific the computer it was running on, made conversions of the game to other systems a breeze. Because writing Z-Machine instructions by hand had many of the drawbacks of assembly code, Infocom also created a compiler for a Z-Machine high level language which made it easy to implement new games.

zork

Now what does ZORK have to do with Chuck Jones? Well everything actually. In Chuck Jones: Space Cop of the Future, I use the exact same approach. I created a Virtual Machine which executes byte-code that is specifically designed to play graphical adventure games and in doing so I save time, memory and disk space. I call this somewhat unimaginatively: The Chuck-Machine! On top of this I built a high level but specialized language for controlling what happens in the game. Well at this point, if you’re brave enough, we’d better dive into the implementation details.

Chuck-Machine

The above diagram gives a simplified overview of how my interpreter is structured, we have several processes each with their own set of registers which are cooperatively juggled on the single CPU that the game is running on. Additionally the interpreter maintains a global list of objects, rooms and sounds that are dealt with by the scripts as well as provides 128 global registers for inter-process communication. Now lets move on to the language itself.

Chuckscript

Here’s a screenshot of me editing one of the scripts in the wonderful Notepad++ editor. The language is very much like a structured BASIC but with quite a bit of influence from C and C++. Because of the design of the underlying Chuck-Machine the language manages to compile to usually one or two byte-code instructions per line (with the obvious exception of macros and procedure calls), this is because complex operations can be built into the Chuck-Machine itself as special instructions.

basm.png

Once a script is written it is compiled using the somewhat confusingly named BASM tool (the Bytecode ASseMbler), because this tool is actually a compiler and not an assembler. BASM is written in C++, when I wrote BASM I was moving really quickly and as such it’s pretty inelegant and is a bit of a hack. Despite this it works.

In summary creating a custom language doesn’t have to be that complicated and you can save yourself a lot of time and memory if you do it right.

 

What I’ve been working on

While I intended to post the second part of my article about VGA programming I regret to inform you that I haven’t finished writing it as its not the easiest thing to explain. Don’t fret however as it’s on the way. Instead I have decided to give an update on the stuff that I’ve been working on instead of writing that article.

In addition to working on art, designing puzzles and composing music, which occupies the bulk of my development time as I do all of those tasks myself, I have:

Overhauled the music system

adlib_sound_card_version_1-5

Original Adlib card

Before this, the game played MIDI music and only supported the original Adlib music card and it’s Yamaha OPL2 sound chip, and the game engine mapped a single midi channel to each of the Adlib’s six melodic voices (the other 3 of 9 were taken up by the drums). The problem with this is that it limits all the music to six instruments and none of those could play chords.

This has been replaced with a more MIDI centric system where each of the 16 MIDI channels are mapped on the fly to a free voice on the Adlib card, replacing the oldest note if a free channel is not available. This allows the system to play chords and juggle 16 channels pretty well among a smaller number hardware voices. An interesting result of this is that now if I give the playback engine more voices, say 18, I could make more complex music sound even richer. This is exactly what happens with the OPL3 chip, used on the SoundBlaster Pro 2, SoundBlaster 16, Adlib Gold etc. The OPL3 chip supports stereo music, 18 hardware voices and more complex timbres.

Additionally I have added support for the much loved Roland MT-32 synthesizer/MPU-401, This works by just pumping all the MIDI events through the MPU401 card and is slightly more efficient than the voice juggling that happens with the OPL cards, this is offloaded to the external MIDI module.

Of course if you’re using a modern system don’t worry if you don’t have this hardware as it will be emulated by DOSBox which will be bundled with the Windows, Mac and Linux versions of the game. If you are using a vintage system don’t have any of this hardware I am working on adding PC speaker support 😉

chuck_019

Felix McKnight: lead singer/guitar player of the funk band Felix McKnight and the SoundBlasters

Made save game actually work

Anyone who has had to implement this feature probably understands that It’s actually a miracle that you can save your game and pick up exactly where you left off. There’s just so much in a game that you need to find and stick into a file and then make sure you can actually load that data back in at any point without crashing the game. As you might imagine many games cheat at this and don’t offer a perfect load/save. As I don’t use save states as a game mechanic (like a Sierra game were you need to save your game every 5 seconds because Roberta Williams is trying to kill you) it’s probably not that important but I really wanted to get it right because it makes testing easier.

Added an inventory view

Up till now Chuck (and every other character) had an inventory and the game would keep track of it but you the player could not look at it or use items in it. If Chuck had an item that he needed for a puzzle he would automatically use it. Now you can press the “i” key at any time to see the items you are carrying and interact with them, each inventory item having a unique icon. This allows for some more interesting puzzles as well as encouraging players to pick things up.

Optimized sprites and lower Memory usage

Explaining how I did this is probably too long for this post but the memory usage was basically improved using better data structures and paging things in and out from disk but in such a way that it actually helps to allow the game to run off floppies by optimizing for locality to minimize disk swapping (disk swaps are most likely to occur when travelling between planets and between acts of the game).

Generally the engine has become far less buggy, more optimized and the scripting language has become more powerful as needed for implementing certain puzzles. I always make sure that as I add new content that I also keep disk usage down. When I ship this game in a box I don’t want it to be on a ridiculous number of floppies.

If anyone has any questions about anything regarding this game (except spoilers) I’d love to answer them.

Lets talk VGA, Part 1

When I say VGA, most people these days will think about that funny blue connector that used to be so common for connecting computers to display devices.

1200px-vga-cable
That thing on the back of my monitor next to the HDMI port?

Why would I wanna talk about that you might wonder? Well I won’t really be talking about that. What I’m talking about is the Video Graphics Array, a graphics card standard introduced in 1987 by IBM, the connector pictured above was only one part of that standard. VGA was important, really important. Before VGA, PCs could only display 16 colours on screen at a time and most of the time programmers and artists had no choice what those colours were. With VGA you could have 256 colours on screen at the same time! Not only that but you could also pick those colours from a massive set of over 200 thousand colours, this was truly groundbreaking. Since the a VGA card would have been the most common type of thing to see in PCs in the era Chuck Jones targets (around 1992), this is what I have chosen to support. Lets get down to the nitty gritty then shall we?

VGA Graphics Modes

For the uninitiated graphics programming for DOS can seem quite daunting, there’s no API, you just write data directly to the address of the video memory as if its just one big array. The mode you choose determines how that data is interpreted. Most games that used VGA used its 320×200 pixel 256 colour mode (mode 13h). Mode 13h is really fast and dead easy to program, one byte is one pixel and the next pixel is at the address of that pixel plus one. You might wonder then why Chuck Jones doesn’t use it, in fact it doesn’t use any documented mode at all. Chuck Jones uses what is commonly known a Mode X, popularized by the genius programmer and talented writer Michael Abrash (he describes how to use it here).

chuck_010
The Planet Orlokis in glorius 320×240

I don’t use mode 13h because it has a couple drawbacks, firstly the pixel aren’t square so drawing art using a modern image editor would be tough. The second thing is that the VGA card has a minimum 256k of ram but 13h only gives you 64k of that (just enough for 320×200 pixels) meaning that the pixels you write to are always the same ones that are being displayed so the user is gonna see terrible tearing all the time, unless of course you manage to make all your changes to the screen during the vertical retrace (the time it takes for an electron beam in a CRT to go back to the top at the end of a frame, retained in LCDs for compatibility). In practice most games managed this alright but there was no way to guarantee that tearing would not occur. Mode X allows a technique called page flipping where you can write the next frame to a part of the video memory that is off screen and then point the graphics card to that at the end of the current frame. Mode X also gives a resolution of 320×240 which is slightly higher and has square pixels.

Mode X isn’t perfect however, to understand why you’ll need to understand why mode 13h only gives you access to 64k of video memory. The VGA card organizes it’s memory 256k of memory into 4 64k planes, each pixel on the plane having a 16bit address (216 = 65536 = 64k). In order for mode X to give you 320×240 it actually has to put the screen across multiple planes, meaning that the next pixel isn’t the next byte in memory, its actually on the next plane! This complicates things more than a little bit. You actually need to not just write to the pixel you want (actually pixel address / 4) but actually select which planes you want to write to. Notice how I said planes and not plane? Well the interesting thing is that you can actually write to multiple planes at the same time. I’ll talk more about this and how this has some interesting performance characteristics when I talk about my code in Part 2.