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