PS2 Basics
PS2 Basics
PS2 Basics
Playstation2 Basics
"Welcome to the Machine"
Version 1.0, 2002/03/06 - Tony Saveski (dreamtime), t_saveski@yahoo.com
Introduction
Welcome to the first of the Playstation2 Development (ps2dev) Tutorials. I don't want to waste any time on fluffy introductions to the 'scene' and the PS2, so after a quick intro I'll get right into what everyone is reading this for. Just a note first though: This tutorial is intended for the almost total newbie to PS2 development. If you're already compiling and running your own code you probably won't learn much here :-) There is already a fair bit of information available on the net about programming the PS2. Some people and groups have even released small demos and source code. Many thanks to them! But the main thing missing is detailed explanations of what's going on in this code, so that the less experienced of us can learn and join in the fun. I have to admit I am completely new to console development. I have never programmed a MIPS chip before and my knowledge of hardware is almost non-existent. This should be good for the reader because it means I won't be able to confuse you with detailed and cryptic explanations (although I have been called a 'vague bastard' in the past). My aim is to carefully understand and fully explain everything that goes into achieving some programming task on the PS2 and end up with a clean and well-documented code base that I can share with everyone. Where things are not clear to me, I will put a note asking for someone to 'please explain'. If I am totally wrong about something, and I'm sure that sometimes I will be, please tell me. I will make an effort to keep the tutorials up to date and as accurate as possible. So, let's get started with some baby-steps. This tutorial will provide you with knowledge of basic PS2 hardware components and interfaces, and code for: * Initialising the graphics system, * Setting a video mode, * Making something show up on the screen. In the process, we will also end up with a set of DMA and other routines that we will reuse many times in the future.
Development Environment
Before you can start testing your code on a PS2, you will need to set up a development environment. I have chosen not to write anything about this, but instead point you to an
excellent tutorial currently maintained by now3d at http://ps2dev.sourceforge.net/guide.txt . The main components you will need to have are: * A USB to USB cable, * A working set of compilers (GCC), * Naplink. More detailed information about setting up compilers and using Naplink can be found in the Tutorials section of http://ps2dev.livemedia.com.au (awsome site by Oobles). Once you have everything set-up, compile some of the already available demos and run them in your environment. You can find the source code to some of these at LiveMedia. By the way, I learned almost everything described in this tutorial from the 3stars and funslower demos. Go out and get them now!
I prefer not to use data-type names relevant to a particular machine because it makes porting to machines with a different word size difficult. Instead, I use explicit names based on the data-type size in bits. Here are the definitions (from defines.h):
typedef char typedef short typedef int int8; int16; int32;
typedef unsigned char uint8; typedef unsigned short uint16; typedef unsigned int uint32; typedef unsigned long uint64; typedef long int64; typedef struct int128 { int64 lo, hi; } int128 __attribute__((aligned(16))); typedef struct uint128 { uint64 lo, hi; } uint128 __attribute__((aligned(16)));
The -mips3 switch on the gcc command line ensures that the long data-type is 64 bits. Don't forget it.
// // // // //
Saved Temporaries. Make sure to restore to original value if your function changes their value.
// More Temporaries. // Reserved for Kernel // // // // Global Pointer Stack Pointer Frame Pointer Function Return Address
Privileged GS Registers
As mentioned above, these registers are actually mapped to the EE's memory address space. Writing values to the addresses defined here will actually set the value of the GS register. These registers are all 64 bits wide.
#define #define #define #define #define #define #define #define #define pmode smode2 dispfb1 display1 dispfb2 display2 bgcolor csr imr 0x12000000 0x12000020 0x12000070 0x12000080 0x12000090 0x120000a0 0x120000e0 0x12001000 0x12001010 // // // // // // // // // Setup CRT Controller CRTC Video Settings RC1 data source settings RC1 display output settings RC2 data source settings RC2 display output settings Set CRTC background color System status and reset Interrupt Mask Register
Privileged GS Registers
Let's take the bgcolor register as an example. It is a 64-bit register used to set the background color of the CRT controller, where: bits 0 to 7 represent the Red component; bits 8-15 represent the Green component; and bits 16-23 represent the Blue component. To simplify the task of setting the register, I have defined a set of convenience macros in the gs.h file. For each privileged GS register, you will find a macro like this:
#define BGCOLOR ((volatile uint64 *)(bgcolor)) #define GS_SET_BGCOLOR(R,G,B) \ *BGCOLOR = \ ((uint64)(R) << 0) | \ ((uint64)(G) << 8) | \ ((uint64)(B) << 16)
The macro accepts a parameter for each register field, shifts each by an appropriate number of bits, and performs a bitwise OR of all the fields to come up with the 'magic number' to assign to the register. A cool side effect of using this method to set register values is that if any macro parameter value is 0, gcc will not generate the code to needlessly shift the 0 and OR it, even when you tell it to not optimize (-O0). Go ahead and try it! Some other techniques I've seen would generate code to assign 0 to the fields. The volatile keyword tells the C compiler not to optimise access to and from the memory location pointed to. The value is always explicitly read just before usage, and written to immediately when asked to. This ensures that if the value of the register mapped to this address is changed by another thread or interrupt handler, we will always read the current value and not some 'cached' value that is out of date.
Code Discussion
I can hear you screaming out, GET TO THE POINT!. Well, we now have enough information and code to start doing the interesting stuff. I was originally planning on pasting the source code here and talking about it line by line. I think that would probably be way too boring for you (and take way to long for me to type it up, in addition to driving me crazy). Instead, I will spend the next few sections only talking about some interesting and/or important things I've discovered by playing around with different registers. The rest is pretty self-explanatory by looking at the source code (don't they always say that). I've included some pretty detailed comments to help.
File Name defines.h regs.h ps2.h, ps2.c, ps2_asm.s gs.h, gs.c, gs_asm.s dma.h, dma.c, dma_asm.s gif.h g2.h, g2.c
Description Contains common data type definitions. Defines the addresses and id's of the PS2 registers used throughout the code. This set of files contains any general PS2 'system' functions. GS Specific functions and macros. A bunch of DMA routines and macros. Contains macros to make creating GIF tags and buffers easier. A 'high level' 2D graphics library, making use of all the above, to be used by the demos. The final 'demo', which uses the G2 library.
demo1.c
Note: I've named the xxx_asm.s files that way to avoid accidentally overwriting the .s files when telling gcc to generate an assembler listing of a C file named xxx.c (as would happen if they were named just xxx.s).
CRTC Initialization
The signal that is sent from the PS2 to your TV or Monitor is controlled by the CRTC. You must tell the CRTC whether you want the signal to be PAL or NTSC, and Interlaced or Non-Interlaced (among other things). This is all controlled using the SMODE2 register. In the source code, I actually use a PS2 system call to set these values, mainly because I couldn't get it to work properly with just the SMODE2 register only (although I havn't tried recently). I hope someone can explain what syscall 0x02 is doing in addition to setting the SMODE2 register (come on all you 'BIOS hackers'). Note that it's not necessary to set the SMODE2 register if you've used the syscall, as all the demos I've seen so far seem to do.
memory. You could have debug and other info going to the text frame, but not being displayed until the user hits the 'Console Key'. You could then turn on mixing of the two RC's, using some wonderful blending method. Animated, of course. I'd like to hear about any other ideas people have had about effects with the RC's.
magv - Vertical magnification. I don't think we'd ever need to set it to anything other than zero. dw - Display Area Width - 1, in VCK units. By observing the behaviour of setting different values here and in the MAGH field, I've worked out the following things about VCK units and video modes in general: * A VCK is a unit of measure of a constant horizontal distance across the screen (like a pixel). * You should set this value to: GraphicModeWidth * MAGH - 1. If you multiply all the 'standard' screen widths above with the recommended horizontal magnification amount, it will always equal about 2560 VCK's. * You can actually do whatever you want with these values, even create a custom 'video mode', as long as you consistently set-up the CRTC and the GS. For custom modes you can probably calculate the MAGH value as (2560 / CustomWidth) and round down to the nearest integer or something.
* You could probably do some sort of split screen graphics mode using the two CRTC Read Circuits and two different frame buffers. dh - Display Area Height - 1, in Pixels.
GS Initialization
Once the CRTC is set up and knows where to get it's image data from and what format that data is in, we pretty much need to tell the GS the same information. Only this time, the info is used by the drawing routines to control where in display memory the output goes (to be subsequently picked up by the CTRC for drawing to the screen). You will notice that I've put the (one and only in this tutorial) frame buffer at the start of the GS video memory, and told both the CRTC (using DISPFB_2) and the GS (using FRAME_1) the same things about its location and pixel format. I've only set up the bare minimum of GS registers to be able to draw in 2D and keep this tutorial simple.
* If we like, we can then do nothing until the STR bit of the CHCR register becomes 0, meaning that the transfer is complete. You can find all this code in dma.h.
Drawing Routines
To save you the hassle of doing all the above by yourself, I've created a collection of simple 2D drawing routines (see g2.h and g2.c). If you have understood everything in this tutorial, and have had a look at the code, then the G2 library drawing routines will be totally trivial for you and there's no point in me explaining them (u can tell I really want to finish this thing :-) Here is are the prototypes of what's implemented so far (very Borland-like for those of you who remember):
int g2_init(g2_video_mode mode); void g2_end(void); uint16 g2_get_max_x(void); uint16 g2_get_max_y(void); void g2_set_color(uint8 r, uint8 g, uint8 b); void g2_set_fill_color(uint8 r, uint8 g, uint8 b); void g2_get_color(uint8 *r, uint8 *g, uint8 *b); void g2_get_fill_color(uint8 *r, uint8 *g, uint8 *b); void g2_put_pixel(uint16 x, uint16 y); void g2_line(uint16 x0, uint16 y0, uint16 x1, uint16 y1); void g2_rect(uint16 x0, uint16 y0, uint16 x1, uint16 y1); void g2_fill_rect(uint16 x0, uint16 y0, uint16 x1, uint16 y1); void g2_set_viewport(uint16 x0, uint16 y0, uint16 x1, uint16 y1); void g2_get_viewport(uint16 *x0, uint16 *y0, uint16 *x1, uint16 *y1);
Conclusion
We ve covered quite a bit of ground in this tutorial and I hope you ve been able to learn something new. I intentionally kept the 2D routines and DEMO for this tutorial simple so we could concentrate on understanding how the PS2 works. I promise this will be the
most boring tutorial of the set because there was so much background information and supportingcode to develop (that you probably already knew). In the next tutorial I will be extending the G2 library with the following features: * Routines for handling BMP and/or PCX files. * Display an image to the screen. * Define a simple Sprite format and write a blitfunction that can handle transparent regions. * Set up a double buffering system for animation. I ll also write a more interesting animated demo to tie it all together (maybe an animated 'line' if we re lucky :-) But I don't think I'll write so much text to describe it all next time. It takes too much time away from coding (I knew there was a reason I hated doing documentation!). I'd love to hear what you thought of this tutorial. Please send any comments, corrections and/or suggestions to t_saveski@yahoo.com. Cheers.