This info is still rather preliminary. Take it with a grain of salt, so the saying goes, because I'm still learning. I've encountered 3 different methods of moving between threads, and I've only done 3 overall games(zelda 1.0 and 1.2 were the same).
HCS' USF Walkthrough. I didn't start here, but only because I didn't know it was around.
Feel free to just skim the document for a bit of help, as I do rips differently in many ways. I don't even touch IDA, and I'm up to my 4th rip(2nd unique).
http://www.hcs64.com/mboard/forum.php?showthread=776
Remember to grab info on PJ64 from there, such as how to force it to save a state at the program counter address you want. I do all my editing on the Save States, so I can test it out a little right before using it, and because it takes no program recompile time.
(I accomplish such writes by taking the address I want to edit, then adding 75C to it)
I also use a slightly edited Nemu64, version 0.8. The edit is that it doesn't compress the sections of the save state, which makes it easier to work with NEmu to find/kill threads and get song init addresses, as well as check to see if the data in the Nemu save state is the same as in PJ64 save states.
I'm also working on a save state conversion utility, but that's a ways off at the rate I'm going.
USF Ripping Stuff(most recent)
http://hcs64.com/usf/ripping/
Just get some Project 64 source code, and hack it up to log data reads from the ROM region.
04040010 written to for Dlist events(SP_STATUS_REG) (Written with 00002B00)
DLists are graphics processing. This can at least lead you to code trying to produce graphics.
Values of 8008 and 0125 are known to be rather edgy about being removed, and often kill them music or the whole execution process. 0125 preceeds a call to 80000180. 8008 can sometimes have the write itself coded out, but usually the routine(or code subsection) that uses it can't be removed. Some 2B00 writes are small routines that don't do much other than that, even though the same address is used for some other 04040010 writes. Ususally, you just step out and code out the direct access to the subroutine. This way, it's not in your way to test for additional writes of 2B00 that may be more important.
Globally relavent to USF ripping:
AM_RANGE(0x04500000, 0x045fffff) AM_READWRITE(n64_ai_reg_r, n64_ai_reg_w) // Audio Interface
04500000
Remember to trail SR64 segments with 00 00 00 00
Controller access register:
04800000 (Address at that location in RDRAM is for controller data)
HCS' PJ64 Ripping version:
May need to make the desired rip the second one done, as the first rip of a process seems to get certain error stuffs that happen to it. Should be easy to implement a dummy start-stop, then get it to continue ripping from the desired spot.
Threads:
Use ERET, combined with watching SP to determine if a thread is unique. Recording both is a good way to figure a good deal of info out pretty efficiently. Sometimes ERET goes directly to the threads, which means you should record SP, while sometimes it goes to a routine to handle threads, which means you should find the code that goes into the threads, then record whatever address is used for the jump(such as RA, if JR RA is used. It'll be a proper address by itself if this is used)
In the case of Wave Race, I had to find a common routine used between all thread that seems to dictate which one gets to go next. It's called, then returns to a point in another thread right after an identical call. In these cases, it may be just as good to break, go, break, go until you've sufficiently cleaned the thing out of useless threads. This type of system may also be succeptible to the below mentioned method setting a break in 2 threads, then finding the address that caused it to jump to the other thread's breakpoint. Keeping tabs on the rough location of the PC can make it easier to reproduce it multiple times, until you discover the top level address.
You should note that in most games, the threads that can't be killed safely are below address 80007FFF. Some of them can be, so it's always a good idea to try, but know the probability of a thead being needed increases as the address approaches 80000000.
Quake 2:
Thread Entrance: 80051F8C
(To find this, I just breakpointed a known thread, counted the number of runs between hitting the ERET and that thread, and then ran through code using 'Step Over' until the thread code ran. Then I did it again and stepped into the code that I was at right before I ended up at the thread code I was breaking on. This happens to be a JR RA instruction in Quake 2)
Hexen:
Start it up, then go to Plugins>Rom settings...
Change the line:
Interpreter=1
to
Interpreter=0
This will make it run fast, and let you use the debugger with it. It probably won't play right or at all, but the music will be more easily ripped.
HCS' USF Walkthrough. I didn't start here, but only because I didn't know it was around.
Feel free to just skim the document for a bit of help, as I do rips differently in many ways. I don't even touch IDA, and I'm up to my 4th rip(2nd unique).
http://www.hcs64.com/mboard/forum.php?showthread=776
Remember to grab info on PJ64 from there, such as how to force it to save a state at the program counter address you want. I do all my editing on the Save States, so I can test it out a little right before using it, and because it takes no program recompile time.
(I accomplish such writes by taking the address I want to edit, then adding 75C to it)
I also use a slightly edited Nemu64, version 0.8. The edit is that it doesn't compress the sections of the save state, which makes it easier to work with NEmu to find/kill threads and get song init addresses, as well as check to see if the data in the Nemu save state is the same as in PJ64 save states.
I'm also working on a save state conversion utility, but that's a ways off at the rate I'm going.
USF Ripping Stuff(most recent)
http://hcs64.com/usf/ripping/
Just get some Project 64 source code, and hack it up to log data reads from the ROM region.
04040010 written to for Dlist events(SP_STATUS_REG) (Written with 00002B00)
DLists are graphics processing. This can at least lead you to code trying to produce graphics.
Values of 8008 and 0125 are known to be rather edgy about being removed, and often kill them music or the whole execution process. 0125 preceeds a call to 80000180. 8008 can sometimes have the write itself coded out, but usually the routine(or code subsection) that uses it can't be removed. Some 2B00 writes are small routines that don't do much other than that, even though the same address is used for some other 04040010 writes. Ususally, you just step out and code out the direct access to the subroutine. This way, it's not in your way to test for additional writes of 2B00 that may be more important.
Globally relavent to USF ripping:
AM_RANGE(0x04500000, 0x045fffff) AM_READWRITE(n64_ai_reg_r, n64_ai_reg_w) // Audio Interface
04500000
Remember to trail SR64 segments with 00 00 00 00
Controller access register:
04800000 (Address at that location in RDRAM is for controller data)
HCS' PJ64 Ripping version:
May need to make the desired rip the second one done, as the first rip of a process seems to get certain error stuffs that happen to it. Should be easy to implement a dummy start-stop, then get it to continue ripping from the desired spot.
Threads:
Use ERET, combined with watching SP to determine if a thread is unique. Recording both is a good way to figure a good deal of info out pretty efficiently. Sometimes ERET goes directly to the threads, which means you should record SP, while sometimes it goes to a routine to handle threads, which means you should find the code that goes into the threads, then record whatever address is used for the jump(such as RA, if JR RA is used. It'll be a proper address by itself if this is used)
In the case of Wave Race, I had to find a common routine used between all thread that seems to dictate which one gets to go next. It's called, then returns to a point in another thread right after an identical call. In these cases, it may be just as good to break, go, break, go until you've sufficiently cleaned the thing out of useless threads. This type of system may also be succeptible to the below mentioned method setting a break in 2 threads, then finding the address that caused it to jump to the other thread's breakpoint. Keeping tabs on the rough location of the PC can make it easier to reproduce it multiple times, until you discover the top level address.
You should note that in most games, the threads that can't be killed safely are below address 80007FFF. Some of them can be, so it's always a good idea to try, but know the probability of a thead being needed increases as the address approaches 80000000.
Quake 2:
Thread Entrance: 80051F8C
(To find this, I just breakpointed a known thread, counted the number of runs between hitting the ERET and that thread, and then ran through code using 'Step Over' until the thread code ran. Then I did it again and stepped into the code that I was at right before I ended up at the thread code I was breaking on. This happens to be a JR RA instruction in Quake 2)
Hexen:
Start it up, then go to Plugins>Rom settings...
Change the line:
Interpreter=1
to
Interpreter=0
This will make it run fast, and let you use the debugger with it. It probably won't play right or at all, but the music will be more easily ripped.
Comment