VU code isn't hard, but it can be a little awkward. VCL is a tool that makes writing VU code a lot easier for you by taking care of lots of things that I'll only brush over here. The VU can actually pair up instructions and run 2 of them at once, you have to be careful about using results from previous operations before they're ready, and other things like that. But that's the last you'll hear of them hear, because we're using VCL
(Note to native PS2DEVers, this is what makes VCL so useful. If it's not available cross-platform soon, I'll make a basic sample without using VCL just for you.)
If you haven't installed VCL yet, you might need to here. It's found on the first DVD of the Linux kit, which is the one you have to use to boot Linux, so it's already in your drive! Go to your favourite directory, and type:
mount /mnt/cdrom
cp /mnt/cdrom/vcl/vcl-12_1.tgz .
tar zxfv vcl-12_1.tgz
cp vcl-1.28 /usr/local/bin
Note: There's an updated VCL available at the playstation2-linux.com web site, which contains a PDF manual. The version on the disk does have some text files to read though, so please do so!
So, to actually make our code file, first we run it through VCL to turn it into a proper VU assembly source file, then we assemble that file with ee-dvp-as, the vector unit assembler, into an object file. However, the assembler leaves the file in a form that is suitable for linking into big projects, not suitable for uploading into VU instruction memory, so we must take a final step to convert our object file into a binary file that can be uploaded. The details of this process are hidden away in the Makefile for the basic sample, if you just stick to editing the basic.vcl file, the Makefile will do the job for you.
All the basic sample does is take the GIFpacket we made and stored in our data file, and send it to the GS. Let's dissect the basic.vcl file:
kGifPacket .assign 0
.init_vf_all
.init_vi_all
.syntax new
.vu
--enter
--endenter
START:
iaddiu iKick, vi00, kGifPacket
xgkick iKick
--exit
--endexit
Like all VCL code files, we have a --enter--endenter block, and a --exit--endexit block. These can do handy things for us when we integrate VU microcode into a larger program. However, I don't think there's anything clever they can do for us in this competition, so I'll leave them for now. Just use them to mark the start and end of your code.
You may be more comfortable with seeing the line
kGifPacket .assign 0
written as
#define kGifPacket 0
The latter is syntax you expect in a C file, processed by the CPP preprocessor. The former is syntax expect by the GASP preprocessor. VCL can be set up to use either, but for now we'll leave it in GASP syntax. All the line does is change every occurrence of kGifPacket into a 0 throughout the file, *before* any actual assembling is done.
Caveat: GASP doesn't recognise some numbers correctly - namely those in Hex notation and floating point numbers. There may be others out there, but these are known problems. To fix them, write hex numbers as H'123 instead of 0x123, and 5.325e-2 instead of 0.05325. I don't know whether this caveat applies to the CPP preprocessor, I guess that it doesn't.
The directives .init_vi_all, and .init_vf_all tell VCL that we're allowed to use all of the integer and float registers, which makes sense as our demo is the only one running. .syntax new tell VCL to use the new syntax model, presumably there was an old syntax model, but I never knew of it :) Todo: I'm not sure what the .vu directive actually does, I think it's a directive to use the VU instruction set, so just leave it in and don't worry about it.
So far, everything has just been syntax to get up and running, so where are the lines that actually do something? They're here:
START:
iaddiu iKick, vi00, kGifPacket
xgkick iKick
The START: line is just a label, it's not necessary for this program, so I'll get on to labels later... satisfy yourself for now that they mark a place in the code that you can jump to later.
The xgkick line is the really important instruction, it says "Find the GIFpacket stored in data memory at the address stored in the iKick register, and send it to the GS"
But, what's in the iKick register? That's what the line above does. Let's translate it into a form you might be more used to:
iaddiu is the instruction to 'Integer ADD an Immediate Unsigned' value. Immediate means the value is specified on the line directly, not stored in some register. The command is followed by what register you want to put the answer in, what register you're adding to, and finally the value you're adding.
So, iaddiu iKick, vi00, kGifPacket means to add the value kGifPacket to vi00, and put the result in iKick. A couple of things to note. vi00 is an integer register that *always* holds the value zero. kGifPacket was assigned the value 0 earlier, but where's the iKick register? iKick is just a name I gave to the register. VCL will translate that for me into a real register name, most probably vi01, but it doesn't matter. As long I use the name iKick consistently, I'm safe.
So, what the command really says is "Take the value 0, add 0 to it, and put the value in some register" or even more simply, "Put the value 0 in the iKick register". Taken together the two lines of code say "Kick the GIFpacket stored in data memory at address 0 to the GS". Where is address 0? It's the start of data memory. In other words, it's the GIFpacket we carefully constructed in 3: Making your data file.
The final thing to notice with the code is that it just ends. There's nothing at the end, save for the --exit--endexit block. VCL puts a magic instruction in the end the code for you, which gives control back to the harness. This is important, how can the harness tell you what the controllers are doing, and maybe switch to another demo, unless you give control back every now and again? Your code is called once per frame, and must stop. Don't worry, soon after you've stopped, you're code will be called again for the next frame!
Let's do something a little more interesting though. How about we make one of the vertices move? We'll take one bit of our vertex information, the x co-ordinate of the first co-ordinate of the triangle, and increment it, making the vertex move horizontally to the right.
In assembly, we have to do it a little like this. Load up the value from data memory, change it, and store it back. Here's the three lines you need.
ilw.x iFirstX, 2(vi00)
iaddiu iFirstX, iFirstX, 16
isw.x iFirstX, 2(vi00)
The first line says, "Integer Load the Word" stored in the x field of the *3rd* QWord into the iFirstX register. What's the 2(vi00) for? It means, take the address stored in vi00 (Which you may remember is *always* zero), and offset it by 2 (A fancy way to say add 2 to it). So, we're looking at QWord 2, but remember the first QWord is numbered 0. So, that's the 3rd QWord.
Why the 3rd QWord? Look back at gen_data.c in 3: Making your data file. QW0 contains the GIFtag, QW1 contains the first vertex's colour info, and QW2 contains the co-ordinates of the first vertex. Now do you see why I've called it the iFirstX register?
Then the iaddiu instruction appears again, this time to add 16 to the iFirstX register and store the result in... the iFirstX register! It's perfectly OK to specify the same register as inputs and outputs to an instruction.
The third line looks exactly like the first, but "Integer Load the Word" has become "Integer Store the Word". In other words, we're writing the value in the iFirstX register back where we got it from.
These three lines should go in-between the START: line and the existing iaddiu instruction, leaving you with this:
START:
ilw.x iFirstX, 2(vi00)
iaddiu iFirstX, iFirstX, 16
isw.x iFirstX, 2(vi00)
iaddiu iKick, vi00, kGifPacket
xgkick iKick
Remake the sample, run it in the harness, and watch that triangle move!
If you want to know more about VCL, there is a PDF manual included with it. If you want to know more about the VU instruction set available to you in micromode, see the VU User's Manual. Or see 5: Instruction set. Until then, see if you can answer these homework questions.
1) Change the code to move a different vertex, in a different direction.
2) Move all 3 vertices in the same direction at once, moving the triangle.
3) Read up on 2D rotation and scaling, and make your triangle zoom in and out and spin. Note: this is a hard question - you may want to just skip it and come back to it when you're a little happier with VU coding.
4) The code above will quite happily move the vertex off screen and keep incrementing it until it hit's the end of the world and wraps back round to the left. Make the vertex change direction or warp to the left of the screen before it can go off screen. Hint: The screen is 640x224, and you'll need to read up on branching instructions and comparison instructions, or maybe the CLIP instruction - again you may want to come back to this later.
5) Make a GIFpacket with a few hundred random points of random colours with your data generation program. Make them all scroll to the left, at different speeds, making a starfield effect. When a 'star' hits the left of the screen, put it back over at the right of the screen.
Prev - 3: Making your data file Next - 5: Instruction set