1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles,Nicola Salmoria
3 /***************************************************************************
4 
5     Art & Magic hardware
6 
7 ***************************************************************************/
8 
9 #include "emu.h"
10 #include "cpu/tms34010/tms34010.h"
11 #include "video/tlc34076.h"
12 #include "includes/artmagic.h"
13 
14 
15 #define INSTANT_BLIT        1
16 
17 
18 /*************************************
19  *
20  *  Inlines
21  *
22  *************************************/
23 
address_to_vram(offs_t * address)24 inline uint16_t *artmagic_state::address_to_vram(offs_t *address)
25 {
26 	offs_t original = *address;
27 	*address = (original & 0x001fffff) >> 4;
28 	if (original < 0x001fffff)
29 		return m_vram[0];
30 	else if (original >= 0x00400000 && original < 0x005fffff)
31 		return m_vram[1];
32 	return nullptr;
33 }
34 
35 
36 
37 /*************************************
38  *
39  *  Video start
40  *
41  *************************************/
42 
video_start()43 void artmagic_state::video_start()
44 {
45 	save_item(NAME(m_xor));
46 	save_item(NAME(m_is_stoneball));
47 	save_item(NAME(m_blitter_data));
48 	save_item(NAME(m_blitter_page));
49 }
50 
51 
52 
53 /*************************************
54  *
55  *  Shift register transfers
56  *
57  *************************************/
58 
TMS340X0_TO_SHIFTREG_CB_MEMBER(artmagic_state::to_shiftreg)59 TMS340X0_TO_SHIFTREG_CB_MEMBER(artmagic_state::to_shiftreg)
60 {
61 	uint16_t *vram = address_to_vram(&address);
62 	if (vram)
63 		memcpy(shiftreg, &vram[address], 0x400);
64 }
65 
66 
TMS340X0_FROM_SHIFTREG_CB_MEMBER(artmagic_state::from_shiftreg)67 TMS340X0_FROM_SHIFTREG_CB_MEMBER(artmagic_state::from_shiftreg)
68 {
69 	uint16_t *vram = address_to_vram(&address);
70 	if (vram)
71 		memcpy(&vram[address], shiftreg, 0x400);
72 }
73 
74 
75 
76 /*************************************
77  *
78  *  Custom blitter
79  *
80  *************************************/
81 
execute_blit()82 void artmagic_state::execute_blit()
83 {
84 	uint16_t *dest = m_vram[m_blitter_page ^ 1];
85 	int offset = ((m_blitter_data[1] & 0xff) << 16) | m_blitter_data[0];
86 	int color = (m_blitter_data[1] >> 4) & 0xf0;
87 	int x = (int16_t)m_blitter_data[2];
88 	int y = (int16_t)m_blitter_data[3];
89 	int maskx = m_blitter_data[6] & 0xff;
90 	int masky = m_blitter_data[6] >> 8;
91 	int w = ((m_blitter_data[7] & 0xff) + 1) * 4;
92 	int h = (m_blitter_data[7] >> 8) + 1;
93 	int i, j, sx, sy, last;
94 
95 #if 0
96 {
97 	static uint32_t hit_list[50000];
98 	static int hit_index;
99 	static FILE *f;
100 
101 	logerror("%s:Blit from %06X to (%d,%d) %dx%d -- %04X %04X %04X %04X %04X %04X %04X %04X\n",
102 				machine().describe_context(), offset, x, y, w, h,
103 				m_blitter_data[0], m_blitter_data[1],
104 				m_blitter_data[2], m_blitter_data[3],
105 				m_blitter_data[4], m_blitter_data[5],
106 				m_blitter_data[6], m_blitter_data[7]);
107 
108 	if (!f) f = fopen("artmagic.log", "w");
109 
110 	for (i = 0; i < hit_index; i++)
111 		if (hit_list[i] == offset)
112 			break;
113 	if (i == hit_index)
114 	{
115 		int tempoffs = offset;
116 		hit_list[hit_index++] = offset;
117 
118 		fprintf(f, "----------------------\n"
119 					"%s:Blit from %06X to (%d,%d) %dx%d -- %04X %04X %04X %04X %04X %04X %04X %04X\n",
120 					machine().describe_context(), offset, x, y, w, h,
121 					m_blitter_data[0], m_blitter_data[1],
122 					m_blitter_data[2], m_blitter_data[3],
123 					m_blitter_data[4], m_blitter_data[5],
124 					m_blitter_data[6], m_blitter_data[7]);
125 
126 		fprintf(f, "\t");
127 		for (i = 0; i < h; i++)
128 		{
129 			for (j = 0; j < w; j += 4)
130 				fprintf(f, "%04X ", m_blitter_base[tempoffs++]);
131 			fprintf(f, "\n\t");
132 		}
133 		fprintf(f, "\n\t");
134 		tempoffs = offset;
135 		for (i = 0; i < h; i++)
136 		{
137 			last = 0;
138 			if (i == 0) /* first line */
139 			{
140 				/* ultennis, stonebal */
141 				last ^= (m_blitter_data[7] & 0x0001);
142 				if (m_is_stoneball)
143 					last ^= ((m_blitter_data[0] & 0x0020) >> 3);
144 				else    /* ultennis */
145 					last ^= ((m_blitter_data[0] & 0x0040) >> 4);
146 
147 				/* cheesech */
148 				last ^= ((m_blitter_data[7] & 0x0400) >> 9);
149 				last ^= ((m_blitter_data[0] & 0x2000) >> 10);
150 			}
151 			else    /* following lines */
152 			{
153 				int val = m_blitter_base[tempoffs];
154 
155 				/* ultennis, stonebal */
156 				last ^= 4;
157 				last ^= ((val & 0x0400) >> 8);
158 				last ^= ((val & 0x5000) >> 12);
159 
160 				/* cheesech */
161 				last ^= 8;
162 				last ^= ((val & 0x0800) >> 8);
163 				last ^= ((val & 0xa000) >> 12);
164 			}
165 
166 			for (j = 0; j < w; j += 4)
167 			{
168 				static const char hex[] = ".123456789ABCDEF";
169 				int val = m_blitter_base[tempoffs++];
170 				int p1, p2, p3, p4;
171 				p1 = last = ((val ^ m_xor[last]) >>  0) & 0xf;
172 				p2 = last = ((val ^ m_xor[last]) >>  4) & 0xf;
173 				p3 = last = ((val ^ m_xor[last]) >>  8) & 0xf;
174 				p4 = last = ((val ^ m_xor[last]) >> 12) & 0xf;
175 				fprintf(f, "%c%c%c%c ", hex[p1], hex[p2], hex[p3], hex[p4]);
176 			}
177 			fprintf(f, "\n\t");
178 		}
179 		fprintf(f, "\n");
180 	}
181 }
182 #endif
183 
184 	g_profiler.start(PROFILER_VIDEO);
185 
186 	last = 0;
187 	sy = y;
188 	for (i = 0; i < h; i++)
189 	{
190 		if ((i & 1) || !((masky << ((i/2) & 7)) & 0x80))
191 		{
192 			if (sy >= 0 && sy < 256)
193 			{
194 				int tsy = sy * 0x200;
195 				sx = x;
196 
197 				/* The first pixel of every line doesn't have a previous pixel
198 				   to depend on, so it takes the "feed" from other bits.
199 				   The very first pixel blitted is also treated differently.
200 
201 				   ultennis/stonebal use a different encryption from cheesech,
202 				   however the former only need to set bits 0 and 2 of the
203 				   feed (the others are irrelevant), while the latter only
204 				   bits 1 and 3, so I can handle both at the same time.
205 				 */
206 				last = 0;
207 				if (i == 0) /* first line */
208 				{
209 					/* ultennis, stonebal */
210 					last ^= (m_blitter_data[7] & 0x0001);
211 					if (m_is_stoneball)
212 						last ^= ((m_blitter_data[0] & 0x0020) >> 3);
213 					else    /* ultennis */
214 						last ^= (((m_blitter_data[0] + 1) & 0x0040) >> 4);
215 
216 					/* cheesech */
217 					last ^= ((m_blitter_data[7] & 0x0400) >> 9);
218 					last ^= ((m_blitter_data[0] & 0x2000) >> 10);
219 				}
220 				else    /* following lines */
221 				{
222 					int val = m_blitter_base[offset & m_blitter_base.mask()];
223 
224 					/* ultennis, stonebal */
225 					last ^= 4;
226 					last ^= ((val & 0x0400) >> 8);
227 					last ^= ((val & 0x5000) >> 12);
228 
229 					/* cheesech */
230 					last ^= 8;
231 					last ^= ((val & 0x0800) >> 8);
232 					last ^= ((val & 0xa000) >> 12);
233 				}
234 
235 				for (j = 0; j < w; j += 4)
236 				{
237 					uint16_t val = m_blitter_base[(offset + j/4) & m_blitter_base.mask()];
238 					if (sx < 508)
239 					{
240 						if (h == 1 && m_is_stoneball)
241 							last = ((val) >>  0) & 0xf;
242 						else
243 							last = ((val ^ m_xor[last]) >>  0) & 0xf;
244 						if (!((maskx << ((j/2) & 7)) & 0x80))
245 						{
246 							if (last && sx >= 0 && sx < 512)
247 								dest[tsy + sx] = color | (last);
248 							sx++;
249 						}
250 
251 						if (h == 1 && m_is_stoneball)
252 							last = ((val) >>  4) & 0xf;
253 						else
254 							last = ((val ^ m_xor[last]) >>  4) & 0xf;
255 						{
256 							if (last && sx >= 0 && sx < 512)
257 								dest[tsy + sx] = color | (last);
258 							sx++;
259 						}
260 
261 						if (h == 1 && m_is_stoneball)
262 							last = ((val) >>  8) & 0xf;
263 						else
264 							last = ((val ^ m_xor[last]) >>  8) & 0xf;
265 						if (!((maskx << ((j/2) & 7)) & 0x40))
266 						{
267 							if (last && sx >= 0 && sx < 512)
268 								dest[tsy + sx] = color | (last);
269 							sx++;
270 						}
271 
272 						if (h == 1 && m_is_stoneball)
273 							last = ((val) >> 12) & 0xf;
274 						else
275 							last = ((val ^ m_xor[last]) >> 12) & 0xf;
276 						{
277 							if (last && sx >= 0 && sx < 512)
278 								dest[tsy + sx] = color | (last);
279 							sx++;
280 						}
281 					}
282 				}
283 			}
284 			sy++;
285 		}
286 		offset += w/4;
287 	}
288 
289 	g_profiler.stop();
290 
291 #if (!INSTANT_BLIT)
292 	m_blitter_busy_until = machine.time() + attotime::from_nsec(w*h*20);
293 #endif
294 }
295 
296 
blitter_r()297 uint16_t artmagic_state::blitter_r()
298 {
299 	/*
300 	    bit 1 is a busy flag; loops tightly if clear
301 	    bit 2 is tested in a similar fashion
302 	    bit 4 reflects the page
303 	*/
304 	uint16_t result = 0xffef | (m_blitter_page << 4);
305 #if (!INSTANT_BLIT)
306 	if (attotime_compare(machine().time(), m_blitter_busy_until) < 0)
307 		result ^= 6;
308 #endif
309 	return result;
310 }
311 
312 
blitter_w(offs_t offset,uint16_t data,uint16_t mem_mask)313 void artmagic_state::blitter_w(offs_t offset, uint16_t data, uint16_t mem_mask)
314 {
315 	COMBINE_DATA(&m_blitter_data[offset]);
316 
317 	/* offset 3 triggers the blit */
318 	if (offset == 3)
319 		execute_blit();
320 
321 	/* offset 4 contains the target page */
322 	else if (offset == 4)
323 		m_blitter_page = (data >> 1) & 1;
324 }
325 
326 
327 
328 /*************************************
329  *
330  *  Video update
331  *
332  *************************************/
333 
TMS340X0_SCANLINE_RGB32_CB_MEMBER(artmagic_state::scanline)334 TMS340X0_SCANLINE_RGB32_CB_MEMBER(artmagic_state::scanline)
335 {
336 	offs_t offset = (params->rowaddr << 12) & 0x7ff000;
337 	uint16_t const *vram = address_to_vram(&offset);
338 	uint32_t *const dest = &bitmap.pix(scanline);
339 	pen_t const *const pens = m_tlc34076->pens();
340 	int coladdr = params->coladdr << 1;
341 
342 	vram += offset;
343 	for (int x = params->heblnk; x < params->hsblnk; x++)
344 		dest[x] = pens[vram[coladdr++ & 0x1ff] & 0xff];
345 }
346