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