Post subject: N64 Debugging : Find code related to specific game mechanism
Joined: 5/26/2014
Posts: 7
First off, I'm pretty new to this. I've been using Nemu64 for stepping through asm. If anyone knows other emulators/tools which might have a friendlier or more full-featured interface... please let me know. I had an idea for a type of code search... 1) Play the game for awhile (call this period A) 2) Do something special in the game you didn't do during period A (call this period B) 3) Have the emulator find all addresses/instructions which were executed during period B but not in period A Is that sort of thing possible? I guess the B minus A thing is a bit specialized, but more generally is there a way to collect the set of instructions which get executed during some period? I could do the set subtraction myself.
Joined: 10/20/2006
Posts: 1248
I started writing a lengthy reply that seemed to turn into a rushed general tutorial on how I do debugging with Nemu. I guess that's probably not what you are looking for. Why don't you tell us the real problem you are trying to solve? (What's the type of situation where you would want to use this approach?) It should help me come up with a better reply. I can tell you this much already though: Nemu is probably the N64 emulator with the best debugging tools you will find. What you want to do here is indeed possible, but it can't be fully automated as of yet (not that I know of), so it'd be quite laborious. I think it's pretty unlikely to be the best solution to the [URL=http://blogs.msdn.com/b/oldnewthing/archive/2011/11/04/10233798.aspx]real problem[/URL] you have.* Also, it takes quite a while to explain all the details and special cases of the approach I had in mind (which is why my original reply started to blow up to unreasonable lengths). Opcode breaks at JAL and JALR will let you see all function calls, and you could manually log them. I don't recommend this though. You'd be surprised at how many there are, and every single loop over a function call will cause you lots of trouble. *) That link is not meant to be condescending, it's just an example of a situation that illustrates why stating the real problem you are trying to solve might be helpful when asking questions.
Joined: 5/26/2014
Posts: 7
Thanks for the reply. Here's what I'm trying to do (not really TAS related, sorry about that, if there's other active communities out there please redirect me). I come from the Mario Kart 64 community (www.mariokart64.com). I want to investigate some of the game mechanics to see under what conditions they occur. For example, in 150cc mode, if you correct your steering too much, you spin out. For some other things, I've found RAM addresses whose values relate to the game mechanism in question, then put read and/or write breakpoints on those addresses to find the code which is using them. In this case, I haven't found any relevant RAM values (I could probably try harder), so I thought it would helpful to find instructions that are only executed when doing this behavior in the game. To help with replies... I'm a software developer. For my job I write mostly backend java code. I did some projects related to MIPS assembly in college so I'm moderately familiar with assembly, but I'm not particularly fast at looking at a bunch of instructions and knowing immediately what the purpose of the code is.
Site Admin, Skilled player (1254)
Joined: 4/17/2010
Posts: 11478
Location: Lake Char­gogg­a­gogg­man­chaugg­a­gogg­chau­bun­a­gung­a­maugg
Warning: When making decisions, I try to collect as much data as possible before actually deciding. I try to abstract away and see the principles behind real world events and people's opinions. I try to generalize them and turn into something clear and reusable. I hate depending on unpredictable and having to make lottery guesses. Any problem can be solved by systems thinking and acting.
Joined: 10/20/2006
Posts: 1248
SeanSullivan86 wrote:
I want to investigate some of the game mechanics to see under what conditions they occur. For example, in 150cc mode, if you correct your steering too much, you spin out.
Find an address that indicates whether you are currently spinning out or not. Correct your steering too much with a breakpoint on write to that address. Then you should be inside some function related to it. It's very likely that it's not the actual function you are looking for though, you probably want to find some function that calls it. To find out what called the function you are currently in, sometimes all that needs to be done is to look at the RA register and subtract 8 to find the line of code that called it. If it instead leads to another function called from within the function you are currently in, the RA value you are looking for should be somewhere on the stack (SP plus or minus something, depending on the game). Alternatively, and very often faster, you could just find the end of that function, set a breakpoint there (click the grey area left of that line of code) and see where it returns to. Once you got the line that calls the function you're currently in, set a breakpoint there, repeat the process (i.e. load a save state, steer too much, this time the game should halt at the breakpoint you've manually set before the break on write). That way you can manually step through function calls backwards. Though for this particular case, it would probably be better to search for a value that increases (or decreases) (might even be a float) as you keep correcting your steering a lot. Then set a breakpoint on read and hope to find a branching command afterwards that compares it against some value depending on the mode you are currently in. If you're unlucky, this branch is inside another function that gets called by the function you are currently in. In that case, coming up with a creative solution might be faster than stepping through all those functions (that's assuming there'd be a lot of them). That's some general ideas. Edit:
SeanSullivan86 wrote:
I'm not particularly fast at looking at a bunch of instructions and knowing immediately what the purpose of the code is.
Find the start of that function and modify the first line to JR, the second to NOP. Or find the line that calls this function and change the JAL there to NOP. That way you patch the function (or that particular function call) out of the game. Very often, this leads to quick results, as it'd be obvious what kind of behavior is now missing from the game. To quickly patch a line of code, let's say you want to change 80034bac to JR, manually add a 4 byte value to your list of RAM values that corresponds to that line of code. Then freeze it to 03e00008 (that's JR RA, NOP would be 00000000). If you let your "Commands" window in Nemu refresh now (f.e. by using up or down arrow buttons), the code should appear to be updated already. However, Nemu won't execute it as that, as it executes most code from a buffer. Load a save state and your changes will be functional. Note: Be careful not to overwrite your save state after patching a line of code. That would lead to that line of code being permanently patched from that save state onwards. Another trick to speed up your deciphering of code (sometimes): If you aren't interested in all of the code within a certain code segment, but just what happens to a certain value, see which register it gets loaded into, set a break on register change, hit "Go", and you'll hit the next line that uses that value.
Joined: 5/26/2014
Posts: 7
Thanks Kuwaga. I appreciate the reply. I had already been using those techniques for following function calls backwards, but the part about editing out function calls might be helpful. I hadn't tried that before. I guess I'll try looking harder for a relevant memory address. Haven't tried too hard yet. I just realized I can't even produce this behavior in Nemu. I use bizhawk for finding memory addresses usually... and it's easy to produce the behavior there. I guess Nemu's input plugin is converting my left/right key presses into analog movements differently than bizhawk does. I still think dumping all the addresses of the instructions executed over a period of time would be a helpful thing. Seems that bizhawk has a lua hook event.onmemoryexecute , but it requires you the specify the address. I suppose calling it for every address probably wouldn't perform very well...
Joined: 10/20/2006
Posts: 1248
SeanSullivan86 wrote:
I guess I'll try looking harder for a relevant memory address. Haven't tried too hard yet. I just realized I can't even produce this behavior in Nemu. I use bizhawk for finding memory addresses usually... and it's easy to produce the behavior there. I guess Nemu's input plugin is converting my left/right key presses into analog movements differently than bizhawk does.
Ah, yes, Nemu is horrible in that regard. Maybe reconfiguring the input plugin would help. Else, you could maybe manually modify the address that indicates how far to the left or right you are pressing your analog stick. It should be closeby to Gameshark enabler codes in memory ("D0xxxxxx xxxx" or "D1xxxxxx xxxx", checking for whether you pressed a certain button).
SeanSullivan86 wrote:
I still think dumping all the addresses of the instructions executed over a period of time would be a helpful thing.
Maybe. Though I fear it would be so much data that browsing through it would almost always be slower than other approaches. It's not impossible to add that kind of functionality. Seems like something I could do with a very silly and dirty hack if I took the time, but I'm not convinced it'd be very beneficial yet. I agree that better debugging tools in general would be awesome to have for N64, which is why I started working on some very slowly. But that project is currently on hold for at least another month.
Player (146)
Joined: 7/16/2009
Posts: 686
SeanSullivan86 wrote:
Seems that bizhawk has a lua hook event.onmemoryexecute , but it requires you the specify the address. I suppose calling it for every address probably wouldn't perform very well...
Apart from the fact that it isn't implemented for the N64 core, that is. I know, I've tried to use it to figure out how the item tables are accessed.
Joined: 5/26/2014
Posts: 7
Ah, the docs ( http://tasvideos.org/Bizhawk/LuaFunctions.html ) say onmemoryexecute is supported for N64 but then later says onmemory* events are unsupported for N64. edit: well I found the memory address I was looking for. :) edit2 : I eventually found the assembly code. The address I was interested in was written to many times per frame, but I was interested in only the case where it was set to 0x40. I ended up attaching Cheat Engine to Project 64 (in Interpreter mode, not recompiler) and using the conditional breakpoints functionality to break when the address got the value I was interested in. Then you have to step forward in the CheatEngine debugger (through the x86 instructions that Project 64 is executing) until the MIPS $PC value shows up in one of the x86 registers (was just looking for a value like 0x800*****). I don't think it works if you run the emulator in Recompiler mode since the original MIPS $PC is irrelevant if the code is recompiled to x86.
Joined: 10/20/2006
Posts: 1248
That's pretty great! I haven't had to use that approach yet, but it seems to be a great idea! Usually, I just patch out the lines that do the reads/writes I'm not interested in, so I can use Nemu's break on read/write feature to find what I'm looking for. Works fine most of the time, though sometimes it doesn't do the trick and I need to find a workaround. Also, I haven't been able to find a memory address that corresponds to PC in my version of PJ64 (maybe haven't looked hard enough), but the RA register is at Project64.exe+D53E0 for me. If it isn't at that address for you, searching for the K0 register in memory should be easy (search for A430000C). The RA register should then be at the address of K0 + 0x28. Looking at that one could often save you the stepping through the x86 assembler code part. (The code you're looking for should either be inside the preceding function call or immediately afterwards)