Radeon R100 CP Engine Microcode

Chad Jablonski - 2026-11-06

The CP engine1 is a packet processor used by ATI GPUs beginning with the Rage 128 and continuing through the Radeon X1000 (R500)2, roughly from 1998-2005. It processes command packets fed to it from a queue (FIFO) usually bus mastered via AGP or PCI GART.

These command packets replaced programming the chip directly using memory mapped registers (MMIO). The job of the CP engine is pretty simple. Read command packets from the FIFO and translate them into a series of register writes as quickly as possible.

The primary benefit of replacing direct MMIO programming with the CP engine was that it allowed commands to be quickly written to buffers in memory and then asynchronously drained and processed without involvement of the CPU. But a secondary benefit was that ATI was able to encode some common usage patterns into the packet format, which made life at least a little easier for driver authors. For this reason, the CP engine itself is a bit more complicated than “just copy data from memory into registers”. The contents of the packets often need to be broken apart and operated on before the actual register writes make it out the other side.

Because the internal operations of the CP engine itself aren’t trivial, ATI’s design included a replaceable microcode that defines the processing of packets. Presumably this was done so that the engine could be reused and bugs could be fixed if they were later found. This microcode is what I’m aiming to understand.

The Packets ↑

A packet is just a series of 32-bit dwords that the CP engine picks off of the FIFO one at a time and processes. Each packet has a header word followed in most cases by a number of data words.

There are 4 types of packets that can be sent to the CP engine. And two of those types have sub-types of their own. Just a quick rundown of what these look like at a high level.

Type-0 ↑

Type-0 packets write Count values to registers.

31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 0 0 Count - 1 (dwords) a Base Index Register Data 1 … Register Data N

There are two sub-types of this packet determined by the a flag. When a is unset, data words are written to consecutive registers starting with Base Index. When a is set, each data word is written to Base Index. This second variant is useful for registers that increment an internal index on write, like PALETTE_DATA.

Type-1 ↑

Type-1 packets write values to two registers. From the drivers I’ve seen this goes unused.

31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 0 1 Reserved Register Index 2 Register Index 1 Register Data 1 Register Data 2

Type-2 ↑

Type-2 packets are NOPs used for filler and alignment.

31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 1 0 Reserved

Type-3 ↑

Type-3 packets are much more complex than the others. They are used to execute an operation determined by the IT_OPCODE field.

31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 1 1 Count c IT_OPCODE Reserved Data 1 … Data N

There are around 26 IT_OPCODEs for the R100, although many of them overlap and are essentially aliases. The opcode determines the layout of the data that follows. One of the simpler examples is HOSTDATA_BLT (opcode 0x14). This op is used to copy bitmaps into video memory. Most often this means copying characters of text to the framebuffer for display. The following dwords are placed in the data slots shown above:

31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 Foreground Color Background Color Char 1 … Char M

This is simplified in that Char 1-M are themselves data blocks that contain multiple dwords describing the character to be copied. But this should at least illustrate how these packets are constructed.

The Microcode ↑

The CP engine is controlled by a 256-qword microcode, loaded by the driver. Each qword is an instruction numbered from 0x00 to 0xff and is displayed split into two dwords (high and low). The format of an instruction changed slightly over the chip’s lifetime but the changes appear to have been pretty minor3. All of my testing and reverse engineering work was done using the CP engine of the R100. This means we’ll be looking at R100_cp.bin.

The Encoding ↑

This being my first experience reversing something of this sort, I made a lot of bad assumptions. The biggest one was that the microcode was encoded roughly in the way an assembly language would be. I spent a lot of time trying to figure out what the “opcode” for a given instruction could be.

One of the first steps was to look at the frequency of values in the bottom byte of the high dword. Most of the values were 0x02, with 0x00 coming in distant second.

The 0x00 instructions looked suspiciously like data:

Instruction High Dword Low Dword
⋮
0x04 0x00000000 0x6f5b4d4c
0x05 0x00000000 0x4c4c427f
0x06 0x00000000 0x5b568a92
0x07 0x00000000 0x4ca09c6d
0x08 0x00000000 0xad4c4c4c
0x09 0x00000000 0x4ce1af3d
0x0a 0x00000000 0xd8afafaf
0x0b 0x00000000 0xd64c4cdc
0x0c 0x00000000 0x4cd10d10
⋮

On the other hand, 0x02 instructions looked a lot more like instructions:

Instruction High Dword Low Dword
⋮
0x7f 0x00000002 0x000380e6
0x80 0x00000002 0x040025c5
0x81 0x00000002 0x00007576
0x82 0x00000002 0x00007577
0x83 0x00000002 0x0000750e
0x84 0x00000002 0x0000750f
0x85 0x00000002 0x00a05000
⋮

The frequency of 0x02, combined with another early discovery, led me to believe that 0x02 had to be an opcode. Many of the instructions beginning with 0x02 ended with a field that encoded an MMIO register address. Instructions 0x81 and 0x82 above are a perfect example of this. The bottom 12 bits contain 0x576 and 0x577 respectively. These didn’t immediately look like register addresses. But then I realized that when register addresses are present in a packet header (specifically Base Index and Register Index 1/2 fields above) they are always in units of dwords (not bytes).

Just like the packet headers, this 12-bit field is a register address in dwords. 0x576 << 2 becomes 0x15d8 or DP_SRC_FRGD_CLR. And 0x577 << 2 becomes 0x15dc or DP_SRC_BKGD_CLR. Foreground and background color, two registers that are very likely to be written together. And in fact, if you remember, the packet definition of HOSTDATA_BLT shown above includes values for these two registers back-to-back in its data block. Definitely not a coincidence. So the thinking was that these are obviously instructions that write to these registers. This must be a MOV instruction of some sort and 0x02 is the opcode.

After the address field discovery I was able to start seeing regions that seemed to belong to specific packet types. But before I could really start to isolate fields and understand the encoding more fully, I needed to be able to write my own microcode.

Writing my own microcode ↑

Fortunately, it’s incredibly easy to load new microcode into the CP engine. Without getting too much into the weeds, this is done with a series of MMIO writes to indexed registers. It can even be done while the CP engine itself is running. The engine also implements a mode that allows packets to be sent via register writes instead of the more typical asynchronous bus mastering mode. All of this means that the feedback loop for testing can be very quick.

I may eventually write a bit more about the test environment and how the hardware is set up. But for now, the specific card I am testing on is the Radeon QY (RV100), PCI ID 0x5159. And tests are run via a baremetal test environment implemented in the ati-tests repo.

My first step was to try zeroing out whole sections of the stock microcode just to see if I could break things. The goal being to find a minimal microcode to start from when writing my own.

From this I learned that the microcode has multiple entrypoints. And these entrypoints are determined by the packet type. For example, Type-0 packets will execute with a single non-zero instruction (instruction 0x00). I could zero the rest of the microcode and the engine would happily continue to process Type-0 packets while hanging on any other type. These are the entrypoints I was able to find:

Packet Type Entrypoint Instruction
Type-0 0x00
Type-0 (“a” flag set) 0x01
Type-1 0x02
Type-2 None
Type-3 (“c” flag set) 0x03
Type-3 0x04*

*Sort of, it’s complicated

Type-2 packets have no entrypoint. Given that these are NOPs I have to assume that the engine handles these in hardware and never dispatches them to the microcode. But the really interesting packet is Type-3.

Type-3 entrypoints ↑

I found that there are two flavors of Type-3 packet. Bit 15, or the c flag of the Type-3 header is what differentiates these two types. The “Rage 128 Software Development Guide” calls these packets “2D Packets”. When this bit is set the packet is required to include a settings block before any IT_OPCODE specific data blocks. The settings block is closely related to the DP_GUI_MASTER_CNTL register and allows the setting of many of the parameters commonly used for 2D operations.

The interesting, undocumented, part is that all of the “2D Packets” can also can be constructed with the c flag unset. In that case no settings block is present in the packet and just the IT_OPCODE specific parts are executed.

The way this works as far as entrypoints is that a c flag packet enters at instruction 0x03. In the stock microcode this handler immediately jumps to a subroutine that parses and processes the settings block. Without getting too deep into the mechanism here, the settings block subroutine eventually returns and the packet is re-dispatched using a jump table beginning at instruction 0x04. The non-c variant just immediately dispatches to the jump table, skipping the instruction 0x03 entry and settings subroutine entirely.

The jump table ↑

The jump table in the stock microcode begins at instruction 0x04 and continues through instruction 0xc. At least in theory though it could be a larger region as you’ll see.

Each instruction in the table contains 4 destinations, 1 byte each, all in the low dword. This means each entry has an addressable range matching the size of the microcode RAM. The table is byte indexed using the 7-bit IT_OPCODE field of the Type-3 packet. The indexing begins at instruction 0x00 not 0x04, and bytes are packed little endian. So for example a NOP packet has an IT_OPCODE of 0x10. This means instructions 0x00-0x03 containing bytes 0-15 are skipped entirely and we land on the lowest byte of instruction 0x04 with a destination of 0x4c, an immediate jump to the subroutine at instruction 0x4c.

Instruction High Dword Low Dword
0x00 0x00000000 0x21007000
0x01 0x00000000 0x20007000
0x02 0x00000004 0x000000b4
0x03 0x00000004 0x000000b8
0x04 0x00000000 0x6f5b4d4c
⋮

Once I knew that 0x00 was the entrypoint for Type-0 packets it should have been obvious that the low byte of the high dword couldn’t be an opcode. The “opcode” for that instruction in the stock microcode is 0x00, which is what I thought data looked like. I admit for a while I convinced myself that this may not be the entrypoint at all and may just be configuration of some sort for Type-0 packets. It was hard to believe that a single instruction could cover the entirety of Type-0 packet processing.

Experiments ↑

With the discovery of entrypoints and the jump table the structure of the microprogram started to emerge. The jump table gave me labels for Type-3 handlers. That combined with the knowledge that some instructions contained register offsets meant that it was pretty easy to pull out an instruction that was clearly just writing the value of the current packet word through to a register. The previously mentioned instruction 0x81 is an example of this:

Instruction High Dword Low Dword
⋮
0x81 0x00000002 0x00007576
⋮

So I copied this instruction into instruction 0x00:

Instruction High Dword Low Dword
0x00 0x00000002 0x00007576
⋮

and sent a minimal Type-0 packet:

0x00000000 0xdeadbeef

Two packet words, the first being a Type-0 header and the second data. A Type-0 packet must have at least one dword of data.

And after execution, sure enough, 0xdeadbeef was written to register 0x15d8 (0x576 << 2). This meant that instruction 0x00 was without question an entrypoint and not just configuration. Kind of amazing in that this single instruction must then fully process a Type-0 packet.

So what’s the deal with the 0x00000000 in the high dword of the original 0x00 instruction? I tried zeroing it for the replacement:

Instruction High Dword Low Dword
0x00 0x00000000 0x00007576
⋮

and I sent the same packet.

0x00000000 0xdeadbeef

To my surprise the result was exactly the same4. At this point I had to let go of the opcode theory altogether, 0x2 isn’t a MOV. As I understood more and more of the instruction fields it became clear that all instructions shared the same encoding and there was no overall opcode. And at right around this time I also stumbled across a description of horizontal microcode 5.

Horizontal Microcode ↑

And so, it turns out that this is a horizontal microcode, which largely avoids the sort of decoding that a machine code for a more traditional CPU would require. In general machine code encodes an instruction with an opcode and operands and then a decoder determines how the functional units of the processor executes the instruction. A horizontal microinstruction is composed of bits that control those functional units directly, sans-decoder6.

For example, an encoded x86 instruction may look like:

b8 ef be ad de

b8 is the opcode which encodes “move 32-bit immediate into EAX” and is followed by the immediate value 0xdeadbeef (little endian, hence the backwardsness.)

A microinstruction for the CP engine on the other hand:

00 00 00 02 00 00 75 76

There is no single opcode telling us what the operation is here. In fact, multiple things may be happening at the same time. It’s easiest to view this broken down by field:

  63-40   39-32   31-15      14        13       12        11-0
┌───────┬───────┬───────┬───────────┬───────┬──────────┬────────┐
│   …   │  ???  │   …   │  advance  │ write │ latchpkt │  addr  │
│   0   │  0x02 │   0   │     1     │   1   │    1     │  0x576 │
└───────┴───────┴───────┴───────────┴───────┴──────────┴────────┘

advance fetches the next packet word in the packet FIFO, latchpkt stores it in the pkt register and write writes the packet out to the register in addr.

Our 0x02 “opcode” turns out to be something else entirely. Even when I thought this was an opcode, it was a bit odd that the values tended to be mostly even.

“opcode” Instruction Count
0x00 60
0x02 128
0x04 18
0x06 13
0x08 2
0x0c 4
0x12 2
0x14 1
0x16 4
0x18 12
0x1a 3
0x1c 2
0x1e 3
0x20 2
0x21 2

If the entire byte was an opcode you would expect to see both even and odd values in use without a lot of gaps. Now, it could have been that the odd opcodes were used less frequently or not at all in the stock microcode. But the easier explanation was that my idea of the size of this field was just wrong. My assumption was that the field was the bottom byte. But it turned out to make much more sense if the bottom bit was instead either a flag or part of a different field. Once you do that the “holes” in the opcodes (for the most part) disappear.

Unknown Field Instruction Count
0x00 60
0x01 128
0x02 18
0x03 13
0x04 2
0x06 4
0x09 2
0x0a 1
0x0b 4
0x0c 12
0x0d 3
0x0e 2
0x0f 3
0x10 4

So the previous value of 0x02 becomes 0x01 in this interpretation of the field. And based on the observed values, the highest being 0x10, it’s not crazy to make the assumption that this is a 5-bit field:

  63-38   37-33   32-15      14        13       12        11-0
┌───────┬───────┬───────┬───────────┬───────┬──────────┬────────┐
│   …   │  ???  │   …   │  advance  │ write │ latchpkt │  addr  │
│   0   │  0x01 │   0   │     1     │   1   │    1     │  0x576 │
└───────┴───────┴───────┴───────────┴───────┴──────────┴────────┘

What is the field? ↑

Then what is the field? The packet entrypoint instructions offer some pretty big clues. Let’s look at the Type-0 and Type-1 entrypoints.

Type-0 with “a” flag set (entrypoint 0x01) ↑

Instruction ↑

0x00000000 0x20007000

Fields ↑

  63-38   37-33    32-15       14        13       12       11-0
┌───────┬───────┬────────┬───────────┬───────┬──────────┬────────┐
│   …   │  ???  │   …    │  advance  │ write │ latchpkt │  addr  │
│   0   │  0x00 │ 0x2000 │     1     │   1   │    1     │  0x000 │
└───────┴───────┴────────┴───────────┴───────┴──────────┴────────┘

Type-1 (entrypoint 0x02) ↑

Instruction ↑

0x00000004 0x000000b4

Fields ↑

  63-38   37-33    32-15       14        13       12       11-0
┌───────┬───────┬────────┬───────────┬───────┬──────────┬────────┐
│   …   │  ???  │   …    │  advance  │ write │ latchpkt │  addr  │
│   0   │  0x02 │   0    │     0     │   0   │    0     │  0x0b4 │
└───────┴───────┴────────┴───────────┴───────┴──────────┴────────┘

Let’s start by looking at the Type-1 instruction, there’s a bit less going on. In fact there are really only two non-zero fields in the instruction, our ??? field and a value in the addr field. Up until this point values in the addr field have been registers targeted for a write. But the value doesn’t really make sense as a card register 0xb4 << 2 = 0x2d0 = LVDS_GEN_CNTL which has something to do with an interface for a flat panel. Clearly not something that makes sense for a generic Type-1 packet handler. And on top of that the write flag isn’t even set. The value is small though. Small enough to be the address of an instruction in the microcode itself. This almost has to be a jump instruction. The instructions located at 0xb4 look like this:

Instruction High Dword Low Dword
⋮
0xb4 0x00000002 0x01200000
0xb5 0x00000002 0x20077000
0xb6 0x00000002 0x01200000
0xb7 0x00000002 0x20007000
⋮

These at least plausibly look like an instruction handler. In fact 0xb7 is nearly identical to the instruction at the Type-0 (with a flag) entry point. Which would make sense. Both of these packet types are just copying values from the packet into registers specified in the packet header.

There is one big difference between the two packet types though. Type-1 packets are really simple in that they always just write to two registers. The packet has two data dwords that contain the values to be written to those registers. So you would need to read each of those dwords and write them out. Even though we don’t understand all of the fields in these instructions yet you can see that this handler is advancing the packet FIFO twice, meaning it is stepping through the data dwords in the packet. This can be seen in instructions 0xb5 and 0xb7.

       63-38   37-33    32-15       14        13       12       11-0
     ┌───────┬───────┬────────┬───────────┬───────┬──────────┬────────┐
     │   …   │  ???  │    …   │  advance  │ write │ latchpkt │  addr  │
0xb5 │   0   │  0x01 │ 0x2007 │     1     │   1   │    1     │  0x000 │
     ├───────┴───────┴────────┴───────────┴───────┴──────────┴────────┤
0xb6 │                              …                                 │
     ├───────┬───────┬────────┬───────────┬───────┬──────────┬────────┤
0xb7 │   …   │  ???  │    …   │  advance  │ write │ latchpkt │  addr  │
     │   0   │  0x01 │ 0x2000 │     1     │   1   │    1     │  0x000 │
     └───────┴───────┴────────┴───────────┴───────┴──────────┴────────┘

Unfortunately, it isn’t really easy/possible to prove this bit through static analysis. But I found in testing that a program will run until the packet FIFO is exhausted and then halt and wait for a new packet. Given that we know that Type-1 packets always have two dwords of data and we see two advance instructions execution will halt after 0xb7.

Type-0 packets on the other hand need to do a variable number of writes based on the count in their packet header. This alone implies some sort of loop. But let’s look at the entry point:

       63-38   37-33    32-15       14        13       12       11-0
     ┌───────┬───────┬────────┬───────────┬───────┬──────────┬────────┐
     │   …   │  ???  │   …    │  advance  │ write │ latchpkt │  addr  │
0x01 │   0   │  0x00 │ 0x2000 │     1     │   1   │    1     │  0x000 │
     └───────┴───────┴────────┴───────────┴───────┴──────────┴────────┘

Notice that the only difference here is the 0x00 value in the ??? field. Otherwise this is the 0xb7 instruction again. What this entry point isn’t is another jump. It also wouldn’t exhaust the packet FIFO in most cases.7

These three examples combined point to ??? actually being a sequencer field or seq. Or more simply a field that controls the program counter in the program. A seq of 0x2 set the program counter to 0xb4 in the Type-1 entry point, this is a jmp. Next, a seq of 0x01, the most common, is used in every single instruction of the Type-1 handler after the jump. This just increments the program counter. It’s a next instruction or nxt. And finally the most surprising, and what proved me wrong about a single instruction being able to implement a whole handler: 0x00 is a repeat or rpt. The program counter is untouched and the program continues executing the same instruction until being parked by exhausting the packet FIFO. In other words the instruction at 0x01 will run repeatedly each time advancing the packet FIFO and finally halting.

What now? ↑

This doesn’t cover all that is known about the CP engine but I hope that it gives a decent feel for how the machine works conceptually. With the general outline of the machine in place a lot of the remaining work was (and is) filling in the unknown fields/control lines and decoding ops for the individual units like the alu and sequencer. The very rough documentation thus far from that process is below. Right now my biggest focus is on trying to understand the block of bits b21-b25 and the remaining sequencer ops. There are notes on this and I have some theories about the remaining bits. Some of which even hold up well in isolation but feel fuzzy when viewed as a whole.


1 It’s called the CCE engine by Rage 128 docs and CP engine by subsequent GPUs

2 This is based on the firmware in the linux-firmware repo.

3 More investigation is needed but initially it appears that the addr field increased width by one bit from R128 -> R100 and then again from R100 -> R200. Which makes some sense in that the register space grew. Semantics are another thing entirely and will require testing on actual hardware.

4 Well, roughly the same. At least from what I could tell at this point.

5 The best coverage of this that I found was Chapter 4 of an old version of Tanenbaum’s Structured Computer Organization (archive.org/details/structuredcomput0000tane). But sadly, it’s not borrowable on archive.org.

6 That’s not to say that there’s no decoding going on but the overall instruction isn’t decoded. Cases where fields are decoded are owned by their functional units (the sequencer and ALU for example).

7 save for count = 0, meaning a single write

Documentation

Microinstruction Encoding

37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 seq b32 cond mcsel idxsel b28 src b25 b24 b23 b22 b21 alu advance write latchpkt addr

seq [37:33]

The SEQ field controls the program counter and the flow of the program.

Values

0x00 rpt

Do not increment the program counter. Execute this instruction repeatedly. Often used with the advance field to drain the pkt FIFO.

Evidence
  • seq-rpt-drain-fifo
    seq b32 cond mcsel idxsel b28 src b25 b24 b23 b22 b21 alu advance write latchpkt addr
    entry:
    0x00 jmp • • • • • pkt • • • • • 0x000 • • • 0xa0
    0x0 159 times
    0xa0 rpt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG0
    0xa1 rpt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG1
    0x0 94 times
    Packet
    Header 0x00010000
    Data0 0xdeadbeef
    Data1 0x1337fee7
    State After Execution
    GUI_SCRATCH_REG0: 0x1337fee7
    GUI_SCRATCH_REG1: 0x00000000
    Notes

    advance is set to advance the packet FIFO and decrement the packet word count. latchpkt is set to latch the current packet word into the pkt register.

  • seq-rpt-does-not-inc-pc
    seq b32 cond mcsel idxsel b28 src b25 b24 b23 b22 b21 alu advance write latchpkt addr
    entry:
    0x00 jmp • • • • • pkt • • • • • 0x000 • • • 0xa0
    0x01 jmp • • • • • pkt • • • • • 0x000 • • • 0xa1
    0x0 158 times
    0xa0 rpt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG0
    0xa1 rpt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG1
    0x0 94 times
    Packet
    Header 0x00000000
    Data0 0xdeadbeef
    State After Execution
    GUI_SCRATCH_REG0: 0xdeadbeef
    GUI_SCRATCH_REG1: 0x00000000
    Notes

    latchpkt and advance are not set so the pktfifo is not drained and the packet word counter is not decremented. This appears to wedge the CP engine. If a second packet with an entrypoint at 0x01 is sent it also never writes to GUI_SCRATCH_REG1. My read is that without advance decrementing the packet word count and without an advancing PC, instruction 0xa0 enters an infinite loop.

0x01 nxt

Increment the program counter.

0x02 jmp

Set the program counter to the value in the ADDR field. An unconditional jump.

Evidence
  • seq-jmp-unconditional
    seq b32 cond mcsel idxsel b28 src b25 b24 b23 b22 b21 alu advance write latchpkt addr
    entry:
    0x00 jmp • • • • • pkt • • • • • 0x000 • • • 0xa0
    0x01 nxt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG0
    0x0 158 times
    0xa0 nxt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG1
    0x0 95 times
    Packet
    Header 0x00000001
    Data0 0xdeadbeef
    State After Execution
    GUI_SCRATCH_REG0: 0x00000000
    GUI_SCRATCH_REG1: 0x00000001
    Notes

    GUI_SCRATCH_REG1 is written to but GUI_SCRATCH_REG0 is jumped over and is not written to.

  • seq-jmp-loop-wedge
    seq b32 cond mcsel idxsel b28 src b25 b24 b23 b22 b21 alu advance write latchpkt addr
    entry:
    0x00 jmp • • • • • pkt • • • • • 0x000 • • • 0x00
    0x01 nxt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG0
    0x0 254 times
    Packet
    Header 0x00000000
    Data0 0xdeadbeef
    Data1 0x00008000
    Data2 0x1337fee7
    State After Execution
    GUI_SCRATCH_REG0: 0x00000000
    GUI_SCRATCH_REG1: 0x00000000
    Notes

    This creates an infinite loop using a self-referential jump instruction. The second packet is never processed showing that the CP engine is wedged.

0x03 ret

Pop the last address off of the (2 slot) stack and jump to it. ret on an empty stack halts.

This is used by the c variant of type-3 packets. The hardware places the address found in the jump table for the packet type on the stack. After the common settings are processed ret is called, popping the IT_OPCODE-specific handler address off the stack and jumping to it.

Evidence
  • seq-ret-dispatches-to-jump-table
    seq b32 cond mcsel idxsel b28 src b25 b24 b23 b22 b21 alu advance write latchpkt addr
    0x0 3 times
    entry:
    0x03 jmp • • • • • pkt • • • • • 0x000 • • • 0xf0
    0x04 0x00000000 0x00aa0000
    0x0 165 times
    0xaa rpt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG0
    0x0 69 times
    0xf0 ret • • • • • pkt • • • • • 0x000 • • • 0x000
    0x0 15 times
    Packet
    Header 0xc0009200
    Data0 0xdeadbeef
    State After Execution
    GUI_SCRATCH_REG0: 0xdeadbeef
    Notes

    A type-3 packet with the c flag enters at 0x03. The hardware must also look up the IT_OPCODE in the jump table and place the address found on the stack. A ret then pops the handler address off the stack and jumps to it.

  • seq-ret-will-not-dispatch-type-0-packets
    seq b32 cond mcsel idxsel b28 src b25 b24 b23 b22 b21 alu advance write latchpkt addr
    entry:
    0x00 jmp • • • • • pkt • • • • • 0x000 • • • 0xf0
    0x0 3 times
    0x04 0x00000000 0x00aa0000
    0x0 165 times
    0xaa rpt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG0
    0x0 69 times
    0xf0 ret • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG1
    0xf1 rpt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG2
    0x0 14 times
    Packet
    Header 0x00001200
    Data0 0xdeadbeef
    State After Execution
    GUI_SCRATCH_REG0: 0x00000000
    GUI_SCRATCH_REG1: 0xdeadbeef
    GUI_SCRATCH_REG2: 0x00000000
    Notes

    ret will not dispatch for type-0 packets. It also does not advance the PC. Type-0 packets don’t require a jump table, so it makes sense that they wouldn’t place anything on the stack leading to a stack underflow (and a halt).

  • seq-call-and-return
    seq b32 cond mcsel idxsel b28 src b25 b24 b23 b22 b21 alu advance write latchpkt addr
    0x0 4 times
    0x04 0x00000000 0x00a00000
    0x0 155 times
    entry:
    0xa0 nxt • • • • • pkt • • • • • ld #addr • • • 0xf0
    0xa1 call • • • • • pkt • • • • • 0x000 • • • 0xb0
    0xa2 nxt • • • • • alu • • • • • 0x000 • • • GUI_SCRATCH_REG0
    0xa3 nxt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG2
    0xa4 nxt • • • • • pkt • • • • • ld #addr • • • 0xf1
    0xa5 call • • • • • pkt • • • • • 0x000 • • • 0xc0
    0xa6 nxt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG3
    0xa7 rpt • • • • • alu • • • • • 0x000 • • • GUI_SCRATCH_REG1
    0x0 8 times
    0xb0 ret • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG0
    0x0 15 times
    0xc0 ret • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG1
    0x0 47 times
    0xf0 0x00000000 0xaaaaaaaa
    0xf1 0x00000000 0xbbbbbbbb
    0x0 14 times
    Packet
    Header 0xc0011200
    Data0 0xdeadbeef
    Data1 0xcafebeef
    State After Execution
    GUI_SCRATCH_REG0: 0xaaaaaaaa
    GUI_SCRATCH_REG1: 0xbbbbbbbb
    GUI_SCRATCH_REG2: 0xdeadbeef
    GUI_SCRATCH_REG3: 0xcafebeef
  • seq-call-and-return-has-two-entry-stack
    seq b32 cond mcsel idxsel b28 src b25 b24 b23 b22 b21 alu advance write latchpkt addr
    0x00 rpt • • • • • pkt • • • • • 0x000 • • • BIOS_1_SCRATCH
    0x0 3 times
    0x04 0x00000000 0x00a00000
    0x0 155 times
    entry:
    0xa0 nxt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG5
    0xa1 call • • • • • pkt • • • • • 0x000 • • • 0xb0
    0xa2 nxt • • • • • pkt • • • • • ld #addr • • • 0xf0
    0xa3 rpt • • • • • alu • • • • • 0x000 • • • GUI_SCRATCH_REG0
    0x0 12 times
    0xb0 call • • • • • pkt • • • • • 0x000 • • • 0xc0
    0xb1 nxt • • • • • pkt • • • • • ld #addr • • • 0xf1
    0xb2 ret • • • • • alu • • • • • 0x000 • • • GUI_SCRATCH_REG1
    0x0 13 times
    0xc0 call • • • • • pkt • • • • • 0x000 • • • 0xd0
    0xc1 nxt • • • • • pkt • • • • • ld #addr • • • 0xf2
    0xc2 ret • • • • • alu • • • • • 0x000 • • • GUI_SCRATCH_REG2
    0xc3 rpt • • • • • alu • • • • • 0x000 • • • GUI_SCRATCH_REG5
    0x0 12 times
    0xd0 call • • • • • pkt • • • • • 0x000 • • • 0xe0
    0xd1 nxt • • • • • pkt • • • • • ld #addr • • • 0xf3
    0xd2 ret • • • • • alu • • • • • 0x000 • • • GUI_SCRATCH_REG3
    0x0 13 times
    0xe0 nxt • • • • • pkt • • • • • ld #addr • • • 0xf4
    0xe1 nxt • • • • • pkt • • • • • 0x000 • • • 0x000
    0xe2 nxt • • • • • pkt • • • • • 0x000 • • • BIOS_0_SCRATCH
    0xe3 ret • • • • • alu • • • • • 0x000 • • • GUI_SCRATCH_REG4
    0x0 12 times
    0xf0 0x00000000 0xaaaaaaaa
    0xf1 0x00000000 0xbbbbbbbb
    0xf2 0x00000000 0xcccccccc
    0xf3 0x00000000 0xdddddddd
    0xf4 0x00000000 0xeeeeeeee
    0x0 11 times
    Packet
    Header 0xc0031200
    Data0 0xdeadbeef
    Data1 0xcafebeef
    Data2 0x1337beef
    Data3 0x1337fee7
    State After Execution
    GUI_SCRATCH_REG0: 0x00000000
    GUI_SCRATCH_REG1: 0x00000000
    GUI_SCRATCH_REG2: 0xcccccccc
    GUI_SCRATCH_REG3: 0xdddddddd
    GUI_SCRATCH_REG4: 0xeeeeeeee
    GUI_SCRATCH_REG5: 0xdeadbeef
    BIOS_0_SCRATCH: 0xcafebeef
    BIOS_1_SCRATCH: 0x00000000
    Notes

    I suspected for a while that ret on an empty stack would return to the type-3 jump table. This no longer appears to be the case. There is an advance at 0xe1, which means that we are making progress through the pktfifo when this call chain executes.

    If the whole chain ran more than once we would expect to see different values in REG5 and BIOS0. The test shows that REG5 gets the first data packet word, BIOS0 gets the second. If the ret stack underflow on 0xc2 dispatched to the jump table the entire chain will run again and we would see later packet words. I believe it would settle on REG5=0x1337beef and BIOS0=0x1337fee7. It does not.

    So on underflow it looks like execution stops, either wedging or waiting for the next packet.

0x04–0x05 ?
0x06 jcz.l

This is a bit of a work in progress. The theory is that there is a count register of some sort that is selected and written to via b21-b23. jcz.l jumps when the low word of that register is zero. This needs more investigation.

Evidence
  • jcz.l-jumps-when-count.l-is-zero
    seq b32 cond mcsel idxsel b28 src b25 b24 b23 b22 b21 alu advance write latchpkt addr
    entry:
    0x00 jmp • • • • • pkt • • • • • 0x000 • • • 0xa0
    0x0 159 times
    0xa0 nxt • • • • • pkt • • • • • 0x000 • • • 0x000
    0xa1 jcz.l • • • • • pkt • • • • • 0x000 • • • 0xb0
    0xa2 nxt • • • • • pkt • • • • • ld #addr • • • 0xf0
    0xa3 rpt • • • • • alu • • • • • 0x000 • • • GUI_SCRATCH_REG0
    0x0 12 times
    0xb0 nxt • • • • • pkt • • • • • ld #addr • • • 0xf1
    0xb1 rpt • • • • • alu • • • • • 0x000 • • • GUI_SCRATCH_REG1
    0x0 62 times
    0xf0 0x00000000 0xdeadbeef
    0xf1 0x00000000 0xcafebeef
    0x0 14 times
    Packet
    Header 0x00000001
    Data0 0xffff0000
    State After Execution
    GUI_SCRATCH_REG0: 0x00000000
    GUI_SCRATCH_REG1: 0xcafebeef
  • jcz.l-does-not-jump-when-count.l-is-nonzero
    seq b32 cond mcsel idxsel b28 src b25 b24 b23 b22 b21 alu advance write latchpkt addr
    entry:
    0x00 jmp • • • • • pkt • • • • • 0x000 • • • 0xa0
    0x0 159 times
    0xa0 nxt • • • • • pkt • • • • • 0x000 • • • 0x000
    0xa1 jcz.l • • • • • pkt • • • • • 0x000 • • • 0xb0
    0xa2 nxt • • • • • pkt • • • • • ld #addr • • • 0xf0
    0xa3 rpt • • • • • alu • • • • • 0x000 • • • GUI_SCRATCH_REG0
    0x0 12 times
    0xb0 nxt • • • • • pkt • • • • • ld #addr • • • 0xf1
    0xb1 rpt • • • • • alu • • • • • 0x000 • • • GUI_SCRATCH_REG1
    0x0 62 times
    0xf0 0x00000000 0xdeadbeef
    0xf1 rpt • • • • • alu.h • • • • • 0x03d • • • 0xeef
    0x0 14 times
    Packet
    Header 0x00000001
    Data0 0xffff0001
    State After Execution
    GUI_SCRATCH_REG0: 0xdeadbeef
    GUI_SCRATCH_REG1: 0x00000000
0x07–0x08 ?
0x09 jn.h

If the high word of the value stored in the ALU register is negative set the program counter to the value in the addr field.

Evidence
  • seq-jn.h-jumps-when-aluh-negative
    seq b32 cond mcsel idxsel b28 src b25 b24 b23 b22 b21 alu advance write latchpkt addr
    entry:
    0x00 jmp • • • • • pkt • • • • • 0x000 • • • 0xa0
    0x0 159 times
    0xa0 nxt • • • • • pkt • • • • • ld #addr • • • 0xf0
    0xa1 jn.h • • • • • pkt • • • • • 0x000 • • • 0xf1
    0xa2 rpt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG0
    0x0 77 times
    0xf0 0x00000000 0xffff1111
    0xf1 rpt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG1
    0x0 14 times
    Packet
    Header 0x00000000
    Data0 0xdeadbeef
    State After Execution
    GUI_SCRATCH_REG0: 0x00000000
    GUI_SCRATCH_REG1: 0xdeadbeef
  • seq-jn.h-continues-when-aluh-positive
    seq b32 cond mcsel idxsel b28 src b25 b24 b23 b22 b21 alu advance write latchpkt addr
    entry:
    0x00 jmp • • • • • pkt • • • • • 0x000 • • • 0xa0
    0x0 159 times
    0xa0 nxt • • • • • pkt • • • • • ld #addr • • • 0xf0
    0xa1 jn.h • • • • • pkt • • • • • 0x000 • • • 0xf1
    0xa2 rpt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG0
    0x0 77 times
    0xf0 0x00000000 0x1111ffff
    0xf1 rpt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG1
    0x0 14 times
    Packet
    Header 0x00000000
    Data0 0xdeadbeef
    State After Execution
    GUI_SCRATCH_REG0: 0xdeadbeef
    GUI_SCRATCH_REG1: 0x00000000
0x0a jn.l

If the low word of the value stored in the ALU is negative set the program counter to the value in the addr field.

Evidence
  • seq-jn.l-jumps-when-alul-negative
    seq b32 cond mcsel idxsel b28 src b25 b24 b23 b22 b21 alu advance write latchpkt addr
    entry:
    0x00 jmp • • • • • pkt • • • • • 0x000 • • • 0xa0
    0x0 159 times
    0xa0 nxt • • • • • pkt • • • • • ld #addr • • • 0xf0
    0xa1 jn.l • • • • • pkt • • • • • 0x000 • • • 0xf1
    0xa2 rpt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG0
    0x0 77 times
    0xf0 0x00000000 0x1111ffff
    0xf1 rpt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG1
    0x0 14 times
    Packet
    Header 0x00000000
    Data0 0xdeadbeef
    State After Execution
    GUI_SCRATCH_REG0: 0x00000000
    GUI_SCRATCH_REG1: 0xdeadbeef
  • seq-jn.l-continues-when-alul-positive
    seq b32 cond mcsel idxsel b28 src b25 b24 b23 b22 b21 alu advance write latchpkt addr
    entry:
    0x00 jmp • • • • • pkt • • • • • 0x000 • • • 0xa0
    0x0 159 times
    0xa0 nxt • • • • • pkt • • • • • ld #addr • • • 0xf0
    0xa1 jn.l • • • • • pkt • • • • • 0x000 • • • 0xf1
    0xa2 rpt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG0
    0x0 77 times
    0xf0 0x00000000 0xffff1111
    0xf1 rpt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG1
    0x0 14 times
    Packet
    Header 0x00000000
    Data0 0xdeadbeef
    State After Execution
    GUI_SCRATCH_REG0: 0xdeadbeef
    GUI_SCRATCH_REG1: 0x00000000
0x0b skp

Unconditionally increment the program counter by two. In other words, skip the next instruction.

Evidence
  • seq-skp-skips-next-instruction
    seq b32 cond mcsel idxsel b28 src b25 b24 b23 b22 b21 alu advance write latchpkt addr
    entry:
    0x00 jmp • • • • • pkt • • • • • 0x000 • • • 0xa0
    0x0 159 times
    0xa0 skp • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG0
    0xa1 nxt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG1
    0xa2 rpt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG2
    0x0 93 times
    Packet
    Header 0x00000000
    Data0 0xdeadbeef
    State After Execution
    GUI_SCRATCH_REG0: 0xdeadbeef
    GUI_SCRATCH_REG1: 0x00000000
    GUI_SCRATCH_REG2: 0xdeadbeef
0x0c call

Used in conjunction with ret. Jump to addr and push PC+1 onto a stack. On ret pop the stack. The stack has a max depth of 2. Further calls evict the oldest.

Evidence
  • seq-call-and-return
    seq b32 cond mcsel idxsel b28 src b25 b24 b23 b22 b21 alu advance write latchpkt addr
    0x0 4 times
    0x04 0x00000000 0x00a00000
    0x0 155 times
    entry:
    0xa0 nxt • • • • • pkt • • • • • ld #addr • • • 0xf0
    0xa1 call • • • • • pkt • • • • • 0x000 • • • 0xb0
    0xa2 nxt • • • • • alu • • • • • 0x000 • • • GUI_SCRATCH_REG0
    0xa3 nxt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG2
    0xa4 nxt • • • • • pkt • • • • • ld #addr • • • 0xf1
    0xa5 call • • • • • pkt • • • • • 0x000 • • • 0xc0
    0xa6 nxt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG3
    0xa7 rpt • • • • • alu • • • • • 0x000 • • • GUI_SCRATCH_REG1
    0x0 8 times
    0xb0 ret • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG0
    0x0 15 times
    0xc0 ret • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG1
    0x0 47 times
    0xf0 0x00000000 0xaaaaaaaa
    0xf1 0x00000000 0xbbbbbbbb
    0x0 14 times
    Packet
    Header 0xc0011200
    Data0 0xdeadbeef
    Data1 0xcafebeef
    State After Execution
    GUI_SCRATCH_REG0: 0xaaaaaaaa
    GUI_SCRATCH_REG1: 0xbbbbbbbb
    GUI_SCRATCH_REG2: 0xdeadbeef
    GUI_SCRATCH_REG3: 0xcafebeef
  • seq-call-and-return-has-two-entry-stack
    seq b32 cond mcsel idxsel b28 src b25 b24 b23 b22 b21 alu advance write latchpkt addr
    0x00 rpt • • • • • pkt • • • • • 0x000 • • • BIOS_1_SCRATCH
    0x0 3 times
    0x04 0x00000000 0x00a00000
    0x0 155 times
    entry:
    0xa0 nxt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG5
    0xa1 call • • • • • pkt • • • • • 0x000 • • • 0xb0
    0xa2 nxt • • • • • pkt • • • • • ld #addr • • • 0xf0
    0xa3 rpt • • • • • alu • • • • • 0x000 • • • GUI_SCRATCH_REG0
    0x0 12 times
    0xb0 call • • • • • pkt • • • • • 0x000 • • • 0xc0
    0xb1 nxt • • • • • pkt • • • • • ld #addr • • • 0xf1
    0xb2 ret • • • • • alu • • • • • 0x000 • • • GUI_SCRATCH_REG1
    0x0 13 times
    0xc0 call • • • • • pkt • • • • • 0x000 • • • 0xd0
    0xc1 nxt • • • • • pkt • • • • • ld #addr • • • 0xf2
    0xc2 ret • • • • • alu • • • • • 0x000 • • • GUI_SCRATCH_REG2
    0xc3 rpt • • • • • alu • • • • • 0x000 • • • GUI_SCRATCH_REG5
    0x0 12 times
    0xd0 call • • • • • pkt • • • • • 0x000 • • • 0xe0
    0xd1 nxt • • • • • pkt • • • • • ld #addr • • • 0xf3
    0xd2 ret • • • • • alu • • • • • 0x000 • • • GUI_SCRATCH_REG3
    0x0 13 times
    0xe0 nxt • • • • • pkt • • • • • ld #addr • • • 0xf4
    0xe1 nxt • • • • • pkt • • • • • 0x000 • • • 0x000
    0xe2 nxt • • • • • pkt • • • • • 0x000 • • • BIOS_0_SCRATCH
    0xe3 ret • • • • • alu • • • • • 0x000 • • • GUI_SCRATCH_REG4
    0x0 12 times
    0xf0 0x00000000 0xaaaaaaaa
    0xf1 0x00000000 0xbbbbbbbb
    0xf2 0x00000000 0xcccccccc
    0xf3 0x00000000 0xdddddddd
    0xf4 0x00000000 0xeeeeeeee
    0x0 11 times
    Packet
    Header 0xc0031200
    Data0 0xdeadbeef
    Data1 0xcafebeef
    Data2 0x1337beef
    Data3 0x1337fee7
    State After Execution
    GUI_SCRATCH_REG0: 0x00000000
    GUI_SCRATCH_REG1: 0x00000000
    GUI_SCRATCH_REG2: 0xcccccccc
    GUI_SCRATCH_REG3: 0xdddddddd
    GUI_SCRATCH_REG4: 0xeeeeeeee
    GUI_SCRATCH_REG5: 0xdeadbeef
    BIOS_0_SCRATCH: 0xcafebeef
    BIOS_1_SCRATCH: 0x00000000
    Notes

    I suspected for a while that ret on an empty stack would return to the type-3 jump table. This no longer appears to be the case. There is an advance at 0xe1, which means that we are making progress through the pktfifo when this call chain executes.

    If the whole chain ran more than once we would expect to see different values in REG5 and BIOS0. The test shows that REG5 gets the first data packet word, BIOS0 gets the second. If the ret stack underflow on 0xc2 dispatched to the jump table the entire chain will run again and we would see later packet words. I believe it would settle on REG5=0x1337beef and BIOS0=0x1337fee7. It does not.

    So on underflow it looks like execution stops, either wedging or waiting for the next packet.

0x0d jz

Jump to the instruction in addr if the ALU register is zero.

Evidence
  • seq-jz-jumps-on-alu-zero
    seq b32 cond mcsel idxsel b28 src b25 b24 b23 b22 b21 alu advance write latchpkt addr
    entry:
    0x00 jmp • • • • • pkt • • • • • 0x000 • • • 0xa0
    0x0 159 times
    0xa0 nxt • • • • • pkt • • • • • ld #addr • • • 0xf0
    0xa1 jz • • • • • pkt • • • • • 0x000 • • • 0xb0
    0xa2 rpt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG0
    0x0 13 times
    0xb0 rpt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG1
    0x0 79 times
    Packet
    Header 0x00000001
    Data0 0xdeadbeef
    State After Execution
    GUI_SCRATCH_REG0: 0x00000000
    GUI_SCRATCH_REG1: 0xdeadbeef
  • seq-jz-does-not-jump-on-alu.h-positive
    seq b32 cond mcsel idxsel b28 src b25 b24 b23 b22 b21 alu advance write latchpkt addr
    entry:
    0x00 jmp • • • • • pkt • • • • • 0x000 • • • 0xa0
    0x0 159 times
    0xa0 nxt • • • • • pkt • • • • • ld #addr • • • 0xf0
    0xa1 jz • • • • • pkt • • • • • 0x000 • • • 0xb0
    0xa2 rpt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG0
    0x0 13 times
    0xb0 rpt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG1
    0x0 63 times
    0xf0 0x00000000 0x00010000
    0x0 15 times
    Packet
    Header 0x00000001
    Data0 0xdeadbeef
    State After Execution
    GUI_SCRATCH_REG0: 0xdeadbeef
    GUI_SCRATCH_REG1: 0x00000000
  • seq-jz-does-not-jump-on-alu.h-negative
    seq b32 cond mcsel idxsel b28 src b25 b24 b23 b22 b21 alu advance write latchpkt addr
    entry:
    0x00 jmp • • • • • pkt • • • • • 0x000 • • • 0xa0
    0x0 159 times
    0xa0 nxt • • • • • pkt • • • • • ld #addr • • • 0xf0
    0xa1 jz • • • • • pkt • • • • • 0x000 • • • 0xb0
    0xa2 rpt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG0
    0x0 13 times
    0xb0 rpt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG1
    0x0 63 times
    0xf0 0x00000000 0xffff0000
    0x0 15 times
    Packet
    Header 0x00000001
    Data0 0xdeadbeef
    State After Execution
    GUI_SCRATCH_REG0: 0xdeadbeef
    GUI_SCRATCH_REG1: 0x00000000
  • seq-jz-does-not-jump-on-alu.l-positive
    seq b32 cond mcsel idxsel b28 src b25 b24 b23 b22 b21 alu advance write latchpkt addr
    entry:
    0x00 jmp • • • • • pkt • • • • • 0x000 • • • 0xa0
    0x0 159 times
    0xa0 nxt • • • • • pkt • • • • • ld #addr • • • 0xf0
    0xa1 jz • • • • • pkt • • • • • 0x000 • • • 0xb0
    0xa2 rpt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG0
    0x0 13 times
    0xb0 rpt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG1
    0x0 63 times
    0xf0 0x00000000 0x00000001
    0x0 15 times
    Packet
    Header 0x00000001
    Data0 0xdeadbeef
    State After Execution
    GUI_SCRATCH_REG0: 0xdeadbeef
    GUI_SCRATCH_REG1: 0x00000000
  • seq-jz-does-not-jump-on-alu.l-negative
    seq b32 cond mcsel idxsel b28 src b25 b24 b23 b22 b21 alu advance write latchpkt addr
    entry:
    0x00 jmp • • • • • pkt • • • • • 0x000 • • • 0xa0
    0x0 159 times
    0xa0 nxt • • • • • pkt • • • • • ld #addr • • • 0xf0
    0xa1 jz • • • • • pkt • • • • • 0x000 • • • 0xb0
    0xa2 rpt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG0
    0x0 13 times
    0xb0 rpt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG1
    0x0 63 times
    0xf0 0x00000000 0x0000ffff
    0x0 15 times
    Packet
    Header 0x00000001
    Data0 0xdeadbeef
    State After Execution
    GUI_SCRATCH_REG0: 0xdeadbeef
    GUI_SCRATCH_REG1: 0x00000000
0x0e jz.l

Jump to the instruction in addr if the low word of the ALU register is zero.

Evidence
  • seq-jz.l-jumps-on-alu.l-zero
    seq b32 cond mcsel idxsel b28 src b25 b24 b23 b22 b21 alu advance write latchpkt addr
    entry:
    0x00 jmp • • • • • pkt • • • • • 0x000 • • • 0xa0
    0x0 159 times
    0xa0 nxt • • • • • pkt • • • • • ld #addr • • • 0xf0
    0xa1 jz.l • • • • • pkt • • • • • 0x000 • • • 0xb0
    0xa2 rpt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG0
    0x0 13 times
    0xb0 rpt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG1
    0x0 63 times
    0xf0 0x00000000 0xffff0000
    0x0 15 times
    Packet
    Header 0x00000001
    Data0 0xdeadbeef
    State After Execution
    GUI_SCRATCH_REG0: 0x00000000
    GUI_SCRATCH_REG1: 0xdeadbeef
  • seq-jz.l-does-not-jump-on-alu.l-positive
    seq b32 cond mcsel idxsel b28 src b25 b24 b23 b22 b21 alu advance write latchpkt addr
    entry:
    0x00 jmp • • • • • pkt • • • • • 0x000 • • • 0xa0
    0x0 159 times
    0xa0 nxt • • • • • pkt • • • • • ld #addr • • • 0xf0
    0xa1 jz.l • • • • • pkt • • • • • 0x000 • • • 0xb0
    0xa2 rpt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG0
    0x0 13 times
    0xb0 rpt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG1
    0x0 63 times
    0xf0 0x00000000 0xffff0001
    0x0 15 times
    Packet
    Header 0x00000001
    Data0 0xdeadbeef
    State After Execution
    GUI_SCRATCH_REG0: 0xdeadbeef
    GUI_SCRATCH_REG1: 0x00000000
  • seq-jz.l-does-not-jump-on-alu.l-negative
    seq b32 cond mcsel idxsel b28 src b25 b24 b23 b22 b21 alu advance write latchpkt addr
    entry:
    0x00 jmp • • • • • pkt • • • • • 0x000 • • • 0xa0
    0x0 159 times
    0xa0 nxt • • • • • pkt • • • • • ld #addr • • • 0xf0
    0xa1 jz.l • • • • • pkt • • • • • 0x000 • • • 0xb0
    0xa2 rpt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG0
    0x0 13 times
    0xb0 rpt • • • • • pkt • • • • • 0x000 • • • GUI_SCRATCH_REG1
    0x0 63 times
    0xf0 0x00000000 0xffffffff
    0x0 15 times
    Packet
    Header 0x00000001
    Data0 0xdeadbeef
    State After Execution
    GUI_SCRATCH_REG0: 0xdeadbeef
    GUI_SCRATCH_REG1: 0x00000000
0x0f–0x10 ?

b32 [32]

Unknown

cond [31]

Conditionally execute the current instruction based on the low bit of the idx register. If set execute, if unset skip the instruction. It’s currently unclear how the idx register works or if it exists at all. Confidence is high that this does control execution of the write (but not instruction seq). But… the rest is very fuzzy.

mcsel [30]

When enabled the addr field refers to a microcode instruction. This becomes the destination for write. Yes, the CP engine can modify it’s own microcode! The stock microcode contains a subroutine that does this.

Evidence

idxsel [29]

Select the idx register replacing the addr field for some operations. A good example of this is instruction 0, the Type-0 packet handler. The write flag combined with idxsel writes to the card register in the BASE_INDEX field of the current Type-0 packet header. This is often combined with b24 to walk a number of card registers.

Evidence

b28 [28]

Unknown

src [27:26]

Values

0x00 pkt

Use the pkt register as a source.

0x01 alu

Use the full dword stored in the alu register as a source.

0x02 alu.h

Use the high word stored in the alu register as a source.

0x03 alu.l

Use the low word stored in the alu register as a source.

b25 [25]

Unknown

b24 [24]

In at least some cases this increments the value stored in the idx register for use in walking a number of card registers. This is usually used together with idxsel. A good example of this is instruction 0, the Type-0 packet handler.

Evidence

b23 [23]

Unknown

b22 [22]

Seen used in conjunction with the cond bit. It in some way controls which bit is used to determine if a write happens. My initial thought was that it shifted the stored GUI_CONTROL 2D packet header stored in a flag register of some sort. But there’s also the possibility that it increments a counter that is then used to index into header bits.

Regardless, it is almost certainly used to iterate through the bits of a word and conditionally execute a write. (see dp_gui_2d, instruction 0xb8)

b21 [21]

While b24 is also set this will load the alu register contents into the idx register. When b24 is not set it so far appears to be a nop.

alu [20:15]

This appears to be an opcode for the ALU. The operations listed, for the most part, were discovered in a very quick sweep. Confidence is low unless otherwise stated. Many are likely missing/wrong.

Values

0x01 ?
0x02 sub.h

Subtract the high word of the alu register from the high word of the pkt register. Used by the paint packet handler (beginning at instruction 0x4d) to calculate the height of the blit.

0x03 sub.l

Subtract the low word of the alu register from the low word of the pkt register. Used by the paint packet handler (beginning at instruction 0x4d) to calculate the width of the blit.

0x04 neg.h
0x05 neg.l
0x07 ld #addr
0x08–0x0b ?
0x0c ld pkt
0x0d asr
0x0e shr11

Shift value in the ALU right by 11 bits. Used by the Type-1 packet handler (instruction 0xb4) to pull out REG_INDEX1 and REG_INDEX2.

0x0f ?
0x10 ld [pc+1]

Load the low dword of the next instruction into the ALU register.

0x11 mul3.h
0x12 mul3.l
0x13–0x14 ?
0x15 clr.l
0x16 clr.h
0x17 swp
0x18 ?
0x19 clr
0x1a lb
0x1b inc.l
0x1c inc.h
0x1e ?
0x1f add.h
0x20 add.l
0x21–0x22 ?
0x23 bswp.l
0x25 dec.l
0x26 neg.b

advance [14]

This is a control line for the pkt FIFO. The pkt FIFO head register latches the next word in the pkt, if available. If nothing is available the head register is untouched. This also controls dispatch in that the current pkt word count is decremented. If the word count for the packet has been exhausted then the header of the next packet is read and dispatched on. Which means this will cause the CP engine to either wait for the next packet or jump to the correct entrypoint for the next packet.

Evidence

write [13]

Enable a write to the register address in the addr field. The destination can also be redirected via idxsel, and possibly others. The src seems to be selected by the src field.

latchpkt [12]

Read the current word at the head of the pktfifo. Store it in the pkt register.

Evidence

addr [11:0]

The addr field may hold an instruction address (0x00-0xff) or a card register address in words (SCRATCH_REG0 >> 2 = 0x578). This field is used by a number of chip functions. The sequencer (controlled by seq) can use it to set the program counter. The ALU can use it as an indirect reference to a full word stored in the microcode. write can use it as a destination card register.

Full Listing

seq b32 cond mcsel idxsel b28 src b25 b24 b23 b22 b21 alu advance write latchpkt addr
type_0_pkt_entry:
0x00 rpt • • • • • pkt • • • • • 0x000 • • • 0x000
type_0_one_reg_pkt_entry:
0x01 rpt • • • • • pkt • • • • • 0x000 • • • 0x000
entry_type_1:
0x02 jmp • • • • • pkt • • • • • 0x000 • • • 0xb4
entry_type_3_gui_cntl_2d:
0x03 jmp • • • • • pkt • • • • • 0x000 • • • 0xb8
entry_type_3_jump_table:
0x04 0x00000000 0x6f5b4d4c
0x05 0x00000000 0x4c4c427f
0x06 0x00000000 0x5b568a92
0x07 0x00000000 0x4ca09c6d
0x08 0x00000000 0xad4c4c4c
0x09 0x00000000 0x4ce1af3d
0x0a 0x00000000 0xd8afafaf
0x0b 0x00000000 0xd64c4cdc
0x0c 0x00000000 0x4cd10d10
pkt_0x31:
0x0d skp • • • • • pkt • • • • • alu_0x1e • • • 0x000
0x0e 0x00000000 0x362f242d
0x0f jmp • • • • • pkt • • • • • 0x000 • • • 0x12
pkt_0x30:
0x10 skp • • • • • pkt • • • • • alu_0x1e • • • 0x000
0x11 0x00000000 0x362f282d
0x12 nxt • • • • • pkt • • • • • ld #addr • • • 0xe7
0x13 nxt • • • • • alu • • • • • 0x000 • • • RB3D_DSTCACHE_CTLSTAT
0x14 skp • • • • • pkt • • • • • alu_0x1e • • • MM_DATA
0x15 0x00000000 0x333a3730
0x16 nxt • • • • • pkt • • • • • 0x000 • • • IDCT_CONTROL
0x17 nxt • • • • • pkt • • • • • ld pkt • • • 0x000
0x18 jz • • • • • pkt • • • • • 0x000 • • • 0x21
0x19 seq_0x0f • • • • • pkt • • • • • 0x000 • • • 0x000
0x1a nxt • • • • • pkt • • • • • ld pkt • • • 0x000
0x1b jz • • • • • pkt • • • • • 0x000 • • • 0x21
0x1c seq_0x0f • • • • • pkt • • • • • 0x000 • • • 0x000
0x1d nxt • • • • • pkt • • • • • ld pkt • • • 0x000
0x1e jz • • • • • pkt • • • • • 0x000 • • • 0x21
0x1f seq_0x0f • • • • • pkt • • • • • 0x000 • • • 0x000
0x20 jmp • • • • • pkt • • • • • 0x000 • • • 0x17
0x21 nxt • • • • • pkt • • • • • ld #addr • • • 0x2b
0x22 nxt • • • • • alu • • • • • 0x000 • • • IDCT_RUNS
0x23 jmp • • • • • pkt • • • • • 0x000 • • • 0x17
0x24 nxt • • • • • pkt • • • • • 0x000 • • • IDCT_RUNS
0x25 nxt • • • • • pkt • • • • • ld pkt • • • 0x000
0x26 nxt • • • • • pkt • • • • • 0x000 • • • IDCT_LEVELS
0x27 ret • • • • • alu • • • • • 0x000 • • • IDCT_LEVELS
0x28 nxt • • • • • pkt • • • • • 0x000 • • • IDCT_RUNS
0x29 nxt • • • • • pkt • • • • • 0x000 • • • IDCT_LEVELS
0x2a ret • • • • • pkt • • • • • 0x000 • • • IDCT_LEVELS
0x2b 0x00000000 0xffffffff
0x2c 0x00000000 0x10000000
0x2d nxt • • • • • pkt • • • • • ld #addr • • • 0x2b
0x2e ret • • • • • alu • • • • • 0x000 • • • IDCT_RUNS
0x2f nxt • • • • • pkt • • • • • 0x000 • • • MC_SRC2_CNTL
0x30 nxt • • • • • pkt • • • • • 0x000 • • • MC_SRC1_CNTL
0x31 nxt • • • • • pkt • • • • • 0x000 • • • MC_DST_CNTL
0x32 ret • • • • • pkt • • • • • 0x000 • • • MC_START_CNTL
0x33 nxt • • • • • pkt • • • • • ld #addr • • • 0x2c
0x34 nxt • • • • • alu • • • • • 0x000 • • • MC_SRC1_CNTL
0x35 nxt • • • • • pkt • • • • • 0x000 • • • MC_DST_CNTL
0x36 ret • • • • • pkt • • • • • 0x000 • • • MC_START_CNTL
0x37 call • • • • • pkt • • • • • 0x000 • • • 0x2f
0x38 call • • • • • pkt • • • • • 0x000 • • • 0x2f
0x39 ret • • • • • pkt • • • • • 0x000 • • • 0x000
0x3a call • • • • • pkt • • • • • 0x000 • • • 0x30
0x3b call • • • • • pkt • • • • • 0x000 • • • 0x30
0x3c ret • • • • • pkt • • • • • 0x000 • • • 0x000
load_microcode:
0x3d nxt • • • • • pkt • • • • • 0x000 • • • 0x000
0x3e nxt • • • • • pkt • • • • • ld pkt • • • 0x000
0x3f nxt • • • • • pkt • • • • • alu_0x13 • • • 0x000
0x40 nxt • • • • • pkt • • • • • ld pkt • • • 0x000
0x41 jmp • • • • • alu • • • • • 0x000 • • • 0x3e
polyline:
0x42 nxt • • • • • pkt • • • • • ld #addr • • • 0xe6
0x43 nxt • • • • • alu • • • • • 0x000 • • • DSTCACHE_CTLSTAT
0x44 skp • • • • • pkt • • • • • ld [pc+1] • • • 0x000
0x45 0x00000000 0x00000000
0x46 nxt • • • • • alu • • • • • 0x000 • • • BRUSH_Y_X
0x47 nxt • • • • • pkt • • • • • 0x000 • • • DST_LINE_START
0x48 nxt • • • • • pkt • • • • • ld pkt • • • DST_LINE_END
0x49 nxt • • • • • alu • • • • • 0x000 • • • DST_LINE_START
0x4a nxt • • • • • pkt • • • • • ld pkt • • • DST_LINE_END
0x4b jmp • • • • • pkt • • • • • 0x000 • • • 0x49
nop:
0x4c rpt • • • • • pkt • • • • • 0x000 • • • 0x000
paint:
0x4d nxt • • • • • pkt • • • • • ld #addr • • • 0xe6
0x4e nxt • • • • • alu • • • • • 0x000 • • • DSTCACHE_CTLSTAT
0x4f nxt • • • • • pkt • • • • • ld pkt • • • 0x000
0x50 nxt • • • • • pkt • • • • • 0x000 • • • DST_Y_X
0x51 nxt • • • • • pkt • • • • • sub.l • • • 0x000
0x52 jn.l • • • • • pkt • • • • • sub.h • • • 0x55
0x53 jn.h • • • • • pkt • • • • • 0x000 • • • 0x55
0x54 nxt • • • • • alu • • • • • 0x000 • • • DST_HEIGHT_WIDTH
0x55 jmp • • • • • pkt • • • • • 0x000 • • • 0x4f
paint_multi:
0x56 nxt • • • • • pkt • • • • • ld #addr • • • 0xe6
0x57 nxt • • • • • alu • • • • • 0x000 • • • DSTCACHE_CTLSTAT
0x58 nxt • • • • • pkt • • • • • 0x000 • • • DST_X_Y
0x59 nxt • • • • • pkt • • • • • 0x000 • • • DST_WIDTH_HEIGHT
0x5a jmp • • • • • pkt • • • • • 0x000 • • • 0x58
bitblt:
bitblt_multi:
0x5b nxt • • • • • pkt • • • • • ld #addr • • • 0xe6
0x5c nxt • • • • • alu • • • • • 0x000 • • • DSTCACHE_CTLSTAT
0x5d nxt • • • • • pkt • • • • • ld pkt • • • DP_CNTL_XDIR_YDIR_YMAJOR
0x5e nxt • • • • • alu • • • • • sub.l • • • 0xe4
0x5f nxt • • • • • pkt • • • • • sub.h • • • 0x0e4
0x60 call • • • • • alu • • • • • ld pkt • • • 0x66
0x61 nxt • • • • • alu • • • • • alu_0x18 • • • DST_X_Y
0x62 call • • • • • pkt • • • • • 0x000 • • • 0x66
0x63 nxt • • • • • alu • • • • • 0x000 • • • SRC_X_Y
0x64 nxt • • • • • pkt • • • • • 0x000 • • • DST_WIDTH_HEIGHT
0x65 jmp • • • • • pkt • • • • • 0x000 • • • 0x5d
0x66 seq_0x04 • • • • • pkt • • • • • 0x000 • • • 0x069
0x67 nxt • • • • • pkt • • • • • add.l • • • 0x000
0x68 nxt • • • • • pkt • • • • • inc.l • • • 0x0ff
0x69 seq_0x04 • • • • • pkt • • • • • 0x000 • • • 0x06c
0x6a nxt • • • • • pkt • • • • • add.h • • • 0x000
0x6b nxt • • • • • pkt • • • • • inc.h • • • 0x0ff
0x6c ret • • • • • pkt • • • • • 0x000 • • • 0x000
trans_bitblt:
0x6d call • • • • • pkt • • • • • 0x000 • • • 0x8f
0x6e jmp • • • • • pkt • • • • • 0x000 • • • 0x5b
small_text:
0x6f nxt • • • • • pkt • • • • • ld #addr • • • 0xe6
0x70 nxt • • • • • alu • • • • • 0x000 • • • DSTCACHE_CTLSTAT
0x71 nxt • • • • • pkt • • • • • 0x000 • • • DP_SRC_FRGD_CLR
0x72 nxt • • • • • pkt • • • • • ld pkt • • • 0x000
0x73 nxt • • • • • pkt • • • • • alu_0x01 • • • 0x000
0x74 nxt • • • • • pkt • • • • • alu_0x08 • • • 0x000
0x75 nxt • • • • • alu.l • • • • • 0x000 • • • DST_Y_X
0x76 nxt • • • • • pkt • • • • • alu_0x09 • • • 0x000
0x77 nxt • • • • • pkt • • • • • alu_0x0a • • • 0x000
0x78 nxt • • • • • pkt • • • • • alu_0x0f • • • HOST_DATA_LAST
0x79 nxt • • • • • pkt • • • • • 0x000 • • • 0x000
0x7a jcz.l • • • • • pkt • • • • • 0x000 • • • 0x7e
0x7b nxt • • • • • pkt • • • • • 0x000 • • • DST_HEIGHT_WIDTH_8
0x7c seq_0x10 • • • • • pkt • • • • • 0x000 • • • HOST_DATA0
0x7d jmp • • • • • pkt • • • • • 0x000 • • • 0x73
0x7e jmp • • • • • pkt • • • • • 0x000 • • • 0x73
hostdata_blt:
0x7f nxt • • • • • pkt • • • • • ld #addr • • • 0xe6
0x80 nxt • • • • • alu • • • • • 0x000 • • • DSTCACHE_CTLSTAT
0x81 nxt • • • • • pkt • • • • • 0x000 • • • DP_SRC_FRGD_CLR
0x82 nxt • • • • • pkt • • • • • 0x000 • • • DP_SRC_BKGD_CLR
0x83 nxt • • • • • pkt • • • • • 0x000 • • • DST_Y_X
0x84 nxt • • • • • pkt • • • • • 0x000 • • • DST_HEIGHT_WIDTH
0x85 nxt • • • • • pkt • • • • • 0x000 • • • 0x000
0x86 jcz.l • • • • • pkt • • • • • 0x000 • • • 0x83
0x87 seq_0x10 • • • • • pkt • • • • • 0x000 • • • HOST_DATA0
0x88 nxt • • • • • pkt • • • • • 0x000 • • • HOST_DATA_LAST
0x89 jmp • • • • • pkt • • • • • 0x000 • • • 0x83
nextchar:
0x8a nxt • • • • • pkt • • • • • alu_0x14 • • • DST_Y_X
0x8b nxt • • • • • pkt • • • • • ld #addr • • • 0xe6
0x8c nxt • • • • • alu • • • • • 0x000 • • • DSTCACHE_CTLSTAT
0x8d nxt • • • • • pkt • • • • • 0x000 • • • DST_HEIGHT_WIDTH
0x8e jmp • • • • • pkt • • • • • 0x000 • • • 0x86
0x8f nxt • • • • • pkt • • • • • 0x000 • • • CLR_CMP_CNTL
0x90 nxt • • • • • pkt • • • • • 0x000 • • • CLR_CMP_CLR_SRC
0x91 ret • • • • • pkt • • • • • 0x000 • • • CLR_CMP_CLR_DST
polyscanlines:
0x92 nxt • • • • • pkt • • • • • ld #addr • • • 0xe6
0x93 nxt • • • • • alu • • • • • 0x000 • • • DSTCACHE_CTLSTAT
0x94 nxt • • • • • pkt • • • • • 0x000 • • • 0x000
0x95 nxt • • • • • pkt • • • • • 0x000 • • • 0x000
0x96 nxt • • • • • pkt • • • • • 0x000 • • • DST_HEIGHT_Y
0x97 nxt • • • • • pkt • • • • • ld pkt • • • 0x000
0x98 jcz.l • • • • • pkt • • • • • 0x000 • • • 0x95
0x99 nxt • • • • • pkt • • • • • alu_0x0b • • • 0x000
0x9a nxt • • • • • alu.l • • • • • 0x000 • • • DST_WIDTH_X
0x9b jmp • • • • • pkt • • • • • 0x000 • • • 0x97
ply_nextscan:
0x9c nxt • • • • • pkt • • • • • ld #addr • • • 0xe6
0x9d nxt • • • • • alu • • • • • 0x000 • • • DSTCACHE_CTLSTAT
0x9e jmp • • • • • pkt • • • • • 0x000 • • • 0x96
0x9f rpt • • • • • pkt • • • • • 0x000 • • • 0xe5
set_scissors:
0xa0 nxt • • • • • pkt • • • • • ld #addr • • • 0xe6
0xa1 nxt • • • • • alu • • • • • 0x000 • • • DSTCACHE_CTLSTAT
0xa2 nxt • • • • • pkt • • • • • ld #addr • • • 0xe5
0xa3 jz.l • • • • • pkt • • • • • 0x000 • • • 0xa8
0xa4 call • • • • • pkt • • • • • ld pkt • • • 0xaa
0xa5 nxt • • • • • alu • • • • • 0x000 • • • SC_TOP_LEFT
0xa6 call • • • • • pkt • • • • • ld pkt • • • 0xab
0xa7 rpt • • • • • alu • • • • • 0x000 • • • SC_BOTTOM_RIGHT
0xa8 nxt • • • • • pkt • • • • • 0x000 • • • SC_TOP_LEFT
0xa9 rpt • • • • • pkt • • • • • 0x000 • • • SC_BOTTOM_RIGHT
0xaa ret • • • • • pkt • • • • • mul3.l • • • 0x000
0xab nxt • • • • • pkt • • • • • mul3.l • • • 0x000
0xac ret • • • • • pkt • • • • • inc.l • • • CLOCK_CNTL_INDEX
3d_rndr_gen_indx_prim:
0xad nxt • • • • • pkt • • • • • 0x000 • • • SE_VTX_AOD_ADDR0
0xae nxt • • • • • pkt • • • • • 0x000 • • • 0x000
3d_rndr_gen_prim:
3d_draw_vbuf:
3d_draw_immd:
3d_draw_indx:
0xaf nxt • • • • • pkt • • • • • ld #addr • • • 0xe7
0xb0 nxt • • • • • alu • • • • • 0x000 • • • RB3D_DSTCACHE_CTLSTAT
0xb1 nxt • • • • • pkt • • • • • 0x000 • • • SE_VTX_FMT
0xb2 nxt • • • • • pkt • • • • • 0x000 • • • SE_VF_CNTL
0xb3 rpt • • • • • pkt • • • • • 0x000 • • • SE_PORT_DATA0
type_1_handler:
0xb4 nxt • • • • • pkt • • • • • 0x000 • • • 0x000
0xb5 nxt • • • • • pkt • • • • • shr11 • • • 0x000
0xb6 nxt • • • • • pkt • • • • • 0x000 • • • 0x000
0xb7 nxt • • • • • pkt • • • • • 0x000 • • • 0x000
dp_gui_2d:
0xb8 nxt • • • • • pkt • • • • • ld pkt • • • 0x000
0xb9 nxt • • • • • pkt • • • • • 0x000 • • • DP_GUI_MASTER_CNTL
0xba nxt • • • • • pkt • • • • • 0x000 • • • SRC_PITCH_OFFSET
0xbb nxt • • • • • pkt • • • • • 0x000 • • • DST_PITCH_OFFSET
0xbc nxt • • • • • pkt • • • • • alu_0x22 • • • 0x000
0xbd nxt • • • • • pkt • • • • • ld #addr • • • 0xe5
0xbe jz.l • • • • • pkt • • • • • 0x000 • • • 0xc6
0xbf call • • • • • pkt • • • • • ld pkt • • • 0xab
0xc0 nxt • • • • • alu • • • • • 0x000 • • • SRC_SC_BOTTOM_RIGHT
0xc1 call • • • • • pkt • • • • • ld pkt • • • 0xaa
0xc2 nxt • • • • • alu • • • • • 0x000 • • • SC_TOP_LEFT
0xc3 call • • • • • pkt • • • • • ld pkt • • • 0xab
0xc4 nxt • • • • • alu • • • • • 0x000 • • • SC_BOTTOM_RIGHT
0xc5 jmp • • • • • pkt • • • • • 0x000 • • • 0xc9
0xc6 nxt • • • • • pkt • • • • • 0x000 • • • SRC_SC_BOTTOM_RIGHT
0xc7 nxt • • • • • pkt • • • • • 0x000 • • • SC_TOP_LEFT
0xc8 nxt • • • • • pkt • • • • • 0x000 • • • SC_BOTTOM_RIGHT
0xc9 nxt • • • • • pkt • • • • • alu_0x21 • • • 0x000
0xca nxt • • • • • pkt • • • • • 0x000 • • • 0x000
0xcb jcz.l • • • • • pkt • • • • • 0x000 • • • 0xcd
0xcc seq_0x10 • • • • • pkt • • • • • 0x000 • • • 0x000
0xcd jn.h • • • • • pkt • • • • • 0x000 • • • 0xcf
0xce ret • • • • • pkt • • • • • 0x000 • • • 0x000
0xcf ret • • • • • pkt • • • • • 0x000 • • • BRUSH_Y_X
0xd0 0x00000000 0x00000000
pkt_0x32:
0xd1 nxt • • • • • pkt • • • • • 0x000 • • • 0x75c
0xd2 nxt • • • • • pkt • • • • • 0x000 • • • 0x000
0xd3 nxt • • • • • pkt • • • • • ld pkt • • • 0x000
0xd4 seq_0x10 • • • • • alu • • • • • 0x000 • • • 0x75d
0xd5 rpt • • • • • pkt • • • • • 0x000 • • • 0x000
3d_load_vbpntr:
0xd6 nxt • • • • • pkt • • • • • 0x000 • • • SE_VTX_NUM_ARRAYS
0xd7 rpt • • • • • pkt • • • • • 0x000 • • • 0x000
pkt_0x2b:
0xd8 0x00000000 0x6464614d
0xd9 0x00000000 0x69687420
0xda 0x00000000 0x00000073
0xdb 0x00000000 0x00000000
load_palette:
0xdc nxt • • • • • pkt • • • • • 0x000 • • • 0x000
0xdd nxt • • • • • pkt • • • • • ld #addr • • • 0xd0
0xde nxt • • • • • alu • • • • • 0x000 • • • SRC_CLUT_ADDRESS
0xdf rpt • • • • • pkt • • • • • 0x000 • • • SRC_CLUT_DATA
0xe0 0x00000000 0x00000001
wait_for_idle:
0xe1 nxt • • • • • pkt • • • • • ld #addr • • • 0xe0
0xe2 nxt • • • • • alu • • • • • 0x000 • • • NQWAIT_UNTIL
0xe3 rpt • • • • • pkt • • • • • 0x000 • • • 0x000
0xe4 0x00000000 0x00000000
0xe5 0x00000000 0x00000000
0xe6 0x00000000 0x00000008
0xe7 0x00000000 0x00000004
0xe8 0x00000000 0x00000000
0xe9 0x00000000 0x00000000
0xea 0x00000000 0x00000000
0xeb 0x00000000 0x00000000
0xec 0x00000000 0x00000000
0xed 0x00000000 0x00000000
0xee 0x00000000 0x00000000
0xef 0x00000000 0x00000000
0xf0 0x00000000 0x00000000
0xf1 0x00000000 0x00000000
0xf2 0x00000000 0x00000000
0xf3 0x00000000 0x00000000
0xf4 0x00000000 0x00000000
0xf5 0x00000000 0x00000000
0xf6 0x00000000 0x00000000
0xf7 0x00000000 0x00000000
0xf8 0x00000000 0x00000000
0xf9 0x00000000 0x00000000
0xfa 0x00000000 0x00000000
0xfb 0x00000000 0x00000000
0xfc 0x00000000 0x00000000
0xfd 0x00000000 0x00000000
0xfe 0x00000000 0x00000000
0xff 0x00000000 0x00000000