3 Getting Started

In this section we introduce a simple program that outputs a square shape to the screen.

The source is located in the nu0 directory below the nusys/sample.

Please copy this source to your working directory, then make (compile), load, and execute with the development system.

(To learn about using development tools, please refer to the N64 Introductory Manual, Step 2, Sections 5-7.)

Caution: If you do not have the latest N64 OS PATCH distributed by Nintendo then "make" can generate errors and may not run as expected.


3.1 Main Routine

The source for the main routine (main.c) is very simple, as shown below:

void mainproc(void)
{
  /*  initialize graphics  */
  nuGfxInit();

  /* register callback function  */
  nuGfxFuncSet((NUGfxFunc)stage00);
  /*  turn display on*/
  nuGfxDisplayOn();

  while(1)
    ;
}

void stage00(int pendingGfx)
{
  /* peform display process if no RCP task is being processed */
  if(pendingGfx < 1)
    makeDL00();
}

Functions and types are specified in NuSystem using names starting with nu or NU.

The NuSystem program starts from the mainproc function.

First nuGfxInit() is called to initialize the graphics.

Then nuGfxFuncSet() registers the callback function.

Based on this entry, the function specified by the argument (in this case stage00) will be executed every time there is a retrace (vertical synchronization).

Next nuGfxDisplayOn() turns the display on.

In this sample, the callback function is not re-registered so it enters an infinite loop.

"stage00()," where the callback function is registered, receives "the number of graphic tasks processing or waiting for processing" as an argument from NuSystem.

After it has been confirmed from this value that there are no tasks being processed, the makeDL00() function is called to perform the display process.


3.2 Graphics Routine

The source code for makeDL00() is located in the first half of stage00.c.

This routine creates a display list and activates a graphics task.

void makeDL00(void)
{
  /*  specify display list buffer */
  glistp = gfx_glist;

  /*  initialize RCP*/
  gfxRCPInit();

  /*  clear frame buffer and Z buffer */
  gfxClearCfb();

  /* set projection/modeling matrix */
  guOrtho(&gfx_dynamic.projection,
	  -(float)SCREEN_WD/2.0F, (float)SCREEN_WD/2.0F,
	  -(float)SCREEN_HT/2.0F, (float)SCREEN_HT/2.0F,
	  1.0F, 10.0F, 1.0F);
  guRotate(&gfx_dynamic.modeling, 0.0F, 0.0F, 0.0F, 1.0F);

  /* draw square */
  shadetri(&gfx_dynamic);

  /* end construction of display list */
  gDPFullSync(glistp++);
  gSPEndDisplayList(glistp++);

  /* check if it fits in the array */
  assert(glistp - gfx_glist < GFX_GLIST_LEN);

  /* activate RSP task, and swap display buffer at task end */
  nuGfxTaskStart(gfx_glist,
		 (s32)(glistp - gfx_glist) * sizeof (Gfx),
		 NU_GFX_UCODE_F3DEX , NU_SC_SWAPBUFFER);
}

"makeDL00" begins with specification of the display list buffer.

"glistp," which is the pointer to the display list, then indicates the location of the display list buffer for the next GBI command write.

(This variable is used as a global variable so it can be referenced from outside of this source file.)

"gxfRCPInit()" and gfxClearCfb() are functions defined by graphic.c that initialize the RCP and create the display list to clear and set the frame buffer. They are not NuSystem functions, but they are regularly used in the sample software employed here.

Next the projection matrix and modeling matrix are set in gfx_dynamic, which is the structure variable containing the various matrix data. Once that is done, the function is called to make the display list for drawing the square.

This is the end of the graphic display, so a termination of the display list is indicated, a GBI command is written, and creation of the display list is ended.

"assert" is a typical C language check function.

If the conditions of assert are not met, a message is displayed on the host computer. It checks to see whether a GBI command has been written that is outside the bounds of the array.

In the final step, the NuSystem function nuGfxTaskStart() is called and the RSP task activated.

Arguments specify the header address of the DL (display list) array and its byte length, the names of the usable microcode, and a flag regarding whether or not to swap the frame buffer at the end of the drawing process.

Note: The nuGfxTaskStart function does not activate the task directly. Rather, it simply passes the task structure to a NuSystem thread. That thread activates graphic tasks in the order in which the requests were received. Since a task will not start until the previous task has been completed, a major process could end up with unprocessed tasks.

When that happens, an argument in the callback function passes the number of unprocessed tasks.

"shadetri()" in the latter half of stage00.c contains no code that is dependent on NuSystem.

/* vertex coordinates  */
static Vtx shade_vtx[] =  {
        {        -64,  64, -5, 0, 0, 0, 0, 0xff, 0, 0xff	},
        {         64,  64, -5, 0, 0, 0, 0, 0, 0, 0xff	},
        {         64, -64, -5, 0, 0, 0, 0, 0, 0xff, 0xff	},
        {        -64, -64, -5, 0, 0, 0, 0xff, 0, 0, 0xff	},
};

/* draw square */
void shadetri(Dynamic* dynamicp)
{
  gSPMatrix(glistp++,OS_K0_TO_PHYSICAL(&(dynamicp->projection)),
		G_MTX_PROJECTION|G_MTX_LOAD|G_MTX_NOPUSH);
  gSPMatrix(glistp++,OS_K0_TO_PHYSICAL(&(dynamicp->modeling)),
		G_MTX_MODELVIEW|G_MTX_LOAD|G_MTX_NOPUSH);

  gSPVertex(glistp++,&(shade_vtx[0]),4, 0);

  gDPPipeSync(glistp++);
  gDPSetCycleType(glistp++,G_CYC_1CYCLE);
  gDPSetRenderMode(glistp++,G_RM_AA_OPA_SURF, G_RM_AA_OPA_SURF2);
  gSPClearGeometryMode(glistp++,0xFFFFFFFF);
  gSPSetGeometryMode(glistp++,G_SHADE| G_SHADING_SMOOTH);

  gSP2Triangles(glistp++,0,1,2,0,0,2,3,0);
}

"shade_vtx[]" is the vertex data, while the shadetri argument "dynamicp" is the pointer to the various matrix data.

The functions in shadetri() are all N64OS graphics functions. This manual does not contain explanations of each function, so for details please refer to the NuSystem Function Reference.


3.3 Memory Map

In Nintendo 64 the locations of the frame buffer, Z buffer, and audio heap buffer are variable. In NuSystem, the following default memory locations are set by the functions which initialize the graphics and audio:

Z buffer 80000400-80025BFF
Audio heap 8030F800-8038F7FF (512 Kbyte)
Frame buffer 8038F800-803FFFFF(0G_IM_SIZ_16b mode: 3 buffers)

For this reason the 0x2E9C00 bytes from 80025C00-8030F7FF is the region which the user can use.

You can reference the frame buffer address for drawing and the Z buffer address using the NuSystem global variables nuGfxCfb_ptr and nuGfxZBuffer.

(The nuGfxCfb_ptr address is swapped every time a frame is drawn, but the nuGfxZBuffer value does not change after the initial setting.)

In the samples in this tutorial, the graphic.c function gfxClearCfb() is used to clear the RCP settings of the frame buffer address and the Z buffer address.

gThe Z buffer address is specified with the gDPSetDepthImage() function and the frame buffer address is specified with the gDPSetColorImage() function.

For details of each function, please refer to the function reference manual.

void gfxClearCfb(void)
{
  /* clear Z buffer  */
  gDPSetDepthImage(glistp++, OS_K0_TO_PHYSICAL(nuGfxZBuffer));
  gDPSetCycleType(glistp++, G_CYC_FILL);
  gDPSetColorImage(glistp++, G_IM_FMT_RGBA, G_IM_SIZ_16b,SCREEN_WD,
		   OS_K0_TO_PHYSICAL(nuGfxZBuffer));
  gDPSetFillColor(glistp++,(GPACK_ZDZ(G_MAXFBZ,0) << 16 |
			       GPACK_ZDZ(G_MAXFBZ,0)));
  gDPFillRectangle(glistp++, 0, 0, SCREEN_WD-1, SCREEN_HT-1);
  gDPPipeSync(glistp++);
  
    /*  clear frame buffer */
  gDPSetColorImage(glistp++, G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_WD,
		   osVirtualToPhysical(nuGfxCfb_ptr));
  gDPSetFillColor(glistp++, (GPACK_RGBA5551(0, 0, 0, 1) << 16 | 
				GPACK_RGBA5551(0, 0, 0, 1)));
  gDPFillRectangle(glistp++, 0, 0, SCREEN_WD-1, SCREEN_HT-1);
  gDPPipeSync(glistp++);
}


3.4 The spec File

The spec file given below is the script file used in these samples to specify the ROM image.

#include <nusys.h>

beginseg
	name	"code"
	flags	BOOT OBJECT
	entry 	nuBoot
	address NU_SPEC_BOOT_ADDR
	stack   NU_SPEC_BOOT_STACK
	include "codesegment.o"
	include "$(ROOT)/usr/lib/PR/rspboot.o"
	include "$(ROOT)/usr/lib/PR/gspF3DEX.fifo.o"
	include "$(ROOT)/usr/lib/PR/gspL3DEX.fifo.o"
	include "$(ROOT)/usr/lib/PR/gspF3DEX.NoN.fifo.o"
	include "$(ROOT)/usr/lib/PR/gspF3DLX.fifo.o"
	include "$(ROOT)/usr/lib/PR/gspF3DLX.NoN.fifo.o"
	include "$(ROOT)/usr/lib/PR/gspF3DLX.Rej.fifo.o"
	include "$(ROOT)/usr/lib/PR/gspF3DLP.Rej.fifo.o"
	include "$(ROOT)/usr/lib/PR/gspS2DEX.fifo.o"
	include "$(ROOT)/usr/lib/PR/gspS2DEX.fifo_d.o"
endseg

beginwave
	name	"nu0"
	include	"code"
endwave

The default setting specifies the loading of all of the graphics microcode. This is a bit wasteful, but at first please use the code segment setting the way it is.

Refer to INSIDE NuSystem to learn the procedure for linking to just the microcode that will be used.

Note: This spec file is for use with F3DEX microcode. If you want to use F3DEX2, you must edit the makefile to use spec2, which is under each sample directory.


3.5 makefile

For makefile the user must specify the nusys include file and library file passes.

The header is named nusys.h, and the libraries are named libnusys.a (for ROM use) and libnusys_d.a (for debugging).

Below we list the NuSystem related portion of the makefile:

(makefile for SGI)

N64KITDIR    = /usr/local/n64kit
NUSYSINCDIR  = $(N64KITDIR)/nusys/include
NUSYSLIBDIR  = $(N64KITDIR)/nusys/lib

CODEOBJECTS =	$(CODEFILES:.c=.o)  $(NUSYSLIBDIR)/nusys.o

LCDEFS =	-DF3DEX_GBI_2

LCINCS =	-I $(NUSYSINCDIR)

LDFLAGS =	$(MKDEPOPT) -nostdlib -L$(ROOT)/usr/lib -L$(NUSYSLIBDIR) -lnusys_d -lultra_d

(makefile for PC)

N64KITDIR    = c:\nintendo\n64kit
NUSYSINCDIR  = $(N64KITDIR)/nusys/include
NUSYSLIBDIR  = $(N64KITDIR)/nusys/lib

CODEOBJECTS =	$(CODEFILES:.c=.o)  $(NUSYSLIBDIR)/nusys.o

LCDEFS =	-DF3DEX_GBI_2

LCINCS =	-I $(NUSYSINCDIR)

LDFLAGS =	$(MKDEPOPT) -L$(ROOT)/usr/lib  -L$(NUSYSLIBDIR)  -lnusys_d -lgultra_d  -L$(GCCDIR)/mipse/lib -lkmc

The NuSystem boot object nusys.o must be linked to the code object. "nusys.o" is also in the library pass.