summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/video/snk68_spr.cpp
blob: 7115713669b626cdb5426a672565ea02283b814f (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
// license:BSD-3-Clause
// copyright-holders:Bryan McPhail, Acho A. Tang, Nicola Salmoria
/*
 * Alpha 68k II/V sprite system
 * tile-based, with 8-bit accesses.
 * Two banks, first at 0x0000-0x1000, second at 0x1000 until end of VRAM.
 * First bank is processed by 64 bytes stepping starting from address $8,
 * then once it reaches end it restarts at $c, finally at $4.
 *
 * 0x0000-0x1000
 * [0]
 * ???? ???? untested in POST, actually written to by Gang Wars ($ff in snk68.cpp so NOP)
 * [1]
 * XXXX XXXX lower X offset
 * [2]
 * X--- ---- upper X offset
 * -*** ---- unknown, more fractional X? Definitely used by snk68.cpp
 * ---- ---Y upper Y offset
 * [3]
 * YYYY YYYY lower Y offset
 *
 * Second bank has the actual tile info, and is arranged in vertical strips.
 * [0]
 * ???? ????  untested in POST, actually written to by Sky Adventure ($ff in snk68.cpp so NOP)
 * [1]
 * cccc cccc color entry
 * [2]
 * uuu- ---- user selectable, either flipx/flipy or tile bank
 * ---t tttt high tile offset
 * [3]
 * tttt tttt low tile offset
 *
 * TODO:
 * - Currently lags compared to itself in Sky Adventure at least, examples:
 *   - player death animation has first frame with inverted horizontal halves;
 *   - stage 1 priest desyncs with background;
 *   - glitchy first frame on title screen;
 *   Given how this and the actual HW works it is pretty likely this having a consistent delay,
 *   however it isn't known how exactly DMA triggers, and one frame of bufferd spriteram isn't enough.
 * - Why entry 0x7c0 requires a one line and a priority hack for alpha68k.cpp games?
 * - Super Champion Baseball: selecting a 3rd or 5th column team causes a shadow to appear on the drawn
 *   object which isn't present on the other columns, verify real HW behaviour;
 * - Sky Adventure: stage 1 priest priority goes above the bonus ship, confirmed to be a btanb;
 *
 */

#include "emu.h"
#include "snk68_spr.h"

DEFINE_DEVICE_TYPE(SNK68_SPR, snk68_spr_device, "snk68_spr", "SNK68 Sprites")

snk68_spr_device::snk68_spr_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, SNK68_SPR, tag, owner, clock)
	, m_newtilecb(*this, FUNC(snk68_spr_device::tile_callback_noindirect))
	, m_gfxdecode(*this, finder_base::DUMMY_TAG)
	, m_spriteram(*this, "^spriteram")
	, m_screen(*this, "^screen")
	, m_flipscreen(false)
	, m_partialupdates(1)
{
}

void snk68_spr_device::tile_callback_noindirect(int &tile, int& fx, int& fy, int& region)
{
}

void snk68_spr_device::device_start()
{
	// bind our handler
	m_newtilecb.resolve();
}

void snk68_spr_device::device_reset()
{
}

READ16_MEMBER(snk68_spr_device::spriteram_r)
{
	// streetsj expects the MSB of every 32-bit word to be FF. Presumably RAM
	// exists only for 3 bytes out of 4 and the fourth is unmapped.
	if (!(offset & 1))
		return m_spriteram[offset] | 0xff00;
	else
		return m_spriteram[offset];
}

WRITE16_MEMBER(snk68_spr_device::spriteram_w)
{
	uint16_t newword = m_spriteram[offset];

	if (!(offset & 1))
		data |= 0xff00;

	COMBINE_DATA(&newword);

	if (m_spriteram[offset] != newword)
	{
		int vpos = m_screen->vpos();

		if (vpos > 0)
			if (m_partialupdates) m_screen->update_partial(vpos - 1);

		m_spriteram[offset] = newword;
	}
}

void snk68_spr_device::draw_sprites(bitmap_ind16 &bitmap, const rectangle &cliprect, int group, u16 start_offset, u16 end_offset)
{
	const uint16_t* tiledata = &m_spriteram[0x800*group + start_offset];

	bool const flip = m_flipscreen;

	for (int offs = start_offset; offs < end_offset; offs += 0x40)
	{
		int mx = (m_spriteram[offs + 2*group] & 0xff) << (16-m_xpos_shift);
		int my = m_spriteram[offs + 2*group + 1];
		int i;

		// TODO: for some reason alpha68k wants this to be 15 instead of 12, different row/col arrangement?
		mx = mx | (my >> m_xpos_shift);

		mx = ((mx + 16) & 0x1ff) - 16;
		my = -my;

		// TODO: alpha68k games all wants this hack, why?
		if (group == 1 && start_offset == 0x7c0)
			my++;

		if (flip)
		{
			mx = 240 - mx;
			my = 240 - my;
		}

		// every sprite is a column 32 tiles (512 pixels) tall
		for (i = 0; i < 0x20; ++i)
		{
			my &= 0x1ff;

			if (my <= cliprect.max_y && my + 15 >= cliprect.min_y)
			{
				int color = *(tiledata++) & m_color_entry_mask;
				int tile = *(tiledata++);
				int fx = 0,fy = 0;
				int region = 0;

				m_newtilecb(tile, fx, fy, region);

				// the black touch '96 cloned hardware has some tiles
				// as 8bpp, we need to shift the colour bits in those cases
				int depth = m_gfxdecode->gfx(region)->depth();
				if (depth == 256) color >>= 4;

				if (flip)
				{
					fx = !fx;
					fy = !fy;
				}

				m_gfxdecode->gfx(region)->transpen(bitmap,cliprect,
						tile,
						color,
						fx, fy,
						mx, my, 0);
			}
			else
			{
				tiledata += 2;
			}

			if (flip)
				my -= 16;
			else
				my += 16;
		}
	}
}

void snk68_spr_device::draw_sprites_all(bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	/* This appears to be the correct priority order */
	draw_sprites(bitmap, cliprect, 2, 0, 0x800);
	draw_sprites(bitmap, cliprect, 3, 0, 0x800);
	draw_sprites(bitmap, cliprect, 1, 0, 0x800);
}

void snk68_spr_device::draw_sprites_alt(bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	// TODO: alpha68k priority, different vram accessors? Special meaning of bit 0 for my variable?
	draw_sprites(bitmap, cliprect, 1, 0x7c0, 0x800);
	draw_sprites(bitmap, cliprect, 2, 0x000, 0x800);
	draw_sprites(bitmap, cliprect, 3, 0x000, 0x800);
	draw_sprites(bitmap, cliprect, 1, 0x000, 0x7c0);
}

void snk68_spr_device::set_flip(bool flip)
{
	m_flipscreen = flip;
}