Latest revision as of 16:52, 6 July 2018

Nemesis compression is the method created by Sega to reduce the amount of space that graphics data takes up in a Sega Mega DriveROM. A decompression algorithm takes the compressed data, unpacks it and transfers it to the VRAM. The graphics can then be displayed on the screen. The Nemesis format is specifically designed for Mega Drive graphics, as it relies on the length of the data being divisible by $20 (hex), which is the exact number of bytes in a single 8x8 tile. However, Nemesis compression is also used for 16x16 block mappings in Sonic CD, because the data conforms to the $20 rule.

Nemesis compression is named after Nemesis, the hacker who first created a program to decompress it. The format is used in numerous Mega Drive games, especially Sonic the Hedgehog games.

Contents

Compression Theory

The Nemesis compression format uses both run-length encoding and entropy encoding to compress data. The source data is broken into nybbles which are then grouped into runs of 1 to 8 nybbles in length; there are multiple ways to do this grouping, which is called a parse. Given a parse of the file into nybble runs, the file will then be entropy encoded based on the frequency of each nybble run in the file; the code of each nybble run is used to build a dictionary which is used when encoding and decoding the file.

The final Nemesis compressed file is then composed of a 2-byte header, a dictionary associating nybble runs to their respective codes, and a bitstream (the encoded bitstream) where each nybble run in the parse is replaced with its corresponding code in the dictionary. The dictionary is present in the compressed file so that it is possible to decode it; the decoder used in Mega Drive ROMs converts the dictionary into a simple table, using the code read as an index.

There are some restrictions to what forms of entropy encoding can be used: first, is limited to 8 bits for each encoded nybble run; this happens because the dictionary leaves only 8 bits for each code. Second, the set of all codes in the dictionary must form a prefix-free code; this makes the encoded bitstream unambiguous in its decoding, but it reduces the number of codes that can be used when entropy-encoding the file. Third, a nibble run can be directly inlined using a special 6-bit code prefix that was specified in the format; because the entropy encoding must result in a prefix-free code, neither this inlining bit pattern, nor any of its prefixes, can be used as codes when building the dictionary.

As noted above, there are multiple ways to parse the input file into nybble runs; each such parse will give rise to different dictionaries because the different frequencies of the nybble runs will change the resulting entropy encoding. Given a parse, the package-merge algorithm can be used to find an optimal dictionary; but since it is not possible to know the length of each dictionary entry beforehand, or how many entries are useful to have in the dictionary, the final file size can't be known during the parsing stage. Finding a parse that gives the smallest possible file size is difficult, particularly since the number of possible parses is staggering even for a small file. Nevertheless, more modern techniques, including the aforementioned package-merge algorithm, have generally improved compression ratios compared to what was found within Mega Drive ROMs.

The high compression rate offered by the Nemesis compression format made it a very appropriate format for cartridge-based systems, where storage space was at a premium. Although the Nemesis format provided high compression, it also took a fair amount of time for the 68000 CPU to decompress. To prevent noticeable stalls while loading new sets of tiles into the VRAM, the Nemesis decompression routine limited the console to decompressing a maximum of 3 tiles worth of pattern data each frame; some variants of the decoding routines can decode 6 or even 12 tiles in a frame (with a correspondingly larger decompression buffer), but they are much more rarely used.

Format Description

In order to understand Nemesis compression it is vital to know how the Mega Drive stores graphics. Graphics are stored in the form of 8x8 tiles known as patterns. Each pixel in a pattern is represented by a single nybble, that nybble being the palette index to use for that pixel. One pattern therefore takes up 8 * 8 = 64 nybbles or 32 bytes.

Nemesis compression works by replacing the most common nybble runs with codes representing that run. A prefix-free code system is used, meaning that no valid code can be a prefix of a longer valid code. For example, if 10 is a valid 2-bit code, 110 is a valid 3-bit code but 100 isn't (because it starts with 10). The length of each code is selected according to the frequency of the nybble run that code represents - the most commonly occurring runs will be represented using shorter codes.

The Nemesis compressor can operate in two modes: normal mode, in which each row of pixels in a pattern is encoded as-is, and XOR mode, in which each row of pixels in a pattern is encoded in terms of its differences from the previous row. The first word in a Nemesis-compressed file gives the number of patterns in the uncompressed data (i.e. the size of the uncompressed data / $20). The sign bit of this word being set indicates that the file uses XOR mode instead of normal mode.

Following this word is a section which describes the codes used in the file. The lower nybble of the first byte gives the palette index. The upper nybble of the next byte gives the number of times that palette index is to be copied - 1, its lower nybble is the length of the code in bits, and the byte after that gives the actual code itself. The byte following this can be interpreted in three ways. If it's FF, it marks the end of the section. Otherwise, if its sign bit is set, its lower nybble gives the new palette index and the two bytes following it have the same format as described above. If its sign bit is clear, however, the palette index remains unchanged and the byte contains the copy count and code length with the actual code being stored in the next byte, as described above. The process repeats until the end of the section is encountered, and a code table is built up in RAM to be used in the next stage of the decompression.

The next section of the file contains the actual compressed data, which is stored as a series of bits. The bit series 111111 has a special purpose: it indicates inline data, and the 7 bits following this series have the format XXXYYYY, where YYYY gives the palette index and XXX gives the copy count - 1. All other series represent a code, and the palette index and copy count for that code are fetched from the code table. When an entire row of a pattern (i.e. 8 pixels/4 bytes long) has been decompressed, it is written directly to the destination if in normal mode, or XORed by the previous row and then written to the destination if in XOR mode. The decompression terminates when the number of decompressed patterns matches the pattern count given in the first word of the file.

Usage in Mega Drive Games

The Nemesis compression format was widely used in many Mega Drive games, both those produced by Sega, as well as a variety of third party developers. Additionally, the decompression routine for Nemesis archives embedded in ROMs that use the format is identical in the games in which it was used. It can be concluded from this that the Nemesis compression format was included as one of the standard libraries provided by Sega in their SDK for the console.

Although Nemesis archives were used for all the compressed art in Sonic 1, for Sonic 2, the main block of art used for level tiles in each stage was compressed using the Kosinski compression format. This was almost certainly done to decrease load times at the start of the level. Kosinski is an LZ77-based compression algorithm, which provides extremely fast decompression. While the Nemesis format was limited to only 3 tiles of decompressed data per frame, the Kosinski decompression routine may be able to decompress 100 tiles in the same time. This could eliminate up to 3 seconds worth of load times from the start of a level. Although Kosinski compression offered a lightning fast decompression rate, the algorithm was not well suited to the large number of small matches that generally occur in most tiles. As a result, Kosinski compression generally produced a much lower compression ratio than the Nemesis format achieves for Mega Drive art tiles. Additionally, while the Nemesis format could compress any data in 2 passes, the number of passes required with the Kosinski format increased with the size of the data. Kosinski could potentially require hundreds or even thousands of passes over the data to achieve its maximum compression ratios. As a result, the Kosinski format provided much slower compression times, which although insignificant today, may have significantly increased the time it would have taken developers to build test ROMs during development.

For Sonic 3 and Sonic & Knuckles, most of the art was compressed using Kosinski compression. Despite the lower compression ratio, the use of Kosinski allowed the developers to make large changes to the appearance of the level between acts, and even during the level, without any apparent seams, and without causing visible slowdowns. This was a key factor in creating some of the indoor/outdoor and other mid-level transitions used throughout these games, and allowed the developers to create maps which had much more variety within each level. The extensive use of Kosinski compression for art contributed to the larger ROM size for these later games: 2MB for each game, as opposed to 512KB for Sonic 1, and 1MB for Sonic 2.

Decompression code

An annotated version of the Nemesis decompression code is provided below for reference. The code is taken from Sonic 3 & Knuckles, but the same or similar code is used in all the games which use the format.