ReplayTV Code Disassembly Notes

The ReplayTV uses an FPGA programmed with the MIPS instruction set as its CPU. I've written a MIPS disassembler to reverse engineer the code and learn how it works. I've used it to find Easter eggs and to explore the shell. It uses a text file (*.DAT) to tell it labels, comments and data tables.

If you find any bugs or create any DAT files, please let me know: Sean Riddle

mipsdasm.c (12/26/00)


disassembly tips

DAT files

MIPS links

Disassembly tips

MIPS processors are RISC- reduced instruction set CPUs. There aren't many opcodes, but lots of registers. The registers are named, but ReplayTV doesn't always use them in a standard manner. $zero always returns a 0 on reads, and writes don't do anything. $ra contains the return address to jump to after a subroutine call. $sp is the stack pointer. Most other registers are general purpose, but $a0 and $a1 are used a lot for function parameters. All registers are 32 bits in length, and the CPU requires 32-bit alignment (although there are some load and store byte commands that can access non-aligned data).

If you're used to disassembling CISC machine code, there are some interesting differences. Instruction pipelining causes delayed branches and loads. The CPU is already processing the next operand before it decides whether or not to branch. So when you look at machine code, the instruction that you see immediately following a branch was really executed before the branch.

Delayed loads mean that the results of the load are not available in the next opcode. This results in a lot of no-operation (NOOP) instructions being inserted. Both of these are confusing, but much easier to handle when disassembling code than when writing it!

JAL jumps to a subroutine, storing the return address in $ra. BEQ $zero,$zero is a branch always. Actually, a BEQ comparing any register to itself will always branch, but I've only noticed $zero used in this way.

JR $ra is a return from subroutine. mipsdasm automatically inserts a blank line after both of these instructions to help break up the code into logical chunks.

It's a good idea to go through and locate all the text in the file first, and put those addresses into the DAT file so mipsdasm doesn't try to disassemble them. Then look for obvious data chunks and put them into the DAT file. Then search the mipsdasm output file for ??? which indicated an unknown opcode. This is probably also data or text.

DAT files




PTV.BIN is the main program on the ReplayTV unit, so I started disassembling it first. The file has a 512-byte header that contains the header size as well as the binary file load address and length. PTV.BIN loads at $9C000000, so you'll see a lot of pointers in the code that start with 9C. Here are the top 20 subroutines that are called (I've figured some of them out):

  name/address                        count
  print_string_a0                      1566     
  process_str_ptr_a1                    796     
  save_t6_s0-s2                         612     
  restore_t6_s0-s2                      610     
  strcmp_a0_a1                          595     
  save_t6_s0-s3                         499     
  restore_t6_s0-s3                      497     
  save_t6_s0-s7_fp                      447     
  restore_t6_s0-s7_fp                   443     
  strlen_a0                             402     
  9C158834                              397     
  9C0B0F74                              372     
  save_t6_s0-s4                         364     
  restore_t6_s0-s4                      363     
  9C10DF3C                              343     
  9C0B9B9C                              311     
  9C0B97F8                              289     
  9C154CA0                              283     
  9C08FAD4                              262     
  9C14B1E0                              261     


SPIM- the MIPS emulator

MIPS opcodes

MIPS instruction reference

MIPS notes

notes on implementing MIPS on an FPGA

PSX hacking- it also uses a MIPS processor