summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/video/decbac06.cpp
blob: c575ba10bda600f11cc5c3ce6f4fde002da2ce65 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
// license:BSD-3-Clause
// copyright-holders:Bryan McPhail, David Haywood
/*
 Deco BAC06 tilemap generator:

 this a direct relative of the later chip implemented in deco16ic.cpp
 we could implement this as either an 8-bit or a 16-bit chip, for now
 I'm using the 16-bit implementation from dec0.cpp

 used by:

 actfancr.cpp
 dec0.cpp
 dec8.cpp (oscar, cobracom, ghostb)
 madmotor.cpp
 stadhero.cpp
 pcktgal.cpp
 thedeep.cpp

 Notes (from dec0.cpp)

 All games contain three BAC06 background generator chips, usual (software)
configuration is 2 chips of 16*16 tiles, 1 of 8*8.

 Playfield control registers:
   bank 0:
   0:
        bit 0 (0x1) set = 8*8 tiles, else 16*16 tiles
        Bit 1 (0x2) set = row major tile layout, else column major*
        bit 2 (0x4) set enables rowscroll
        bit 3 (0x8) set enables colscroll
        bit 7 (0x80) set in playfield 1 is reverse screen (set via dip-switch)
        bit 7 (0x80) in other playfields unknown
   2: unknown (00 in bg, 03 in fg+text - maybe controls pf transparency?)
   4: unknown (always 00) [Used to access 2nd bank of tiles in Stadium Hero)
   6: playfield shape: 00 = 4x1, 01 = 2x2, 02 = 1x4 (low 4 bits only)

   bank 1:
   0: horizontal scroll
   2: vertical scroll
   4: colscroll shifter (low 4 bits, top 4 bits do nothing)
   6: rowscroll shifter (low 4 bits, top 4 bits do nothing)

   Row & column scroll can be applied simultaneously or by themselves.
   The shift register controls the granularity of the scroll offsets
   (more details given later).

   * Bandit is the only game known to use column major tile layout, when in this
   mode X scrolling is also inverted, and tile character data is flipped on X.

Playfield priority (Bad Dudes, etc):
    In the bottommost playfield, pens 8-15 can have priority over the next playfield.
    In that next playfield, pens 8-15 can have priority over sprites.

Bit 0:  Playfield inversion
Bit 1:  Enable playfield mixing (for palettes 8-15 only)
Bit 2:  Enable playfield/sprite mixing (for palettes 8-15 only)

Priority word (Midres):
    Bit 0 set = Playfield 3 drawn over Playfield 2
            ~ = Playfield 2 drawn over Playfield 3
    Bit 1 set = Sprites are drawn inbetween playfields
            ~ = Sprites are on top of playfields
    Bit 2
    Bit 3 set = ...

    Note that priority mixing is handled outside of the BAC-06 chip.

*/

#include "emu.h"
#include "decbac06.h"

#include <algorithm>

DEFINE_DEVICE_TYPE(DECO_BAC06, deco_bac06_device, "deco_back06", "DECO BAC06 Tilemap")

deco_bac06_device::deco_bac06_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
	: device_t(mconfig, DECO_BAC06, tag, owner, clock)
	, m_pf_data(nullptr)
	, m_pf_rowscroll(nullptr)
	, m_pf_colscroll(nullptr)
	, m_tile_region_8(0)
	, m_tile_region_16(0)
	, m_supports_8x8(true)
	, m_supports_16x16(true)
	, m_supports_rc_scroll(true)
	, m_gfxcolmask(0)
	, m_rambank(0)
	, m_gfxregion8x8(0)
	, m_gfxregion16x16(0)
	, m_wide(0)
	, m_bppmult_8x8(0)
	, m_bppmask_8x8(0)
	, m_bppmult_16x16(0)
	, m_bppmask_16x16(0)
	, m_gfxdecode(*this, finder_base::DUMMY_TAG)
	, m_thedeep_kludge(0)
{
	std::fill(std::begin(m_pf_control_0), std::end(m_pf_control_0), 0);
	std::fill(std::begin(m_pf_control_1), std::end(m_pf_control_1), 0);
}

void deco_bac06_device::device_start()
{
	if(!m_gfxdecode->started())
		throw device_missing_dependencies();

	m_pf_data = make_unique_clear<u16[]>(0x4000 / 2); // 0x2000 is the maximum needed, some games / chip setups map less and mirror - stadium hero banks this to 0x4000?!
	m_pf_rowscroll = make_unique_clear<u16[]>(0x2000 / 2);
	m_pf_colscroll = make_unique_clear<u16[]>(0x2000 / 2);

	create_tilemaps(m_gfxregion8x8, m_gfxregion16x16);
	m_gfxcolmask = 0x0f;

	m_bppmult_8x8 = m_gfxdecode->gfx(m_gfxregion8x8)->granularity();
	m_bppmask_8x8 = m_gfxdecode->gfx(m_gfxregion8x8)->depth() - 1;

	m_bppmult_16x16 = m_gfxdecode->gfx(m_gfxregion16x16)->granularity();
	m_bppmask_16x16 = m_gfxdecode->gfx(m_gfxregion16x16)->depth() - 1;

	m_rambank = 0;
	m_flip_screen = false;

	save_pointer(NAME(m_pf_data), 0x4000 / 2);
	save_pointer(NAME(m_pf_rowscroll), 0x2000 / 2);
	save_pointer(NAME(m_pf_colscroll), 0x2000 / 2);
	save_item(NAME(m_pf_control_0));
	save_item(NAME(m_pf_control_1));
	save_item(NAME(m_gfxcolmask));
	save_item(NAME(m_rambank));
	save_item(NAME(m_flip_screen));
}

void deco_bac06_device::device_reset()
{
}

void deco_bac06_device::set_flip_screen(bool flip)
{
	if (m_flip_screen != flip)
	{
		m_flip_screen = flip;
		for (int i = 0; i < 3; i++)
		{
			m_pf8x8_tilemap[i]->set_flip(flip ? (TILEMAP_FLIPY | TILEMAP_FLIPX) : 0);
			m_pf16x16_tilemap[i]->set_flip(flip ? (TILEMAP_FLIPY | TILEMAP_FLIPX) : 0);
		}
	}
}

TILEMAP_MAPPER_MEMBER(deco_bac06_device::tile_shape0_scan)
{
	if ((m_pf_control_0[0] & 2) == 0)
	{
		int col_mask = num_cols - 1;
		return (row & 0xf) + ((col_mask - (col & col_mask)) << 4);
	}
	return (col & 0xf) + ((row & 0xf) << 4) + ((col & 0x1f0) << 4);
}

TILEMAP_MAPPER_MEMBER(deco_bac06_device::tile_shape1_scan)
{
	//if (m_pf_control_0[0] & 2) // Needs testing on real hardware, not used by any game
	//  return (row & 0xf) + ((col & 0x1f) << 4) + ((col & 0xf0) << 5);
	return (col & 0xf) + ((row & 0x1f) << 4) + ((col & 0xf0) << 5);
}

TILEMAP_MAPPER_MEMBER(deco_bac06_device::tile_shape2_scan)
{
	//if (m_pf_control_0[0] & 2)  // Needs testing on real hardware, not used by any game
	//  return (col & 0xf) + ((row & 0x3f) << 4) + ((row & 0x70) << 6);
	return (col & 0xf) + ((row & 0x3f) << 4) + ((col & 0x70) << 6);
}

TILEMAP_MAPPER_MEMBER(deco_bac06_device::tile_shape0_8x8_scan)
{
	//if (m_pf_control_0[0] & 2)   // Needs testing on real hardware, not used by any game
	//  return (col & 0x1f) + ((row & 0x1f) << 5) + ((row & 0x60) << 5);
	return (col & 0x1f) + ((row & 0x1f) << 5) + ((col & 0x60) << 5);
}

TILEMAP_MAPPER_MEMBER(deco_bac06_device::tile_shape1_8x8_scan)
{
	//if (m_pf_control_0[0] & 2)   // Needs testing on real hardware, not used by any game
	//  return (row & 0x1f) + ((col & 0x1f) << 5) + ((col & 0x20) << 5) + ((row & 0x20) << 6);
	return (col & 0x1f) + ((row & 0x1f) << 5) + ((row & 0x20) << 5) + ((col & 0x20) << 6);
}

TILEMAP_MAPPER_MEMBER(deco_bac06_device::tile_shape2_8x8_scan)
{
	//if (m_pf_control_0[0] & 2)   // Needs testing on real hardware, not used by any game
	//  return (row & 0x1f) + ((col & 0x7f) << 5);
	return (col & 0x1f) + ((row & 0x7f) << 5);
}

TILE_GET_INFO_MEMBER(deco_bac06_device::get_pf8x8_tile_info)
{
	if (m_rambank & 1) tile_index += 0x1000;
	int tile = m_pf_data[tile_index];
	int colourpri = (tile >> 12);
	int flags = (m_pf_control_0[0] & 2) ? 0 : TILE_FLIPX;
	tileinfo.set(m_tile_region_8,tile & 0xfff,0,flags);
	tileinfo.category = colourpri;
}

TILE_GET_INFO_MEMBER(deco_bac06_device::get_pf16x16_tile_info)
{
	if (m_rambank & 1) tile_index += 0x1000;
	int tile = m_pf_data[tile_index];
	int colourpri = (tile >> 12);
	int flags = (BIT(m_pf_control_0[0], 1) ^ m_thedeep_kludge) ? 0 : TILE_FLIPX;
	tileinfo.set(m_tile_region_16,tile & 0xfff,0,flags);
	tileinfo.category = colourpri;
}

void deco_bac06_device::create_tilemaps(int region8x8, int region16x16)
{
	m_tile_region_8 = region8x8;
	m_tile_region_16 = region16x16;

	m_pf8x8_tilemap[0] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(deco_bac06_device::get_pf8x8_tile_info)), tilemap_mapper_delegate(*this, FUNC(deco_bac06_device::tile_shape0_8x8_scan)), 8, 8, 128,  32);
	m_pf8x8_tilemap[1] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(deco_bac06_device::get_pf8x8_tile_info)), tilemap_mapper_delegate(*this, FUNC(deco_bac06_device::tile_shape1_8x8_scan)), 8, 8,  64,  64);
	m_pf8x8_tilemap[2] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(deco_bac06_device::get_pf8x8_tile_info)), tilemap_mapper_delegate(*this, FUNC(deco_bac06_device::tile_shape2_8x8_scan)), 8, 8,  32, 128);

	if (m_wide == 2)
	{
		m_pf16x16_tilemap[0] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(deco_bac06_device::get_pf16x16_tile_info)), tilemap_mapper_delegate(*this, FUNC(deco_bac06_device::tile_shape0_scan)), 16, 16, 256, 16);
		m_pf16x16_tilemap[1] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(deco_bac06_device::get_pf16x16_tile_info)), tilemap_mapper_delegate(*this, FUNC(deco_bac06_device::tile_shape1_scan)), 16, 16, 128, 32);
		m_pf16x16_tilemap[2] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(deco_bac06_device::get_pf16x16_tile_info)), tilemap_mapper_delegate(*this, FUNC(deco_bac06_device::tile_shape2_scan)), 16, 16,  64, 64);
	}
	else if (m_wide == 1)
	{
		m_pf16x16_tilemap[0] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(deco_bac06_device::get_pf16x16_tile_info)), tilemap_mapper_delegate(*this, FUNC(deco_bac06_device::tile_shape0_scan)), 16, 16, 128, 16);
		m_pf16x16_tilemap[1] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(deco_bac06_device::get_pf16x16_tile_info)), tilemap_mapper_delegate(*this, FUNC(deco_bac06_device::tile_shape1_scan)), 16, 16,  64, 32);
		m_pf16x16_tilemap[2] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(deco_bac06_device::get_pf16x16_tile_info)), tilemap_mapper_delegate(*this, FUNC(deco_bac06_device::tile_shape2_scan)), 16, 16,  32, 64);
	}
	else
	{
		m_pf16x16_tilemap[0] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(deco_bac06_device::get_pf16x16_tile_info)), tilemap_mapper_delegate(*this, FUNC(deco_bac06_device::tile_shape0_scan)), 16, 16, 64, 16);
		m_pf16x16_tilemap[1] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(deco_bac06_device::get_pf16x16_tile_info)), tilemap_mapper_delegate(*this, FUNC(deco_bac06_device::tile_shape1_scan)), 16, 16, 32, 32);
		m_pf16x16_tilemap[2] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(deco_bac06_device::get_pf16x16_tile_info)), tilemap_mapper_delegate(*this, FUNC(deco_bac06_device::tile_shape2_scan)), 16, 16, 16, 64);
	}
}

void deco_bac06_device::custom_tilemap_draw(bitmap_ind16 &bitmap,
		bitmap_ind8 &primap,
		const rectangle &cliprect,
		tilemap_t *tilemap_ptr,
		const u16 *rowscroll_ptr,
		const u16 *colscroll_ptr,
		const u16 *control0,
		const u16 *control1,
		int flags,
		u16 penmask,
		u16 pencondition,
		u16 colprimask,
		u16 colpricondition,
		u8 bppmult,
		u8 bppmask,
		u8 pri,
		u8 pmask)
{
	const bitmap_ind16 &src_bitmap = tilemap_ptr->pixmap();
	const bitmap_ind8 &flags_bitmap = tilemap_ptr->flagsmap();
	int column_offset = 0, src_x = 0, src_y = 0;
	u32 scrollx = 0;
	u32 scrolly = 0;

	if (control1)
	{
		if (control0 && (control0[0] & 2) == 0) // Use of column major mode inverts scroll direction
			scrollx = -control1[0] - 0x100;
		else
			scrollx = control1[0];
		scrolly = control1[1];
	}

	int row_scroll_enabled = 0;
	int col_scroll_enabled = 0;

	if (m_supports_rc_scroll)
	{
		if (control0)
		{
			row_scroll_enabled = (rowscroll_ptr && (control0[0] & 0x4));
			col_scroll_enabled = (colscroll_ptr && (control0[0] & 0x8));
		}
	}

	const int width_mask = src_bitmap.width() - 1;
	const int height_mask = src_bitmap.height() - 1;

	/* Column scroll & row scroll may per applied per pixel, there are
	shift registers for each which control the granularity of the row/col
	offset (down to per line level for row, and per 8 lines for column).

	Nb:  The row & col selectors are _not_ affected by the shape of the
	playfield (ie, 256*1024, 512*512 or 1024*256).  So even if the tilemap
	width is only 256, 'src_x' should not wrap at 256 in the code below (to
	do so would mean the top half of row RAM would never be accessed which
	is incorrect).

	Nb2:  Real hardware exhibits a strange bug with column scroll on 'mode 2'
	(256*1024) - the first column has a strange additional offset, but
	curiously the first 'wrap' (at scroll offset 256) does not have this offset,
	it is displayed as expected.  The bug is confirmed to only affect this mode,
	the other two modes work as expected.  This bug is not emulated, as it
	doesn't affect any games.
	*/

	if (m_flip_screen)
		src_y = (src_bitmap.height() - 256) - scrolly;
	else
		src_y = scrolly;

	src_y += cliprect.top();
	for (int y = cliprect.top(); y <= cliprect.bottom(); y++)
	{
		u16 *dstpix = &bitmap.pix16(y);
		u8 *dstpri = &primap.pix8(y);
		if (row_scroll_enabled)
			src_x=scrollx + rowscroll_ptr[(src_y >> (control1[3] & 0xf)) & (0x1ff >> (control1[3] & 0xf))];
		else
			src_x=scrollx;

		if (m_flip_screen)
			src_x=(src_bitmap.width() - 256) - src_x;

		src_x += cliprect.left();
		for (int x = cliprect.left(); x <= cliprect.right(); x++)
		{
			if (col_scroll_enabled)
				column_offset=colscroll_ptr[((src_x >> 3) >> (control1[2] & 0xf)) & (0x3f >> (control1[2] & 0xf))];

			const u16 p = src_bitmap.pix16((src_y + column_offset) & height_mask, src_x & width_mask);
			const u8 colpri = flags_bitmap.pix8((src_y + column_offset) & height_mask, src_x & width_mask) & 0xf;

			src_x++;
			if ((flags & TILEMAP_DRAW_OPAQUE) || (p & bppmask))
			{
				if ((p & penmask) == pencondition)
					if((colpri & colprimask) == colpricondition)
					{
						dstpix[x] = p + (colpri & m_gfxcolmask) * bppmult;
						dstpri[x] = (dstpri[x] & pmask) | pri;
					}
			}
		}
		src_y++;
	}
}

void deco_bac06_device::deco_bac06_pf_draw(screen_device &screen,bitmap_ind16 &bitmap,const rectangle &cliprect,int flags,u16 penmask, u16 pencondition,u16 colprimask, u16 colpricondition, u8 pri, u8 primask)
{
	tilemap_t* tm = nullptr;
	u8 bppmult = 0, bppmask = 0;

	int tm_dimensions = m_pf_control_0[3] & 0x3;
	if (tm_dimensions == 3) tm_dimensions = 1; // 3 is invalid / the same as 1?

	if (m_pf_control_0[0] & 0x1) // is 8x8 tiles mode selected?
	{
		if (m_supports_8x8)
		{
			tm = m_pf8x8_tilemap[tm_dimensions];
			bppmult = m_bppmult_8x8;
			bppmask = m_bppmask_8x8;
		}
		else if (m_supports_16x16)
		{
			tm = m_pf16x16_tilemap[tm_dimensions];
			bppmult = m_bppmult_16x16;
			bppmask = m_bppmask_16x16;
		}
	}
	else // 16x16 tiles mode is selected
	{
		if (m_supports_16x16)
		{
			tm = m_pf16x16_tilemap[tm_dimensions];
			bppmult = m_bppmult_16x16;
			bppmask = m_bppmask_16x16;
		}
		else if (m_supports_8x8)
		{
			tm = m_pf8x8_tilemap[tm_dimensions];
			bppmult = m_bppmult_8x8;
			bppmask = m_bppmask_8x8;
		}
	}

	if (tm)
		custom_tilemap_draw(bitmap,screen.priority(),cliprect,tm,m_pf_rowscroll.get(),m_pf_colscroll.get(),m_pf_control_0,m_pf_control_1,flags, penmask, pencondition, colprimask, colpricondition, bppmult, bppmask, pri, primask);
}

// used for pocket gal bootleg, which doesn't set registers properly and simply expects a fixed size tilemap.
void deco_bac06_device::deco_bac06_pf_draw_bootleg(screen_device &screen,bitmap_ind16 &bitmap,const rectangle &cliprect,int flags, int mode, int type, u8 pri, u8 primask)
{
	tilemap_t* tm = nullptr;
	u8 bppmult, bppmask;
	if (!mode)
	{
		tm = m_pf8x8_tilemap[type];
		bppmult = m_bppmult_8x8;
		bppmask = m_bppmask_8x8;
	}
	else
	{
		tm = m_pf16x16_tilemap[type];
		bppmult = m_bppmult_16x16;
		bppmask = m_bppmask_16x16;
	}

	custom_tilemap_draw(bitmap,screen.priority(),cliprect,tm,m_pf_rowscroll.get(),m_pf_colscroll.get(),nullptr,nullptr,flags, 0, 0, 0, 0, bppmult, bppmask, pri, primask);
}



void deco_bac06_device::pf_control_0_w(offs_t offset, u16 data, u16 mem_mask)
{
	int old_register0 = m_pf_control_0[0];

	offset &= 3;

	COMBINE_DATA(&m_pf_control_0[offset]);

	bool dirty_all = false;
	if (offset == 0)
	{
		if ((old_register0 & 2) != (m_pf_control_0[offset] & 2))
		{
			// The tilemap has changed from row major to column major or vice versa.
			// Must force an update of the mapping.
			for (int i = 0; i < 3; i++)
			{
				m_pf8x8_tilemap[i]->mark_mapping_dirty();
				m_pf16x16_tilemap[i]->mark_mapping_dirty();
			}
			dirty_all = true;
		}
	}
	if (offset == 2)
	{
		int newbank = m_pf_control_0[offset] & 1;
		if ((newbank & 1) != (m_rambank & 1))
		{
			// I don't know WHY Stadium Hero uses this as a bank but the RAM test expects it..
			// I'm curious as to if anything else sets it tho
			if (strcmp(machine().system().name,"stadhero"))
				logerror("tilemap ram bank change to %02x\n", newbank & 1);

			dirty_all = true;
			m_rambank = newbank & 1;
		}
	}

	if (dirty_all)
	{
		for (int i = 0; i < 3; i++)
		{
			m_pf8x8_tilemap[i]->mark_all_dirty();
			m_pf16x16_tilemap[i]->mark_all_dirty();
		}
	}
}

u16 deco_bac06_device::pf_control_1_r(offs_t offset)
{
	offset &= 7;
	return m_pf_control_1[offset];
}

void deco_bac06_device::pf_control_1_w(offs_t offset, u16 data, u16 mem_mask)
{
	offset &= 7;
	COMBINE_DATA(&m_pf_control_1[offset]);
}

void deco_bac06_device::pf_data_w(offs_t offset, u16 data, u16 mem_mask)
{
	if (m_rambank & 1) offset += 0x1000;

	COMBINE_DATA(&m_pf_data[offset]);

	for (int i = 0; i < 3; i++)
	{
		m_pf8x8_tilemap[i]->mark_tile_dirty(offset);
		m_pf16x16_tilemap[i]->mark_tile_dirty(offset);
	}
}

u16 deco_bac06_device::pf_data_r(offs_t offset)
{
	if (m_rambank & 1) offset += 0x1000;

	return m_pf_data[offset];
}

void deco_bac06_device::pf_data_8bit_w(offs_t offset, u8 data)
{
	if (offset & 1)
		pf_data_w(offset / 2, data, 0x00ff);
	else
		pf_data_w(offset / 2, data << 8, 0xff00);
}

u8 deco_bac06_device::pf_data_8bit_r(offs_t offset)
{
	if (offset & 1) /* MSB */
		return pf_data_r(offset / 2);
	else
		return pf_data_r(offset / 2)>>8;
}

void deco_bac06_device::pf_rowscroll_w(offs_t offset, u16 data, u16 mem_mask)
{
	COMBINE_DATA(&m_pf_rowscroll[offset]);
}

void deco_bac06_device::pf_colscroll_w(offs_t offset, u16 data, u16 mem_mask)
{
	COMBINE_DATA(&m_pf_colscroll[offset]);
}

u16 deco_bac06_device::pf_rowscroll_r(offs_t offset)
{
	return m_pf_rowscroll[offset];
}

u16 deco_bac06_device::pf_colscroll_r(offs_t offset)
{
	return m_pf_colscroll[offset];
}

/* used by dec8.cpp */
void deco_bac06_device::pf_control0_8bit_w(offs_t offset, u8 data)
{
	if (offset & 1)
		pf_control_0_w(offset / 2, data, 0x00ff); // oscar (mirrors?)
	else
		pf_control_0_w(offset / 2, data, 0x00ff);
}

/* used by dec8.cpp */
u8 deco_bac06_device::pf_control1_8bit_r(offs_t offset)
{
	if (offset & 1)
		return pf_control_1_r(offset / 2);
	else
		return pf_control_1_r(offset / 2)>>8;
}

/* used by dec8.cpp */
void deco_bac06_device::pf_control1_8bit_w(offs_t offset, u8 data)
{
	if (offset<4) // these registers are 16-bit?
	{
		if (offset & 1)
			pf_control_1_w(offset / 2, data, 0x00ff);
		else
			pf_control_1_w(offset / 2, data << 8, 0xff00);
	}
	else // these registers are 8-bit and mirror? (triothep vs actfancr)
	{
		if (offset & 1)
			pf_control_1_w(offset / 2, data, 0x00ff);
		else
			pf_control_1_w(offset / 2, data, 0x00ff);
	}
}

u8 deco_bac06_device::pf_rowscroll_8bit_r(offs_t offset)
{
	if (offset & 1)
		return pf_rowscroll_r(offset / 2);
	else
		return pf_rowscroll_r(offset / 2)>>8;
}


void deco_bac06_device::pf_rowscroll_8bit_w(offs_t offset, u8 data)
{
	if (offset & 1)
		pf_rowscroll_w(offset / 2, data, 0x00ff);
	else
		pf_rowscroll_w(offset / 2, data << 8, 0xff00);
}

u8 deco_bac06_device::pf_rowscroll_8bit_swap_r(offs_t offset)
{
	if (offset & 1)
		return pf_rowscroll_r(offset / 2)>>8;
	else
		return pf_rowscroll_r(offset / 2);
}

void deco_bac06_device::pf_rowscroll_8bit_swap_w(offs_t offset, u8 data)
{
	if (offset & 1)
		pf_rowscroll_w(offset / 2, data << 8, 0xff00);
	else
		pf_rowscroll_w(offset / 2, data, 0x00ff);
}

// used by thedeep
u8 deco_bac06_device::pf_colscroll_8bit_swap_r(offs_t offset)
{
	if (offset & 1)
		return pf_colscroll_r(offset / 2)>>8;
	else
		return pf_colscroll_r(offset / 2);
}

void deco_bac06_device::pf_colscroll_8bit_swap_w(offs_t offset, u8 data)
{
	if (offset & 1)
		pf_colscroll_w(offset / 2, data << 8, 0xff00);
	else
		pf_colscroll_w(offset / 2, data, 0x00ff);
}

/* used by hippodrm */
void deco_bac06_device::pf_control0_8bit_packed_w(offs_t offset, u8 data)
{
	if (offset & 1)
		pf_control_0_w(offset / 2, data << 8, 0xff00);
	else
		pf_control_0_w(offset / 2, data, 0x00ff);
}

/* used by hippodrm */
void deco_bac06_device::pf_control1_8bit_swap_w(offs_t offset, u8 data)
{
	pf_control1_8bit_w(offset ^ 1, data);
}

/* used by hippodrm */
u8 deco_bac06_device::pf_data_8bit_swap_r(offs_t offset)
{
	return pf_data_8bit_r(offset ^ 1);
}

/* used by hippodrm */
void deco_bac06_device::pf_data_8bit_swap_w(offs_t offset, u8 data)
{
	pf_data_8bit_w(offset ^ 1, data);
}