October/November 1995

GAME DEVELOPER MAGAZINE GAME PLAN GGAMEAEM

Editor Larry O’Brien [email protected] The Sound of Jerry Senior Editor Nicole Freeman [email protected] Managing Editor Nicole Claro [email protected] erry Lewis breaks character: “Lis- similar levels of competence. Editorial Assistant Deborah Sommers ten...lay-DEEE!” It’s not much, Damn Yankees! succeeds in plastering [email protected] just a single word delivered in a goofy grin on your face for two-and-a- Contributing Editors Alex Dunne that characteristic way, but the half hours not through any one thing [email protected] audience goes nuts. Everyone on (although the presence of the King of Barbara Hanscome stage freezes for probably a good Comedy is obviously what draws a full [email protected] 15 seconds while the Marquis theater every night), but through a balance Chris Hecker [email protected] Theatre roars with approval. The of elements that are a lesson to anyone in David Sieks Jseptuagenarian has let the audience know digital entertainment. The play itself is [email protected] that tonight it’s not going to be the saintly, fluff, even by the standards of 50s musi- Editor-at-Large Alexander Antoniades Brylcreem-haired, (ex-) chain-smoking, cals. The book is fine but not particularly [email protected] dear-dear-friend-of-Liza, nominated-for- familiar (the only song you’re likely to rec- Cover Photography Charles Ingram Photography a-Nobel-Peace-Prize, hyper-earnest shill ognize is “[You gotta’ have] Heart.”). with the 21-hour Labor Day patter. Indeed, it’s the very modesty of the play, Tonight, it’s going to be the zany, wacky coupled with professional delivery, that Publisher Veronica Costanza goofball with the (once-) rubber skeleton, makes it so enjoyable. Group Director Regina Starr Ridley the Geisha Boy, the Nutty Professor, the You don’t need state-of-the-art spec- Jim-Carrey-Eat-Your-Heart-Out daffy tacle to entertain. Indeed, state-of-the-art Advertising Sales Staff laughmeister. Tonight, we know, there will spectacle can easily overwhelm a modest West/Southwest be shtick. entertainment. Although I know I’m in the Yvonne Labat (415) 905-2353 Jerry Lewis is playing Applegate (aka minority, I’ve always felt that Doom was [email protected] The Devil) in the Broadway revival of inferior to Castle Wolfenstein 3-D, its New England/Midwest Damn Yankees!, and the audience is filled technically limited predecessor. That’s one Kristin Morgan (212) 626-2498 with people with the good sense to turn up reason I was doubly delighted when the [email protected] their noses at Beauty and The Beast, Sunset programming wizards at id made the Marketing Manager Susan McDonald Boulevard, Cats!, and half a dozen other source code for Wolfenstein available for Advertising Production Coordinator Denise Temple spectacles for which stage effects and the public perusal (you can download it from Director of Production Andrew A. Mickus sheer shock of live orchestral music played our ftp site or CompuServe forum; the file- Vice President/Circulation Jerry M. Okabe competently stand in for...well, what? The name is WOLF3DSC.ZIP). Group Circulation Director Gina Oh Damn Yankees! sets are spare, filled with What you need is the enthusiasm and Group Circulation Manager Kathy Henry pastels, forced-perspective, and dressed courage to make the game you want to Circulation Manager Mike Poplardo with what look like authentic chrome-and- make. A little talent helps, but it’s not as Newsstand Manager Pam Santoro bakelite radios, toasters, and card tables. important as heart. When others say you Reprints Stella Valdez (415) 655-4269 There’s some of the stage magic demanded can’t win, that’s when the grin should start. of a Broadway show nowadays, with sets You gotta have heart... Oops. There I go Chairman of the Board rolling on and off silently, descending from with that damnably hummable tune from Graham J.S. Wilson President/CEO the catwalks, popping up through trap- the show again. ■ Marshall W. Freeman Executive Vice President/COO doors, a shim or two, and so forth, but you Larry O’Brien Thomas L. Kemp Senior Vice President/CFO get the feeling it’s the bare minimum Editor Warren “Andy” Ambrose Senior Vice Presidents required by the unions. The cast is fabulous David Nussbaum, H. Verne both in form (when Charlotte d’Amboise, ere is the final verdict on accessing Packer, Donald A. Pazour, Wini D. Ragus Vice President/Production playing Lola, first appears, I do the whole HGame Developer code. Go to our ftp Andrew A. Mickus Vice President/Circulation Tex Avery thing: my eyes pop three feet, site at ftp://ftp.mfi.com/gamedev/ Jerry Okabe Vice President/Software Development Division smoke shoots from my ears, and I involun- pub/src. If you’re using Compuserve, Regina tarily scream “Hooga-hooga!”) and func- GO SDFORUM. That’s it. We Starr Ridley tion, but I’m sure the various Andrew promise. Miller Freeman Lloyd Webber spectaculars offer at-least- A United News & Media publication

2 GAME DEVELOPER • OCTOBER/NOVEMBER 1995 CROSSFIRE

A 3D Benchmark for the Industry

Alex Dunne ecently, I began receiving a or trademark that would indicate the steady stream of e-mail from level of hardware required for different Ken Nicholson of ATI games. Creating a method to Technologies. Ken’s an Although the ideas are still being active member of the bantered around, I imagine the rating GamePC Consortium, a system would be two or three tiered, group of close to 100 mem- each tier representing a computer with game play madness ber companies, each of progressively more power and capabili- whichR has a hand in the game develop- ty. Using this rating system on game ment arena, including title developers, boxes would instantly indicate to con- tool vendors, system manufacturers, sumers the type of system required to chip, and board manufacturers. The enjoy the game. A rating of this sort may help consumers stated goal of the consortium is a sim- necessitates the hardware manufactur- ple and noble one: to raise the level of ers getting involved because it’s their game play on IBM PCs and compati- products that will be rated. Many bles. companies have jumped on board decide what games The e-mail that Ken sent consist- already. ed of discussion threads about two The hurdles to developing a related issues that are long overdue for three-dimensional benchmark, as you addressing: a three-dimensional might surmise, are fairly large. Bench- will work best on their graphics benchmark and a hardware marks are notoriously hard to devise specification for PC game play. and maintain, even when you don’t The intent of both issues is to have to reach a consensus across the provide consumers with a way to look industry. The idea of using systems. Development at a game on the store shelf and tell Microsoft’s Funstones benchmark whether the title will run adequately (which isn’t available yet) or asking an on their system. It’s far from an easy independent organization such as Ziff task to get an entire industry (or even a (the folks at PC Week Labs) to develop of this industry-wide portion!) to agree upon sticky issues a benchmark been discussed. like these, but if a middle ground is Although either of these scenarios reached, it will be a huge leap forward may come true, right now the three- for consumers and developers alike. dimensional benchmark development This month, I’m going to focus is in the hands of the industry–of standard won‘t be on the consortium’s proposal for an which you are a part. industry-wide three-dimensional The discussions by consortium benchmark that would serve as one of members about the benchmark have the components within their second been very insightful. Although this easy–will you be aim, the GamePC hardware specifica- article represents an excerpt of the dis- tion. Like the Multimedia PC Work- cussion, it gives you an idea of the ing Group’s MPC specification for problems that have to be solved before multimedia hardware, the GamePC devising a standard three-dimensional involved? Consortium wants to create a symbol benchmark.

GAME DEVELOPER • OCTOBER/NOVEMBER 1995 5 CROSSFIRE

Define What’s around in my head: “What are you a very difficult thing to account for in Being Tested testing: the 3D API, the chip, the benchmarking.” It’s a problem that I First, Rob Glidden of SoftPress cut board, the host processor, the device certainly don’t see any easy way right to the question that was rolling driver, or a 3D application? All these around, short of appointing someone are in the pipeline for getting a 3D or a group to subjectively judge the WHO’S INVOLVED? image to the screen. You need a way to quality of rendered images. And that’s isolate out the relative performance opening a whole new can of worms. contribution of one aspect. To do this, f you are interested in finding out you need a baseline and a flexible Coping with the more about the GamePC Consor- enough implementation so that no one Three-Dimensional API tium or want membership infor- is left out.” To account for the effect of the three- mation, the group can be reached Glidden proposed finding one or dimensional API on hardware tests, Iat: more games that run on a number of there were a number of suggestions: different chip sets and using the games’ • Everyone could agree to standardize • http://www.mmwire.com/gamepc/ frame rates on each configuration as an on one API for the testing. Dan gpchome.html indicator of performance. He then Wood as well as Lou Long of Oki • CompuServe: GO GAMEPC explained the drawback: every chip Advanced Products suggested using • or contact Ken Nicholson of ATI maker would have to make sure that Microsoft’s 3D DDI or Intel’s 3DR. Technologies at 75300.2772@com- puserve.com. those particular games ran well on their However, this may have to wait chip sets. There’s also the problem of until the day when either of these Here’s an sampling of GamePC Consor- finding the proper game to exercise all APIs is in widespread use. An inter- tium member companies: facets of the hardware within a short im solution would have to be found enough time without having to jump to until that day comes, if ever. GAME DEVELOPERS: different scenes, levels, or whatever to • The group could optimize the Acclaim get the right mix of animations. benchmark for each API. However, Accolade Glidden also submitted ideas for that situation would be quite a chore Broderbund measuring polygons per second, pixels for the consortium (which is young Interplay per second, and frames per second. and probably not too flush with Lucas Arts Each, he explained, has its Achilles cash). Because the benchmark would Maxis Microprose heel as well: What size polygons, and have to be optimized for every API, Origin how many vertices? Are pixel fill num- the consortium would have to main- Spectrum Holobyte bers too abstract for consumers? tain multiple benchmarks over a Frames per second containing what period of time as the APIs evolved. TOOL VENDORS: kind of objects? These questions were • The group could devise the bench- Argonaut insightful, and Glidden’s final com- mark to be API independent. In my Autodesk ment was also good: there’s a qualita- mind, this seems to be the best Caligari tive element that cannot be objectively option. Intel measured by a benchmark. For To illustrate how an API-free instance, one chip set that blows away benchmark could be accomplished, SYSTEM VENDORS: another on a frame rate benchmark Neil Trevett of 3DLabs proposed a test Acer Apple displays inaccurate colors. Which is of the whole system, analogous to IBM better in that case? Glidden’s idea. He would devise a real- Another person who echoed the istic test, one that required moving CHIP AND BOARD MANUFACTURERS: concerns of basing a three-dimensional overlapping three-dimensional objects, ATI benchmark on objective vs. subjective hidden surface removal, and back-face Cirrus Logic yardsticks was Dan Wood of Matrox. culling in front of a textured back- Creative Labs He summed it up like this: “A word of ground (he suggested multiple spheres, Diamond Multimedia warning—testing three-dimensional cubes, and so on, bouncing around and Matrox accelerators will be very difficult, as colliding in a three-dimensional box). Media Vision most hardware...will offer different lev- The benchmark would specify Trident els of quality and features besides what textures to use, the lighting, the VLSI Weitek speed. In some cases, boards may be position of the eye, and other details. Yamaha able to achieve very high frame-per- Then, when the benchmark is tightly second rates, while rendering very low specified, the source and binaries quality. Unfortunately, image quality is would be distributed to the various

6 GAME DEVELOPER • OCTOBER/NOVEMBER 1995 CROSSFIRE

hardware companies, and they could last time (“The Windows 95 Game So, as you can see, a flock of issues use whatever API they wished for the Plan,” August/September 1995), the must be dealt with before a PC three- testing, as long as the resulting anima- market for Windows 95 is not going to dimensional benchmark is finalized. tion was identical to the specification. kill the DOS and Win16 game market Despite the technical hurdles, I believe This would also mimic the optimiza- overnight, and it bears some considera- the benchmark will be here sooner tions that game developers make for tion in whatever three-dimensional rather than later. If you’d like to throw various hardware. benchmark is adopted. your two cents into the discussion, I’ve included the contact information for Oh Yeah–the Testing Conditions the GamePC Consortium. You may be Other Platforms Once the GamePC Consortium agrees testing your own games with this While virtually everyone involved in on a standard benchmark, whether it’s benchmark in the near future, so it’s the discussion was focused on Win- API independent or dependent, it must worth the time to donate input. Next dows 95 as the testing platform, Dave specify the conditions under which the time I’ll examine the plans for a Thielen of Windward Studios remind- benchmark will be run. The tests must GamePC hardware level; it is akin to ed everyone that there were other oper- occur in an environment every compa- MPC hardware ratings of which the ating systems and environments to ny can duplicate, and none can deviate three-dimensional benchmark is but a consider. from in the interests of a better perfor- single part. ■ “This benchmark needs to run on mance rating. Lou Long explained that Win16, Win/NT, and OS/2 as well as the test “...must state the physical para- Windows 95,” Thielen said. “In all the meters like the width and height of the hoopla over Windows 95, everyone window (if not full screen) in pixels, seems to have forgotten that Win16 the depth (bits per pixel), the color Alex Dunne is a contributing editor still owns the desktop market and mode (palletized or true color), and for Game Developer magazine. Contact OS/2 has 10%–while Windows 95 has any other parameter that affects the him via e-mail at 75010.2665@com- 0%.” Very good point, and one I hope total number of bits rendered by the puserve.com or through Game Developer people don’t overlook. As I mentioned hardware.” magazine.

8 GAME DEVELOPER • OCTOBER/NOVEMBER 1995 BIT BLASTS

Sweet Animation Suite

Nicole Claro Barbara Hanscome ven though you shouldn’t put addition, the set comes with four CD- all your eggs in one basket, ROMS with textures, images, and three- count your chickens before dimensional models from a variety of they hatch, or put the old cart other manufacturers. Crystal Kaleido- Sound, animation, before the horse, you should scope retails for $1,995. try to keep all your develop- ■ For more information contact: ment tools in one place. What CrystalGraphics Inc. better way to expedite three- 3110 Patrick Henry Dr. Santa Clara, Calif. 95054 dimensionalE animation than with a full upgrades, trade Tel: (408) 496-6175 suite of tools at your fingertips? Fax: (408) 496-0970 Crystal graphics is now shipping just such a product. The company’s new Bring the Noise shows, and Crystal Kaleidoscope (pictured here however minutely) is centered around Now you can perform a post-recording cleansing on any game audio you create. Tracer Technologies recently began shipping its Digital Audio Reconstruc- gossip.These are the tion Technology (DART) software. DART removes all surface noise, pops, clicks, and other disturbances from any audio source. As long as you’re using a tools of the trade. Windows-compatible sound card, you can use DART. After recording, the software applies TriCleanse, a three-part process Here‘s a sampling of that includes a smoothing processor, a CrystalGraphics’s Kaleidoscope features postfiltering processor, and an outlier a quartet of powerful rendering and ani- detector. The first component smooths mation tools built around the core of the and reconstructs the signal, the second some new and company’s TOPAS technology. removes surface noise, hiss, and any other distortion, the third searches for TOPAS Professional 5.1 a three-dimen- hard noises, such as pops and clicks, and sional modeling, rendering, and anima- automatically removes them. You can upcoming stuff you tion package for the PC. Also included even keep the noise you’ve removed in a are Fractal Design Painter 3.1 (to create separate file and reapply different levels natural-looking art from calligraphy to of the TriCleanse process until you’re oils to airbrushes), Kai’s Power Tools 2.0 satisfied with the sound quality. DART might want to SE (gradients, textures, and filters), also features Soundtree, which lets you Elastic Reality 1.01 (for morphing and review several takes of a sound file and special effects), and Leadview 3.0 (for choose your favorite one. Its toolbox managing and compressing images). includes an eight-band graphic equalizer; check out. Each tool has won awards on its own. In cut, copy, and paste editing; sound left

10 GAME DEVELOPER • OCTOBER/NOVEMBER 1995 cmoenw ... Schmooze news cmoenw ... Schmooze news Christmas Crunch Sneak Attack...As we move into Christmas crunch season id has, with one master stroke, subverted the competition’s pro- grammers by releasing the source code for Wolfenstein 3-D. What game pro- grammer could resist delving into John Carmack’s code to the detriment of his or her current deadline? The source is available on the Internet from ftp.idsoft- ware.com or on CompuServe in the GAMDEV library. There’s an intro by John Carmack pointing out what’s cool and what needs updating. Shareware Authors out for blood...Game authors are on the warpath against unscrupulous publishers who take shareware to retail and CD-ROMs without permission. Apogee, id, MVP, Red- wood Games, and TriSoft are all sicc- ing lawyers on these pirates. With the and right splitter; low, high, bandpass, ■ For more information contact: entry of Interplay’s Descent into the and notch filtering; visual and audio Blevins Enterprises shareware market, we may see some 121 Sweet Ave. expert copyright muscle behind this effort. markers; unlimited active windows; a Moscow, Id. 83843 Question: What giant distributor/publisher gain adjuster; and a mixer application. It Tel: (208) 885-3805 told a developer whose game it had pirat- retails for $399. Fax: (208) 885-3803 ed, “We’re so big we can use our petty ■ For more information contact: cash fund to keep you tied up fighting us Tracer Technologies Inc. until you go bankrupt”? P.O. Box 188 Two-Legged Wonders Musical Chairs...Matt Gruson has Dallastown, Pa. 17313 To accompany its upcoming 3D Studio joined Disney Interactive because he Tel: (717) 747-0200 “saw a lot of interesting opportunities Fax: (717) 741-6709 MAX (the newest version of 3D Studio and the chance to work under an experi- for Windows NT), Autodesk has enced management team.” Gruson announced Biped, a software product for (whose first big game was Earthrise Digitize, Digitize, Digitize! creating lifelike, free-form animations of back in the 80s) founded the Graphic Looking to make realistic, three-dimen- two-legged characters. Adventure Group at Microprose, developed the MADS game engine sional models in less time? Check out According to Autodesk, the techno- which was used in Rex Nebular (still on Vertisketch 2.0, a new plug-in from logically advanced software combines sev- the top 100 list of games), and then went Idaho’s Blevins enterprises. (I spent two eral innovative techniques with advanced to Sanctuary Woods. He says the days in Boise on a recent road trip. I am inverse kinematics and skinning functions Graphic Adventure Group was “the now officially a seasoned traveler.) which let the characters move at any pace. most energetic and wonderful group,” and his goal is to get together with a Vertisketch 2.0 is designed for use Gait, arm swing, and center of gravity group like that again. When asked if he’ll with LightWave 3D 4.0 and any sup- will automatically correspond to any pace try to reassemble the crew (some of ported three-dimensional digitizer. It you create. You use Biped by creating a whom followed him to Sanctuary), he features many new functions such as set of character footprints, which you replied, “No comment.” lofting, which automatically creates then manipulate easily. Change the posi- Sega Exodus...Wallace Poulter has tion and time on the ground of one foot- left Sega to become an Executive Produc- er of GameTek’s new sports division. In print from the next, and your creation can a switch, instead of Poulter having to walk, run, stagger, dance, jump, flip, or move to Florida, GameTek will move to the any variety of these movements. Biped San Francisco Bay Area. Relocating all features three elements that allow for this: those Sega folks they picked up must step-driven animation, free-form have looked more expensive than moving the whole company. Others jumping from keyframing, and physics-based interpola- Sega to GameTek include Tom Reuter- tion. It also includes a skeletal/skin defor- dahl (now VP of development) and Andy Vertisketch lets you create lifelike mation module for creation of seamless Johnson, who will work under Poulter. three-dimensional models with the sim- joints and realistic muscles and body mass Meanwhile the exodus from Sega contin- ple stroke of a digitizing brush. changes as a character moves over time. ues, with Chris Garske going to Good Times Software and Wayne Biped and 3D Studio MAX will ship Townsend, former head of Sega polygons, and autosurf, which creates a sometime in 1996. Sports, leaving to start his own develop- surface at the stroke of a key. Vertisketch ■ For more information contact: ment group. 2.0 comes in two different models, one Autodesk Inc. that relies on a footswitch alone and one 111 McInnis Pkwy. San Rafael, Calif. 94903 that incorporates a rotating digitizing Got gossip? E-mail The Gossip Lady at Tel: (415) 507-6112 [email protected]. platform. Fax: (415) 507-6112

GAME DEVELOPER • OCTOBER/NOVEMBER 1995 11 BIT BLASTS

closed to the general public. During the UPGRADE Kids are People, Too weekend, the doors open to everyone Creators of children’s edutainment will and special family-focused Internet and have the chance to connect with 3,000 of World Wide Web demos and workshops their potential customers October 5-8 at will take place. At the end of the Expo, the second Children’s Multimedia Expo students and teachers will vote on their in San Francisco, Calif. The event, favorite titles in the Kids’ Choice and YOURS! which debuted in June of 1995, will Teachers’ Choice Awards. bring together thousands of kids, educa- Vendors exhibiting at the show are • Caligari’s newest upgrade is trueSpace2 for Windows. It tors, parents, retail buyers, and multime- by invitation only. Companies interested features new animation and dia pros for four days of show and tell. in participating in this event or future video capabilities and true- Edutainment developers will have the Children’s Multimedia Expos must sub- Clips, a CD-ROM of more opportunity to gain important feedback mit their products for review. For more than 200 textures and 600 on their products during demos and information, contact event producer three-dimensional objects. The upgrade is available to focus group testing with some 3,000 Shannon Tobin at (415) 788-9990 or fax trueSpace1.0 users for $149 children and teachers brought in from (415) 243-8939. ■ and $795 for new users. ■ schools throughout California. (Wow For more information contact: • Borland is shipping Turbo kids, here’s a field trip to a place where Shannon Tobin C++ 4.5, which includes five you can play computer games all day Event Producer free games with full source Children’s Multimedia Expo ! code. This latest version is long—the only catch is you’ve got to Tel: (415)788-9990 answer a few questions from some geared toward beginning programmers and retails for friendly marketing executives....) Last $79.95 for new users and year, multimedia companies demonstrat- Nicole Claro is managing editor for $49.95 for owners of any ed works in progress as well as titles cur- Game Developer magazine. Barbara previous versions of Turbo rently shipping. Hanscome is managing editor for Software C++. Two days of the four-day event are Development magazine.

12 GAME DEVELOPER • OCTOBER/NOVEMBER 1995 BEHIND THE SCREEN

Memory Miscellanea

Chris Hecker s you can see from the title, Lies, Damn Lies, and this article is not “Perspective Bandwidth Numbers Texture Mapping, Part IV,” On today’s machines, we usually mea- Don‘t worry, perspec- the continuation of our epic sure memory bandwidth in megabytes perspective texture mapping per second (MB/s), but you’ll sometimes series. Don’t panic, I’m not see bytes per second, dwords (a dword is breaking my promise to deliv- four bytes in this article) per second, and tive texture mapping is er a wicked fast perspective so on. If you’re looking at a bunch of Atexture mapper, but to break up the memory bandwidth numbers, it’s obvi- series a bit I thought I’d insert a non- ously important to know which measure- texture mapping article here in the mid- ment units they’re in. dle (although the topic is definitely Like most statistics, the bandwidth alive and well. But this applicable to texture mapping, as you’ll numbers themselves aren’t enough to tell see). We’ll resume with Part IV next the whole story, and you need to know issue. exactly how the numbers were generated This time through, we’re going to for a specific machine to give them month, Chris Hecker discuss memory bandwidth. Plainly stat- meaning. For example, I could tell you ed, memory bandwidth is a measure of the laptop on which I’m typing right now how much memory you can read and or gets 42 MB/s, but you really aren’t any write in a given amount of time. more knowledgeable than before because takes a break from his From that description, it should you don’t know if I mean read band- be clear that memory bandwidth width, write bandwidth, copy bandwidth, affects every kind of game on every sequential or random reads or writes, or platform, from scrolling platform any combination thereof. All these para- series on texture games on an 8-bit Nintendo or Atari meters can make a big difference inwhat 2600 system to high-end military sim- a bandwidth number really means. ulators that cost millions of dollars. In fact, it’s rare that any general Memory bandwidth governs how many bandwidth number will mean anything in mapping to explore the sprites the hardware in the older con- the context of your specific game. I’m soles can move around, and how many going to talk about various things that can polygons can be textured per second in affect your game’s memory bandwidth, hardware on the newest machines or in techniques for measuring that bandwidth, software on the PC. and pitfalls you’ll encounter along the way. nuances of In fact, an oft-cited goal of PC graphics programmers is to “get your Pyramid Power texture mapper running at memory First, we need a one-minute refresher on bandwidth,” because there’s not much how modern CPUs and motherboards memory bandwidth in more you can do to increase its speed work. I’m certainly no hardware engi- after that. To get a tad flowery, memory neer, so we’ll limit our discussion to how bandwidth can be an open door or a the software sees the hardware. brick wall. It all depends on how much Throughout computer history, game programming. of it you’ve got. there’s always been a pyramid diagram

GAME DEVELOPER • OCTOBER/NOVEMBER 1995 15 BEHIND THE SCREEN

Figure 1. Component Cost vs. Amount ent kinds of memory bandwidth, each different because the access pattern used to generate the numbers is different. The access pattern is the way the application moves the memory around, and there are as many different types as there are pro- Decreasing grams. Three general categories that are Registers cost per important, but by no means form a com- megabyte plete list, are sequential copy bandwidth, sequential write bandwidth, and random On-chip Cache (L1) read-sequential write bandwidth. Sequential copy bandwidth is the number that applies when you’re copying Off-chip Cache (L2) a block of memory from one place to another—to copy a new piece of digital audio into the play buffer, for example. Sequential write bandwidth is what you Main Memory see when filling a rectangle or polygon, zeroing an array, or anything else where you’re writing a single value or a value Hard Disk that’s generated using instructions (as opposed to read from a source) to a desti- nation. Finally, random read-sequential that describes the speed of the compo- Second to last in our diagram, we write bandwidth is what you see when nent vs. how much of that component have main memory—of which there’s texture mapping, where your source loca- you’re likely to have in your system (usu- usually a relatively large amount (4 to tions are fairly randomly distributed, but ally because faster components are more 32MB). As you’d expect, it’s even slower you’re usually writing a scanline at a time expensive). Figure 1 shows this diagram than any of the memories above it. Final- to the destination. for memory components. ly, we end up with the hard disk, which From these descriptions, you can At the top of Figure 1 we have the has oodles of storage (well, okay, my hard easily come up with other kinds of band- CPU registers, which usually take a single disk sometimes has less space free than I widths and situations in which they’d cycle to access and are the most flexible of have main memory, but it still has more arise. You might find multiple reads and all types of memory, but are pretty scarce raw storage!) if you’re willing to pay for a single write interesting if you’re mixing (Intel x86 processors have only eight gen- the access time and transfer rates. CD- digital audio or alpha blending sprites. eral purpose registers, each of which holds ROMs and tape drives would be below Likewise, a single read and multiple four bytes, while most new processors like hard disks if we put them in the dia- writes might be your thing if you’re the PowerPC have around 32—all the par- grams, because they’re cheaper (and stretching an image. The key is to figure enthetical numbers in this paragraph are slower) per megabyte. out which type of bandwidth is most estimates and will vary in practice). Below The most interesting thing about appropriate to your application and mea- the registers, we have the on-chip cache Figure 1 is that it holds for almost every sure it. memory, sometimes called the level 1 machine architecture, from Commodore It’s clear that any useful and inter- cache. This is usually a small amount (8 to 64s to Crays. On the lowest end, you esting application is going to do more 32KB or so) of fast memory with access might not have caches, and at the higher than just copy bits all day, but memory times slightly slower than registers. Cache end you might have more layers, but the bandwidth gives a good upper bound on memory is a little less flexible than regis- speed vs. cost ratios still stand. your performance. In other words, even ters, as well. Most CPU architectures don’t With that refresher, let’s get to the if you were the best optimizer on earth, let you add a number in the cache directly hints, tips, and techniques for determin- you still wouldn’t be able to get your to another number in the cache without ing the memory bandwidth for your code faster than memory bandwidth if using the registers for temporary storage. game, so you can strive to achieve it. you need to move those bits around. On the next rung down, we have the This may seem like a limitation, and it off-chip level 2 cache, which usually has What’s Your is, but you can also look at it as an more storage space (256KB to 1MB), but Access Pattern? opportunity. If you can figure out a way is much slower than the on-chip cache, As I mentioned, a single number doesn’t to reduce your memory bandwidth generally on the order of five times slower tell the whole story about memory band- requirements by redesigning your algo- or more. width. In fact, there are zillions of differ- rithm or possibly by changing your

16 GAME DEVELOPER • OCTOBER/NOVEMBER 1995 BEHIND THE SCREEN

access pattern you can open up new pos- neck in your game’s run-time speed (and suring bandwidth. Cache effects would sibilities for optimization. you shouldn’t even be bothering to mea- definitely be at the top of this list. sure this stuff if it isn’t a bottleneck), is to An Accessible Example Understand the Cache For example, if you were writing a solid The processor cache is usually an object polygon rasterizer, you could measure What‘s fastest on of great fear, wonder, and misunder- sequential write bandwidth and compare standing. A friend of mine named Terje it to the fill rate you get through your Mathisen says, “All programming can be rasterizer. The difference is your over- thought of as an exercise in caching.” head above memory bandwidth. Your your machine Although Terje isn’t talking specifically goal is to minimize this overhead or, if about the processor cache, this is a rule to possible, cheat somehow so you get the live by when you’re trying to optimize on same effect on the screen but aren’t modern processors. If we apply this idea bound by the same memory bandwidth to the processor cache and memory limitations. might not be bandwidth, it means, “Figure out how to Let’s say you have the world’s fastest put your important data in the cache and 90-degree bitmap rotator; you can take a keep it there.” This may seem obvious, bitmap and rotate it 90 degrees at memo- but keeping your data in the cache is ry bandwidth on your machine—you’re fastest on mine. more difficult than you might think. very proud of this code. You know it Before we bother getting into this, works at memory bandwidth because you what difference does it make? Well, on measured it without any instructions my laptop, the speed difference between except the copies in the inner loop and The best we can reading from the on-chip cache and got the same bandwidth number when reading from sequential uncached memo- you added your rotator code. Let’s also ry is tenfold—and this isn’t even the say your code is “destination-centric,” whole story. I’m reading sequentially in that is, it scans horizontally in the desti- do is have a list of this example, so at least some of the reads nation and therefore it scans vertically in are cached for reasons I’ll explain shortly. the source to accomplish the rotation. Of If I ensure that all the reads are uncached course, you measured memory bandwidth by reading pseudo-randomly, the pro- doing the same thing, so let’s call this things to look for gram reads from the cache about 30 access pattern vertical read-sequential times faster than from main memory. write. Since we’re running up against this You can do a lot in 30 cycles on a mod- memory bandwidth limitation, how can ern processor, so I’d rather not spend we restructure the algorithm to have a when we‘re mea- them waiting on memory. different limitation? I’m going to assume you know gen- It’s immediately apparent that you erally what a cache is and how it works, so should measure sequential read-vertical the only high-level description I’ll give is write, which will accomplish the same this: the cache stores frequently accessed rotation, but might be a different speed. suring bandwidth. data in fast on-chip memory, so when you Also, another possibility is to spend a lit- reference it the chip doesn’t have to go out tle memory and pre-rotate your bitmaps, profile the various techniques at startup to the memory bus to fetch your request. so your access pattern is sequential copy. and have your game self-configure to use Caches are broken up into cache Will either of these be faster? I don’t the fastest possible pattern for the given lines, which are usually 16 or 32 bytes know, and we can’t say with certainty machine. long, and the processor reads in an entire until we’ve timed it. My hunch is that It may seem like I’m being wishy- cache line from memory when a cache sequential copy will be the fastest in washy by not just declaring a single miss occurs. These cache lines are aligned terms of pure bandwidth because it’s access pattern the fastest, but we’ve got on address boundaries that correspond to probably the access pattern for which the far too many variables to do so. The their length, so 16-byte cache lines are memory subsystem was optimized, but problem is compounded by the number aligned on 16-byte boundaries, for exam- it’s just that, a hunch. It’s entirely possi- of different hardware architectures out ple (addresses ending in 0 hexadecimal). ble the extra memory overhead from pre- there, so what’s fastest on your machine This is why my previous sequential reads rotated bitmaps would make the overall might not be fastest on mine and vice were partially cached. Assuming a 16- code slower because of paging. versa. The best we can do is have a list of byte cache line, every fourth dword I read The real solution, if this is a bottle- things to look out for when we’re mea- in my test brought in another cache line,

18 GAME DEVELOPER • OCTOBER/NOVEMBER 1995 BEHIND THE SCREEN

and the next three dwords were read You can see why this works well in when it is happening in your code. To from the cache’s fast memory. The use of the general case because referenced do this, you need to get good at profil- cache lines also means that if you’re refer- addresses are likely to be near each ing. Profiling at this level doesn’t mean encing two bytes at addresses that differ other—a phenomenon called locality of just running the code profiler that comes by more than a cache line (or if there’s a reference—so they’ll have different set with your compiler and examining the cache line boundary between them) you’ll addresses. The set architecture allows for results, it means figuring out exactly be using two lines, even if you’re only some addresses to be not-so-near each where your code is spending its time in accessing those two bytes. other because it lets very different the inner loop. You can definitely use Life in the cache gets even worse addresses with the same low bits map to the high level profiler to find the inner when we delve deeper into its behavior. N different cache lines (in contrast, a loop in the first place, but once you’ve Most modern caches are N-way set asso- cache architecture called direct mapped found it, if you want to max it out and ciative for some small integer N, usually has no sets, so each address with the really pin down why it’s taking the time 2 or 4. A cache set is a group of N cache same low bits shares a single cache line). it is, you’re going to need to get down lines, so a two-way set associative cache However, in certain cases this kind of and dirty with a very accurate timer (per- has a bunch of sets, each containing two cache architecture can really screw you up. sonally, I use timeGetTime or QueryPerfor- cache lines. You can tell how many sets For example, let’s say you’re reading verti- manceCounter on Windows, but any accu- there are by taking the size of the cache cal strips from a bitmap like the bitmap rate timer will work) and a knowledge of in bytes, dividing by the number of bytes rotator we discussed previously—down assembly language. per line to get the total number of lines, one vertical scanline, then down the next, Before continuing I should stress and then dividing by N for your cache to and so on. The width of your bitmap will that this kind of profiling and optimiza- get the total number of sets. For example, the Pentium has 8KB of two-way set associative data cache with 32-byte cache Figure 2. Pentium Cache Addressing lines, so 8KB / 32 bytes / 2 lines per set = 128 sets. The cache translates a memory Address Bits address into a cache line address by using the lowest bits for the intra-line address, 31 12 11 5 4 0 the next few bits for the set address, and the remaining high bits for the cache tag. Intra-line Cache tag Set address Figure 2 shows the breakdown for the address Pentium. Because there are 32 bytes in a cache line, the lower five bits are used for the intra-cache line address, and the next seven bits give us the 128 sets we calculat- dictate how much of the cache you end up tion takes a very long time, so you should ed previously. Which line a given address using. If your bitmap is 256 bytes wide, a make absolutely sure you’re applying that uses in its set is up to a replacement algo- single increment vertically will step bit 8— time to the right part of your code. In a rithm (usually least recently used or an and never any bits below bit 8—in your game, there are probably 10 lines of code algorithm close to it) based on the cache address. If you compare that with the in the entire project that might need this tag bits. In other words, every address Pentium’s cache address layout in Figure sort of attention, and if you’re going to that contains the same set address bits 2, you’ll notice that you’re only using four spend a week looking at them you had will map to the same set, and all those bits of your possible seven bits of set better make sure they’re the right 10 lines. addresses must share the lines in that set. address. This means that instead of using Michael Abrash’s phrase, “Assume This is where the replacement algo- all 128 sets, you’re only using 16 of them nothing!” and its corollary, “Time every- rithm comes in. If all the lines in the set or only 1/8 of your total cache! The star- thing,” are words to live by in this neck of are currently full and none of the tags tling implication of this is the next hori- the woods. Abrash is the master of this matches the requested address, then one zontal byte from the first scanline will not sort of optimizing, so you should definit- of the currently cached lines needs to be be in the cache when you get back up ley read his book Zen of Code Optimiza- replaced by the current requested line. there if you’ve gone farther than 32 scan tion (Coriolis, 1994). As a bonus, the For example, on the Pentium only two of lines (16 sets x 2 lines per set) because it’s disk that accopanies the book comes with the many possible cache lines (20 bits been pushed out of its set by another line. a very accurate timer designed specifically worth of lines because bits 12-31 make for this in-depth profiling. up the tag—that’s 1,048,576 possible Assume Nothing While I was writing this column, lines!) for a given set can be in the cache What can you do about this sort of the need for this very kind of profiling at the same time. thing? Well, first you need to realize came up. I was gathering bandwidth

20 GAME DEVELOPER • OCTOBER/NOVEMBER 1995 statistics for various access patterns on Intel-speak for a type of pipeline stall), Heisenberg is alive and well at this level. my 486 laptop and I was trying to time but there weren’t any. I haven’t covered video memory and cached reads. I know from both experi- Finally it hit me. I remembered that its associated bandwidth weirdnesses at ence and the 486 manual that a read if you’re continuously reading from all. Nor have I discussed processor write from the cache is a single cycle, but I cached memory without allowing even a buffers, write-back versus write-through couldn’t seem to convince my timing single free memory cycle for prefetching caches, processors that don’t write allocate program of this fact. It kept returning instructions, the 486 will stall your code cache lines (like the Pentium), new trends around 1.5 cycles per read, and when to fill the prefetch queue. Eureka! I veri- in memory that affect the bandwidth you’re timing at this level that’s 50% fied this was the culprit by changing the numbers (like EDO memory, RAMBUS, off. My test program had an unrolled number of consecutive reads and got the and SDRAM), groovy new cache/access loop of a couple of hundred reads, and expected one cycle per read. I also looked pattern debugging instructions (like then I looped back to the top a bunch it up in the 486 Programmer’s Reference RDMSR on the Pentium), and much of times. I was very careful not to Manual from Intel, and the stall was list- more. Hopefully this article gives you unroll my loop so much that it blew out ed there among the others. enough background and forewarning the code cache, so I simply couldn’t about the strangeness you’ll encounter figure out what was going on. I timed Time to Cache Out that when it’s time to max out your inner other single cycle instructions with the As you can see, figuring out where every loop, memory bandwidth won’t be the same timing harness (using a millisec- cycle is going in your inner loop, espe- mystery it can be for the unprepared. ■ ond timer and looping a lot), and they cially when there are strange effects returned reasonable times, like 1.02 brought on by your memory access pat- cycles, but my reads kept returning 1.5. tern, is very difficult and time consum- If I stuck a nop in between the unrolled ing. I highly recommend reading and Chris Hecker wonders why five-year- reads I got the expected two cycles, one rereading the manual for your processor old workstations with incredibly slow for the read and one for the nop. I before you try to do this. Also, always CPUS still have better memory bandwidth stared at the code, trying to find an test your timing program with known than today’s top-of-the-line PCs. You can address generation interlock, (AGI— inputs so you can verify that it works; commiserate with him at [email protected].

GAME DEVELOPER • OCTOBER/NOVEMBER 1995 21 ORGANIZING USER INPUT

Organizing User Input, Part I: The Input Queue Manager and Keyboard Events

Figure 1. Input Queue Manager Block Diagram from the hardware devices and stores each input as an event on an internally main- keyboard Keyboard tained queue. When a client program interrupt Hdlr requests input, the oldest event is returned. The main design goals for the input queue manager were: mouse Mouse • Gather user input asynchronously to the event Hdlr client program Input Queue Events Client • Minimize memory and execution over- Manager Program head • Return user input in a single format Mouse Cursor regardless of the input device. Interface User Defined Events I developed the input queue manager in C and assembly language using ancient timer Timer versions of Borland C/C++ and Turbo interrupt Hdlr Assembler, though I’ve now converted most of the assembly language to C for the sake of maintenance. ost programs I’ve written have required some form of input The Input Queue from the user. In most cases, Manager Structure the programming language or Figure 1 is a block diagram of the input the library provided with the queue manager. The keyboard, mouse, and language was sufficient to get timer generate interrupts when they are in the input in a reasonable need of service. The input queue manager manner. Of course, none of handles these interrupts, storing the infor- Mthese programs was a game. mation gathered on an internal queue and Fast action games need to process supplying that information to the client multiple, simultaneous key presses at a program, on request, as events. In addition, frantic rate. Most fighting games provide the client program may post events to the combinations of keystrokes that, when input queue if required. The mouse cursor pressed within a limited time frame, cause interface allows the event manager to some special move to be executed. Not to accept mouse events even if the video mention the mouse and joystick, other mode isn’t supported by the default mouse common game input devices that aren’t driver (as in Mode X and VESA). even supported by most compiler libraries. “Why no joystick handler?” you may The question is, how can we obtain the ask. The input queue manager only han- user’s input in a reasonably efficient and dles events that can be gathered via inter- general manner? rupt. One of my design goals was to pro- The input queue manager presented vide as little overhead to the client program in this article is my solution to this prob- as possible. The joystick has to be polled at lem. This manager accepts the raw input regular intervals for its current position and

22 GAME DEVELOPER • OCTOBER/NOVEMBER 1995 Mike Michaels button state, so I chose not to make joy- repository for the event, and its pointer will stick support intrinsic to the design. be the return value. Otherwise, an event structure internal to the input queue man- Using the Input ager will be used. This structure is only Fire keys, key Queue Manager valid until the next call to INPQ_dequeue() Before digging into the nuts and bolts of with a NULL event parameter. the input queue manager, I’ll present a When INPQ_dequeue() is called, the simple example of its use to give you a feel- next event matching the supplied event ing for the interface. This example exits mask, key_down in this case, will be combinations, timed after the escape key is pressed. Pressing any removed from the input queue and other key causes that key’s ASCII value to returned to the client program. be printed. Listing 1 contains the code for In our example, if the event is valid, it the example. will be checked to determine if the key You must include the header file, pressed was the escape key. If it is, we key combinations... inputq.h, at the top of all source files that break out of the input gathering loop. make use of the input queue manager. Otherwise, the ASCII value of the key is This file defines the input queue manager’s printed. data structures and provides prototypes for Finally, INPQ_release() is called to What‘s a programmer all its visible functions, the names of which deallocate memory held by the input queue are prefaced with INPQ_. manager and restore the vectors of all The first call to the input queue man- interrupts that had been taken over. This ager is INPQ_allocate(). This routine allo- call is just a formality, though. Because the cates all required events and a correspond- execution of this routine is critical to the to do? Create a ing queue large enough to hold them. The continued operation of the machine after number of events to allocate is supplied as the client program terminates, INPQ_allo- an input parameter to the call. cate() installs it as an exit handler. So, Next, we enable key press events with while calling this routine is a good practice, a call to INPQ_enable_keyboard(). This rou- it’s not absolutely necessary. single, high-perfor- tine installs a keyboard interrupt handler This is all I’m going to say for and tells it to notify us every time a key is now about the visible interface to the pressed. input queue manager. These functions, At this point, every key pressed will along with the functions to enable the mance input enqueue an event. These events will sit mouse and timer handlers, make up the patiently on the input queue until the crux of the interface. Table 1 contains a client program makes a call to list of all visible input queue manager INPQ_dequeue(). functions. INPQ_dequeue() is implemented like manager, that‘s the standard C library time() routine. If no The Event Structure events are on the queue, NULL is returned. If Figure 2 is a graphical representation of there is an event, a pointer to the event is the event structure. Listing 2 shows the returned. If an event structure is passed actual type definitions. Each event is iden- into INPQ_dequeue(), it will be used as the tified by its type field, which contains a what!

GAME DEVELOPER • OCTOBER/NOVEMBER 1995 23 ORGANIZING USER INPUT

Listing 1. Input Queue Manager responding data and attributes will be type. Read “efficiently” as “in reasonable discussed more thoroughly when we time.” Pull out those data structures #include are discussing specific event types. For books and dust them off: a priority queue #include “inputq.h” int main (void) the time being, let’s move on to look at is a data structure that supports insertion { the data structure on which the input of new prioritized elements and deletion EVENT event; queue is based, the priority queue. of the element with the smallest (or if (!INPQ_allocate (32) || !INPQ_enable_keyboard (key_down)) largest) priority. { Priority Queue Concepts The priority queue structure can be printf (“Unable to initialize \ The client program isn’t necessarily implemented to require a fixed memory input queue manager.\n”); exit (1); going to process events as soon as overhead. In other words, all memory for } they are available, so some sort of events and support structures can be preal- queue is required. A priority queue located when the input queue manager is for (;;) { data structure was chosen for a num- initialized. This precludes the input queue if (INPQ_dequeue (&event, key_down)) ber of reasons. manager allocating and freeing memory as { The priority queue structure events are processed, eliminating the risk if (event.data.kbd.scancode == ky_ESC) allows events to be processed effi- of the input queue manager causing mem- break; ciently, in order or according to event ory fragmentation. printf (“%c\n”, INPQ_ascii (&event)); } } Table 1. Input Queue Manager API INPQ_release (); } Input Queue Control Functions INPQ_allocate() Initializes the input queue manager. INPQ_release() Cleans up after the input queue manager. INPQ_enable_keyboard() Enable keyboard events. unique binary value depending upon the INPQ_disable_keyboard() Disable keyboard events. event in question. The values are assigned INPQ_enable_mouse() Enable mouse events. from the EVENT_TYPE enumeration defined INPQ_disable_mouse() Disable mouse events. in Listing 2. INPQ_enable_timer() Enable timer events. Each event type has some unique INPQ_disable_timer() Disable timer events. information assoicated with it. This infor- INPQ_enable_user() Enable user-defined events. mation is stored in the event’s data field. INPQ_disable_user() Disable user-defined events. The information returned by keyboard INPQ_events_enabled() Return enable events. events, for example, is the scan code of the Queue Management Functions key that was pressed or released. The information associated with some events INPQ_dequeue() Dequeue an event, based upon event type. may not be complete without additional INPQ_enqueue() Enqueue an event, possibly user defined. context information. This information is Keyboard Support Functions stored in the attributes field. Scan codes do not take into account the state of the shift, INPQ_ascii() Translate a scan code to its ASCII equivalent. ctrl, and alt keys. These keys must be reg- Mouse Support Functions istered by the keyboard handler and their state passed along in the attributes field of INPQ_show_mouse() Show the mouse cursor. the event. INPQ_hide_mouse() Hide the mouse cursor. INPQ_mouse.visible() Return true if the mouse is currently visible. Finally, each event contains a time- INPQ_obscure_mouse() Hide mouse cursor without affecting events. stamp obtained from the CMOS clock at INPQ_unobscure_mouse() Show mouse cursor without affecting events. the time the event is queued. The time- INPQ_set_graphics_cursor_shape() Set the mouse cursor shape. stamp serves two purposes. First, it allows INPQ_set_mouse_position() Reposition mouse to new coordinates. the input queue manager to properly INPQ_get_mouse_position() Return mouse’s current coordinates. order events internally (we’ll discuss this INPQ_push_mouse_regions() Prepare to define hotspots. in more detail in a subsequent section). INPQ_define_mouse_region() Define a hotspot. Second, it allows the client program to INPQ_pop_mouse_regions() Discard mouse regions. determine the span between two or more events, à la the fighting game example Timer Support Functions mentioned previously. INPQ_set_alarm() Enable one of 16 countdown timer alarms. Each of the event types and their cor-

24 GAME DEVELOPER • OCTOBER/NOVEMBER 1995 Listing 2. Excerpts from INPUTQ.H of the node located at position 7 is (int)(7/2) = 3. #include “basic_dt.h” } INPQDATA; Given that we can find the parent #include “scancode.h” and children of any node regardless of its /* #define LAST_ALARM 15 ** Event structure location on the heap, how do we perform */ insertion and deletion operations? Listing typedef enum { 3 contains the heap manager code, includ- null_event = 0x0000, typedef struct { key_down = 0x0001, u32 timestamp; ing code to perform insertion (enqueue) key_up = 0x0002, u16 type; and deletion (dequeue) operations on the mouse_down = 0x0004, INPQATTR attr; heap. mouse_up = 0x0008, INPQDATA data; mouse_move = 0x0010, } EVENT; To insert an element into a heap, we timer_alarm = 0x0020, see that the enqueue() function starts by first_usr = 0x0040, inserting the new element at the end of the last_usr = 0x8000 } EVENT_MASK; Our priority queue implementation array. This corresponds to placing the new will be based upon a specific type of binary element at the bottom of the heap. At this /* tree data structure known as a heap. A point, we may not even have a heap any- ** Attributes Structures */ heap is a binary tree that satisfies the heap more, because the heap condition may be condition: the priority of any given node is violated at this terminal position. The rou- typedef struct { less than or equal to the priorities of the tine reorder_heap() is called to fix any vio- u16 k_extended : 1; u16 k_shift : 1; node’s children (if they exist). This condi- lations of the heap condition. It does this u16 k_ctrl : 1; tion can easily be encapsulated in an array by checking the priority of the newly u16 k_alt : 1; representation, as shown in Figure 3. inserted event against the priority of its u16 k_unused : 12; } KEYBOARD_ATTR; Assume that the values within the parent. If the parent priority is larger, the nodes are the priorities. It is trivial to verify two events are swapped. This continues typedef struct { that every node in this tree satisfies the until the new event can no longer be u16 m_region : 8; u16 m_left : 1; heap condition, and therefore this tree is a swapped with its parent, resulting in a tree u16 m_right : : 1; u16 m_center : 1; u16 m_unused : 5; Figure 2. Event Structure } MOUSE_ATTR; 31 0 typedef union { KEYBOARD_ATTR kbd; MOUSE_ATTR mouse; u16 value; Timestamp } INPQATTR;

/* Attributes Type ** Data Structures */ typedef struct { Data u8 scancode; u8 reserved1; u16 reserved2; } KEYBOARD_DATA; heap. Now we can convert this tree repre- that satisfies the heap condition. sentation into an array representation by Deleting an element from the queue typedef struct { simply starting at the root of the tree and is just slightly more complicated. The s16 x; s16 y; adding elements to the array in a top-to- dequeue() operation first deallocates the } MOUSE_DATA; bottom, left-to-right, fashion (a breadth- event to be deleted, returning it to the pool first traversal if you will). of available events. Then, the event at the typedef struct { u8 alarm; Within the array, the children for any last array index in the heap is inserted in u8 reserved1; node, say the node at position k, are locat- the position of the deleted event. Again, u16 reserved2; ed at positions 2k and 2k+1. This in turn we may no longer have a valid heap, since } TIMER_DATA; implies that the parent of any node, say the the event we just inserted has an arbitrary typedef union { node at position j, can be found at position priority. The reorder_heap() routine is KEYBOARD_DATA kbd; (int)(j/2). For example, in Figure 2, the again called to restore the heap to a valid MOUSE_DATA mouse; TIMER_DATA timer; children of the node located at position 3 state. s32 value; are located at positions 2 * 3 = 6 and 2 * 3 + This time though, reorder_heap() 1 = 7 respectively. Conversely, the parent must try and work the inserted event down

GAME DEVELOPER • OCTOBER/NOVEMBER 1995 25 ORGANIZING USER INPUT

Figure 3. Conversion of a Heap Represented as a Tree into an Array

4 position 1 2 3 4 5 6 7 8

priority 4 7 10 30 9 20 50 21

7 10

30 9 20 50

21 the heap until the heap condition is satis- want to delete items with the smallest pri- cated, events are stored in a pool, which is fied. It does this by checking the children orities). Assuming we keep a pointer to the managed by the allocate_event() and deal- of the newly inserted event. If no children tail of the queue, this operation can be per- locate_event() routines. exist, the heap condition is satisfied, and formed in constant time (O(1)). Deleting Allocate_event() is called from our the routine is done. the element with the smallest priority is event handlers when input is received. If If children exist, the newly inserted also trivial: just remove the element at the there is an event to return, the CMOS event’s priority is checked against the head of the list. Again, this is a constant timer function, GetTickCount (also shown in largest of its children’s priorities. If the time operation. But if you want to find an Table 2) is consulted and its current value newly inserted event’s priority is greater, it arbitrary element, searching the queue is is stored in the timestamp field of the and the larger child are swapped. This going to take time proportional to O(N) event. continues until there are no more children on average. The timestamp value is important in to check (at which point the event is at the The heap implementation takes two ways. First and foremost, it is used to bottom level of the heap) or the event’s slightly longer to insert and delete ele- order the input queue. The queue is priority is less than both of its children’s ments, because it must maintain the ordered from smallest timestamp (oldest priorities, satisfying the heap condition. The final operation we want the pri- Table 2. CMOS Get Tick Count Function ority queue to perform is a search for an arbitrary element. The routines Int 1AH Function 00H locate_event() and find_event() work Get Tick Count together to perform a recursive binary Returns with the contents of the clock tick counter. search of the heap based upon an event Call with: type. I used a recursive search because it was easy—I didn’t anticipate a burgeoning AH = 00H heap and I was getting lazy. Needless to Returns: say, a better implementation would use a AL = rolled-over flag state variable and a loop to remove the 00H if midnight not passed since last read dangers associated with the recursion. <> 00H if midnight passed since last read Both the insertion and deletion oper- CX:DX = tick count (high 16 bits in CX) ations can be performed in time propor- tional to O(ln N). The binary search oper- ation, for an arbitrary value, can be per- heap condition, but when searching for event) to largest timestamp (newest event). formed in time proportional to O(2ln N) arbitrary elements, it is significantly All queued events have nonzero timestamp on average, with a worst case time propor- faster on average than the standard values, so this field is also used as a free tional to O(N). queue implementation. indicator in the event pool. Any event with To provide some basis for compari- a zero timestamp cannot possibly reside on son, consider implementing the priority The Event Pool the input queue and is therefore available queue with a standard, albeit modified, As I mentioned previously, INPQ_allocate() for allocation. queue structure. Insertion would require and INPQ_release(), shown in Listing 4, Before it returns the address of the nothing more than adding a new element work together to allocate and free memory new event to its caller, allocate_event() to the end of the queue (remember, we for events and the input queue. Once allo- enqueues the event. The only responsi-

26 GAME DEVELOPER • OCTOBER/NOVEMBER 1995 bility the caller has is to fill in the event quence is an extended scan Listing 4. Excerpts from INPUTQ.C type, data, and attributes fields. Deallo- code sequence; a single key- cate_event() does little more than reset stroke generates multiple BOOLEAN INPQ_allocate (s16 n) { the timestamp of the supplied event to events. if (n <= 0) zero, freeing it for later use. Our keyboard handler n = DEFAULT_QUEUE_DEPTH; will perform the following Taking Control pool = (EVENT *) calloc (n, sizeof (EVENT)); functions: if (!pool) of the Keyboard • Obtain the scan code for return False; Now that we can allocate and deallocate the keystroke (taking into pool_last = n - 1; pool_first_free = 0; events, we’re ready to start talking about account the extended scan populating the queue. The input queue code behavior) queue = (EVENT **) calloc (n + 1, sizeof (EVENT **)); manager has three event handlers—one • Acknowledge to the hard- if (!queue) { each for the keyboard, the system timer, ware that the interrupt was free (pool); and the mouse. The event handlers for the received and handled pool = NULL; keyboard and the system timer both grab • Track the current state of return False; } interrupt vectors and install interrupt ser- the shift, ctrl, and alt keys queue_max = n; vice routines to gain control of all input (the keyboard handler does queue_last = 0; from the given device. The mouse handler not differentiate between atexit (INPQ_release); is slightly different. It makes use of the left and right instances of return True; existing mouse device driver for its event these keys) } notification. • If appropriate, allocate an void INPQ_release (void) The final topic we’re going to discuss event and fill in the current { is the keyboard event handler. We’ll cover attributes and scan code INPQ_disable_keyboard (events_enabled); the timer and mouse event handlers in the values. INPQ_disable_mouse (events_enabled); INPQ_disable_timer (events_enabled); second part of this series. The IBM PC keyboard communi- Keyboard Events if (queue) cates with the PC BIOS via hardware Figure 4 is a graphical repre- { 09h free (queue); interrupt . Whenever a key is pressed or sentation of a keyboard event. queue = NULL; released, an interrupt 09h is generated and The event type field will } in most cases, the scan code of the key either have bit 1 or bit 2 set to if (pool) key_down key_up { pressed can be read from the keyboard port indicate a or free (pool); located at address 60h. event, respectively. The attrib- pool = NULL Certain keys generate extended scan utes field contains bit flags } } codes. A key that generates an extended indicating whether the event BOOLEAN INPQ_enable_keyboard (EVENT_MASK keyboard_events) scan code requires the keyboard handler to was obtained as a result of an { processes two or more interrupts for the extended scan code as well as keyboard_events &= (key_down | key_up); if (keyboard_events) single key. On the first interrupt, the the state of the shift, ctrl, and { extended scan code identifier, 0E0h, is read alt keys at the time that the initialize_keyboard (); from the keyboard port. The actual scan event was generated. Finally, events_enabled |= keyboard_events; } code may be read from the keyboard port the data field contains the return True; on the next interrupt. actual scan code received. } In most cases, extended scan codes You might ask why the void INPQ_disable_keyboard (EVENT_MASK keyboard_events) are used when there are multiple identical ASCII code for the key isn’t { keys on the keyboard. For example, the ctrl stored in the event as well. keyboard_events &= (key_down | key_up); key on the lefthand side of the keyboard Originally, it was, and the events_enabled &= ~keyboard_events; if (!(events_enabled & (key_down | key_up))) returns a standard scan code, and the one event handler was spending release_keyboard (); on the right returns the same scan code most of its time doing the } value, but as an extended scan code combi- scan code to ASCII value nation. translation. Because of the Some keys, such as print screen/sys- overhead and because each tem request and pause/break, generate key is uniquely identified by its scan code, I advantage: if an ASCII value is never multiple extended scan code sequences. made an executive decision to perform the requested (via a call to INPQ_ascii()), the The simple keyboard handler we’re devel- ASCII translation outside the interrupt code to perform the translation (and the oping will not look for these, though this and only at the request of the client pro- substantial translation tables) never get will not really be a problem. Each subse- gram. This method offers one more linked into the client program’s executable.

GAME DEVELOPER • OCTOBER/NOVEMBER 1995 27 ORGANIZING USER INPUT

The Keyboard Event Handler scancode = inp (KEYBOARD_PORT); the handler informs it that the interrupt Listing 5 contains the code for the key- outp (PIC_REGISTER, has been noticed. The interrupt is board event handler. The first two routines NONSPECIFIC_EOI); acknowledged by outputting a value of are used to initialize and release the key- // acknowledge_interrupt 0x20 (nonspecific EOI) to the PIC control board interrupt, respectively. The key- port at address 0x20. board_events() routine, as the name The interrupt controller must be Originally, this operation was per- implies, gathers keyboard events and cleared because interrupt 09h is a hardware formed at the end of the interrupt handler, returns them on the input queue. interrupt. It is actually attached to a line on which I consider a much safer place for it. The first thing that keyboard_events() the programmable interrupt controller But as soon as I had the debugger stop on does is grab the byte from the keyboard (PIC). Whenever the PIC generates an a breakpoint within the keyboard handler, port and clear the interrupt controller: interrupt, that interrupt is masked off until the machine would hang, and no further keyboard input was possible. This was because the PIC refused to allow any Listing 5. KBD.C more keyboard interrupts to occur until it saw an EOI for the current interrupt. static void interrupt (*oldint9h)(void) = NULL; static_attributes |= MOD_CTRL; static u16 static_attributes = 0; break; But I couldn’t make the debugger con- case ky_ALT : tinue to the point of the EOI until I BOOLEAN initialize_keyboard (void) static_attributes |= MOD_ALT; could use the keyboard again (a frustrat- { break; if (!oldint9h) case ky_LEFT_SHIFT : ing catch-22). { case ky_RIGHT_SHIFT : After the interrupt has been oldint9h = getvect (KBD_INT); static_attributes |= acknowledged, the scan code is checked setvect (KBD_INT, keyboard_events); MOD_SHIFT; return True; break; against the extended scan code. If it } default : matches, we record the fact and exit the return False; break; handler, knowing that the next interrupt } } } we receive will contain the actual scan void release_keyboard (void) else code. { { If the scan code isn’t the extended if (oldint9h) // key release modifiers { switch (scancode) { scan code, the handler determines the setvect (KBD_INT, oldint9h); case ky_CTRL : event type (key_down or key_up) and the oldint9h = NULL; static_attributes &= state of the special keys (shift, ctrl, and } ~MOD_CTRL; } break; alt). This is all the information we need case ky_ALT : to generate an event. But we only gener- static void interrupt keyboard_events (void) static_attributes &= ~MOD_ALT; ate an event if the current event type { break; register u8 scancode; case ky_LEFT_SHIFT : matches one of the currently enabled u16 evt_mask = 0; case ky_RIGHT_SHIFT : event types. static_attributes &= If the input queue manager is scancode = inp (KEYBOARD_PORT); ~MOD_SHIFT; // acknowledge_interrupt break; enabled for the event that’s been outp (PIC_REGISTER, NONSPECIFIC_EOI); default : received, an event structure is allocated break; with a call to allocate_event(), and the if (scancode == EXTENDED_SCAN_CODE) } { } keyboard attributes and data fields are static_attributes |= MOD_EXTENDED; filled in. If the input queue is full (all return; if (events_enabled & evt_mask) events have been allocated, but the user } { EVENT *event = allocate_event (); hasn’t dequeued any at the time we want // assume key down event, modify if we to post an event), we increment the // find out otherwise. if (event) lost_input variable. This variable isn’t evt_mask |= key_down; { if (scancode & 0x80) event->event_mask = evt_mask; currently looked at anywhere, but I fig- evt_mask <<= 1; event->data.value = scancode; ured that eventually, it might be a useful event->attributes.value = debugging aid. // clear key up flag in scancode static_attributes; scancode &= 0x7F; } else Where We’re Going if (evt_mask & key_down) ++lost_input; That does it for now. We’ve covered the { } // key press modifiers basic input queue manager, the event switch (scancode) { static_attributes &= ~MOD_EXTENDED; structures, and how keyboards events are case ky_CTRL : } actually generated. Next time, we’ll dis- cuss user-defined events, timer events,

28 GAME DEVELOPER • OCTOBER/NOVEMBER 1995 Figure 4. Keyboard Event Structure

31 0

Timestamp keyup keydown alt ctrl shift extended

Scan Code

and mouse events. We’ll also go into the do have an e-mail address, I’d be happy to mouse cursor interface, which will allow us e-mail a uuencoded version of the sources to self-draw the mouse cursor in any video to you. Just send mail to inpq- mode. [email protected] with a subject line of Because of space limitations inherent “inpq source.” ■ in a magazine format, there is no way that the entire source for the input queue man- ager could be included with this article. Further, most of the comments were stripped out of the source that is included. Therefore, I encourage you to download Mike Michaels is a senior software engi- the full source from the Game Developer ftp neer with Irvine Compiler Corp., a small site (ftp://ftp.mfi.com/pub/gamedev/ company that produces ADA compilers. Yes, he src) or on CompuServe SDFORUM’s realizes that Ada is dead. No, he doesn’t write Game Developer library. If you don’t have games in Ada. You can contact him via e-mail access to either of these resources, but you at [email protected]. REFERENCES

Algorithms, Second Edition; Robert Sedgewick, Addison Wesley, 1988 This book provided a wealth of information when I was trying to remember how priority queues worked—that data structures class was a long, long time ago!

PC Game Programmers Encyclopedia v. 1.0a; Mark Feldman, et. al. 1994 The PCGPE—is a collection of text files and a viewer program—can be located at ftp://x2ftp.oulu.fi:/pub/msdos/programming/gpe/. The viewer and some of the text files were written by Feldman. Other files were garnered from sources throughout the Internet. Topics range from basic tutorials on assembly language to algorithmic explanations of texture mapping. I found this to be an indespensible guide when it came time to write the keyboard and timer interrupt handlers.

Advanced MS-DOS Programming, Second Edition; Ray Duncan, Microsoft Press, 1988 This volume’s reference section lists all the BIOS and MS-DOS interrupt functions you are likely to need. While the text glosses over mouse programming, the reference guide lists all the mouse driver functions.

Microsoft Macro Assembler Reference v. 6.0; Microsoft Corp. I used this volume to create the scan code to ASCII translation tables used by the INPQ_ascii() routine.

GAME DEVELOPER • OCTOBER/NOVEMBER 1995 29 XSPLAT

XSplat: A Foundation for Cross-Platform Development

Figure 1. The XSplat generic game architecture

Handle Input Win32 Macintosh PeekMessage WaitNextEvent

XSplatWindowProc Event Handlers

Generic Game Loop CXSplatWindow

COffscreenBuffer Simulate Render

he UC Theatre in Berkeley, ovation for his passionate performance as Calif. plays kung fu double fea- conductor of a major orchestra, and the tures every Thursday. Recently, I musician brother helps to defeat the had the pleasure of catching forces of a major organized crime boss. Twin Dragons, in which Jackie All the while, Jackie Chan pulls off plen- Chan plays twin brothers acci- ty of the insane gymnastic stunts that dentally separated at birth in make his movies so painfully enjoyable. Hong Kong. One moves to the The “twins separated at birth” plot TU.S. with his wealthy parents, studies at is awfully predictable. Every few years we posh New York private schools, and see a new interpretation. I’m quite sure grows up to be an acclaimed piano player you’ve caught a few choice scenes from with very refined tastes. The other is the 90s courtroom remake, Apple vs. found by a prostitute and grows up on Microsoft. the streets of Hong Kong to become an expert fighter. Neither brother knows of Separated at Birth the other’s existence. I would gladly bet that for the last several Eventually, of course, the brothers years, Microsoft and Apple have meet. They become fast friends and learn employed more lawyers, clerks, judges, to take advantage of their identical witnesses, paralegals, stenographers, and appearances for maximum comic effect. pizza makers while arguing over their The kung fu master receives a standing claims and counterclaims than there are

30 GAME DEVELOPER • OCTOBER/NOVEMBER 1995 Jon Blossom middle managers at IBM. The Battle of system, throw in a 98% combined market the Operating Systems rages between the share, and almost any game programmer twin children of Xerox PARC as they will be in heaven. struggle over the look and feel of modern In this five-part series, I’ll explore With operating GUI computing. and develop a simple, reusable, platform- As the distance between Apple and independent, GUI-based game library Microsoft shrinks, each begins to exert a for the Macintosh and Windows that strange influence on the other. Windows gives us something close to this ideal sys- systems becoming 95 introduces an integrated desktop tem. For historical reasons, I’ve chosen to metaphor, complete with a drag-and- call this small library XSplat. drop recycling bin, plug-and-play, long This is actually the second article of filenames, and shortcuts to programs and the series, which I began with “A C++ documents. In Copland (MacOS 96?), Class for Cross-Platform Double increasingly similar, Apple promises a true kernel-based oper- Buffered Graphics” (June/July 1995). ating system with a device driver layer, Subsequent articles will continue to build protected memory, and user interface ele- on each other until we have created a ments such as the ability to minimize full-fledged playable game whose core complete cross-plat- windows or gather them in “trays” at the code has been written for XSplat and bottom of the screen. compiles for Macintosh and Windows All this belies the secret truth it’s with no additional effort. The game taken hundreds of lawyers to obscure: won’t be a cutting-edge mega-technolog- form development is Windows and the Macintosh operating ical hit, but it will be a full-featured one system are identical twins, born of the that (I have high hopes) will be fun and same womb and separated at birth. playable. QuickTime, meet Video for Windows. Developing a complete cross-plat- essential. In the first QuickDraw, GDI. OpenDoc, OLE. form game library involves a lot of code. Component, DLL. PPC, DDE. What’s Generally, there will be more code asso- the ultimate difference? As programmers, ciated with these articles than we can we can create a single lollipop that will print here, but you’ll find complete of a five-part series, make both twin babies happy. source code and compiled executables on I’m being naive on purpose to make the Game Developer ftp site, a point: games generally bypass a large ftp://ftp.mfi.com/pub/gamedev/src/, in portion of the operating system in favor the /xsplat directory. All code will com- of algorithms specific to their application. pile on the Macintosh using Metrowerks Jon Blossom lays the Show me a system capable of displaying C/C++ and on Windows using Microsoft 256-color palletized images and handling Visual C++ v. 2.1. user input via a mouse and keyboard, and I’ll show you an ideal platform for today’s The System Speaks groundwork with a games and most of tomorrow’s. Tack on a The most significant feature of any stan- rich set of standard system services such dard Windows/Mac application is a loop as event management, a 32-bit flat mem- that waits for the operating system to ory model, a heap manager, and a file I/O percolate events up from the user. When library called XSPLAT.

GAME DEVELOPER • OCTOBER/NOVEMBER 1995 31 XSPLAT

Listing 1. XSplat Windowing and Messaging System Declaration windows as the user performs actions on them. The application just creates a win- class CXSplatWindow dow, calls on the system to process events, { handles game logic, and renders until the public: program terminates. Our goal today is to CXSplatWindow(char const *Caption, int WindowWidth=0, encapsulate all this in a platform-inde- int WindowHeight=0, unsigned char const *Colors = 0); ~CXSplatWindow(void); pendent game loop that looks like this:

void Activate(void); void XSplatMain(void) void Deactivate(void); { void Terminate(void); // Create a 320x240 window with the title “XSplat Window” void KeyDown(char Key, int RepeatCount); CXSplatWindow *pWindow = new CXSplatWindow(“XSplatWindow”, 320, void MouseDown(int x, int y); 240); void MouseUp(int x, int y); void MouseMove(int x, int y); if (pWindow) COffscreenBuffer *GetOffscreenBuffer(void) { { return pOffscreenBuffer; }; while (GameIsRunning) { protected: DispatchEvents(); int IsActiveFlag; DoGameLogic(); int MouseDownFlag; Render(); COffscreenBuffer *pOffscreenBuffer; } #if defined(_WINDOWS) // Windows implementation details // Get rid of the window for good delete pWindow; HPALETTE Palette; } HWND Window; }

friend LONG PASCAL XSplatWindowProc(HWND, UINT, WPARAM, LPARAM); All the logic of the user interface takes place in the CXSplatWindow object. #elif defined(_MACINTOSH) DispatchEvents translates messages from // Macintosh implementation details the system into CXSplatWindow methods, WindowPtr Window; DoGameLogic controls the actual flow of the PaletteHandle WinPalette; non-event-based portions of the game, if any, and Render displays the current game friend void HandleMouseDown(EventRecord *pEvent); state on the screen. Figure 1 shows this platform-independent architecture. #endif Event notification differs signifi- }; cantly between the Macintosh and Win- dows systems. The Macintosh pours all // This is the XSplat message processing system void DispatchEvents(void); its messages through one pipe, requiring that the application filter and distribute // This is the XSplat main function, called from WinMain or messages at the receiving end of the pipe. // from main after platform-specific setup In Windows, the message filter sits at the void XSplatMain(void); sending end of the pipe, and the system eases the burden by passing messages out to individual windows instead of to the controlling application. the loop sees a message it cares about, it All we have to do is plug in to the appro- Big deal. CXSplatWindow provides an calls out to some other set of functions to priate queue. adapter that plugs onto either end and process the event, then returns its ear to On both systems, windows provide redirects messages however we want the queue and continues until the user the atomic units of graphical applications, them. This funnel adapter can even han- exits the application. Pretty basic stuff. so most messages are tied to individual dle many system messages itself, passing

32 GAME DEVELOPER • OCTOBER/NOVEMBER 1995 Listing 2. CXSplatWindow Constructor for Win32

// This will be used as the window class name for CXSplatWindows SetWindowLong(Window, GWL_USERDATA, (long)this); static char XSplatWindowClassName[] = “XSPLAT”; // TODO: Set up the palette from the given Colors // This will be the window style of all new created CXSplatWindows // TODO: We’ll do this in a later article... // For now, don’t allow the window to be resized // For now, create a gray wash static const DWORD XSplatWindowStyle = struct WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME; { WORD Version; CXSplatWindow::CXSplatWindow(const char *Caption, int WindowWidth, WORD NumberOfEntries; int WindowHeight, unsigned char const *Colors) : PALETTEENTRY aEntries[256]; Palette(0), Window(0), pOffscreenBuffer(0), } LogPalette = IsActiveFlag(0), MouseDownFlag(0) { { 0x300, // Palette version extern HINSTANCE ghInstance; 256 // Number of colors assert(ghInstance); };

// Register the XSplatWindow class if it isn’t already registered for (int i=0; i<256; ++i) int Success = 1; { WNDCLASS ClassInfo; LogPalette.aEntries[i].peRed = if (!GetClassInfo(ghInstance, XSplatWindowClassName, &ClassInfo)) LogPalette.aEntries[i].peGreen = { LogPalette.aEntries[i].peBlue = i; ClassInfo.hCursor = LoadCursor(0, IDC_ARROW); ClassInfo.hIcon = LoadIcon(ghInstance, IDI_APPLICATION); LogPalette.aEntries[i].peFlags = 0; ClassInfo.lpszMenuName = 0; } ClassInfo.lpszClassName = XSplatWindowClassName; Palette = CreatePalette((LOGPALETTE *)&LogPalette); ClassInfo.hbrBackground = HBRUSH(GetStockObject(WHITE_BRUSH)); ClassInfo.hInstance = ghInstance; // Initialize the Window’s DC as necessary ClassInfo.style = CS_VREDRAW | CS_HREDRAW | CS_OWNDC; // Since it’s a CS_OWNDC, these settings will last ClassInfo.lpfnWndProc = WNDPROC(XSplatWindowProc); // forever ClassInfo.cbWndExtra = 0; HDC hdc = GetDC(Window); ClassInfo.cbClsExtra = 0; if (hdc) { Success = RegisterClass(&ClassInfo); SetMapMode(hdc, MM_TEXT); } SelectPalette(hdc, Palette, FALSE); RealizePalette(hdc); if (Success) { // This isn’t really necessary, but... // Determine screen dimensions ReleaseDC(Window, hdc); int ScreenWidth = GetSystemMetrics(SM_CXSCREEN); } int ScreenHeight = GetSystemMetrics(SM_CYSCREEN); // All ready to go, show the window // Given a zero or negative dimension, fill the screen ShowWindow(Window, SW_NORMAL); if (WindowWidth <= 0) WindowWidth = ScreenWidth; // Set up the offscreen environment. Note that because if (WindowHeight <= 0) // of the way COffscreenBuffer was originally defined, WindowHeight = ScreenHeight; // the window must be visible and active assert(GetActiveWindow() == Window); // Determine the window size for the requested client area pOffscreenBuffer = new COffscreenBuffer; RECT WindowRect = { 0, 0, WindowWidth, WindowHeight }; } AdjustWindowRect(&WindowRect, XSplatWindowStyle, 0); } } // Make sure it’s not larger than the screen int WindowWidth = WindowRect.right - WindowRect.left; if (WindowWidth > ScreenWidth) WindowWidth = ScreenWidth;

int WindowHeight = WindowRect.bottom - WindowRect.top; along only a functions you may want to virtualize. I’ve if (WindowHeight > ScreenHeight) WindowHeight = ScreenHeight; handful of combined Macintosh and 32-bit Win- // Create the window centered on the screen important dows declarations using the preprocessor int Left = (ScreenWidth - WindowWidth) / 2; events such as symbols _WINDOWS and _MACINTOSH, defined int Top = (ScreenHeight - WindowHeight) / 2; keyboard and elsewhere. There’s also a friend called Window = CreateWindow(XSplatWindowClassName, Caption, mouse input. XSplatWindowProc required by the Win32 XSplatWindowStyle, Left, Top, WindowWidth, WindowHeight, Listing 1 CXSplatWindow implementation, for reasons 0, 0, ghInstance, 0); shows a decla- I’ll describe shortly. if (Window) ration of a min- { imal CXSplatWin- Constructing Windows // Store a pointer back to this object in GWL_USERDATA dow interface, A CXSplatWindow is more than just a mes- whose member sage filter. It’s the atomic unit of the

GAME DEVELOPER • OCTOBER/NOVEMBER 1995 33 XSPLAT

XSplat interface, the atomic unit of Listing 3. CXSplat Window Constructor for Macintosh either target system. It’s a visible, interactive piece of the user interface. CXSplatWindow::CXSplatWindow(const char *Caption, } int WindowWidth, The CXSplatWindow constructor int WindowHeight, unsigned char const *Colors) : // Initialize the Window creates a window using platform- WinPalette(0), Window(0), pOffscreenBuffer(0), SetPort(Window); specific system functions. We pass IsActiveFlag(0), MouseDownFlag(0) ForeColor(blackColor); { BackColor(whiteColor); along a title, an optional height and // Determine the screen size PenNormal(); width, and an optional array of 768 int ScreenWidth = qd.screenBits.bounds.right - bytes describing 256 RGB triplets qd.screenBits.bounds.left; // Set up the offscreen int ScreenHeight = qd.screenBits.bounds.bottom - // environment. Note that because (XSplat assumes an 8-bit palletized qd.screenBits.bounds.top; // of the way COffscreenBuffer was display, and we’ll deal with palettes // originally defined, the window in a future article). If height or width // Given a zero or negative dimension, fill the // must be visible and active // screen pOffscreenBuffer = new COffscreen- is omitted, zero, or negative, the if (WindowWidth <= 0) Buffer; constructor creates a window that WindowWidth = ScreenWidth; } fills the screen in the omitted dimen- if (WindowHeight <= 0) } WindowHeight = ScreenHeight; sion. No value or a null pointer for ColorArray tells the constructor to use // Create the window centered on the screen a default gray wash for the window. int Left = (ScreenWidth - WindowWidth) / 2; messaging mechanism, a little later. int Top = (ScreenHeight - WindowHeight) / 2; So the command: The XSPLAT window class uses the Rect WindowRect = {Top, Left, CS_OWNDC style; changes to the window’s CXSplatWindow *pWindow = new Top + WindowHeight, Left + WindowWidth}; device context will remain across CXSplatWindow(“Testing”); // Convert the caption to a pascal string GetDC/ReleaseDC calls. On Windows 3.1 // TODO: This doesn’t do well with non-ASCII (and on Win32s), CS_OWNDC windows eat will create a full-screen window // character sets... up precious system resources, but it’s well char unsigned PascalCaption[256]; with a gray wash and the title “Test- strcpy((char *)&PascalCaption[1], Caption); worth it for this type of application. On ing.” PascalCaption[0] = (char unsigned)strlen(Cap- Windows NT and Windows 95, there’s We’ll want the window to tion); not really anything to worry about. display an image of our own cre- // Create a new color document window with these Once it has a window to work with, ation, so we’ll create a COffscreen- // dimensions the Windows constructor creates a gray Buffer for every CXSplatWindow Window = NewCWindow(0, &WindowRect, PascalCap wash palette using a fake LOGPALETTE tion, TRUE, noGrowDocProc, WindowPtr(-1), using the code introduced in my TRUE, 0); structure on the stack, initializes the win- last article (to access this code, go if (Window) dow’s device context, and creates an asso- to the Game Developer ftp site at { ciated COffscreenBuffer. When the win- // Store a pointer back to the window ftp://ftp.mfi.com/gdmag/src.). SetWRefCon(Window, (long)this); dow is ready, it’s revealed to the user. The CXSplatWindow constructors The Macintosh CXSplatWindow con- are very simple and hide the nuances // TODO: Set up the palette from the given structor does almost exactly the same // Colors of each platform from XSplat appli- // TODO: We’ll do this in a later article... thing. It creates a window with the given cations. Listing 2 shows the 32-bit // For now, create a gray wash dimensions using the system call NewCWin- Windows version, and Listing 3 // Create a palette for the window and make dow, creates a gray wash palette associated // sure it will give a 1:1 color mapping shows the Macintosh version. Let’s // with pmExplicit | pmAnimated. with the window, initializes the drawing take a look at what’s going on in WinPalette = NewPalette(256, 0, pmExplicit | system for the window, and attaches a each. pmAnimated, 0); COffscreenBuffer object. if (WinPalette) In Windows, every window { The CXSplatWindow object holds a requires an associated window class, for (int i=0; i<256; ++i) system-specific reference to the associat- and every window class requires an { ed system-specific window (HWND on RGBColor rgb; associated window procedure. Before rgb.red = i << 8; Windows and WindowPtr on Macintosh). creating a window, the constructor rgb.green = rgb.red; However, the message dispatching func- makes sure the window class XSPLAT rgb.blue = rgb.red; tions need to associate a CXSplatWindow has been registered for a window SetEntryColor(WinPalette, i, &rgb); object with a system-specific window. To based on the XSplatWindowProc proce- } do this, we’ll take advantage of functions dure. This requires an instance han- SetEntryUsage(WinPalette, 0, pmExplicit on both platforms that attach 32 bits of | pmAnimated, 0); dle, provided through a global ghIn- SetEntryUsage(WinPalette, 255, pmExplic- application-specific data to any window. stance handle that we set up in Win- it | pmAnimated, 0); On Windows, the API to do this is Main. We’ll take a look at XSplatWin- SetPalette(Window, WinPalette, FALSE); SetWindowLong. Its Macintosh twin is dowProc, a target for the Windows SetWRefCon. XSplat uses these 32 bits to

34 GAME DEVELOPER • OCTOBER/NOVEMBER 1995 store a pointer back to a CXSplatWindow Listing 4. Some of the Win Dispatch Architecture (Con’t. on p. 36) object, used by DispatchEvents to reroute system-specific messages to more general // Windows DispatchEvents is very minimal. It just forces the CXSplatWindow // system to pass events on to XSplatWindowProc controllers. void DispatchEvents(void) Two other member variables, IsAc- { tiveFlag and MouseDownFlag need some MSG Message;

examination. These variables (I’ve made while (PeekMessage(&Message, 0, 0, 0, PM_REMOVE)) them individual integers for simplicity) { will store information received as a result TranslateMessage(&Message); MouseDown/MouseUp Activate/Deacti- DispatchMessage(&Message); of and } vate events. There’s a lot more to cover } before we can address those functions, LONG PASCAL XSplatWindowProc(HWND Window, UINT Message, WPARAM wParam, LPARAM lParam) though. Specifically, we need a message { switchboard to translate system events // You don’t really need the pXSplatWindow to process all into calls to CXSplatWindow objects. // messages — most just go through DefWindowProc anyway. If you’re // concerned, move this into the messages that really care. CXSplatWindow *pXSplatWindow = Passing Messages (CXSplatWindow *)GetWindowLong(Window, GWL_USERDATA); The XSplat function DispatchEvents han- switch(Message) dles the polling and processing of system { messages. To build a single-window case WM_ACTIVATE: application, just create a CXSplatWindow // Re-realize the palette on activate DispatchEvents if (pXSplatWindow) object, and will do the rest. { On Windows, DispatchEvents per- HDC hdc = GetDC(Window); forms a standard PeekMessage/Trans- SelectPalette(hdc, pXSplatWindow->Palette, FALSE); ReleaseDC(Window, hdc); lateMessage/DispatchMessage dance. The real event filtering occurs in the // Let the CXSplatWindow know that it’s becoming active XSplatWindowProc procedure, the recipient if (LOWORD(wParam) != WA_INACTIVE) XSPLAT pXSplatWindow->Activate(); of all messages dispatched to win- else dows this way. When a message of inter- pXSplatWindow->Deactivate(); est enters XSplatWindowProc, the procedure } break; passes it on to the CXSplatWindow object associated with the target window. case WM_CHAR: Unless some evil application has mucked pXSplatWindow->KeyDown((char)wParam, LOWORD(lParam)); break; with the window’s instance data, we can be sure of finding a valid pointer to the case WM_CLOSE: CXSplatWindow object using GetWindowLong. case WM_DESTROY: DispatchEvents if (pXSplatWindow) On the Macintosh, { calls WaitNextEvent to look for events. As // Avoid a bad delete/WM_DESTROY loop long as it finds events in the queue, it // Reset the pointer back here to avoid confusion pXSplatWindow->Window = 0; either handles these according to some } default behavior or passes them on to the GameRunningFlag = 0; appropriate CXSplatWindow, retrieved break; GetWRefCon through a call. This is exactly case WM_LBUTTONDOWN: the same process as DispatchEvents under SetCapture(Window); Windows, without the XSplatWindowProc if (pXSplatWindow) pXSplatWindow->MouseDown(LOWORD(lParam), HIWORD(lParam)); step in the middle. break; The Macintosh XSplat switchboard has to be smarter than its Windows case WM_LBUTTONUP: ReleaseCapture(); counterpart because the operating system if (pXSplatWindow) doesn’t always associate events with win- pXSplatWindow->MouseUp(LOWORD(lParam), HIWORD(lParam)); dows. In particular, there’s no real sup- break;

port for the promised mouse movement case WM_MOUSEMOVE: events. WaitNextEvent can tell you when if (pXSplatWindow) the mouse leaves a given region, but we pXSplatWindow->MouseMove(LOWORD(lParam), HIWORD(lParam)); want to know whenever and wherever the

GAME DEVELOPER • OCTOBER/NOVEMBER 1995 35 XSPLAT

Listing 4. Some of the Win Dispatch Architecture (Con’t. from p. 35) events to that CXSplatWindow, resetting the capture pointer on the next mouse up break; event. If there’s no window receiving the case WM_PAINT: capture, DispatchEvents will pass the // Since we’ve always got a complete picture of the game around events on to the CXSplatWindow associated // in the offscreen buffer, default paint handling poses no with the front window. // problem: copy the offscreen buffer, if any, to the screen On both platforms, DispatchEvents PAINTSTRUCT Paint; provides default behaviors for application HDC hdc; activation and deactivation, window hdc = BeginPaint(Window, &Paint); dragging and resizing, and basic system if (pXSplatWindow) events. It also handles window updates { by copying the COffscreenBuffer to the COffscreenBuffer *pOffscreen = pXSplatWindow->pOffscreenBuffer; screen through standard methods. All the if (pOffscreen) application has to deal with are the { aspects that make it unique: responding // Lock and Unlock are no-ops on Windows, but include // them for good measure. to the mouse, pausing the game on deac- pOffscreen->Lock(); tivation, and constructing the off-screen pOffscreen->SwapBuffer(); image. pOffscreen->Unlock(); } Unfortunately, there’s not enough } space here to print or describe the two systems in their entirety. To give you the EndPaint(Window, &Paint); break; general feel for what’s going on, Listings 3 and 4 show important pieces of the case WM_PALETTECHANGED: XSplat event management functions for // Ignore palette changed messages for this window if ((HWND)wParam == Window) both platforms. Events coming through break; system-specific channels such as WM_LBUT- TONDOWN or mouseDown notifications are // For others, re-realize palette case WM_QUERYNEWPALETTE: turned into calls to CXSplatWindow objects, if (pXSplatWindow) which don’t have to worry about the { source of the message. Again, complete HDC hdc = GetDC(Window); source code is available on ftp.mfi.com. SelectPalette(hdc, pXSplatWindow->Palette, FALSE); BOOL Redraw = RealizePalette(hdc); A Complete Application ReleaseDC(Window, hdc); Let’s create a complete application using if (Redraw) the XSplat system so far, just to prove InvalidateRect(Window, 0, FALSE); that it can actually be done. Since all the return Redraw; messaging, window creation, and double } buffering has been set up, we only have break; to implement XSplatMain, Windows Win- } Main, and Macintosh main, then we need to return DefWindowProc(Window, Message, wParam, lParam); implement a CXSplatWindow object. } We need a simple application that’s going to use the architecture so far. How about a rudimentary paint program? This mouse changes position anywhere on the subsequent mouse events go to that win- application will present a small (320-by- screen, relative to the origin of the win- dow, then release the capture when the 240) window filled with the colors of the dow’s content region. So every time it button comes up. Because the Macin- palette (a gray wash, for now). As you goes through the loop, DispatchEvents tosh version generates its own mouse click the mouse button and drag the cur- checks the current mouse position and events, it also must keep its own capture sor across the window, the application passes it through to the appropriate CXS- information. will invert the pixels it touches, and you’ll platWindow if it has changed. When a mouse down event occurs be able to draw simple pictures. But how do we decide on an in the content region of a window, Han- First, we’ll need an XSplatMain that “appropriate window?” In Windows, we dleMouseDown stores a pointer to the CXS- sets up a CXSplatWindow and fills it with a can capture the mouse using SetCapture platWindow receiving the event. Dis- cycle of palette colors before entering a after a mouse down event so that all patchEvents passes subsequent mouse standard message processing loop.

36 GAME DEVELOPER • OCTOBER/NOVEMBER 1995 XSPLAT

Listing 5. A Very Simple Drawing Application Using XSplat

// GameRunningFlag is reset by the event handling system when pOffscreenBuffer->Lock(); // the user quits the game. int GameRunningFlag = 1; char unsigned *pPixel = pOff- screenBuffer->GetBits() + void XSplatMain(void) pOffscreenBuffer->GetStride() { * y + x; // Create a 320x240 window with no specific colors *pPixel = ~(*pPixel); CXSplatWindow *pWindow = new CXSplatWindow(“XSplat Test Application”, 320, 240); pOffscreenBuffer->SwapBuffer(); pOffscreenBuffer->Unlock(); if (pWindow) } { } // Fill the buffer with the palette in horizontal lines COffscreenBuffer *pBuffer = pWindow->GetOffscreenBuffer(); if (pBuffer) { Second, we’ll need a CXSplatWin- pBuffer->Lock(); dow::MouseMove that flips pixels in response char unsigned *pBits = pBuffer->GetBits(); for (int y=0; yGetHeight(); ++y) to mouse movement whenever the mouse { button is down and the window is active. memset((void *)pBits, y % 256, pBuffer->GetWidth()); The other CXSplatWindow functions set pBits += pBuffer->GetStride(); } mouse down and window activation flags. pBuffer->Unlock(); To keep things easy to implement, we’ll } swap in the whole buffer whenever a pixel // Loop until termination changes. In a real situation, this would be while (GameRunningFlag) ridiculous, but for now all we have is { COffscreen::SwapBuffer, which is good DispatchEvents(); } enough for this little test. Listing 5 shows the implementation delete pWindow; of the platform-independent application. } } See You Real Soon // Here’s where the work is done: any time the mouse moves So far, we have a completely functional // within the buffered area while the window is active, invert a // pixel and redraw the window. (though very simple) drawing application // Of course, swapping in the whole buffer in response to one that runs on several platforms. The code // pixel change is ridiculous, but hey, this is only a test. to implement the specific behavior of the void CXSplatWindow::Activate(void) application used XSplat interfaces to { avoid platform dependencies, and as the IsActiveFlag = 1; XSplat messaging, windowing, and dou- } ble-buffering system grows beneath us, void CXSplatWindow::Deactivate(void) we’ll be able to write more complex { games that should only rarely have to IsActiveFlag = 0; } know what kind of computer they’re run- ning on. void CXSplatWindow::MouseDown(int x, iny) In the next article in this series, we’ll { MouseDownFlag = 1; address palettes and the color zero prob- } lem, take a look at some of the flaws in the design so far, and move on with the void CXSplatWindow::MouseUp(int x, int y) { design and implementation of a real MouseDownFlag = 0; XSplat demo game. See you then! ■ }

void CXSplatWindow::MouseMove(int x, int y) After revealing his employer at the end { of his last article, Jon Blossom left himself if (IsActiveFlag && MouseDownFlag && wide open to an onslaught of vicious head- x >= 0 && y >= 0 && x < pOffscreenBuffer->GetWidth() && hunters and had to fight them off with a y < pOffscreenBuffer->GetHeight()) bamboo cane. You can reach him via e-mail { at [email protected] or through Game Developer magazine.

38 GAME DEVELOPER • OCTOBER/NOVEMBER 1995 THE MODE X-FILES

The Mode X-Files

pril 1, 1995, is a day I’ll remem- held chain saws and shovels. A fresh ber as long as I live. I was up in dent was in the side of the truck. I the gold country of California looked through the driver’s open door for a nice drive. It wasn’t the Sil- and saw a laptop plugged into the AC icon Valley, so it suited me all outlet of a cigerette lighter. I looked up the better. I pulled into an out- at one of the young men. His fearful eyes of-the-way filling station to top met mine, and he looked back at the lap- off my tank—enough to make it top. I could tell by the frightened look Aback to the freeway. It was getting dark, on his face that this was the key to what- and the last thing I wanted to do was to ever happened in the woods. run out of gas on a lonely back road. He watched me as I pulled the com- The station attendant, old and sim- pass off the dashboard of the Blazer and ple, spoke only in binary, answering each held it over the computer. It spun wildly question with “Yep” and “Nope.” I handed with no sense of direction to the magnetic him my 15 bucks for the lousy seven gal- North Pole. Amazing, I thought to myself. lons of gas. That’s when it all happened. This can’t be happening, but it is. These An old topless Chevy Blazer skidded onto were young people, who but just an hour the crumbling asphalt, almost rear-ending ago, believed their young selves to be my car. Three young men leaped out of the immortal. But something changed us all truck. Their faces were stone white like that night. Something I never believed to they had seen a ghost. One of them be true. I was bearing witness to people yanked the passenger side door open, who had actually had a mode X encounter almost pulling the muddy door off its of the first kind. hinges. A young woman lifelessly fell out When I got back that night to my of the seat. She was alive but limp, her eyes house. I immediately searched the internet were open and focused on nothing. for information regarding mode X. There I raced over to lend a hand. “What was hardly any solid information—nothing happened here gentlemen?” I asked as I to attribute to what I saw the previous cushioned her head with my rolled up night. There were documents of others jacket. who had witnessed mode X and a listing of “We’ve seen it...,” the driver spoke talk shows like Montel Williams and Don- frantically. ahue with upcoming episodes featuring “Seen what?” I asked. There was no mode X encounters. answer. The young men stared at each But what interested me the most other, looking for a reason to break some were the references to a certain man— unknown secret. Michael Abrash—supposedly hired to Something was strange about these reverse engineer this alien technology. fellows. I walked around their vehicle, Abrash had published several articles about but saw nothing out of the ordinary. it in credible journals. These allegedly con- There were four yellow helmets in the tained detailed accounts of programming back with California Dept. of Forestry mode X. In some cases, there were refer- insignias on them. The back of the truck ences to using mode X to go beyond two

40 GAME DEVELOPER • OCTOBER/NOVEMBER 1995 Michael J. Norton dimensions at an unearthly speed. stumbled on this mysterious graphics After more exhausting searches, again mode early on, and the most elaborate and all pointing to Michael Abrash, I decided credible witness is Michael Abrash author to rip apart his code and get answers for of these now infamous accounts. Fox‘s new advertising myself. My quest was clear: define the fun- Witnesses describe mode X as having damentals of mode X to use in my graph- a resolution of 320-by-240 pixels, using ics library. So I set forth on my metaphysi- four 64K planes of VGA memory, and cal pilgrimage, a man, his computer, a supporting page flipping. Further, a page theme: Programming bible on graphics, a notebook, a pencil, a of memory in mode X is only 76,800 pixels scientific calculator, and some Cheetos. long. Documents released under the Free- The following is the result of my out- dom of Information Act describe industrial of-assembly-language experience—every- implementations using a page-flipping thing I witnessed, touched, sensed, and scheme in mode X with speeds unprece- for Mode X is cool. discovered in the couple of weeks that I dented by the IBM-supported mode 13h. locked myself in my study, unshaven but This document goes on to indicate that not fasting. I wish to clarify that I was a mode X still has adequate video memory digital religious zealot and not a fanatic. to store a static backdrop and sprites. Cool like us. When I finally cracked open my door and Information released, based on emerged with the answers, my nostrils Michael Abrash’s prophetic testimony, is flared as fresh air from the hall rushed into described herein. Places where IBM trun- the cluttered room. What I discovered cated Abrash’s original text with heavy Here is an account of during this time alone is carefully docu- black markers is carefully pieced together mented. Some of you out there may not for continuity and validity. believe what I have seen. But I don’t care. It’s true. And it really happened to me. Untangling VGA Mode X a close encounter Without further ado, I present an After placing a few phone calls, I located a accurate account of information, unedited, credible eyewitness. I convinced the as it happened, of the strange goings on woman, who requested not to be identi- and alien technology used in setting mode fied, to meet me in a restaurant and discuss with this alien X. Oddly, this information is in no way what she knew about configuring this documented by physicist Bob Lazar and in mysterious mode. I convinced her that I no way relates to accounts of extraterrestri- wasn’t with IBM, and as long as she al technology hidden in the Nevada desert divulged what she knew to me, I would get or the alleged Sport model UFO. The log- it to the public. technology. ical place to start is with a prophetic ques- Fearing for her own safety, she insist- tion, “What is mode X, where did it come ed that she tell me only one of two secrets from, and what is it doing here?” she knew firsthand—the location of the Mode X is an undocumented and alien corpses from the 1947 Roswell inci- (The truth is unsupported video mode that uses VGA dent or solid information on setting mode hardware efficiently in 256 colors. The Air X. I wanted mode X. Her face went white Force, the mysterious men in black, and and, with a shaking hand, she reached into IBM deny its existence. Game developers her purse and pulled out a gold Cross pen. out there).

GAME DEVELOPER • OCTOBER/NOVEMBER 1995 41 THE MODE X-FILES

She glanced over her shoulder, then #define MISC_OUTPUT 0x032c sequencer registers needs to be informed of sketched out the following information on // miscellaneous output register how the memory planes are to be orga- a napkin. She finished in under five min- outpw( SEQC_INDEX, 0x0100 ); nized. In the case of mode X, all four utes and downed her Fresca. Before her // reset, stop the sequencer planes need to be enabled: hasty flight she told me never to contact outpw( MISC_OUTPUT, 0x0e3 ); her again. // select 25MHz dot clock and outpw( SEQC_INDEX, 0x0f02 ); The notes from the napkin are trans- 60Hz scanning rate. // enable writes on all four planes. lated here. It was a very big napkin. outpw( SEQC_INDEX, 0x0300 ); Programming in mode X is tedious // restart the sequencer Why didn’t IBM include mode X as and not as straightforward as mode 13h. a supported video mode? Other video For instance, VGA can be set to mode 13h The last obstacle to tackle in setting modes are plane oriented, like VGA mode by calling the bios function. The function mode X is to write the proper values into 02h. In fact, mode 13h is plane oriented, in Listing 1, written in Watcom C++, sets the CRT controller (CRTC) registers. but its planes are chained to make it appear a video mode by passing it the mode value. Writing to the CRTC registers requires as a linear address space. This feature is a For example: having write privileges. On VGA, bits 6 good one because of the simplicity it offers and 7 of the CRTC register, vertical in programming. However, chained setVideoMode( 0x13 ); retrace end, are used to protect CRTC planes, in this case four planes per pixel, // sets video to mode 13h registers at indexes 0 through 7: throw off the pixel spacing. The resolution is nonsquare, where mode X provides Listing 1. Using Bios to Set #define CRTC_INDEX 0x03d4 square pixel spacing, with one plane per #define VRE_REGISTER 0x11 pixel. The aspect ratio of mode X is 1:1. void setVideoMode( int xmode) outp( CRTC_INDEX, VRE_REGISTER); Why IBM didn’t offer a complimentary { // CRTC write protect bit plane-oriented mode to 13h like mode X is union REGS regs; outp(CRTC_INDEX + 1, (inp(CRTC_INDEX + certainly a mystery. regs.w.ax = xmode; 1) & 0x7f)); Figures 1A and 1B show chained- int386( 0x10, ®s, ®s); // remove write protect plane mode 13h and mode X referencing } pixels. Listing 3 shows hows to set mode X The write protect on various registers with the Watcom 10.0 compiler. of the CRTC has now been lifted, so new Because mode X is unsupported by values for mode X can be set. The values Navigating Mode X bios, you are left with the task of chang- being passed are the parameters Abrash Of course, no information or key witness is ing the video mode. You can achieve this provided in his code, shown in Listing 2. valid without the testimony of an expert. I by setting mode 13h with a call to bios Finally, to wrap up setting mode X, took the original notes and the napkin to a and tweaking the video registers to the the map mask register, index 02h, of the close friend at Stanford University who is desired mode. The first objective is to remove the chain 4 attribute from video mode 13h. This is what makes mode 13h Listing 2. Abrash’s Parameter Values appear to be a single linear plane. The chain 4 mode can be turned off by alter- #define VALUE_VERTICAL_TOTAL 0x00d06 #define VALUE_OVERFLOW 0x03e07 ing bits in the VGA’s sequencer registers. #define VALUE_MAX_SCAN_LINE 0x04109 The reset register, index 1, and the mem- #define VALUE_VERT_RETRACE_START 0x0ea10 ory mode register at index 4 need to be #define VALUE_VERT_RETRACE_LOW 0x0ac11 #define VALUE_VERT_DISPLAY_END 0x0df12 set as follows: #define VALUE_UNDERLINE_LOCATION 0x00014 #define VALUE_VERT_BLANK_START 0x0e715 #define SEQC_INDEX 0x03c4 #define VALUE_VERT_BLANK_END 0x00616 #define VALUE_MODE_CONTROL 0x0e317 // sequence controller index outpw( SEQC_INDEX, 0x0604 ); outpw( CRTC_INDEX, VALUE_VERTICAL_TOTAL ); // vertical total // disable chain 4 mode outpw( CRTC_INDEX, VALUE_OVERFLOW ); // overflow outpw( CRTC_INDEX, VALUE_MAX_SCAN_LINE ); // set for double scan outpw( CRTC_INDEX, VALUE_VERT_RETRACE_START ); // vsync start Abrash recommends setting the clock outpw( CRTC_INDEX, VALUE_VERT_RETRACE_LOW ); // v sync end and protect to 25MHz for fixed-frequency monitors. outpw( CRTC_INDEX, VALUE_VERT_DISPLAY_END ); // vert displayed outpw( CRTC_INDEX, VALUE_UNDERLINE_LOCATION ); // turn off dword mode This involves invoking a synchronous reset outpw( CRTC_INDEX, VALUE_VERT_BLANK_START ); // v vblank start to stop the sequencer, setting the clock to outpw( CRTC_INDEX, VALUE_VERT_BLANK_END ); // v blank end the new scan rate, and restarting the outpw( CRTC_INDEX, VALUE_MODE_CONTROL ); // turn on byte mode sequencer:

42 GAME DEVELOPER • OCTOBER/NOVEMBER 1995 Figure 1a. VGA Mode 13h 64K Linear Bitmap Organization Figure 1b. VGA Mode X Plane Memory Organization

(0,0) (0,0) 320 320 Display A000:0000 A000:0000 1 234

200 240

A000:F8C0 A000:F9FF

(0,199) (319,199) part of the SETI project. He took one look example uses at the notes and sat down in awe. He told DOS4/GW and Pixel 0Pixel 4 Pixel 8 • • • Pixel n-3 Plane 0 me that while I was waiting I could take a C++, so the video peak at some data of what he believed to pointer will not be be signals from an extra-terrestrial civiliza- type far. In protect- Pixel 1Pixel 5 Pixel 9 • • • Pixel n-2 Plane 1 tion. ed mode under I told him I was not in the least bit DOS4/GW, the interested and to concentrate on the infor- video memory is mation I had provided him. By 2:00 the mapped in the same Pixel 2Pixel 6 Pixel 10 • • • Pixel n-1 Plane 2 following morning—and three large segment as the combo pizzas later—we staggered over to code. To set a an XWindows terminal logged into a Cray pointer to VGA Pixel 3Pixel 7 Pixel 11 • • • Pixel n Plane 3 supercomputer. Characters of alien origin memory, be it mode 4 x 64k Planes of Display Memory shot across the screen. What does this all 13h or mode X, we mean I asked myself. My affiliate reached use the following into his drawer and pulled out a wire snippet of code: // write the value to memory hanger wrapped in aluminum foil. He informed me to stick my head out the win- char *pVideoMem; This code is useful if we only write to dow and hold the hanger still on his mark. // pointer to VGA memory, DOS4/GW the first coordinate of the screen, but what “Now,” he yelled as he scrambled to pVideoMem = (char*)(0xA0000); about the tens of thousands of other pixels dump the file to a print queue. // initialize pointer to VGA memory. we wish to set? To plot other pixels, we By now, its obvious that working in will need to understand how video memo- the mysterious mode X is not as simple as DOS4/GW uses physical memory ry is organized. its supported, chained planar video mode addresses; that’s why the pointer was ini- In mode 13h, video memory is 13h is. Plotting pixels in a linear bitmap is tialized to location 0xA0000 and not seg- chained to give it a flat bitmap effect. The easy. But now that the attributes of the ment A000:000. This pointer references the plotting of a pixel in one of the 320 video memory have been modified and its first byte in VGA memory at offset 0. In columns by 200 rows is a simple matter of chained mode disabled, navigating this plain English, this is the first pixel in the arithmetic. For instance, to plot a point, alien mode can be difficult. upper left-hand corner of the screen. The P(109,47), where X=109 and Y=47, we Mode X and mode 13h do share two pixel can be plotted by assigning the loca- simply calculate the scan line and the byte commonalities: that they’re both one byte tion a byte value: offset into the scan line of the location we per pixel and they both have 320 columns wish to plot. per row. The latter is true because mode X char pixelValue; To compute the scanline (or row) is a hybrid video mode of 13h and only the // a value to write to memory from a given point, P, multiply the Y coor- scan lines were reconfigured. pixelValue = 127; dinate times the maximum number of To write a pixel, a pointer must be // assign a value bytes per scanlines. This value will tell us at initialized to reference video memory. This *pVideoMem = pixelValue; what logical row in memory the pixel is

GAME DEVELOPER • OCTOBER/NOVEMBER 1995 43 THE MODE X-FILES

Figure 2. Pointer Arithmetic, Point Coordinates, and Mode 13h scanLine = Y * 80; // mode X scan line pVideoMem = A0000

Display Figure 3 shows the pointer, planes, and points in mode X. Scanlines from This value is useless unless we can pVideoMem tell the VGA which memory plane this = 47 x 320 P(109,47) scan line corresponds to. The plane can bytes Offset = 109 bytes be determined by playing a few games 200 x 320 with the X coordinate using logical bytes operators:

X = 109 = 0x006D = 0000 0000 0110 1101

We want to filter and mask out the X value:

320 bytes X = 109 0000 0000 0110 1101 & MASK = 255 & 0000 0000 1111 1111 located. From this location, the pixel will know to unchained planar memory, such Result= 109 0000 0000 0110 1101 be at an offset X from the start of the row. as mode X. The important thing to keep Sounds a little confusing? Let’s figure an in mind is that mode X scanlines are not The operation is very uneventful and example. 320 bytes per row. Mode X is is in place only to act as a bit filter. We Find the pixel location in VGA unchained—the bytes are organized across want to be certain that we accurately grab memory of P(109,47) in mode 13h: four planes of memory. So a mode X scan the low order eight bits. Actually, the low- line now has (320 bytes/scan line) / 4 order two bits are the only ones of concern scanline = Y *320 = 47 *320 = 15040 = planes = 80 bytes / scan line plane. because these will reveal which memory 0x3AC0 The computation of a scan line in plane this scanline belongs to. Masking out offset = X = 109 = 0x6D mode X, using the example P(109, 47) the plane is also accomplished with a logi- looks like: cal AND operation. pixel memory location = VGA memory ptr + scanline Listing 3. Setting Mode X with the Watcom 10.0 Compiler + offset = A0000 + 0x6D + 0x3AC0 void setModeX(void) = 0xA3B2D { setVideoMode(0x13); // set the bios supported video mode 13h

The code looks like this: outpw( SEQC_INDEX, 0x0604 ); // disable chain 4 mode outpw( SEQC_INDEX, 0x0100 ); // reset, stop the sequencer unsigned int scanLine, offset; scanLine = Y * 320; outp( MISC_OUTPUT, 0xe3 ); // select 25 Mhz dot clock and 60 Hz scan rate outpw( SEQC_INDEX, 0x0300 ); // restart the sequencer // compute the scan line, Y *(BYTES/ROW) outp(CRTC_INDEX, 0x11 ); // vertical retrace end register offset = X; outp(CRTC_INDEX + 1, (inp(CRTC_INDEX + 1) & 0x7f)); // remove write protects // offset of pixel from scanline outpw( CRTC_INDEX, VALUE_VERTICAL_TOTAL ); // vertical total pVideoMem = (char *) (0xA0000) + scanLine outpw( CRTC_INDEX, VALUE_OVERFLOW ); // overflow, bit 8, vert counts + offset; outpw( CRTC_INDEX, VALUE_MAX_SCAN_LINE ); // set for double scan outpw( CRTC_INDEX, VALUE_VERT_RETRACE_START ); // vsync start // ptr to pixel location outpw( CRTC_INDEX, VALUE_VERT_RETRACE_LOW ); // v sync end *pVideoMem = pixelValue; outpw( CRTC_INDEX, VALUE_VERT_DISPLAY_END ); // vert displayed // assign a value to the pixel outpw( CRTC_INDEX, VALUE_UNDERLINE_LOCATION ); // dword mode off outpw( CRTC_INDEX, VALUE_VERT_BLANK_START ); // v vblank start outpw( CRTC_INDEX, VALUE_VERT_BLANK_END ); // v blank end Figure 2 shows the pointer arithmetic outpw( CRTC_INDEX, VALUE_MODE_CONTROL ); // turn on byte mode and point coordinates for this example. outpw( SEQC_INDEX, 0x0f02 ); // enable writes on all four planes Navigating mode 13h is easy. Now with the understanding of how a chained } linear bitmap works, we can apply what we

44 GAME DEVELOPER • OCTOBER/NOVEMBER 1995 Figure 3. Pointers, Planes, Points, and Mode X planes 0 to 3. Setting // set the bit for the pixel’s plane bit 0 allows write outpw( SEQC_INDEX, regs.w.ax ); pVideoMem = A0000 operations by the // set Map Mask to enable write Display CPU to memory plane 0. This behav- The write to the memory plane can Scanlines ior is identical for all now be performed in the familiar manner. from P(109,47) four bits in the Map- The one exception to this operation is that pVideoMem Mask register at index X is no longer a linear mapping. The offset = 47 x 80 Offset = 240 02h. is now at X/4. So: bytes 109/4 = 27 The code for this operation looks scanLineOffset = (y*80) + (x>>2); like this: pVideoMem = (char*)(0xA0000 + scanLineOffset); nPlane = (x & 0x00ff) *pVideoMem = pixelValue; & 0x03; 320 /* calculate the That’s it for writing a pixel to a mode plane from the X X planar memory location. You can now 80 bytes/scanline Plane coordinate*/ port your old mode 13h line drawing and regs.w.ax = 0x0100 + graphics libraries over to mode X. The 0x02; complete listing is shown in Listing 4. Plane 0 /* set the index to By now, both my colleague and I had 02h and initialize had very little sleep but we were eager, bit for pixel’s nonetheless, to test this unearthly technol- plane plane*/ ogy. We loaded up a laptop and drove out = 109 mod 4 Plane 1 to a dry lake bed in the Nevada desert = 1 Pay careful where we executed the following test pro- attention to the last gram. The test was short. We merely pow- 27 bytes line of code. This is ered up mode X, threw out a rectangle on not computing an the laptop’s display, and powered it down. Plane 2 index into the I can attest to the fact that my adrenaline sequence controller. was racing, for we had no idea what mode It’s actually a clever X would do, nor to the destructive power it scheme to initialize was capable of. The test was successful, Plane 3 the AX register before and as UFOlogist Bob Lazar would say, calling the outpw() uneventful. The source code used at the function. The low Nevada test site is shown in Listing 5. byte, AL, is being ini- There are four planes ordered 0 to 3: tialized to the index 02h, the MapMask regis- Copying Linear Bitmaps to ter in the sequence controller. The high VGA Planar Memory PLANE MASK = 3 = 0x03 = 0000 0000 0000 byte, AH, is being initialized to set bit 1 of My scientific colleague made some good 0011 the MapMask register: assumptions about how to use mode X, but it was my radical cyberpunk anarchist Result = 109 0000 0000 0110 1101 regs.w.ax = 0x0100 + 0x02 = 0x0102 = friend who made the most astounding & PLANE MASK= 3 & 0000 0000 0000 0011 0000 0001 0000 0010 contributions. Based on the frequency of Plane = 1 0000 0000 0000 0001 index of Map Mask register = AL = 0000 cattle mutilations per year divided by the 0010 rate that the giant stone heads on Easter This operation tells us which plane of initialize pixel plane to 1 = AH = 0000 Island creep toward the sea, the following display memory we are going to write a 0001 piece to the big puzzle came to light. And value to. The sequence controller MapMask I quote, “This technology could be radical- register needs to be instructed which plane The value in AH is then shifted left ly applied to games, dude!” What revela- this is to allow the CPU to carry out a nPlane times. This value will be loaded into tion had my reality-twisted friend stum- write operation on this memory. The Map- the MapMask register to enable a write to the bled upon? What could this possibly Mask register is an 8-bit register, and only desired plane of video memory: mean? And why did he have a “Linux the four low-order bits, 0 to 3, are of any Inside” sticker stuck to his tower case? significance. Bits 0 to 3 correspond to regs.h.ah = regs.h.ah << nPlane; Plotting pixels is okay for tinkering

GAME DEVELOPER • OCTOBER/NOVEMBER 1995 45 THE MODE X-FILES

with mode X, but let’s face it, we want to in terms of the rectangular boundaries. It is Listing 5. Test Site Source Code get down to some serious business. Games easy to determine physical memory attrib- must have cool backgrounds and sprites, utes of a source image from a descriptive #include #include right? The only way this can be accom- bounding rectangle. Using the example of #include plished is if .PCX and .IFF files can be a Deluxe Paint II image, the physical loaded into display memory. These files attributes of memory can easily be #include “modex.hpp” are typically decoded into memory and are retrieved. This pseudocode calculates the void main() suitable for a display mode like 13h. Now number of bytes per scanline and starting { you’re going to learn how you can expand offset of the image in the chunky bitmap: unsigned int i; your horizons and your graphics library at setModeX(); the same time. The goal here is to write a unsigned int srcBitMapWidth, left, top, useful tool that takes the decoded graphics right, bottom; for (i=0; i<240;i++) { image from linear memory and writes it to srcBitMapWidth = right - left = 320 - 0 writePixelModeX(0,i,0x23); display memory for use with mode X. = 320 writePixelModeX(319,i, 0x23); A good starting point is to identify srcOffset = (srcBitMapWidth * top) } the differences in linear memory and VGA + left = (320 * 0) + 0 = 0 for (i=0;i<320;i++) planar memory. A Deluxe Paint LBM { image in memory is a 320-by-200 pixel These are precisely the values we writePixelModeX(i,0,0x23); writePixelModeX(i,199,0x23); bitmap with 256 colors. Describing this would need if we were to write this bitmap writePixelModeX(i,239,0x23); image in rectangular terms we have: to VGA mode 13h memory. In fact, all } • Left = 0 that’s needed from here is a pointer to getch(); • Right = 320 VGA memory and a pointer to the source setVideoMode(0x03); • Top = 0 image. The entire image could simply be printf(“done... press any key to • Bottom = 200. blasted to the screen with a call to strcpy() continue\n”); } Images are commonly described in because the source is 320 by 200 bytes and terms of logical rectangular coordinates. In so is the destination VGA memory. actuality, they are stored as chunks of That’s basically all we would have to memory, where their scan lines are stored do if we were working in video mode 13h. difference between the source image mem- consecutively, one right after the other. For However, we’re working with mode X. ory and destination video memory is that the programmer and game designer, it is We must now calculate a destination the image is stored in linear memory, and simpler to maintain descriptions of images bitmap based on four memory planes. The the destination is planar video memory. Our task is to devise a means to map a Listing 4. Plotting a Pixel in Mode X chunky bitmap in memory to four VGA memory planes. // plot pixel in mode X The description of the source image #define BYTES_PER_SCAN_LINE 80 in terms of srcBitMapWidth and srcOffset #define MAP_MASK 0x02 #define INITIAL_PLANE_BIT 0x100 needs no further attention. This is all the #define MAP_MASK_PLANE_INIT INITIAL_PLANE_BIT + MAP_MASK information, including the image srcPtr #define BIT_FILTER_MASK 0x00FF itself, we will need to map the chunky #define MAX_PLANE_N 0x03 bitmap. Similar descriptions are needed for void writePixelModeX( unsigned int x, unsigned int y, char pixelValue) the destination memory. The destination { bitmap width for VGA plane memory is union REGS regs; char *pVideoMem; simply the source bitmap width divided char nPlane; among four planes of VGA memory. unsigned int scanLineOffset; With the value of srcBitMapWidth, dest-

nPlane = 0; BitMapWidth and destBitMapOffset can be scanLineOffset = 0; computed as follows:

scanLineOffset = (y*BYTES_PER_SCAN_LINE) + (x>>2); nPlane = (x & BIT_FILTER_MASK) & MAX_PLANE_N; destBitMapWidth = srcBitMapWidth / 4 regs.w.ax = MAP_MASK_PLANE_INIT; // set index 02h, and initialize plane = 1 destBitMapOffset = (destBitMapWidth * regs.h.ah = regs.h.ah << nPlane; top) + (left/4) outpw( SEQC_INDEX, regs.w.ax ); pVideoMem = (char*)(0xA0000 + scanLineOffset); *pVideoMem = pixelValue; If you’re still in the game here, you’ve } determined by now that this information sheds no light as to which plane of memo-

46 GAME DEVELOPER • OCTOBER/NOVEMBER 1995 ry we are to enable CPU writes to. Recall mode 13h blitter. The exception is the In the final ROL operation on the from the earlier discussion of plotting pix- initialization of register bitsROLplane planeMask, the bit in nibble 7 moved left els in mode X that the X coordinate was with the current planeMask value we com- out of the byte and set the CPU CarryFlag. the key in unraveling the mystery of which puted earlier. The code then drops inside In a ROL operation, when the CarryFlag is plane is to be write enabled. Well, the to the nested loop where a miracle set, the bit that rolled out of nibble 7 rolls bitmap doesn’t have an X coordinate occurs RectWidth - 1 times. back into nibble 0. This emulated ROL or...does it? Simple high school geometry Now what’s so miraculous about the operation on the planeMask bits provides a proofs showed that a rectangle can be inner loop? It needs to do several things: simple mechanism for cycling the planes to described by two points: P1(x1, y1) and • Enable the desired plane for a write write enable. The only drawback is that ROL P2(x2, y2). The bounding rectangle of the operation operations are not provided in the C bitmap is defined by two points. They are • Change the plane for the next pixel library function calls or by any binary oper- P1(left, top) and P2(right, bottom). The • Copy the source image pixel to the ators. So we need to write one ourselves: left boundary is what we’ll use to deter- proper VGA plane memory location. mine the initial plane to write enable: The first step is simple and is already unsigned int bitCarryFlag = 0; included in the pseudocode in Listing 6. char bitsROLplane = planeMask; char nPlane, planeMask; The plane, as you already know, is enabled nPlane = left & 0x03; by setting the proper bits in the MapMask The operation of performing a ROL on left = 0000 0000 0000 0000 register of the sequence controller. That’s a byte value can be accomplished by using & mask = 0000 0000 0000 0011 nPlane = 0000 0000 0000 0000 = 0 Listing 6. Pseudocode for a Nested Loop planeMask = 0x11 SHL nPlane = 0001 0001 RectWidth = right - left; << 0 = 0001 0001 nRows = bottom - top;

By performing a logical AND operation outp( SEQC_INDEX, MAP_MASK ); for(rows=0; rows

GAME DEVELOPER • OCTOBER/NOVEMBER 1995 47 THE MODE X-FILES

bitCarryFlag = bitsROLplane & nibbleMask; updating each pixel in the bitmap to the his tongue with no concern to their designated plane of memory from the ROL impending dangerous implications. bitsROLplane = 10001000 operation. “Rock and roll, dude!” he said, and nibbleMask= & 1000 0000 = 0x80 Translating offsets in mode X mem- his finger pressed the enter key. I froze in bitCarryFlag = 1000 0000 = 0x80 ory is simple. Remember that we treated my tracks as the computer set the alien the X coordinate in the earlier examples mode and wrote the bitmap in system Now perform the logical SHL opera- as the offset in memory. The coordinate memory out to the mode X display. My tion on bitsROLplane: X, or the offset, mapped to a VGA plane heart pounded harder and shorter than it at offset X/4. The mapping of a byte from did out in the Nevada desert. I looked at bitsROLplane << 1 = 0001 0000 a chunky bitmap to a defined VGA plane him, my facial expression scorning him for of memory is the same. Translating pixels being so technically irresponsible. The This is the condition we need to test is accomplished by the following code consequences could have been far worse. for to wrap the plane back to zero. When snippet: The thought that raced through my mind the value in bitCarryFlag is equal to 80h, was that mode X could somehow chaoti- the plane will wrap. This test carried out in pVideoMem[scanlines>>2] = cally react with our atmosphere and burn the following manner: SourcePtr[scanlines]; up all the oxygen. But that didn’t happen. “Whoa, dude,” he said. “Check it if (bitCarryFlag == 0x80) The pointer pVideoMem is referencing out—we’re all still here.” His hands wrapPlane; the VGA memory plane, and SourcePtr is pressed firmly against the chest of his tie- referencing the byte to be mapped. The dyed t-shirt. He wanted to make certain What precisely does it mean to wrap index, scanlines, is byte offset in memory that the molecular organization of his body the plane? The value 0x80 is a bit filter to from SourcePtr of the pixel to be mapped: was still structurally intact. He got up from alter the contents in bitsROLplane to fit the his chair and pulled back the curtain. cyclic model. The wrapping around occurs pVideoMem[scanlines>>2] = “Excellent!” he nodded, “The time contin- by performing a logical OR operation on SourcePtr[scanlines]; uum seems to be in order as well.” bitsROLplane: This test somehow alerted the boys Trust No One from Big Blue. A network tap started to bitsROLplane = bitsROLplane | 0x01; Now you have enough information to be set off an alarm. They were getting a fix on bitsROLplane = 0001 0000 dangerous. No longer can the assembly our location. The anarchist cyber dude OR 0000 0001 language listings in Michael Abrash’s The started scrambling his IP octets to confuse bitsROLplane = 0001 0001 Zen of Graphics Programming (Coriolis, the people tracking his network routes and 1994) be of any threat to you. If you don’t hops. That’s the whole tamale for plane have this book yet, buy it! Abrash gave “Until we can finish constructing our wrapping. It’s not so difficult once you’ve away all the industry secrets in one book. They Live VR visors, no one is safe,” he walked through it. The code for perform- As I mentioned, the source code is old, real gravely stated. ing this operation looks like this: mode 8086 code, and requires some atten- Due to space constraints,I couldn’t tion when porting it to Watcom protect- list all the code necessary to this encounter. bitCarryFlag = 0; ed-mode compilers. You can access the complete listing for bitCarryFlag = bitsROLplane & 0x80; As I paced the small apartment of my copying a chunky bitmap to VGA mode X // test for non-zero high order nibble cyberpunk anarchist friend, my deep prob- planar memory and the code used in the bitsROLplane = bitsROLplane << 1; ing meditations were disrupted by a stereo test by the cyber dude on the Game Devel- // set mask for next pixel’s plane tone from the Sound Blaster 16 card of his oper ftp site. if (bitCarryFlag == 0x80) computer, signalling the succesful end of a All I can say for now is, watch out. // if high order nibble non-zero compile. Mode X works—on Windows 95, too, in bitsROLplane = bitsROLplane | 0x01; He had compiled the linear bitmap protected mode. Maybe Redmond is // carry the bit to lowest nibble translator and was ready to execute it. where we’ll find them. You don’t have to Time flowed in slow motion as I ran believe me, see for yourself. Mode X is out The third and final dilemma we need towards him, shouting, “Nooo!” My hands there and it’s in your system. ■ to resolve is mapping linear bitmap bytes reached out to stop his right index finger as to the corresponding VGA planes. The it glided over to tap the enter key on his outer loop controls what row is being keyboard. updated on both the bitmap and the VGA His head turned slowly, grinning like Michael J. Norton holds a B.S. in physics memory. The inner loop is walking the some sinister goblin, his eyes hidden and has worked as a programmer for 12 years. scanlines. This is simply moving to the off- behind round purple reflective lensed He is currently working on a book for pro- set from the start of the scanline and cyberpunk sunglasses. The words rolled off gramming the Windows 95 SDK.

48 GAME DEVELOPER • OCTOBER/NOVEMBER 1995 CHOPPING BLOCK

Gamin’ for Grrrrls

Barbara Hanscome ow do we make games girls when playing with “boy” software. (At and women will buy? It’s a the same time, a common belief exists in question today’s game compa- this industry that girls will play “boys’ How have some game nies must ask themselves or games,” but boys will not play “girls’ lose out on a little more than games”—an attitude that can’t do much half the potential electronic except tilt the scales toward the tastes and entertainment market. Most preferences of males.) game industry executives seem Still, game developers are cautious Hto conclude that if they are to consider about stereotyping and generalizing gen- companies tried to targeting girls and women at all, it will der differences in game preference and not be at the risk of excluding their core play. “If we say ‘girls don’t like violence market of boys and men, which is why so we’ll give them Barbie Dolls’ or ‘ girls games for kids are often touted as being want story, so we won’t let them play “gender inclusive.” But what does gender Sonic the Hedgehog,’ we’re creating par- appeal to girls? Are inclusive mean, exactly, and is it the most adigms of exclusion for both boys and effective way to attract the female mar- girls,” says Margy Gilman, an interactive ket? Game industry pros have conflicting TV producer in San Diego. “It’s just as opinions. discriminating to boys [to give them only Some feel that the games claiming violence] and not to give them story and these games vastly to be gender inclusive really aren’t, and character. There are lots of boys who care that all this talk of gender-inclusivity just about that.” perpetuates serving the needs of a male Creative Wonders is also cautious audience. “Until the day when ‘gender about excluding boys. Its new title Made- neutral’ stops meaning ‘really boys,’ I line and the Magnificent Puppet Show truly believe that we have to create games boasts a famous female heroine chosen for different from those specifically for girls,” says cartoonist her pluckiness and popularity with girls Trina Robbins, who wrote and designed and women from the storybooks of Lud- Sanctuary Woods’ interactive CD-ROM wig Bemelmans. But Creative Wonders Hawaii High, Mystery of the Tiki. says Madeline is not a game “for girls,” If you look at some of the research but a game for children that girls “espe- designed for a more into gender differences, the idea of a gen- cially” will like. Even the Women’s Inter- der inclusive game seems like a diffi- active Entertainment Association has cult—if not impossible—goal to reach. A taken a stance supporting “gender inclu- 1984 edition of Personal Computing sivity” over “gender specificity.” “It’s not Teacher features a study “Situational about making gender specific titles,” Stress as a Consequence of Sex-Stereo- explained WIEA president Valerie Hen- general audience? typed Software,” which states that if you nigan. “It’s about making them ‘gender take games with the characteristics girls friendly’....and it’s marketing those games like and make boys play it, boys show to girls. Interactive entertainment isn’t signs of situational anxiety and their per- even marketed to females.” formance and focus deteriorates. The While the discussion continues And how? study claims girls have the same reaction about how to best reach girls and

GAME DEVELOPER • OCTOBER/NOVEMBER 1995 51 CHOPPING BLOCK

women—be it with an affirmative action Players must help Crystal find seven crys- Crystal’s Pony Tale a “story-driven” approach or with gender inclusive games tal shapes to free her pony pals from cap- game—and research indicates that girls for boys and girls—this installment of tivity. Using the Sega keypad, players help are more interested in story lines than fast Chopping Block takes a look at a few Crystal gallop and leap in the air and action. Crystal’s Pony Tale follows the titles for children and what characteristics capture keys and horseshoes. The horse- same tried-and-true, linear action formula might make them appeal to girls (and in shoes let Crystal move to three other “lev- of any Sega title. The “story” is more akin some cases, to boys as well). els” or environments in the game, and the to a game “theme.” keys let her open treasure chests, where The game is slower paced than your Sega and Nintendo Games crystals and clues can be found. The hid- typical Sega or Nintendo action adven- Much of the research into girls’ game den ponies are marked in the game with a ture. There is no time limit; Crystal can preferences suggests that what many of silhouette of a horse and the shape of a stop and talk to a cow in the barn, the classic arcade, Nintendo, and Sega crystal. If Crystal has the correct rock in munch on hay, or sniff sunflowers with- style games are made of (repetition, scor- tow, the player can move Crystal next to out the threat of being bonked or killed ing, speed, violence, target shooting, time the pony’s shape, press the action key, and by incoming villains. The player also has limits, reinforcement based on skill release Crystal’s friend. a game map of sorts, which enables instead of effort) is unappealing, boring, Crystal must collect the right num- Crystal to move between levels if she and senseless to girls. ber of horseshoes before she can pass into wants to. In 1994, Sega introduced Crystal’s the next environment, but the game isn’t In keeping with research showing Pony Tale, a game designed to attract based on scoring or attaining the next girls are more motivated by sound, voices, girls aged four to seven to the Sega plat- level as much as it is on helping Crystal and music than boys, the game lets girls choose the background music at the WHAT DO GIRLS LIKE? WHAT DO BOYS LIKE? beginning of the game from a number of classical pieces. ndustry and academic research reveal some interesting differences in what many As for violence, well, Crystal isn’t a girls find fun, challenging, and engaging in electornic entertainment. But of course, as exactly Steven Segal, but she does exhibit you’d expect when dealing with human preferences and tastes, contradictions some aggressive behavior. Whenever abound. Still, some of this research is hard to ignore. Here’s a summary of some of the Crystal tries to rescue a friend, the pastel- Ifindings: colored sky turns dark, the music picks up • Females prefer utility, reality, and constructive goals, while males prefer aggressive tempo, and the evil witch swoops onto and competitive themes and are less interested in the far-reaching benefits of a game. the scene. If you continuously press the • Females find sound, especially voices, motivating, while males are often observed play- action key, Crystal rears on her hind legs ing with the sound turned off. and whinnies repeatedly. Without blood- • Females are less interested in “games” (meaning play involving established rules, clear shed or bullets, the witch disappears and objectives, and the notion of winning) than other forms of play. serenity returns. • Females like interactive products without scoring or competition and are less interest- Character involvement is crucial to a ed in winning. Males often mention scoring (and winning) when discussing games. girl’s enjoyment of a game experience, • Females are flexible with rules and will change rules to suit a situation, while males are according to research. But we know very more inflexible with rules. They use rules to determine domination and enjoy following little about this pony. The players’ guide them. tells us that Crystal is the “shyest” pony in • Females are frustrated when having to start over from the beginning of a game. Males find this motivating. the land, but if you don’t read the guide, • Females are more confident, optimistic, and motivated when feedback focuses on effort you miss out on this information (which vs. ability. They see feedback as a reflection of their ability and will question them- might give some players a sense of selves if the feedback is negative. accomplishment after helping Crystal • When feedback is based on effort, males see it as a reflection of poor performance and overcome her shyness and fight the loose motivation. witch). Sega seems to prefer using animals in their gender-inclusive children’s games form. The game was created by Sega’s all- help her friends—a goal a four-year-old instead of humans, perhaps because they women Girls Task Force to be a girl’s first girl might find more worthwhile than are easier to gender-neutralize. In fact, Sega game—and in many ways that is shooting at a target. When Crystal suc- Crystal could be either male or female; exactly what it is (kind of a Sonic the ceeds in doing this, hearts of love and the character is so poorly rendered you Hedgehog with pink training wheels.) gratitude spring forth from her pony can barely see its face. If I were a five- In Crystal’s Pony Tale, Crystal’s friend onto Crystal’s head. It’s corny, but year-old girl and my big brother were friends have been kidnapped by a wicked the positive feedback can’t be ignored. playing a richly animated game like witch and hidden throughout the game. Still, you certainly wouldn’t call Earthworm Jim on our family Sega sys-

52 GAME DEVELOPER • OCTOBER/NOVEMBER 1995 CHOPPING BLOCK

tem, I’d feel a little slighted. that a large number of girls out there game,” and they say girls find this game Some girls, especially girls at the enjoy the fast action, the funny anima- plan more of a turn-off than blood spurt- older end of the game’s age-range, might tions, and three-dimensionally rendered ing from a corpse any day. find the Arthurian castles a bit tired and jungle worlds, and the challenge of attain- Sure, girls can master the game just cliché. According to Sega, they set the as well as boys can, but research suggests story in this environment because that they’ll become bored (or frustrated) research indicated girls like fantasy, but with the experience long before that will does fantasy really mean witches and gob- happen. lins? It’s no wonder many of them Donkey Kong Country 2 coming skedaddle on over to the more exciting out this December, might have more “girl world of Donkey Kong Country—a game appeal.” Girls might find the goal of the popular with both girls and boys. game more “worthwhile”: you’re actually trying to save Donkey Kong from capture Donkey Kong Country by the evil Kremlings. But more impor- Nintendo’s Donkey Kong Country wasn’t tant, it stars a female character (at last, a designed “for girls” or with girls in mind, female saving a male in distress!). This but girls certainly like it. In a nationwide game stars Dixie Kong, Diddy’s little survey of 1,600 kids taken by interactive monkey friend, who sports a pink t-shirt TV producer Margy Gilman, Donkey and a long, platinum-blonde ponytail. Kong was listed in the top five favorite Some might argue that she’s really just games of girls (along with Mario Broth- Diddy in drag, but it’s hard not to smile ers, Sonic the Hedgehog, Lion King, when watching her in action. Face it, and—believe it or not—Mortal Kombat). she’s cute. And she takes the lead The game takes players on a jungle throughout the game, dragging Diddy adventure with two primates: Donkey around and leaping above obstacles, her Kong (a big macho ape) and his friend ponytail whipping about like a propeller. Diddy (a small, agile monkey). Your goal According to a Nintendo spokesperson, is to help them recapture their horde of the company hopes Dixie will be the next bananas, which have been stolen from flagship character for the Nintendo line. them by the evil Kremlings. You help Donkey and Diddy run and jump over Unfortunately, Candy Kong, the only Hawaii High, obstacles, grab ropes and swing through female character in Donkey Kong Country, Mystery of the Tiki the forest, even swim through water serves as little more than a showpiece. Released in 1993, Hawaii High, Mystery worlds capturing bananas, letters (that of the Tiki, from Sanctuary Woods was spell out Donkey Kong of course), and ing points and mastering the levels? one of the first interactive CD-ROMs extra lives. You’ve got to move fast or any What I find disappointing in Don- created specifically for girls aged eight and number of beasts will run you down. key Kong is that the game features only up. Hawaii High is more interactive story When you “lose a life,” you start over one female character: Candy Kong, an than adventure game, in keeping with the from the beginning of the level; if you ape in a hot-pink bathing suit with noth- theory that girls like character and story lose several lives in succession, you “die” ing better to do than pose provocatively in more than fast action. It stars a teenager and start over from the beginning. a booth and help Donkey and Diddy save named Jennifer, who just moved from Going by the research into what their points. Basically, she’s stuck playing New York to Hawaii, where her mom, a girls like in games, there would be little in Vanna White while the guys go out for a working professional, has taken a job. She Donky Kong to get a girl hooked on swingin’ banana hunt. Humph! That’s and her new friend Malaya are on an video gaming. The game isn’t based in not very “gender inclusive.” adventure to return a lost Tiki statue to its reality; nor does it involve a story. (You’re Another gripe: you’re deprived of rightful place. Hot on their trail are two supposed to be helping Donkey and most of the game until you reach a certain dastardly villains (a man and a woman— Diddy regain their hoard of bananas, but level of skill. Donkey Kong contains equally bad) who want the Tiki for its if you don’t read the players guide, you seven environments and 40 levels. Each resale value on the black market. don’t know this or care.) Game feedback level is wonderfully different and features Trina Robbins, who wrote and is based on the number of points you new obstacles and characters, which you’ll designed the game, said she used the score and the level you achieve—which never see unless you play the game well Nancy Drew mysteries for inspiration. research says girls find less motivating enough and score high enough to reach Those stories, said Robbins contain the than feedback based on trying. So why is these levels. Game developers I’ve spoken classic things many girls like. “Girls the game popular with girls? Could it be with call this setup “having to earn the haven’t changed; girls will never change.

54 GAME DEVELOPER • OCTOBER/NOVEMBER 1995 CHOPPING BLOCK

We will always like adventure and prob- to romance. Robbins purposely kept this prose read by National Public Radio’s lem-solving.” Robbins says she created to a minimum. “It’s really the idea of David Sedaris (who was selected for the two female protagonists because girls like romance that they like. At that age, most job, according to Magnet, for his androg- to play together. She also wanted charac- girls think little boys are horrible. Girls ynous voice). In fact, if you were to give it ters that girls could relate to and who are much more interested in interaction edutainment value, it would be that it were a little older than the game’s target between females.” She took care of the shows the beauty and power of words. market. “Nancy Drew was a teenager but romance issue with a scene in which Jen- Duncan said she modeled the game it was much younger girls who read her nifer watches a cute boy play the ukulele after classic children’s literature such as books.” and sing, while little hearts dance over her Maurice Sendak’s Where the Wild Things An assumption game developers Are. She said she didn’t pay much atten- make is that girls like “fantasy,” yet tion to research, but “designed it from the research states they like themes based in REFERENCES heart.” Chop Suey features two sisters: reality. Hawaii High takes both into Lily and June Bugg, who live in Cortland, account. Robbins uses elements of fantasy he gender differences cited in Ohio. In the first scene, all we see of and mythology in the game, but the story this article come from a wide them are their legs and anklette socked takes place in today’s modern world—and range of sources, including feet—one sister’s legs are brown, the published academic and indus- Robbins provides real-life situations her other’s are white. Other than that, we see try research as well as unpub- players can relate to. For example, we little of them throughout the game, since Tlished surveys. If you’d like more begin the game on Jennifer’s first day of information on gender differences, we experience everything from their point school, where she faces a classroom full of here are some excellent sources: of view. The two girls have just gone beachniks in shorts. Dressed in New York • “Gender and the Art of Designing shopping for red licorice at a candy store City urban chic, it’s apparent that she Interactive Media,” by Heidi Dangel- called Cupid’s Treats and eaten chop suey feels like an outcast, as anyone would in maier, Computer Game Developer’s with their father at the Ping Ping Palace. the same situation. Report, Aug. 1995 They’re lounging in the grass, watching Like many interactive stories, Hawaii • “We Have Never Forgetful Flowers clouds pass overhead and trying to make High lets the players move at their own in Our Garden: Girls’ Responses to sense of their shapes. pace and interact with the environment. It Electronic Games,” Maria Klawe, et. Soon, we’re floating above a colorful al.; University of British Columbia, also includes a “story map” that lets the map of Cortland, and the city is our oys- Dept. of Computer Science, Dec. player begin where she left off or “lost” the ter. We can explore the Carnival, Aunt 1993. game. The things that work least well in • “Exploring Common Conceptions Vera’s House, Cupid’s Treats, and several the game, according to Robbins, are a few About Boys and Electronic Games”, other spots on the map. Once we venture puzzles, which were thrown in by male Maria Klawe, et. al., University of forth, we can return to the map with a programmers on her team to “make the British Columbia Department of Com- click of the mouse—we’re not going to game more exciting.” In one, the player puter Science, Jan. 1994. get caught in a puzzle or a maze where we must maneuver the characters through a • “How the other Half Plays,” by Bar- can’t escape. three-dimensional maze—an annoying bara Lanza, Computer Game Develop- The “story” comprises memories and interruption for any player, especially if ers Conference Proceedings, 1994. moments in a few character’s past and pre- she or he is trying to get to the next clue. sent lives. We’re voyeurs sneaking into Robbins says her gut feeling was that girls bedrooms, peaking through windows, and wouldn’t like the puzzles because “they’re head. “Requisite romance taken care of,” rummaging through drawers. In Aunt closer to boys’ twitch games.” explains Robbins, “back to the girls!” Vera’s room, her life unfolds as June Bugg Hawaii High’s dress-up segment was and Lily remember things she’s said criticized in the press, as was the choice of Chop Suey before, embellish her stories, and fantasize pink attire for one of the characters. Rob- Created by writer Theresa Duncan and about her adventures. (And none of these bins calls such criticism “throwing the artist Monica Gesue with the idea of cre- fantasies involves castles or witches, by the baby out with the bath water” in an ating a CD-ROM girls would like (but way.) We discover Miss Vera’s life is “a attempt to avoid sexism. “Girls do wear not a “game for girls,” per se), Chop Suey long series of magical stories, like shiny, pink, and I felt that it was one of the per- from Magnet Interactive and 20th Cen- dull pearls on a long, long, necklace.” Vera sonality traits of this particular girl to wear tury Fox isn’t easy to define. It’s neither is a former Rockette who lived in New lots of pink. The other girl character wears game (no action, no rules, no scoring) nor York City. Click on a picture on the wall bright reds and blues.” As for the dress up interactive story (there’s no beginning, and learn about her three ex-husbands (all game: “Girls like to play dress up. Women middle, or end), nor creative activity pro- named Bob). One Bob lives on the edge do, too!” says Robbins. gram. You might call it an “interactive of town and still sends her “roses of the Women and girl protagonists, espe- poem.” The CD-ROM combines hip, palest pink.” Click on the matchbook cially in preteen adventures, are also prone engaging art, with melodious, alliterative cover on her bureau and go to a nightclub

56 GAME DEVELOPER • OCTOBER/NOVEMBER 1995 CHOPPING BLOCK

sarcastic—but he’s vulnerable, too. It’s clear that Elroy is in a predicament: if he doesn’t catch the beetle by the end of the weekend, he’s going to lose the Insec- tathon and his nemesis, the evil Gordon Smugs, will win. It’s your job as the player to help him. The game features long stretches of animated action with few chances to use the mouse. Eventually, the story stops, Elroy asks you what he should do, and you tell him by clicking your mouse. Your actions usually involve telling Elroy to go one direction or another, exploring ele- ments in a room, or choosing from a list Neither game nor puzzle, but a quiet exploration of a small town from the point of view of of options, but most of them require some two sisters, Chop Suey’s main focus is on interactive video, audio, and storytelling. thinking. You need to use what you learn throughout the game (such as information about insects) to determine Elroy’s—and the wisecracking technoloptera’s—fate. called the Single Spotlight, where a segment, you put eyelashes, sunglasses, Like Chop Suey, Elroy relies on lounge singer performs a sad love song. and a goatee on a woman’s face. story and character as well as its own style Even better, you can snoop in a boy’s Chop Suey isn’t based on scoring or of cornball humor. These elements lean room. In this case it’s Vera’s teenage son, fast action, but on a mood, style, and toward stereotypic “male” preferences, Dooner, a rock and roll musician with a sense of humor that kids and adults can which might turn off some girls. But the motorcycle and a silk aviator scarf, who enjoy. While the gender stereotyped style of play lends itself to a wide range of “used to sing songs about girls named experiences of romance and dress-up characters and story elements—just as Lurlalou, or Pitapat, or just plain Mary.” might sound corny, these elements are Chop Suey does. Adding a female heroine Dooner sits on his bed amidst record handled in a humorous, fun way. It’s clear to Elroy’s story could make the game album covers and bags of “Fame-On!” that Chop Suey appeals to a different more appealing to girls, especially if she’s chips, oblivious that you’re rifling through sense of play—one based on exploration an important character who girls can iden- his bedroom drawers. We find a maga- instead of target shooting and violence, tify with—and not just a “token” show- zine called Girly (which we can’t read) one that delivers entertainment value piece. According to Headbone, Elroy will and his diary (which we can read), reveal- from humor, characters, and the beauty get a female sidekick in the next title. ing an account of his date at the carnival and power of language and art. This Appealing to the huge range of play with a painter named Monica. defies the traditional “game” experience preferences that girls and boys have is a In this game, at the picnic scene that boys seem to like so much, but is this great challenge for game developers, and (where you watch Aunt Vera dance with type of computer play something that one that might seem daunting. However, an Elvis Presley look-alike named Ned), only girls can appreciate? as interactive entertainment designer a lawnmower buzzes in the background, Heidi Dangelmaier puts it, “Gender is not birds sing, wind chimes jingle in the Elroy Goes Bugzerk a burden to bear but an intriguing human breeze, and a dog barks. (If you pick up Elroy Goes Bugzerk from Headbone dynamic that can bring texture and pas- x-ray sunglasses at the bottom of the Interactive is another story-based game sion to interactivity.” To reach girls and screen, you’ll see Vera and her current that boys might enjoy as well as girls—if women with electronic entertainment beau in their underwear!) not more so. The CD-ROM is billed as a (and a new audience of boys and men), it’s For those who enjoy dress-up “comic adventure for kids aged 7 to 97” obvious that game developers need to con- games, you have a few options to choose and features a boy named Elroy and his sider new directions in computer play. from. You can raid Aunt Vera’s closet. dog, Blue, who are hunting for a rare Addressing different preferences and Click on a long, red evening gown and stink-bomb-dropping beetle called the styles in gameplay (be they gender influ- see Aunt Vera lounging languidly in a Technoloptera, in the hopes of winning enced or not) will make those new direc- hammock, a bowl of fruit on her lap. You their city’s annual Insectathon. tions more interesting and exciting—for can also dress up a drooling rambunctious This game’s protagonist is all boy. male and female gamers alike. ■ dog named Mud Pup in loud boxer He loves scatological humor (in one scene shorts, socks, and a shirt, while punk rock he enters a cave of locusts and comes out Barbara Hanscome is managing editor bounces in the background. In another covered in locust poop). He’s loud, he’s of Software Development magazine.

58 GAME DEVELOPER • OCTOBER/NOVEMBER 1995 ARTIST‘S VIEW

Void Pirates on Parade

David Sieks igital entertainment has come a project, and because I like you I’ll let you long way since Pong was able to in on what he had to say: captivate us for hours on end. Developing a visually As the technology has grown, GD: Give us a little background: what is so has the scope of game devel- Diversions Software, what’s your role opment. The digitized faces of there, and what did you have to do with big-name stars have taken the the upcoming Void Pirates? place of humble sprites, story- Dlines are assembled by herds of suits, and IF: Diversions Software was formed in exciting game takes graphics and programming are farmed out 1992, after I created the artwork for Win- to production houses in faraway countries. Fish, a joint venture of 2 Guys Software In such big-stakes projects, Creative Con- (myself and Andrew Novotak). I am based trol is an elusive bird: once the cage door is in Denver and am the sole employee. My opened, off it flutters...and it doesn’t come role is “lone wolf,” and I am responsible more than the right back when you whistle. for every aspect of Void: code, art, anima- But if you’re starting to think “I tion, sound, even ad copy and box design. Did It My Way” is a tune no longer heard in the game industry, you should GD: So what sort of projects has Diver- listen more closely to that humming sions developed? design tools. Hard sound emanating from Ian Firth. Firth is putting the finishing touches on Void IF: I currently have one retail product on Pirates from Diversions Software and the market—Grey Wolf, a WWII U- SofSource Inc. I recently had the chance Boat shooter—and a dozen shareware work, capital, and to ask him some questions about the games and apps, including Prairie Dog actors who‘ll work for

beer are all

New features in Caligari trueSpace2—such as three-dimensional booleans and three-dimen- sionally rendered solid modeling—helped in the creation of this space station built into an part of the mix. asteroid, which players see from the main viewer on the bridge of their ship.

GAME DEVELOPER • OCTOBER/NOVEMBER 1995 61 ARTIST‘S VIEW

Hunt 2-Judgment Day, second place win- I did was a flyby of Durango airport with DLLs, nothing. The interface is also the ner in last year’s Ziff-Davis Shareware AutoCAD 9 in 1989. Just seeing what was finest in the world, making designing so Awards. Two years of shareware develop- possible is what drew me into it, and I much easier. Win95 uses cascading menus, ment have included Prairie Dog Hunt for don’t think it will ever end. something I have hated since AutoCAD Windows and its sequel, Trap Shooting, 2.6. I can’t believe Microsoft would go Fortress, TailGunner, StarGunner, and a GD: There’s a lot of rendered 3D in Void back so far in user interface design. Anoth- few applications including StarTex, a star Pirates, which I understand was done with er reason for using trueSpace is the pletho- texture generator for rendering programs. Caligari trueSpace. With your CAD back- ra of game companies touting “3D Studio- ground, how did you settle on that tool? Rendered Everything!!!” I want to be able GD: What’s the connection with Sof- to say “Not a single thing was rendered in Source? IF: My background in 3D includes 3D 3DS, and not a single actor you will recog- Studio, Macromodel, AutoCAD AME, nize!” My actors are all people from a bar IF: SofSource licensed rights to a few of and anything else I could get my hands on. downtown and are working for beer. my shareware games and, after I sent them trueSpace was chosen due to price when Money is another reason. I don’t have a demo of Grey Wolf in mid-94, agreed to Grey Wolf began, and I quickly adapted to $4K plus another $5K for plug-ins for 3D feed me while I developed that project. I it. Money was not available for 3D Studio Studio. I paid $469 for trueSpace...and I’ve was recently out of work as a database pro- and, even if it were, I would not have used seen some terrible 3D Studio stuff hit the grammer and jumped at the chance. I had it due to the fact that it is DOS based. shelves. always wondered why games I am shipping the true- cost so much to develop, so I Space2 demo along with set out to create Grey Wolf for some models with Void $5K and did. Void came next, Pirates. with a little bigger budget. GD: What other tools did GD: How do you come to be you make use of to create making computer games? Are the graphics for Void you an artist who’s gotten Pirates? into computers, or a techie type who’s gotten into digital IF: Everything was done art, or someone who’s always on a Digital P90 with wanted to make computer 40MB of RAM, 2GB [of games and just took on all the storage], and Stealth 64 Moving beyond the single-screen user interface, Void Pirates uses a main necassary roles, or what? bridge screen with seven hotspots leading to detailed control screens. VRAM. Post production was done with Adobe Pre- IF: I think more the techie miere and in:sync RAZOR. type. The last decade of my All textures (400MB life has been spent as a mechanical design- When Void Pirates started, I picked up a worth) and touch-up were done with er. I have been interested in graphics and second system (P90), for rendering, and Corel Photo-Paint 5+. Live footage was 3D work since 1985, when first exposed to stuck with trueSpace. The only limitation shot with a Sony Hi-8 against my living it. I spent time as a kid attempting to write was lack of 3D Boolean operations, but I room wall. Footage was captured with an games for the Atari 800 in Basic, but got still managed. When trueSpace2 showed Intel ISVR Pro card. All artwork in the tired of it after getting a car. My passion up, I did go back and redesign some of the game uses the Indeo 3.2 fixed palette. went from writing silly shooters for Win- ships in the game using the new Boolean There is a bit of graininess because of dows to full-bore game development while features. I also created some interesting this, but palette problems are hexed. creating Grey Wolf. stations built into tunnels on asteroids, There is a little choppiness to some of which couldn’t be done in trueSpace 1.0. I the images, but I am willing to sacrifice GD: What was that first exposure to 3D just wish the motion blur option was image quality in favor of easy, flash-free you just mentioned, and what about it faster. palettes. interested you and inspired you to get involved? GD: What made you stick with trueSpace GD: Let’s talk about the look of the game. after Grey Wolf? The sci-fi setting in Void Pirates is realistic IF: My first exposure to 3D was on an and nicely detailed. What were some Applicon CAD CAM system at Stor- IF: trueSpace is the easiest, most refined influences, and what did you try to do to ageTek here in Colorado. From there I piece of software I’ve ever seen. There is so differentiate your game visuals from sci-fi moved to AutoCAD. The first animation much packed into 1.2 MB of EXE: no imagery we’ve all seen before?

62 GAME DEVELOPER • OCTOBER/NOVEMBER 1995 ARTIST‘S VIEW

IF: I was always impressed by the style of attacked during strafing runs similar to leaves the ship while in port. the original Aliens movie. The interior of Rebel Assault, displayed with an AVI. System requirements are MPC2 for a the Nostromo was very well done—people After disabling a ship, the hull is breached minimum, DX2/66 8MB RAM recom- smoking, porno magazines, empty coffee with a drone, which you then pilot mended, accelerated video highly recom- cups, and the two bobbing ducks in the around the interior of the ship as in Iron mended. I expect a lot of people calling main area really gave a realistic look to life Helix. The animations are not the main tech support saying their Packard Bell in space. point of the game, and I have tried to 486/25 won’t play the game. I’ve run into The Orinoco (the player’s ship) was integrate them as seamlessly as possible. about 90% of Grey Wolf customers having designed after looking at the mess on my There are also cut scenes that play as the their systems set up wrong for decent desks here at home: ashtrays, soda cans, player progresses through the game. Windows performance. The only limiting general disarray everywhere, but I still factor in the game is video card speed, and know exactly where everything is. Since GD: You voiced some strong opinions CD-ROM transfer speed. space is dark, the overall mood of the game about user interface design earlier. One of I did run into a problem with CD- is dark; a sad future similar to Blade Run- the great logistical and creative challenges ROM playback speed. My limit of 270kps ner. The shininess and lighting of the Star in game design is creating an interface that AVIs has caused a bit of image degrada- Trek series always seemed too happy. Void manages tos atisfy all the various gameplay tion, and there are certain parts of the Pirates focuses on trading of narcotics, requirements, coexist peacefully with all game where frames cannot be skipped. stolen property, and the like, and a darkly the technical limitations of the minimum There is a graphics diagnostic program, lit design sets the mood (hopefully). The target system, be readily usable by the along with VidTest in case the user needs darkness also helps by masking areas where player, and stillf it the game milieu and to determine whether their system is there should be more detail but I couldn’t look good. MPC2 compliant. afford to put it in. How did you approach this task in Void Pirates? GD: So what’s next after you burn in GD: Couldn’t afford in what sense? Void Pirates? IF: The Void Pirates user interface has IF: Total budget was around $20K—this changed only a little during development. IF: I and two other developers are coming includes my rent and bills. Seven months, I had grown tired of the single-screen user out with a Visual Basic Game Toolkit, $20K, and one person is very little to create interface (such as Daedalus Encounter, which will include a lot of source from a saleable multimedia product these days. I Journeyman, Iron Helix) and wanted the Void, plus a replacement for Surround- would love to spend twelve months on player to see more. Grey Wolf has a Video (scrollable, scaleable virtual reality Void Pirates and $100K, but it isn’t avail- Myst-type interface, where the player can AVI files) I have come up with using able, so I have to create what I can with walk through the ship. The only problem Visual Basic and trueSpace. Hopefully the what I have in as little time as possible. is a lack of crew members. It was basically Toolkit will get finished this year. It is in The design was limited by what I could a ghost ship, and the player had to do the planning stage right now, and I have render with 40MB RAM, without hitting everything. very little time to work on it. It will the swap file. Time is limited developing a In Void Pirates, the user interface include source for various types of games: game with one or two systems. Total hours consists of a main bridge screen with seven scrolling, platform, shooters, AVI walk- for modeling and rendering would be hotspots. Five of them take you to closeups throughs... Plenty of code for blitted but- about 800 so far. of the control clicked on, and the other tons, instead of the standard Windows two take you to the Main Cannon or the look. The other two authors are contribut- GD: So far? What remains to be done? Gun Turret. Originally, a walkthrough ing sound and music goodies, and busi- design was used, but the image quality suf- ness multimedia stuff, as it isn’t aimed at IF: More animations, more cut scenes. fered from compression, so still image game design only. The still artwork is 99% finished. I think interfaces it was. The player can get tired If we can find some capital, Andrew of something new each day. I’ll spend thir- of walking around the ship in short time— (WinFish guy) and I are going to hook up ty minutes trying to get an idea into true- as in 7th Guest—so stills make sense. again. He is working diligently with the Space2. If I can’t, I give up and try some- To lose the ghost ship image, there Intel 3DR toolkit, and we are thinking thing else the next day. is a VidPhone the player can use to talk along the lines of a networkable Tank to crew members. The crew members are Simulation, due to the speed of 3DR. GD: Will the game feature a lot of live video shot against blue screen here With trueSpace, we should be able to cre- animation? in my home. All communications with ate a very nice interface for a Tank. nonplayer characters are handled via And yesterday, SofSource and I IF: The majority of the animations are remote video drones or a text based ter- agreed on a vastly improved Grey Wolf 2, actual gameplay. Void Pirates uses many minal. All selling and trading is handled for next spring. At least I don’t have to concepts from other games. Ships are in this manner, and the player never wear a tie for another eight months. ■

64 GAME DEVELOPER • OCTOBER/NOVEMBER 1995