1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles
3 /*************************************************************************
4 
5     BattleToads
6 
7     Video hardware emulation
8 
9 **************************************************************************/
10 
11 #include "emu.h"
12 #include "includes/btoads.h"
13 
14 
15 #define BT_DEBUG 0
16 
17 
18 
19 /*************************************
20  *
21  *  Video system start
22  *
23  *************************************/
24 
video_start()25 void btoads_state::video_start()
26 {
27 	/* initialize the swapped pointers */
28 	m_vram_fg_draw = m_vram_fg0;
29 	m_vram_fg_display = m_vram_fg1;
30 
31 	save_item(NAME(m_xscroll0));
32 	save_item(NAME(m_xscroll1));
33 	save_item(NAME(m_yscroll0));
34 	save_item(NAME(m_yscroll1));
35 	save_item(NAME(m_screen_control));
36 
37 	save_item(NAME(m_sprite_source_offs));
38 	save_item(NAME(m_sprite_dest_offs));
39 	save_item(NAME(m_misc_control));
40 }
41 
42 
43 
44 /*************************************
45  *
46  *  Control registers
47  *
48  *************************************/
49 
misc_control_w(offs_t offset,uint16_t data,uint16_t mem_mask)50 void btoads_state::misc_control_w(offs_t offset, uint16_t data, uint16_t mem_mask)
51 {
52 	COMBINE_DATA(&m_misc_control);
53 
54 	/* bit 3 controls sound reset line */
55 	m_audiocpu->set_input_line(INPUT_LINE_RESET, (m_misc_control & 8) ? CLEAR_LINE : ASSERT_LINE);
56 }
57 
58 
display_control_w(offs_t offset,uint16_t data,uint16_t mem_mask)59 void btoads_state::display_control_w(offs_t offset, uint16_t data, uint16_t mem_mask)
60 {
61 	if (ACCESSING_BITS_8_15)
62 	{
63 		/* allow multiple changes during display */
64 		int scanline = m_screen->vpos();
65 		if (scanline > 0)
66 			m_screen->update_partial(scanline - 1);
67 
68 		/* bit 15 controls which page is rendered and which page is displayed */
69 		if (data & 0x8000)
70 		{
71 			m_vram_fg_draw = m_vram_fg1;
72 			m_vram_fg_display = m_vram_fg0;
73 		}
74 		else
75 		{
76 			m_vram_fg_draw = m_vram_fg0;
77 			m_vram_fg_display = m_vram_fg1;
78 		}
79 
80 		/* stash the remaining data for later */
81 		m_screen_control = data >> 8;
82 	}
83 }
84 
85 
86 
87 /*************************************
88  *
89  *  Scroll registers
90  *
91  *************************************/
92 
scroll0_w(offs_t offset,uint16_t data,uint16_t mem_mask)93 void btoads_state::scroll0_w(offs_t offset, uint16_t data, uint16_t mem_mask)
94 {
95 	/* allow multiple changes during display */
96 //  m_screen->update_now();
97 	m_screen->update_partial(m_screen->vpos());
98 
99 	/* upper bits are Y scroll, lower bits are X scroll */
100 	if (ACCESSING_BITS_8_15)
101 		m_yscroll0 = data >> 8;
102 	if (ACCESSING_BITS_0_7)
103 		m_xscroll0 = data & 0xff;
104 }
105 
106 
scroll1_w(offs_t offset,uint16_t data,uint16_t mem_mask)107 void btoads_state::scroll1_w(offs_t offset, uint16_t data, uint16_t mem_mask)
108 {
109 	/* allow multiple changes during display */
110 //  m_screen->update_now();
111 	m_screen->update_partial(m_screen->vpos());
112 
113 	/* upper bits are Y scroll, lower bits are X scroll */
114 	if (ACCESSING_BITS_8_15)
115 		m_yscroll1 = data >> 8;
116 	if (ACCESSING_BITS_0_7)
117 		m_xscroll1 = data & 0xff;
118 }
119 
120 
121 
122 /*************************************
123  *
124  *  Background video RAM
125  *
126  *************************************/
127 
vram_bg0_w(offs_t offset,uint16_t data,uint16_t mem_mask)128 void btoads_state::vram_bg0_w(offs_t offset, uint16_t data, uint16_t mem_mask)
129 {
130 	COMBINE_DATA(&m_vram_bg0[offset & 0x3fcff]);
131 }
132 
133 
vram_bg1_w(offs_t offset,uint16_t data,uint16_t mem_mask)134 void btoads_state::vram_bg1_w(offs_t offset, uint16_t data, uint16_t mem_mask)
135 {
136 	COMBINE_DATA(&m_vram_bg1[offset & 0x3fcff]);
137 }
138 
139 
vram_bg0_r(offs_t offset)140 uint16_t btoads_state::vram_bg0_r(offs_t offset)
141 {
142 	return m_vram_bg0[offset & 0x3fcff];
143 }
144 
145 
vram_bg1_r(offs_t offset)146 uint16_t btoads_state::vram_bg1_r(offs_t offset)
147 {
148 	return m_vram_bg1[offset & 0x3fcff];
149 }
150 
151 
152 
153 /*************************************
154  *
155  *  Foreground video RAM
156  *
157  *************************************/
158 
vram_fg_display_w(offs_t offset,uint16_t data,uint16_t mem_mask)159 void btoads_state::vram_fg_display_w(offs_t offset, uint16_t data, uint16_t mem_mask)
160 {
161 	if (ACCESSING_BITS_0_7)
162 		m_vram_fg_display[offset] = data;
163 }
164 
165 
vram_fg_draw_w(offs_t offset,uint16_t data,uint16_t mem_mask)166 void btoads_state::vram_fg_draw_w(offs_t offset, uint16_t data, uint16_t mem_mask)
167 {
168 	if (ACCESSING_BITS_0_7)
169 		m_vram_fg_draw[offset] = data;
170 }
171 
172 
vram_fg_display_r(offs_t offset)173 uint16_t btoads_state::vram_fg_display_r(offs_t offset)
174 {
175 	return m_vram_fg_display[offset];
176 }
177 
178 
vram_fg_draw_r(offs_t offset)179 uint16_t btoads_state::vram_fg_draw_r(offs_t offset)
180 {
181 	return m_vram_fg_draw[offset];
182 }
183 
184 
185 
186 /*************************************
187  *
188  *  Sprite rendering
189  *
190  *************************************/
191 
render_sprite_row(uint16_t * sprite_source,uint32_t address)192 void btoads_state::render_sprite_row(uint16_t *sprite_source, uint32_t address)
193 {
194 	int flipxor = ((*m_sprite_control >> 10) & 1) ? 0xffff : 0x0000;
195 	int width = (~*m_sprite_control & 0x1ff) + 2;
196 	int color = (~*m_sprite_control >> 8) & 0xf0;
197 	int srcoffs = m_sprite_source_offs << 8;
198 	int srcend = srcoffs + (width << 8);
199 	int srcstep = 0x100 - m_sprite_scale[0];
200 	int dststep = 0x100 - m_sprite_scale[8];
201 	int dstoffs = m_sprite_dest_offs << 8;
202 
203 	/* non-shadow case */
204 	if (!(m_misc_control & 0x10))
205 	{
206 		for ( ; srcoffs < srcend; srcoffs += srcstep, dstoffs += dststep)
207 		{
208 			uint16_t src = sprite_source[(srcoffs >> 10) & 0x1ff];
209 			if (src)
210 			{
211 				src = (src >> (((srcoffs ^ flipxor) >> 6) & 0x0c)) & 0x0f;
212 				if (src)
213 					m_sprite_dest_base[(dstoffs >> 8) & 0x1ff] = src | color;
214 			}
215 		}
216 	}
217 
218 	/* shadow case */
219 	else
220 	{
221 		for ( ; srcoffs < srcend; srcoffs += srcstep, dstoffs += dststep)
222 		{
223 			uint16_t src = sprite_source[(srcoffs >> 10) & 0x1ff];
224 			if (src)
225 			{
226 				src = (src >> (((srcoffs ^ flipxor) >> 6) & 0x0c)) & 0x0f;
227 				if (src)
228 					m_sprite_dest_base[(dstoffs >> 8) & 0x1ff] = color;
229 			}
230 		}
231 	}
232 
233 	m_sprite_source_offs += width;
234 	m_sprite_dest_offs = dstoffs >> 8;
235 }
236 
237 
238 
239 /*************************************
240  *
241  *  Shift register read/write
242  *
243  *************************************/
244 
TMS340X0_TO_SHIFTREG_CB_MEMBER(btoads_state::to_shiftreg)245 TMS340X0_TO_SHIFTREG_CB_MEMBER(btoads_state::to_shiftreg)
246 {
247 	address &= ~0x40000000;
248 
249 	/* reads from this first region are usual shift register reads */
250 	if (address >= 0xa0000000 && address <= 0xa3ffffff)
251 		memcpy(shiftreg, &m_vram_fg_display[(address & 0x3fffff) >> 4], 0x200);
252 
253 	/* reads from this region set the sprite destination address */
254 	else if (address >= 0xa4000000 && address <= 0xa7ffffff)
255 	{
256 		m_sprite_dest_base = &m_vram_fg_draw[(address & 0x3fc000) >> 4];
257 		m_sprite_dest_offs = (address & 0x003fff) >> 5;
258 	}
259 
260 	/* reads from this region set the sprite source address */
261 	else if (address >= 0xa8000000 && address <= 0xabffffff)
262 	{
263 		memcpy(shiftreg, &m_vram_fg_data[(address & 0x7fc000) >> 4], 0x400);
264 		m_sprite_source_offs = (address & 0x003fff) >> 3;
265 	}
266 
267 	else
268 		logerror("%s:btoads_to_shiftreg(%08X)\n", machine().describe_context(), address);
269 }
270 
271 
TMS340X0_FROM_SHIFTREG_CB_MEMBER(btoads_state::from_shiftreg)272 TMS340X0_FROM_SHIFTREG_CB_MEMBER(btoads_state::from_shiftreg)
273 {
274 	address &= ~0x40000000;
275 
276 	/* writes to this first region are usual shift register writes */
277 	if (address >= 0xa0000000 && address <= 0xa3ffffff)
278 		memcpy(&m_vram_fg_display[(address & 0x3fc000) >> 4], shiftreg, 0x200);
279 
280 	/* writes to this region are ignored for our purposes */
281 	else if (address >= 0xa4000000 && address <= 0xa7ffffff)
282 		;
283 
284 	/* writes to this region copy standard data */
285 	else if (address >= 0xa8000000 && address <= 0xabffffff)
286 		memcpy(&m_vram_fg_data[(address & 0x7fc000) >> 4], shiftreg, 0x400);
287 
288 	/* writes to this region render the current sprite data */
289 	else if (address >= 0xac000000 && address <= 0xafffffff)
290 		render_sprite_row(shiftreg, address);
291 
292 	else
293 		logerror("%s:btoads_from_shiftreg(%08X)\n", machine().describe_context(), address);
294 }
295 
296 
297 
298 /*************************************
299  *
300  *  Main refresh
301  *
302  *************************************/
303 
TMS340X0_SCANLINE_RGB32_CB_MEMBER(btoads_state::scanline_update)304 TMS340X0_SCANLINE_RGB32_CB_MEMBER(btoads_state::scanline_update)
305 {
306 	uint32_t fulladdr = ((params->rowaddr << 16) | params->coladdr) >> 4;
307 	uint16_t *bg0_base = &m_vram_bg0[(fulladdr + (m_yscroll0 << 10)) & 0x3fc00];
308 	uint16_t *bg1_base = &m_vram_bg1[(fulladdr + (m_yscroll1 << 10)) & 0x3fc00];
309 	uint8_t *spr_base = &m_vram_fg_display[fulladdr & 0x3fc00];
310 	uint32_t *const dst = &bitmap.pix(scanline);
311 	const pen_t *pens = m_tlc34076->pens();
312 	int coladdr = fulladdr & 0x3ff;
313 
314 	/* for each scanline, switch off the render mode */
315 	switch (m_screen_control & 3)
316 	{
317 		/* mode 0: used in ship level, snake boss, title screen (free play) */
318 		/* priority is:
319 		    1. Sprite pixels with high bit clear
320 		    2. BG1 pixels with the high bit set
321 		    3. Sprites
322 		    4. BG1
323 		    5. BG0
324 		*/
325 		case 0:
326 			for (int x = params->heblnk; x < params->hsblnk; x += 2, coladdr++)
327 			{
328 				uint8_t sprpix = spr_base[coladdr & 0xff];
329 
330 				if (sprpix && !(sprpix & 0x80))
331 				{
332 					dst[x + 0] = pens[sprpix];
333 					dst[x + 1] = pens[sprpix];
334 				}
335 				else
336 				{
337 					uint16_t bg0pix = bg0_base[(coladdr + m_xscroll0) & 0xff];
338 					uint16_t bg1pix = bg1_base[(coladdr + m_xscroll1) & 0xff];
339 					uint8_t sprpix = spr_base[coladdr & 0xff];
340 
341 					if (bg1pix & 0x80)
342 						dst[x + 0] = pens[bg1pix & 0xff];
343 					else if (sprpix)
344 						dst[x + 0] = pens[sprpix];
345 					else if (bg1pix & 0xff)
346 						dst[x + 0] = pens[bg1pix & 0xff];
347 					else
348 						dst[x + 0] = pens[bg0pix & 0xff];
349 
350 					if (bg1pix & 0x8000)
351 						dst[x + 1] = pens[bg1pix >> 8];
352 					else if (sprpix)
353 						dst[x + 1] = pens[sprpix];
354 					else if (bg1pix >> 8)
355 						dst[x + 1] = pens[bg1pix >> 8];
356 					else
357 						dst[x + 1] = pens[bg0pix >> 8];
358 				}
359 			}
360 			break;
361 
362 		/* mode 1: used in snow level, title screen (free play), top part of rolling ball level */
363 		/* priority is:
364 		    1. Sprite pixels with high bit clear
365 		    2. BG0
366 		    3. BG1 pixels with high bit set
367 		    4. Sprites
368 		    5. BG1
369 		*/
370 		case 1:
371 			for (int x = params->heblnk; x < params->hsblnk; x += 2, coladdr++)
372 			{
373 				uint8_t sprpix = spr_base[coladdr & 0xff];
374 
375 				if (sprpix && !(sprpix & 0x80))
376 				{
377 					dst[x + 0] = pens[sprpix];
378 					dst[x + 1] = pens[sprpix];
379 				}
380 				else
381 				{
382 					uint16_t bg0pix = bg0_base[(coladdr + m_xscroll0) & 0xff];
383 					uint16_t bg1pix = bg1_base[(coladdr + m_xscroll1) & 0xff];
384 
385 					if (bg0pix & 0xff)
386 						dst[x + 0] = pens[bg0pix & 0xff];
387 					else if (bg1pix & 0x80)
388 						dst[x + 0] = pens[bg1pix & 0xff];
389 					else if (sprpix)
390 						dst[x + 0] = pens[sprpix];
391 					else
392 						dst[x + 0] = pens[bg1pix & 0xff];
393 
394 					if (bg0pix >> 8)
395 						dst[x + 1] = pens[bg0pix >> 8];
396 					else if (bg1pix & 0x8000)
397 						dst[x + 1] = pens[bg1pix >> 8];
398 					else if (sprpix)
399 						dst[x + 1] = pens[sprpix];
400 					else
401 						dst[x + 1] = pens[bg1pix >> 8];
402 				}
403 			}
404 			break;
405 
406 		/* mode 2: used in EOA screen, jetpack level, first level, high score screen */
407 		/* priority is:
408 		    1. Sprites
409 		    2. BG1
410 		    3. BG0
411 		*/
412 		case 2:
413 			for (int x = params->heblnk; x < params->hsblnk; x += 2, coladdr++)
414 			{
415 				uint8_t sprpix = spr_base[coladdr & 0xff];
416 
417 				if (sprpix)
418 				{
419 					dst[x + 0] = pens[sprpix];
420 					dst[x + 1] = pens[sprpix];
421 				}
422 				else
423 				{
424 					uint16_t bg0pix = bg0_base[(coladdr + m_xscroll0) & 0xff];
425 					uint16_t bg1pix = bg1_base[(coladdr + m_xscroll1) & 0xff];
426 
427 					if (bg1pix & 0xff)
428 						dst[x + 0] = pens[bg1pix & 0xff];
429 					else
430 						dst[x + 0] = pens[bg0pix & 0xff];
431 
432 					if (bg1pix >> 8)
433 						dst[x + 1] = pens[bg1pix >> 8];
434 					else
435 						dst[x + 1] = pens[bg0pix >> 8];
436 				}
437 			}
438 			break;
439 
440 		/* mode 3: used in toilet level, toad intros, bottom of rolling ball level */
441 		/* priority is:
442 		    1. BG1 pixels with the high bit set
443 		    2. Sprite pixels with the high bit set
444 		    3. BG1
445 		    4. Sprites
446 		    5. BG0
447 		*/
448 		case 3:
449 			for (int x = params->heblnk; x < params->hsblnk; x += 2, coladdr++)
450 			{
451 				uint16_t bg0pix = bg0_base[(coladdr + m_xscroll0) & 0xff];
452 				uint16_t bg1pix = bg1_base[(coladdr + m_xscroll1) & 0xff];
453 				uint8_t sprpix = spr_base[coladdr & 0xff];
454 
455 				if (bg1pix & 0x80)
456 					dst[x + 0] = pens[bg1pix & 0xff];
457 				else if (sprpix & 0x80)
458 					dst[x + 0] = pens[sprpix];
459 				else if (bg1pix & 0xff)
460 					dst[x + 0] = pens[bg1pix & 0xff];
461 				else if (sprpix)
462 					dst[x + 0] = pens[sprpix];
463 				else
464 					dst[x + 0] = pens[bg0pix & 0xff];
465 
466 				if (bg1pix & 0x8000)
467 					dst[x + 1] = pens[bg1pix >> 8];
468 				else if (sprpix & 0x80)
469 					dst[x + 1] = pens[sprpix];
470 				else if (bg1pix >> 8)
471 					dst[x + 1] = pens[bg1pix >> 8];
472 				else if (sprpix)
473 					dst[x + 1] = pens[sprpix];
474 				else
475 					dst[x + 1] = pens[bg0pix >> 8];
476 			}
477 			break;
478 	}
479 
480 	/* debugging - dump the screen contents to a file */
481 #if BT_DEBUG
482 	popmessage("screen_control = %02X", m_screen_control & 0x7f);
483 
484 	if (machine().input().code_pressed(KEYCODE_X))
485 	{
486 		char name[10];
487 		FILE *f;
488 
489 		while (machine().input().code_pressed(KEYCODE_X)) ;
490 
491 		sprintf(name, "disp%d.log", m_xcount++);
492 		f = fopen(name, "w");
493 		fprintf(f, "screen_control = %04X\n\n", m_screen_control << 8);
494 
495 		for (int i = 0; i < 3; i++)
496 		{
497 			uint16_t *base = (i == 0) ? (uint16_t *)m_vram_fg_display : (i == 1) ? m_vram_bg0 : m_vram_bg1;
498 			int xscr = (i == 0) ? 0 : (i == 1) ? m_xscroll0 : m_xscroll1;
499 			int yscr = (i == 0) ? 0 : (i == 1) ? m_yscroll0 : m_yscroll1;
500 			int y;
501 
502 			for (y = 0; y < 224; y++)
503 			{
504 				uint32_t offs = ((y + yscr) & 0xff) * TOWORD(0x4000);
505 				for (x = 0; x < 256; x++)
506 				{
507 					uint16_t pix = base[offs + ((x + xscr) & 0xff)];
508 					fprintf(f, "%02X%02X", pix & 0xff, pix >> 8);
509 					if (x % 16 == 15) fprintf(f, " ");
510 				}
511 				fprintf(f, "\n");
512 			}
513 			fprintf(f, "\n\n");
514 		}
515 		fclose(f);
516 	}
517 
518 	logerror("---VBLANK---\n");
519 #endif
520 }
521