Model 2: faster 3D, 2D layers, SCSP DSP, i960 and SHARC (#2201)

Renderer:
- texture sheets also kept one byte per texel, the texel fetch reads them
  straight (inlined, an opaque and a translucent span)
- the pixel of each texel worked out once for the colours of a polygon
- translucent texel pairs packed from a table; a translucent pixel sure to
  be discarded skips the next mipmap level
- 3D pixels kept as the 15-bit colour of the bitmap
- the tile layers drawn straight into the bitmap: the polygons are only
  drawn over the tile pixels behind them, no copy passes

System 24 tiles (rgb version, Model 1 and 2): eight pixels at a time, a
group all of the layer's priority without a test each, the last pixels of
the line as groups too.

SCSP DSP: the steps that only multiply and accumulate on a short path,
decoded when MPRO changes; the delay RAM float packing from a table.

i960: opcodes read straight from the memory block of the last fetch
(memory_get_read_range), execute_op inlined.

SHARC: no DMA call while channels 6 and 7 are idle.

AArch64 (QEMU, instructions per frame): fvipers -26%, doa -28%, daytona
-31%, vf2 -25%. Frames and audio are the same on the Model 2 sets, the
ST-V sets and in save states.


Claude-Session: https://claude.ai/code/session_013Vje3pEtpLHubqCGwCgoQX

Co-authored-by: Claude Opus 5.5 <[email protected]>
This commit is contained in:
zanacandClaude Opus 5.5 authored and GitHub committed 2026-11-03 02:19:01 -05:00
1 parent 35fee378f5
commit 7ed1af5370
10 files changed
+480 -92

No files matched your search

+34 -2
View File
@@ -120,9 +120,33 @@ static INLINE int i960_is_burst( UINT32 address )
return i960_burst_cb == NULL || i960_burst_cb( address );
}
/* port: the opcodes are read straight from the memory block of the last
fetch (ROM, RAM or a bank: what cpu_readmem32ledw_dword reads there);
anything else goes through the memory system. Forgotten at every
timeslice, as the banks are only set up between them */
static UINT32 i960_fetch_start, i960_fetch_len;
static const UINT8 *i960_fetch_ptr;
static UINT32 i960_readop_slow( UINT32 address )
{
offs_t start, len;
const UINT8 *p = (const UINT8 *)memory_get_read_range( cpu_getactivecpu(), address, &start, &len );
if( p != NULL )
{
/* the block in the CPU's addresses (the mask only drops high bits) */
i960_fetch_start = address - ( ( address & mem_amask ) - start );
i960_fetch_len = len;
i960_fetch_ptr = p;
}
return cpu_readmem32ledw_dword( address );
}
static INLINE UINT32 i960_readop( UINT32 address )
{
return cpu_readmem32ledw_dword( address );
UINT32 off = address - i960_fetch_start;
if( off < i960_fetch_len )
return *(const data32_t *)( i960_fetch_ptr + ( off & ~3 ) );
return i960_readop_slow( address );
}
static void i960_fatal( const char *fmt, ... )
@@ -880,7 +904,14 @@ static void execute_burst_stall_op(UINT32 opcode)
check_immediate_irqs();
}
static void execute_op(UINT32 opcode)
/* port: inlined in its only caller, the i960_execute loop: as a function,
saving and restoring its registers on every instruction cost a fifth of
the time spent in the CPU */
#if defined(__GNUC__)
static INLINE __attribute__((always_inline)) void execute_op(UINT32 opcode)
#else
static INLINE void execute_op(UINT32 opcode)
#endif
{
UINT32 t1, t2;
double t1f, t2f;
@@ -2521,6 +2552,7 @@ int i960_execute( int cycles )
UINT32 opcode;
i960_icount = cycles;
i960_fetch_len = 0;
/* delay checking irqs if we are in burst stall mode */
if( m_stall_state.burst_mode == 0 )
+2 -1
View File
@@ -1188,7 +1188,8 @@ int sharc_execute(int cycles)
}
}
if (!m_core->write_stalled)
/* port: no call when channels 6 and 7 are idle (most instructions) */
if (!m_core->write_stalled && (m_core->dma_status & ((1 << 6) | (1 << 7))))
{
dma_stalled = dma_run_cycle(6);
dma_stalled |= dma_run_cycle(7);
+44
View File
@@ -37,6 +37,42 @@ data32_t *model2_bufferram;
static data32_t *model2_backup1;
data32_t *model2_textureram0;
data32_t *model2_textureram1;
UINT8 *model2_texdec0;
UINT8 *model2_texdec1;
/*
The texture sheets taken apart, one byte per texel, for the renderer
(fetch_bilinear_texel): a 32-bit word of texture RAM holds two 2x2
blocks of 4-bit texels. Its texels are at x = xbase + 0..3 and
y = ybase + 0..1 of the 2048x1024 sheet, the rows of the columns past
1024 being folded into the upper half of the RAM; within the word, bit 1
of x picks the block and the parity of x and y the texel. The byte array
is indexed y * 2048 + x and is rebuilt with every write.
*/
static void model2_tex_decode( UINT8 *dec, UINT32 index, UINT32 word )
{
UINT32 r = ( index >> 8 ) & 0x3ff, xa = index & 0xff;
UINT32 xbase = ( r < 512 ) ? xa * 4 : 1024 + xa * 4;
UINT32 ybase = ( r < 512 ) ? r * 2 : r * 2 - 1024;
int j, k;
for( j = 0; j < 2; j++ )
for( k = 0; k < 4; k++ )
{
UINT32 x = xbase + k, y = ybase + j;
UINT32 shift = ( ( x & 2 ) << 3 ) | ( ( ~x & 1 ) << 2 ) | ( ( ~y & 1 ) << 3 );
dec[ y * 2048 + x ] = ( word >> shift ) & 0x0f;
}
}
static void model2_tex_decode_all( void )
{
UINT32 i;
for( i = 0; i < 0x40000; i++ )
{
model2_tex_decode( model2_texdec0, i, model2_textureram0[ i ] );
model2_tex_decode( model2_texdec1, i, model2_textureram1[ i ] );
}
}
data16_t *model2_palram;
data16_t *model2_colorxlat;
UINT8 *model2_lumaram;
@@ -637,6 +673,7 @@ static WRITE32_HANDLER( tex0_w )
model2_textureram0[ offset >> 1 ] = ( model2_textureram0[ offset >> 1 ] & 0xffff0000 ) | ( data & 0xffff );
else
model2_textureram0[ offset >> 1 ] = ( model2_textureram0[ offset >> 1 ] & 0x0000ffff ) | ( ( data & 0xffff ) << 16 );
model2_tex_decode( model2_texdec0, offset >> 1, model2_textureram0[ offset >> 1 ] );
}
static WRITE32_HANDLER( tex1_w )
@@ -646,6 +683,7 @@ static WRITE32_HANDLER( tex1_w )
model2_textureram1[ offset >> 1 ] = ( model2_textureram1[ offset >> 1 ] & 0xffff0000 ) | ( data & 0xffff );
else
model2_textureram1[ offset >> 1 ] = ( model2_textureram1[ offset >> 1 ] & 0x0000ffff ) | ( ( data & 0xffff ) << 16 );
model2_tex_decode( model2_texdec1, offset >> 1, model2_textureram1[ offset >> 1 ] );
}
static READ32_HANDLER( tex0_r )
@@ -672,6 +710,7 @@ static WRITE32_HANDLER( tex2b_w )
{
data32_t *ram = ( offset & 0x80000 ) ? model2_textureram1 : model2_textureram0;
COMBINE_DATA( &ram[ offset & 0x3ffff ] );
model2_tex_decode( ( offset & 0x80000 ) ? model2_texdec1 : model2_texdec0, offset & 0x3ffff, ram[ offset & 0x3ffff ] );
}
static READ32_HANDLER( luma2b_r )
@@ -1750,6 +1789,7 @@ static void model2_postload( void )
{
int i;
model2_sound_postload();
model2_tex_decode_all();
m_palette_dirty = 1;
model2_palette_refresh();
for( i = 0; i < 4; i++ )
@@ -1779,6 +1819,8 @@ static DRIVER_INIT( model2a )
model2_backup1 = auto_malloc( 0x4000 );
model2_textureram0 = auto_malloc( 0x100000 );
model2_textureram1 = auto_malloc( 0x100000 );
model2_texdec0 = auto_malloc( 2048 * 1024 );
model2_texdec1 = auto_malloc( 2048 * 1024 );
model2_palram = auto_malloc( 0x4000 );
model2_colorxlat = auto_malloc( 0xc000 );
model2_lumaram = auto_malloc( 0x8000 );
@@ -1786,6 +1828,8 @@ static DRIVER_INIT( model2a )
memset( model2_ram2, 0, 0x40000 );
memset( model2_textureram0, 0, 0x100000 );
memset( model2_textureram1, 0, 0x100000 );
memset( model2_texdec0, 0, 2048 * 1024 );
memset( model2_texdec1, 0, 2048 * 1024 );
memset( model2_palram, 0, 0x4000 );
memset( model2_colorxlat, 0, 0xc000 );
memset( model2_lumaram, 0, 0x8000 );
+2
View File
@@ -11,6 +11,8 @@
extern data32_t *model2_bufferram;
extern data32_t *model2_textureram0;
extern data32_t *model2_textureram1;
extern UINT8 *model2_texdec0; /* the sheets one byte per texel, y * 2048 + x */
extern UINT8 *model2_texdec1;
extern data16_t *model2_palram;
extern data16_t *model2_colorxlat;
extern UINT8 *model2_lumaram;
+47
View File
@@ -777,6 +777,53 @@ void *memory_get_read_ptr(int cpunum, offs_t offset)
}
/*-------------------------------------------------
memory_get_read_range - like memory_get_read_ptr,
also giving the range of addresses around the
offset read from the same memory (*start, *len,
masked addresses); NULL when the offset is not
plain memory
-------------------------------------------------*/
void *memory_get_read_range(int cpunum, offs_t offset, offs_t *start, offs_t *len)
{
struct memport_data *memport = &cpudata[cpunum].mem;
struct handler_data *handlist = (memport->dbits == 32) ? rmemhandler32 : (memport->dbits == 16) ? rmemhandler16 : rmemhandler8;
UINT8 minbits = memport->abits - memport->ebits;
int l2bits = LEVEL2_BITS(memport->abits - minbits);
UINT8 entry;
offs_t first, last;
offset &= memport->mask;
entry = memport->read.table[LEVEL1_INDEX(offset, memport->abits, minbits)];
if (entry >= SUBTABLE_BASE)
{
/* the run of the same entry in the subtable, at most 4 KB each side */
UINT8 sub = entry;
offs_t base = offset & ~(((offs_t)1 << (l2bits + minbits)) - 1);
offs_t i = (offset - base) >> minbits, n = (offs_t)1 << l2bits, j;
offs_t lim = 0x1000 >> minbits;
entry = memport->read.table[LEVEL2_INDEX(sub, offset, memport->abits, minbits)];
for (j = i; j > 0 && i - j < lim && memport->read.table[LEVEL2_INDEX(sub, base + ((j - 1) << minbits), memport->abits, minbits)] == entry; j--) ;
first = base + (j << minbits);
for (j = i; j + 1 < n && j - i < lim && memport->read.table[LEVEL2_INDEX(sub, base + ((j + 1) << minbits), memport->abits, minbits)] == entry; j++) ;
last = base + (j << minbits) + (((offs_t)1 << minbits) - 1);
}
else
{
first = offset & ~(((offs_t)1 << (l2bits + minbits)) - 1);
last = first + (((offs_t)1 << (l2bits + minbits)) - 1);
}
/* 8-bit case: RAM/ROM/RAMROM */
if (entry > STATIC_RAM || (minbits == 0 && entry != STATIC_RAM))
return NULL;
*start = first;
*len = last - first + 1;
return &cpu_bankbase[entry][first - handlist[entry].offset];
}
/*-------------------------------------------------
memory_get_write_ptr - return a pointer to the
base of RAM associated with the given CPU
+1
View File
@@ -825,6 +825,7 @@ extern offs_t encrypted_opcode_start[],encrypted_opcode_end[];
/* ----- return a base pointer to memory ---- */
void * memory_find_base(int cpunum, offs_t offset);
void * memory_get_read_ptr(int cpunum, offs_t offset);
void * memory_get_read_range(int cpunum, offs_t offset, offs_t *start, offs_t *len);
void * memory_get_write_ptr(int cpunum, offs_t offset);
/* ----- dynamic memory mapping ----- */
+5
View File
@@ -815,6 +815,7 @@ static void SCSP_w16(unsigned int addr,unsigned short val)
else if(addr>=0x800)
{
SCSP->DSP.MPRO[(addr-0x800)/2]=val;
SCSP->DSP.Dirty=1;
if(addr==0xbf0)
SCSPDSP_Start(&SCSP->DSP);
}
@@ -1208,6 +1209,8 @@ static void SCSP_presave(void)
int i, s;
for (i = 0; i < 3; i++)
SCSP_timer_left[i] = SCSP_timer[i] ? timer_timeleft(SCSP_timer[i]) : TIME_NEVER;
for (i = 0; i < MAX_SCSP; i++)
SCSPs[i].DSP.Dirty = 1; /* MPRO was loaded */
for (i = 0; i < MAX_SCSP; i++)
for (s = 0; s < 32; s++)
{
@@ -1230,6 +1233,8 @@ static void SCSP_postload(void)
else
timer_adjust(SCSP_timer[i], TIME_NEVER, i, 0);
}
for (i = 0; i < MAX_SCSP; i++)
SCSPs[i].DSP.Dirty = 1; /* MPRO was loaded */
for (i = 0; i < MAX_SCSP; i++)
for (s = 0; s < 32; s++)
{
+117 -10
View File
@@ -36,6 +36,19 @@ struct _SCSPDSP
int Stopped;
int LastStep;
/* port: the steps that only multiply and accumulate (no write to TEMP,
MEMS, FRC, Y, ADRS, EFREG or the delay RAM), decoded from MPRO when
it changes (Dirty) and run by a short path in SCSPDSP_Step */
int Dirty;
struct
{
UINT8 mac; /* the step is one of them */
UINT8 tra, ira, coef;
UINT8 xsel, ysel;
UINT8 bsel; /* 0: zero, 1: ACC, 2: TEMP */
UINT8 negb;
} MAC[128];
};
/* sign-extend the low "bits" bits of v */
@@ -53,22 +66,29 @@ static INLINE INT32 SCSPDSP_clamp24( INT32 v )
return v;
}
/* port: leading zeros of a 12-bit value (12 for 0), for SCSPDSP_PACK */
static UINT8 SCSPDSP_lz12[ 4096 ];
static void SCSPDSP_InitTables( void )
{
int i;
for( i = 0; i < 4096; i++ )
{
int n = 0;
while( n < 12 && !( i & ( 0x800 >> n ) ) )
n++;
SCSPDSP_lz12[ i ] = n;
}
}
/* 24 bit value to the 16 bit floating point format of the delay RAM */
static UINT16 SCSPDSP_PACK( INT32 val )
{
UINT32 uval = (UINT32)val;
UINT32 sign = ( uval >> 23 ) & 1;
UINT32 temp = ( uval ^ ( uval << 1 ) ) & 0xffffff;
UINT32 exponent = 0;
int k;
for( k = 0; k < 12; k++ )
{
if( temp & 0x800000 )
break;
temp <<= 1;
exponent += 1;
}
/* the leading zeros of bits 23-12, up to 12 */
UINT32 exponent = SCSPDSP_lz12[ temp >> 12 ];
if( exponent < 12 )
uval = ( uval << exponent ) & 0x3fffff;
else
@@ -103,11 +123,45 @@ static INT32 SCSPDSP_UNPACK( UINT16 val )
static void SCSPDSP_Init( struct _SCSPDSP *DSP, UINT16 *ram, UINT32 ramsize )
{
SCSPDSP_InitTables();
memset( DSP, 0, sizeof( *DSP ) );
DSP->RAM = ram;
DSP->RAMmask = ( ramsize / 2 ) - 1;
DSP->RBL = 8 * 1024;
DSP->Stopped = 1;
DSP->Dirty = 1;
}
static void SCSPDSP_Decode( struct _SCSPDSP *DSP )
{
int step;
for( step = 0; step < 128; ++step )
{
UINT16 *IPtr = DSP->MPRO + ( step * 4 );
UINT32 TWT = ( IPtr[0] >> 7 ) & 0x01;
UINT32 IRA = ( IPtr[1] >> 6 ) & 0x3f;
UINT32 IWT = ( IPtr[1] >> 5 ) & 0x01;
UINT32 MWT = ( IPtr[2] >> 14 ) & 0x01;
UINT32 MRD = ( IPtr[2] >> 13 ) & 0x01;
UINT32 EWT = ( IPtr[2] >> 12 ) & 0x01;
UINT32 ADRL = ( IPtr[2] >> 7 ) & 0x01;
UINT32 FRCL = ( IPtr[2] >> 6 ) & 0x01;
UINT32 YRL = ( IPtr[2] >> 3 ) & 0x01;
UINT32 ZERO = ( IPtr[2] >> 1 ) & 0x01;
UINT32 BSEL = ( IPtr[2] >> 0 ) & 0x01;
DSP->MAC[step].mac = !TWT && !IWT && !EWT && !ADRL && !FRCL && !YRL && IRA <= 0x31 &&
!( ( MRD || MWT ) && ( step & 1 ) );
DSP->MAC[step].tra = ( IPtr[0] >> 8 ) & 0x7f;
DSP->MAC[step].ira = IRA;
DSP->MAC[step].coef = ( IPtr[3] >> 9 ) & 0x3f;
DSP->MAC[step].xsel = ( IPtr[1] >> 15 ) & 0x01;
DSP->MAC[step].ysel = ( IPtr[1] >> 13 ) & 0x03;
DSP->MAC[step].bsel = ZERO ? 0 : BSEL ? 1 : 2;
DSP->MAC[step].negb = ( IPtr[2] >> 2 ) & 0x01;
}
DSP->Dirty = 0;
}
static void SCSPDSP_Step( struct _SCSPDSP *DSP )
@@ -130,8 +184,59 @@ static void SCSPDSP_Step( struct _SCSPDSP *DSP )
memset( DSP->EFREG, 0, sizeof( DSP->EFREG ) );
if( DSP->Dirty )
SCSPDSP_Decode( DSP );
for( step = 0; step < DSP->LastStep; ++step )
{
if( DSP->MAC[step].mac )
{
/* the same as below for such a step: the shifter output and
INPUTS are not used */
INT32 TEMPV = 0;
UINT32 ysel = DSP->MAC[step].ysel;
if( !DSP->MAC[step].xsel || DSP->MAC[step].bsel == 2 )
TEMPV = SCSPDSP_sext( DSP->TEMP[( DSP->MAC[step].tra + DSP->DEC ) & 0x7f], 24 );
if( DSP->MAC[step].xsel )
{
UINT32 IRA = DSP->MAC[step].ira;
if( IRA <= 0x1f )
X = DSP->MEMS[IRA];
else if( IRA <= 0x2f )
X = DSP->MIXS[IRA - 0x20] << 4;
else
X = DSP->EXTS[IRA - 0x30] << 8;
X = SCSPDSP_sext( X, 24 );
}
else
X = TEMPV;
if( DSP->MAC[step].bsel == 0 )
B = 0;
else
{
B = DSP->MAC[step].bsel == 1 ? ACC : TEMPV;
if( DSP->MAC[step].negb )
B = 0 - B;
}
if( ysel == 0 )
Y = FRC_REG;
else if( ysel == 1 )
Y = DSP->COEF[DSP->MAC[step].coef] >> 3;
else if( ysel == 2 )
Y = ( Y_REG >> 11 ) & 0x1fff;
else
Y = ( Y_REG >> 4 ) & 0x0fff;
Y = SCSPDSP_sext( Y, 13 );
ACC = (INT32)( ( ( (INT64)X * (INT64)Y ) >> 12 ) + B );
continue;
}
{
UINT16 *IPtr = DSP->MPRO + ( step * 4 );
UINT32 TRA = ( IPtr[0] >> 8 ) & 0x7f;
@@ -284,6 +389,7 @@ static void SCSPDSP_Step( struct _SCSPDSP *DSP )
if( EWT )
DSP->EFREG[EWA] += SHIFTED >> 8;
}
}
--DSP->DEC;
memset( DSP->MIXS, 0, sizeof( DSP->MIXS ) );
@@ -301,4 +407,5 @@ static void SCSPDSP_Start( struct _SCSPDSP *DSP )
break;
}
DSP->LastStep = i + 1;
DSP->Dirty = 1;
}
+153 -67
View File
@@ -2285,6 +2285,8 @@ void model2_geo_parse( void )
/* drawn pixels carry an opaque marker in the top byte (the alpha of rgb_t in
MAME), so a black pixel (dark shadows) is not taken as transparent */
#define M2_PIXEL_OPAQUE 0xff000000
/* a pixel is kept as the 15-bit colour of the bitmap, from 8-bit components */
#define M2_PIXEL( r, g, b ) ( M2_PIXEL_OPAQUE | ( ( (r) >> 3 ) << 10 ) | ( ( (g) >> 3 ) << 5 ) | ( (b) >> 3 ) )
/* the 3D frame. A drawn pixel is never 0 (M2_PIXEL_OPAQUE), so the buffer
is its own fill map: polygons are drawn front to back and the first one
@@ -2298,8 +2300,11 @@ static int m_dirty_minx, m_dirty_maxx, m_dirty_miny, m_dirty_maxy; /* empty: min
typedef struct
{
const UINT32 *sheet;
const UINT8 *dsheet; /* the same sheet one byte per texel (model2_texdec0/1) */
UINT32 width, height; /* in texels, powers of two */
UINT32 x, y; /* position in the texture sheet */
int inside; /* the whole level is inside the sheet */
const UINT8 *dbase; /* inside, at an even x and y: its texel (0, 0) in dsheet */
} m2_tex_level;
typedef struct
@@ -2318,9 +2323,39 @@ typedef struct
m2_tex_level level[ 16 ]; /* 0: the microtexture, 1 + n: mipmap level n */
} m2_poly_extra_data;
/* the texel fetch and the textured span are inlined in the opaque and the
translucent spans */
#if defined(__GNUC__)
#define M2_ALWAYS_INLINE __attribute__((always_inline))
#else
#define M2_ALWAYS_INLINE
#endif
/* two neighbouring texels of a translucent texture, 4 bits each (index:
first << 4 | second), as the filtering takes them: the alpha component
(0x80 when not transparent, value 15) packed into the upper 16 bits,
and a transparent texel taking the luma of the other one, the first
one done first */
static UINT32 m2_tex_pair[ 256 ][ 2 ];
static void m2_tex_pair_init( void )
{
int i;
for( i = 0; i < 256; i++ )
{
UINT32 a = ( i >> 4 ) << 4, b = ( i & 15 ) << 4;
if( a != 0xf0 ) a |= 0x00800000;
if( b != 0xf0 ) b |= 0x00800000;
if( a == 0x000000f0 ) a = b & 0xff;
if( b == 0x000000f0 ) b = a & 0xff;
m2_tex_pair[ i ][ 0 ] = a;
m2_tex_pair[ i ][ 1 ] = b;
}
}
#define LERP( x, y, a ) ( ( (x) + ( ( ( (y) - (x) ) * (a) ) >> 8 ) ) & 0x00ff00ff )
static UINT32 fetch_bilinear_texel( const m2_poly_extra_data *object, int translucent, INT32 miplevel, INT32 u, INT32 v )
static INLINE M2_ALWAYS_INLINE UINT32 fetch_bilinear_texel( const m2_poly_extra_data *object, int translucent, INT32 miplevel, INT32 u, INT32 v )
{
UINT32 tex_wrap_x = object->texwrapx;
UINT32 tex_wrap_y = object->texwrapy;
@@ -2378,6 +2413,32 @@ static UINT32 fetch_bilinear_texel( const m2_poly_extra_data *object, int transl
v1 = v0, v0--, vfrac = 0x100;
}
/* the four texels from the sheet taken apart (model2_tex_decode), when
none of them is past the sheet: the columns with the parity of the
texture coordinate, as the word layout below */
if( level->dbase )
{
/* at an even position the columns and rows are in order */
const UINT8 *r0 = level->dbase + v0 * 2048;
const UINT8 *r1 = level->dbase + v1 * 2048;
tex00 = r0[ u0 ] << 4;
tex01 = r0[ u1 ] << 4;
tex10 = r1[ u0 ] << 4;
tex11 = r1[ u1 ] << 4;
}
else if( level->inside || ( tex_x + u1 < 2048 && tex_x + u0 < 2048 && tex_y + v0 < 1024 && tex_y + v1 < 1024 ) )
{
const UINT8 *d = level->dsheet;
UINT32 xc0 = ( ( tex_x + u0 ) & ~1 ) | ( u0 & 1 );
UINT32 xc1 = ( ( tex_x + u1 ) & ~1 ) | ( u1 & 1 );
UINT32 yr0 = ( ( ( tex_y + v0 ) & ~1 ) | ( v0 & 1 ) ) * 2048;
UINT32 yr1 = ( ( ( tex_y + v1 ) & ~1 ) | ( v1 & 1 ) ) * 2048;
tex00 = d[ yr0 + xc0 ] << 4;
tex01 = d[ yr0 + xc1 ] << 4;
tex10 = d[ yr1 + xc0 ] << 4;
tex11 = d[ yr1 + xc1 ] << 4;
}
else
/* the four 4-bit texels. Texture sheets are mapped as 2048x1024 but
stored in RAM as 1024x2048: past column 1024 a texel is in the other
half of the rows. A 32-bit word holds two 2x2 blocks: bit 1 of the
@@ -2409,17 +2470,16 @@ static UINT32 fetch_bilinear_texel( const m2_poly_extra_data *object, int transl
if( ( tex00 & tex01 & tex10 & tex11 ) == 0xf0 )
return 0x000000f0;
/* pack the alpha components into the upper 16 bits */
if( tex00 != 0xf0 ) tex00 |= 0x00800000;
if( tex01 != 0xf0 ) tex01 |= 0x00800000;
if( tex10 != 0xf0 ) tex10 |= 0x00800000;
if( tex11 != 0xf0 ) tex11 |= 0x00800000;
/* a transparent texel takes the luma of the neighbouring texel */
if( tex00 == 0x000000f0 ) tex00 = tex01 & 0xff;
if( tex01 == 0x000000f0 ) tex01 = tex00 & 0xff;
if( tex10 == 0x000000f0 ) tex10 = tex11 & 0xff;
if( tex11 == 0x000000f0 ) tex11 = tex10 & 0xff;
/* the alpha components packed into the upper 16 bits, a transparent
texel taking the luma of its neighbour: m2_tex_pair, per pair */
{
const UINT32 *q0 = m2_tex_pair[ tex00 | ( tex01 >> 4 ) ];
const UINT32 *q1 = m2_tex_pair[ tex10 | ( tex11 >> 4 ) ];
tex00 = q0[ 0 ];
tex01 = q0[ 1 ];
tex10 = q1[ 0 ];
tex11 = q1[ 1 ];
}
}
tex0x = LERP( tex00, tex01, ufrac );
@@ -2490,7 +2550,7 @@ static void draw_scanline_solid( int scanline, int startx, int stopx, const m2_p
tr = model2_gamma_table[ colortable_r[ luma ] & 0xff ];
tg = model2_gamma_table[ colortable_g[ luma ] & 0xff ];
tb = model2_gamma_table[ colortable_b[ luma ] & 0xff ];
color = M2_PIXEL_OPAQUE | ( tr << 16 ) | ( tg << 8 ) | tb;
color = M2_PIXEL( tr, tg, tb );
x = startx;
dx = object->checker ? 2 : 1;
@@ -2504,7 +2564,13 @@ static void draw_scanline_solid( int scanline, int startx, int stopx, const m2_p
}
}
static void draw_scanline_tex( int scanline, int startx, int stopx, float ooz, float uoz, float voz,
/* the pixels of the texels of the last colours drawn (0: not worked out
yet; a pixel always has M2_PIXEL_OPAQUE). Cleared at every frame, since
the luma RAM, the colour tables and the gamma can change between frames */
static UINT32 m_texcolor[ 128 ];
static UINT32 m_texcolor_key[ 3 ];
static INLINE M2_ALWAYS_INLINE void draw_scanline_tex( int scanline, int startx, int stopx, float ooz, float uoz, float voz,
float dooz, float duoz, float dvoz, const m2_poly_extra_data *object, int translucent )
{
UINT32 *p = &m_destmap[ scanline * DEST_W ];
@@ -2515,9 +2581,18 @@ static void draw_scanline_tex( int scanline, int startx, int stopx, float ooz, f
UINT32 lumabase = object->lumabase;
INT32 max_level = object->max_level;
UINT32 obj_luma = object->luma;
INT32 texlod = object->texlod;
int utex = object->utex, utexminlod = object->utexminlod;
int x, dx;
colorbase = model2_palram[ ( colorbase + 0x1000 ) & 0x1fff ] & 0x7fff;
if( colorbase != m_texcolor_key[ 0 ] || lumabase != m_texcolor_key[ 1 ] || obj_luma != m_texcolor_key[ 2 ] )
{
memset( m_texcolor, 0, sizeof( m_texcolor ) );
m_texcolor_key[ 0 ] = colorbase;
m_texcolor_key[ 1 ] = lumabase;
m_texcolor_key[ 2 ] = obj_luma;
}
colortable_r += ( ( colorbase >> 0 ) & 0x1f ) << 8;
colortable_g += ( ( colorbase >> 5 ) & 0x1f ) << 8;
colortable_b += ( ( colorbase >> 10 ) & 0x1f ) << 8;
@@ -2550,7 +2625,7 @@ static void draw_scanline_tex( int scanline, int startx, int stopx, float ooz, f
continue;
z = 1.0f / ooz;
mml = -object->texlod + fast_log2( z );
mml = -texlod + fast_log2( z );
level = mml >> 7;
if( level < 0 ) level = 0;
if( level > max_level ) level = max_level;
@@ -2562,17 +2637,23 @@ static void draw_scanline_tex( int scanline, int startx, int stopx, float ooz, f
if( mml > 0 && level < max_level )
{
UINT32 t2 = fetch_bilinear_texel( object, translucent, level + 1, u, v );
UINT32 t2;
INT32 frac = ( mml & 127 ) << 1;
/* translucent, four transparent texels and less than half of the
next level: under 50% whatever that one gives (its alpha is at
most 0x80), discarded below */
if( translucent && t == 0x000000f0 && frac < 128 )
continue;
t2 = fetch_bilinear_texel( object, translucent, level + 1, u, v );
t = LERP( t, t2, frac );
}
else if( object->utex && mml < 0 && ( !translucent || t >= 0x00010000 ) )
else if( utex && mml < 0 && ( !translucent || t >= 0x00010000 ) )
{
/* (with no alpha, a translucent texel stays under 50% after this
blend, and is discarded below) */
/* microtexture; blend up to almost 50% */
UINT32 t2 = fetch_bilinear_texel( object, translucent, -1, u, v );
INT32 frac = -mml >> object->utexminlod;
INT32 frac = -mml >> utexminlod;
if( frac > 127 ) frac = 127;
t = LERP( t, t2, frac );
}
@@ -2585,19 +2666,41 @@ static void draw_scanline_tex( int scanline, int startx, int stopx, float ooz, f
t &= 0xff;
}
/* 8 bits of filtered texel, 128 entries in the translator map */
luma = (UINT8)( ( (UINT32)model2_lumaram[ ( lumabase + ( t >> 1 ) ) & 0x7fff ] * obj_luma ) >> 8 );
if( luma > 0x3f )
luma = 0x3f;
/* 8 bits of filtered texel, 128 entries in the translator map; the
pixel of each entry is worked out once for the polygon's colours */
{
UINT32 c = m_texcolor[ t >> 1 ];
if( !c )
{
luma = (UINT8)( ( (UINT32)model2_lumaram[ ( lumabase + ( t >> 1 ) ) & 0x7fff ] * obj_luma ) >> 8 );
if( luma > 0x3f )
luma = 0x3f;
tr = model2_gamma_table[ colortable_r[ luma ] & 0xff ];
tg = model2_gamma_table[ colortable_g[ luma ] & 0xff ];
tb = model2_gamma_table[ colortable_b[ luma ] & 0xff ];
tr = model2_gamma_table[ colortable_r[ luma ] & 0xff ];
tg = model2_gamma_table[ colortable_g[ luma ] & 0xff ];
tb = model2_gamma_table[ colortable_b[ luma ] & 0xff ];
p[ x ] = M2_PIXEL_OPAQUE | ( tr << 16 ) | ( tg << 8 ) | tb;
c = M2_PIXEL( tr, tg, tb );
m_texcolor[ t >> 1 ] = c;
}
p[ x ] = c;
}
}
}
/* the opaque and the translucent spans, each with the texel fetches inlined */
static void draw_scanline_tex_opaque( int scanline, int startx, int stopx, float ooz, float uoz, float voz,
float dooz, float duoz, float dvoz, const m2_poly_extra_data *object )
{
draw_scanline_tex( scanline, startx, stopx, ooz, uoz, voz, dooz, duoz, dvoz, object, 0 );
}
static void draw_scanline_tex_trans( int scanline, int startx, int stopx, float ooz, float uoz, float voz,
float dooz, float duoz, float dvoz, const m2_poly_extra_data *object )
{
draw_scanline_tex( scanline, startx, stopx, ooz, uoz, voz, dooz, duoz, dvoz, object, 1 );
}
/* scanline rasterization of a convex polygon, pixel centres at +0.5 */
static void rasterize_polygon( const poly_vertex *v, int n, int minx, int maxx, int miny, int maxy,
const m2_poly_extra_data *extra, int textured, int translucent )
@@ -2680,9 +2783,9 @@ static void rasterize_polygon( const poly_vertex *v, int n, int minx, int maxx,
if( textured )
{
float off = ( startx + 0.5f ) - xl;
draw_scanline_tex( y, startx, stopx,
( translucent ? draw_scanline_tex_trans : draw_scanline_tex_opaque )( y, startx, stopx,
pl[ 0 ] + dpdx[ 0 ] * off, pl[ 1 ] + dpdx[ 1 ] * off, pl[ 2 ] + dpdx[ 2 ] * off,
dpdx[ 0 ], dpdx[ 1 ], dpdx[ 2 ], extra, translucent );
dpdx[ 0 ], dpdx[ 1 ], dpdx[ 2 ], extra );
}
else
draw_scanline_solid( y, startx, stopx, extra, translucent );
@@ -2743,18 +2846,24 @@ static void model2_3d_render( polygon *poly, const struct rectangle *cliprect )
int level;
extra.max_level = 30 - count_leading_zeros32( minwh );
extra.level[ 0 ].sheet = extra.texsheet[ 1 ];
extra.level[ 0 ].dsheet = ( extra.texsheet[ 1 ] == model2_textureram0 ) ? model2_texdec0 : model2_texdec1;
extra.level[ 0 ].width = 128;
extra.level[ 0 ].height = 128;
extra.level[ 0 ].x = extra.utexx;
extra.level[ 0 ].y = extra.utexy;
extra.level[ 0 ].inside = 1;
extra.level[ 0 ].dbase = extra.level[ 0 ].dsheet + extra.utexy * 2048 + extra.utexx;
for( level = 0; level <= extra.max_level; level++ )
{
m2_tex_level *l = &extra.level[ level + 1 ];
l->sheet = extra.texsheet[ level & 1 ];
l->dsheet = ( l->sheet == model2_textureram0 ) ? model2_texdec0 : model2_texdec1;
l->width = extra.texwidth >> level;
l->height = extra.texheight >> level;
l->x = ( ( extra.texx - 2048 ) >> level ) & 2047;
l->y = ( ( extra.texy - 1024 ) >> level ) & 1023;
l->inside = l->x + l->width <= 2048 && l->y + l->height <= 1024;
l->dbase = ( l->inside && !( ( l->x | l->y ) & 1 ) ) ? l->dsheet + l->y * 2048 + l->x : NULL;
}
}
@@ -2791,11 +2900,14 @@ static void composite_3d( struct mame_bitmap *bitmap, const struct rectangle *cl
{
UINT16 *dst = (UINT16 *)bitmap->line[ y ];
const UINT32 *src = &m_destmap[ y * DEST_W ];
const UINT8 *pri = (const UINT8 *)priority_bitmap->line[ y ];
for( x = minx; x <= maxx; x++ )
{
UINT32 c = src[ x ];
if( c )
dst[ x ] = ( ( c >> 9 ) & 0x7c00 ) | ( ( c >> 6 ) & 0x03e0 ) | ( ( c >> 3 ) & 0x001f );
/* not over a tile pixel in front of the polygons: the highest
bit of its priority value is odd (see VIDEO_UPDATE) */
if( c && ( pri[ x ] & 0xaa ) <= ( pri[ x ] & 0x55 ) )
dst[ x ] = (UINT16)c;
}
}
}
@@ -2822,6 +2934,7 @@ static void render_polygons( struct mame_bitmap *bitmap, const struct rectangle
memset( &m_destmap[ y * DEST_W + m_dirty_minx ], 0, ( m_dirty_maxx - m_dirty_minx + 1 ) * sizeof( UINT32 ) );
m_dirty_minx = m_dirty_miny = DEST_W;
m_dirty_maxx = m_dirty_maxy = -1;
memset( m_texcolor, 0, sizeof( m_texcolor ) );
for( window = raster->cur_window; window >= 0; window-- )
{
@@ -2862,8 +2975,6 @@ READ32_HANDLER( model2_polygon_count_r )
}
/* the tile layers, mixed (see VIDEO_UPDATE) */
static struct mame_bitmap *m_tilemap; /* the tile layers, mixed */
static UINT8 m_tile_front[ 256 ];
/* light gun crosshairs (Virtua Cop, Virtua Cop 2, The House of the Dead):
the gun ports span the whole screen, as PORT_CROSSHAIR( X/Y, 1.0, 0.0 )
@@ -2921,23 +3032,11 @@ static void draw_gun_crosshairs( struct mame_bitmap *bitmap, const struct rectan
VIDEO_START( model2 )
{
int i;
m2_tex_pair_init();
if( sys24_tile_vh_start( 0x3fff ) )
return 1;
m_tilemap = auto_bitmap_alloc( Machine->drv->screen_width, Machine->drv->screen_height );
if( !m_tilemap )
return 1;
/* priority bitmap value -> the winning tile pixel is in front of the polygons */
for( i = 0; i < 256; i++ )
{
int b = 7;
while( b > 0 && !( i & ( 1 << b ) ) )
b--;
m_tile_front[ i ] = b & 1;
}
m_raster = auto_malloc( sizeof( raster_state ) );
m_geo = auto_malloc( sizeof( geo_state ) );
memset( m_raster, 0, sizeof( raster_state ) );
@@ -3006,25 +3105,13 @@ VIDEO_START( model2 )
A: on the Virtua Cop 2 stage select the city map (B, priority set) hid
"SHOOT TO SELECT" (A).
The tiles are drawn back to front into m_tilemap, each layer and
priority setting its own bit in the priority bitmap: bit 2*depth+prio,
with depth 0 for the back layer. The highest bit set gives the winning
pixel and its priority.
The tiles are drawn back to front straight into the bitmap, each
layer and priority setting its own bit in the priority bitmap: bit
2*depth+prio, with depth 0 for the back layer. The highest bit set
gives the winning pixel and its priority: the polygons are then drawn
where it is even (behind them) or there is no tile pixel. A highest bit
odd is the same as the odd bits above the even ones.
*/
static void copy_tiles( struct mame_bitmap *bitmap, const struct rectangle *cliprect, int front )
{
int x, y;
for( y = cliprect->min_y; y <= cliprect->max_y; y++ )
{
UINT16 *dst = (UINT16 *)bitmap->line[ y ];
const UINT16 *src = (const UINT16 *)m_tilemap->line[ y ];
const UINT8 *pri = (const UINT8 *)priority_bitmap->line[ y ];
for( x = cliprect->min_x; x <= cliprect->max_x; x++ )
if( pri[ x ] && m_tile_front[ pri[ x ] ] == front )
dst[ x ] = src[ x ];
}
}
VIDEO_UPDATE( model2 )
{
int layer;
@@ -3039,13 +3126,12 @@ VIDEO_UPDATE( model2 )
for( layer = 3; layer >= 0; layer-- )
{
int depth = 3 - layer;
sys24_tile_draw( m_tilemap, cliprect, layer << 1, depth * 2, 0 );
sys24_tile_draw( m_tilemap, cliprect, ( layer << 1 ) | 1, depth * 2 + 1, 0 );
sys24_tile_draw( bitmap, cliprect, layer << 1, depth * 2, 0 );
sys24_tile_draw( bitmap, cliprect, ( layer << 1 ) | 1, depth * 2 + 1, 0 );
}
copy_tiles( bitmap, cliprect, 0 );
/* the polygons go over the tile pixels behind them only */
render_polygons( bitmap, cliprect );
copy_tiles( bitmap, cliprect, 1 );
draw_gun_crosshairs( bitmap, cliprect );
}
+75 -12
View File
@@ -368,6 +368,25 @@ static void sys24_tile_draw_rect(struct mame_bitmap *bm, struct mame_bitmap *tm,
find the topmost tile layer of each pixel.
*/
/* eight bytes of the transparency bitmap at once: nonzero when one of them is
the byte repeated in every lane of 'pat' (the usual zero byte test) */
static INLINE int sys24_trans8_has(const UINT8 *p, UINT64 pat)
{
const UINT64 ones = ((UINT64)0x01010101 << 32) | 0x01010101;
UINT64 w;
memcpy(&w, p, 8);
w ^= pat;
return ((w - ones) & ~w & (ones << 7)) != 0;
}
/* eight bytes of the transparency bitmap all the byte of 'pat' */
static INLINE int sys24_trans8_all(const UINT8 *p, UINT64 pat)
{
UINT64 w;
memcpy(&w, p, 8);
return w == pat;
}
static void sys24_tile_draw_rect_rgb(struct mame_bitmap *bm, struct mame_bitmap *tm, struct mame_bitmap *dm, const UINT16 *mask,
UINT16 tpri, UINT8 lpri, int win, int sx, int sy, int xx1, int yy1, int xx2, int yy2)
{
@@ -377,8 +396,11 @@ static void sys24_tile_draw_rect_rgb(struct mame_bitmap *bm, struct mame_bitmap
UINT16 *dest = dm->base;
UINT8 *prib = priority_bitmap->base;
pen_t *pens = Machine->pens;
UINT64 tpat;
tpri |= TILE_FLAG_FG_OPAQUE;
/* tpri in every byte (a value over 0xff never matches: no group is skipped) */
tpat = (((UINT64)0x01010101 << 32) | 0x01010101) * (tpri & 0xff);
dest += yy1*dm->rowpixels + xx1;
prib += yy1*priority_bitmap->rowpixels + xx1;
@@ -410,14 +432,26 @@ static void sys24_tile_draw_rect_rgb(struct mame_bitmap *bm, struct mame_bitmap
/* Fast paths for the 128-pixels without side clipping case */
if(!m) {
/* 1- 128 pixels from this layer */
/* 1- 128 pixels from this layer, eight at a time: a group
with no pixel of this priority is skipped */
int x;
for(x=0; x<128; x++) {
if(*srct++ == tpri)
{ *dst = pens[*src]; *pr |= lpri; }
src++;
dst++;
pr++;
for(x=0; x<128; x+=8) {
if(tpri > 0xff || sys24_trans8_has(srct, tpat)) {
int xx;
if(tpri <= 0xff && sys24_trans8_all(srct, tpat)) {
/* eight pixels of this priority */
for(xx=0; xx<8; xx++)
{ dst[xx] = pens[src[xx]]; pr[xx] |= lpri; }
} else {
for(xx=0; xx<8; xx++)
if(srct[xx] == tpri)
{ dst[xx] = pens[src[xx]]; pr[xx] |= lpri; }
}
}
src += 8;
srct += 8;
dst += 8;
pr += 8;
}
} else if(m == 0xffff) {
@@ -431,11 +465,17 @@ static void sys24_tile_draw_rect_rgb(struct mame_bitmap *bm, struct mame_bitmap
/* 3- 128 pixels from both layers */
int x;
for(x=0; x<128; x+=8) {
if(!(m & 0x8000)) {
if(!(m & 0x8000) && (tpri > 0xff || sys24_trans8_has(srct, tpat))) {
int xx;
for(xx=0; xx<8; xx++)
if(srct[xx] == tpri)
{ dst[xx] = pens[src[xx]]; pr[xx] |= lpri; }
if(tpri <= 0xff && sys24_trans8_all(srct, tpat)) {
/* eight pixels of this priority */
for(xx=0; xx<8; xx++)
{ dst[xx] = pens[src[xx]]; pr[xx] |= lpri; }
} else {
for(xx=0; xx<8; xx++)
if(srct[xx] == tpri)
{ dst[xx] = pens[src[xx]]; pr[xx] |= lpri; }
}
}
src += 8;
srct += 8;
@@ -448,7 +488,30 @@ static void sys24_tile_draw_rect_rgb(struct mame_bitmap *bm, struct mame_bitmap
/* Clipped path */
int llx1 = llx >= 128 ? 128 : llx;
if(!m) {
if(!m && !cur_x && !(llx1 & 7)) {
/* 1- the last pixels of the line from this layer, a
whole number of groups of eight: as the fast path */
int x;
for(x=0; x<llx1; x+=8) {
if(tpri > 0xff || sys24_trans8_has(srct, tpat)) {
int xx;
if(tpri <= 0xff && sys24_trans8_all(srct, tpat)) {
/* eight pixels of this priority */
for(xx=0; xx<8; xx++)
{ dst[xx] = pens[src[xx]]; pr[xx] |= lpri; }
} else {
for(xx=0; xx<8; xx++)
if(srct[xx] == tpri)
{ dst[xx] = pens[src[xx]]; pr[xx] |= lpri; }
}
}
src += 8;
srct += 8;
dst += 8;
pr += 8;
}
} else if(!m) {
/* 1- 128 pixels from this layer */
int x;
for(x = cur_x; x<llx1; x++) {