1 // license:BSD-3-Clause
2 // copyright-holders:Bryan McPhail
3 /***************************************************************************
4 
5     Lemmings video emulation - Bryan McPhail, mish@tendril.co.uk
6 
7 *********************************************************************
8 
9     There are two sets of sprites, the combination of custom chips 52 & 71.
10     There is a background pixel layer implemented with discrete logic
11     rather than a custom chip and a foreground VRAM tilemap layer that the
12     game mostly uses as a pixel layer (the vram format is arranged as
13     sequential pixels, rather than sequential characters).
14 
15 ***************************************************************************/
16 
17 #include "emu.h"
18 #include "includes/lemmings.h"
19 
20 #include <algorithm>
21 
22 /******************************************************************************/
23 
TILE_GET_INFO_MEMBER(lemmings_state::get_tile_info)24 TILE_GET_INFO_MEMBER(lemmings_state::get_tile_info)
25 {
26 	uint16_t tile = m_vram_data[tile_index];
27 
28 	tileinfo.set(2,
29 			tile&0x7ff,
30 			(tile>>12)&0xf,
31 			0);
32 }
33 
video_start()34 void lemmings_state::video_start()
35 {
36 	m_vram_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(lemmings_state::get_tile_info)), TILEMAP_SCAN_COLS, 8, 8, 64, 32);
37 
38 	m_vram_tilemap->set_transparent_pen(0);
39 	m_bitmap0.fill(0x100);
40 
41 	m_vram_buffer = make_unique_clear<uint8_t[]>(2048 * 64); // 64 bytes per VRAM character
42 	m_gfxdecode->gfx(2)->set_source(m_vram_buffer.get());
43 
44 	m_sprgen[0]->alloc_sprite_bitmap();
45 	m_sprgen[1]->alloc_sprite_bitmap();
46 
47 	m_sprite_triple_buffer[0] = make_unique_clear<uint16_t[]>(0x800/2);
48 	m_sprite_triple_buffer[1] = make_unique_clear<uint16_t[]>(0x800/2);
49 
50 	save_item(NAME(m_bitmap0));
51 	save_pointer(NAME(m_vram_buffer), 2048 * 64);
52 	save_pointer(NAME(m_sprite_triple_buffer[0]), 0x800/2, 0);
53 	save_pointer(NAME(m_sprite_triple_buffer[1]), 0x800/2, 1);
54 }
55 
WRITE_LINE_MEMBER(lemmings_state::screen_vblank_lemmings)56 WRITE_LINE_MEMBER(lemmings_state::screen_vblank_lemmings)
57 {
58 	// rising edge
59 	if (state)
60 	{
61 		for (int chip = 0; chip < 2; chip++)
62 			std::copy_n(&m_spriteram[chip]->buffer()[0], 0x800/2, &m_sprite_triple_buffer[chip][0]);
63 	}
64 }
65 
66 /******************************************************************************/
67 
68 // RAM based
lemmings_pixel_0_w(offs_t offset,uint16_t data,uint16_t mem_mask)69 void lemmings_state::lemmings_pixel_0_w(offs_t offset, uint16_t data, uint16_t mem_mask)
70 {
71 	int const old = m_pixel_data[0][offset];
72 	COMBINE_DATA(&m_pixel_data[0][offset]);
73 	int const src = m_pixel_data[0][offset];
74 	if (old == src)
75 		return;
76 
77 	int const sy = (offset << 1) >> 11;
78 	int const sx = (offset << 1) & 0x7ff;
79 
80 	if (sx > 2047 || sy > 255)
81 		return;
82 
83 	m_bitmap0.pix(sy, sx + 0) = ((src >> 8) & 0xf) | 0x100;
84 	m_bitmap0.pix(sy, sx + 1) = ((src >> 0) & 0xf) | 0x100;
85 }
86 
87 // RAM based tiles for the FG tilemap
lemmings_pixel_1_w(offs_t offset,uint16_t data,uint16_t mem_mask)88 void lemmings_state::lemmings_pixel_1_w(offs_t offset, uint16_t data, uint16_t mem_mask)
89 {
90 	int sx, sy, src, tile;
91 
92 	COMBINE_DATA(&m_pixel_data[1][offset]);
93 	src = m_pixel_data[1][offset];
94 
95 	sy = (offset << 1) >> 9;
96 	sx = (offset << 1) & 0x1ff;
97 
98 	/* Copy pixel to buffer for easier decoding later */
99 	tile = ((sx / 8) * 32) + (sy / 8);
100 	m_gfxdecode->gfx(2)->mark_dirty(tile);
101 	m_vram_buffer[(tile * 64) + ((sx & 7)) + ((sy & 7) * 8)] = (src >> 8) & 0xf;
102 
103 	sx += 1; /* Update both pixels in the word */
104 	m_vram_buffer[(tile * 64) + ((sx & 7)) + ((sy & 7) * 8)] = (src >> 0) & 0xf;
105 }
106 
lemmings_vram_w(offs_t offset,uint16_t data,uint16_t mem_mask)107 void lemmings_state::lemmings_vram_w(offs_t offset, uint16_t data, uint16_t mem_mask)
108 {
109 	COMBINE_DATA(&m_vram_data[offset]);
110 	m_vram_tilemap->mark_tile_dirty(offset);
111 }
112 
113 
lemmings_copy_bitmap(bitmap_rgb32 & bitmap,int * xscroll,int * yscroll,const rectangle & cliprect)114 void lemmings_state::lemmings_copy_bitmap(bitmap_rgb32& bitmap, int* xscroll, int* yscroll, const rectangle& cliprect)
115 {
116 	pen_t const *const paldata = m_palette->pens();
117 
118 	for (int y=cliprect.top(); y<cliprect.bottom();y++)
119 	{
120 		uint32_t *const dst = &bitmap.pix(y,0);
121 
122 		for (int x=cliprect.left(); x<cliprect.right();x++)
123 		{
124 			uint16_t const src = m_bitmap0.pix((y-*yscroll)&0xff,(x-*xscroll)&0x7ff);
125 
126 			if (src!=0x100)
127 				dst[x] = paldata[src];
128 		}
129 	}
130 }
131 
screen_update_lemmings(screen_device & screen,bitmap_rgb32 & bitmap,const rectangle & cliprect)132 uint32_t lemmings_state::screen_update_lemmings(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
133 {
134 	int x1 = -m_control_data[0];
135 	int x0 = -m_control_data[2];
136 	int y = 0;
137 	rectangle rect(0, 0, cliprect.top(), cliprect.bottom());
138 
139 	// sprites are flipped relative to tilemaps
140 	m_sprgen[0]->set_flip_screen(true);
141 	m_sprgen[1]->set_flip_screen(true);
142 	m_sprgen[0]->draw_sprites(bitmap, cliprect, m_sprite_triple_buffer[1].get(), 0x400);
143 	m_sprgen[1]->draw_sprites(bitmap, cliprect, m_sprite_triple_buffer[0].get(), 0x400);
144 
145 	bitmap.fill(m_palette->black_pen(), cliprect);
146 	m_sprgen[0]->inefficient_copy_sprite_bitmap(bitmap, cliprect, 0x0800, 0x0800, 0x300, 0xff);
147 
148 	/* Pixel layer can be windowed in hardware (two player mode) */
149 	if ((m_control_data[6] & 2) == 0)
150 	{
151 		lemmings_copy_bitmap(bitmap, &x1, &y, cliprect);
152 	}
153 	else
154 	{
155 		rect.setx(0, 159);
156 		lemmings_copy_bitmap(bitmap, &x0, &y, rect);
157 
158 		rect.setx(160, 319);
159 		lemmings_copy_bitmap(bitmap, &x1, &y, rect);
160 	}
161 
162 	m_sprgen[1]->inefficient_copy_sprite_bitmap(bitmap, cliprect, 0x0800, 0x0800, 0x200, 0xff);
163 	m_sprgen[0]->inefficient_copy_sprite_bitmap(bitmap, cliprect, 0x0000, 0x0800, 0x300, 0xff);
164 	m_vram_tilemap->draw(screen, bitmap, cliprect, 0, 0);
165 	m_sprgen[1]->inefficient_copy_sprite_bitmap(bitmap, cliprect, 0x0000, 0x0800, 0x200, 0xff);
166 	return 0;
167 }
168