1 // license:BSD-3-Clause
2 // copyright-holders:Mirko Buffoni
3 /***************************************************************************
4 
5   video.c
6 
7   Functions to emulate the video hardware of the machine.
8 
9 ***************************************************************************/
10 
11 #include "emu.h"
12 #include "includes/tutankhm.h"
13 #include "video/resnet.h"
14 
15 #define STAR_RNG_PERIOD         ((1 << 17) - 1)
16 #define RGB_MAXIMUM             224
17 
18 /*************************************
19  *
20  *  Write handlers
21  *
22  *************************************/
23 
WRITE_LINE_MEMBER(tutankhm_state::flip_screen_x_w)24 WRITE_LINE_MEMBER(tutankhm_state::flip_screen_x_w)
25 {
26 	m_flipscreen_x = state;
27 }
28 
29 
WRITE_LINE_MEMBER(tutankhm_state::flip_screen_y_w)30 WRITE_LINE_MEMBER(tutankhm_state::flip_screen_y_w)
31 {
32 	m_flipscreen_y = state;
33 }
34 
35 
36 /*************************************
37  *
38  *  Video update
39  *
40  *************************************/
41 
screen_update_tutankhm_bootleg(screen_device & screen,bitmap_rgb32 & bitmap,const rectangle & cliprect)42 uint32_t tutankhm_state::screen_update_tutankhm_bootleg(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
43 {
44 	bitmap.fill(rgb_t::black(), cliprect);
45 
46 	int xorx = m_flipscreen_x ? 255 : 0;
47 	int xory = m_flipscreen_y ? 255 : 0;
48 
49 	for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
50 	{
51 		uint32_t *const dst = &bitmap.pix(y);
52 
53 		for (int x = cliprect.min_x / GALAXIAN_XSCALE; x <= cliprect.max_x / GALAXIAN_XSCALE; x++)
54 		{
55 			uint8_t effx = x ^ xorx;
56 			uint8_t yscroll = (effx < 192 && m_scroll.found()) ? *m_scroll : 0;
57 			uint8_t effy = (y ^ xory) + yscroll;
58 			uint8_t vrambyte = m_videoram[effy * 128 + effx / 2];
59 			uint8_t shifted = vrambyte >> (4 * (effx % 2));
60 
61 			uint8_t blink_state = m_stars_blink_state & 3;
62 			uint8_t enab = 0;
63 			switch (blink_state)
64 			{
65 				case 0: enab = 1; break;
66 				case 1: enab = (y & 1) == 1; break;
67 				case 2: enab = (y & 2) == 2; break;
68 				case 3: enab = (x & 8) == 0; break;
69 			}
70 			//enab &= (((y>>1) ^ (x >> 3)) & 1);
71 
72 			int offset = y * 384 + x + 84;
73 
74 			uint8_t star = m_stars[offset % STAR_RNG_PERIOD ];
75 			if (m_stars_enabled && enab && (shifted & 0x02) == 0 && (star & 0x80) != 0
76 				&& x > 63)
77 			{
78 				bitmap.pix(y, GALAXIAN_XSCALE*x + 0) = m_star_color[star & 0x3f];
79 				bitmap.pix(y, GALAXIAN_XSCALE*x + 1) = m_star_color[star & 0x3f];
80 				bitmap.pix(y, GALAXIAN_XSCALE*x + 2) = m_star_color[star & 0x3f];
81 			}
82 
83 			else if (shifted)
84 			{
85 				dst[x * GALAXIAN_XSCALE + 0] = m_palette->pen_color(shifted & 0x0f);
86 				dst[x * GALAXIAN_XSCALE + 1] = m_palette->pen_color(shifted & 0x0f);
87 				dst[x * GALAXIAN_XSCALE + 2] = m_palette->pen_color(shifted & 0x0f);
88 			}
89 		}
90 	}
91 
92 	return 0;
93 }
94 
screen_update_tutankhm_scramble(screen_device & screen,bitmap_rgb32 & bitmap,const rectangle & cliprect)95 uint32_t tutankhm_state::screen_update_tutankhm_scramble(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
96 {
97 	scramble_draw_background(bitmap, cliprect);
98 
99 	int xorx = m_flipscreen_x ? 255 : 0;
100 	int xory = m_flipscreen_y ? 255 : 0;
101 
102 	for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
103 	{
104 		uint32_t *const dst = &bitmap.pix(y);
105 
106 		for (int x = cliprect.min_x / GALAXIAN_XSCALE; x <= cliprect.max_x / GALAXIAN_XSCALE; x++)
107 		{
108 			uint8_t effx = x ^ xorx;
109 			uint8_t yscroll = (effx < 192 && m_scroll.found()) ? *m_scroll : 0;
110 			uint8_t effy = (y ^ xory) + yscroll;
111 			uint8_t vrambyte = m_videoram[effy * 128 + effx / 2];
112 			uint8_t shifted = vrambyte >> (4 * (effx % 2));
113 			if (shifted)
114 			{
115 				dst[x * GALAXIAN_XSCALE + 0] = m_palette->pen_color(shifted & 0x0f);
116 				dst[x * GALAXIAN_XSCALE + 1] = m_palette->pen_color(shifted & 0x0f);
117 				dst[x * GALAXIAN_XSCALE + 2] = m_palette->pen_color(shifted & 0x0f);
118 			}
119 		}
120 	}
121 
122 	return 0;
123 }
124 
screen_update_tutankhm(screen_device & screen,bitmap_rgb32 & bitmap,const rectangle & cliprect)125 uint32_t tutankhm_state::screen_update_tutankhm(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
126 {
127 	u8 mode = m_stars_config.read_safe(m_star_mode);
128 	if (mode != m_star_mode)
129 	{
130 		m_star_mode = mode;
131 		stars_init();
132 	}
133 
134 	if (m_star_mode)
135 		return screen_update_tutankhm_scramble(screen, bitmap, cliprect);
136 	else
137 		return screen_update_tutankhm_bootleg(screen, bitmap, cliprect);
138 }
139 
140 /*************************************
141  *
142  *  Copied from galaxian.cpp video code
143  *
144  *************************************/
145 
raw_to_rgb_func(u32 raw)146 rgb_t tutankhm_state::raw_to_rgb_func(u32 raw)
147 {
148 	static const int rgb_resistances[3] = { 1000, 470, 220 };
149 	/*
150 	    Sprite/tilemap colors are mapped through a color PROM as follows:
151 
152 	      bit 7 -- 220 ohm resistor  -- BLUE
153 	            -- 470 ohm resistor  -- BLUE
154 	            -- 220 ohm resistor  -- GREEN
155 	            -- 470 ohm resistor  -- GREEN
156 	            -- 1  kohm resistor  -- GREEN
157 	            -- 220 ohm resistor  -- RED
158 	            -- 470 ohm resistor  -- RED
159 	      bit 0 -- 1  kohm resistor  -- RED
160 
161 	    Note that not all boards have this configuration. Namco PCBs may
162 	    have 330 ohm resistors instead of 220, but the default setup has
163 	    also been used by Namco.
164 
165 	    In parallel with these resistors are a pair of 150 ohm and 100 ohm
166 	    resistors on each R,G,B component that are connected to the star
167 	    generator.
168 
169 	    And in parallel with the whole mess are a set of 100 ohm resistors
170 	    on each R,G,B component that are enabled when a shell/missile is
171 	    enabled.
172 
173 	    When computing weights, we use RGB_MAXIMUM as the maximum to give
174 	    headroom for stars and shells/missiles. This is not fully accurate,
175 	    but if we included all possible sources in parallel, the brightness
176 	    of the main game would be very low to allow for all the oversaturation
177 	    of the stars and shells/missiles.
178 	*/
179 	double rweights[3], gweights[3], bweights[2];
180 	compute_resistor_weights(0, RGB_MAXIMUM, -1.0,
181 			3, &rgb_resistances[0], rweights, 470, 0,
182 			3, &rgb_resistances[0], gweights, 470, 0,
183 			2, &rgb_resistances[1], bweights, 470, 0);
184 
185 	// decode the palette first
186 
187 	uint8_t bit0, bit1, bit2;
188 
189 	// red component
190 	bit0 = BIT(raw, 0);
191 	bit1 = BIT(raw, 1);
192 	bit2 = BIT(raw, 2);
193 	int const r = combine_weights(rweights, bit0, bit1, bit2);
194 
195 	// green component
196 	bit0 = BIT(raw, 3);
197 	bit1 = BIT(raw, 4);
198 	bit2 = BIT(raw, 5);
199 	int const g = combine_weights(gweights, bit0, bit1, bit2);
200 
201 	// blue component
202 	bit0 = BIT(raw, 6);
203 	bit1 = BIT(raw, 7);
204 	int const b = combine_weights(bweights, bit0, bit1);
205 
206 	return rgb_t(r, g, b);
207 }
208 
galaxian_palette(palette_device & palette)209 void tutankhm_state::galaxian_palette(palette_device &palette)
210 {
211 	/*
212 	    The maximum sprite/tilemap resistance is ~130 Ohms with all RGB
213 	    outputs enabled (1/(1/1000 + 1/470 + 1/220)). Since we normalized
214 	    to RGB_MAXIMUM, this maps RGB_MAXIMUM -> 130 Ohms.
215 
216 	    The stars are at 150 Ohms for the LSB, and 100 Ohms for the MSB.
217 	    This means the 3 potential values are:
218 
219 	        150 Ohms -> RGB_MAXIMUM * 130 / 150
220 	        100 Ohms -> RGB_MAXIMUM * 130 / 100
221 	         60 Ohms -> RGB_MAXIMUM * 130 / 60
222 
223 	    Since we can't saturate that high, we instead approximate this
224 	    by compressing the values proportionally into the 194->255 range.
225 	*/
226 	int const minval = RGB_MAXIMUM * 130 / 150;
227 	int const midval = RGB_MAXIMUM * 130 / 100;
228 	int const maxval = RGB_MAXIMUM * 130 / 60;
229 
230 	// compute the values for each of 4 possible star values
231 	uint8_t const starmap[4]{
232 			0,
233 			minval,
234 			minval + (255 - minval) * (midval - minval) / (maxval - minval),
235 			255 };
236 
237 	// generate the colors for the stars
238 	for (int i = 0; i < 64; i++)
239 	{
240 		uint8_t bit0, bit1;
241 
242 		// bit 5 = red @ 150 Ohm, bit 4 = red @ 100 Ohm
243 		bit0 = BIT(i, 5);
244 		bit1 = BIT(i, 4);
245 		int const r = starmap[(bit1 << 1) | bit0];
246 
247 		// bit 3 = green @ 150 Ohm, bit 2 = green @ 100 Ohm
248 		bit0 = BIT(i, 3);
249 		bit1 = BIT(i, 2);
250 		int const g = starmap[(bit1 << 1) | bit0];
251 
252 		// bit 1 = blue @ 150 Ohm, bit 0 = blue @ 100 Ohm
253 		bit0 = BIT(i, 1);
254 		bit1 = BIT(i, 0);
255 		int const b = starmap[(bit1 << 1) | bit0];
256 
257 		// set the RGB color
258 		m_star_color[i] = rgb_t(r, g, b);
259 	}
260 }
261 
262 
video_start()263 void tutankhm_state::video_start()
264 {
265 	/* initialize globals */
266 	m_flipscreen_x = 0;
267 	m_flipscreen_y = 0;
268 
269 	/* initialize stars */
270 	m_stars_enabled = 0;
271 	m_stars_blink_state = 0;
272 	stars_init();
273 
274 	galaxian_palette(*m_palette);
275 }
276 
stars_init()277 void tutankhm_state::stars_init()
278 {
279 	(m_star_mode) ? stars_init_scramble() : stars_init_bootleg();
280 }
281 
stars_init_bootleg()282 void tutankhm_state::stars_init_bootleg()
283 {
284 	/* reset the blink and enabled states */
285 	m_stars_enabled = false;
286 	m_stars_blink_state = 0;
287 
288 	/* precalculate the RNG */
289 	m_stars = std::make_unique<uint8_t[]>(STAR_RNG_PERIOD);
290 	uint32_t shiftreg = 0;
291 	for (int i = 0; i < STAR_RNG_PERIOD; i++)
292 	{
293 		int newbit = ((shiftreg >> 12) ^ ~shiftreg) & 1;
294 
295 		/* stars are enabled if the upper 8 bits are 1 and the new bit is 0 */
296 		int enabled = ((shiftreg & 0x1fe00) == 0x1fe00) && (newbit == 0);
297 		//int enabled = ((shiftreg & 0x1fe01) == 0x1fe00); // <- scramble
298 
299 		/* color comes from the 6 bits below the top 8 bits */
300 		int color = (~shiftreg & 0x1f8) >> 3;
301 
302 		/* store the color value in the low 6 bits and the enable in the upper bit */
303 		m_stars[i] = color | (enabled << 7);
304 
305 		/* the LFSR is fed based on the XOR of bit 12 and the inverse of bit 0 */
306 		shiftreg = (shiftreg >> 1) | (newbit << 16);
307 	}
308 }
309 
stars_init_scramble()310 void tutankhm_state::stars_init_scramble()
311 {
312 	/* precalculate the RNG */
313 	m_stars = std::make_unique<uint8_t[]>(STAR_RNG_PERIOD);
314 	uint32_t shiftreg = 0;
315 	for (int i = 0; i < STAR_RNG_PERIOD; i++)
316 	{
317 		const uint8_t shift = 12;
318 		/* stars are enabled if the upper 8 bits are 1 and the low bit is 0 */
319 		int enabled = ((shiftreg & 0x1fe01) == 0x1fe00);
320 
321 		/* color comes from the 6 bits below the top 8 bits */
322 		int color = (~shiftreg & 0x1f8) >> 3;
323 
324 		/* store the color value in the low 6 bits and the enable in the upper bit */
325 		m_stars[i] = color | (enabled << 7);
326 
327 		/* the LFSR is fed based on the XOR of bit 12 and the inverse of bit 0 */
328 		//shiftreg = (shiftreg >> 1) | ((((shiftreg >> 12) ^ ~shiftreg) & 1) << 16);
329 		shiftreg = (shiftreg >> 1) | ((((shiftreg >> shift) ^ ~shiftreg) & 1) << 16);
330 	}
331 }
332 
333 
334 
335 /*************************************
336  *
337  *  Star blinking
338  *
339  *************************************/
340 
TIMER_DEVICE_CALLBACK_MEMBER(tutankhm_state::scramble_stars_blink_timer)341 TIMER_DEVICE_CALLBACK_MEMBER(tutankhm_state::scramble_stars_blink_timer)
342 {
343 	m_stars_blink_state++;
344 }
345 
stars_draw_row(bitmap_rgb32 & bitmap,int maxx,int y,uint32_t star_offs)346 void tutankhm_state::stars_draw_row(bitmap_rgb32 &bitmap, int maxx, int y, uint32_t star_offs)
347 {
348 	uint8_t flipxor = (m_flipscreen_x ? 0xC0 : 0x00);
349 
350 	/* ensure our star offset is valid */
351 	star_offs %= STAR_RNG_PERIOD;
352 
353 	/* iterate over the specified number of 6MHz pixels */
354 	for (int x = 0; x < maxx; x++)
355 	{
356 		uint8_t h8q = ((x>>3) & 1) ^ 1; // H8 signal is inverted.
357 		/* stars are suppressed unless V1 ^ H8 == 1 */
358 		int enable_star = (y ^ h8q) & 1;
359 		uint8_t star;
360 
361 		uint8_t blink_state = m_stars_blink_state & 3;
362 		uint8_t enab = 0;
363 		switch (blink_state)
364 		{
365 			case 0: enab = 1;            break;
366 			case 1: enab = (y & 1) == 1; break;
367 			case 2: enab = (y & 2) == 2; break;
368 			case 3: enab = h8q;          break; // H8 signal is inverted.
369 		}
370 
371 		enable_star &= (enab && ((x & 0xC0) ^ flipxor) != 0xC0);
372 
373 		/*
374 		    The RNG clock is the master clock (18MHz) ANDed with the pixel clock (6MHz).
375 		    The divide-by-3 circuit that produces the pixel clock generates a square wave
376 		    with a 2/3 duty cycle, so the result of the AND generates a clock like this:
377 		                _   _   _   _   _   _   _   _
378 		      MASTER: _| |_| |_| |_| |_| |_| |_| |_| |
379 		                _______     _______     ______
380 		      PIXEL:  _|       |___|       |___|
381 		                _   _       _   _       _   _
382 		      RNG:    _| |_| |_____| |_| |_____| |_| |
383 
384 		    Thus for each pixel, there are 3 master clocks and 2 RNG clocks, and the RNG
385 		    is clocked asymmetrically. To simulate this, we expand the horizontal screen
386 		    size by 3 and handle the first RNG clock with one pixel and the second RNG
387 		    clock with two pixels.
388 		*/
389 
390 		/* first RNG clock: one pixel */
391 		star = m_stars[star_offs++];
392 		if (star_offs >= STAR_RNG_PERIOD)
393 			star_offs = 0;
394 		if (enable_star && (star & 0x80) != 0)
395 			bitmap.pix(y, GALAXIAN_XSCALE*x + 0) = m_star_color[star & 0x3f];
396 
397 		/* second RNG clock: two pixels */
398 		star = m_stars[star_offs++];
399 		if (star_offs >= STAR_RNG_PERIOD)
400 			star_offs = 0;
401 		if (enable_star && (star & 0x80) != 0)
402 		{
403 			bitmap.pix(y, GALAXIAN_XSCALE*x + 1) = m_star_color[star & 0x3f];
404 			bitmap.pix(y, GALAXIAN_XSCALE*x + 2) = m_star_color[star & 0x3f];
405 		}
406 	}
407 }
408 
scramble_draw_stars(bitmap_rgb32 & bitmap,const rectangle & cliprect,int maxx)409 void tutankhm_state::scramble_draw_stars(bitmap_rgb32 &bitmap, const rectangle &cliprect, int maxx)
410 {
411 	/* update the star origin to the current frame */
412 	//stars_update_origin();
413 
414 	/* render stars if enabled */
415 	if (m_stars_enabled)
416 	{
417 		/* iterate over scanlines */
418 		for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
419 		{
420 			stars_draw_row(bitmap, maxx, y, y * 512);
421 		}
422 	}
423 }
424 
425 
scramble_draw_background(bitmap_rgb32 & bitmap,const rectangle & cliprect)426 void tutankhm_state::scramble_draw_background(bitmap_rgb32 &bitmap, const rectangle &cliprect)
427 {
428 	/* blue background - 390 ohm resistor */
429 	bitmap.fill(rgb_t::black(), cliprect);
430 
431 	scramble_draw_stars(bitmap, cliprect, 256);
432 }
433 
galaxian_stars_enable_w(uint8_t data)434 void tutankhm_state::galaxian_stars_enable_w(uint8_t data)
435 {
436 	if ((m_stars_enabled ^ data) & 0x01)
437 	{
438 //      m_screen->update_now();
439 		m_screen->update_partial(m_screen->vpos());
440 	}
441 
442 	m_stars_enabled = data & 0x01;
443 }
444