1 // license:BSD-3-Clause
2 // copyright-holders:Bryan McPhail, David Graves
3 #include "emu.h"
4 #include "includes/groundfx.h"
5 #include "screen.h"
6 
7 /******************************************************************/
8 
video_start()9 void groundfx_state::video_start()
10 {
11 	m_spritelist = std::make_unique<gfx_tempsprite[]>(0x4000);
12 
13 	/* Hack */
14 	m_hack_cliprect.set(69, 250, 24 + 5, 24 + 44);
15 }
16 
17 /***************************************************************
18             SPRITE DRAW ROUTINES
19 
20 We draw a series of small tiles ("chunks") together to
21 create each big sprite. The spritemap rom provides the lookup
22 table for this. The game hardware looks up 16x16 sprite chunks
23 from the spritemap rom, creating a 64x64 sprite like this:
24 
25      0  1  2  3
26      4  5  6  7
27      8  9 10 11
28     12 13 14 15
29 
30 (where the number is the word offset into the spritemap rom).
31 It can also create 32x32 sprites.
32 
33 NB: unused portions of the spritemap rom contain hex FF's.
34 It is a useful coding check to warn in the log if these
35 are being accessed. [They can be inadvertently while
36 spriteram is being tested, take no notice of that.]
37 
38 Heavy use is made of sprite zooming.
39 
40         ***
41 
42     Sprite table layout (4 long words per entry)
43 
44     ------------------------------------------
45      0 | ........ x....... ........ ........ | Flip X
46      0 | ........ .xxxxxxx ........ ........ | ZoomX
47      0 | ........ ........ .xxxxxxx xxxxxxxx | Sprite Tile
48        |                                     |
49      2 | ........ ....xx.. ........ ........ | Sprite/tile priority [*]
50      2 | ........ ......xx xxxxxx.. ........ | Palette bank
51      2 | ........ ........ ......xx xxxxxxxx | X position
52        |                                     |
53      3 | ........ .....x.. ........ ........ | Sprite size (0=32x32, 1=64x64)
54      3 | ........ ......x. ........ ........ | Flip Y
55      3 | ........ .......x xxxxxx.. ........ | ZoomY
56      3 | ........ ........ ......xx xxxxxxxx | Y position
57     ------------------------------------------
58 
59     [*  00=over BG0, 01=BG1, 10=BG2, 11=BG3 ]
60     [or 00=over BG1, 01=BG2, 10=BG3, 11=BG3 ]
61 
62 ***************************************************************/
63 
draw_sprites(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect,int do_hack,int x_offs,int y_offs)64 void groundfx_state::draw_sprites(screen_device &screen, bitmap_ind16 &bitmap,const rectangle &cliprect,int do_hack,int x_offs,int y_offs)
65 {
66 	int sprites_flipscreen = 0;
67 	static const u32 primasks[4] = {0xffff, 0xfffc, 0xfff0, 0xff00 };
68 
69 	/* pdrawgfx() needs us to draw sprites front to back, so we have to build a list
70 	   while processing sprite ram and then draw them all at the end */
71 	struct gfx_tempsprite *sprite_ptr = m_spritelist.get();
72 
73 	for (int offs = (m_spriteram.bytes() / 4 - 4); offs >= 0; offs -= 4)
74 	{
75 		u32 data = m_spriteram[offs+0];
76 		int flipx =          (data & 0x00800000) >> 23;
77 		int zoomx =          (data & 0x007f0000) >> 16;
78 		const u32 tilenum =  (data & 0x00007fff);
79 
80 		data = m_spriteram[offs+2];
81 		const int priority = (data & 0x000c0000) >> 18;
82 		u32 color =          (data & 0x0003fc00) >> 10;
83 		int x =              (data & 0x000003ff);
84 
85 		data = m_spriteram[offs+3];
86 		const int dblsize =  (data & 0x00040000) >> 18;
87 		int flipy =          (data & 0x00020000) >> 17;
88 		int zoomy =          (data & 0x0001fc00) >> 10;
89 		int y =              (data & 0x000003ff);
90 
91 //      color |= (0x100 + (priority << 6));     /* priority bits select color bank */
92 		color /= 2;     /* as sprites are 5bpp */
93 		flipy = !flipy;
94 		y = (-y & 0x3ff);
95 
96 		if (!tilenum) continue;
97 
98 		flipy = !flipy;
99 		zoomx += 1;
100 		zoomy += 1;
101 
102 		y += y_offs;
103 
104 		/* treat coords as signed */
105 		if (x>0x340) x -= 0x400;
106 		if (y>0x340) y -= 0x400;
107 
108 		x -= x_offs;
109 
110 		const int dimension = ((dblsize*2) + 2);  // 2 or 4
111 		const int total_chunks = ((dblsize*3) + 1) << 2;  // 4 or 16
112 		const int map_offset = tilenum << 2;
113 
114 		{
115 			for (int sprite_chunk = 0; sprite_chunk < total_chunks; sprite_chunk++)
116 			{
117 				const int j = sprite_chunk / dimension;   /* rows */
118 				const int k = sprite_chunk % dimension;   /* chunks per row */
119 
120 				int px = k;
121 				int py = j;
122 				/* pick tiles back to front for x and y flips */
123 				if (flipx)  px = dimension-1-k;
124 				if (flipy)  py = dimension-1-j;
125 
126 				const u16 code = m_spritemap[map_offset + px + (py << (dblsize + 1))];
127 
128 				if (code==0xffff)
129 				{
130 					continue;
131 				}
132 
133 				int curx = x + ((k * zoomx) / dimension);
134 				int cury = y + ((j * zoomy) / dimension);
135 
136 				const int zx = x + (((k + 1) * zoomx) / dimension) - curx;
137 				const int zy = y + (((j + 1) * zoomy) / dimension) - cury;
138 
139 				if (sprites_flipscreen)
140 				{
141 					/* -zx/y is there to fix zoomed sprite coords in screenflip.
142 					   drawgfxzoom does not know to draw from flip-side of sprites when
143 					   screen is flipped; so we must correct the coords ourselves. */
144 
145 					curx = 320 - curx - zx;
146 					cury = 256 - cury - zy;
147 					flipx = !flipx;
148 					flipy = !flipy;
149 				}
150 
151 				sprite_ptr->gfx = 0;
152 				sprite_ptr->code = code;
153 				sprite_ptr->color = color;
154 				sprite_ptr->flipx = !flipx;
155 				sprite_ptr->flipy = flipy;
156 				sprite_ptr->x = curx;
157 				sprite_ptr->y = cury;
158 				sprite_ptr->zoomx = zx << 12;
159 				sprite_ptr->zoomy = zy << 12;
160 				sprite_ptr->pri = priority;
161 				sprite_ptr++;
162 			}
163 		}
164 	}
165 
166 	/* this happens only if primsks != nullptr */
167 	while (sprite_ptr != m_spritelist.get())
168 	{
169 		const rectangle *clipper;
170 
171 		sprite_ptr--;
172 
173 		if (do_hack && sprite_ptr->pri == 1 && sprite_ptr->y < 100)
174 			clipper = &m_hack_cliprect;
175 		else
176 			clipper = &cliprect;
177 
178 		m_gfxdecode->gfx(sprite_ptr->gfx)->prio_zoom_transpen(bitmap,*clipper,
179 				sprite_ptr->code,
180 				sprite_ptr->color,
181 				sprite_ptr->flipx,sprite_ptr->flipy,
182 				sprite_ptr->x,sprite_ptr->y,
183 				sprite_ptr->zoomx,sprite_ptr->zoomy,
184 				screen.priority(),primasks[sprite_ptr->pri],0);
185 	}
186 }
187 
188 /**************************************************************
189                 SCREEN REFRESH
190 **************************************************************/
191 
screen_update(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)192 u32 groundfx_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
193 {
194 	u8 layer[5];
195 	u8 scclayer[3];
196 
197 	m_tc0620scc->tilemap_update();
198 	m_tc0480scp->tilemap_update();
199 
200 	const u16 priority = m_tc0480scp->get_bg_priority();
201 
202 	layer[0] = (priority & 0xf000) >> 12;   /* tells us which bg layer is bottom */
203 	layer[1] = (priority & 0x0f00) >>  8;
204 	layer[2] = (priority & 0x00f0) >>  4;
205 	layer[3] = (priority & 0x000f) >>  0;   /* tells us which is top */
206 	layer[4] = 4;   /* text layer always over bg layers */
207 
208 	scclayer[0] = m_tc0620scc->bottomlayer();
209 	scclayer[1] = scclayer[0]^1;
210 	scclayer[2] = 2;
211 
212 	screen.priority().fill(0, cliprect);
213 	bitmap.fill(0, cliprect);   /* wrong color? */
214 
215 	m_tc0620scc->tilemap_draw(screen, bitmap, cliprect, scclayer[0], TILEMAP_DRAW_OPAQUE, 0);
216 	m_tc0620scc->tilemap_draw(screen, bitmap, cliprect, scclayer[1], 0, 0);
217 
218 	/*  BIG HACK!
219 
220 	    The rear view mirror is a big priority trick - the text
221 	    layer of TC0100SCN is used as a stencil to display
222 	    the bottom layer of TC0480SCP and a particular sprite
223 	    priority.  These never appear outside of the stencil.
224 
225 	    I'm not sure how the game turns this effect on/off
226 	    (the 480 layer is used normally in the frontend
227 	    of the game).
228 
229 	    I haven't implemented it properly yet, instead I'm
230 	    doing a hacky cliprect around the rearview and drawing
231 	    it's contents the usual way.
232 
233 	*/
234 	if (m_tc0620scc->ram_r(0x4090 / 2) || m_tc0620scc->ram_r(0x4092 / 2) ||
235 			((m_tc0480scp->ram_r(0x20 / 2) == 0x24) && (m_tc0480scp->ram_r(0x22 / 2) == 0x0866)))  /* Anything in text layer - really stupid hack */
236 	{
237 		m_tc0480scp->tilemap_draw(screen, bitmap, cliprect, layer[1], 0, 2);
238 		m_tc0480scp->tilemap_draw(screen, bitmap, cliprect, layer[2], 0, 4);
239 		m_tc0480scp->tilemap_draw(screen, bitmap, cliprect, layer[3], 0, 8);
240 
241 		//m_tc0620scc->tilemap_draw(screen, bitmap, cliprect, 0, scclayer[2], 0, 0);
242 
243 		if ((m_tc0480scp->ram_r(0x20 / 2) != 0x24) && (m_tc0480scp->ram_r(0x22 / 2) != 0x0866)) /* Stupid hack for start of race */
244 			m_tc0480scp->tilemap_draw(screen, bitmap, m_hack_cliprect, layer[0], 0, 0);
245 		draw_sprites(screen, bitmap, cliprect, 1, 44, -574);
246 	}
247 	else
248 	{
249 		m_tc0480scp->tilemap_draw(screen, bitmap, cliprect, layer[0], 0, 1);
250 		m_tc0480scp->tilemap_draw(screen, bitmap, cliprect, layer[1], 0, 2);
251 		m_tc0480scp->tilemap_draw(screen, bitmap, cliprect, layer[2], 0, 4);
252 		m_tc0480scp->tilemap_draw(screen, bitmap, cliprect, layer[3], 0, 8);
253 
254 		m_tc0620scc->tilemap_draw(screen, bitmap, cliprect, scclayer[2], 0, 0);
255 
256 		draw_sprites(screen, bitmap, cliprect, 0, 44, -574);
257 	}
258 
259 	m_tc0480scp->tilemap_draw(screen, bitmap, cliprect, layer[4], 0, 0);    /* TC0480SCP text layer */
260 	return 0;
261 }
262