45

One thing that was surprisingly common in older video games (around the same era assembly language was king) was the inclusion of uncompiled, plaintext source code in the ROM. You can see many examples of this here.

My question is...why? It doesn't make much sense for a variety of reasons:

  1. The source code is not usable by the machine in any way, and is not pointed to by any part of executable code.

  2. I figured it could be used for padding an EPROM when writing it during testing, but wouldn't it be trivial to just use a sequence of zeroes?

  3. A bug/error in the assembly process. I've used assemblers before and it doesn't quite make sense to me how it could include the source it's reading by mistake unless the assembler tools were heroically bad.

  4. Another thing that crossed my mind is deliberate inclusion, either by disgruntled employees or as an easter egg. But almost nobody of the era could actually see it without a ROM dumper, which wasn't exactly a common household item. Additionally, even if the intention were to surreptitiously release the source, very seldom was enough code included to actually rebuild the game.

Was there any technical or other reason that source code would end up in a game ROM?

  • 13
    I can imagine why, but I don't have any evidence so posting as a comment; feel free to steal this as an answer if someone has more evidence. I reckon it's probably the assembler having loaded in a source file, being done with (part of) it and so freed the memory. Then this memory is re-allocated for use in the output, but not cleared because that wouldn't be efficient. Thus the old source code is still in that part of memory. Then when the generated code and data doesn't take up the whole space allocated, the source code is left in the unmodified gaps, and makes it all the way to the ROMs. – Muzer Mar 10 '17 at 13:40
  • 2
    Not an answer to your question, but your understanding of Easter Eggs is a little off. They weren't traditionally designed to be viewed by the user of the software, or even viewed at all in some cases. Atari's forbidding of attribution in software was circumvented by hiding the programmer's name in the source without Atari's knowledge, for example. – GGMG-he-him Mar 10 '17 at 18:41
  • The same thing happened with early MSDOS .COM programs. – rcgldr Mar 11 '17 at 0:17
  • Not about fragments of source code, but rather full rants: youtube.com/watch?v=2ndNSRy04YI – tofro Mar 14 '17 at 15:30
  • I would wonder if these fragments of source code were leftovers (or enablers) of a port of a program to another type of system. I mean a C64 and an Apple II both had 6502 CPUs, so some of the code would have been the same and portions localized to the particular components of the intended computer right? (I'm not a programmer so I may be 1000% wrong.....) – PhasedOut Mar 15 '17 at 13:47
27

I'd guess a lot of the systems used for development didn't clean the memory down in between usages. It's also likely that for games "gaps" got left between binary chunks. So if for a hypothetical system redefined characters had to start on a multiple of 256 (0x100) byte boundaries the memory layout might have looked like this:

0x0000 - 0x1789 - Compiled code
0x178a - 0x1800 - Whatever was in memory
0x1800 - 0x2000 - 256*8 byte redefined characters

So unless someone explicitly set the chunk between 0x178a & 0x1800 to be a value you might see some left over source code.

  • 1
    something similar happens on the Apple II - the DOS can read blocks that were smaller than a sector in length, but write only multiples of sectors, so everything from end of read until next sector would be written out. – peter ferrie Mar 10 '17 at 17:03
  • 1
    Saw a similar effect in MS-DOS; only I ended up with the symbol table rather than source code. – Joshua Mar 10 '17 at 17:57
  • 1
    you'd assemble your code at a specific location, and then do a binary save of the block; sometimes stuff left in memory would just make it to the save. – Thomas Mar 14 '17 at 4:01
16

I don't know if this applies in every case, but, on the Commodore PET and by extension C64, an executable program file on disk/tape consisted of a load address (normally $0401 on the PET) and then literally a memory dump of the the region of RAM containing the program. The normal save routine would save all the memory until it got to the end of the BASIC program.

Most games were written in assembler but to load and run them the executable on disk/tape would be a memory dump of a one line BASIC program followed by the assembled machine code. The one line BASIC program would just call the machine code, using a SYS command.

Because you had all that machine code after the BASIC program you couldn't just use the default save routine because it would stop at the end of the BASIC, so you told it to save a block of memory. If you didn't get the size of it right (your block was too big) or there were gaps (e.g. between the end of the BASIC and start of the machine code) whatever garbage happened to be there would also get saved. These machines had no virtual memory, so the garbage that happened to be there had a good chance of being part of the source code you might have been working on in your assembler just before saving.

  • 3
    The Commodore 64 games loaded by a bootstrap loader that use indirection vector $0326 - which when it tried to print "READY" via the $FFD2 print indirection vector, would then initiate the second stage "turbo loader", be it by cassette or floppy. Hence, the SYS call never existed in many C64 games. Back to the original subject though, it is true that even old MS Office docs (think 1997-2000 and before) contain memory dumps that should not be there. – birdwes Mar 14 '17 at 1:40
  • I only know this because me, and a guy that works in a Swiss bank now used to hack game loaders for fun about 36 years ago, and we did break their copy protection. Maybe 100 or so? I don't have them now. We weren't interested in playing the games. The game for us was the hack. – birdwes Mar 14 '17 at 1:59
15

The page you linked on The Cutting Room Floor offers its own explanation:

Sometimes this is to pad out a disc or ROM to fill any empty space, other times it's just funky compiler behavior...

"Funky compiler behaviour" is the most likely explanation for the older games. More specifically, as explained in Muzer's comment, it's likely because memory was reused without erasing it first or completely overwriting it with new data. One can imagine this resulting in source code ending up in a game in one of two ways. Either a compiler/assembler that ends up reusing part of its own memory for both storing the source code input and the machine code output, or a linker that ends up using memory previously used by a compiler/assembler.

The first case case could happen in a scenario something like the following:

  1. The program allocates an input buffer
  2. The source code is read into the input buffer
  3. The source code is converted into a parse tree or some other intermediate representation
  4. The input buffer is freed
  5. The program allocates an output buffer which happens to overlap the the location in memory where the input buffer was
  6. The program converts the intermediate representation to machine code and stores it in the output buffer
  7. The entire output buffer is written out

If the output buffer isn't cleared in step 5 or entirely overwritten in step 6 it could end up containing part of the source code read into memory in step 2.

Older single-user operating systems like MS-DOS didn't (and/or couldn't) provide any sort of real process isolation and there's was no security to speak of. While a modern multitasking OS will erase any memory it provides to a process so it can't see any data that might have been stored there by other processes, these older OS'es didn't do that. So the second case mentioned above could happen in a scenario like the following:

  1. The compiler/assembler allocates space for an input buffer
  2. The source code is read into the input buffer
  3. The source code is compiled/assembled into an object file
  4. The compiler/assembler exits
  5. The linker allocates space for an output buffer which happens to overlap the location in memory where the compiler/assembler allocated the input buffer
  6. The game's object files are linked together and stored in the output buffer
  7. The entire output buffer is written out

Again, if the output buffer isn't cleared in step 5 or entirely overwritten in step 6 it could end up containing part of the source code read into memory in step 2.

This later scenario is probably more likely, at least for anything other than the earliest of games. The program is only likely to write an entire buffer regardless of how much of that buffer was used when the output needs to be a certain size like when creating a ROM image. A smaller game might use assembler that produces the ROM image directly, but bigger games are more likely to use separate compilation and produce the ROM image using a linker.

Padding out a disc or ROM is a much less likely explanation, though you see something similar in the newer games listed on that The Cutting Room Floor page. Instead of explicit padding, or even actual game source code, you often see build scripts and build configuration files included in games. These are files that happened to be in the build output directory for use by the game's build process but aren't actually needed at runtime by the game itself. They end up being included in the game because the entire build output directory ends being included in the game's disc (or flash cart) file system. Essentially they turn that entire directory into an ISO and use it as the image they send to manufacturing.

6

There are many reasons why a program might be written to include unspecified data, including:

  • if the platform utilises any sort of paging, each individual page may need to be self-enclosed as the machine will have visibility of only a subset at a time, often implying unused space at the end of pages;
  • if the platform was also the development machine, using all available RAM for object code implies no room for both object and source code; therefore source code may need to be assembled in chunks, each with a fixed start address (either because it's programmer specified like a ORG $4000, or because the working environment would make reassembling all files for every edit infeasible, so the assembler applies some other fixed scheme), so that when glued together gaps are left;
  • some CPUs may prefer certain data alignments (e.g. with 6502 lookup tables, it's usually faster not to overflow a page) so an appropriately directed assembler may be asked to skip certain output addresses (e.g. with the 6502, to move directly to the beginning of the next page).

If the assembler is told "I don't care what's at address N" then it is legitimate not specifically to put anything there. It is also likely, since it is faster. Therefore whatever was in memory before may linger. Which is likely to be source code.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.