Tales of the Rampant Coyote
Ye Olde Archives. Visit the new blog at http://www.rampantgames.com/blog/ - and use the following feed: http://rampantgames.com/blog/wp-rss2.php


(  RSS Feed! | Games! | Forums! )

Thursday, July 20, 2006
 
My Worst Bug Ever
I'm gonna tell you about a bug that destroyed a game.

Not destroyed as in, "ruined the player's experience so that the game was no fun." I mean as in "put a gun to its head and blew its codey bits so that it was never seen or heard from by mortals again."

The Setting: The Commodore 64
To explain how this worked, I'm first gonna have to talk a little bit about the memory architecture of the Commodore 64, the scene of this particular crime. I'll try not to be too technical here.

Everything that happens on your computer happens in memory (AKA "RAM"). You may store stuff out to your hard drive, but in order for the computer to actually do anything with it, it has to be read off of the hard drive (or CD-ROM, or USB memory stick, or whatever) and put into memory.

The C-64's memory was laid out so that information that was on the screen occured in an area from memory location 1024 to memory location 2024. Think of it as a big block on street. Technical note: This was actually where the character data was stored... the screen was 40 characters wide by 25 lines. 25 x 40 = 1000 characters. So area 1024 to 2024. The color data was stored elsewhere, and there were lots of other fiddley bits in other memory locations you could squirrel with. Like creating custom character sets so that the letter "A" actually looked like a sword. And so forth.

The program memory was the next block down. Starting at location 2048. The program memory was where the actual code for running the game resided. Where I was programming in BASIC. In-between the screen-character memory and the program memory was this little chunk of memory that the system used for other things, and I don't recall what it was used for. But it's not important to the story.

The important thing to remember is that the character memory and the program memory were next-door neighbors, with the program memory just "below" the graphics memory.

The Set-Up
Back in those days, we stored everything on 5 1/4" floppy disks. The Commodore 64's 1541 disk drive was notorious for being both slow and flakey. It was crapping out on us, so my Dad had to take it in to get it repaired. This was on a Thursday, I think. He was supposed to be bringing it on Friday.

Bored Thursday night, unable to play any games without my drive, I decided to work on a game. A cool fake-3D game slightly reminiscent of the maze arcade game in the movie TRON. I got some stuff hacked together, and left the computer on, since I couldn't save my work until the disk drive was back.

Friday came, and I worked on the game some more. I was starting to get a little antsy about having a power outage or something, because by this point I'd probably put 4-5 hours into developing this game. I'd hate to lose it. Finally, 6:00 rolled by, and my dad came home.

Without the Disk Drive. Turns out the shop wasn't able to fix it yet, and they weren't open on the weekend, so he'd have to pick it up on Monday.

That meant I was going to be the entire weekend without a disk drive. No saving my game... no loading anything else up. The computer was pretty much useless for the entire weekend. EXCEPT, I reminded myself, for continued development on my game.

The Final Stretch
So I got back to work on the game, and worked on it off-and-on through Saturday and Sunday. I got it so you could drive around the maze (in sort of a stepwise fashion as seen in the old Wizardry and Bard's Tale games). And I got the enemy ships (which also resembled Recognizers from Tron) to appear. They didn't really move yet - they were static targets. When I got home from school on Monday, I was almost afraid to TOUCH the computer, for fear that I'd accidentally type "new" or something and delete this game on which I now had over 10 hours of development invested.

Still, after the homework was done, I found myself a little bored. It was around 4:00 in the afternoon, my dad wasn't going to be home for a couple of hours, and where our house was located we had LOUSY TV reception. So I shuffled off down to the computer. I'd put a couple more hours into the game, then my dad would come home with the repaired disk drive, I'd save the game, and then life would be back to normal again. I could waste time that night playing Ultima III.

The Gun Shot
It happened with a gunshot. A laser-gun actually.

I had a gun at the bottom of the screen that would fire up, towards a targeting cursor. The algorithm to have the laser (or "bullet") fly to the target was mostly simple. You find the X,Y position of the target on the screen. You find the X,Y position of the bullet (which starts just above the launcher). You subtract the target's position from the bullet's position. That's called the offset, and from that you can figure the direction of the bullet. Move the bullet in that direction every frame.

Now, I was using integer math, so I had to do some funky stuff to get it to fly in something resembling a straight line. One trick I did was recalculate the offset every frame, so the bullet might fly in a slightly crooked path, but would always eventually get to the target.

And then I'd just repeat every frame until the bullet's position was the same as the targeting cursor's position. Then I'd see if the targeting cursor (or laser) was over a valid target, and if so... boom.

Okay, it's pretty primitive stuff. I was something like 13 years old when I did this, cut me some slack! :)

So I tested the game again with the gunshot, about an hour before my Dad was supposed to get home.

I saw the laser appear briefly over the top of the gun, and then that was it. Something was wrong. I waited to see if anything else would happen to help me understand the bug. After a few seconds, I hit the "break" key.

And got a garbled message.

I tried to list my program, and saw nothing but a bunch of garbage. It was impossible to execute, or to even fix, as the computer was not responding correctly.

WHAT HAD I DONE?

Solving the Crime
It took me only a minute to figure out what had happened.

I had made two mistakes:

#1 - I'd calculated the offset between the target and the bullet improperly. Instead of subtracting the bullet's position from the target's, I'd done it the other way around. So the bullet actually tried to fly in the OPPOSITE direction of where it was supposed to go.

#2 - I hadn't written any defensive code to make sure that the bullet was restricted to the screen's position. This isn't something you wanted in final code (as it would slow the game's processing down), but it would have been nice during development.

So my gun had actually fired BACKWARDS. Instead of flying up towards the targeting cursor, it had flown.... down. And since I was directly modifying memory to display the blast, it had fired down out of screen memory, into program memory. It had ripped through program memory, cutting a bloody (well, as bloody as bytes of code can get) swath through the entire thing. In fact, it might have been a double-barrelled hit, as I was also directly writing to color memory at the same time, and that one had gone out-of-bounds.

Eventually, either the image or the color had ripped through the area of RAM that held the actual operating system code that normally got loaded on boot. Thus the garbled error message.

There was nothing left worth saving. Four days of work. I knew the risks, I took the chance, and I still blew it.

An hour later, my dad came home with the newly fixed (or was it simply replaced) disk drive.

It's so nice to have learned these lessons at an early age.

Labels: ,



Did you enjoy this post? Feel free to share it: del.icio.us | Digg it | Furl | reddit | Yahoo MyWeb

Comments:
I had a mondo bug similar in my Atari. I had written a C compiler for it, and was trying out some programs in a book to verify it was working correctly. When adding numbers I would get the wrong results and if statements didn't work quite right. And it all seemed so random. I thought for sure it was a memory stomp, and I spent forever looking for that.

What happened was I had accidentally gotten the index off by one in my opcode lookup table at compile time, which didn't crash the system but ended up using undefined opcodes! These opcodes worked for the most part, but had unintended consequences such as setting the flags incorrectly after an add. It took me FOREVER to track that one down. If it had crashed, that would have made it easier, but NOOOOO. Stupid 6502 and their undocumented opcodes. :)
 
I did a little contract game writing while in college. One of the guys at the office had written a program for the Atari ST that would let you modify the pointer to the start of the video buffer using a joystick. It was pretty cool, since you could scroll through the regular frame buffer, any loaded textures, even the code space.

Then he got an idea. How about turning it into a game? he added a targeting cursor, and the object was to see how many times you could blow a hole in the running code before it stopped running.
 
Undocumented 6502 opcodes! Man, I remember those. Trying to learn assembly on the 6502 was something of a nightmare. Well, not just for the undocumented opcodes (I had a tough enough time with the documented ones). But the different special-purpose registers, etc...
 
Haha.. My brother Scott used to do this on purpose - he'd write (on the Commodore 64) a program to put random values in random locations in memory, and he'd let it run as long as it could. We'd get funky stuff on the screen, video mode switches, color changes, sound glitches, etc., until the program finally locked up.
 
I vaguely remember playing around with self-modifying code on the C-64 at one point... nothing too extreme, just one of the things I thought was cool as I tought myself 6502 ML / Assembly. I discovered I *could* do it, so I came up with a really stupid excuse to try it.
 
Post a Comment

Links to this post:

Create a Link



<< Home

Powered by Blogger