47
\$\begingroup\$

I'm thinking of like the SNES, N64, Atari... even the DS today, I suppose.

SNES games did not usually take up more than 4 MB of space, and N64 games were usually 32 to 64 MB worth of data.

These days, you can barely compile a "hello world!" program without the resulting compilation generating 1.21 gigabytes!! of data. (Joking aside, files today do take up tons and tons of space compared to some of the technology back then.)

So... how did they do it?

  • What did they program these games in? ASM? The entire thing in ASM?!
  • How were graphics created? What technology did they have to create sprite sheets and, in some cases (like the N64), 3D models?
  • How did they fit so many levels, characters, quests and items on these cartridges? I mean, Super Mario World on the SNES clocks in around 1 MB, and it has 96 exits! Ocarina of Time, Banjo-Kazooie, DK64 and a few other games take up less than 64 MB and had huge worlds, tons of content and 3D models!

Sorry if my questions seem a little out-there, I'm just amazed that a lot of great titles out there managed to fit in such a small storage space.

It's fascinating to me because even tiniest and most trivial of files and games manage to take up at least a few MB, so imagining that huge levels like those in GoldenEye 007 managed to take almost no data at all is mind-blowing.

\$\endgroup\$
7
  • 1
    \$\begingroup\$ Also, regarding the duplicate I know people will point out: I'm mostly interested in how the actual data was put into the games and how huge levels were created while retaining a small file size - not so much the development process and tools used. \$\endgroup\$
    – Corey
    Oct 9, 2010 at 0:30
  • \$\begingroup\$ Possible duplicate: gamedev.stackexchange.com/questions/443/… \$\endgroup\$
    – Sergio
    Oct 9, 2010 at 1:14
  • 1
    \$\begingroup\$ NES(see Metroid Source at MDB) and SNES(the source code of some random 3rd party games is out there on the web) used ASM, N64(Zelda: MM's debug screen displays the filename in the crash info) used C. \$\endgroup\$
    – Ivo Wetzel
    Oct 9, 2010 at 4:53
  • \$\begingroup\$ Game programming was very expansive back in the 8-bit days. For instance making Pacman cost a fortune when it could be made today rather cheaply. The reasons being that the constraints of hardware were more limiting than they are today by a thousand times (or more). That meant that you had to rely on assembler code for these 8-bit games. The reason games today are so big isn't that they need to be. It is mainly that they can be. You could read about Wirth's law. \$\endgroup\$
    – AturSams
    Mar 14, 2014 at 9:59
  • \$\begingroup\$ Yes, 8-bit games were often written in Assembly. The SMS games were made with a Z80 in mind, this is well known. When you write in Assembly, you can still create useful libraries. I don't see how keeping the code compact is relevant to game development nowadays. It sounds like someone asking how to feed and groom horses in a modern car forum. If you write native binary instructions, in a machine with one purpose, of course you can and likely will keep the code compact. How bloated can it be when you need your code to run on at a few megahertz. :) \$\endgroup\$
    – AturSams
    Mar 14, 2014 at 13:22

4 Answers 4

28
\$\begingroup\$

It's the art and audio resources that take up space, choice of programming language was more about getting the most of out of the hardware.

Using N64 as an example, most of the 3rd party games were 8, 12, or 16Mb. The 32 & 64Mb games were mostly from Nintendo as it was so expensive to ship on carts that big for everyone else. That sounds tiny, but then so were the art assets and the final visual output. You have to remember that most N64 games rendered at 320x240 not the 1280x760 or more of today. With such a small output resolution, textures and sprites were much smaller than they are today.

Because of the tiny texture cache on the N64, most textures were 32x64 pixels with a 4/8bit palette (aka 16/256 colors). Extra color detail was often done by mixing textures and vertex colors. The Banjo games are a good example of this.

Today a single rock in an Unreal engine game will have multiple 512x512x24bpp or even 32bpp. Plus instead of just a single diffuse texture, you've now got normal maps, specular masks, reflection masks, reflection cubemaps and more. So an object that used to have 4Kb of textures is now covered in several MB of data.

Old games also have a massive amount of reuse of art. The bushes in Super Mario Bros. are the same sprites as the clouds, the hills are the same as the mushrooms. Different characters are just color shifted versions of the same art resources. All of this got more usage out of each texture or sprite that was on the cart.

Audio is another big difference for modern games. Nearly everything in the old days was done with sequenced tracks. Now both music tracks, voice and sound effects are stored in various compressed audio formats. While certainly smaller than uncompressed data, they are still significantly bigger than their sequenced equivalents.

\$\endgroup\$
3
  • 8
    \$\begingroup\$ Ah, the mario bushes/trees incest with a logical explanation! Excellent. \$\endgroup\$
    – Kzqai
    Oct 9, 2010 at 3:57
  • 10
    \$\begingroup\$ It's worth pointing out that the carts were often sized in megabits, not megabytes. Those 64Mb carts were only 8MB. \$\endgroup\$ Oct 11, 2010 at 17:13
  • 3
    \$\begingroup\$ The output wasn't 320 x 240 in N64. The details are incorrect. Most game were probably using 256 × 224. see here \$\endgroup\$
    – AturSams
    Mar 14, 2014 at 9:51
14
\$\begingroup\$

As for the NES (and SNES too mostly), here's a basic overview. I did not write any NES games but did write an NES emulator (Graybox) and did a fair amount of rev-engineering of old carts.

As for programming language: yes, it was all assembly. Programming the NES meant working directly with hardware interrupts, DMA ports, bank switching etc. Luckily, programming the 6502 (or rather, the 2A03) is quite easy[1]:

  • there are few registers: A, X and Y mainly, the latter two being usable only for indexing and iterating
  • the instruction set is small and mostly straightforward
  • not a lot of memory: main RAM is 2KB, with an optional battery backed 8KB extension. Of that 2KB, 256 bytes are reserved for the stack and page 0 (the first 256 bytes) was where you'd want to store your most used pointers and values because of some special addressing modes

These 3 things together make for an environment that is easy enough to memorise while working with it. Yes, you manage all memory yourself but that meant essentially that you create a full map of where everything goes up ahead and that map isn't very big because you only have to worry about 2K, so you could plot that out on a piece of graph paper. You had to plan things out a bit more and statically assign variables and constants to RAM and ROM (on cartridge) locations.

It gets a bit tricker once your cartridge data goes beyond the addressable limits of the CPU. That's 64KB, of which the lower 32KB is set in stone and mapped to all kinds of hardware ports and RAM. This is where bank-switching comes into play, which means mapping a section of the ROM into (part of) the higher 32KB address space.

This can be used however the programmer wants, but an example use might be having a game with 3 levels, with all the level data, meta data and code for each level crammed into separate 8KB memory areas on the cartridge. The level might have callbacks for e.g. initialisation, per frame update, etc. "Loading" the level would mean mapping that 8KB chunk of memory at e.g. 0xC000. You could then specify that the init routine is always at 0xC000, the frame update routine is at 0xC200 and the level data starts at 0xC800. The game's main code housed in another memory chunk then controls level changes simply by swapping in the right chunk and jumping to absolute addresses 0xC000 and 0xC200 at the appropriate times.

W.r.t. graphical data: the NES's tiles data are 2-bit 8x8 pixel maps. For the background they are combined with a 1/4 resolution 2-bit layer. These 4-bit values were then indexed into a 16-entry palette, with I believe 53 effective unique colours available. Sprites also used the 2-bit pixel data and each sprite specified its own 2-bit group index again forming a 4-bit pal index. The BG image on screen is a 32x30 array of tile index numbers.

Essentially, by having a ton of repetition and indexes into indexes you can keep data very small. Level data was often stored as vertical bars of tile indexes and because those vertical bars were re-used as well, those were indexed as well and only stored once on the cartridge. Simple data compression techniques work similarly. This allowed Mario 1 to be 32KB of data (with room to spare) and 8KB of bitmap data.

As for dev environments, I've seen some photos where people worked on some certifiably ancient computers hooked up to EEPROM burners for work. Tool-assisted debugging was not really a possibility until after the SNES age[2]. This is the main reason so many old games have "obvious" bugs in them and why things like Gameshark could do what they do; player health would always be at mem-location X, so you can force it to be 100 at all times.

If you find these things interesting I encourage you to look at e.g. http://wiki.nesdev.com/w/index.php/Nesdev_Wiki There are quite a few programming courses for NES to be found online as well.

I hope this simplified overview gave some insight into 80s-era game development.

[1] Relatively speaking. Also I'm biased as I wrote Graybox itself in about 85% PowerPC assembly. [2] See the making of FF6 article: http://www.edge-online.com/features/the-making-of-final-fantasy-vi/

\$\endgroup\$
3
\$\begingroup\$

There are a lot of sub-topics in almost all of the questions you are asking. Optimization is a huge field all to itself and there are a lot of things to explore.

If you are interested in this sort of optimization, one of the things you might explore is the demoscene. The demoscene, and some of its related art cultures, has long retained a sense of wonder about trying to create intricate art for computers that takes up as little space as possible. Many of them will have information on how they did some or all of their "tricks".

Often there is an artful mix of common sense, although there are "tricks" and "hacks" specific to a game or genre. Often there's a bit of "luck" involved, and a team knowing the limits they are working for (perhaps continually butting heads with those limits throughout the process), knowing their available trade-offs, and be willing to make some of the necessary trade-offs and sacrifices to meet their limits.

Here are some of the things that I can think of that can help a team get a game to smaller sizes:

  • Reuse What You Can: reusing the same sprites, and the variations that you can easily make from a single sprite image (such as reflections, rotations, palette shifts) will save you space. The same goes for code, music, and nearly everything else in a game.
  • Compress What You Can: there are a number of compression schemes out there, and knowing which ones to use can be a huge space savings. Even sometimes simple compression schemes like run-length encoding can make a surprising difference. Not only that, but there are compression schemes (and alternative formats that aren't exactly compression) for individual file types, often with quality trade-offs. For instance, wave/CD audio files can be compressed, with some marginal loss of information, into MP3 files. Also, file formats like MIDI and sampler-based MOD are alternative formats that take up a lot less space, but encode music entirely differently and require different skills to make them sound good.
  • Lose What You Don't Need: can you do it cheaper? For instance, can you still get the "personality" of a character across in fewer pixels (or polygons)? Does the placement of tiles need to be exactly defined by a designer or can they be randomly generated in your program code?
  • Code Is Often Cheaper: although you made a joke about how much space a code compile typically takes now ideas (and there are reasons for why this 'platform' has increased over the years, and ways to shrink it when you absolutely need to), but generally if you can do something algorithmically/procedurally/in-code easily, that approach will also be easier to tweak and to reuse for other similar but different looking/feeling assets. Fractals are a particularly easy to see example: you could have an image of an intricate fractal that takes up a lot of space with comparison to the mathematical formula that generates it. The mathematical formula, additionally, may have parameters that can generate similar, but sometimes surprisingly different looking images.

Anyway, for such a big, loaded set of questions, hopefully some of the topics above will be good starting points for you to learn more.

\$\endgroup\$
6
  • \$\begingroup\$ Also, use technology that use less space. \$\endgroup\$
    – speeder
    Oct 9, 2010 at 4:29
  • 3
    \$\begingroup\$ (sorry, enter issue again... there are a way to disable it? I hate that every time I press enter the comment submits). \$\endgroup\$
    – speeder
    Oct 9, 2010 at 4:29
  • \$\begingroup\$ Another enter :/ Anyway, use technology that use less space, like procedural maps (Noctis has a entire galaxy with several million solar systems, with planets that you can land and see life, trees, ruins, buildings... in less than 3MB), module music (music in formats like .mod, .xm, .it...), procedural textures (see werkkzeug, mapzone, and some other software), procedural sound effects (almost any sound effect is possible to make from math equations, or manipulation of basic sound waves), and so on. \$\endgroup\$
    – speeder
    Oct 9, 2010 at 4:32
  • \$\begingroup\$ @speeder it's easy to click 'edit' or 'delete' on accidental comments... \$\endgroup\$ Oct 11, 2010 at 17:14
  • \$\begingroup\$ Re: "Compress what you can," on the old hardware you typically would compress to whatever the hardware could handle. You would never compress audio to MP3, because the audio hardware didn't handle it natively and you wouldn't want to waste the time decompressing it on the CPU when you could just stream it straight off of the media into the audio hardware. MIDI was great though because everyone had (and has) a wavetable synth on board; just load up your samples and there you go. \$\endgroup\$ Oct 11, 2010 at 17:16
0
\$\begingroup\$

One thing is I am not sure if it still stands in the post N64 era but the SNES and N64 often reused textures on other 3D objects which often save considerable space and pre rendered art which the backgrounds are often fake. Another trick was to create a border background fog would be used.

San Francisco Rush N64 always had some fog though settings could change the density where the San Francisco Rush arcade didn't have any and you could see the Golden Gate Bridge on Track 1 compared to the N64 version.

Also games often reuse music like Zelda Ocarina of Time reuses music a lot which I find annoying as there could've been done a better job like how Banjo Kazooie/DK64 often had themes within themes!

Zelda Ocarina of time could've had the Hyrule Overworld and then an underwater version of the theme if you go underwater or make the instruments in the Shop Theme reflect the outside area where flutes and fiddles would be used for the Kokiri Forest shop and horns and trumpets for the Hyrule Castle Town shop and drums in the Goron village.etc

In PC's 3D modules are compiled into libraries to quickly access them using a program to unpack it but I am not sure if that is the case with Nintendo which is ROM based. PC's is RAM like going into a messy room in which things don't always stay where they are suppose to and information can be overwritten to the point the computer won't even start!

Game Consoles are ROM where everything is stored in an allotted space so every time you turn the game on it will look for the files in that allotted space with a guarantee it will remain there.

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged .