Midway Zeus

From MAMEDEV Wiki
Revision as of 03:19, 3 December 2007 by Aaron (talk | contribs)

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.

Zeus 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). Data is written/read via two 32-bit registers $0B0 and $0B2. The address of writes is controlled by register $0B4, and 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.

Zeus Register Map

Address R/W Purpose
$006 W Low 16 bits specify texture mode
$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
$068 W Written at startup:
$00030000 = toggled on then off at startup
$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)
$080 W Master control; configures some core chip behavior:
$40000000 = set along with $02000000 when enabling FIFO empty interrupt
$02000000 = enable internal data FIFO empty interrupt
$01000000 = cleared along with $02000000 when disabling FIFO empty interrupt
$00020000 = 16-bit (0) or 32-bit (1) mode
$00000080 = set just before writing direct command to $60
$00000040 = set just before writing direct command to $60
$00000008 = cleared just before writing to offsets $200-$3FF
$084 W $00000080 = select render page? (generally set when $CC is set to $000000 and clear 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
$07FF0100 = mask for Y coordinate
$000000FF = mask for X coordinate
$0B6 W Direct RAM access control
$80000000 = enable writes to wave RAM 0
$40000000 = enable writes to wave RAM 1
$02000000 = perform access on write to B0(0) or B2(1) ?????
$00800000 = enable writes from [$B2] to depth buffer
$00400000 = enable writes from [$B0] to depth buffer
$00200000 = enable writes from [$B2] to RAM buffer
$00100000 = enable writes from [$B0] to RAM buffer
$00020000 = auto increment the address
$00010000 = perform a read when the address is written
$00000001 = enable direct RAM access

Examples:

$80A00000 when writing a single pixel (address written each time)
$80A20001 when writing the right pixel of a pair (autoinc assumed)
$80F60001 when writing the middle pairs of pixels (autoinc assumed)
$80520001 when writing the left pixel of a pair (autoinc assumed)
$82F00001 when testing wave RAM page 1 (address written each pair)
$42F00001 when testing wave RAM page 0 (address written each pair)
$82F60001 when uploading to wave RAM page 1 (autoinc assumed)
$42F60001 when uploading to wave RAM page 0 (autoinc assumed)
$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 start address? switches to $00800000)
$0CE W Written at startup = $00C87620
$0E0 W Data FIFO (see FIFO commands, below)
$0F2 R Looped on until it returns a stable value; must change frequently; PRNG? current video line?
$0F4 R Status register
$00000800 = VBLANK? code waits for it to clear to 0 and then reset to 1
$00000140 = tested together in tight loop until both are 0
$00000008 = internal data FIFO full
$00000004 = tested in tight loop along with $00000002 until at least one is set
$00000002 = tested in tight loop after handling FIFO interrupt until set
$0F6 R Status register 2
$00000010 = tested in tight loop after writing 5A/59/58 until set to 0
$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,...

Zeus 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.

Command Words Format Description
$00/$01 2 $00aaaaaa

$bbbbbbbb

Set an internal data pointer. The low 24 bits (a) of the first word specify 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 (b) specifies the actual pointer. The top 8 bits of the pointer value is the length of the data referenced (minus one). The low 24 bits of the pointer value are the row/column addresses (12 bits each).

Some known pointer values:

$008000: set pointer to model data to render
$00C040: set pointer to palette to use for texture lookups
$00C0B0: set pointer to texture to use for rendering (I think this is wrong)
$80C0A5: set pointer to something (unknown)
$13 1 $13aaaaaa 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 the data in (a) represents anything useful.
$17 1 $17aabbbb Write a 16-bit value to a register. The register index (a) 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 (b).
$18 2 $18aabbbb

$cccccccc

Write a 32-bit value to a register. The register index is (a) specified in bits 16-22, allowing for references to registers $000-$07F. It is unknown if the low bits (b) mean anything. The second word (c) contains the 32-bit value to write.
$1A 1 $1A000000 Synchronize the pipeline, or something. These are scattered throughout the data.
$1C 8 $1Caabbbb

$ccccdddd
$eeeeffff
$gggghhhh
$iiiijjjj
$kkkkkkkk
$llllllll
$mmmmmmmm

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 matrix is a set of 9 1.1.14 values packed in an odd order. The top row is (ffff eeee bbbb). The middle row is (hhhh gggg cccc). The bottom row is (jjjj iiii dddd). The translation vector (k l m) is in 1.15.16 format.
$23 2 $23aabbbb

$ccccdddd

Specify some other vector (bbbb cccc dddd). It is unknown what this is.
$25/$30 4 $25aaaaaa

$bbbbbbbb
$cccccccc
$dddddddd

Some sort of display control. Bit 7 of (a) 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.
$67 3 $67aaaaaa

$bbbbbbbb
$cccccccc

Render a model stored in Wave RAM. It is unknown if (a) means anything. (b) is a pointer to the model data, with the length of the data in the upper 8 bits and the row/column in the lower 24 bits. (c) specifies information about the texture. The address of the texture in Wave RAM seems to come from bits 0-15 and bits 26-31. The texture mode comes from bits 16-25 and controls information such as the texture width and format. Model data is stored in Wave RAM as another sequence of command words that is similar but not identical to the FIFO commands.