Midway Zeus: Difference between revisions
No edit summary |
m (→Command $17) |
||
Line 365: | Line 365: | ||
Render a model which has been previously configured by the internal data pointers. Invasion uses this; MK4 does not appear to do it this way. It is unknown if anything other than 0 is valid in the low 24 bits of the command word. | Render a model which has been previously configured by the internal data pointers. Invasion uses this; MK4 does not appear to do it this way. It is unknown if anything other than 0 is valid in the low 24 bits of the command word. | ||
===== Command $17 ===== | ===== FIFO Command $17 ===== | ||
{| border="1" | {| border="1" | ||
! Bit||31||30||29||28||27||26||25||24||23||22||21||20||19||18||17||16||15||14||13||12||11||10||09||08||07||06||05||04||03||02||01||00 | ! Bit||31||30||29||28||27||26||25||24||23||22||21||20||19||18||17||16||15||14||13||12||11||10||09||08||07||06||05||04||03||02||01||00 |
Revision as of 02:43, 20 December 2007
Midway Zeus Hardware
This topic is primarily focused on the 3D graphics hardware of the Zeus. The Zeus chip is a Midway custom 3D controller that is capable of high polygon rates. Not much is yet known about the chip, but a number of details have been reverse engineered. This page describes what is currently known/understood.
Main CPU
The main CPU driving the Zeus is -- at least in the case of Mortal Kombat 4 -- a TMS32C032 DSP running at 60 MHz.
Zeus 3D Graphics
The Zeus chip is memory mapped into the main CPU's address space at addresses from $880000-$8803FF. Keep in mind that the TMS32C032 only accesses 32-bit memory, so each of the 1,024 addresses is 32 bits wide (i.e., address $880000 references one 32-bit word, and address $880001 references a completely independent 32-bit word).
As with most external chips, the memory map for the Zeus consists of a number of registers, in this case 512 registers. One interesting aspect is that the Zeus internally appears to be a 32-bit chip, but can be accessed in (and defaults to) a 16-bit mode. The way this is done is that in 16-bit mode, accesses to odd addresses reference the upper 16 bits of a register, while accesses to even addresses reference the lower 16 bits of a register. When the chip is switched to 32-bit mode, almost all accesses are to the even registers. It is not clear what happens if you read or write to an odd address when the chip is in 32-bit mode, though the games on the hardware occasionally do so.
The 512 registers on the chip are described by their addresses. So register 0 maps to address $000, while register 1 maps to address $002, etc. This helps keep things straight when referring back to the code.
Wave RAM
The Zeus chip provides access to some local RAM called "Wave RAM" in the diagnostic tests. There are two banks of Wave RAM, 8MB each, for a total of 16MB of 3D graphics RAM. Wave RAM holds the frame buffers, textures, model data, palettes, and other tables used by the Zeus.
Most references to Wave RAM are done in terms of what I suspect are row/column addresses. The low 12 bits of an address are the column, and the upper 12 bits of an address are the row (the top 8 bits are used for other purposes). Wave RAM appears to be configured as 512 columns, so really only 9 of the 12 lower bits are used. 11 of the 12 upper bits are used, giving 512 * 2048 (1M) addressable locations.
Direct access to Wave RAM from the CPU is done in 8 byte (64-bit) chunks. This is where the 8MB comes from (512 * 2048 * 8).
Wave RAM appears to be addressed in several different ways. The most common format for referencing a Wave RAM address is to use a special length/row/column format as follows:
Bit | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | LENGTH | x | ROW | x | x | x | COLUMN |
I will call this form "block form", since it is used to reference not only an address but a block of data up to 512 bytes long. I also call it "block form" because everything is specified in terms of 8-byte "blocks". The actual byte address into Wave RAM is given by (8 * (COLUMN + (ROW * 512))). The length LENGTH specifies a length of 1-64 blocks (0 means 1 block, 63 means 64 blocks).
When accessing Wave RAM via the direct RAM access registers, a similar form is used but without the length:
Bit | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
x | x | x | x | x | ROW | x | x | x | x | x | x | x | COLUMN |
I will call this form "expanded form", since the ROW and COLUMN parts are expanded out to 16 bits each. Again, the ROW and COLUMN values reference 8-byte blocks of data, so the byte address is given by (8 * (COLUMN + (ROW * 512))). Data is written/read via two 32-bit registers $0B0 and $0B2. The address of direct accesses is controlled by register $0B4 and is in the expanded form described here. Writing modes are controlled by register $0B6.
The frame buffer consists of 15-bit RGB pixels. It appears that the frame buffer and depth buffer are interleaved at two-pixel intervals. If Pn represents a pixel value, and Zn represents a depth value, it is ordered as:
P0 | P1 | Z0 | Z1 | P2 | P3 | Z2 | Z3 | ... |
When initializing buffers, the depth value is often written as $7FFF.
Register Map
Address | R/W | Purpose |
---|---|---|
$000 | W | Controls solid color when a direct command is written to $060. Used in Invasion's test mode for the solid color monitor screens (and for clearing the screen between test mode screens). |
$004 | W | This is a bitmask controlling some texture behaviors:
invasn writes via model command $19. Values seen = $00001C00 |
$006 | W | Low 16 bits specify texture mode |
$008 | W | Specifies Y (in the upper 16 bits) and X (in the lower 16 bits) of the vertex 1 coordinate when a direct command is written to $060. |
$00A | W | Specifies Y (in the upper 16 bits) and X (in the lower 16 bits) of the vertex 2 coordinate when a direct command is written to $060. |
$00C | W | Specifies Y (in the upper 16 bits) and X (in the lower 16 bits) of the vertex 3 coordinate when a direct command is written to $060. |
$00E | W | Specifies Y (in the upper 16 bits) and X (in the lower 16 bits) of the vertex 4 coordinate when a direct command is written to $060. |
$04E | W | Seems to affect blending. Is generally set to $00FFFFFF. However, during screen fades, the system sets this to $000080 (indicating no fade) and ramps it down to $000000 (at full blackness) just before rendering a quad that overlays the area to fade out. Is it alpha blending or modulation of existing pixels?
Also note that when rendering directly via a write to $060, this is set to $00808080. |
$058 | W | Usually written after $05A; after writing, waits for read from $0F6 to return with bit 4 cleared |
$05A | W | Usually written immediately before writing $058 |
$060 | W | A value of $00000001 is written here immediately after filling the lower registers with what look like parameters for a quad. Before writing a 1 here, the low 24 bits of the master control at $080 are set to $22FCFF. |
$068 | W | MK4 written at startup: $00030000 = toggled on then off at startup
invasn writes via FIFO command $17 (both high and low) and via model command $19. Values seen = $40C41370 |
$070 | W | Seems to specify the X coordinate of a model (written as 16-bit from FIFO command $17) |
$072 | W | Seems to specify the Y coordinate of a model (written as 16-bit from FIFO command $17) |
$074 | W | Seems to specify the Z coordinate of a model (written as 16-bit from FIFO command $17) |
$076 | W | invasn writes to this both directly and via FIFO command $18. Values seen = $F8800000 |
$078 | W | invasn writes to this via FIFO command $18. Values seen = $000000C7. Could be the screen X origin in the low 16 bits. |
$07A | W | invasn writes to this via FIFO command $18. Values seen = $00800000. Could be the screen Y origin in the high 16 bits. |
$07C | W | invasn writes to this via FIFO command $18. Values seen = $FFFFFFE0 |
$07E | W | This is almost certainly a Z buffer offset. The computed Z value is added to this 16.16 value before doing Z comparisons and other Z buffering. It is necessary to implement this in order for many objects to show in front of their backgrounds. |
$080 | W | Master control; configures some core chip behavior:
|
$084 | W | $00000xxx = select framebuffer base offset for rendering? (generally set to $080 when $CC is set to $000000 and set to $000 when $CC is set to $800000) |
$0B0 | R/W | Data port for direct RAM access |
$0B2 | R/W | Data port for direct RAM access |
$0B4 | W | Address for direct RAM access in expanded form |
$0B6 | W | Direct RAM access control
Examples:
|
$0C0 | W | Written at startup = $801F2500 |
$0C2 | W | Written at startup = $0015E591 |
$0C4 | W | Written at startup = $000C0042 (HSYNC start/HSYNC end?) |
$0C6 | W | Written at startup = $0211007F (HTOTAL/HBLANK end?) |
$0C8 | W | Written at startup = $010300FF (VSYNC start/VBLANK start?) |
$0CA | W | Written at startup = $01160107 (VTOTAL/VSYNC end?) |
$0CC | W | Written at startup = $00000000 (display framebuffer start address? toggles between $00000000 and $00800000) |
$0CE | W | Written at startup = $00C87620 |
$0E0 | W | Data FIFO (see FIFO commands, below) |
$0F0 | R | Current horizontal video beam position; used by invasn for reading the lightgun (low 11 bits) |
$0F2 | R | Current vertical video beam position; used by invasn for reading the lightgun (low 10 bits) |
$0F4 | R | Status register
|
$0F6 | R | Status register 2
|
$200-$3FF | W | Unknown, but written in a loop with master control bit $00000008 cleared
Possible fast buffer clear? One set of writes stores $7F7F to all entries. A second set writes $E00,$25,$E01,$25,...,$E7F,$25. A third set writes $52,$A0000000,... |
Internal Pointer Registers
There appears to be a set of internal registers which hold pointers to data in wave RAM. They don't appear to be directly accessible via the register map, but are written to via special FIFO or model commands. Which pointer register is being accessed is controlled by a parameter WHICHPTR. The value of pointer registers is always a block pointer into wave RAM. Below is a table of the values that have been seen so far, and what they mean:
WHICHPTR | Purpose |
---|---|
$008000 | pointer to model data to render via FIFO command $13 |
$018000 | pointer to model data to render via FIFO command $13 (does the top byte matter?) |
$00C040 | pointer to palette to use for texture lookups |
$004040 | set via FIFO command in mk4 (len=02) |
$02C0F0 | set in model data in mk4 (len=0F) |
$03C0F0 | set via FIFO command in mk4 (len=00) |
$02C0E7 | set via FIFO command in mk4 (len=08) |
$04C09C | set via FIFO command in mk4 (len=08) |
$05C0A5 | set via FIFO command in mk4 (len=21) |
$80C0A5 | set via FIFO command in mk4 (len=3F) |
$81C0A5 | set via FIFO command in mk4 (len=35) |
$82C0A5 | set via FIFO command in mk4 (len=41) |
$00C0F0 | set via FIFO command in invasn (len=0F) |
$00C0B0 | set via FIFO command in invasn (len=3F) -- seems to be the same as C0A5 |
$05C0B0 | set via FIFO command in invasn (len=21) |
$00C09C | set via FIFO command in invasn (len=06) |
$00C0A3 | set via FIFO command in invasn (len=0A) |
FIFO Commands
Commands are written through the FIFO register ($0E0). There is a whole mechanism in the chip for overflowing the FIFO, returning status in the status register ($0F2) and signalling an interrupt when there is space freed up to continue pumping data. But so far we don't need to emulate that, since we are being simplistic and pretending the whole chip runs infinitely fast.
FIFO commands are a stream of 32-bit data. The top 8 bits (or is it 7 bits?) of the first 32-bit word indicate the command. Some commands require just a single 32-bit word, while others require multiple words to follow.
Several FIFO commands appear to be able to write back to registers. Most registers are written either directly (via accessing memory $880000-$8803FF) or indirectly (via FIFO commands), but a few are accessed in both ways, and match up reasonably well. From what I can tell, only the low 64 registers (from $000-$07F) can be written via the FIFO. This makes sense as the registers higher up are less frequently accessed and appear to be primarily for control.
FIFO Command $00
Bit | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Word 0 | $00 | WHICHPTR | ||||||||||||||||||||||||||||||
Word 1 | x | x | LENGTH | x | ROW | x | x | x | COLUMN |
Set an internal pointer register. The low part of the first word (WHICHPTR) specifies which internal pointer to set and probably some mode bits. It is not known if these pointers are associated with registers or if they are only accessible internally. The second word specifies the actual pointer, in block form, providing an explicit LENGTH of 1-64 blocks, and separate ROW and COLUMN addresses.
FIFO Command $13
Bit | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Word 0 | $13 | 0 |
Render a model which has been previously configured by the internal data pointers. Invasion uses this; MK4 does not appear to do it this way. It is unknown if anything other than 0 is valid in the low 24 bits of the command word.
FIFO Command $17
Bit | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Word 0 | $17 | ? | REGNUM | REGDATA |
Write a 16-bit value to a register. The register index REGNUM is specified in bits 16-22, allowing for references to registers $000-$07F. Odd register numbers write to the top 16 bits. Even register numbers write to the low 16 bits. (Does this make sense for registers $070,$072,$074 which write X,Y,Z coordinates for the rain effect in MK4?) The data to be written is specified in the low 16 bits REGDATA.
FIFO Command $18
Bit | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Word 0 | $18 | ? | REGNUM | 0 | ||||||||||||||||||||||||||||
Word 1 | REGDATA |
Write a 32-bit value to a register. The register index REGNUM specified in bits 16-22, allowing for references to registers $000-$07F. It is unknown if anything other than 0 means anything in the low half of the first word. The second word contains the 32-bit REGDATA value to write.
FIFO Command $1A
Bit | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Word 0 | $1A | 0 |
Synchronize the pipeline, or something. These are scattered throughout the data.
FIFO Command $1C
Bit | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Word 0 | $1C | 0 | MATRIX[0,2] | |||||||||||||||||||||||||||||
Word 1 | MATRIX[1,2] | MATRIX[2,2] | ||||||||||||||||||||||||||||||
Word 2 | MATRIX[0,1] | MATRIX[0,0] | ||||||||||||||||||||||||||||||
Word 3 | MATRIX[1,1] | MATRIX[1,0] | ||||||||||||||||||||||||||||||
Word 4 | MATRIX[2,1] | MATRIX[2,0] | ||||||||||||||||||||||||||||||
Word 5 | XOFFSET | |||||||||||||||||||||||||||||||
Word 6 | YOFFSET | |||||||||||||||||||||||||||||||
Word 7 | ZOFFSET |
Set rotation matrix and translation vector. This command specifies a rotation matrix to be applied to all models rendered, along with a translation vector. The 3x3 MATRIX is a set of 16-bit 1.1.14 values packed two to a word. The translation vector (XOFFSET,YOFFSET,ZOFFSET) is specified as a set of 32-bit values in 1.15.16 format.
Bit | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Word 0 | $1C | 0 | $7FFF | |||||||||||||||||||||||||||||
Word 1 | MATRIXA[1,0] | MATRIXA[0,0] | ||||||||||||||||||||||||||||||
Word 2 | MATRIXA[1,1] | MATRIXA[0,1] | ||||||||||||||||||||||||||||||
Word 3 | MATRIXA[1,2] | MATRIXA[0,2] | ||||||||||||||||||||||||||||||
Word 4 | MATRIXB[0,1] | MATRIXB[0,0] | ||||||||||||||||||||||||||||||
Word 5 | MATRIXB[0,2] | MATRIXA[2,0] | ||||||||||||||||||||||||||||||
Word 6 | MATRIXB[1,2] | MATRIXA[2,1] | ||||||||||||||||||||||||||||||
Word 7 | MATRIXB[2,2] | MATRIXA[2,2] | ||||||||||||||||||||||||||||||
Word 8 | MATRIXB[1,1] | MATRIXB[1,0] | ||||||||||||||||||||||||||||||
Word 9 | MATRIXB[2,1] | MATRIXB[2,0] | ||||||||||||||||||||||||||||||
Word 10 | XOFFSET | |||||||||||||||||||||||||||||||
Word 11 | YOFFSET | |||||||||||||||||||||||||||||||
Word 12 | ZOFFSET |
This is an extended form of the command which accepts two matrices. The low 16 bits of the command word are set to $7FFF to indicate that we are using the extended form. The two matrices contain 16-bit values in 1.0.15 format, and are multiplied together (MATRIXB x MATRIXA), taking the top 16 bits to give a 1.1.14 value just like the single matrix form. The translation vector is the same format as the regular form.
FIFO Command $23/$2E
Bit | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Word 0 | $23 or $2E | 0 | XVECTOR | |||||||||||||||||||||||||||||
Word 1 | YVECTOR | ZVECTOR |
Specify a vector. It is unknown what the purpose of this vector is, though it is possible that it is a point light source.
FIFO Command $25/$30
Bit | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Word 0 | $25 or $30 | VALUE1 | ||||||||||||||||||||||||||||||
Word 1 | VALUE2 | VALUE3 | ||||||||||||||||||||||||||||||
Word 2 | VALUE4 | VALUE5 | ||||||||||||||||||||||||||||||
Word 3 | VALUE6 | VALUE7 |
Some sort of control. Bit 7 of VALUE1 is toggled on each frame. Does this select the page? The other data is completely unknown. It is also strange that MK4 uses $25 and Invasion uses $30, but they both appear to be the same commands. With MK4 the first word is usually $25000000, $25000080, $25008000, or $25800000. The latter two cases expect a total of 10 data words, while the former two expect a total of 4 data words.
FIFO Command $2D/$70
Bit | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Word 0 | $2D or $70 | UNKNOWN |
This command is rarely used and its purpose is unknown.
FIFO Command $67
Bit | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Word 0 | $25 or $30 | 0 | UNKNOWN | |||||||||||||||||||||||||||||
Word 1 | x | x | MLENGTH | x | MROW | x | x | x | MCOLUMN | |||||||||||||||||||||||
Word 2 | TEXOFFSHI | TEXMODE | TEXOFFSLO |
Render a model stored in Wave RAM. UNKNOWN may or may not mean anything. The second word specifies a pointer to model data in wave RAM, using the block form. The texture data is specified in the second word. TEXMODE describes a texture mode, including information on the texture data width and format. TEXOFFSHI combines with TEXOFFSLO to produce the texture row for rendering.
Model Commands
Model commands are stored as a stream of data in Wave RAM. A pointer to the model is specified either through FIFO command $67 or by writing command $00008000 and specifying the pointer to the model data there. The pointer to model data contains a 6-bit length in bits 24-29 and a row/column pointer in bits 0-23. The 6-bit length is the length of the model data in 8 byte chunks, minus 1. Thus, a length of $3F references 64 8-byte chunks.
It is possible that the commands below are related to the FIFO commands (it would make sense). However, there is not a perfect overlap and at least one conflict.
Model Command $08
Bit | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Word 0 | $08 | 0 | ||||||||||||||||||||||||||||||
Word 1 | 0 |
Indicates end of model data.
Model Command $0C
Bit | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Word 0 | $0C | WHICHPTR | ||||||||||||||||||||||||||||||
Word 1 | x | x | LENGTH | x | ROW | x | x | x | COLUMN |
Set an internal pointer register. This is similar to the $00/$01 FIFO command. Note that for models that require more than 64 8-byte chunks of data, this command can be used to chain to another chunk of data by specifying a=$008000 and b=pointer to the next chunk.
Model Command $17
Bit | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Word 0 | $17 | ? | REGNUM | REGDATA | ||||||||||||||||||||||||||||
Word 1 | 0 |
Set a 16-bit register value. This is similar to the $17 FIFO command.
Model Command $19
Bit | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Word 0 | $19 | ? | REGNUM | 0 | ||||||||||||||||||||||||||||
Word 1 | REGDATA |
Set a 32-bit register value. This is similar to the $18 FIFO command.
Model Command $25/$30
Bit | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Word 0 | $25 or $30 | ZNORMQ | YNORMQ | XNORMQ | ||||||||||||||||||||||||||||
Word 1 | TEXPARAMS | TEXOFFS | ||||||||||||||||||||||||||||||
Word 2 | Y1 | X1 | ||||||||||||||||||||||||||||||
Word 3 | V1 | U1 | Z1 | |||||||||||||||||||||||||||||
Word 4 | Y2 | X2 | ||||||||||||||||||||||||||||||
Word 5 | V2 | U2 | Z2 | |||||||||||||||||||||||||||||
Word 6 | Y3 | X3 | ||||||||||||||||||||||||||||||
Word 7 | V3 | U3 | Z3 | |||||||||||||||||||||||||||||
Word 8 | Y4 | X4 | ||||||||||||||||||||||||||||||
Word 9 | V4 | U4 | Z4 | |||||||||||||||||||||||||||||
Word 10 | 0 | ZNORM1 | YNORM1 | XNORM1 | ||||||||||||||||||||||||||||
Word 11 | 0 | ZNORM2 | YNORM2 | XNORM2 | ||||||||||||||||||||||||||||
Word 12 | 0 | ZNORM3 | YNORM3 | XNORM3 | ||||||||||||||||||||||||||||
Word 13 | 0 | ZNORM4 | YNORM4 | XNORM4 |
Specify a quad for rendering. XNORMQ, YNORMQ, and ZNORMQ describe a 1.0.7 unit vector for the polygon normal. Note that in some cases this isn't properly normalized, but I suspect lighting is disabled when this is the case.
TEXPARAMS control texturing. The upper byte is usually either $02, $21, or $23; it is unknown what this means. Bit 7 of TEXPARAMS is sometimes set to one; its purpose is likewise unknown. Bit 0 of TEXPARAMS, when set, means that color 0 in the texture data is transparent. The 16-bit TEXOFFS is a texture V offset. In most quads, the smallest V is normalized to 0 and this is an offset. MK4 uses it as a true offset, specifying a base texture address in the $67 command. Invasion uses this as the full offset, using the $13 command which does not specify a base texture address. In this case, assuming the base == 0 seems to work.
(X1, Y1, Z1) describe the coordinates of the first vertex, in 1.13.2 format. (Note that 1.13.2 × 1.1.14 (matrix value) gives 1.14.16 which can be directly added to the 1.15.16 translation vector.) The texture coordinates of the first vertex are specified by (U1, V1). This pattern is repeated for the remaining 3 vertices.
The final four words appear to be packed vertex normals for each vertex. Each 32-bit word contains three 10-bit packed values in 1.1.8 format. The top two bits appear to be unused.
An interesting note here is that this command intersects the FIFO command $25/$30 perfectly. That is, MK4 uses $25 in the FIFO and $25 in the model data. Invasion uses $30 in the FIFO and $30 in the model data. This bolsters the case that the commands are related.
Textures
Texture references are a little baffling at the moment. It is clear that a texture is specified by the third word in FIFO command $67 (used by MK4), or in part by a write to register $06 (used by Invasion).
The format of the FIFO command word, based on analyzing the code, appears to be something like this:
Bit | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
A | A | A | A | A | A | B | W | W | W | D | C | C | C | C | C | A | A | A | A | A | A | A | A | A | A | A | A | A | A | A | A |
The various 'A' bits seem to go together to form an address of sorts (more on that in a minute).
The 'B' bit's purpose is unknown at this time.
The three 'W' bits control the width of the texture. The width appears to be 512 >> W, giving potential widths of 512, 256, 128, 64, 32, 16, 8, and 4.
The 'D' bit controls whether the texture data is 8bpp or 4bpp. All textures so far appear to be palettized, so a palette base must be set prior to rendering.
The purpose of the 5 'C' bits is unknown at this time.
So, back to the address. The weird thing about texture addressing is that the width specified by the 'W' bits seems to affect the texture address. In essence, if you treat Wave RAM as an array of bytes arranged with a width of 'W', the address 'A' is simply the row number.
W | Width | Wave RAM Address |
---|---|---|
0 | 512(?) | 'A' << 9 |
1 | 256 | 'A' << 8 |
2 | 128 | 'A' << 7 |
3 | 64 | 'A' << 6 |
4 | 32 | 'A' << 5 |
5 | 16 | 'A' << 4 |
6 | 8 | 'A' << 3 |
7 | 4 | 'A' << 2 |