diff options
Diffstat (limited to 'src/mame/video/slapshot.c')
-rw-r--r-- | src/mame/video/slapshot.c | 633 |
1 files changed, 633 insertions, 0 deletions
diff --git a/src/mame/video/slapshot.c b/src/mame/video/slapshot.c new file mode 100644 index 00000000000..e6dbd0cff61 --- /dev/null +++ b/src/mame/video/slapshot.c @@ -0,0 +1,633 @@ +#include "driver.h" +#include "video/taitoic.h" + +#define TC0480SCP_GFX_NUM 1 +#define TC0100SCN_GFX_NUM 1 + +extern UINT8 TC0360PRI_regs[16]; + +struct tempsprite +{ + int gfx; + int code,color; + int flipx,flipy; + int x,y; + int zoomx,zoomy; + int primask; +}; +static struct tempsprite *spritelist; + +static INT32 sprites_disabled,sprites_active_area,sprites_master_scrollx,sprites_master_scrolly; +static int sprites_flipscreen = 0; +static UINT16 *spriteram_buffered,*spriteram_delayed; + +INT32 taito_sprite_type = 0; +UINT16 *taito_sprite_ext; +size_t taito_spriteext_size; +static UINT16 spritebank[8]; + +static INT32 taito_hide_pixels; + + + +/**********************************************************/ + +static VIDEO_START( slapshot_core ) +{ + int i; + + spriteram_delayed = auto_malloc(spriteram_size); + spriteram_buffered = auto_malloc(spriteram_size); + spritelist = auto_malloc(0x400 * sizeof(*spritelist)); + + if (has_TC0480SCP()) /* it's a tc0480scp game */ + TC0480SCP_vh_start(machine,TC0480SCP_GFX_NUM,taito_hide_pixels,30,9,-1,1,0,2,256); + else /* it's a tc0100scn game */ + TC0100SCN_vh_start(machine,1,TC0100SCN_GFX_NUM,taito_hide_pixels,0,0,0,0,0,0); + + TC0360PRI_vh_start(); /* Purely for save-state purposes */ + + for (i = 0; i < 8; i ++) + spritebank[i] = 0x400 * i; + + sprites_disabled = 1; + sprites_active_area = 0; + + state_save_register_global(taito_hide_pixels); + state_save_register_global(taito_sprite_type); + state_save_register_global_array(spritebank); + state_save_register_global(sprites_disabled); + state_save_register_global(sprites_active_area); + state_save_register_global_pointer(spriteram_delayed, spriteram_size/2); + state_save_register_global_pointer(spriteram_buffered, spriteram_size/2); +} + +VIDEO_START( slapshot ) +{ + taito_hide_pixels = 3; + taito_sprite_type = 2; + video_start_slapshot_core(machine); +} + + +/************************************************************ + SPRITE DRAW ROUTINES +************************************************************/ + +static void draw_sprites(running_machine *machine, mame_bitmap *bitmap,const rectangle *cliprect,int *primasks,int y_offset) +{ + /* + Sprite format: + 0000: ---xxxxxxxxxxxxx tile code (0x0000 - 0x1fff) + 0002: xxxxxxxx-------- sprite y-zoom level + --------xxxxxxxx sprite x-zoom level + + 0x00 - non scaled = 100% + 0x80 - scaled to 50% + 0xc0 - scaled to 25% + 0xe0 - scaled to 12.5% + 0xff - scaled to zero pixels size (off) + + [this zoom scale may not be 100% correct, see Gunfront flame screen] + + 0004: ----xxxxxxxxxxxx x-coordinate (-0x800 to 0x07ff) + ---x------------ latch extra scroll + --x------------- latch master scroll + -x-------------- don't use extra scroll compensation + x--------------- absolute screen coordinates (ignore all sprite scrolls) + xxxx------------ the typical use of the above is therefore + 1010 = set master scroll + 0101 = set extra scroll + 0006: ----xxxxxxxxxxxx y-coordinate (-0x800 to 0x07ff) + x--------------- marks special control commands (used in conjunction with 00a) + If the special command flag is set: + ---------------x related to sprite ram bank + ---x------------ unknown (deadconx, maybe others) + --x------------- unknown, some games (growl, gunfront) set it to 1 when + screen is flipped + 0008: --------xxxxxxxx color (0x00 - 0xff) + -------x-------- flipx + ------x--------- flipy + -----x---------- if set, use latched color, else use & latch specified one + ----x----------- if set, next sprite entry is part of sequence + ---x------------ if clear, use latched y coordinate, else use current y + --x------------- if set, y += 16 + -x-------------- if clear, use latched x coordinate, else use current x + x--------------- if set, x += 16 + 000a: only valid when the special command bit in 006 is set + ---------------x related to sprite ram bank. I think this is the one causing + the bank switch, implementing it this way all games seem + to properly bank switch except for footchmp which uses the + bit in byte 006 instead. + ------------x--- unknown; some games toggle it before updating sprite ram. + ------xx-------- unknown (finalb) + -----x---------- unknown (mjnquest) + ---x------------ disable the following sprites until another marker with + this bit clear is found + --x------------- flip screen + + 000b - 000f : unused + + */ + int x,y,off,extoffs; + int code,color,spritedata,spritecont,flipx,flipy; + int xcurrent,ycurrent,big_sprite=0; + int y_no=0, x_no=0, xlatch=0, ylatch=0, last_continuation_tile=0; /* for zooms */ + UINT32 zoomword, zoomx, zoomy, zx=0, zy=0, zoomxlatch=0, zoomylatch=0; /* for zooms */ + int scroll1x, scroll1y; + int scrollx=0, scrolly=0; + int curx,cury; + int x_offset; + + /* pdrawgfx() needs us to draw sprites front to back, so we have to build a list + while processing sprite ram and then draw them all at the end */ + struct tempsprite *sprite_ptr = spritelist; + + /* must remember enable status from last frame because driftout fails to + reactivate them from a certain point onwards. */ + int disabled = sprites_disabled; + + /* must remember master scroll from previous frame because driftout + sometimes doesn't set it. */ + int master_scrollx = sprites_master_scrollx; + int master_scrolly = sprites_master_scrolly; + + /* must also remember the sprite bank from previous frame. */ + int area = sprites_active_area; + + scroll1x = 0; + scroll1y = 0; + x = y = 0; + xcurrent = ycurrent = 0; + color = 0; + + x_offset = taito_hide_pixels; /* Get rid of 0-3 unwanted pixels on edge of screen. */ + if (sprites_flipscreen) x_offset = -x_offset; + + /* safety check to avoid getting stuck in bank 2 for games using only one bank */ + if (area == 0x8000 && + spriteram_buffered[(0x8000+6)/2] == 0 && + spriteram_buffered[(0x8000+10)/2] == 0) + area = 0; + + + for (off = 0;off < 0x4000;off += 16) + { + /* sprites_active_area may change during processing */ + int offs = off + area; + + if (spriteram_buffered[(offs+6)/2] & 0x8000) + { + disabled = spriteram_buffered[(offs+10)/2] & 0x1000; + sprites_flipscreen = spriteram_buffered[(offs+10)/2] & 0x2000; + x_offset = taito_hide_pixels; /* Get rid of 0-3 unwanted pixels on edge of screen. */ + if (sprites_flipscreen) x_offset = -x_offset; + area = 0x8000 * (spriteram_buffered[(offs+10)/2] & 0x0001); + continue; + } + +//popmessage("%04x",area); + + /* check for extra scroll offset */ + if ((spriteram_buffered[(offs+4)/2] & 0xf000) == 0xa000) + { + master_scrollx = spriteram_buffered[(offs+4)/2] & 0xfff; + if (master_scrollx >= 0x800) master_scrollx -= 0x1000; /* signed value */ + master_scrolly = spriteram_buffered[(offs+6)/2] & 0xfff; + if (master_scrolly >= 0x800) master_scrolly -= 0x1000; /* signed value */ + } + + if ((spriteram_buffered[(offs+4)/2] & 0xf000) == 0x5000) + { + scroll1x = spriteram_buffered[(offs+4)/2] & 0xfff; + if (scroll1x >= 0x800) scroll1x -= 0x1000; /* signed value */ + + scroll1y = spriteram_buffered[(offs+6)/2] & 0xfff; + if (scroll1y >= 0x800) scroll1y -= 0x1000; /* signed value */ + } + + if (disabled) + continue; + + spritedata = spriteram_buffered[(offs+8)/2]; + + spritecont = (spritedata & 0xff00) >> 8; + + if ((spritecont & 0x08) != 0) /* sprite continuation flag set */ + { + if (big_sprite == 0) /* are we starting a big sprite ? */ + { + xlatch = spriteram_buffered[(offs+4)/2] & 0xfff; + ylatch = spriteram_buffered[(offs+6)/2] & 0xfff; + x_no = 0; + y_no = 0; + zoomword = spriteram_buffered[(offs+2)/2]; + zoomylatch = (zoomword>>8) & 0xff; + zoomxlatch = (zoomword) & 0xff; + big_sprite = 1; /* we have started a new big sprite */ + } + } + else if (big_sprite) + { + last_continuation_tile = 1; /* don't clear big_sprite until last tile done */ + } + + + if ((spritecont & 0x04) == 0) + color = spritedata & 0xff; + + +// DG: the bigsprite == 0 check fixes "tied-up" little sprites in Thunderfox +// which (mostly?) have spritecont = 0x20 when they are not continuations +// of anything. + if (big_sprite == 0 || (spritecont & 0xf0) == 0) + { + x = spriteram_buffered[(offs+4)/2]; + +// DG: some absolute x values deduced here are 1 too high (scenes when you get +// home run in Koshien, and may also relate to BG layer woods and stuff as you +// journey in MjnQuest). You will see they are 1 pixel too far to the right. +// Where is this extra pixel offset coming from?? + + if (x & 0x8000) /* absolute (koshien) */ + { + scrollx = - x_offset - 0x60; + scrolly = 0; + } + else if (x & 0x4000) /* ignore extra scroll */ + { + scrollx = master_scrollx - x_offset - 0x60; + scrolly = master_scrolly; + } + else /* all scrolls applied */ + { + scrollx = scroll1x + master_scrollx - x_offset - 0x60; + scrolly = scroll1y + master_scrolly; + } + x &= 0xfff; + y = spriteram_buffered[(offs+6)/2] & 0xfff; + + xcurrent = x; + ycurrent = y; + } + else + { + if ((spritecont & 0x10) == 0) + y = ycurrent; + else if ((spritecont & 0x20) != 0) + { + y += 16; + y_no++; /* keep track of y tile for zooms */ + } + if ((spritecont & 0x40) == 0) + x = xcurrent; + else if ((spritecont & 0x80) != 0) + { + x += 16; + y_no=0; + x_no++; /* keep track of x tile for zooms */ + } + } + +/* Black lines between flames in Gunfront attract before the zoom + finishes suggest these calculations are flawed? */ + + if (big_sprite) + { + zoomx = zoomxlatch; + zoomy = zoomylatch; + zx = 0x10; /* default, no zoom: 16 pixels across */ + zy = 0x10; /* default, no zoom: 16 pixels vertical */ + + if (zoomx || zoomy) + { + /* "Zoom" zx&y is pixel size horizontally and vertically + of our sprite chunk. So it is difference in x and y + coords of our chunk and diagonally adjoining one. */ + + x = xlatch + x_no * (0x100 - zoomx) / 16; + y = ylatch + y_no * (0x100 - zoomy) / 16; + zx = xlatch + (x_no+1) * (0x100 - zoomx) / 16 - x; + zy = ylatch + (y_no+1) * (0x100 - zoomy) / 16 - y; + } + } + else + { + zoomword = spriteram_buffered[(offs+2)/2]; + zoomy = (zoomword>>8) & 0xff; + zoomx = (zoomword) & 0xff; + zx = (0x100 - zoomx) / 16; + zy = (0x100 - zoomy) / 16; + } + + if (last_continuation_tile) + { + big_sprite=0; + last_continuation_tile=0; + } + + code = 0; + extoffs = offs; + if (extoffs >= 0x8000) extoffs -= 0x4000; /* spriteram[0x4000-7fff] has no corresponding extension area */ + + if (taito_sprite_type == 0) + { + int bank; + + code = spriteram_buffered[(offs)/2] & 0x1fff; + + bank = (code & 0x1c00) >> 10; + code = spritebank[bank] + (code & 0x3ff); + } + + if (taito_sprite_type == 1) /* Yuyugogo */ + { + int i; + + code = spriteram_buffered[(offs)/2] & 0x3ff; + i = (taito_sprite_ext[(extoffs >> 4)] & 0x3f ) << 10; + code = (i | code); + } + + if (taito_sprite_type == 2) /* Pulirula, Slapshot */ + { + int i; + + code = spriteram_buffered[(offs)/2] & 0xff; + i = (taito_sprite_ext[(extoffs >> 4)] & 0xff00 ); + code = (i | code); + } + + if (taito_sprite_type == 3) /* Dinorex and a few quizzes */ + { + int i; + + code = spriteram_buffered[(offs)/2] & 0xff; + i = (taito_sprite_ext[(extoffs >> 4)] & 0xff ) << 8; + code = (i | code); + } + + if (code == 0) continue; + + flipx = spritecont & 0x01; + flipy = spritecont & 0x02; + + curx = (x + scrollx) & 0xfff; + if (curx >= 0x800) curx -= 0x1000; /* treat it as signed */ + + cury = (y + scrolly) & 0xfff; + if (cury >= 0x800) cury -= 0x1000; /* treat it as signed */ + + if (sprites_flipscreen) + { + /* -zx/y is there to fix zoomed sprite coords in screenflip. + drawgfxzoom does not know to draw from flip-side of sprites when + screen is flipped; so we must correct the coords ourselves. */ + + curx = 319 - curx - zx; + cury = 256 - cury - zy; + flipx = !flipx; + flipy = !flipy; + } + + cury += y_offset; + + { + sprite_ptr->code = code; + sprite_ptr->color = color; + if (machine->gfx[0]->color_granularity == 64) /* Final Blow, Slapshot are 6bpp */ + sprite_ptr->color /= 4; + sprite_ptr->flipx = flipx; + sprite_ptr->flipy = flipy; + sprite_ptr->x = curx; + sprite_ptr->y = cury; + sprite_ptr->zoomx = zx << 12; + sprite_ptr->zoomy = zy << 12; + + if (primasks) + { + sprite_ptr->primask = primasks[(color & 0xc0) >> 6]; + + sprite_ptr++; + } + else + { + drawgfxzoom(bitmap,machine->gfx[0], + sprite_ptr->code, + sprite_ptr->color, + sprite_ptr->flipx,sprite_ptr->flipy, + sprite_ptr->x,sprite_ptr->y, + cliprect,TRANSPARENCY_PEN,0, + sprite_ptr->zoomx,sprite_ptr->zoomy); + } + } + } + + + /* this happens only if primsks != NULL */ + while (sprite_ptr != spritelist) + { + sprite_ptr--; + + pdrawgfxzoom(bitmap,machine->gfx[0], + sprite_ptr->code, + sprite_ptr->color, + sprite_ptr->flipx,sprite_ptr->flipy, + sprite_ptr->x,sprite_ptr->y, + cliprect,TRANSPARENCY_PEN,0, + sprite_ptr->zoomx,sprite_ptr->zoomy, + sprite_ptr->primask); + } +} + +static int prepare_sprites; + +static void taito_handle_sprite_buffering(void) +{ + if (prepare_sprites) /* no buffering */ + { + memcpy(spriteram_buffered,spriteram16,spriteram_size); + prepare_sprites = 0; + } +} + +static void taito_update_sprites_active_area(void) +{ + int off; + + + /* if the frame was skipped, we'll have to do the buffering now */ + taito_handle_sprite_buffering(); + + /* safety check to avoid getting stuck in bank 2 for games using only one bank */ + if (sprites_active_area == 0x8000 && + spriteram_buffered[(0x8000+6)/2] == 0 && + spriteram_buffered[(0x8000+10)/2] == 0) + sprites_active_area = 0; + + for (off = 0;off < 0x4000;off += 16) + { + /* sprites_active_area may change during processing */ + int offs = off + sprites_active_area; + + if (spriteram_buffered[(offs+6)/2] & 0x8000) + { + sprites_disabled = spriteram_buffered[(offs+10)/2] & 0x1000; + sprites_active_area = 0x8000 * (spriteram_buffered[(offs+10)/2] & 0x0001); + continue; + } + + /* check for extra scroll offset */ + if ((spriteram_buffered[(offs+4)/2] & 0xf000) == 0xa000) + { + sprites_master_scrollx = spriteram_buffered[(offs+4)/2] & 0xfff; + if (sprites_master_scrollx >= 0x800) sprites_master_scrollx -= 0x1000; /* signed value */ + sprites_master_scrolly = spriteram_buffered[(offs+6)/2] & 0xfff; + if (sprites_master_scrolly >= 0x800) sprites_master_scrolly -= 0x1000; /* signed value */ + } + } +} + +VIDEO_EOF( taito_no_buffer ) +{ + taito_update_sprites_active_area(); + + prepare_sprites = 1; +} + + +/************************************************************** + SCREEN REFRESH + +Slapshot and Metalb use in the PRI chip +--------------------------------------- + ++4 xxxx0000 BG1 + 0000xxxx BG0 ++6 xxxx0000 BG3 + 0000xxxx BG2 + +Slapshot mostly keeps all sprites above the bg layers. +One exception is the "puck" in early attract which is +a bg layer given priority over some sprites. +********************************************************************/ + +VIDEO_UPDATE( slapshot ) +{ + UINT8 layer[5]; + UINT8 tilepri[5]; + UINT8 spritepri[4]; + UINT16 priority; + +#ifdef MAME_DEBUG + static int dislayer[5]; /* Layer toggles to help get the layers correct */ +#endif + +#ifdef MAME_DEBUG + if (input_code_pressed_once (KEYCODE_Z)) + { + dislayer[0] ^= 1; + popmessage("bg0: %01x",dislayer[0]); + } + + if (input_code_pressed_once (KEYCODE_X)) + { + dislayer[1] ^= 1; + popmessage("bg1: %01x",dislayer[1]); + } + + if (input_code_pressed_once (KEYCODE_C)) + { + dislayer[2] ^= 1; + popmessage("bg2: %01x",dislayer[2]); + } + + if (input_code_pressed_once (KEYCODE_V)) + { + dislayer[3] ^= 1; + popmessage("bg3: %01x",dislayer[3]); + } + + if (input_code_pressed_once (KEYCODE_B)) + { + dislayer[4] ^= 1; + popmessage("text: %01x",dislayer[4]); + } +#endif + + taito_handle_sprite_buffering(); + + TC0480SCP_tilemap_update(machine); + + priority = TC0480SCP_get_bg_priority(); + + layer[0] = (priority &0xf000) >> 12; /* tells us which bg layer is bottom */ + layer[1] = (priority &0x0f00) >> 8; + layer[2] = (priority &0x00f0) >> 4; + layer[3] = (priority &0x000f) >> 0; /* tells us which is top */ + layer[4] = 4; /* text layer always over bg layers */ + + tilepri[0] = TC0360PRI_regs[4] & 0x0f; /* bg0 */ + tilepri[1] = TC0360PRI_regs[4] >> 4; /* bg1 */ + tilepri[2] = TC0360PRI_regs[5] & 0x0f; /* bg2 */ + tilepri[3] = TC0360PRI_regs[5] >> 4; /* bg3 */ + +/* we actually assume text layer is on top of everything anyway, but FWIW... */ + tilepri[layer[4]] = TC0360PRI_regs[7] & 0x0f; /* fg (text layer) */ + + spritepri[0] = TC0360PRI_regs[6] & 0x0f; + spritepri[1] = TC0360PRI_regs[6] >> 4; + spritepri[2] = TC0360PRI_regs[7] & 0x0f; + spritepri[3] = TC0360PRI_regs[7] >> 4; + + fillbitmap(priority_bitmap,0,cliprect); + fillbitmap(bitmap,machine->pens[0],cliprect); + +#ifdef MAME_DEBUG + if (dislayer[layer[0]]==0) +#endif + TC0480SCP_tilemap_draw(bitmap,cliprect,layer[0],0,1); + +#ifdef MAME_DEBUG + if (dislayer[layer[1]]==0) +#endif + TC0480SCP_tilemap_draw(bitmap,cliprect,layer[1],0,2); + +#ifdef MAME_DEBUG + if (dislayer[layer[2]]==0) +#endif + TC0480SCP_tilemap_draw(bitmap,cliprect,layer[2],0,4); + +#ifdef MAME_DEBUG + if (dislayer[layer[3]]==0) +#endif + TC0480SCP_tilemap_draw(bitmap,cliprect,layer[3],0,8); + + { + int primasks[4] = {0,0,0,0}; + int i; + + for (i = 0;i < 4;i++) + { + if (spritepri[i] < tilepri[(layer[0])]) primasks[i] |= 0xaaaa; + if (spritepri[i] < tilepri[(layer[1])]) primasks[i] |= 0xcccc; + if (spritepri[i] < tilepri[(layer[2])]) primasks[i] |= 0xf0f0; + if (spritepri[i] < tilepri[(layer[3])]) primasks[i] |= 0xff00; + } + + draw_sprites(machine,bitmap,cliprect,primasks,0); + } + + /* + TODO: This isn't the correct way to handle the priority. At the moment of + writing, pdrawgfx() doesn't support 5 layers, so I have to cheat, assuming + that the FG layer is always on top of sprites. + */ + +#ifdef MAME_DEBUG + if (dislayer[layer[4]]==0) +#endif + TC0480SCP_tilemap_draw(bitmap,cliprect,layer[4],0,0); + return 0; +} + |