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