Example #1
0
//---------------------------------------------------------------------------
void g2_fill_rect(s16 x0, s16 y0, s16 x1, s16 y1)
{
	x0 += gs_origin_x;
	y0 += gs_origin_y;
	x1 += gs_origin_x;
	y1 += gs_origin_y;

	BEGIN_GS_PACKET(gs_dma_buf);
	GIF_TAG_AD(gs_dma_buf, 4, 1, 0, 0, 0);

	GIF_DATA_AD(gs_dma_buf, prim,
		GS_PRIM(PRIM_SPRITE, 0, 0, 0, 0, 0, 0, 0, 0));

	GIF_DATA_AD(gs_dma_buf, rgbaq,
		GS_RGBAQ(g2_fill_r, g2_fill_g, g2_fill_b, 0x80, 0));

	GIF_DATA_AD(gs_dma_buf, xyz2, GS_XYZ2(x0<<4, y0<<4, 0));

	// It looks like the default operation for the SPRITE primitive is to
	// not draw the right and bottom 'lines' of the rectangle refined by
	// the parameters. Add +1 to change this.
	GIF_DATA_AD(gs_dma_buf, xyz2, GS_XYZ2((x1+1)<<4, (y1+1)<<4, 0));

	SEND_GS_PACKET(gs_dma_buf);
}
Example #2
0
//---------------------------------------------------------------------------
void gs_load_texture(u16 x, u16 y, u16 w, u16 h, u32 data_adr, u32 dest_adr, u16 dest_w)
{
u32 i;			// DMA buffer loop counter
u32 frac;		// flag for whether to run a fractional buffer or not
u32 current;		// number of pixels to transfer in current DMA
u32 qtotal;		// total number of qwords of data to transfer

	BEGIN_GS_PACKET(gs_dma_buf);
	GIF_TAG_AD(gs_dma_buf, 4, 1, 0, 0, 0);
	GIF_DATA_AD(gs_dma_buf, bitbltbuf,
		GS_BITBLTBUF(0, 0, 0,
			dest_adr/256,		// frame buffer address
			dest_w/64,			// frame buffer width
			0));
	GIF_DATA_AD(gs_dma_buf, trxpos,
		GS_TRXPOS(
			0,
			0,
			x,
			y,
			0));	// left to right/top to bottom
	GIF_DATA_AD(gs_dma_buf, trxreg, GS_TRXREG(w, h));
	GIF_DATA_AD(gs_dma_buf, trxdir, GS_TRXDIR(XDIR_EE_GS));
	SEND_GS_PACKET(gs_dma_buf);

	qtotal = w*h/4;					// total number of quadwords to transfer.
	current = qtotal % MAX_TRANSFER;// work out if a partial buffer transfer is needed.
	frac=1;							// assume yes.
	if(!current)					// if there is no need for partial buffer
	{
		current = MAX_TRANSFER;		// start with a full buffer
		frac=0;						// and don't do extra partial buffer first
	}
	for(i=0; i<(qtotal/MAX_TRANSFER)+frac; i++)
	{
		BEGIN_GS_PACKET(gs_dma_buf);
		GIF_TAG_IMG(gs_dma_buf, current);
		SEND_GS_PACKET(gs_dma_buf);

		SET_QWC(GIF_QWC, current);
		SET_MADR(GIF_MADR, data_adr, 0);
		SET_CHCR(GIF_CHCR, 1, 0, 0, 0, 0, 1, 0);
		DMA_WAIT(GIF_CHCR);

		data_adr += current*16;
		current = MAX_TRANSFER;		// after the first one, all are full buffers
	}

	// Access the TEXFLUSH register with anything to flush the texture
	BEGIN_GS_PACKET(gs_dma_buf);
	GIF_TAG_AD(gs_dma_buf, 1, 1, 0, 0, 0);
	GIF_DATA_AD(gs_dma_buf, texflush, 0x42);
	SEND_GS_PACKET(gs_dma_buf);
}
Example #3
0
void
gsUpdateContext(void)
{
	GsState *gs = gsCurState;
	GIF_BEGIN_PACKET(gifDmaBuf);
	GIF_TAG(gifDmaBuf, 2, 1, 0, 0, 0, 1, 0x0e);
	GIF_DATA_AD(gifDmaBuf, GS_TEST_1,
	            MAKE_GS_TEST(0, 0, 0, 0, 0, 0, gs->ztest, gs->zfunc));
	GIF_DATA_AD(gifDmaBuf, GS_ALPHA_1,
	            MAKE_GS_ALPHA(gs->blend.a, gs->blend.b, gs->blend.c,
	                          gs->blend.d, gs->blend.fix));
	GIF_SEND_PACKET(gifDmaBuf);
}
Example #4
0
//---------------------------------------------------------------------------
void g2_line(s16 x0, s16 y0, s16 x1, s16 y1)
{
	x0 += gs_origin_x;
	y0 += gs_origin_y;
	x1 += gs_origin_x;
	y1 += gs_origin_y;

	BEGIN_GS_PACKET(gs_dma_buf);
	GIF_TAG_AD(gs_dma_buf, 4, 1, 0, 0, 0);

	GIF_DATA_AD(gs_dma_buf, prim,
		GS_PRIM(PRIM_LINE, 0, 0, 0, 0, 0, 0, 0, 0));

	GIF_DATA_AD(gs_dma_buf, rgbaq,
		GS_RGBAQ(g2_col_r, g2_col_g, g2_col_b, 0x80, 0));

	GIF_DATA_AD(gs_dma_buf, xyz2, GS_XYZ2(x0<<4, y0<<4, 0));
	GIF_DATA_AD(gs_dma_buf, xyz2, GS_XYZ2(x1<<4, y1<<4, 0));

	SEND_GS_PACKET(gs_dma_buf);
}
Example #5
0
void
gsClear(void)
{
	struct GsState *gs = gsCurState;
	uint16 x, y;
	int r = gs->clearcol & 0xFF;
	int g = (gs->clearcol >> 8) & 0xFF;
	int b = (gs->clearcol >> 16) & 0xFF;
	int a = (gs->clearcol >> 24) & 0xFF;

	x = (gs->width << 4) + gs->xoff;
	y = (gs->height << 4) + gs->yoff;

	GIF_BEGIN_PACKET(gifDmaBuf);
	GIF_TAG(gifDmaBuf, 6, 1, 0, 0, 0, 1, 0x0e);
	/* clear z-buffer too */
	GIF_DATA_AD(gifDmaBuf, GS_TEST_1,
	            MAKE_GS_TEST(0, 0, 0, 0, 0, 0, 1, ZTST_ALWAYS));
	GIF_DATA_AD(gifDmaBuf, GS_PRIM,
	            MAKE_GS_PRIM(PRIM_SPRITE, 0, 0, 0, 0, 0, 0, 0, 0));
	GIF_DATA_AD(gifDmaBuf, GS_RGBAQ,
	            MAKE_GS_RGBAQ(r, g, b, a, 0));
	GIF_DATA_AD(gifDmaBuf, GS_XYZ2, MAKE_GS_XYZ(0, 0, 0));
	GIF_DATA_AD(gifDmaBuf, GS_XYZ2, MAKE_GS_XYZ(x, y, 0));
	/* re-enable z-test */
	GIF_DATA_AD(gifDmaBuf, GS_TEST_1,
	            MAKE_GS_TEST(0, 0, 0, 0, 0, 0, gs->ztest, gs->zfunc));
	GIF_SEND_PACKET(gifDmaBuf);
}
Example #6
0
void
gsSelectActiveFb(int n)
{
	struct GsState *g = gsCurState;
	int addr;

	addr = g->fbAddr[n]/4/2048;
	GIF_BEGIN_PACKET(gifDmaBuf);
	GIF_TAG(gifDmaBuf, 1, 1, 0, 0, 0, 1, 0x0e);
	GIF_DATA_AD(gifDmaBuf, GS_FRAME_1,
	            MAKE_GS_FRAME(addr, g->width/64, g->psm, 0));
	GIF_SEND_PACKET(gifDmaBuf);
}
Example #7
0
//---------------------------------------------------------------------------
void g2_put_pixel(s16 x, s16 y)
{
	x += gs_origin_x;
	y += gs_origin_y;

	BEGIN_GS_PACKET(gs_dma_buf);
	GIF_TAG_AD(gs_dma_buf, 4, 1, 0, 0, 0);

	GIF_DATA_AD(gs_dma_buf, prim,
		GS_PRIM(PRIM_POINT, 0, 0, 0, 0, 0, 0, 0, 0));

	GIF_DATA_AD(gs_dma_buf, rgbaq,
		GS_RGBAQ(g2_col_r, g2_col_g, g2_col_b, 0x80, 0));

	// The XYZ coordinates are actually floating point numbers between
	// 0 and 4096 represented as unsigned integers where the lowest order
	// four bits are the fractional point. That's why all coordinates are
	// shifted left 4 bits.
	GIF_DATA_AD(gs_dma_buf, xyz2, GS_XYZ2(x<<4, y<<4, 0));

	SEND_GS_PACKET(gs_dma_buf);
}
Example #8
0
void
drawrect(uint16 x1, uint16 y1, uint16 x2, uint16 y2, uint32 col)
{
	struct GsState *gs = gsCurState;
	x1 = (x1<<4) + gs->xoff;
	y1 = (y1<<4) + gs->yoff;
	x2 = (x2<<4) + gs->xoff;
	y2 = (y2<<4) + gs->yoff;
	int r = col & 0xFF;
	int g = (col >> 8) & 0xFF;
	int b = (col >> 16) & 0xFF;
	int a = (col >> 24) & 0xFF;

	GIF_BEGIN_PACKET(gifDmaBuf);
	GIF_TAG(gifDmaBuf, 4, 1, 0, 0, 0, 1, 0x0e);
	GIF_DATA_AD(gifDmaBuf, GS_PRIM,
	            MAKE_GS_PRIM(PRIM_SPRITE, 0, 0, 0,
	                         gs->blend.enable, 0, 0, 0, 0));
	GIF_DATA_AD(gifDmaBuf, GS_RGBAQ,
	            MAKE_GS_RGBAQ(r, g, b, a, 0));
	GIF_DATA_AD(gifDmaBuf, GS_XYZ2, MAKE_GS_XYZ(x1, y1, 0));
	GIF_DATA_AD(gifDmaBuf, GS_XYZ2, MAKE_GS_XYZ(x2, y2, 0));
	GIF_SEND_PACKET(gifDmaBuf);
}
Example #9
0
//---------------------------------------------------------------------------
void g2_set_viewport(u16 x0, u16 y0, u16 x1, u16 y1)
{
	g2_view_x0 = x0;
	g2_view_x1 = x1;
	g2_view_y0 = y0;
	g2_view_y1 = y1;

	BEGIN_GS_PACKET(gs_dma_buf);

	GIF_TAG_AD(gs_dma_buf, 1, 1, 0, 0, 0);

	GIF_DATA_AD(gs_dma_buf, scissor_1,
		GS_SCISSOR(x0, x1, y0, y1));

	SEND_GS_PACKET(gs_dma_buf);
}
Example #10
0
//---------------------------------------------------------------------------
void g2_set_active_frame(u8 frame)
{
	BEGIN_GS_PACKET(gs_dma_buf);
	GIF_TAG_AD(gs_dma_buf, 1, 1, 0, 0, 0);

	GIF_DATA_AD(gs_dma_buf, frame_1,
		GS_FRAME(
			g2_frame_addr[frame]/8192,	// FrameBuffer base pointer = Address/8192
			cur_mode->width/64,			// Frame buffer width (Pixels/64)
			cur_mode->psm,				// Pixel Storage Format
			0));

	SEND_GS_PACKET(gs_dma_buf);

	g2_active_frame = frame;
}
Example #11
0
//---------------------------------------------------------------------------
void g2_put_image(s16 x, s16 y, s16 w, s16 h, u32 *data)
{
	x += gs_origin_x;
	y += gs_origin_y;

	// - Call this to copy the texture data from EE memory to GS memory.
	// - The g2_texbuf_addr variable holds the byte address of the
	//   'texture buffer' and is setup in g2_init() to be just after
	//   the frame buffer(s). When only the standard resolutions are
	//   used this buffer is guaranteed to be correctly aligned on 256
	//   bytes.
	gs_load_texture(0, 0, w, h, (u32)data, g2_texbuf_addr, g2_max_x+1);

	BEGIN_GS_PACKET(gs_dma_buf);
	GIF_TAG_AD(gs_dma_buf, 6, 1, 0, 0, 0);

	// Setup the Texture Buffer register. Note the width and height! They
	// must both be a power of 2.
	GIF_DATA_AD(gs_dma_buf, tex0_1,
		GS_TEX0(
			g2_texbuf_addr/256,	// base pointer
			(g2_max_x+1)/64,	// width
			0,					// 32bit RGBA
			gs_texture_wh(w),	// width
			gs_texture_wh(h),	// height
			1,					// RGBA
			TEX_DECAL,			// just overwrite existing pixels
			0,0,0,0,0));

	GIF_DATA_AD(gs_dma_buf, prim,
		GS_PRIM(PRIM_SPRITE,
			0,			// flat shading
			1, 			// texture mapping ON
			0, 0, 0,	// no fog, alpha, or antialiasing
			1, 			// use UV register for coordinates.
			0,
			0));

	// Texture and vertex coordinates are specified consistently, with
	// the last four bits being the decimal part (always X.0 here).

	// Top/Left, texture coordinates (0, 0).
	GIF_DATA_AD(gs_dma_buf, uv,    GS_UV(0, 0));
	GIF_DATA_AD(gs_dma_buf, xyz2,  GS_XYZ2(x<<4, y<<4, 0));

	// Bottom/Right, texture coordinates (w, h).
	GIF_DATA_AD(gs_dma_buf, uv,    GS_UV(w<<4, h<<4));
	GIF_DATA_AD(gs_dma_buf, xyz2,  GS_XYZ2((x+w)<<4, (y+h)<<4, 0));

	// Finally send the command buffer to the GIF.
	SEND_GS_PACKET(gs_dma_buf);
}
Example #12
0
void
gsInit(void)
{
	struct GsState *g = gsCurState;
	int addr0, addr1;

	g->visibleFb = 0;
	g->activeFb = 1;
	printf("%d %d\n", g->fbAddr[0], g->fbAddr[1]);
	addr0 = g->fbAddr[g->visibleFb]/4/2048;
	addr1 = g->fbAddr[g->activeFb]/4/2048;

	GS_RESET();
	__asm__("sync.p; nop");
	SET_REG64(GS_CSR, 0);
	GsPutIMR(0xff00);
	SetGsCrt(g->interlaced, g->mode, g->field);

	SET_REG64(GS_PMODE, MAKE_GS_PMODE(0, 1, 0, 1, 0, 0xFF));
	SET_REG64(GS_DISPFB2, MAKE_GS_DISPFB(addr0, g->width/64, g->psm, 0, 0));
	SET_REG64(GS_DISPFB1, MAKE_GS_DISPFB(addr0, g->width/64, g->psm, 0, 0));
	SET_REG64(GS_DISPLAY2, MAKE_GS_DISPLAY(g->startx, g->starty,
	                                       g->magh, g->magv,
	                                       g->dw-1, g->dh-1));
	SET_REG64(GS_DISPLAY1, MAKE_GS_DISPLAY(g->startx, g->starty,
	                                       g->magh, g->magv,
	                                       g->dw-1, g->dh-1));
	SET_REG64(GS_BGCOLOR, MAKE_GS_BGCOLOR(100, 100, 100));

	GIF_BEGIN_PACKET(gifDmaBuf);
	GIF_TAG(gifDmaBuf, 7, 1, 0, 0, 0, 1, 0x0e);
	GIF_DATA_AD(gifDmaBuf, GS_PRMODECONT, MAKE_GS_PRMODECONT(1));
	GIF_DATA_AD(gifDmaBuf, GS_FRAME_1,
	            MAKE_GS_FRAME(addr1, g->width/64, g->psm, 0));
	GIF_DATA_AD(gifDmaBuf, GS_ZBUF_1,
	            MAKE_GS_ZBUF(g->zbAddr/4/2048, g->zpsm, 0));
	GIF_DATA_AD(gifDmaBuf, GS_XYOFFSET_1,
	            MAKE_GS_XYOFFSET(g->xoff, g->yoff));
	GIF_DATA_AD(gifDmaBuf, GS_SCISSOR_1,
	            MAKE_GS_SCISSOR(0, g->xmax, 0, g->ymax));
	GIF_DATA_AD(gifDmaBuf, GS_TEST_1,
	            MAKE_GS_TEST(0, 0, 0, 0, 0, 0, g->ztest, g->zfunc));
	GIF_DATA_AD(gifDmaBuf, GS_COLCLAMP, MAKE_GS_COLCLAMP(1));
	GIF_SEND_PACKET(gifDmaBuf);
}
Example #13
0
//---------------------------------------------------------------------------
void g2_set_font_color(u8 red, u8 green, u8 blue)
{
u64 x0;
u64 y0;
u64 x1;
u64 y1;

	x0 = gs_origin_x-10;
	y0 = gs_origin_y-10;
	x1 = g2_fontbuf_w + gs_origin_x;
	y1 = g2_fontbuf_h + gs_origin_y;

	BEGIN_GS_PACKET(gs_dma_buf);
	GIF_TAG_AD(gs_dma_buf, 6, 1, 0, 0, 0);

	// Restore the frame buffer to the current active frame buffer.
	GIF_DATA_AD(gs_dma_buf, frame_1,
		GS_FRAME(
			g2_fontbuf_addr/8192,
			cur_mode->width/64,
			cur_mode->psm,
			0));

	// Draw the colored rectangle over the entire Font Bitmap.
	GIF_DATA_AD(gs_dma_buf, prim,
		GS_PRIM(PRIM_LINE, 0, 0, 0, 0, 0, 0, 0, 0));
//		GS_PRIM(PRIM_SPRITE, 0, 0, 0, 0, 0, 0, 0, 0));

	GIF_DATA_AD(gs_dma_buf, rgbaq,
		GS_RGBAQ(red, green, blue, 0x80, 0));

	GIF_DATA_AD(gs_dma_buf, xyz2, GS_XYZ2(x0<<4, y0<<4, 0));
	GIF_DATA_AD(gs_dma_buf, xyz2, GS_XYZ2(x1<<4, y1<<4, 0));

	// Restore the frame buffer to the current active frame buffer.
	GIF_DATA_AD(gs_dma_buf, frame_1,
		GS_FRAME(
			g2_frame_addr[g2_active_frame]/8192,
			cur_mode->width/64,
			cur_mode->psm,
			0));

	SEND_GS_PACKET(gs_dma_buf);
}
Example #14
0
//---------------------------------------------------------------------------
int g2_init(g2_video_mode mode)
{
vmode_t *v;

	v = &(vmodes[mode]);
	cur_mode = v;

	g2_max_x = v->width - 1;
	g2_max_y = v->height - 1;

	g2_view_x0 = 0;
	g2_view_y0 = 0;
	g2_view_x1 = g2_max_x;
	g2_view_y1 = g2_max_y;

	gs_origin_x = 1024;
	gs_origin_y = 1024;

	gs_mem_current = 0;		// nothing allocated yet

	g2_visible_frame = 0;	// display frame 0
	g2_active_frame  = 0;	// draw to frame 0

	// - Initialize the DMA.
	// - Writes a 0 to most of the DMA registers.
	dma_reset();

	// - Sets the RESET bit if the GS CSR register.
	GS_RESET();

#ifndef FLASH
	// - Can someone please tell me what the sync.p
	// instruction does. Synchronizes something :-)
	__asm__("
		sync.p
		nop
	");
#endif

	// - Sets up the GS IMR register (i guess).
	// - The IMR register is used to mask and unmask certain interrupts,
	//   for example VSync and HSync. We'll use this properly in Tutorial 2.
	// - Does anyone have code to do this without using the 0x71 syscall?
	// - I havn't gotten around to looking at any PS2 bios code yet.
	gs_set_imr();

	// - Use syscall 0x02 to setup some video mode stuff.
	// - Pretty self explanatory I think.
	// - Does anyone have code to do this without using the syscall? It looks
	//   like it should only set the SMODE2 register, but if I remove this syscall
	//   and set the SMODE2 register myself, it donesn't work. What else does
	//   syscall 0x02 do?
	gs_set_crtc(NON_INTERLACED, v->ntsc_pal, FRAME);

	// - I havn't attempted to understand what the Alpha parameters can do. They
	//   have been blindly copied from the 3stars demo (although they don't seem
	//   do have any impact in this simple 2D code.
	GS_SET_PMODE(
		0,		// ReadCircuit1 OFF
		1,		// ReadCircuit2 ON
		1,		// Use ALP register for Alpha Blending
		1,		// Alpha Value of ReadCircuit2 for output selection
		0,		// Blend Alpha with the output of ReadCircuit2
		0xFF	// Alpha Value = 1.0
	);
/*
	// - Non needed if we use gs_set_crt()
	GS_SET_SMODE2(
		0,		// Non-Interlaced mode
		1,		// FRAME mode (read every line)
		0		// VESA DPMS Mode = ON		??? please explain ???
	);
*/
	GS_SET_DISPFB2(
		0,				// Frame Buffer base pointer = 0 (Address/8192)
		v->width/64,	// Buffer Width (Pixels/64)
		v->psm,			// Pixel Storage Format
		0,				// Upper Left X in Buffer = 0
		0				// Upper Left Y in Buffer = 0
	);

	// Why doesn't (0, 0) equal the very top-left of the TV?
	GS_SET_DISPLAY2(
		656,		// X position in the display area (in VCK units)
		36,			// Y position in the display area (in Raster units)
		v->magh-1,	// Horizontal Magnification - 1
		0,						// Vertical Magnification = 1x
		v->width*v->magh-1,		// Display area width  - 1 (in VCK units) (Width*HMag-1)
		v->height-1				// Display area height - 1 (in pixels)	  (Height-1)
	);

	GS_SET_BGCOLOR(
		0,	// RED
		0,	// GREEN
		0	// BLUE
	);


	BEGIN_GS_PACKET(gs_dma_buf);
	GIF_TAG_AD(gs_dma_buf, 6, 1, 0, 0, 0);

	// Use drawing parameters from PRIM register
	GIF_DATA_AD(gs_dma_buf, prmodecont, 1);

	// Setup frame buffers. Point to 0 initially.
	GIF_DATA_AD(gs_dma_buf, frame_1,
		GS_FRAME(
			0,					// FrameBuffer base pointer = 0 (Address/8192)
			v->width/64,		// Frame buffer width (Pixels/64)
			v->psm,				// Pixel Storage Format
			0));

	// Save address and advance GS memory pointer by buffer size (in bytes)
	// Do this for both frame buffers.
	g2_frame_addr[0] = gs_mem_current;
	gs_mem_current += v->width * v->height * (v->bpp/8);

	g2_frame_addr[1] = gs_mem_current;
	gs_mem_current += v->width * v->height * (v->bpp/8);

	// Displacement between Primitive and Window coordinate systems.
	GIF_DATA_AD(gs_dma_buf, xyoffset_1,
		GS_XYOFFSET(
			gs_origin_x<<4,
			gs_origin_y<<4));

	// Clip to frame buffer.
	GIF_DATA_AD(gs_dma_buf, scissor_1,
		GS_SCISSOR(
			0,
			g2_max_x,
			0,
			g2_max_y));

	// Create a single 256x128 font buffer
	g2_fontbuf_addr = gs_mem_current;
	gs_mem_current += g2_fontbuf_w * g2_fontbuf_h * (v->bpp/8);

	// Create a texture buffer as big as the screen.
	// Just save the address advance GS memory pointer by buffer size (in bytes)
	// The TEX registers are set later, when drawing.
	g2_texbuf_addr = gs_mem_current;
	gs_mem_current += v->width * v->height * (v->bpp/8);

	// Setup test_1 register to allow transparent texture regions where A=0
	GIF_DATA_AD(gs_dma_buf, test_1,
		GS_TEST(
			1,						// Alpha Test ON
			ATST_NOTEQUAL, 0x00,	// Reject pixels with A=0
			AFAIL_KEEP,				// Don't update frame or Z buffers
			0, 0, 0, 0));			// No Destination Alpha or Z-Buffer Tests

	// Setup the ALPHA_1 register to correctly blend edges of
	// pre-antialiased fonts using Alpha Blending stage.
	// The blending formula is
	//   PIXEL=(SRC-FRAME)*SRC_ALPHA>>7+FRAME
	GIF_DATA_AD(gs_dma_buf, alpha_1,
		GS_ALPHA(
			0,			// A - source
			1, 			// B - frame buffer
			0,			// C - alpha from source
			1, 			// D - frame buffer
			0));		// FIX - not needed

	SEND_GS_PACKET(gs_dma_buf);

	return(1);
}
Example #15
0
//---------------------------------------------------------------------------
void g2_out_text(s16 x, s16 y, char *str)
{
char	c;				// current character
u16	*tc;			// current texture coordinates [4]
u16	x0, y0, x1, y1;	// rectangle for current character
u16	w, h;			// width and height of above rectangle

	x += gs_origin_x;
	y += gs_origin_y;

	c = *str;
	while(c)
	{
		// Read the texture coordinates for current character
		tc = &g2_font_tc[c*4];
		x0 = *tc++;
		y0 = *tc++;
		x1 = *tc++;
		y1 = *tc++;
		w  = x1-x0+1;
		h  = y1-y0+1;

		// Draw a sprite with current character mapped onto it
		BEGIN_GS_PACKET(gs_dma_buf);
		GIF_TAG_AD(gs_dma_buf, 6, 1, 0, 0, 0);

		GIF_DATA_AD(gs_dma_buf, tex0_1,
			GS_TEX0(
				g2_fontbuf_addr/256,		// base pointer
				(g2_fontbuf_w)/64,			// width
				0,							// 32bit RGBA
				gs_texture_wh(g2_fontbuf_w),// width
				gs_texture_wh(g2_fontbuf_w),// height
				1,							// RGBA
				TEX_DECAL,					// just overwrite existing pixels
				0,0,0,0,0));
/*
		GIF_DATA_AD(gs_dma_buf, tex1_1,
			GS_TEX1(
				0, 0,
				FILTER_LINEAR,
				FILTER_LINEAR,
				0, 0, 0));

		GIF_DATA_AD(gs_dma_buf, clamp_1, 0x05);
*/
		GIF_DATA_AD(gs_dma_buf, prim,
			GS_PRIM(PRIM_SPRITE,
				0,			// flat shading
				1, 			// texture mapping ON
				0, 1, 0,	// no fog or antialiasing, but use alpha
				1, 			// use UV register for coordinates.
				0,
				0));

		GIF_DATA_AD(gs_dma_buf, uv,    GS_UV(x0<<4, y0<<4));
		GIF_DATA_AD(gs_dma_buf, xyz2,  GS_XYZ2(x<<4, y<<4, 0));
		GIF_DATA_AD(gs_dma_buf, uv,    GS_UV((x1+1)<<4, (y1+1)<<4));
		GIF_DATA_AD(gs_dma_buf, xyz2,  GS_XYZ2(
				(x+w*g2_font_mag)<<4, (y+h*g2_font_mag)<<4, 0));

		SEND_GS_PACKET(gs_dma_buf);

		// Advance drawing position
		x += (w + g2_font_spacing) * g2_font_mag;

		// Get next character
		str++;
		c = *str;
	}
}