nkeynes@189: /** nkeynes@561: * $Id$ nkeynes@189: * nkeynes@189: * PVR2 Tile Accelerator implementation nkeynes@189: * nkeynes@189: * Copyright (c) 2005 Nathan Keynes. nkeynes@189: * nkeynes@189: * This program is free software; you can redistribute it and/or modify nkeynes@189: * it under the terms of the GNU General Public License as published by nkeynes@189: * the Free Software Foundation; either version 2 of the License, or nkeynes@189: * (at your option) any later version. nkeynes@189: * nkeynes@189: * This program is distributed in the hope that it will be useful, nkeynes@189: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@189: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@189: * GNU General Public License for more details. nkeynes@189: */ nkeynes@645: #include nkeynes@669: #include "lxdream.h" nkeynes@677: #include "pvr2/pvr2.h" nkeynes@677: #include "pvr2/pvr2mmio.h" nkeynes@189: #include "asic.h" nkeynes@669: #include "dream.h" nkeynes@189: nkeynes@189: #define STATE_IDLE 0 nkeynes@189: #define STATE_IN_LIST 1 nkeynes@194: #define STATE_IN_POLYGON 2 nkeynes@194: #define STATE_EXPECT_POLY_BLOCK2 3 nkeynes@194: #define STATE_EXPECT_VERTEX_BLOCK2 4 nkeynes@194: #define STATE_ERROR 5 nkeynes@189: #define STATE_EXPECT_END_VERTEX_BLOCK2 7 nkeynes@189: nkeynes@189: #define TA_CMD(i) ( (i) >> 29 ) nkeynes@189: #define TA_CMD_END_LIST 0 nkeynes@189: #define TA_CMD_CLIP 1 nkeynes@189: #define TA_CMD_POLYGON_CONTEXT 4 nkeynes@189: #define TA_CMD_SPRITE_CONTEXT 5 nkeynes@189: #define TA_CMD_VERTEX 7 nkeynes@189: nkeynes@189: #define TA_LIST_NONE -1 nkeynes@189: #define TA_LIST_OPAQUE 0 nkeynes@189: #define TA_LIST_OPAQUE_MOD 1 nkeynes@189: #define TA_LIST_TRANS 2 nkeynes@189: #define TA_LIST_TRANS_MOD 3 nkeynes@189: #define TA_LIST_PUNCH_OUT 4 nkeynes@189: #define TA_IS_MODIFIER_LIST(list) (list == TA_LIST_OPAQUE_MOD || list == TA_LIST_TRANS_MOD) nkeynes@189: nkeynes@189: #define TA_GROW_UP 0 nkeynes@189: #define TA_GROW_DOWN 1 nkeynes@189: nkeynes@189: #define TA_VERTEX_NONE -1 nkeynes@189: #define TA_VERTEX_PACKED 0x00 nkeynes@189: #define TA_VERTEX_TEX_PACKED 0x08 nkeynes@189: #define TA_VERTEX_TEX_SPEC_PACKED 0x0C nkeynes@189: #define TA_VERTEX_TEX_UV16_PACKED 0x09 nkeynes@189: #define TA_VERTEX_TEX_UV16_SPEC_PACKED 0x0D nkeynes@189: #define TA_VERTEX_FLOAT 0x10 nkeynes@189: #define TA_VERTEX_TEX_FLOAT 0x18 nkeynes@189: #define TA_VERTEX_TEX_SPEC_FLOAT 0x1C nkeynes@189: #define TA_VERTEX_TEX_UV16_FLOAT 0x19 nkeynes@189: #define TA_VERTEX_TEX_UV16_SPEC_FLOAT 0x1D nkeynes@189: #define TA_VERTEX_INTENSITY 0x20 nkeynes@189: #define TA_VERTEX_TEX_INTENSITY 0x28 nkeynes@189: #define TA_VERTEX_TEX_SPEC_INTENSITY 0x2C nkeynes@189: #define TA_VERTEX_TEX_UV16_INTENSITY 0x29 nkeynes@189: #define TA_VERTEX_TEX_UV16_SPEC_INTENSITY 0x2D nkeynes@189: #define TA_VERTEX_PACKED_MOD 0x40 nkeynes@189: #define TA_VERTEX_TEX_PACKED_MOD 0x48 nkeynes@189: #define TA_VERTEX_TEX_SPEC_PACKED_MOD 0x4C nkeynes@189: #define TA_VERTEX_TEX_UV16_PACKED_MOD 0x49 nkeynes@189: #define TA_VERTEX_TEX_UV16_SPEC_PACKED_MOD 0x4D nkeynes@189: #define TA_VERTEX_INTENSITY_MOD 0x60 nkeynes@189: #define TA_VERTEX_TEX_INTENSITY_MOD 0x68 nkeynes@189: #define TA_VERTEX_TEX_SPEC_INTENSITY_MOD 0x6C nkeynes@189: #define TA_VERTEX_TEX_UV16_INTENSITY_MOD 0x69 nkeynes@189: #define TA_VERTEX_TEX_UV16_SPEC_INTENSITY_MOD 0x6D nkeynes@189: #define TA_VERTEX_SPRITE 0x80 nkeynes@189: #define TA_VERTEX_TEX_SPRITE 0x88 nkeynes@189: #define TA_VERTEX_MOD_VOLUME 0x81 nkeynes@206: #define TA_VERTEX_LISTLESS 0xFF nkeynes@189: nkeynes@189: #define TA_IS_NORMAL_POLY() (ta_status.current_vertex_type < TA_VERTEX_SPRITE) nkeynes@189: nkeynes@189: static int strip_lengths[4] = {3,4,6,8}; /* in vertexes */ nkeynes@189: #define TA_POLYCMD_LISTTYPE(i) ( ((i) >> 24) & 0x0F ) nkeynes@194: #define TA_POLYCMD_USELENGTH(i) ( i & 0x00800000 ) nkeynes@189: #define TA_POLYCMD_LENGTH(i) strip_lengths[((i >> 18) & 0x03)] nkeynes@189: #define TA_POLYCMD_CLIP(i) ((i>>16)&0x03) nkeynes@203: #define TA_POLYCMD_CLIP_NONE 0 nkeynes@203: #define TA_POLYCMD_CLIP_INSIDE 2 nkeynes@203: #define TA_POLYCMD_CLIP_OUTSIDE 3 nkeynes@189: #define TA_POLYCMD_COLOURFMT(i) (i & 0x00000030) nkeynes@189: #define TA_POLYCMD_COLOURFMT_ARGB32 0x00000000 nkeynes@189: #define TA_POLYCMD_COLOURFMT_FLOAT 0x00000010 nkeynes@189: #define TA_POLYCMD_COLOURFMT_INTENSITY 0x00000020 nkeynes@189: #define TA_POLYCMD_COLOURFMT_LASTINT 0x00000030 nkeynes@189: nkeynes@189: #define TA_POLYCMD_MODIFIED 0x00000080 nkeynes@189: #define TA_POLYCMD_FULLMOD 0x00000040 nkeynes@189: #define TA_POLYCMD_TEXTURED 0x00000008 nkeynes@189: #define TA_POLYCMD_SPECULAR 0x00000004 nkeynes@189: #define TA_POLYCMD_SHADED 0x00000002 nkeynes@189: #define TA_POLYCMD_UV16 0x00000001 nkeynes@189: nkeynes@189: #define TA_POLYCMD_IS_SPECULAR(i) ((i & 0x0000000C)==0x0000000C) /* Only applies to textured polys */ nkeynes@189: #define TA_POLYCMD_IS_FULLMOD(i) ((i & 0x000000C0)==0x000000C0) nkeynes@189: nkeynes@189: nkeynes@189: #define TA_IS_END_VERTEX(i) (i & 0x10000000) nkeynes@189: nkeynes@199: /** Note these are not the IEEE 754 definitions - the TA treats NANs nkeynes@199: * as if they were INFs of the appropriate sign. nkeynes@199: */ nkeynes@199: #define TA_IS_INF(f) (((*((uint32_t *)&f)) & 0xFF800000) == 0x7F800000) nkeynes@199: #define TA_IS_NINF(f) (((*((uint32_t *)&f)) & 0xFF800000) == 0xFF800000) nkeynes@199: nkeynes@189: #define MIN3( x1, x2, x3 ) ( (x1)<(x2)? ((x1)<(x3)?(x1):(x3)) : ((x2)<(x3)?(x2):(x3)) ) nkeynes@189: #define MAX3( x1, x2, x3 ) ( (x1)>(x2)? ((x1)>(x3)?(x1):(x3)) : ((x2)>(x3)?(x2):(x3)) ) nkeynes@189: nkeynes@189: #define TILESLOT( x, y ) (ta_status.current_tile_matrix + (ta_status.current_tile_size * (y * ta_status.width+ x) << 2)) nkeynes@189: nkeynes@934: #define PVRRAM(addr) (*(uint32_t *)(pvr2_main_ram + ((addr)&PVR2_RAM_MASK))) nkeynes@189: nkeynes@189: struct pvr2_ta_vertex { nkeynes@189: float x,y,z; nkeynes@189: uint32_t detail[8]; /* 0-8 detail words */ nkeynes@189: }; nkeynes@189: nkeynes@189: struct tile_bounds { nkeynes@674: int32_t x1, y1, x2, y2; nkeynes@189: }; nkeynes@189: nkeynes@189: struct pvr2_ta_status { nkeynes@674: int32_t state; nkeynes@674: int32_t width, height; /* Tile resolution, ie 20x15 */ nkeynes@674: int32_t tilelist_dir; /* Growth direction of the tilelist, 0 = up, 1 = down */ nkeynes@189: uint32_t tilelist_size; /* Size of the tilelist segments */ nkeynes@193: uint32_t tilelist_start; /* Initial address of the tilelist */ nkeynes@674: uint32_t polybuf_start; /* Initial bank address of the polygon buffer (ie &0x00F00000) */ nkeynes@674: int32_t current_vertex_type; nkeynes@674: uint32_t accept_vertexes; /* 0 = NO, 1 = YES */ nkeynes@674: int32_t vertex_count; /* index of last start-vertex seen, or -1 if no vertexes nkeynes@736: * are present nkeynes@736: */ nkeynes@674: uint32_t max_vertex; /* Maximum number of vertexes in the current polygon (3/4/6/8) */ nkeynes@674: uint32_t current_list_type; nkeynes@189: uint32_t current_tile_matrix; /* Memory location of the first tile for the current list. */ nkeynes@189: uint32_t current_tile_size; /* Size of the tile matrix space in 32-bit words (0/8/16/32)*/ nkeynes@189: uint32_t intensity1, intensity2; nkeynes@203: struct tile_bounds clip; nkeynes@674: int32_t clip_mode; nkeynes@189: /** nkeynes@189: * Current working object nkeynes@189: */ nkeynes@674: int32_t poly_context_size; nkeynes@674: int32_t poly_vertex_size; nkeynes@674: int32_t poly_parity; nkeynes@189: uint32_t poly_context[5]; nkeynes@189: uint32_t poly_pointer; nkeynes@189: struct tile_bounds last_triangle_bounds; nkeynes@189: struct pvr2_ta_vertex poly_vertex[8]; nkeynes@674: uint32_t debug_output; nkeynes@189: }; nkeynes@189: nkeynes@189: static struct pvr2_ta_status ta_status; nkeynes@189: nkeynes@189: static int tilematrix_sizes[4] = {0,8,16,32}; nkeynes@189: nkeynes@189: /** nkeynes@189: * Convenience union - ta data is either 32-bit integer or 32-bit float. nkeynes@189: */ nkeynes@189: union ta_data { nkeynes@189: unsigned int i; nkeynes@189: float f; nkeynes@189: }; nkeynes@189: nkeynes@189: nkeynes@189: void pvr2_ta_reset() { nkeynes@189: ta_status.state = STATE_ERROR; /* State not valid until initialized */ nkeynes@189: ta_status.debug_output = 0; nkeynes@189: } nkeynes@189: nkeynes@193: void pvr2_ta_save_state( FILE *f ) nkeynes@193: { nkeynes@193: fwrite( &ta_status, sizeof(ta_status), 1, f ); nkeynes@193: } nkeynes@193: nkeynes@193: int pvr2_ta_load_state( FILE *f ) nkeynes@193: { nkeynes@193: if( fread( &ta_status, sizeof(ta_status), 1, f ) != 1 ) nkeynes@736: return 1; nkeynes@193: return 0; nkeynes@193: } nkeynes@193: nkeynes@189: void pvr2_ta_init() { nkeynes@189: ta_status.state = STATE_IDLE; nkeynes@189: ta_status.current_list_type = -1; nkeynes@189: ta_status.current_vertex_type = -1; nkeynes@189: ta_status.poly_parity = 0; nkeynes@189: ta_status.vertex_count = 0; nkeynes@189: ta_status.max_vertex = 3; nkeynes@206: ta_status.current_vertex_type = TA_VERTEX_LISTLESS; nkeynes@206: ta_status.poly_vertex_size = 0; nkeynes@206: memset(&ta_status.poly_context[1], 0, 4); nkeynes@189: ta_status.last_triangle_bounds.x1 = -1; nkeynes@193: ta_status.accept_vertexes = TRUE; nkeynes@198: ta_status.clip.x1 = 0; nkeynes@198: ta_status.clip.y1 = 0; nkeynes@203: ta_status.clip_mode = TA_POLYCMD_CLIP_NONE; nkeynes@198: nkeynes@189: uint32_t size = MMIO_READ( PVR2, TA_TILESIZE ); nkeynes@189: ta_status.width = (size & 0xFFFF) + 1; nkeynes@189: ta_status.height = (size >> 16) + 1; nkeynes@198: ta_status.clip.x2 = ta_status.width-1; nkeynes@198: ta_status.clip.y2 = ta_status.height-1; nkeynes@189: uint32_t control = MMIO_READ( PVR2, TA_TILECFG ); nkeynes@189: ta_status.tilelist_dir = (control >> 20) & 0x01; nkeynes@189: ta_status.tilelist_size = tilematrix_sizes[ (control & 0x03) ]; nkeynes@189: MMIO_WRITE( PVR2, TA_POLYPOS, MMIO_READ( PVR2, TA_POLYBASE ) ); nkeynes@189: uint32_t plistpos = MMIO_READ( PVR2, TA_LISTBASE ) >> 2; nkeynes@189: if( ta_status.tilelist_dir == TA_GROW_DOWN ) { nkeynes@736: plistpos -= ta_status.tilelist_size; nkeynes@189: } nkeynes@189: MMIO_WRITE( PVR2, TA_LISTPOS, plistpos ); nkeynes@193: ta_status.tilelist_start = plistpos; nkeynes@215: ta_status.polybuf_start = MMIO_READ( PVR2, TA_POLYBASE ) & 0x00F00000; nkeynes@189: } nkeynes@189: nkeynes@189: static uint32_t parse_float_colour( float a, float r, float g, float b ) { nkeynes@200: int ai,ri,gi,bi; nkeynes@200: nkeynes@200: if( TA_IS_INF(a) ) { nkeynes@736: ai = 255; nkeynes@200: } else { nkeynes@736: ai = 256 * CLAMP(a,0.0,1.0) - 1; nkeynes@736: if( ai < 0 ) ai = 0; nkeynes@200: } nkeynes@200: if( TA_IS_INF(r) ) { nkeynes@736: ri = 255; nkeynes@200: } else { nkeynes@736: ri = 256 * CLAMP(r,0.0,1.0) - 1; nkeynes@736: if( ri < 0 ) ri = 0; nkeynes@200: } nkeynes@200: if( TA_IS_INF(g) ) { nkeynes@736: gi = 255; nkeynes@200: } else { nkeynes@736: gi = 256 * CLAMP(g,0.0,1.0) - 1; nkeynes@736: if( gi < 0 ) gi = 0; nkeynes@200: } nkeynes@200: if( TA_IS_INF(b) ) { nkeynes@736: bi = 255; nkeynes@200: } else { nkeynes@736: bi = 256 * CLAMP(b,0.0,1.0) - 1; nkeynes@736: if( bi < 0 ) bi = 0; nkeynes@200: } nkeynes@200: return (ai << 24) | (ri << 16) | (gi << 8) | bi; nkeynes@189: } nkeynes@189: nkeynes@189: static uint32_t parse_intensity_colour( uint32_t base, float intensity ) nkeynes@189: { nkeynes@189: unsigned int i = (unsigned int)(256 * CLAMP(intensity, 0.0,1.0)); nkeynes@736: nkeynes@189: return nkeynes@736: (((((base & 0xFF) * i) & 0xFF00) | nkeynes@736: (((base & 0xFF00) * i) & 0xFF0000) | nkeynes@736: (((base & 0xFF0000) * i) & 0xFF000000)) >> 8) | nkeynes@736: (base & 0xFF000000); nkeynes@189: } nkeynes@736: nkeynes@189: /** nkeynes@189: * Initialize the specified TA list. nkeynes@189: */ nkeynes@189: static void ta_init_list( unsigned int listtype ) { nkeynes@189: int config = MMIO_READ( PVR2, TA_TILECFG ); nkeynes@189: int tile_matrix = MMIO_READ( PVR2, TA_TILEBASE ); nkeynes@193: int list_end = MMIO_READ( PVR2, TA_LISTEND ); nkeynes@193: nkeynes@193: ta_status.current_tile_matrix = tile_matrix; nkeynes@193: nkeynes@193: /* If the list grows down, the end must be < tile matrix start. nkeynes@193: * If it grows up, the end must be > tile matrix start. nkeynes@193: * Don't ask me why, it just does... nkeynes@193: */ nkeynes@193: if( ((ta_status.tilelist_dir == TA_GROW_DOWN && list_end <= tile_matrix) || nkeynes@736: (ta_status.tilelist_dir == TA_GROW_UP && list_end >= tile_matrix )) && nkeynes@736: listtype <= TA_LIST_PUNCH_OUT ) { nkeynes@736: int i; nkeynes@736: uint32_t *p; nkeynes@736: for( i=0; i < listtype; i++ ) { nkeynes@736: int size = tilematrix_sizes[(config & 0x03)] << 2; nkeynes@736: ta_status.current_tile_matrix += ta_status.width * ta_status.height * size; nkeynes@736: config >>= 4; nkeynes@736: } nkeynes@736: ta_status.current_tile_size = tilematrix_sizes[(config & 0x03)]; nkeynes@189: nkeynes@736: /* Initialize each tile to 0xF0000000 */ nkeynes@736: if( ta_status.current_tile_size != 0 ) { nkeynes@934: p = (uint32_t *)(pvr2_main_ram + ta_status.current_tile_matrix); nkeynes@736: for( i=0; i< ta_status.width * ta_status.height; i++ ) { nkeynes@736: *p = 0xF0000000; nkeynes@736: p += ta_status.current_tile_size; nkeynes@736: } nkeynes@736: } nkeynes@193: } else { nkeynes@736: ta_status.current_tile_size = 0; nkeynes@189: } nkeynes@193: nkeynes@193: if( tile_matrix == list_end ) { nkeynes@736: ta_status.current_tile_size = 0; nkeynes@193: } nkeynes@193: nkeynes@189: ta_status.state = STATE_IN_LIST; nkeynes@189: ta_status.current_list_type = listtype; nkeynes@189: ta_status.last_triangle_bounds.x1 = -1; nkeynes@189: } nkeynes@189: nkeynes@189: static int list_events[5] = {EVENT_PVR_OPAQUE_DONE, EVENT_PVR_OPAQUEMOD_DONE, nkeynes@736: EVENT_PVR_TRANS_DONE, EVENT_PVR_TRANSMOD_DONE, nkeynes@736: EVENT_PVR_PUNCHOUT_DONE }; nkeynes@189: nkeynes@189: static void ta_end_list() { nkeynes@189: if( ta_status.current_list_type != TA_LIST_NONE ) { nkeynes@736: asic_event( list_events[ta_status.current_list_type] ); nkeynes@189: } nkeynes@206: ta_status.current_list_type = TA_LIST_NONE; nkeynes@206: ta_status.current_vertex_type = TA_VERTEX_LISTLESS; nkeynes@206: ta_status.poly_vertex_size = 0; nkeynes@206: memset(&ta_status.poly_context[1], 0, 4); nkeynes@206: ta_status.state = STATE_IDLE; nkeynes@206: } nkeynes@206: nkeynes@206: static void ta_bad_input_error() { nkeynes@206: asic_event( EVENT_PVR_BAD_INPUT ); nkeynes@189: } nkeynes@189: nkeynes@189: /** nkeynes@189: * Write data out to the polygon buffer. nkeynes@189: * If the end-of-buffer is reached, asserts EVENT_PVR_PRIM_ALLOC_FAIL nkeynes@189: * @param data to be written nkeynes@189: * @param length Number of 32-bit words to write. nkeynes@189: * @return number of words actually written nkeynes@189: */ nkeynes@189: static int ta_write_polygon_buffer( uint32_t *data, int length ) nkeynes@189: { nkeynes@189: int rv; nkeynes@189: int posn = MMIO_READ( PVR2, TA_POLYPOS ); nkeynes@189: int end = MMIO_READ( PVR2, TA_POLYEND ); nkeynes@934: uint32_t *target = (uint32_t *)(pvr2_main_ram + posn); nkeynes@189: for( rv=0; rv < length; rv++ ) { nkeynes@736: if( posn == end ) { nkeynes@736: asic_event( EVENT_PVR_PRIM_ALLOC_FAIL ); nkeynes@736: // ta_status.state = STATE_ERROR; nkeynes@736: break; nkeynes@736: } nkeynes@736: if( posn < PVR2_RAM_SIZE ) { nkeynes@736: *target++ = *data++; nkeynes@736: } nkeynes@736: posn += 4; nkeynes@189: } nkeynes@189: nkeynes@189: MMIO_WRITE( PVR2, TA_POLYPOS, posn ); nkeynes@189: return rv; nkeynes@189: } nkeynes@189: nkeynes@193: #define TA_NO_ALLOC 0xFFFFFFFF nkeynes@193: nkeynes@189: /** nkeynes@193: * Allocate a new tile list block from the grow space and update the nkeynes@193: * word at reference to be a link to the new block. nkeynes@189: */ nkeynes@193: static uint32_t ta_alloc_tilelist( uint32_t reference ) { nkeynes@189: uint32_t posn = MMIO_READ( PVR2, TA_LISTPOS ); nkeynes@193: uint32_t limit = MMIO_READ( PVR2, TA_LISTEND ) >> 2; nkeynes@189: uint32_t newposn; nkeynes@189: if( ta_status.tilelist_dir == TA_GROW_DOWN ) { nkeynes@736: newposn = posn - ta_status.tilelist_size; nkeynes@736: if( posn == limit ) { nkeynes@736: PVRRAM(posn<<2) = 0xF0000000; nkeynes@736: PVRRAM(reference) = 0xE0000000 | (posn<<2); nkeynes@736: return TA_NO_ALLOC; nkeynes@736: } else if( posn < limit ) { nkeynes@736: PVRRAM(reference) = 0xE0000000 | (posn<<2); nkeynes@736: return TA_NO_ALLOC; nkeynes@736: } else if( newposn <= limit ) { nkeynes@736: } else if( newposn <= (limit + ta_status.tilelist_size) ) { nkeynes@736: asic_event( EVENT_PVR_MATRIX_ALLOC_FAIL ); nkeynes@736: MMIO_WRITE( PVR2, TA_LISTPOS, newposn ); nkeynes@736: } else { nkeynes@736: MMIO_WRITE( PVR2, TA_LISTPOS, newposn ); nkeynes@736: } nkeynes@736: PVRRAM(reference) = 0xE0000000 | (posn<<2); nkeynes@736: return posn << 2; nkeynes@189: } else { nkeynes@736: newposn = posn + ta_status.tilelist_size; nkeynes@736: if( posn == limit ) { nkeynes@736: PVRRAM(posn<<2) = 0xF0000000; nkeynes@736: PVRRAM(reference) = 0xE0000000 | (posn<<2); nkeynes@736: return TA_NO_ALLOC; nkeynes@736: } else if ( posn > limit ) { nkeynes@736: PVRRAM(reference) = 0xE0000000 | (posn<<2); nkeynes@736: return TA_NO_ALLOC; nkeynes@736: } else if( newposn >= limit ) { nkeynes@736: } else if( newposn >= (limit - ta_status.tilelist_size) ) { nkeynes@736: asic_event( EVENT_PVR_MATRIX_ALLOC_FAIL ); nkeynes@736: MMIO_WRITE( PVR2, TA_LISTPOS, newposn ); nkeynes@736: } else { nkeynes@736: MMIO_WRITE( PVR2, TA_LISTPOS, newposn ); nkeynes@736: } nkeynes@736: PVRRAM(reference) = 0xE0000000 | (posn<<2); nkeynes@736: return posn << 2; nkeynes@189: } nkeynes@189: } nkeynes@189: nkeynes@189: /** nkeynes@189: * Write a tile entry out to the matrix. nkeynes@189: */ nkeynes@429: static void ta_write_tile_entry( int x, int y, uint32_t tile_entry ) { nkeynes@189: uint32_t tile = TILESLOT(x,y); nkeynes@193: uint32_t tilestart = tile; nkeynes@189: uint32_t value; nkeynes@189: uint32_t lasttri = 0; nkeynes@429: int i; nkeynes@189: nkeynes@203: if( ta_status.clip_mode == TA_POLYCMD_CLIP_OUTSIDE && nkeynes@736: x >= ta_status.clip.x1 && x <= ta_status.clip.x2 && nkeynes@736: y >= ta_status.clip.y1 && y <= ta_status.clip.y2 ) { nkeynes@736: /* Tile clipped out */ nkeynes@736: return; nkeynes@203: } nkeynes@203: nkeynes@189: if( (tile_entry & 0x80000000) && nkeynes@736: ta_status.last_triangle_bounds.x1 != -1 && nkeynes@736: ta_status.last_triangle_bounds.x1 <= x && nkeynes@736: ta_status.last_triangle_bounds.x2 >= x && nkeynes@736: ta_status.last_triangle_bounds.y1 <= y && nkeynes@736: ta_status.last_triangle_bounds.y2 >= y ) { nkeynes@736: /* potential for triangle stacking */ nkeynes@736: lasttri = tile_entry & 0xE1E00000; nkeynes@189: } nkeynes@736: nkeynes@736: nkeynes@189: if( PVRRAM(tile) == 0xF0000000 ) { nkeynes@736: PVRRAM(tile) = tile_entry; nkeynes@736: PVRRAM(tile+4) = 0xF0000000; nkeynes@736: return; nkeynes@189: } nkeynes@189: nkeynes@189: while(1) { nkeynes@736: value = PVRRAM(tile); nkeynes@736: for( i=1; i (float)INT_MAX || TA_IS_INF(ta_status.poly_vertex[i].x) ) { nkeynes@736: tx[i] = INT_MAX/32; nkeynes@736: } else { nkeynes@736: tx[i] = (int)(ta_status.poly_vertex[i].x / 32.0); nkeynes@736: } nkeynes@736: if( ta_status.poly_vertex[i].y < 0.0 || TA_IS_NINF(ta_status.poly_vertex[i].y)) { nkeynes@736: ty[i] = -1; nkeynes@736: } else if( ta_status.poly_vertex[i].y > (float)INT_MAX || TA_IS_INF(ta_status.poly_vertex[i].y) ) { nkeynes@736: ty[i] = INT_MAX/32; nkeynes@736: } else { nkeynes@736: ty[i] = (int)(ta_status.poly_vertex[i].y / 32.0); nkeynes@736: } nkeynes@736: nkeynes@189: } nkeynes@189: nkeynes@189: /* Compute bounding box for each triangle individually, as well nkeynes@189: * as the overall polygon. nkeynes@189: */ nkeynes@189: nkeynes@801: triangle_bound[0].x1 = MIN3(tx[0],tx[1],tx[2]); nkeynes@801: triangle_bound[0].x2 = MAX3(tx[0],tx[1],tx[2]); nkeynes@801: triangle_bound[0].y1 = MIN3(ty[0],ty[1],ty[2]); nkeynes@801: triangle_bound[0].y2 = MAX3(ty[0],ty[1],ty[2]); nkeynes@801: polygon_bound.x1 = triangle_bound[0].x1; nkeynes@801: polygon_bound.y1 = triangle_bound[0].y1; nkeynes@801: polygon_bound.x2 = triangle_bound[0].x2; nkeynes@801: polygon_bound.y2 = triangle_bound[0].y2; nkeynes@801: nkeynes@801: for( i=1; i= ta_status.width ) polygon_bound.x2 = ta_status.width-1; nkeynes@189: if( polygon_bound.y1 < 0 ) polygon_bound.y1 = 0; nkeynes@189: if( polygon_bound.y2 >= ta_status.width ) polygon_bound.y2 = ta_status.height-1; nkeynes@189: nkeynes@189: /* Set the "single tile" flag if it's entirely contained in 1 tile */ nkeynes@189: if( polygon_bound.x1 == polygon_bound.x2 && nkeynes@736: polygon_bound.y1 == polygon_bound.y2 ) { nkeynes@736: poly_context[0] |= 0x00200000; nkeynes@189: } nkeynes@736: nkeynes@203: /* If the polygon is entirely clipped, don't even write the polygon data */ nkeynes@203: switch( ta_status.clip_mode ) { nkeynes@203: case TA_POLYCMD_CLIP_NONE: nkeynes@736: if( polygon_bound.x2 < 0 || polygon_bound.x1 >= ta_status.width || nkeynes@736: polygon_bound.y2 < 0 || polygon_bound.y1 >= ta_status.height ) { nkeynes@736: return; nkeynes@736: } nkeynes@736: break; nkeynes@203: case TA_POLYCMD_CLIP_INSIDE: nkeynes@736: if( polygon_bound.x2 < ta_status.clip.x1 || polygon_bound.x1 > ta_status.clip.x2 || nkeynes@736: polygon_bound.y2 < ta_status.clip.y1 || polygon_bound.y1 > ta_status.clip.y2 ) { nkeynes@736: return; nkeynes@736: } else { nkeynes@736: /* Clamp to clip bounds */ nkeynes@736: if( polygon_bound.x1 < ta_status.clip.x1 ) polygon_bound.x1 = ta_status.clip.x1; nkeynes@736: if( polygon_bound.x2 > ta_status.clip.x2 ) polygon_bound.x2 = ta_status.clip.x2; nkeynes@736: if( polygon_bound.y1 < ta_status.clip.y1 ) polygon_bound.y1 = ta_status.clip.y1; nkeynes@736: if( polygon_bound.y2 > ta_status.clip.y2 ) polygon_bound.y2 = ta_status.clip.y2; nkeynes@736: } nkeynes@736: break; nkeynes@203: case TA_POLYCMD_CLIP_OUTSIDE: nkeynes@736: if( polygon_bound.x1 >= ta_status.clip.x1 && polygon_bound.x2 <= ta_status.clip.x2 && nkeynes@736: polygon_bound.y1 >= ta_status.clip.y1 && polygon_bound.y2 <= ta_status.clip.y2 ) { nkeynes@736: return; nkeynes@736: } nkeynes@736: break; nkeynes@203: } nkeynes@189: nkeynes@189: /* Ok, we're good to go - write out the polygon first */ nkeynes@215: uint32_t tile_entry = (MMIO_READ( PVR2, TA_POLYPOS ) - ta_status.polybuf_start) >> 2 | nkeynes@736: ta_status.poly_pointer; nkeynes@736: nkeynes@189: int status = ta_write_polygon_buffer( poly_context, ta_status.poly_context_size ); nkeynes@189: if( status == 0 ) { nkeynes@736: /* No memory available - abort */ nkeynes@736: return; nkeynes@189: } else { nkeynes@736: for( i=0; i= x && nkeynes@736: triangle_bound[i].y1 <= y && triangle_bound[i].y2 >= y ) { nkeynes@736: entry |= (0x40000000>>i); nkeynes@736: } nkeynes@736: } nkeynes@736: ta_write_tile_entry( x, y, entry ); nkeynes@736: } nkeynes@736: } nkeynes@736: ta_status.last_triangle_bounds.x1 = -1; nkeynes@189: } nkeynes@189: } nkeynes@189: nkeynes@189: /** nkeynes@189: * Variant of ta_split_polygon called when vertex_count == max_vertex, but nkeynes@189: * the client hasn't sent the LAST VERTEX flag. Commit the poly as normal nkeynes@189: * first, then start a new poly with the first 2 vertexes taken from the nkeynes@189: * current one. nkeynes@189: */ nkeynes@189: static void ta_split_polygon() { nkeynes@189: ta_commit_polygon(); nkeynes@189: if( TA_IS_NORMAL_POLY() ) { nkeynes@736: /* This only applies to ordinary polys - Sprites + modifier lists are nkeynes@736: * handled differently nkeynes@736: */ nkeynes@736: if( ta_status.vertex_count == 3 ) { nkeynes@736: /* Triangles use an odd/even scheme */ nkeynes@736: if( ta_status.poly_parity == 0 ) { nkeynes@736: memcpy( &ta_status.poly_vertex[0], &ta_status.poly_vertex[2], nkeynes@736: sizeof(struct pvr2_ta_vertex) ); nkeynes@736: ta_status.poly_parity = 1; nkeynes@736: } else { nkeynes@736: memcpy( &ta_status.poly_vertex[1], &ta_status.poly_vertex[2], nkeynes@736: sizeof(struct pvr2_ta_vertex) ); nkeynes@736: ta_status.poly_parity = 0; nkeynes@736: } nkeynes@736: } else { nkeynes@736: /* Everything else just uses the last 2 vertexes in order */ nkeynes@736: memcpy( &ta_status.poly_vertex[0], &ta_status.poly_vertex[ta_status.vertex_count-2], nkeynes@736: sizeof(struct pvr2_ta_vertex)*2 ); nkeynes@736: ta_status.poly_parity = 0; nkeynes@736: } nkeynes@736: ta_status.vertex_count = 2; nkeynes@189: } else { nkeynes@736: ta_status.vertex_count = 0; nkeynes@189: } nkeynes@189: } nkeynes@189: nkeynes@189: /** nkeynes@189: * Parse the polygon context block and setup the internal state to receive nkeynes@189: * vertexes. nkeynes@189: * @param data 32 bytes of parameter data. nkeynes@189: */ nkeynes@189: static void ta_parse_polygon_context( union ta_data *data ) { nkeynes@189: int colourfmt = TA_POLYCMD_COLOURFMT(data[0].i); nkeynes@194: if( TA_POLYCMD_USELENGTH(data[0].i) ) { nkeynes@736: ta_status.max_vertex = TA_POLYCMD_LENGTH(data[0].i); nkeynes@194: } nkeynes@203: ta_status.clip_mode = TA_POLYCMD_CLIP(data[0].i); nkeynes@203: if( ta_status.clip_mode == 1 ) { /* Reserved - treat as CLIP_INSIDE */ nkeynes@736: ta_status.clip_mode = TA_POLYCMD_CLIP_INSIDE; nkeynes@203: } nkeynes@189: ta_status.vertex_count = 0; nkeynes@189: ta_status.poly_context[0] = nkeynes@736: (data[1].i & 0xFC1FFFFF) | ((data[0].i & 0x0B) << 22); nkeynes@189: ta_status.poly_context[1] = data[2].i; nkeynes@189: ta_status.poly_context[3] = data[4].i; nkeynes@189: ta_status.poly_parity = 0; nkeynes@189: if( data[0].i & TA_POLYCMD_TEXTURED ) { nkeynes@736: ta_status.current_vertex_type = data[0].i & 0x0D; nkeynes@736: ta_status.poly_context[2] = data[3].i; nkeynes@736: ta_status.poly_context[4] = data[5].i; nkeynes@736: if( data[0].i & TA_POLYCMD_SPECULAR ) { nkeynes@736: ta_status.poly_context[0] |= 0x01000000; nkeynes@736: ta_status.poly_vertex_size = 4; nkeynes@736: } else { nkeynes@736: ta_status.poly_vertex_size = 3; nkeynes@736: } nkeynes@736: if( data[0].i & TA_POLYCMD_UV16 ) { nkeynes@736: ta_status.poly_vertex_size--; nkeynes@736: } nkeynes@189: } else { nkeynes@736: ta_status.current_vertex_type = 0; nkeynes@736: ta_status.poly_vertex_size = 1; nkeynes@736: ta_status.poly_context[2] = 0; nkeynes@736: ta_status.poly_context[4] = 0; nkeynes@189: } nkeynes@189: nkeynes@189: ta_status.poly_pointer = (ta_status.poly_vertex_size << 21); nkeynes@189: ta_status.poly_context_size = 3; nkeynes@189: if( data[0].i & TA_POLYCMD_MODIFIED ) { nkeynes@736: ta_status.poly_pointer |= 0x01000000; nkeynes@736: if( data[0].i & TA_POLYCMD_FULLMOD ) { nkeynes@736: ta_status.poly_context_size = 5; nkeynes@736: ta_status.poly_vertex_size <<= 1; nkeynes@736: ta_status.current_vertex_type |= 0x40; nkeynes@736: /* Modified/float not supported - behaves as per last intensity */ nkeynes@736: if( colourfmt == TA_POLYCMD_COLOURFMT_FLOAT ) { nkeynes@736: colourfmt = TA_POLYCMD_COLOURFMT_LASTINT; nkeynes@736: } nkeynes@736: } nkeynes@189: } nkeynes@736: nkeynes@189: if( colourfmt == TA_POLYCMD_COLOURFMT_INTENSITY ) { nkeynes@736: if( TA_POLYCMD_IS_FULLMOD(data[0].i) || nkeynes@736: TA_POLYCMD_IS_SPECULAR(data[0].i) ) { nkeynes@736: ta_status.state = STATE_EXPECT_POLY_BLOCK2; nkeynes@736: } else { nkeynes@736: ta_status.intensity1 = nkeynes@736: parse_float_colour( data[4].f, data[5].f, data[6].f, data[7].f ); nkeynes@736: } nkeynes@189: } else if( colourfmt == TA_POLYCMD_COLOURFMT_LASTINT ) { nkeynes@736: colourfmt = TA_POLYCMD_COLOURFMT_INTENSITY; nkeynes@189: } nkeynes@189: nkeynes@189: ta_status.current_vertex_type |= colourfmt; nkeynes@189: } nkeynes@189: nkeynes@189: /** nkeynes@189: * Parse the modifier volume context block and setup the internal state to nkeynes@189: * receive modifier vertexes. nkeynes@189: * @param data 32 bytes of parameter data. nkeynes@189: */ nkeynes@189: static void ta_parse_modifier_context( union ta_data *data ) { nkeynes@189: ta_status.current_vertex_type = TA_VERTEX_MOD_VOLUME; nkeynes@189: ta_status.poly_vertex_size = 0; nkeynes@206: ta_status.clip_mode = TA_POLYCMD_CLIP(data[0].i); nkeynes@206: if( ta_status.clip_mode == 1 ) { /* Reserved - treat as CLIP_INSIDE */ nkeynes@736: ta_status.clip_mode = TA_POLYCMD_CLIP_INSIDE; nkeynes@206: } nkeynes@189: ta_status.poly_context_size = 3; nkeynes@189: ta_status.poly_context[0] = (data[1].i & 0xFC1FFFFF) | nkeynes@736: ((data[0].i & 0x0B)<<22); nkeynes@189: if( TA_POLYCMD_IS_SPECULAR(data[0].i) ) { nkeynes@736: ta_status.poly_context[0] |= 0x01000000; nkeynes@189: } nkeynes@189: ta_status.poly_context[1] = 0; nkeynes@189: ta_status.poly_context[2] = 0; nkeynes@189: ta_status.vertex_count = 0; nkeynes@189: ta_status.max_vertex = 3; nkeynes@189: ta_status.poly_pointer = 0; nkeynes@189: } nkeynes@189: nkeynes@189: /** nkeynes@189: * Parse the sprite context block and setup the internal state to receive nkeynes@189: * vertexes. nkeynes@189: * @param data 32 bytes of parameter data. nkeynes@189: */ nkeynes@189: static void ta_parse_sprite_context( union ta_data *data ) { nkeynes@189: ta_status.poly_context_size = 3; nkeynes@189: ta_status.poly_context[0] = (data[1].i & 0xFC1FFFFF) | nkeynes@736: ((data[0].i & 0x0B)<<22) | 0x00400000; nkeynes@340: ta_status.clip_mode = TA_POLYCMD_CLIP(data[0].i); nkeynes@340: if( ta_status.clip_mode == 1 ) { /* Reserved - treat as CLIP_INSIDE */ nkeynes@736: ta_status.clip_mode = TA_POLYCMD_CLIP_INSIDE; nkeynes@340: } nkeynes@189: if( TA_POLYCMD_IS_SPECULAR(data[0].i) ) { nkeynes@736: ta_status.poly_context[0] |= 0x01000000; nkeynes@189: } nkeynes@189: ta_status.poly_context[1] = data[2].i; nkeynes@189: ta_status.poly_context[2] = data[3].i; nkeynes@189: if( data[0].i & TA_POLYCMD_TEXTURED ) { nkeynes@736: ta_status.poly_vertex_size = 2; nkeynes@736: ta_status.poly_vertex[2].detail[1] = data[4].i; nkeynes@736: ta_status.current_vertex_type = TA_VERTEX_TEX_SPRITE; nkeynes@189: } else { nkeynes@736: ta_status.poly_vertex_size = 1; nkeynes@736: ta_status.poly_vertex[2].detail[0] = data[4].i; nkeynes@736: ta_status.current_vertex_type = TA_VERTEX_SPRITE; nkeynes@189: } nkeynes@189: ta_status.vertex_count = 0; nkeynes@189: ta_status.max_vertex = 4; nkeynes@189: ta_status.poly_pointer = (ta_status.poly_vertex_size << 21); nkeynes@189: } nkeynes@189: nkeynes@189: /** nkeynes@189: * Copy the last read vertex into all vertexes up to max_vertex. Used for nkeynes@189: * Aborted polygons under some circumstances. nkeynes@189: */ nkeynes@189: static void ta_fill_vertexes( ) { nkeynes@189: int i; nkeynes@189: for( i=ta_status.vertex_count; ix = data[1].f; nkeynes@189: vertex->y = data[2].f; nkeynes@189: vertex->z = data[3].f; nkeynes@189: nkeynes@189: switch( ta_status.current_vertex_type ) { nkeynes@189: case TA_VERTEX_PACKED: nkeynes@736: vertex->detail[0] = data[6].i; nkeynes@736: break; nkeynes@189: case TA_VERTEX_FLOAT: nkeynes@736: vertex->detail[0] = parse_float_colour( data[4].f, data[5].f, data[6].f, data[7].f ); nkeynes@736: break; nkeynes@189: case TA_VERTEX_INTENSITY: nkeynes@736: vertex->detail[0] = parse_intensity_colour( ta_status.intensity1, data[6].f ); nkeynes@736: break; nkeynes@189: nkeynes@189: case TA_VERTEX_TEX_SPEC_PACKED: nkeynes@736: vertex->detail[3] = data[7].i; /* ARGB */ nkeynes@736: /* Fallthrough */ nkeynes@189: case TA_VERTEX_TEX_PACKED: nkeynes@736: vertex->detail[0] = data[4].i; /* U */ nkeynes@736: vertex->detail[1] = data[5].i; /* V */ nkeynes@736: vertex->detail[2] = data[6].i; /* ARGB */ nkeynes@736: break; nkeynes@189: case TA_VERTEX_TEX_UV16_SPEC_PACKED: nkeynes@736: vertex->detail[2] = data[7].i; /* ARGB */ nkeynes@736: /* Fallthrough */ nkeynes@189: case TA_VERTEX_TEX_UV16_PACKED: nkeynes@736: vertex->detail[0] = data[4].i; /* UV */ nkeynes@736: vertex->detail[1] = data[6].i; /* ARGB */ nkeynes@736: break; nkeynes@189: nkeynes@189: case TA_VERTEX_TEX_FLOAT: nkeynes@189: case TA_VERTEX_TEX_SPEC_FLOAT: nkeynes@736: vertex->detail[0] = data[4].i; /* U */ nkeynes@736: vertex->detail[1] = data[5].i; /* UV */ nkeynes@736: ta_status.state = STATE_EXPECT_VERTEX_BLOCK2; nkeynes@736: break; nkeynes@189: case TA_VERTEX_TEX_UV16_FLOAT: nkeynes@189: case TA_VERTEX_TEX_UV16_SPEC_FLOAT: nkeynes@736: vertex->detail[0] = data[4].i; /* UV */ nkeynes@736: ta_status.state = STATE_EXPECT_VERTEX_BLOCK2; nkeynes@736: break; nkeynes@189: nkeynes@189: case TA_VERTEX_TEX_SPEC_INTENSITY: nkeynes@736: vertex->detail[3] = parse_intensity_colour( ta_status.intensity2, data[7].f ); nkeynes@736: /* Fallthrough */ nkeynes@189: case TA_VERTEX_TEX_INTENSITY: nkeynes@736: vertex->detail[0] = data[4].i; /* U */ nkeynes@736: vertex->detail[1] = data[5].i; /* V */ nkeynes@736: vertex->detail[2] = parse_intensity_colour( ta_status.intensity1, data[6].f ); nkeynes@736: break; nkeynes@189: case TA_VERTEX_TEX_UV16_SPEC_INTENSITY: nkeynes@736: vertex->detail[2] = parse_intensity_colour( ta_status.intensity2, data[7].f ); nkeynes@736: /* Fallthrough */ nkeynes@189: case TA_VERTEX_TEX_UV16_INTENSITY: nkeynes@736: vertex->detail[0] = data[4].i; /* UV */ nkeynes@736: vertex->detail[1] = parse_intensity_colour( ta_status.intensity1, data[6].f ); nkeynes@736: break; nkeynes@189: nkeynes@189: case TA_VERTEX_PACKED_MOD: nkeynes@736: vertex->detail[0] = data[4].i; /* ARGB */ nkeynes@736: vertex->detail[1] = data[5].i; /* ARGB */ nkeynes@736: break; nkeynes@189: case TA_VERTEX_INTENSITY_MOD: nkeynes@736: vertex->detail[0] = parse_intensity_colour( ta_status.intensity1, data[4].f ); nkeynes@736: vertex->detail[1] = parse_intensity_colour( ta_status.intensity2, data[5].f ); nkeynes@736: break; nkeynes@189: nkeynes@189: case TA_VERTEX_TEX_SPEC_PACKED_MOD: nkeynes@736: vertex->detail[3] = data[7].i; /* ARGB0 */ nkeynes@736: /* Fallthrough */ nkeynes@189: case TA_VERTEX_TEX_PACKED_MOD: nkeynes@736: vertex->detail[0] = data[4].i; /* U0 */ nkeynes@736: vertex->detail[1] = data[5].i; /* V0 */ nkeynes@736: vertex->detail[2] = data[6].i; /* ARGB0 */ nkeynes@736: ta_status.state = STATE_EXPECT_VERTEX_BLOCK2; nkeynes@736: break; nkeynes@189: case TA_VERTEX_TEX_UV16_SPEC_PACKED_MOD: nkeynes@736: vertex->detail[2] = data[7].i; /* ARGB0 */ nkeynes@736: /* Fallthrough */ nkeynes@189: case TA_VERTEX_TEX_UV16_PACKED_MOD: nkeynes@736: vertex->detail[0] = data[4].i; /* UV0 */ nkeynes@736: vertex->detail[1] = data[6].i; /* ARGB0 */ nkeynes@736: ta_status.state = STATE_EXPECT_VERTEX_BLOCK2; nkeynes@736: break; nkeynes@189: nkeynes@189: case TA_VERTEX_TEX_SPEC_INTENSITY_MOD: nkeynes@736: vertex->detail[3] = parse_intensity_colour( ta_status.intensity1, data[7].f ); nkeynes@736: /* Fallthrough */ nkeynes@189: case TA_VERTEX_TEX_INTENSITY_MOD: nkeynes@736: vertex->detail[0] = data[4].i; /* U0 */ nkeynes@736: vertex->detail[1] = data[5].i; /* V0 */ nkeynes@736: vertex->detail[2] = parse_intensity_colour( ta_status.intensity1, data[6].f ); nkeynes@736: ta_status.state = STATE_EXPECT_VERTEX_BLOCK2; nkeynes@736: break; nkeynes@189: case TA_VERTEX_TEX_UV16_SPEC_INTENSITY_MOD: nkeynes@736: vertex->detail[2] = parse_intensity_colour( ta_status.intensity1, data[7].f ); nkeynes@736: /* Fallthrough */ nkeynes@189: case TA_VERTEX_TEX_UV16_INTENSITY_MOD: nkeynes@736: vertex->detail[0] = data[4].i; /* UV0 */ nkeynes@736: vertex->detail[1] = parse_intensity_colour( ta_status.intensity1, data[6].f ); nkeynes@736: ta_status.state = STATE_EXPECT_VERTEX_BLOCK2; nkeynes@736: break; nkeynes@736: nkeynes@189: case TA_VERTEX_SPRITE: nkeynes@189: case TA_VERTEX_TEX_SPRITE: nkeynes@189: case TA_VERTEX_MOD_VOLUME: nkeynes@206: case TA_VERTEX_LISTLESS: nkeynes@736: vertex++; nkeynes@736: vertex->x = data[4].f; nkeynes@736: vertex->y = data[5].f; nkeynes@736: vertex->z = data[6].f; nkeynes@736: vertex++; nkeynes@736: vertex->x = data[7].f; nkeynes@736: ta_status.vertex_count += 2; nkeynes@736: ta_status.state = STATE_EXPECT_VERTEX_BLOCK2; nkeynes@736: break; nkeynes@189: } nkeynes@189: ta_status.vertex_count++; nkeynes@189: } nkeynes@189: nkeynes@189: static void ta_parse_vertex_block2( union ta_data *data ) { nkeynes@189: struct pvr2_ta_vertex *vertex = &ta_status.poly_vertex[ta_status.vertex_count-1]; nkeynes@189: nkeynes@189: switch( ta_status.current_vertex_type ) { nkeynes@189: case TA_VERTEX_TEX_SPEC_FLOAT: nkeynes@736: vertex->detail[3] = parse_float_colour( data[4].f, data[5].f, data[6].f, data[7].f ); nkeynes@736: /* Fallthrough */ nkeynes@189: case TA_VERTEX_TEX_FLOAT: nkeynes@736: vertex->detail[2] = parse_float_colour( data[0].f, data[1].f, data[2].f, data[3].f ); nkeynes@736: break; nkeynes@189: case TA_VERTEX_TEX_UV16_SPEC_FLOAT: nkeynes@736: vertex->detail[2] = parse_float_colour( data[4].f, data[5].f, data[6].f, data[7].f ); nkeynes@736: /* Fallthrough */ nkeynes@189: case TA_VERTEX_TEX_UV16_FLOAT: nkeynes@736: vertex->detail[1] = parse_float_colour( data[0].f, data[1].f, data[2].f, data[3].f ); nkeynes@736: break; nkeynes@189: case TA_VERTEX_TEX_PACKED_MOD: nkeynes@736: vertex->detail[3] = data[0].i; /* U1 */ nkeynes@736: vertex->detail[4] = data[1].i; /* V1 */ nkeynes@736: vertex->detail[5] = data[2].i; /* ARGB1 */ nkeynes@736: break; nkeynes@189: case TA_VERTEX_TEX_SPEC_PACKED_MOD: nkeynes@736: vertex->detail[4] = data[0].i; /* U1 */ nkeynes@736: vertex->detail[5] = data[1].i; /* V1 */ nkeynes@736: vertex->detail[6] = data[2].i; /* ARGB1 */ nkeynes@736: vertex->detail[7] = data[3].i; /* ARGB1 */ nkeynes@736: break; nkeynes@189: case TA_VERTEX_TEX_UV16_PACKED_MOD: nkeynes@736: vertex->detail[2] = data[0].i; /* UV1 */ nkeynes@736: vertex->detail[3] = data[2].i; /* ARGB1 */ nkeynes@736: break; nkeynes@189: case TA_VERTEX_TEX_UV16_SPEC_PACKED_MOD: nkeynes@736: vertex->detail[3] = data[0].i; /* UV1 */ nkeynes@736: vertex->detail[4] = data[2].i; /* ARGB1 */ nkeynes@736: vertex->detail[5] = data[3].i; /* ARGB1 */ nkeynes@736: break; nkeynes@189: nkeynes@189: case TA_VERTEX_TEX_INTENSITY_MOD: nkeynes@736: vertex->detail[3] = data[0].i; /* U1 */ nkeynes@736: vertex->detail[4] = data[1].i; /* V1 */ nkeynes@736: vertex->detail[5] = parse_intensity_colour( ta_status.intensity2, data[2].f ); /* ARGB1 */ nkeynes@736: break; nkeynes@189: case TA_VERTEX_TEX_SPEC_INTENSITY_MOD: nkeynes@736: vertex->detail[4] = data[0].i; /* U1 */ nkeynes@736: vertex->detail[5] = data[1].i; /* V1 */ nkeynes@736: vertex->detail[6] = parse_intensity_colour( ta_status.intensity2, data[2].f ); /* ARGB1 */ nkeynes@736: vertex->detail[7] = parse_intensity_colour( ta_status.intensity2, data[3].f ); /* ARGB1 */ nkeynes@736: break; nkeynes@189: case TA_VERTEX_TEX_UV16_INTENSITY_MOD: nkeynes@736: vertex->detail[2] = data[0].i; /* UV1 */ nkeynes@736: vertex->detail[3] = parse_intensity_colour( ta_status.intensity2, data[2].f ); /* ARGB1 */ nkeynes@736: break; nkeynes@189: case TA_VERTEX_TEX_UV16_SPEC_INTENSITY_MOD: nkeynes@736: vertex->detail[3] = data[0].i; /* UV1 */ nkeynes@736: vertex->detail[4] = parse_intensity_colour( ta_status.intensity2, data[2].f ); /* ARGB1 */ nkeynes@736: vertex->detail[5] = parse_intensity_colour( ta_status.intensity2, data[3].f ); /* ARGB1 */ nkeynes@736: break; nkeynes@189: nkeynes@189: case TA_VERTEX_SPRITE: nkeynes@736: vertex->y = data[0].f; nkeynes@736: vertex->z = data[1].f; nkeynes@736: vertex++; nkeynes@736: ta_status.vertex_count++; nkeynes@736: vertex->x = data[2].f; nkeynes@736: vertex->y = data[3].f; nkeynes@736: vertex->z = 0; nkeynes@736: vertex->detail[0] = 0; nkeynes@736: ta_status.poly_vertex[0].detail[0] = 0; nkeynes@736: ta_status.poly_vertex[1].detail[0] = 0; nkeynes@736: break; nkeynes@189: case TA_VERTEX_TEX_SPRITE: nkeynes@736: vertex->y = data[0].f; nkeynes@736: vertex->z = data[1].f; nkeynes@736: vertex++; nkeynes@736: ta_status.vertex_count++; nkeynes@736: vertex->x = data[2].f; nkeynes@736: vertex->y = data[3].f; nkeynes@736: vertex->z = 0; nkeynes@736: vertex->detail[0] = 0; nkeynes@736: vertex->detail[1] = 0; nkeynes@736: ta_status.poly_vertex[0].detail[0] = data[5].i; nkeynes@736: ta_status.poly_vertex[0].detail[1] = 0; nkeynes@736: ta_status.poly_vertex[1].detail[0] = data[6].i; nkeynes@736: ta_status.poly_vertex[1].detail[1] = 0; nkeynes@736: ta_status.poly_vertex[2].detail[0] = data[7].i; nkeynes@736: break; nkeynes@189: case TA_VERTEX_MOD_VOLUME: nkeynes@206: case TA_VERTEX_LISTLESS: nkeynes@736: vertex->y = data[0].f; nkeynes@736: vertex->z = data[1].f; nkeynes@736: break; nkeynes@189: } nkeynes@194: ta_status.state = STATE_IN_POLYGON; nkeynes@189: } nkeynes@189: nkeynes@189: /** nkeynes@189: * Process 1 32-byte block of ta data nkeynes@189: */ nkeynes@429: void pvr2_ta_process_block( unsigned char *input ) { nkeynes@189: union ta_data *data = (union ta_data *)input; nkeynes@189: nkeynes@189: switch( ta_status.state ) { nkeynes@189: case STATE_ERROR: nkeynes@736: /* Fatal error raised - stop processing until reset */ nkeynes@736: return; nkeynes@189: nkeynes@189: case STATE_EXPECT_POLY_BLOCK2: nkeynes@736: /* This is always a pair of floating-point colours */ nkeynes@736: ta_status.intensity1 = nkeynes@736: parse_float_colour( data[0].f, data[1].f, data[2].f, data[3].f ); nkeynes@736: ta_status.intensity2 = nkeynes@736: parse_float_colour( data[4].f, data[5].f, data[6].f, data[7].f ); nkeynes@736: ta_status.state = STATE_IN_LIST; nkeynes@736: break; nkeynes@189: nkeynes@189: case STATE_EXPECT_VERTEX_BLOCK2: nkeynes@736: ta_parse_vertex_block2( data ); nkeynes@736: if( ta_status.vertex_count == ta_status.max_vertex ) { nkeynes@736: ta_split_polygon(); nkeynes@736: } nkeynes@736: break; nkeynes@189: nkeynes@189: case STATE_EXPECT_END_VERTEX_BLOCK2: nkeynes@736: ta_parse_vertex_block2( data ); nkeynes@736: if( ta_status.vertex_count < 3 ) { nkeynes@736: ta_bad_input_error(); nkeynes@736: } else { nkeynes@736: ta_commit_polygon(); nkeynes@736: } nkeynes@736: ta_status.vertex_count = 0; nkeynes@736: ta_status.poly_parity = 0; nkeynes@736: ta_status.state = STATE_IN_LIST; nkeynes@736: break; nkeynes@189: case STATE_IN_LIST: nkeynes@194: case STATE_IN_POLYGON: nkeynes@189: case STATE_IDLE: nkeynes@736: switch( TA_CMD( data->i ) ) { nkeynes@736: case TA_CMD_END_LIST: nkeynes@736: if( ta_status.state == STATE_IN_POLYGON ) { nkeynes@736: ta_bad_input_error(); nkeynes@736: ta_end_list(); nkeynes@736: ta_status.state = STATE_ERROR; /* Abort further processing */ nkeynes@736: } else { nkeynes@736: ta_end_list(); nkeynes@736: } nkeynes@736: break; nkeynes@736: case TA_CMD_CLIP: nkeynes@736: if( ta_status.state == STATE_IN_POLYGON ) { nkeynes@736: ta_bad_input_error(); nkeynes@736: ta_status.accept_vertexes = FALSE; nkeynes@736: /* Enter stuffed up mode */ nkeynes@736: } nkeynes@736: ta_status.clip.x1 = data[4].i & 0x3F; nkeynes@736: ta_status.clip.y1 = data[5].i & 0x0F; nkeynes@736: ta_status.clip.x2 = data[6].i & 0x3F; nkeynes@736: ta_status.clip.y2 = data[7].i & 0x0F; nkeynes@736: if( ta_status.clip.x2 >= ta_status.width ) nkeynes@736: ta_status.clip.x2 = ta_status.width - 1; nkeynes@736: if( ta_status.clip.y2 >= ta_status.height ) nkeynes@736: ta_status.clip.y2 = ta_status.height - 1; nkeynes@736: break; nkeynes@736: case TA_CMD_POLYGON_CONTEXT: nkeynes@736: if( ta_status.state == STATE_IDLE ) { nkeynes@736: ta_init_list( TA_POLYCMD_LISTTYPE( data->i ) ); nkeynes@736: } nkeynes@189: nkeynes@736: if( ta_status.vertex_count != 0 ) { nkeynes@736: /* Error, and not a very well handled one either */ nkeynes@736: ta_bad_input_error(); nkeynes@736: ta_status.accept_vertexes = FALSE; nkeynes@736: } else { nkeynes@736: if( TA_IS_MODIFIER_LIST( ta_status.current_list_type ) ) { nkeynes@736: ta_parse_modifier_context(data); nkeynes@736: } else { nkeynes@736: ta_parse_polygon_context(data); nkeynes@736: } nkeynes@736: } nkeynes@736: break; nkeynes@736: case TA_CMD_SPRITE_CONTEXT: nkeynes@736: if( ta_status.state == STATE_IDLE ) { nkeynes@736: ta_init_list( TA_POLYCMD_LISTTYPE( data->i ) ); nkeynes@736: } nkeynes@189: nkeynes@736: if( ta_status.vertex_count != 0 ) { nkeynes@736: ta_fill_vertexes(); nkeynes@736: ta_commit_polygon(); nkeynes@736: } nkeynes@736: nkeynes@736: ta_parse_sprite_context(data); nkeynes@736: break; nkeynes@736: case TA_CMD_VERTEX: nkeynes@736: ta_status.state = STATE_IN_POLYGON; nkeynes@736: ta_parse_vertex(data); nkeynes@736: nkeynes@736: if( ta_status.state == STATE_EXPECT_VERTEX_BLOCK2 ) { nkeynes@736: if( TA_IS_END_VERTEX(data[0].i) ) { nkeynes@736: ta_status.state = STATE_EXPECT_END_VERTEX_BLOCK2; nkeynes@736: } nkeynes@736: } else if( TA_IS_END_VERTEX(data->i) ) { nkeynes@736: if( ta_status.vertex_count < 3 ) { nkeynes@736: ta_bad_input_error(); nkeynes@736: } else { nkeynes@736: ta_commit_polygon(); nkeynes@736: } nkeynes@736: ta_status.vertex_count = 0; nkeynes@736: ta_status.poly_parity = 0; nkeynes@736: ta_status.state = STATE_IN_LIST; nkeynes@736: } else if( ta_status.vertex_count == ta_status.max_vertex ) { nkeynes@736: ta_split_polygon(); nkeynes@736: } nkeynes@736: break; nkeynes@736: default: nkeynes@736: if( ta_status.state == STATE_IN_POLYGON ) { nkeynes@736: ta_bad_input_error(); nkeynes@736: } nkeynes@736: } nkeynes@736: break; nkeynes@189: } nkeynes@189: nkeynes@189: } nkeynes@189: nkeynes@753: /** nkeynes@753: * Find the first polygon or sprite context in the supplied buffer of TA nkeynes@753: * data. nkeynes@753: * @return A pointer to the context, or NULL if it cannot be found nkeynes@753: */ nkeynes@753: uint32_t *pvr2_ta_find_polygon_context( uint32_t *buf, uint32_t length ) nkeynes@753: { nkeynes@753: uint32_t *poly; nkeynes@753: for( poly = buf; poly < buf+(length>>2); poly += 8 ) { nkeynes@753: if( TA_CMD(*poly) == TA_CMD_POLYGON_CONTEXT || nkeynes@753: TA_CMD(*poly) == TA_CMD_SPRITE_CONTEXT ) { nkeynes@753: return poly; nkeynes@753: } nkeynes@753: } nkeynes@753: return NULL; nkeynes@753: } nkeynes@189: nkeynes@189: /** nkeynes@189: * Write a block of data to the tile accelerator, adding the data to the nkeynes@189: * current scene. We don't make any particular attempt to interpret the data nkeynes@189: * at this stage, deferring that until render time. nkeynes@189: * nkeynes@189: * Currently copies the data verbatim to the vertex buffer, processing only nkeynes@189: * far enough to generate the correct end-of-list events. Tile buffer is nkeynes@189: * entirely ignored. nkeynes@189: */ nkeynes@429: void pvr2_ta_write( unsigned char *buf, uint32_t length ) nkeynes@189: { nkeynes@189: if( ta_status.debug_output ) { nkeynes@736: fwrite_dump32( (uint32_t *)buf, length, stderr ); nkeynes@189: } nkeynes@189: nkeynes@189: for( ; length >=32; length -= 32 ) { nkeynes@736: pvr2_ta_process_block( buf ); nkeynes@736: buf += 32; nkeynes@189: } nkeynes@189: } nkeynes@931: nkeynes@931: void FASTCALL pvr2_ta_write_burst( sh4addr_t addr, unsigned char *data ) nkeynes@931: { nkeynes@931: if( ta_status.debug_output ) { nkeynes@931: fwrite_dump32( (uint32_t *)data, 32, stderr ); nkeynes@931: } nkeynes@931: pvr2_ta_process_block( data ); nkeynes@931: }