PS2 Linux Programming
A simple VU1 Micro Program (PATH 1)
Introduction
In this tutorial a sprite is rendered using pretty
much the simplest VU micro-program that can be made. The point of the tutorial
is to illustrate the basic techniques and steps that must be undertaken in
order to get VU1 running in micro mode. In the tutorial, a GS packet is
pre-build, upload into VU1 memory, then kick it off to the GS with a simple VU1
micro program. This is probably the simplest PATH1 render that is possible.
Path 1 is illustrated in figure 1 below which shows
the internal structure and pathways within the PS2.
Figure 1
Path 1 is from VU1 micro memory to the GIF. In
order to use path 1, a micro program must be constructed and loaded into VU1
micro memory to control the transfer of data to the GIF every frame.
VU1 works chiefly as a pre-processor for the GS. It
is designed to process three dimensional vector data and send the result
directly to the GS via the GIF. VU1 executes micro programs using a fairly
comprehensive instruction set - it has powerful 128bit multiply and divide
units. In addition, VU1 has 16kB of instruction memory to store the micro
program and 16kB of data memory to store program data. In order to start the execution
of the micro instructions in VU1 a VIF code is sent to VU1 which specifies the
execution start address (MSCAL).
Some new development tools and processes are
introduced in this tutorial. It should be notes that the utility program
(bin2as) and the section of the Makefile being used to generate the VU micro
program are extracted from the samples that come with the SPS2 development
libraries.
The VU code is contained within the file
vu1code.vsm. This is compiled with the assembler ee-dvp-as which comes with the
kit. The utility program bin2as turns the assembler output into a format that
can be used. bin2as creates two external function pointers that are used within
the main program, one that points to the start of the VU code in the binary,
and one that points to the end. These function pointers are used in order to
copy the VU code into VU1 micro memory. (Alternatively, the MPG VIF code, could
be used to transfer micro programs into VU1 memory.)
In the previous tutorial the DIRECT VIF code was
used to transfer data from the DMA packet sent to the VIF, directly to the GIF.
In this example DIRECT is replaced with UNPACK. UNPACK transfers QWC of data
from the DMA packet into VU data memory (note this is different to VU micro
memory which is where the programs are stored). UNPACK takes 3 parameters, the
first V4_32 says to unpack the data as-is. i.e. each quad word is an array of 4
32 bit items. The next parameter is the QWC to transfer. The final parameter is
the address in VU memory where the data is to go (in this case data is being
transferred to the start of memory, address 0).
After the data is unpacked, two more VIF codes are
sent. The first is FLUSH, which basically says to wait until VU1 is idle and
there is no transfer over PATH1 and/or PATH2 (i.e. there is no rendering going
on). The reason for waiting until the current rendering has finished is that if
data was being modifying in the VU micro program that the GS was busy rendering
this could cause significant problems (remember that everything is running in
parallel). The last VIF code added is
MSCALL(0) - This instruction says "Run the micro program code that is in
VU micro memory starting at address 0".
The VU1 micro program is contained in the file
vu1code.vsm and is repeated here for clarity.
.align 4
.vu
NOP iaddiu VI01, VI00, 0
NOP[E] xgkick VI01
NOP NOP
It can be seen that there are two streams of
assembly language. The stream on the left is called the "upper instruction
stream" and the stream on the right is called the "lower instruction
stream". There are no upper instructions here so they are all NOPs. Notice
that the second to last NOP has [E] at the end. The [E] tells the VU that this
is the end of the program. There is a one instruction delay however, so the VU
executes one row after the [E] bit and then ends the program so the final line
of NOPs is necessary. There is a restriction that the xgkick instruction cannot
be in the [E] delay slot. If this wasn't the case then it would be possible to
removed the last line of NOPs and set the [E] bit in the first line.
The first VU1 instruction is "iaddiu"
this instruction adds 0 to VI00 (which is hard coded to zero) and stores the
result in VI01. This is the address of the start of the GS packet in VU memory.
The second instruction is xgkick VI01, which tells the VU to send the GS packet
at the address held in VI01 to the GS via PATH1. This micro program is executed
every frame, so each frame the GS packet contained within the VU1 data memory
is sent to the GIF over PATH 1.
A few additional features to note about the example
program:
·
The VIF packet is
pre-built in SPS2 memory with the function BuildVIFPacket();
·
The UNPACK
instruction is placed at the start of the packet with the FLUSH and MSCAL
instructions being placed at the end of the packet;
·
Notice (and be
careful with) the alignment of the VIF codes and the qword pointers;
·
An extra method
FirePacketToVIF() has been added to the SPS2 Manager class. This is similar to
FirePacketToGS() except that the packet is send over DMAC channel 1 which is to
the VIF. Also, within FirePacketToVIF(), the DIR bit in the channel control
register is set to 1 to indicate that transfer is FROM main memory to the VIF;
·
In this example,
within FirePacketToVIF(), the transfer tag enable (TTE) flag is set to 0. This
means that the DMAC tag is not transferred with the data (remember it was in
the previous tutorial). Notice that the VIF code is now in the first qword of
the VIF packet – this can be seen at the start of the BuildVIFPacket()
function.
In this tutorial probably the simplest PATH1 render
using VU1 has been illustrated. Some additional tools and processes needed for
programming the VUs have been introduced.
Dr Henry S Fortuna
University of Abertay
Dundee