// license:BSD-3-Clause // copyright-holders:Nicola Salmoria /* Taito PC080SN ------- Tilemap generator. Two tilemaps, with gfx data fetched from ROM. Darius uses 3xPC080SN and has double width tilemaps. (NB: it has not been verified that Topspeed uses this chip. Possibly it had a variant with added rowscroll capability.) Standard memory layout (two 64x64 tilemaps with 8x8 tiles) 0000-3fff BG 4000-41ff BG rowscroll (only verified to exist on Topspeed) 4200-7fff unknown/unused? 8000-bfff FG (FG/BG layer order fixed per game; Topspeed has BG on top) c000-c1ff FG rowscroll (only verified to exist on Topspeed) c200-ffff unknown/unused? Double width memory layout (two 128x64 tilemaps with 8x8 tiles) 0000-7fff BG 8000-ffff FG (Tile layout is different; tiles and colors are separated: 0x0000-3fff color / flip words 0x4000-7fff tile number words) Control registers +0x20000 (on from tilemaps) 000-001 BG scroll Y 002-003 FG scroll Y +0x40000 000-001 BG scroll X 002-003 FG scroll X +0x50000 control word (written infrequently, only 2 bits used) ---------------x flip screen ----------x----- 0x20 poked here in Topspeed init, followed by zero (Darius does the same). */ #include "emu.h" #include "pc080sn.h" #include "video/taito_helper.h" #include "screen.h" #define PC080SN_RAM_SIZE 0x10000 #define TOPSPEED_ROAD_COLORS DEFINE_DEVICE_TYPE(PC080SN, pc080sn_device, "pc080sn", "Taito PC080SN") pc080sn_device::pc080sn_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : device_t(mconfig, PC080SN, tag, owner, clock), m_ram(nullptr), m_gfxnum(0), m_x_offset(0), m_y_offset(0), m_y_invert(0), m_dblwidth(0), m_gfxdecode(*this, finder_base::DUMMY_TAG) { for (auto & elem : m_ctrl) elem = 0; for (int i = 0; i < 2; i++) { m_bg_ram[i] = nullptr; m_bgscroll_ram[i] = nullptr; m_bgscrollx[i] = 0; m_bgscrolly[i] = 0; } } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void pc080sn_device::device_start() { if(!m_gfxdecode->started()) throw device_missing_dependencies(); /* use the given gfx set for bg tiles */ if (!m_dblwidth) /* standard tilemaps */ { m_tilemap[0] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(pc080sn_device::get_bg_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 64, 64); m_tilemap[1] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(pc080sn_device::get_fg_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 64, 64); } else /* double width tilemaps */ { m_tilemap[0] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(pc080sn_device::get_bg_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 128, 64); m_tilemap[1] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(pc080sn_device::get_fg_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 128, 64); } m_tilemap[0]->set_transparent_pen(0); m_tilemap[1]->set_transparent_pen(0); m_tilemap[0]->set_scrolldx(-16 + m_x_offset, -16 - m_x_offset); m_tilemap[0]->set_scrolldy(m_y_offset, -m_y_offset); m_tilemap[1]->set_scrolldx(-16 + m_x_offset, -16 - m_x_offset); m_tilemap[1]->set_scrolldy(m_y_offset, -m_y_offset); if (!m_dblwidth) { m_tilemap[0]->set_scroll_rows(512); m_tilemap[1]->set_scroll_rows(512); } m_ram = make_unique_clear(PC080SN_RAM_SIZE / 2); m_bg_ram[0] = m_ram.get() + 0x0000 /2; m_bg_ram[1] = m_ram.get() + 0x8000 /2; m_bgscroll_ram[0] = m_ram.get() + 0x4000 /2; m_bgscroll_ram[1] = m_ram.get() + 0xc000 /2; save_pointer(NAME(m_ram), PC080SN_RAM_SIZE / 2); save_item(NAME(m_ctrl)); } //------------------------------------------------- // device_post_load - device-specific postload //------------------------------------------------- void pc080sn_device::device_post_load() { restore_scroll(); } /***************************************************************************** DEVICE HANDLERS *****************************************************************************/ void pc080sn_device::common_get_pc080sn_bg_tile_info( tile_data &tileinfo, int tile_index, u16 *ram, int gfxnum ) { u16 code, attr; if (!m_dblwidth) { code = (ram[2 * tile_index + 1] & 0x3fff); attr = ram[2 * tile_index]; } else { code = (ram[tile_index + 0x2000] & 0x3fff); attr = ram[tile_index]; } SET_TILE_INFO_MEMBER(gfxnum, code, (attr & 0x1ff), TILE_FLIPYX((attr & 0xc000) >> 14)); } TILE_GET_INFO_MEMBER(pc080sn_device::get_bg_tile_info) { common_get_pc080sn_bg_tile_info( tileinfo, tile_index, m_bg_ram[0], m_gfxnum ); } void pc080sn_device::common_get_pc080sn_fg_tile_info( tile_data &tileinfo, int tile_index, u16 *ram, int gfxnum ) { u16 code,attr; if (!m_dblwidth) { code = (ram[2 * tile_index + 1] & 0x3fff); attr = ram[2 * tile_index]; } else { code = (ram[tile_index + 0x2000] & 0x3fff); attr = ram[tile_index]; } SET_TILE_INFO_MEMBER(gfxnum, code, (attr & 0x1ff), TILE_FLIPYX((attr & 0xc000) >> 14)); } TILE_GET_INFO_MEMBER(pc080sn_device::get_fg_tile_info) { common_get_pc080sn_fg_tile_info( tileinfo, tile_index, m_bg_ram[1], m_gfxnum ); } READ16_MEMBER( pc080sn_device::word_r ) { return m_ram[offset]; } WRITE16_MEMBER( pc080sn_device::word_w ) { COMBINE_DATA(&m_ram[offset]); if (!m_dblwidth) { if (offset < 0x2000) m_tilemap[0]->mark_tile_dirty(offset / 2); else if (offset >= 0x4000 && offset < 0x6000) m_tilemap[1]->mark_tile_dirty((offset & 0x1fff) / 2); } else { if (offset < 0x4000) m_tilemap[0]->mark_tile_dirty((offset & 0x1fff)); else if (offset >= 0x4000 && offset < 0x8000) m_tilemap[1]->mark_tile_dirty((offset & 0x1fff)); } } WRITE16_MEMBER( pc080sn_device::xscroll_word_w ) { COMBINE_DATA(&m_ctrl[offset]); data = m_ctrl[offset]; switch (offset) { case 0x00: m_bgscrollx[0] = -data; break; case 0x01: m_bgscrollx[1] = -data; break; } } WRITE16_MEMBER( pc080sn_device::yscroll_word_w ) { COMBINE_DATA(&m_ctrl[offset + 2]); data = m_ctrl[offset + 2]; if (m_y_invert) data = -data; switch (offset) { case 0x00: m_bgscrolly[0] = -data; break; case 0x01: m_bgscrolly[1] = -data; break; } } WRITE16_MEMBER( pc080sn_device::ctrl_word_w ) { COMBINE_DATA(&m_ctrl[offset + 4]); data = m_ctrl[offset + 4]; switch (offset) { case 0x00: { int flip = (data & 0x01) ? (TILEMAP_FLIPX | TILEMAP_FLIPY) : 0; m_tilemap[0]->set_flip(flip); m_tilemap[1]->set_flip(flip); break; } } #if 0 popmessage("pc080sn ctrl = %4x", data); #endif } /* This routine is needed as an override by Jumping, which doesn't set proper scroll values for foreground tilemap */ void pc080sn_device::set_scroll( int tilemap_num, int scrollx, int scrolly ) { m_tilemap[tilemap_num]->set_scrollx(0, scrollx); m_tilemap[tilemap_num]->set_scrolly(0, scrolly); } /* This routine is needed as an override by Jumping */ void pc080sn_device::set_trans_pen( int tilemap_num, int pen ) { m_tilemap[tilemap_num]->set_transparent_pen(pen); } void pc080sn_device::tilemap_update( ) { int j; m_tilemap[0]->set_scrolly(0, m_bgscrolly[0]); m_tilemap[1]->set_scrolly(0, m_bgscrolly[1]); if (!m_dblwidth) { for (j = 0; j < 256; j++) m_tilemap[0]->set_scrollx((j + m_bgscrolly[0]) & 0x1ff, m_bgscrollx[0] - m_bgscroll_ram[0][j]); for (j = 0; j < 256; j++) m_tilemap[1]->set_scrollx((j + m_bgscrolly[1]) & 0x1ff, m_bgscrollx[1] - m_bgscroll_ram[1][j]); } else { m_tilemap[0]->set_scrollx(0, m_bgscrollx[0]); m_tilemap[1]->set_scrollx(0, m_bgscrollx[1]); } } static u16 topspeed_get_road_pixel_color( u16 pixel, u16 color ) { u16 road_body_color, off_road_color, pixel_type; /* Color changes based on screenshots from game flyer */ pixel_type = (pixel % 0x10); road_body_color = (pixel & 0x7ff0) + 4; off_road_color = road_body_color + 1; if ((color & 0xffe0) == 0xffe0) { pixel += 10; /* Tunnel colors */ road_body_color += 10; off_road_color += 10; } else { /* Unsure which way round these bits go */ if (color & 0x10) road_body_color += 5; if (color & 0x02) off_road_color += 5; } switch (pixel_type) { case 0x01: /* Center lines */ if (color & 0x08) pixel = road_body_color; break; case 0x02: /* Road edge (inner) */ if (color & 0x08) pixel = road_body_color; break; case 0x03: /* Road edge (outer) */ if (color & 0x04) pixel = road_body_color; break; case 0x04: /* Road body */ pixel = road_body_color; break; case 0x05: /* Off road */ pixel = off_road_color; break; default: {} } return pixel; } void pc080sn_device::topspeed_custom_draw(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int layer, int flags, u8 priority, u16 *color_ctrl_ram, u8 pmask) { u16 *dst16, *src16; u8 *tsrc; u16 scanline[1024]; /* won't be called by a wide-screen game, but just in case... */ bitmap_ind16 &srcbitmap = m_tilemap[layer]->pixmap(); bitmap_ind8 &flagsbitmap = m_tilemap[layer]->flagsmap(); u16 a, color; int sx, x_index; int y_index, src_y_index, row_index; int flip = 0; int min_x = cliprect.min_x; int max_x = cliprect.max_x; int min_y = cliprect.min_y; int max_y = cliprect.max_y; int screen_width = max_x - min_x + 1; int width_mask = 0x1ff; /* underlying tilemap */ if (!flip) { sx = m_bgscrollx[layer] + 16 - m_x_offset; y_index = m_bgscrolly[layer] + min_y - m_y_offset; } else // never used { sx = 0; y_index = 0; } for (int y = min_y; y <= max_y; y++) { src_y_index = y_index & 0x1ff; /* tilemaps are 512 px up/down */ row_index = (src_y_index - m_bgscrolly[layer]) & 0x1ff; color = color_ctrl_ram[(row_index + m_y_offset - 2) & 0xff]; x_index = sx - (m_bgscroll_ram[layer][row_index]); src16 = &srcbitmap.pix16(src_y_index); tsrc = &flagsbitmap.pix8(src_y_index); dst16 = scanline; if (flags & TILEMAP_DRAW_OPAQUE) { for (int i = 0; i < screen_width; i++) { a = src16[x_index & width_mask]; #ifdef TOPSPEED_ROAD_COLORS a = topspeed_get_road_pixel_color(a, color); #endif *dst16++ = a; x_index++; } } else { for (int i = 0; i < screen_width; i++) { if (tsrc[x_index & width_mask]) { a = src16[x_index & width_mask]; #ifdef TOPSPEED_ROAD_COLORS a = topspeed_get_road_pixel_color(a,color); #endif *dst16++ = a; } else *dst16++ = 0x8000; x_index++; } } taitoic_drawscanline(bitmap, cliprect, 0, y, scanline, (flags & TILEMAP_DRAW_OPAQUE) ? false : true, ROT0, screen.priority(), priority, pmask); y_index++; } } void pc080sn_device::tilemap_draw(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int layer, int flags, u8 priority, u8 pmask) { m_tilemap[layer]->draw(screen, bitmap, cliprect, flags, priority, pmask); } void pc080sn_device::tilemap_draw_offset(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int layer, int flags, u8 priority, int x_offset, int y_offset, u8 pmask) { int basedx = -16 - m_x_offset; int basedxflip = -16 + m_x_offset; int basedy = m_y_offset; int basedyflip = -m_y_offset; m_tilemap[layer]->set_scrolldx(basedx + x_offset, basedxflip + x_offset); m_tilemap[layer]->set_scrolldy(basedy + y_offset, basedyflip + y_offset); m_tilemap[layer]->draw(screen, bitmap, cliprect, flags, priority, pmask); m_tilemap[layer]->set_scrolldx(basedx, basedxflip); m_tilemap[layer]->set_scrolldy(basedy, basedyflip); } void pc080sn_device::tilemap_draw_special(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int layer, int flags, u8 priority, u16 *ram, u8 pmask) { pc080sn_device::topspeed_custom_draw(screen, bitmap, cliprect, layer, flags, priority, ram, pmask); } void pc080sn_device::restore_scroll() { int flip; m_bgscrollx[0] = -m_ctrl[0]; m_bgscrollx[1] = -m_ctrl[1]; m_bgscrolly[0] = -m_ctrl[2]; m_bgscrolly[1] = -m_ctrl[3]; flip = (m_ctrl[4] & 0x01) ? (TILEMAP_FLIPX | TILEMAP_FLIPY) : 0; m_tilemap[0]->set_flip(flip); m_tilemap[1]->set_flip(flip); }