// license:BSD-3-Clause // copyright-holders:David Haywood,Paul Priest /* video/macrossp.c */ #include "emu.h" #include "includes/macrossp.h" //#define DEBUG_KEYS 1 /* Sprite list is drawn backwards, and priorities with backgrounds are not transitive === Vid Registers === [0] - tiles 0x000003ff - global scrollx 0x00000c00 - color mode 0x0000c000 - priority 0x03ff0000 - global scrolly 0x90000000 - enable? Always 0x9 [1] - ??? 0xffff0000 - another scrolly register, mainly used when zooming. unused by emulation 0x0000ffff - another scrollx register, mainly used when zooming. unused by emulation [2] - zoom params 0xf0000000 - zoom enable (== 0xe, not == 0x2). Presumably one bit for x and y enable 0x01ff0000 - incy (0x40 is 1:1, incx is in lineram. might be more bits) Interesting test cases (macrossp, quizmoon doesn't use tilemap zoom): 1) Title screen logo zoom 2) Second level, as zoom into end of canyon 3) Second level, as doors open to revels tracks/blue background for boss 4) Boss should go under bridge on level 4 when he first appears */ /*** SCR A LAYER ***/ WRITE32_MEMBER(macrossp_state::macrossp_scra_videoram_w) { COMBINE_DATA(&m_scra_videoram[offset]); m_scra_tilemap->mark_tile_dirty(offset); } TILE_GET_INFO_MEMBER(macrossp_state::get_macrossp_scra_tile_info) { uint32_t attr, tileno, color; attr = m_scra_videoram[tile_index]; tileno = attr & 0x0000ffff; switch (m_scra_videoregs[0] & 0x00000c00) { case 0x00000800: color = (attr & 0x000e0000) >> 15; break; case 0x00000400: color = (attr & 0x003e0000) >> 17; break; default: color = machine().rand() & 7; break; } SET_TILE_INFO_MEMBER(1, tileno, color, TILE_FLIPYX((attr & 0xc0000000) >> 30)); } /*** SCR B LAYER ***/ WRITE32_MEMBER(macrossp_state::macrossp_scrb_videoram_w) { COMBINE_DATA(&m_scrb_videoram[offset]); m_scrb_tilemap->mark_tile_dirty(offset); } TILE_GET_INFO_MEMBER(macrossp_state::get_macrossp_scrb_tile_info) { uint32_t attr, tileno, color; attr = m_scrb_videoram[tile_index]; tileno = attr & 0x0000ffff; switch (m_scrb_videoregs[0] & 0x00000c00) { case 0x00000800: color = (attr & 0x000e0000) >> 15; break; case 0x00000400: color = (attr & 0x003e0000) >> 17; break; default: color = machine().rand() & 7; break; } SET_TILE_INFO_MEMBER(2, tileno, color, TILE_FLIPYX((attr & 0xc0000000) >> 30)); } /*** SCR C LAYER ***/ WRITE32_MEMBER(macrossp_state::macrossp_scrc_videoram_w) { COMBINE_DATA(&m_scrc_videoram[offset]); m_scrc_tilemap->mark_tile_dirty(offset); } TILE_GET_INFO_MEMBER(macrossp_state::get_macrossp_scrc_tile_info) { uint32_t attr, tileno, color; attr = m_scrc_videoram[tile_index]; tileno = attr & 0x0000ffff; switch (m_scrc_videoregs[0] & 0x00000c00) { case 0x00000800: color = (attr & 0x000e0000) >> 15; break; case 0x00000400: color = (attr & 0x003e0000) >> 17; break; default: color = machine().rand() & 7; break; } SET_TILE_INFO_MEMBER(3, tileno, color, TILE_FLIPYX((attr & 0xc0000000) >> 30)); } /*** TEXT LAYER ***/ WRITE32_MEMBER(macrossp_state::macrossp_text_videoram_w) { COMBINE_DATA(&m_text_videoram[offset]); m_text_tilemap->mark_tile_dirty(offset); } TILE_GET_INFO_MEMBER(macrossp_state::get_macrossp_text_tile_info) { uint32_t tileno, colour; tileno = m_text_videoram[tile_index] & 0x0000ffff; colour = (m_text_videoram[tile_index] & 0x00fe0000) >> 17; SET_TILE_INFO_MEMBER(4, tileno, colour, 0); } /*** VIDEO START / UPDATE ***/ void macrossp_state::video_start() { m_spriteram_old = make_unique_clear(m_spriteram.bytes() / 4); m_spriteram_old2 = make_unique_clear(m_spriteram.bytes() / 4); m_text_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(FUNC(macrossp_state::get_macrossp_text_tile_info),this), TILEMAP_SCAN_ROWS, 16, 16, 64, 64); m_scra_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(FUNC(macrossp_state::get_macrossp_scra_tile_info),this), TILEMAP_SCAN_ROWS, 16, 16, 64, 64); m_scrb_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(FUNC(macrossp_state::get_macrossp_scrb_tile_info),this), TILEMAP_SCAN_ROWS, 16, 16, 64, 64); m_scrc_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(FUNC(macrossp_state::get_macrossp_scrc_tile_info),this), TILEMAP_SCAN_ROWS, 16, 16, 64, 64); m_text_tilemap->set_transparent_pen(0); m_scra_tilemap->set_transparent_pen(0); m_scrb_tilemap->set_transparent_pen(0); m_scrc_tilemap->set_transparent_pen(0); m_gfxdecode->gfx(0)->set_granularity(64); m_gfxdecode->gfx(1)->set_granularity(64); m_gfxdecode->gfx(2)->set_granularity(64); m_gfxdecode->gfx(3)->set_granularity(64); save_pointer(NAME(m_spriteram_old.get()), m_spriteram.bytes() / 4); save_pointer(NAME(m_spriteram_old2.get()), m_spriteram.bytes() / 4); } void macrossp_state::draw_sprites(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect ) { gfx_element *gfx = m_gfxdecode->gfx(0); // uint32_t *source = m_spriteram; uint32_t *source = (m_spriteram_old2.get() + m_spriteram.bytes() / 4) - 3; /* buffers by two frames */ uint32_t *finish = m_spriteram_old2.get(); /* reverse order */ while (source >= finish) { /* --hh hhyy yyyy yyyy CCww wwxx xxxx xxxx ---- --zz zzzz zzzz ---- --ZZ ZZZZ ZZZZ fFa- pp-- cccc c--- tttt tttt tttt tttt */ int wide = (source[0] & 0x00003c00) >> 10; int high = (source[0] & 0x3c000000) >> 26; int xpos = (source[0] & 0x000003ff) >> 0; int ypos = (source[0] & 0x03ff0000) >> 16; int xzoom = (source[1] & 0x000003ff) >> 0; /* 0x100 is zoom factor of 1.0 */ int yzoom = (source[1] & 0x03ff0000) >> 16; int col; int tileno = (source[2] & 0x0000ffff) >> 0; int flipx = (source[2] & 0x40000000) >> 30; int flipy = (source[2] & 0x80000000) >> 31; int alpha = (source[2] & 0x20000000)?0x80:0xff; /* alpha blending enable? */ int loopno = 0; int xcnt, ycnt; int xoffset, yoffset; int pri = (source[2] & 0x0c000000) >> 26; int primask = 0; if(pri <= 0) primask |= GFX_PMASK_1; if(pri <= 1) primask |= GFX_PMASK_2; if(pri <= 2) primask |= GFX_PMASK_4; if(pri <= 3) primask |= GFX_PMASK_8; switch (source[0] & 0x0000c000) { case 0x00008000: col = (source[2] & 0x00380000) >> 17; break; case 0x00004000: col = (source[2] & 0x00f80000) >> 19; break; default: col = machine().rand(); break; } if (xpos > 0x1ff) xpos -=0x400; if (ypos > 0x1ff) ypos -=0x400; /* loop params */ int ymin = 0; int ymax = high+1; int yinc = 1; int yoffst = 0; if(flipy) { yoffst = (high * yzoom * 16); ymin = high; ymax = -1; yinc = -1; } int xmin = 0; int xmax = wide+1; int xinc = 1; int xoffst = 0; if(flipx) { xoffst = (wide * xzoom * 16); xmin = wide; xmax = -1; xinc = -1; } yoffset = yoffst; for (ycnt = ymin; ycnt != ymax; ycnt += yinc) { xoffset = xoffst; for (xcnt = xmin; xcnt != xmax; xcnt += xinc) { int fudged_xzoom = xzoom<<8; int fudged_yzoom = yzoom<<8; /* cover seams as don't know exactly how many pixels on target will cover, and can't specify fractional offsets to start */ if(xzoom < 0x100) fudged_xzoom += 0x600; if(yzoom < 0x100) fudged_yzoom += 0x600; gfx->prio_zoom_alpha(bitmap,cliprect,tileno+loopno,col, flipx,flipy,xpos+(xoffset>>8),ypos+(yoffset>>8), fudged_xzoom,fudged_yzoom, screen.priority(),primask,0,alpha); xoffset += ((xzoom*16) * xinc); loopno++; } yoffset += ((yzoom*16) * yinc); } source -= 3; } } void macrossp_state::draw_layer( screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect, int layer, int line, int pri ) { tilemap_t *tm; uint32_t *vr; uint32_t *lr; switch (layer) { case 0: default: tm = m_scra_tilemap; vr = m_scra_videoregs; lr = m_scra_linezoom; break; case 1: tm = m_scrb_tilemap; vr = m_scrb_videoregs; lr = m_scrb_linezoom; break; case 2: tm = m_scrc_tilemap; vr = m_scrc_videoregs; lr = m_scrc_linezoom; break; } if ((vr[2] & 0xf0000000) == 0xe0000000) /* zoom enable (guess, surely wrong) */ { int startx=0, starty=0, incy, incx; startx = ((vr[0] & 0x000003ff) << 16 ); starty = ((vr[0] & 0x03ff0000) >> 0); incy = (vr[2] & 0x01ff0000) >> 6; if (line&1) incx = (lr[line/2] & 0x0000ffff)>>0; else incx = (lr[line/2] & 0xffff0000)>>16; incx <<= 10; /* scroll register contain position relative to the center of the screen, so adjust */ startx -= (368/2) * (incx - 0x10000); starty -= (240/2) * (incy - 0x10000); // previous logic, which gives mostly comparable results, vr[1] is now unused // startx = (vr[1] & 0x0000ffff) << 16; // starty = (vr[1] & 0xffff0000) >> 0; // startx -= (368/2) * incx; // starty -= (240/2) * incy; tm->draw_roz(screen, bitmap, cliprect, startx,starty,incx,0,0,incy, 1, /* wraparound */ 0, 1<set_scrollx(0, ((vr[0] & 0x000003ff) >> 0 ) ); tm->set_scrolly(0, ((vr[0] & 0x03ff0000) >> 16) ); tm->draw(screen, bitmap, cliprect, 0, 1<> 14; layerpri[1] = (m_scrb_videoregs[0] & 0x0000c000) >> 14; layerpri[2] = (m_scrc_videoregs[0] & 0x0000c000) >> 14; screen.priority().fill(0, cliprect); bitmap.fill(m_palette->black_pen(), cliprect); #ifdef DEBUG_KEYS const input_code lay_keys[8] = {KEYCODE_Q, KEYCODE_W, KEYCODE_E, KEYCODE_R, KEYCODE_T}; bool lay_debug = false; for (int pri = 0; pri <= 4; pri++) { if(machine().input().code_pressed(lay_keys[pri])) { lay_debug = true; } } if(machine().input().code_pressed(KEYCODE_G)) { sprites = false; } if(machine().input().code_pressed(KEYCODE_H)) { backgrounds = false; } #endif for(int pri = 0; pri <= 3; pri++) { #ifdef DEBUG_KEYS if(lay_debug && !machine().input().code_pressed(lay_keys[pri])) continue; #endif if(!backgrounds) continue; for (int y=0; y<240; y++) { clip.min_y = clip.max_y = y; /* quizmoon map requires that layer 2 be drawn over layer 3 when same pri */ for(int layer = 2; layer >= 0; layer--) { if(layerpri[layer] == pri) { draw_layer(screen, bitmap, clip, layer, y, pri); } } } } #ifdef DEBUG_KEYS if(!lay_debug && !machine().input().code_pressed(lay_keys[4])) #endif m_text_tilemap->draw(screen, bitmap, cliprect, 0, 8); if(sprites) draw_sprites(screen, bitmap, cliprect); #if 0 popmessage ("scra - %08x %08x %08x\nscrb - %08x %08x %08x\nscrc - %08x %08x %08x", m_scra_videoregs[0]&0xffffffff, // yyyyxxxx m_scra_videoregs[1], // ??? more scrolling? m_scra_videoregs[2], // 08 - 0b m_scrb_videoregs[0]&0xffffffff, // 00 - 03 m_scrb_videoregs[1], // 04 - 07 m_scrb_videoregs[2], // 08 - 0b m_scrc_videoregs[0]&0xffffffff, // 00 - 03 m_scrc_videoregs[1], // 04 - 07 m_scrc_videoregs[2]);// 08 - 0b #endif return 0; } WRITE_LINE_MEMBER(macrossp_state::screen_vblank_macrossp) { // rising edge if (state) { /* looks like sprites are *two* frames ahead, like nmk16 */ memcpy(m_spriteram_old2.get(), m_spriteram_old.get(), m_spriteram.bytes()); memcpy(m_spriteram_old.get(), m_spriteram, m_spriteram.bytes()); } }