1 // license:BSD-3-Clause
2 // copyright-holders:Nicola Salmoria, Tormod Tjaberg, Mirko Buffoni,Lee Taylor, Valerio Verrando, Zsolt Vasvari
3 // thanks-to:Michael Strutts, Marco Cassili
4 /***************************************************************************
5 
6     Midway 8080-based black and white hardware
7 
8 ****************************************************************************/
9 
10 #include "emu.h"
11 #include "includes/mw8080bw.h"
12 
13 
screen_update_mw8080bw(screen_device & screen,bitmap_rgb32 & bitmap,const rectangle & cliprect)14 uint32_t mw8080bw_state::screen_update_mw8080bw(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
15 {
16 	uint8_t x = 0;
17 	uint8_t y = MW8080BW_VCOUNTER_START_NO_VBLANK;
18 	uint8_t video_data = 0;
19 
20 	while (1)
21 	{
22 		// plot the current pixel
23 		pen_t pen = (video_data & 0x01) ? rgb_t::white() : rgb_t::black();
24 		bitmap.pix(y - MW8080BW_VCOUNTER_START_NO_VBLANK, x) = pen;
25 
26 		// next pixel
27 		video_data = video_data >> 1;
28 		x = x + 1;
29 
30 		/* end of line? */
31 		if (x == 0)
32 		{
33 			// yes, flush out the shift register
34 			for (int i = 0; i < 4; i++)
35 			{
36 				pen = (video_data & 0x01) ? rgb_t::white() : rgb_t::black();
37 				bitmap.pix(y - MW8080BW_VCOUNTER_START_NO_VBLANK, 256 + i) = pen;
38 
39 				video_data = video_data >> 1;
40 			}
41 
42 			// next row, video_data is now 0, so the next line will start with 4 blank pixels
43 			y = y + 1;
44 
45 			// end of screen?
46 			if (y == 0)
47 				break;
48 		}
49 		/* the video RAM is read at every 8 pixels starting with pixel 4 */
50 		else if ((x & 0x07) == 0x04)
51 		{
52 			offs_t const offs = ((offs_t)y << 5) | (x >> 3);
53 			video_data = m_main_ram[offs];
54 		}
55 	}
56 
57 	return 0;
58 }
59 
60 
61 
62 /*************************************
63  *
64  *  Space Encounters
65  *
66  *************************************/
67 
68 /* video signals mixed via R2 open collector nand gates and 'brite' RC circuit
69 
70     * when /BRITE lo, cap C23 discharge rapidly, disabling Q2, overpowering all other video signals
71     * when /BRITE hi, cap C23 charge through 10k res, brite voltage decrease to 0 over approx 0.4 sec
72 
73     * inverted video data is fed into R2 nand gates:
74         * when /VIDEO lo, all gates open for max brightness
75             * max V = (5 - 0.7) * 470 / (470 + 100) = 3.5 V
76         * when /video hi, pin 5 always gnd, max = 3 V, min = 1 V
77         * (guess) pin 11 state controls trench color
78         * (guess) pin 3 low for trench side
79         * (guess) pin 8 low for trench floor
80         * thus, trench side: 1.4 or 2.2 V
81         * trench floor: 1.3 or 2.0 V
82         * trech top: 1.8 or 3 V
83         * scaled to 3.2 V = 255, 1.2 V = 0 (arbitrary values chosen to match video)
84 */
85 
86 #define SPCENCTR_TOP_TRENCH_DARK_RGB32_PEN       rgb_t(0x4d, 0x4d, 0x4d)
87 #define SPCENCTR_TOP_TRENCH_LIGHT_RGB32_PEN      rgb_t(0xe6, 0xe6, 0xe6)
88 #define SPCENCTR_SIDE_TRENCH_DARK_RGB32_PEN      rgb_t(0x1a, 0x1a, 0x1a)
89 #define SPCENCTR_SIDE_TRENCH_LIGHT_RGB32_PEN     rgb_t(0x80, 0x80, 0x80)
90 #define SPCENCTR_BOTTOM_TRENCH_DARK_RGB32_PEN    rgb_t(0x0d, 0x0d, 0x0d)
91 #define SPCENCTR_BOTTOM_TRENCH_LIGHT_RGB32_PEN   rgb_t(0x66, 0x66, 0x66)
92 #define SPCENCTR_BRIGHTNESS_DECAY                10
93 
94 
screen_update(screen_device & screen,bitmap_rgb32 & bitmap,const rectangle & cliprect)95 uint32_t spcenctr_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
96 {
97 	uint8_t line_buf[256]; /* 256x1 bit RAM */
98 
99 	uint8_t x = 0;
100 	uint8_t y = MW8080BW_VCOUNTER_START_NO_VBLANK;
101 	uint8_t video_data = 0;
102 	uint8_t draw_line = 0;
103 	uint8_t draw_trench = 0;
104 	uint8_t draw_floor = 0;
105 	uint8_t width = m_trench_width;
106 	uint8_t floor_width = width;
107 	uint8_t center = m_trench_center;
108 
109 	memset(line_buf, 0, 256);
110 
111 	if (m_bright_control)
112 		m_brightness = 255;
113 	else if (m_brightness > SPCENCTR_BRIGHTNESS_DECAY)
114 		m_brightness -= SPCENCTR_BRIGHTNESS_DECAY;
115 	else
116 		m_brightness = 0;
117 
118 	while (1)
119 	{
120 		// plot the current pixel
121 		uint8_t bit = video_data & 0x01;
122 		pen_t pen = bit ? rgb_t::white() : rgb_t::black();
123 
124 		if (!(width & 0x80) && draw_trench) // possibly draw trench in the background, top of trench first
125 		{
126 			line_buf[x] = draw_line;
127 
128 			if (!bit)
129 				pen = draw_line ? SPCENCTR_TOP_TRENCH_LIGHT_RGB32_PEN : SPCENCTR_TOP_TRENCH_DARK_RGB32_PEN;
130 		}
131 		else if (!(floor_width & 0x80) && (draw_trench || draw_floor)) // sides of trench?
132 		{
133 			if (!bit)
134 				pen = line_buf[x] ? SPCENCTR_SIDE_TRENCH_LIGHT_RGB32_PEN : SPCENCTR_SIDE_TRENCH_DARK_RGB32_PEN;
135 		}
136 		else if (draw_floor) // bottom of trench?
137 		{
138 			line_buf[x] = line_buf[x - 1];
139 
140 			if (!bit)
141 				pen = line_buf[x] ? SPCENCTR_BOTTOM_TRENCH_LIGHT_RGB32_PEN : SPCENCTR_BOTTOM_TRENCH_DARK_RGB32_PEN;
142 		}
143 
144 		if (m_brightness > (pen & 0xff))
145 			pen = rgb_t(m_brightness, m_brightness, m_brightness);
146 
147 		bitmap.pix(y - MW8080BW_VCOUNTER_START_NO_VBLANK, x) = pen;
148 
149 		center = center + 1;
150 		width = width + ((center & 0x80) ? -1 : 1);
151 		floor_width = floor_width + ((center & 0x80) ? -1 : 1);
152 
153 		// next pixel
154 		video_data = video_data >> 1;
155 		x = x + 1;
156 
157 		if (x == 0) // end of line?
158 		{
159 			// yes, flush out the shift register
160 			for (int i = 0; i < 4; i++)
161 			{
162 				if (video_data & 0x01)
163 					pen = rgb_t::white();
164 				else if(m_brightness)
165 					pen = rgb_t(m_brightness, m_brightness, m_brightness);
166 				else
167 					pen = rgb_t::black();
168 
169 				bitmap.pix(y - MW8080BW_VCOUNTER_START_NO_VBLANK, 256 + i) = pen;
170 
171 				video_data = video_data >> 1;
172 			}
173 
174 			/* update the trench control for the next line */
175 			offs_t const offs = ((offs_t)y << 5) | 0x1f;
176 			uint8_t const trench_control = m_main_ram[offs];
177 
178 			if (trench_control & 0x40)
179 				draw_trench = 1;
180 
181 			if (trench_control & 0x20)
182 				draw_trench = 0;
183 
184 			if (trench_control & 0x10)
185 				draw_floor = 1;
186 
187 			if (trench_control & 0x08)
188 				draw_floor = 0;
189 
190 			draw_line = (trench_control & 0x80) >> 7;
191 
192 			// add the lower 2 bits stored in the slope array to width
193 			if (draw_trench)
194 				width = width + (m_trench_slope[y & 0x0f] & 0x03);
195 
196 			// add the higher 2 bits stored in the slope array to floor width
197 			if (draw_floor)
198 				floor_width = floor_width + ((m_trench_slope[y & 0x0f] & 0x0c) >> 2);
199 
200 			// next row, video_data is now 0, so the next line will start with 4 blank pixels
201 			y = y + 1;
202 
203 			// end of screen?
204 			if (y == 0)
205 				break;
206 		}
207 		else if ((x & 0x07) == 0x04) // the video RAM is read at every 8 pixels starting with pixel 4
208 		{
209 			offs_t const offs = ((offs_t)y << 5) | (x >> 3);
210 			video_data = m_main_ram[offs];
211 		}
212 	}
213 
214 	return 0;
215 }
216 
217 
218 
219 /*************************************
220  *
221  *  Phantom II
222  *
223  *************************************/
224 
225 
226 /* the cloud generator comprises of 2 counters and a shift register:
227 
228    * counter 1 is 8 bits and and clocked every pixel. It gets cleared at the end of HBLANK .
229      Bit 0 is used to clock the shift register, thus repeating every pixel twice.
230      Bits 4-7 go to address line A0-A3 of the cloud gfx prom.
231    * counter 2 is 12 bits starting from 0xe0b and counts up to 0xfff.  It gets clocked at the
232      beginning of HBLANK and is never cleared.
233      Bits 1-7 go to address line A4-A10 of the cloud gfx prom.
234 */
235 
236 #define PHANTOM2_CLOUD_COUNTER_START      (0x0e0b)
237 #define PHANTOM2_CLOUD_COUNTER_END        (0x1000)
238 #define PHANTOM2_CLOUD_COUNTER_PERIOD     (PHANTOM2_CLOUD_COUNTER_END - PHANTOM2_CLOUD_COUNTER_START)
239 
240 #define PHANTOM2_RGB32_CLOUD_PEN          rgb_t(0xc0, 0xc0, 0xc0)
241 
242 
screen_update_phantom2(screen_device & screen,bitmap_rgb32 & bitmap,const rectangle & cliprect)243 uint32_t mw8080bw_state::screen_update_phantom2(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
244 {
245 	uint8_t x = 0;
246 	uint8_t y = MW8080BW_VCOUNTER_START_NO_VBLANK;
247 	uint8_t video_data = 0;
248 	uint8_t cloud_data = 0;
249 
250 	uint16_t cloud_counter = m_phantom2_cloud_counter;
251 
252 	uint8_t *cloud_region = memregion("proms")->base();
253 
254 	while (1)
255 	{
256 		int load_shift_reg;
257 		uint8_t cloud_data_to_load = 0;
258 		pen_t pen;
259 
260 		/* plot the current pixel */
261 		uint8_t bit = video_data & 0x01;
262 
263 		/* if background color, cloud gfx in the background */
264 		if ((bit == 0) && (cloud_data & 0x01))
265 			pen = PHANTOM2_RGB32_CLOUD_PEN;
266 		else
267 			pen = bit ? rgb_t::white() : rgb_t::black();
268 
269 		bitmap.pix(y - MW8080BW_VCOUNTER_START_NO_VBLANK, x) = pen;
270 
271 		/* move to next pixel -- if ripple carry is currently set,
272 		   prepare for loading the shift register */
273 		load_shift_reg = ((x & 0x0f) == 0x0f);
274 
275 		if (load_shift_reg)
276 		{
277 			offs_t cloud_offs = ((cloud_counter & 0xfe) << 3) | (x >> 4);
278 			cloud_data_to_load = cloud_region[cloud_offs];
279 		}
280 
281 		video_data = video_data >> 1;
282 		x = x + 1;
283 
284 		/* the sift register is clocked on the falling edge of bit 0 */
285 		if (!(x & 0x01))
286 		{
287 			/* load or shift? */
288 			if (load_shift_reg)
289 				cloud_data = cloud_data_to_load;
290 			else
291 				cloud_data = cloud_data >> 1;
292 		}
293 
294 		/* end of line? */
295 		if (x == 0)
296 		{
297 			/* yes, flush out the shift register */
298 			int i;
299 
300 			for (i = 0; i < 4; i++)
301 			{
302 				pen = (video_data & 0x01) ? rgb_t::white() : rgb_t::black();
303 				bitmap.pix(y - MW8080BW_VCOUNTER_START_NO_VBLANK, 256 + i) = pen;
304 
305 				video_data = video_data >> 1;
306 			}
307 
308 			/* next row of clouds */
309 			cloud_counter = cloud_counter + 1;
310 
311 			if (cloud_counter == PHANTOM2_CLOUD_COUNTER_END)
312 				cloud_counter = PHANTOM2_CLOUD_COUNTER_START;
313 
314 			/* next row of pixels, video_data is now 0, so the next
315 			   line will start with 4 blank pixels */
316 			y = y + 1;
317 
318 			/* end of screen? */
319 			if (y == 0)
320 				break;
321 		}
322 		/* the video RAM is read at every 8 pixels starting with pixel 4 */
323 		else if ((x & 0x07) == 0x04)
324 		{
325 			offs_t offs = ((offs_t)y << 5) | (x >> 3);
326 			video_data = m_main_ram[offs];
327 		}
328 	}
329 
330 	return 0;
331 }
332 
333 
WRITE_LINE_MEMBER(mw8080bw_state::screen_vblank_phantom2)334 WRITE_LINE_MEMBER(mw8080bw_state::screen_vblank_phantom2)
335 {
336 	// falling edge
337 	if (!state)
338 	{
339 		m_phantom2_cloud_counter += MW8080BW_VTOTAL;
340 
341 		if (m_phantom2_cloud_counter >= PHANTOM2_CLOUD_COUNTER_END)
342 			m_phantom2_cloud_counter = PHANTOM2_CLOUD_COUNTER_START + (m_phantom2_cloud_counter - PHANTOM2_CLOUD_COUNTER_END);
343 	}
344 }
345 
346 
347 /*************************************
348  *
349  *  Space Invaders
350  *
351  *************************************/
352 
353 
354 // the flip screen circuit is just a couple of relays on the monitor PCB
355 
screen_update_invaders(screen_device & screen,bitmap_rgb32 & bitmap,const rectangle & cliprect)356 uint32_t mw8080bw_state::screen_update_invaders(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
357 {
358 	uint8_t x = 0;
359 	uint8_t y = MW8080BW_VCOUNTER_START_NO_VBLANK;
360 	uint8_t video_data = 0;
361 
362 	while (1)
363 	{
364 		// plot the current pixel
365 		pen_t pen = (video_data & 0x01) ? rgb_t::white() : rgb_t::black();
366 
367 		if (m_flip_screen)
368 			bitmap.pix(MW8080BW_VBSTART - 1 - (y - MW8080BW_VCOUNTER_START_NO_VBLANK), MW8080BW_HPIXCOUNT - 1 - x) = pen;
369 		else
370 			bitmap.pix(y - MW8080BW_VCOUNTER_START_NO_VBLANK, x) = pen;
371 
372 		// next pixel
373 		video_data = video_data >> 1;
374 		x = x + 1;
375 
376 		// end of line?
377 		if (x == 0)
378 		{
379 			// yes, flush out the shift register
380 			for (int i = 0; i < 4; i++)
381 			{
382 				pen = (video_data & 0x01) ? rgb_t::white() : rgb_t::black();
383 
384 				if (m_flip_screen)
385 					bitmap.pix(MW8080BW_VBSTART - 1 - (y - MW8080BW_VCOUNTER_START_NO_VBLANK), MW8080BW_HPIXCOUNT - 1 - (256 + i)) = pen;
386 				else
387 					bitmap.pix(y - MW8080BW_VCOUNTER_START_NO_VBLANK, 256 + i) = pen;
388 
389 				video_data = video_data >> 1;
390 			}
391 
392 			// next row, video_data is now 0, so the next line will start with 4 blank pixels
393 			y = y + 1;
394 
395 			// end of screen?
396 			if (y == 0)
397 				break;
398 		}
399 		else if ((x & 0x07) == 0x04) // the video RAM is read at every 8 pixels starting with pixel 4
400 		{
401 			offs_t const offs = (offs_t(y) << 5) | (x >> 3);
402 			video_data = m_main_ram[offs];
403 		}
404 	}
405 
406 	return 0;
407 }
408