/*
The MLC graphics hardware is quite complicated - the usual method of having 'object ram' that
controls sprites is expanded into object ram that controls sprite blocks that may be stored
in RAM or ROM. Each tile in a block may be specified explicitly via a display list in ROM or
calculated as part of a block offset.
Blocks can be scaled and subpositioned, and are usually 4-6bpp but two 4bpp blocks can be
combined into 8bpp with a flag.
*/
#include "emu.h"
#include "includes/deco_mlc.h"
//extern int mlc_raster_table[9][256];
//extern UINT32 mlc_clipper[32];
//static bitmap_t *temp_bitmap;
/******************************************************************************/
VIDEO_START( mlc )
{
deco_mlc_state *state = machine.driver_data<deco_mlc_state>();
if (machine.gfx[0]->color_granularity==16)
state->m_colour_mask=0x7f;
else if (machine.gfx[0]->color_granularity==32)
state->m_colour_mask=0x3f;
else
state->m_colour_mask=0x1f;
// temp_bitmap = auto_bitmap_alloc( machine, 512, 512, BITMAP_FORMAT_RGB32 );
state->m_mlc_buffered_spriteram = auto_alloc_array(machine, UINT32, 0x3000/4);
}
#ifdef UNUSED_FUNCTION
static void blitRaster(running_machine &machine, bitmap_t *bitmap, int rasterMode)
{
deco_mlc_state *state = machine.driver_data<deco_mlc_state>();
int x,y;
for (y=0; y<256; y++) //todo
{
UINT32* src=BITMAP_ADDR32(temp_bitmap, y&0x1ff, 0);
UINT32* dst=BITMAP_ADDR32(bitmap, y, 0);
UINT32 xptr=(state->m_mlc_raster_table[0][y]<<13);
if (machine.input().code_pressed(KEYCODE_X))
xptr=0;
for (x=0; x<320; x++)
{
if (src[x])
dst[x]=src[(xptr>>16)&0x1ff];
//if (machine.input().code_pressed(KEYCODE_X))
// xptr+=0x10000;
//else if(rasterHackTest[0][y]<0)
xptr+=0x10000 - ((state->m_mlc_raster_table[2][y]&0x3ff)<<5);
//else
// xptr+=0x10000 + (state->m_mlc_raster_table[0][y]<<5);
}
}
}
#endif
static void mlc_drawgfxzoom(
bitmap_t *dest_bmp,const rectangle *clip,const gfx_element *gfx,
UINT32 code1,UINT32 code2, UINT32 color,int flipx,int flipy,int sx,int sy,
int transparent_color,int use8bpp,
int scalex, int scaley,int alpha)
{
rectangle myclip;
if (!scalex || !scaley) return;
/*
scalex and scaley are 16.16 fixed point numbers
1<<15 : shrink to 50%
1<<16 : uniform scale
1<<17 : double to 200%
*/
/* KW 991012 -- Added code to force clip to bitmap boundary */
if(clip)
{
myclip.min_x = clip->min_x;
myclip.max_x = clip->max_x;
myclip.min_y = clip->min_y;
myclip.max_y = clip->max_y;
if (myclip.min_x < 0) myclip.min_x = 0;
if (myclip.max_x >= dest_bmp->width) myclip.max_x = dest_bmp->width-1;
if (myclip.min_y < 0) myclip.min_y = 0;
if (myclip.max_y >= dest_bmp->height) myclip.max_y = dest_bmp->height-1;
clip=&myclip;
}
{
if( gfx )
{
const pen_t *pal = &gfx->machine().pens[gfx->color_base + gfx->color_granularity * (color % gfx->total_colors)];
const UINT8 *code_base1 = gfx_element_get_data(gfx, code1 % gfx->total_elements);
const UINT8 *code_base2 = gfx_element_get_data(gfx, code2 % gfx->total_elements);
int sprite_screen_height = (scaley*gfx->height+(sy&0xffff))>>16;
int sprite_screen_width = (scalex*gfx->width+(sx&0xffff))>>16;
sx>>=16;
sy>>=16;
if (sprite_screen_width && sprite_screen_height)
{
/* compute sprite increment per screen pixel */
int dx = (gfx->width<<16)/sprite_screen_width;
int dy = (gfx->height<<16)/sprite_screen_height;
int ex = sx+sprite_screen_width;
int ey = sy+sprite_screen_height;
int x_index_base;
int y_index;
if( flipx )
{
x_index_base = (sprite_screen_width-1)*dx;
dx = -dx;
}
else
{
x_index_base = 0;
}
if( flipy )
{
y_index = (sprite_screen_height-1)*dy;
dy = -dy;
}
else
{
y_index = 0;
}
if( clip )
{
if( sx < clip->min_x)
{ /* clip left */
int pixels = clip->min_x-sx;
sx += pixels;
x_index_base += pixels*dx;
}
if( sy < clip->min_y )
{ /* clip top */
int pixels = clip->min_y-sy;
sy += pixels;
y_index += pixels*dy;
}
/* NS 980211 - fixed incorrect clipping */
if( ex > clip->max_x+1 )
{ /* clip right */
int pixels = ex-clip->max_x-1;
ex -= pixels;
}
if( ey > clip->max_y+1 )
{ /* clip bottom */
int pixels = ey-clip->max_y-1;
ey -= pixels;
}
}
if( ex>sx )
{ /* skip if inner loop doesn't draw anything */
int y;
/* case 1: no alpha */
if (alpha == 0xff)
{
{
for( y=sy; y<ey; y++ )
{
const UINT8 *source1 = code_base1 + (y_index>>16) * gfx->line_modulo;
const UINT8 *source2 = code_base2 + (y_index>>16) * gfx->line_modulo;
UINT32 *dest = BITMAP_ADDR32(dest_bmp, y, 0);
int x, x_index = x_index_base;
for( x=sx; x<ex; x++ )
{
int c = source1[x_index>>16];
if (use8bpp)
c=(c<<4)|source2[x_index>>16];
if( c != transparent_color ) dest[x] = pal[c];
x_index += dx;
}
y_index += dy;
}
}
}
/* case 6: alpha blended */
else
{
{
for( y=sy; y<ey; y++ )
{
const UINT8 *source = code_base1 + (y_index>>16) * gfx->line_modulo;
UINT32 *dest = BITMAP_ADDR32(dest_bmp, y, 0);
int x, x_index = x_index_base;
for( x=sx; x<ex; x++ )
{
int c = source[x_index>>16];
if( c != transparent_color ) dest[x] = alpha_blend_r32(dest[x], 0, alpha); //pal[c]);
x_index += dx;
}
y_index += dy;
}
}
}
}
}
}
}
}
static void draw_sprites(running_machine& machine, bitmap_t *bitmap,const rectangle *cliprect)
{
deco_mlc_state *state = machine.driver_data<deco_mlc_state>();
UINT32 *index_ptr=0;
int offs,fx=0,fy=0,x,y,color,colorOffset,sprite,indx,h,w,bx,by,fx1,fy1;
int xoffs,yoffs;
UINT8 *rom = machine.region("gfx2")->base() + 0x20000, *index_ptr8;
UINT8 *rawrom = machine.region("gfx2")->base();
int blockIsTilemapIndex=0;
int sprite2=0,indx2=0,use8bppMode=0;
int yscale,xscale;
int ybase,yinc;
int alpha;
int useIndicesInRom=0;
int hibits=0;
int tileFormat=0;
// int rasterMode=0;
// int lastRasterMode=0;
// int rasterDirty=0;
int clipper=0;
rectangle user_clip;
UINT32* mlc_spriteram=state->m_mlc_buffered_spriteram; // spriteram32
for (offs = (0x3000/4)-8; offs>=0; offs-=8)
{
if ((mlc_spriteram[offs+0]&0x8000)==0)
continue;
if ((mlc_spriteram[offs+1]&0x2000) && (machine.primary_screen->frame_number() & 1))
continue;
/*
Spriteram (1) format (16 bit):
Word 0: 0x8000 - Sprite enable
0x4000 - Use ROM or RAM for spriteram 2 (really top bit of index)
0x3fff - Index into spriteram 2
Word 1: 0x8000 - X flip
0x4000 - Y flip
0x2000 - Auto-flicker (display sprite only every other frame)
0x1000 - If set combine this 4bpp sprite & next one, into 8bpp sprite
0x0800 - ? (Not seen used anywhere)
0x0400 - Use raster IRQ lookup table when drawing object
0x0300 - Selects clipping window to use
0x00ff - Colour/alpha shadow enable
Word 2: 0x07ff - Y position
Word 3: 0x07ff - X position
Word 4: 0x03ff - X scale
Word 5: 0x03ff - Y scale
Spriteram (2) format (16 bit):
Word 0: 0xe000 - ? (Always 0x2000?)
0x1000 - X flip
0x0f00 - Width in tiles (0==16)
0x00ff - X position offset
Word 1: 0xe000 - ? (Always 0x2000?)
0x1000 - Y flip
0x0f00 - Height in tiles (0==16)
0x00ff - Y position offset
Word 2: 0xff00 - ? (Always 0?)
0x00c0 - If set use tile index as pointer into tile index array, else use as tile index directly
0x0080 - If set tile index array format is 12 bit tile, 4 bit colour
0x0040 - If set tile index array is 16 bit tile, 0 bit colour
0x003c - Hi-bits of tile index after array lookup
0x0003 - Hi-bits of tile index, selects ROM or RAM for array
Word 3: 0xffff - Low-bits of tile index
*/
y = mlc_spriteram[offs+2]&0x7ff;
x = mlc_spriteram[offs+3]&0x7ff;
if (x&0x400) x=-(0x400-(x&0x3ff));
if (y&0x400) y=-(0x400-(y&0x3ff));
fx = mlc_spriteram[offs+1]&0x8000;
fy = mlc_spriteram[offs+1]&0x4000;
color = mlc_spriteram[offs+1]&0xff;
// rasterMode = (mlc_spriteram[offs+1]>>10)&0x1;
clipper = (mlc_spriteram[offs+1]>>8)&0x3;
indx = mlc_spriteram[offs+0]&0x3fff;
yscale = mlc_spriteram[offs+4]&0x3ff;
xscale = mlc_spriteram[offs+5]&0x3ff;
colorOffset = 0;
/* Clip windows - this mapping seems odd, but is correct for Skull Fang and StadHr96,
however there are space for 8 clipping windows, where is the high bit? (Or is it ~0x400?) */
clipper=((clipper&2)>>1)|((clipper&1)<<1); // Swap low two bits
user_clip.min_y=state->m_mlc_clip_ram[(clipper*4)+0];
user_clip.max_y=state->m_mlc_clip_ram[(clipper*4)+1];
user_clip.min_x=state->m_mlc_clip_ram[(clipper*4)+2];
user_clip.max_x=state->m_mlc_clip_ram[(clipper*4)+3];
sect_rect(&user_clip, cliprect);
/* Any colours out of range (for the bpp value) trigger 'shadow' mode */
if (color & (state->m_colour_mask+1))
alpha=0x80;
else
alpha=0xff;
color&=state->m_colour_mask;
/* If this bit is set, combine this block with the next one */
if (mlc_spriteram[offs+1]&0x1000) {
use8bppMode=1;
/* In 8bpp the palette base is stored in the next block */
if (offs-8>=0) {
color = (mlc_spriteram[offs+1-8]&0x7f);
indx2 = mlc_spriteram[offs+0-8]&0x3fff;
}
} else
use8bppMode=0;
/* Lookup tiles/size in sprite index ram OR in the lookup rom */
if (mlc_spriteram[offs+0]&0x4000) {
index_ptr8=rom + indx*8; /* Byte ptr */
h=(index_ptr8[1]>>0)&0xf;
w=(index_ptr8[3]>>0)&0xf;
if (!h) h=16;
if (!w) w=16;
sprite = (index_ptr8[7]<<8)|index_ptr8[6];
sprite |= (index_ptr8[4]&3)<<16;
if (use8bppMode) {
UINT8* index_ptr28=rom + indx2*8;
sprite2=(index_ptr28[7]<<8)|index_ptr28[6];
}
//unused byte 5
yoffs=index_ptr8[0]&0xff;
xoffs=index_ptr8[2]&0xff;
fy1=(index_ptr8[1]&0xf0)>>4;
fx1=(index_ptr8[3]&0xf0)>>4;
tileFormat=index_ptr8[4]&0x80;
if (index_ptr8[4]&0xc0)
blockIsTilemapIndex=1;
else
blockIsTilemapIndex=0;
useIndicesInRom=0;
} else {
indx&=0x1fff;
index_ptr=state->m_mlc_vram + indx*4;
h=(index_ptr[0]>>8)&0xf;
w=(index_ptr[1]>>8)&0xf;
if (!h) h=16;
if (!w) w=16;
if (use8bppMode) {
UINT32* index_ptr2=state->m_mlc_vram + ((indx2*4)&0x7fff);
sprite2=((index_ptr2[2]&0x3)<<16) | (index_ptr2[3]&0xffff);
}
sprite = ((index_ptr[2]&0x3)<<16) | (index_ptr[3]&0xffff);
if (index_ptr[2]&0xc0)
blockIsTilemapIndex=1;
else
blockIsTilemapIndex=0;
tileFormat=index_ptr[2]&0x80;
hibits=(index_ptr[2]&0x3c)<<10;
useIndicesInRom=index_ptr[2]&3;
yoffs=index_ptr[0]&0xff;
xoffs=index_ptr[1]&0xff;
fy1=(index_ptr[0]&0xf000)>>12;
fx1=(index_ptr[1]&0xf000)>>12;
}
if(fx1&1) fx^=0x8000;
if(fy1&1) fy^=0x4000;
ybase=y<<16;
if (fy)
ybase+=(yoffs-15) * (yscale<<8);
else
ybase-=yoffs * (yscale<<8);
yinc=(yscale<<8)*16;
if (fy) yinc=-yinc;
for (by=0; by<h; by++) {
int xbase=x<<16;
int xinc=(xscale<<8)*16;
if (fx)
xbase+=(xoffs-15) * (xscale<<8);
else
xbase-=xoffs * (xscale<<8);
if (fx) xinc=-xinc;
for (bx=0; bx<w; bx++) {
int tile=sprite;
int tile2=sprite2;
if (blockIsTilemapIndex) {
if (useIndicesInRom)
{
const UINT8* ptr=rawrom+(sprite*2);
tile=(*ptr) + ((*(ptr+1))<<8);
if (use8bppMode) {
const UINT8* ptr2=rawrom+(sprite2*2);
tile2=(*ptr2) + ((*(ptr2+1))<<8);
}
else
{
tile2=0;
}
if (tileFormat)
{
colorOffset=(tile&0xf000)>>12;
tile=(tile&0x0fff)|hibits;
tile2=(tile2&0x0fff)|hibits;
}
else
{
colorOffset=0;
tile=(tile&0xffff)|(hibits<<2);
tile2=(tile2&0xffff)|(hibits<<2);
}
}
else
{
const UINT32* ptr=state->m_mlc_vram + ((sprite)&0x7fff);
tile=(*ptr)&0xffff;
if (tileFormat)
{
colorOffset=(tile&0xf000)>>12;
tile=(tile&0x0fff)|hibits;
}
else
{
colorOffset=0;
tile=(tile&0xffff)|(hibits<<2);
}
tile2=0;
}
}
// if (rasterMode)
// rasterDirty=1;
mlc_drawgfxzoom(
/*rasterMode ? temp_bitmap : */bitmap,&user_clip,machine.gfx[0],
tile,tile2,
color + colorOffset,fx,fy,xbase,ybase,
0,
use8bppMode,(xscale<<8),(yscale<<8),alpha);
sprite++;
sprite2++;
xbase+=xinc;
}
ybase+=yinc;
}
// if (lastRasterMode!=0 && rasterDirty)
// {
// blitRaster(machine, bitmap, rasterMode);
// bitmap_fill(temp_bitmap,cliprect,0);
// rasterDirty=0;
// }
// lastRasterMode=rasterMode;
if (use8bppMode)
offs-=8;
}
}
SCREEN_EOF( mlc )
{
deco_mlc_state *state = machine.driver_data<deco_mlc_state>();
/* Spriteram is definitely double buffered, as the vram lookup tables
are often updated a frame after spriteram is setup to point to a new
lookup table. Without buffering incorrect one frame glitches are seen
in several places, especially in Hoops.
*/
memcpy(state->m_mlc_buffered_spriteram, state->m_spriteram, 0x3000);
}
SCREEN_UPDATE( mlc )
{
// bitmap_fill(temp_bitmap,cliprect,0);
bitmap_fill(bitmap,cliprect,screen->machine().pens[0]); /* Pen 0 fill colour confirmed from Skull Fang level 2 */
draw_sprites(screen->machine(),bitmap,cliprect);
return 0;
}