Depending on how complex the code is, I've done a few things.
You probably want to go over each method/fucntion/object/whatever (you didn't mention a language) and try to understand what it is doing. If you have ANYTHING that takes you more than a minute to understand, figure out what you didn't understand and write a comment so the next time it won't take you that minute.
Understanding how all the parts relate to each other can be tough unless the design is very well done. Printing debug output at the entry/exit of each routine and using stack dumps can be helpful to see how you got some place. A debugger can be awesome for figuring this stuff out.
A final tool I found to be really useful is a profiler. I had a free profiler plug-in for Eclipse (I forget the name, but I don't think there are many) that would create an awesome sequence diagram for any code it went through as it was running. This may be the best tool I ever saw for understanding what the code was doing. It was a bit difficult to set up at the time, but keep at it, it's doable and well worth it.
I turned on the profiler, hit one button/executed one task, then saved the "Run" for that button.
I filtered out classes that were trivial and got it to a semi-reasonable size (Some were 2 pages, one sequence diagram took 4x6 sheets of paper to print (landscape)). I taped them all together and put them on my cube wall and studied/documented the hell out of that thing.
Sequence diagrams rock when done right, by the way. If you are trying to understand some code and you don't use sequence diagrams, look into them. I think they are probably the most useful design documentation tool I've seen.