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