// license:BSD-3-Clause // copyright-holders:Nicola Salmoria /*************************************************************************** Irem M52 hardware ***************************************************************************/ #include "emu.h" #include "video/resnet.h" #include "includes/m52.h" #define BGHEIGHT 64 /************************************* * * Palette configuration * *************************************/ PALETTE_INIT_MEMBER(m52_state, m52) { const uint8_t *color_prom = memregion("proms")->base(); const uint8_t *char_pal = color_prom + 0x000; const uint8_t *back_pal = color_prom + 0x200; const uint8_t *sprite_pal = color_prom + 0x220; const uint8_t *sprite_table = color_prom + 0x240; static const int resistances_3[3] = { 1000, 470, 220 }; static const int resistances_2[2] = { 470, 220 }; double weights_r[3], weights_g[3], weights_b[3], scale; int i; /* compute palette information for characters/backgrounds */ scale = compute_resistor_weights(0, 255, -1.0, 3, resistances_3, weights_r, 0, 0, 3, resistances_3, weights_g, 0, 0, 2, resistances_2, weights_b, 0, 0); /* character palette */ for (i = 0; i < 512; i++) { uint8_t promval = char_pal[i]; int r = combine_3_weights(weights_r, BIT(promval,0), BIT(promval,1), BIT(promval,2)); int g = combine_3_weights(weights_g, BIT(promval,3), BIT(promval,4), BIT(promval,5)); int b = combine_2_weights(weights_b, BIT(promval,6), BIT(promval,7)); palette.set_indirect_color(i, rgb_t(r,g,b)); } /* background palette */ for (i = 0; i < 32; i++) { uint8_t promval = back_pal[i]; int r = combine_3_weights(weights_r, BIT(promval,0), BIT(promval,1), BIT(promval,2)); int g = combine_3_weights(weights_g, BIT(promval,3), BIT(promval,4), BIT(promval,5)); int b = combine_2_weights(weights_b, BIT(promval,6), BIT(promval,7)); palette.set_indirect_color(512+i, rgb_t(r,g,b)); } /* compute palette information for sprites */ compute_resistor_weights(0, 255, scale, 2, resistances_2, weights_r, 470, 0, 3, resistances_3, weights_g, 470, 0, 3, resistances_3, weights_b, 470, 0); /* sprite palette */ for (i = 0; i < 32; i++) { uint8_t promval = sprite_pal[i]; int r = combine_2_weights(weights_r, BIT(promval,6), BIT(promval,7)); int g = combine_3_weights(weights_g, BIT(promval,3), BIT(promval,4), BIT(promval,5)); int b = combine_3_weights(weights_b, BIT(promval,0), BIT(promval,1), BIT(promval,2)); palette.set_indirect_color(512 + 32 + i, rgb_t(r,g,b)); } /* character lookup table */ for (i = 0; i < 512; i++) palette.set_pen_indirect(i, i); /* sprite lookup table */ for (i = 0; i < 16 * 4; i++) { uint8_t promval = sprite_table[(i & 3) | ((i & ~3) << 1)]; palette.set_pen_indirect(512 + i, 512 + 32 + promval); } /* background */ /* the palette is a 32x8 PROM with many colors repeated. The address of */ /* the colors to pick is as follows: */ /* xbb00: mountains */ /* 0xxbb: hills */ /* 1xxbb: city */ palette.set_pen_indirect(512+16*4+0*4+0, 512); palette.set_pen_indirect(512+16*4+0*4+1, 512+4); palette.set_pen_indirect(512+16*4+0*4+2, 512+8); palette.set_pen_indirect(512+16*4+0*4+3, 512+12); palette.set_pen_indirect(512+16*4+1*4+0, 512); palette.set_pen_indirect(512+16*4+1*4+1, 512+1); palette.set_pen_indirect(512+16*4+1*4+2, 512+2); palette.set_pen_indirect(512+16*4+1*4+3, 512+3); palette.set_pen_indirect(512+16*4+2*4+0, 512); palette.set_pen_indirect(512+16*4+2*4+1, 512+16+1); palette.set_pen_indirect(512+16*4+2*4+2, 512+16+2); palette.set_pen_indirect(512+16*4+2*4+3, 512+16+3); } /************************************* * * Tilemap info callback * *************************************/ TILE_GET_INFO_MEMBER(m52_state::get_tile_info) { uint8_t video = m_videoram[tile_index]; uint8_t color = m_colorram[tile_index]; int flag = 0; int code = 0; code = video; if (color & 0x80) { code |= 0x100; } if (tile_index / 32 <= 6) { flag |= TILE_FORCE_LAYER0; /* lines 0 to 6 are opaqe? */ } SET_TILE_INFO_MEMBER(0, code, color & 0x3f, flag); } /************************************* * * Video startup * *************************************/ void m52_state::video_start() { m_bg_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(FUNC(m52_state::get_tile_info),this), TILEMAP_SCAN_ROWS, 8, 8, 32, 32); m_bg_tilemap->set_transparent_pen(0); m_bg_tilemap->set_scrolldx(127, 127); m_bg_tilemap->set_scrolldy(16, 16); m_bg_tilemap->set_scroll_rows(4); /* only lines 192-256 scroll */ save_item(NAME(m_bg1xpos)); save_item(NAME(m_bg1ypos)); save_item(NAME(m_bg2xpos)); save_item(NAME(m_bg2ypos)); save_item(NAME(m_bgcontrol)); } /************************************* * * Scroll registers * *************************************/ WRITE8_MEMBER(m52_state::m52_scroll_w) { /* According to the schematics there is only one video register that holds the X scroll value with a NAND gate on the V64 and V128 lines to control when it's read, and when 255 (via 8 pull up resistors) is used. So we set the first 3 quarters to 255 and the last to the scroll value */ m_bg_tilemap->set_scrollx(0, 255); m_bg_tilemap->set_scrollx(1, 255); m_bg_tilemap->set_scrollx(2, 255); m_bg_tilemap->set_scrollx(3, -data); } /************************************* * * Video RAM write handlers * *************************************/ WRITE8_MEMBER(m52_state::m52_videoram_w) { m_videoram[offset] = data; m_bg_tilemap->mark_tile_dirty(offset); } WRITE8_MEMBER(m52_state::m52_colorram_w) { m_colorram[offset] = data; m_bg_tilemap->mark_tile_dirty(offset); } /************************************* * * Custom protection * *************************************/ /* This looks like some kind of protection implemented by a custom chip on the scroll board. It mangles the value written to the port m52_bg1xpos_w, as follows: result = popcount(value & 0x7f) ^ (value >> 7) */ READ8_MEMBER(m52_state::m52_protection_r) { int popcount = 0; int temp; for (temp = m_bg1xpos & 0x7f; temp != 0; temp >>= 1) popcount += temp & 1; return popcount ^ (m_bg1xpos >> 7); } /************************************* * * Background control write handlers * *************************************/ WRITE8_MEMBER(m52_state::m52_bg1ypos_w) { m_bg1ypos = data; } WRITE8_MEMBER(m52_state::m52_bg1xpos_w) { m_bg1xpos = data; } WRITE8_MEMBER(m52_state::m52_bg2xpos_w) { m_bg2xpos = data; } WRITE8_MEMBER(m52_state::m52_bg2ypos_w) { m_bg2ypos = data; } WRITE8_MEMBER(m52_state::m52_bgcontrol_w) { m_bgcontrol = data; } /************************************* * * Outputs * *************************************/ WRITE8_MEMBER(m52_state::m52_flipscreen_w) { /* screen flip is handled both by software and hardware */ flip_screen_set((data & 0x01) ^ (~ioport("DSW2")->read() & 0x01)); machine().bookkeeping().coin_counter_w(0, data & 0x02); machine().bookkeeping().coin_counter_w(1, data & 0x20); } WRITE8_MEMBER(m52_state::alpha1v_flipscreen_w) { flip_screen_set(data & 0x01); } /************************************* * * Background rendering * *************************************/ void m52_state::draw_background(bitmap_ind16 &bitmap, const rectangle &cliprect, int xpos, int ypos, int image) { rectangle rect; const rectangle &visarea = m_screen->visible_area(); if (flip_screen()) { xpos = 127 - xpos; ypos = 255 - ypos - BGHEIGHT; } xpos += 128; /* this may not be correct */ ypos = ypos + (22 - 8); m_gfxdecode->gfx(image)->transpen(bitmap,cliprect, 0, 0, flip_screen(), flip_screen(), xpos, ypos, 0); m_gfxdecode->gfx(image)->transpen(bitmap,cliprect, 0, 0, flip_screen(), flip_screen(), xpos - 256, ypos, 0); rect.min_x = visarea.min_x; rect.max_x = visarea.max_x; if (flip_screen()) { rect.min_y = ypos - BGHEIGHT; rect.max_y = ypos - 1; } else { rect.min_y = ypos + BGHEIGHT; rect.max_y = ypos + 2 * BGHEIGHT - 1; } bitmap.fill(m_gfxdecode->gfx(image)->colorbase() + 3, rect); } /************************************* * * Video render * *************************************/ uint32_t m52_state::screen_update_m52(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) { int offs; bitmap.fill(0, cliprect); if (!(m_bgcontrol & 0x20)) { if (!(m_bgcontrol & 0x10)) draw_background(bitmap, cliprect, m_bg2xpos, m_bg2ypos, 2); /* distant mountains */ if (!(m_bgcontrol & 0x02)) draw_background(bitmap, cliprect, m_bg1xpos, m_bg1ypos, 3); /* hills */ if (!(m_bgcontrol & 0x04)) draw_background(bitmap, cliprect, m_bg1xpos, m_bg1ypos, 4); /* cityscape */ } m_bg_tilemap->set_flip(flip_screen() ? TILEMAP_FLIPX | TILEMAP_FLIPY : 0); m_bg_tilemap->draw(screen, bitmap, cliprect, 0, 0); /* draw the sprites */ for (offs = 0xfc; offs >= 0; offs -= 4) { int sy = 257 - m_spriteram[offs]; int color = m_spriteram[offs + 1] & 0x3f; int flipx = m_spriteram[offs + 1] & 0x40; int flipy = m_spriteram[offs + 1] & 0x80; int code = m_spriteram[offs + 2]; int sx = m_spriteram[offs + 3]; rectangle clip; /* sprites from offsets $00-$7F are processed in the upper half of the frame */ /* sprites from offsets $80-$FF are processed in the lower half of the frame */ clip = cliprect; if (!(offs & 0x80)) clip.min_y = 0, clip.max_y = 127; else clip.min_y = 128, clip.max_y = 255; /* adjust for flipping */ if (flip_screen()) { int temp = clip.min_y; clip.min_y = 255 - clip.max_y; clip.max_y = 255 - temp; flipx = !flipx; flipy = !flipy; sx = 112 - sx; sy = 257 + 11 - sy; } sx += 128; /* in theory anyways; in practice, some of the molecule-looking guys get clipped */ #ifdef SPLIT_SPRITES sect_rect(&clip, cliprect); #else clip = cliprect; #endif m_gfxdecode->gfx(1)->transmask(bitmap,clip, code, color, flipx, flipy, sx, sy, m_palette->transpen_mask(*m_gfxdecode->gfx(1), color, 512 + 32)); } return 0; }