1 // license:BSD-3-Clause
2 // copyright-holders:Nicola Salmoria
3 /*
4 Taito TC0480SCP
5 ---------
6 Tilemap generator. Manages four background tilemaps with 16x16 tiles fetched
7 from ROM, and one foreground text tilemap with 8x8 tiles fetched from RAM.
8 All four background tilemaps support zooming and rowscroll, and two of them
9 additionally support per-row zooming and column scroll. The five tilemaps
10 are mixed internally (the text tilemap is always on top, the order of the
11 other four is selectable) and output as 16 bits of pixel data.
12 The TC0480SCP uses 0x10000 bytes of RAM. It seems to be able to address
13 up to 0x800000 bytes of ROM (0x10000 tiles) as it has 21 address lines and
14 32 data lines, but no known game uses more than 0x400000 bytes.
15 
16 Inputs and outputs (based on Gunbuster schematics):
17 - CPU address bus (VA1-VA17)
18 - CPU data bus (VD0-VD15)
19 - CPU control lines (CS, UDS, LDS, R/W, DTACK)
20 - RAM address bus (RA0-RA14)
21 - RAM data bus (RAD0-RAD15)
22 - RAM control lines (RWAH, RWAL, RAOE)
23 - ROM address bus (CH0-CH20)
24 - ROM data bus (RD0-RD31)
25 - Pixel output (SD0-SD15)
26 - Clocks and video sync (HSYNC, HBLANK, VSYNC, VBLANK)
27 
28 Standard memory layout (four 32x32 bg tilemaps, one 64x64 fg tilemap)
29 
30 0000-0fff BG0
31 1000-1fff BG1
32 2000-2fff BG2
33 3000-3fff BG3
34 4000-43ff BG0 rowscroll
35 4400-47ff BG1 rowscroll
36 4800-4bff BG2 rowscroll
37 4c00-4fff BG3 rowscroll
38 5000-53ff BG0 rowscroll low order bytes (see info below)
39 5400-57ff BG1 rowscroll low order bytes
40 5800-5bff BG2 rowscroll low order bytes
41 5c00-5fff BG3 rowscroll low order bytes
42 6000-63ff BG2 row zoom
43 6400-67ff BG3 row zoom
44 6800-6bff BG2 source colscroll
45 6c00-6fff BG3 source colscroll
46 7000-bfff unknown/unused?
47 c000-dfff FG0
48 e000-ffff gfx data for FG0 (4bpp)
49 
50 Double width tilemaps memory layout (four 64x32 bg tilemaps, one 64x64 fg tilemap)
51 
52 0000-1fff BG0
53 2000-3fff BG1
54 4000-5fff BG2
55 6000-7fff BG3
56 8000-83ff BG0 rowscroll
57 8400-87ff BG1 rowscroll
58 8800-8bff BG2 rowscroll
59 8c00-8fff BG3 rowscroll
60 9000-93ff BG0 rowscroll low order bytes (used for accuracy with row zoom or layer zoom)
61 9400-97ff BG1 rowscroll low order bytes [*]
62 9800-9bff BG2 rowscroll low order bytes
63 9c00-9fff BG3 rowscroll low order bytes
64 a000-a3ff BG2 row zoom [+]
65 a400-a7ff BG3 row zoom
66 a800-abff BG2 source colscroll
67 ac00-afff BG3 source colscroll
68 b000-bfff unknown (Slapshot and Superchs poke in TBA OVER error message in FG0 format)
69 c000-dfff FG0
70 e000-ffff gfx data for FG0 (4bpp)
71 
72 [* Gunbustr suggests that high bytes are irrelevant: it leaves them
73 all zeroed. Superchs is the only game which uses high bytes that
74 aren't the low byte of the main rowscroll (Footchmp/Undrfire have
75 this verified in the code).]
76 
77 [+ Usual row zoom values are 0 - 0x7f. Gunbustr also uses 0x80-d0
78 approx. Undrfire keeps to the 0-0x7f range but oddly also uses
79 the high byte with a mask of 0x3f. Meaning of this high byte is
80 unknown.]
81 
82 Bg layers tile word layout
83 
84 +0x00   %yx..bbbb cccccccc      b=control bits(?) c=color .=unused(?)
85 +0x02   tilenum
86 [y=yflip x=xflip b=unknown seen in Metalb]
87 
88 Control registers
89 
90 000-001 BG0 x scroll    (layer priority order is definable)
91 002-003 BG1 x scroll
92 004-005 BG2 x scroll
93 006-007 BG3 x scroll
94 008-009 BG0 y scroll
95 00a-00b BG1 y scroll
96 00c-00d BG2 y scroll
97 00e-00f BG3 y scroll
98 010-011 BG0 zoom        (high byte = X zoom, low byte = Y zoom,
99 012-013 BG1 zoom         compression is allowed on Y axis only)
100 014-015 BG2 zoom
101 016-017 BG3 zoom
102 018-019 Text layer x scroll
103 01a-01b Text layer y scroll
104 01c-01d Unused (not written)
105 01e-01f Layer Control register
106         x-------    Double width tilemaps (4 bg tilemaps become 64x32, and the
107                     memory layout changes). Slapshot changes this on the fly.
108         -x------    Flip screen
109         --x-----    unknown
110 
111                 Set in Metalb init by whether a byte in prg ROM $7fffe is zero.
112                 Subsequently Metalb changes it for some screen layer layouts.
113                 Footchmp clears it, Hthero sets it [then both leave it alone].
114                 Deadconx code at $10e2 is interesting, with possible values of:
115                 0x0, 0x20, 0x40, 0x60 poked in (via ram buffer) to control reg,
116                 dependent on byte in prg ROM $7fffd and whether screen is flipped.
117 
118         ---xxx--    BG layer priority order
119 
120         ...000..    0  1  2  3
121         ...001..    1  2  3  0  (no evidence of this)
122         ...010..    2  3  0  1  (no evidence of this)
123         ...011..    3  0  1  2
124         ...100..    3  2  1  0
125         ...101..    2  1  0  3  [Gunbustr attract and Metalb (c) screen]
126         ...110..    1  0  3  2  (no evidence of this)
127         ...111..    0  3  2  1
128 
129         ------x-    BG3 row zoom enable
130         -------x    BG2 row zoom enable
131 
132 020-021 BG0 dx  (provides extra precision to x-scroll, only changed with xscroll)
133 022-023 BG1 dx
134 024-025 BG2 dx
135 026-027 BG3 dx
136 028-029 BG0 dy  (provides extra precision to y-scroll, only changed with yscroll)
137 02a-02b BG1 dy
138 02c-02d BG2 dy
139 02e-02f BG3 dy
140 
141 [see code at $1b4a in Slapshot and $xxxxx in Undrfire for evidence of row areas]
142 */
143 
144 #include "emu.h"
145 #include "tc0480scp.h"
146 #include "video/taito_helper.h"
147 #include "screen.h"
148 
149 #define TC0480SCP_RAM_SIZE 0x10000
150 #define TC0480SCP_TOTAL_CHARS 256
151 
152 
153 DEFINE_DEVICE_TYPE(TC0480SCP, tc0480scp_device, "tc0480scp", "Taito TC0480SCP")
154 
tc0480scp_device(const machine_config & mconfig,const char * tag,device_t * owner,u32 clock)155 tc0480scp_device::tc0480scp_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
156 	: device_t(mconfig, TC0480SCP, tag, owner, clock)
157 	, device_gfx_interface(mconfig, *this)
158 	, m_pri_reg(0)
159 	, m_dblwidth(0)
160 	, m_gfxlayout(TC0480SCP_LAYOUT_COMMON)
161 	, m_x_offset(0)
162 	, m_y_offset(0)
163 	, m_text_xoffs(0)
164 	, m_text_yoffs(0)
165 	, m_flip_xoffs(0)
166 	, m_flip_yoffs(0)
167 	, m_col_base(0)
168 {
169 	memset(m_ctrl, 0, sizeof(m_ctrl));
170 
171 	for (int i = 0; i < 4; i++)
172 	{
173 		m_bgscroll_ram[i] = nullptr;
174 		m_rowzoom_ram[i] = nullptr;
175 		m_bgcolumn_ram[i] = nullptr;
176 		m_bgscrollx[i] = 0;
177 		m_bgscrolly[i] = 0;
178 	}
179 }
180 
181 /*************************************
182  *
183  *  Graphics definitions
184  *
185  *************************************/
186 
187 GFXDECODE_MEMBER(tc0480scp_device::gfxinfo_default)
188 	GFXDECODE_DEVICE(DEVICE_SELF, 0, gfx_16x16x4_packed_lsb, 0, 256)
189 GFXDECODE_END
190 
191 static const gfx_layout bootleg_tilelayout =
192 {
193 	16,16,  /* 16*16 tiles */
194 	RGN_FRAC(1,4),
195 	4,  /* 4 bits per pixel */
196 	{ RGN_FRAC(0,4),RGN_FRAC(1,4),RGN_FRAC(2,4),RGN_FRAC(3,4) },
197 	{ STEP16(0,1) },
198 	{ STEP16(0,16) },
199 	16*16   /* every tile takes 128 consecutive bytes */
200 };
201 
202 static const gfx_layout bootleg_charlayout =
203 {
204 	8,8,  /* 8*8 tiles */
205 	256,  /* the ROMs are mostly empty */
206 	4,  /* 4 bits per pixel */
207 	{ RGN_FRAC(0,4),RGN_FRAC(1,4),RGN_FRAC(2,4),RGN_FRAC(3,4) },
208 	{ STEP8(0,1) },
209 	{ STEP8(0,8) },
210 	8*8   /* every tile takes 8 consecutive bytes */
211 };
212 
213 GFXDECODE_MEMBER(tc0480scp_device::gfxinfo_bootleg)
214 	GFXDECODE_DEVICE(DEVICE_SELF, 0, bootleg_tilelayout, 0, 256)
215 	GFXDECODE_DEVICE("textrom",   0, bootleg_charlayout, 0,  64)    // bootleg should clearly use this instead of the uploaded tiles
216 GFXDECODE_END
217 
218 //-------------------------------------------------
219 //  device_start - device-specific startup
220 //-------------------------------------------------
221 
device_start()222 void tc0480scp_device::device_start()
223 {
224 	switch (m_gfxlayout)
225 	{
226 		case TC0480SCP_LAYOUT_COMMON:
227 		default:
228 			decode_gfx(gfxinfo_default);
229 			break;
230 		case TC0480SCP_LAYOUT_BOOTLEG:
231 			decode_gfx(gfxinfo_bootleg);
232 			gfx(1)->set_colorbase(m_col_base);
233 			break;
234 	}
235 	gfx(0)->set_colorbase(m_col_base);
236 
237 	static const gfx_layout tc0480scp_charlayout =
238 	{
239 		8,8,    /* 8*8 characters */
240 		256,    /* 256 characters */
241 		4,  /* 4 bits per pixel */
242 		{ 0, 1, 2, 3 },
243 		{ 3*4, 2*4, 1*4, 0*4, 7*4, 6*4, 5*4, 4*4 },
244 		{ 0*32, 1*32, 2*32, 3*32, 4*32, 5*32, 6*32, 7*32 },
245 		32*8    /* every sprite takes 32 consecutive bytes */
246 	};
247 
248 	/* Single width versions */
249 	m_tilemap[0][0] = &machine().tilemap().create(*this, tilemap_get_info_delegate(*this, FUNC(tc0480scp_device::get_bg_tile_info<0x0000>)), TILEMAP_SCAN_ROWS, 16, 16, 32, 32);
250 	m_tilemap[1][0] = &machine().tilemap().create(*this, tilemap_get_info_delegate(*this, FUNC(tc0480scp_device::get_bg_tile_info<0x0800>)), TILEMAP_SCAN_ROWS, 16, 16, 32, 32);
251 	m_tilemap[2][0] = &machine().tilemap().create(*this, tilemap_get_info_delegate(*this, FUNC(tc0480scp_device::get_bg_tile_info<0x1000>)), TILEMAP_SCAN_ROWS, 16, 16, 32, 32);
252 	m_tilemap[3][0] = &machine().tilemap().create(*this, tilemap_get_info_delegate(*this, FUNC(tc0480scp_device::get_bg_tile_info<0x1800>)), TILEMAP_SCAN_ROWS, 16, 16, 32, 32);
253 	m_tilemap[4][0] = &machine().tilemap().create(*this, tilemap_get_info_delegate(*this, FUNC(tc0480scp_device::get_tx_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 64, 64);
254 
255 	/* Double width versions */
256 	m_tilemap[0][1] = &machine().tilemap().create(*this, tilemap_get_info_delegate(*this, FUNC(tc0480scp_device::get_bg_tile_info<0x0000>)), TILEMAP_SCAN_ROWS, 16, 16, 64, 32);
257 	m_tilemap[1][1] = &machine().tilemap().create(*this, tilemap_get_info_delegate(*this, FUNC(tc0480scp_device::get_bg_tile_info<0x1000>)), TILEMAP_SCAN_ROWS, 16, 16, 64, 32);
258 	m_tilemap[2][1] = &machine().tilemap().create(*this, tilemap_get_info_delegate(*this, FUNC(tc0480scp_device::get_bg_tile_info<0x2000>)), TILEMAP_SCAN_ROWS, 16, 16, 64, 32);
259 	m_tilemap[3][1] = &machine().tilemap().create(*this, tilemap_get_info_delegate(*this, FUNC(tc0480scp_device::get_bg_tile_info<0x3000>)), TILEMAP_SCAN_ROWS, 16, 16, 64, 32);
260 	m_tilemap[4][1] = &machine().tilemap().create(*this, tilemap_get_info_delegate(*this, FUNC(tc0480scp_device::get_tx_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 64, 64);
261 
262 	for (int i = 0; i < 2; i++)
263 	{
264 		m_tilemap[0][i]->set_transparent_pen(0);
265 		m_tilemap[1][i]->set_transparent_pen(0);
266 		m_tilemap[2][i]->set_transparent_pen(0);
267 		m_tilemap[3][i]->set_transparent_pen(0);
268 		m_tilemap[4][i]->set_transparent_pen(0);
269 	}
270 
271 	const int xd = -m_x_offset;
272 	const int yd =  m_y_offset;
273 
274 	/* Metalb and Deadconx have minor screenflip issues: blue planet
275 	   is off on x axis by 1 and in Deadconx the dark blue screen
276 	   between stages also seems off by 1 pixel. */
277 
278 	/* It's not possible to get the text scrolldx calculations
279 	   harmonised with the other layers: xd-2, 315-xd is the
280 	   next valid pair:- the numbers diverge from xd, 319-xd */
281 
282 	/* Single width offsets */
283 	m_tilemap[0][0]->set_scrolldx(xd,     320 - xd + m_flip_xoffs);
284 	m_tilemap[0][0]->set_scrolldy(yd,     256 - yd + m_flip_yoffs);
285 	m_tilemap[1][0]->set_scrolldx(xd,     320 - xd + m_flip_xoffs);
286 	m_tilemap[1][0]->set_scrolldy(yd,     256 - yd + m_flip_yoffs);
287 	m_tilemap[2][0]->set_scrolldx(xd,     320 - xd + m_flip_xoffs);
288 	m_tilemap[2][0]->set_scrolldy(yd,     256 - yd + m_flip_yoffs);
289 	m_tilemap[3][0]->set_scrolldx(xd,     320 - xd + m_flip_xoffs);
290 	m_tilemap[3][0]->set_scrolldy(yd,     256 - yd + m_flip_yoffs);
291 	m_tilemap[4][0]->set_scrolldx(xd - 3, 316 - xd);   /* text layer */
292 	m_tilemap[4][0]->set_scrolldy(yd,     256 - yd);   /* text layer */
293 
294 	/* Double width offsets */
295 	m_tilemap[0][1]->set_scrolldx(xd,     320 - xd + m_flip_xoffs);
296 	m_tilemap[0][1]->set_scrolldy(yd,     256 - yd + m_flip_yoffs);
297 	m_tilemap[1][1]->set_scrolldx(xd,     320 - xd + m_flip_xoffs);
298 	m_tilemap[1][1]->set_scrolldy(yd,     256 - yd + m_flip_yoffs);
299 	m_tilemap[2][1]->set_scrolldx(xd,     320 - xd + m_flip_xoffs);
300 	m_tilemap[2][1]->set_scrolldy(yd,     256 - yd + m_flip_yoffs);
301 	m_tilemap[3][1]->set_scrolldx(xd,     320 - xd + m_flip_xoffs);
302 	m_tilemap[3][1]->set_scrolldy(yd,     256 - yd + m_flip_yoffs);
303 	m_tilemap[4][1]->set_scrolldx(xd - 3, 317 - xd);   /* text layer */
304 	m_tilemap[4][1]->set_scrolldy(yd,     256 - yd);   /* text layer */
305 
306 	for (int i = 0; i < 2; i++)
307 	{
308 		/* Both sets of bg tilemaps scrollable per pixel row */
309 		m_tilemap[0][i]->set_scroll_rows(512);
310 		m_tilemap[1][i]->set_scroll_rows(512);
311 		m_tilemap[2][i]->set_scroll_rows(512);
312 		m_tilemap[3][i]->set_scroll_rows(512);
313 	}
314 
315 	m_ram.resize(TC0480SCP_RAM_SIZE / 2);
316 	memset(&m_ram[0], 0, TC0480SCP_RAM_SIZE);
317 	set_layer_ptrs();
318 
319 	/* create the char set (gfx will then be updated dynamically from RAM) */
320 	if (!gfx(1))
321 		set_gfx(1, std::make_unique<gfx_element>(&palette(), tc0480scp_charlayout, (u8 *)&m_ram[0x7000], NATIVE_ENDIAN_VALUE_LE_BE(8,0), 64, m_col_base));  //e000
322 
323 	save_item(NAME(m_ram));
324 	save_item(NAME(m_ctrl));
325 	save_item(NAME(m_bgscrollx));
326 	save_item(NAME(m_bgscrolly));
327 	save_item(NAME(m_pri_reg));
328 	save_item(NAME(m_dblwidth));
329 }
330 
331 //-------------------------------------------------
332 //  device_reset - device-specific reset
333 //-------------------------------------------------
334 
device_reset()335 void tc0480scp_device::device_reset()
336 {
337 	m_dblwidth = 0;
338 
339 	for (auto & elem : m_ctrl)
340 		elem = 0;
341 
342 }
343 
344 /*****************************************************************************
345     DEVICE HANDLERS
346 *****************************************************************************/
347 
348 template<unsigned Offset>
TILE_GET_INFO_MEMBER(tc0480scp_device::get_bg_tile_info)349 TILE_GET_INFO_MEMBER(tc0480scp_device::get_bg_tile_info)
350 {
351 	const u32 code = m_ram[(2 * tile_index) + 1 + Offset] & 0x7fff;
352 	const u16 attr = m_ram[(2 * tile_index) + Offset];
353 	tileinfo.set(0,
354 			code,
355 			(attr & 0xff),
356 			TILE_FLIPYX((attr & 0xc000) >> 14));
357 }
358 
TILE_GET_INFO_MEMBER(tc0480scp_device::get_tx_tile_info)359 TILE_GET_INFO_MEMBER(tc0480scp_device::get_tx_tile_info)
360 {
361 	const u16 attr = m_ram[0x6000 + tile_index]; //c000
362 	tileinfo.set(1,
363 			attr & 0xff,
364 			((attr & 0x3f00) >> 8),
365 			TILE_FLIPYX((attr & 0xc000) >> 14));
366 }
367 
368 
set_layer_ptrs()369 void tc0480scp_device::set_layer_ptrs()
370 {
371 	if (!m_dblwidth)
372 	{
373 		m_bgscroll_ram[0] = &m_ram[0x2000]; //4000
374 		m_bgscroll_ram[1] = &m_ram[0x2200]; //4400
375 		m_bgscroll_ram[2] = &m_ram[0x2400]; //4800
376 		m_bgscroll_ram[3] = &m_ram[0x2600]; //4c00
377 		m_rowzoom_ram[2]  = &m_ram[0x3000]; //6000
378 		m_rowzoom_ram[3]  = &m_ram[0x3200]; //6400
379 		m_bgcolumn_ram[2] = &m_ram[0x3400]; //6800
380 		m_bgcolumn_ram[3] = &m_ram[0x3600]; //6c00
381 	}
382 	else
383 	{
384 		m_bgscroll_ram[0] = &m_ram[0x4000]; //8000
385 		m_bgscroll_ram[1] = &m_ram[0x4200]; //8400
386 		m_bgscroll_ram[2] = &m_ram[0x4400]; //8800
387 		m_bgscroll_ram[3] = &m_ram[0x4600]; //8c00
388 		m_rowzoom_ram[2]  = &m_ram[0x5000]; //a000
389 		m_rowzoom_ram[3]  = &m_ram[0x5200]; //a400
390 		m_bgcolumn_ram[2] = &m_ram[0x5400]; //a800
391 		m_bgcolumn_ram[3] = &m_ram[0x5600]; //ac00
392 	}
393 }
394 
ram_r(offs_t offset)395 u16 tc0480scp_device::ram_r(offs_t offset)
396 {
397 	return m_ram[offset];
398 }
399 
ram_w(offs_t offset,u16 data,u16 mem_mask)400 void tc0480scp_device::ram_w(offs_t offset, u16 data, u16 mem_mask)
401 {
402 	COMBINE_DATA(&m_ram[offset]);
403 
404 	if (offset < 0x2000)
405 	{
406 		m_tilemap[(offset / 0x800)][0]->mark_tile_dirty(((offset % 0x800) / 2));
407 		m_tilemap[(offset / 0x1000)][1]->mark_tile_dirty(((offset % 0x1000) / 2));
408 	}
409 	else if (offset < 0x4000)
410 	{
411 		m_tilemap[(offset / 0x1000)][1]->mark_tile_dirty(((offset % 0x1000) / 2));
412 	}
413 	else if (offset < 0x6000)
414 	{   /* do nothing */
415 	}
416 	else if (offset < 0x7000)
417 	{
418 		m_tilemap[4][0]->mark_tile_dirty((offset - 0x6000));
419 		m_tilemap[4][1]->mark_tile_dirty((offset - 0x6000));
420 	}
421 	else if (offset <= 0x7fff)
422 	{
423 		if (m_gfxlayout != TC0480SCP_LAYOUT_BOOTLEG)
424 			gfx(1)->mark_dirty((offset - 0x7000) / 16);
425 	}
426 }
427 
ctrl_r(offs_t offset)428 u16 tc0480scp_device::ctrl_r(offs_t offset)
429 {
430 	return m_ctrl[offset];
431 }
432 
ctrl_w(offs_t offset,u16 data,u16 mem_mask)433 void tc0480scp_device::ctrl_w(offs_t offset, u16 data, u16 mem_mask)
434 {
435 	int flip = m_pri_reg & 0x40;
436 
437 	COMBINE_DATA(&m_ctrl[offset]);
438 	data = m_ctrl[offset];
439 
440 	switch (offset)
441 	{
442 		/* The x offsets of the four bg layers are staggered by intervals of 4 pixels */
443 		case 0x00:   /* bg0 x */
444 			if (!flip)  data = -data;
445 			m_bgscrollx[0] = data;
446 			break;
447 
448 		case 0x01:   /* bg1 x */
449 			data += 4;
450 			if (!flip)  data = -data;
451 			m_bgscrollx[1] = data;
452 			break;
453 
454 		case 0x02:   /* bg2 x */
455 			data += 8;
456 			if (!flip)  data = -data;
457 			m_bgscrollx[2] = data;
458 			break;
459 
460 		case 0x03:   /* bg3 x */
461 			data += 12;
462 			if (!flip)  data = -data;
463 			m_bgscrollx[3] = data;
464 			break;
465 
466 		case 0x04:   /* bg0 y */
467 			if (flip)  data = -data;
468 			m_bgscrolly[0] = data;
469 			break;
470 
471 		case 0x05:   /* bg1 y */
472 			if (flip)  data = -data;
473 			m_bgscrolly[1] = data;
474 			break;
475 
476 		case 0x06:   /* bg2 y */
477 			if (flip)  data = -data;
478 			m_bgscrolly[2] = data;
479 			break;
480 
481 		case 0x07:   /* bg3 y */
482 			if (flip)  data = -data;
483 			m_bgscrolly[3] = data;
484 			break;
485 
486 		case 0x08:   /* bg0 zoom */
487 		case 0x09:   /* bg1 zoom */
488 		case 0x0a:   /* bg2 zoom */
489 		case 0x0b:   /* bg3 zoom */
490 			break;
491 
492 		case 0x0c:   /* fg (text) x */
493 
494 			/* Text layer can be offset from bg0 (e.g. Metalb) */
495 			if (!flip)  data -= m_text_xoffs;
496 			if (flip)   data += m_text_xoffs;
497 
498 			m_tilemap[4][0]->set_scrollx(0, -data);
499 			m_tilemap[4][1]->set_scrollx(0, -data);
500 			break;
501 
502 		case 0x0d:   /* fg (text) y */
503 
504 			/* Text layer can be offset from bg0 (e.g. Slapshot) */
505 			if (!flip)  data -= m_text_yoffs;
506 			if (flip)   data += m_text_yoffs;
507 
508 			m_tilemap[4][0]->set_scrolly(0, -data);
509 			m_tilemap[4][1]->set_scrolly(0, -data);
510 			break;
511 
512 		/* offset 0x0e unused */
513 
514 		case 0x0f:   /* control register */
515 		{
516 			int old_width = (m_pri_reg & 0x80) >> 7;
517 			flip = (data & 0x40) ? (TILEMAP_FLIPX | TILEMAP_FLIPY) : 0;
518 			m_pri_reg = data;
519 
520 			m_tilemap[0][0]->set_flip(flip);
521 			m_tilemap[1][0]->set_flip(flip);
522 			m_tilemap[2][0]->set_flip(flip);
523 			m_tilemap[3][0]->set_flip(flip);
524 			m_tilemap[4][0]->set_flip(flip);
525 
526 			m_tilemap[0][1]->set_flip(flip);
527 			m_tilemap[1][1]->set_flip(flip);
528 			m_tilemap[2][1]->set_flip(flip);
529 			m_tilemap[3][1]->set_flip(flip);
530 			m_tilemap[4][1]->set_flip(flip);
531 
532 			m_dblwidth = (m_pri_reg & 0x80) >> 7;
533 
534 			if (m_dblwidth != old_width)   /* tilemap width is changing */
535 			{
536 				/* Reinitialise layer pointers */
537 				set_layer_ptrs();
538 			}
539 
540 			break;
541 		}
542 
543 		/* Rest are layer specific delta x and y, used while scrolling that layer */
544 	}
545 }
546 
547 
tilemap_update()548 void tc0480scp_device::tilemap_update()
549 {
550 	int layer, zoom, i, j;
551 	int flip = m_pri_reg & 0x40;
552 
553 	for (layer = 0; layer < 4; layer++)
554 	{
555 		m_tilemap[layer][m_dblwidth]->set_scrolly(0, m_bgscrolly[layer]);
556 		zoom = 0x10000 + 0x7f - m_ctrl[0x08 + layer];
557 
558 		if (zoom != 0x10000)    /* can't use scroll rows when zooming */
559 		{
560 			m_tilemap[layer][m_dblwidth]->set_scrollx(0, m_bgscrollx[layer]);
561 		}
562 		else
563 		{
564 			for (j = 0; j < 512; j++)
565 			{
566 				i = m_bgscroll_ram[layer][j];
567 
568 				if (!flip)
569 					m_tilemap[layer][m_dblwidth]->set_scrollx(j & 0x1ff, m_bgscrollx[layer] - i);
570 				else
571 					m_tilemap[layer][m_dblwidth]->set_scrollx(j & 0x1ff, m_bgscrollx[layer] + i);
572 			}
573 		}
574 	}
575 }
576 
577 
578 /*********************************************************************
579                 BG0,1 LAYER DRAW
580 
581 TODO
582 ----
583 
584 Wouldn't work if y needs to be > 255 (i.e. if some game uses a
585 bigger than usual vertical visible area). Refer to tc0080vco
586 custom draw routine for an example of dealing with this.
587 
588 
589 Historical Issues
590 -----------------
591 
592 1) bg layers got too far left and down, the greater the magnification.
593    Largely fixed by adding offsets (to sx&y) which get bigger as we
594    zoom in (why we have *zoomx and *zoomy in the calculations).
595 
596 2) Hthero and Footchmp bg layers behaved differently when zoomed.
597    Fixed by bringing tc0480scp_x&y_offs into calculations.
598 
599 3) Metalb "TAITO" text in attract too far to the right. Fixed by
600    bringing (layer*4) into offset calculations. But might be possible
601    to avoid this by stepping the scroll deltas for the four layers -
602    currently they are the same, and we have to kludge the offsets in
603    TC0480SCP_ctrl_word_write.
604 
605 4) Zoom movement was jagged: improved by bringing in scroll delta
606    values... but the results are noticeably imperfect.
607 
608 **********************************************************************/
609 
bg01_draw(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect,int layer,int flags,u8 priority,u8 pmask)610 void tc0480scp_device::bg01_draw(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int layer, int flags, u8 priority, u8 pmask)
611 {
612 	/* X-axis zoom offers expansion only: 0 = no zoom, 0xff = max
613 	   Y-axis zoom offers expansion/compression: 0x7f = no zoom, 0xff = max
614 	   (0x1a in Footchmp hiscore = shrunk) */
615 
616 	int zoomx = 0x10000 - (m_ctrl[0x08 + layer] & 0xff00);
617 	int zoomy = 0x10000 - (((m_ctrl[0x08 + layer] & 0xff) - 0x7f) * 512);
618 
619 	if ((zoomx == 0x10000) && (zoomy == 0x10000))   /* no zoom, simple */
620 	{
621 		/* Prevent bad things */
622 		m_tilemap[layer][m_dblwidth]->draw(screen, bitmap, cliprect, flags, priority, pmask);
623 	}
624 	else    /* zoom */
625 	{
626 		u16 scanline[512];
627 		u32 sx;
628 		const bitmap_ind16 &srcbitmap = m_tilemap[layer][m_dblwidth]->pixmap();
629 		bitmap_ind8 &flagsbitmap = m_tilemap[layer][m_dblwidth]->flagsmap();
630 		int flip = m_pri_reg & 0x40;
631 		int y_index;
632 
633 		u16 screen_width = 512; //cliprect.width();
634 		u16 min_y = cliprect.min_y;
635 		u16 max_y = cliprect.max_y;
636 
637 		int width_mask = 0x1ff;
638 		if (m_dblwidth)
639 			width_mask = 0x3ff;
640 
641 		if (!flip)
642 		{
643 			sx = ((m_bgscrollx[layer] + 15 + layer * 4) << 16) + ((255 - (m_ctrl[0x10 + layer] & 0xff)) << 8);
644 			sx += (m_x_offset - 15 - layer * 4) * zoomx;
645 
646 			y_index = (m_bgscrolly[layer] << 16) + ((m_ctrl[0x14 + layer] & 0xff) << 8);
647 			y_index -= (m_y_offset - min_y) * zoomy;
648 		}
649 		else    /* TC0480SCP tiles flipscreen */
650 		{
651 			sx = ((-m_bgscrollx[layer] + 15 + layer * 4 + m_flip_xoffs ) << 16) + ((255-(m_ctrl[0x10 + layer] & 0xff)) << 8);
652 			sx += (m_x_offset - 15 - layer * 4) * zoomx;
653 
654 			y_index = ((-m_bgscrolly[layer] + m_flip_yoffs) << 16) + ((m_ctrl[0x14 + layer] & 0xff) << 8);
655 			y_index -= (m_y_offset - min_y) * zoomy;
656 		}
657 
658 		for (int y = min_y; y <= max_y; y++)
659 		{
660 			int src_y_index = (y_index >> 16) & 0x1ff;
661 
662 			/* row areas are the same in flipscreen, so we must read in reverse */
663 			int row_index = src_y_index;
664 			if (flip)
665 				row_index = 0x1ff - row_index;
666 
667 			int x_index = sx - ((m_bgscroll_ram[layer][row_index] << 16)) - ((m_bgscroll_ram[layer][row_index + 0x800] << 8) & 0xffff);
668 
669 			const u16 *src16 = &srcbitmap.pix(src_y_index);
670 			const u8 *tsrc = &flagsbitmap.pix(src_y_index);
671 			u16 *dst16 = scanline;
672 
673 			int x_step = zoomx;
674 
675 			if (flags & TILEMAP_DRAW_OPAQUE)
676 			{
677 				for (int i = 0; i < screen_width; i++)
678 				{
679 					*dst16++ = src16[(x_index >> 16) & width_mask];
680 					x_index += x_step;
681 				}
682 			}
683 			else
684 			{
685 				for (int i = 0; i < screen_width; i++)
686 				{
687 					if (tsrc[(x_index >> 16) & width_mask])
688 						*dst16++ = src16[(x_index >> 16) & width_mask];
689 					else
690 						*dst16++ = 0x8000;
691 					x_index += x_step;
692 				}
693 			}
694 
695 			taitoic_drawscanline(bitmap, cliprect, 0, y, scanline, (flags & TILEMAP_DRAW_OPAQUE) ? false : true, ROT0, screen.priority(), priority, pmask);
696 
697 			y_index += zoomy;
698 		}
699 	}
700 }
701 
702 
703 /****************************************************************
704                 BG2,3 LAYER DRAW
705 
706 TODO
707 ----
708 
709 Wouldn't work if y needs to be > 255 (i.e. if some game uses a
710 bigger than usual vertical visible area). Refer to tc0080vco
711 custom draw routine for an example of dealing with this.
712 
713 Low order words for overall layer zoom are not really understood.
714 In Metalbj initial text screen zoom you can see they ARE words
715 (not separate bytes); however, I just use the low byte to smooth
716 the zooming sequences. This is noticeably imperfect on the Y axis.
717 
718 Verify behaviour of Taito logo (Gunbustr) against real machine
719 to perfect the row zoom emulation.
720 
721 What do high bytes of row zoom do - if anything - in UndrFire?
722 There is still jaggedness to the road in this game and Superchs.
723 
724 
725 Historical Issues
726 -----------------
727 
728 Sometimes BG2/3 were misaligned by 1 pixel horizontally: this
729 was due to low order byte of 0 causing different (sx >> 16) than
730 when it was 1-255. To prevent this we use (255-byte) so
731 (sx >> 16) no longer depends on the low order byte.
732 
733 In flipscreen we have to bring in extra offsets, since various
734 games don't have exactly (320-,256-) tilemap scroll deltas in
735 flipscreen.
736 
737 ****************************************************************/
738 
bg23_draw(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect,int layer,int flags,u8 priority,u8 pmask)739 void tc0480scp_device::bg23_draw(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int layer, int flags, u8 priority, u8 pmask)
740 {
741 	bitmap_ind16 &srcbitmap = m_tilemap[layer][m_dblwidth]->pixmap();
742 	bitmap_ind8 &flagsbitmap = m_tilemap[layer][m_dblwidth]->flagsmap();
743 
744 	int y_index;
745 	int sx;
746 	u32 zoomx, zoomy;
747 	u16 scanline[512];
748 	int flipscreen = m_pri_reg & 0x40;
749 
750 	u16 screen_width = 512; //cliprect.width();
751 	u16 min_y = cliprect.min_y;
752 	u16 max_y = cliprect.max_y;
753 
754 	int width_mask = 0x1ff;
755 	if (m_dblwidth)
756 		width_mask = 0x3ff;
757 
758 	/* X-axis zoom offers expansion only: 0 = no zoom, 0xff = max
759 	   Y-axis zoom offers expansion/compression: 0x7f = no zoom, 0xff = max
760 	   (0x1a in Footchmp hiscore = shrunk) */
761 
762 	zoomx = 0x10000 - (m_ctrl[0x08 + layer] & 0xff00);
763 	zoomy = 0x10000 - (((m_ctrl[0x08 + layer] & 0xff) - 0x7f) * 512);
764 
765 	if (!flipscreen)
766 	{
767 		sx = ((m_bgscrollx[layer] + 15 + layer * 4) << 16) + ((255-(m_ctrl[0x10 + layer] & 0xff)) << 8);
768 		sx += (m_x_offset - 15 - layer * 4) * zoomx;
769 
770 		y_index = (m_bgscrolly[layer] << 16) + ((m_ctrl[0x14 + layer] & 0xff) << 8);
771 		y_index -= (m_y_offset - min_y) * zoomy;
772 	}
773 	else    /* TC0480SCP tiles flipscreen */
774 	{
775 		sx = ((-m_bgscrollx[layer] + 15 + layer * 4 + m_flip_xoffs ) << 16) + ((255 - (m_ctrl[0x10 + layer] & 0xff)) << 8);
776 		sx += (m_x_offset - 15 - layer * 4) * zoomx;
777 
778 		y_index = ((-m_bgscrolly[layer] + m_flip_yoffs) << 16) + ((m_ctrl[0x14 + layer] & 0xff) << 8);
779 		y_index -= (m_y_offset - min_y) * zoomy;
780 	}
781 
782 	for (int y = min_y; y <= max_y; y++)
783 	{
784 		int src_y_index;
785 		if (!flipscreen)
786 			src_y_index = ((y_index>>16) + m_bgcolumn_ram[layer][(y - m_y_offset) & 0x1ff]) & 0x1ff;
787 		else    /* colscroll area is back to front in flipscreen */
788 			src_y_index = ((y_index>>16) + m_bgcolumn_ram[layer][0x1ff - ((y - m_y_offset) & 0x1ff)]) & 0x1ff;
789 
790 		/* row areas are the same in flipscreen, so we must read in reverse */
791 		int row_index = src_y_index;
792 		if (flipscreen)
793 			row_index = 0x1ff - row_index;
794 
795 		int row_zoom;
796 		if (m_pri_reg & (layer - 1))   /* bit0 enables for BG2, bit1 for BG3 */
797 			row_zoom = m_rowzoom_ram[layer][row_index];
798 		else
799 			row_zoom = 0;
800 
801 		int x_index = sx - ((m_bgscroll_ram[layer][row_index] << 16)) - ((m_bgscroll_ram[layer][row_index + 0x800] << 8) & 0xffff);
802 
803 		/* flawed calc ?? */
804 		x_index -= (m_x_offset - 0x1f + layer * 4) * ((row_zoom & 0xff) << 8);
805 
806 /* We used to kludge 270 multiply factor, before adjusting x_index instead */
807 
808 		int x_step = zoomx;
809 		if (row_zoom)   /* need to reduce x_step */
810 		{
811 			if (!(row_zoom & 0xff00))
812 				x_step -= ((row_zoom * 256) & 0xffff);
813 			else    /* Undrfire uses the hi byte, why? */
814 				x_step -= (((row_zoom & 0xff) * 256) & 0xffff);
815 		}
816 
817 		const u16 *src16 = &srcbitmap.pix(src_y_index);
818 		const u8 *tsrc = &flagsbitmap.pix(src_y_index);
819 		u16 *dst16 = scanline;
820 
821 		if (flags & TILEMAP_DRAW_OPAQUE)
822 		{
823 			for (int i = 0; i < screen_width; i++)
824 			{
825 				*dst16++ = src16[(x_index >> 16) & width_mask];
826 				x_index += x_step;
827 			}
828 		}
829 		else
830 		{
831 			for (int i = 0; i < screen_width; i++)
832 			{
833 				if (tsrc[(x_index >> 16) & width_mask])
834 					*dst16++ = src16[(x_index >> 16) & width_mask];
835 				else
836 					*dst16++ = 0x8000;
837 				x_index += x_step;
838 			}
839 		}
840 
841 		taitoic_drawscanline(bitmap, cliprect, 0, y, scanline, (flags & TILEMAP_DRAW_OPAQUE) ? false : true, ROT0, screen.priority(), priority, pmask);
842 
843 		y_index += zoomy;
844 	}
845 }
846 
847 
tilemap_draw(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect,int layer,int flags,u8 priority,u8 pmask)848 void tc0480scp_device::tilemap_draw(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int layer, int flags, u8 priority, u8 pmask)
849 {
850 	/* no layer disable bits */
851 	switch (layer)
852 	{
853 		case 0:
854 			bg01_draw(screen, bitmap, cliprect, 0, flags, priority, pmask);
855 			break;
856 		case 1:
857 			bg01_draw(screen, bitmap, cliprect, 1, flags, priority, pmask);
858 			break;
859 		case 2:
860 			bg23_draw(screen, bitmap, cliprect, 2, flags, priority, pmask);
861 			break;
862 		case 3:
863 			bg23_draw(screen, bitmap, cliprect, 3, flags, priority, pmask);
864 			break;
865 		case 4:
866 			m_tilemap[4][m_dblwidth]->draw(screen, bitmap, cliprect, flags, priority, pmask);
867 			break;
868 	}
869 }
870 
871 /* For evidence table of TC0480SCP bg layer priorities, refer to mame55 source */
872 
873 static const u16 tc0480scp_bg_pri_lookup[8] =
874 {
875 	0x0123,
876 	0x1230,
877 	0x2301,
878 	0x3012,
879 	0x3210,
880 	0x2103,
881 	0x1032,
882 	0x0321
883 };
884 
get_bg_priority()885 int tc0480scp_device::get_bg_priority()
886 {
887 	return tc0480scp_bg_pri_lookup[(m_pri_reg & 0x1c) >> 2];
888 }
889 
890 // undrfire.cpp also needs to directly access the priority reg
pri_reg_r()891 u8 tc0480scp_device::pri_reg_r()
892 {
893 	return m_pri_reg;
894 }
895 
896 //-------------------------------------------------
897 //  device_post_load - device-specific postload
898 //-------------------------------------------------
899 
device_post_load()900 void tc0480scp_device::device_post_load()
901 {
902 	int reg;
903 	int flip = m_ctrl[0xf] & 0x40;
904 
905 	set_layer_ptrs();
906 
907 	m_tilemap[0][0]->set_flip(flip);
908 	m_tilemap[1][0]->set_flip(flip);
909 	m_tilemap[2][0]->set_flip(flip);
910 	m_tilemap[3][0]->set_flip(flip);
911 	m_tilemap[4][0]->set_flip(flip);
912 
913 	m_tilemap[0][1]->set_flip(flip);
914 	m_tilemap[1][1]->set_flip(flip);
915 	m_tilemap[2][1]->set_flip(flip);
916 	m_tilemap[3][1]->set_flip(flip);
917 	m_tilemap[4][1]->set_flip(flip);
918 
919 	reg = m_ctrl[0];
920 	if (!flip)  reg = -reg;
921 	m_bgscrollx[0] = reg;
922 
923 	reg = m_ctrl[1] + 4;
924 	if (!flip)  reg = -reg;
925 	m_bgscrollx[1] = reg;
926 
927 	reg = m_ctrl[2] + 8;
928 	if (!flip)  reg = -reg;
929 	m_bgscrollx[2] = reg;
930 
931 	reg = m_ctrl[3] + 12;
932 	if (!flip)  reg = -reg;
933 	m_bgscrollx[3] = reg;
934 
935 	reg = m_ctrl[4];
936 	if (flip)  reg = -reg;
937 	m_bgscrolly[0] = reg;
938 
939 	reg = m_ctrl[5];
940 	if (flip)  reg = -reg;
941 	m_bgscrolly[1] = reg;
942 
943 	reg = m_ctrl[6];
944 	if (flip)  reg = -reg;
945 	m_bgscrolly[2] = reg;
946 
947 	reg = m_ctrl[7];
948 	if (flip)  reg = -reg;
949 	m_bgscrolly[3] = reg;
950 
951 	reg = m_ctrl[0x0c];
952 	if (!flip)  reg -= m_text_xoffs;
953 	if (flip)   reg += m_text_xoffs;
954 	m_tilemap[4][0]->set_scrollx(0, -reg);
955 	m_tilemap[4][1]->set_scrollx(0, -reg);
956 
957 	reg = m_ctrl[0x0d];
958 	if (!flip)  reg -= m_text_yoffs;
959 	if (flip)   reg += m_text_yoffs;
960 	m_tilemap[4][0]->set_scrolly(0, -reg);
961 	m_tilemap[4][1]->set_scrolly(0, -reg);
962 }
963