1 /*
2     tms.c --
3     TMS9918 and legacy video mode support.
4 */
5 #include "smsshared.h"
6 
7 INT16 text_counter;               /* Text offset counter */
8 UINT8 tms_lookup[16][256][2];   /* Expand BD, PG data into 8-bit pixels (G1,G2) */
9 UINT8 mc_lookup[16][256][8];    /* Expand BD, PG data into 8-bit pixels (MC) */
10 UINT8 txt_lookup[256][2];       /* Expand BD, PG data into 8-bit pixels (TX) */
11 UINT8 bp_expand[256][8];        /* Expand PG data into 8-bit pixels */
12 UINT8 tms_obj_lut[16*256];      /* Look up priority between SG and display pixels */
13 
14 static const UINT8 diff_mask[]  = {0x07, 0x07, 0x0F, 0x0F};
15 static const UINT8 name_mask[]  = {0xFF, 0xFF, 0xFC, 0xFC};
16 static const UINT8 diff_shift[] = {0, 1, 0, 1};
17 static const UINT8 size_tab[]   = {8, 16, 16, 32};
18 
19 /* Internally latched sprite data in the VDP */
20 typedef struct {
21 	INT16 xpos;
22 	UINT8 attr;
23 	UINT8 sg[2];
24 } tms_sprite;
25 
26 tms_sprite sprites[32];
27 INT16 sprites_found;
28 
parse_line(INT16 line)29 void parse_line(INT16 line)
30 {
31 	INT16 yp, i;
32 	INT16 mode = vdp.reg[1] & 3;
33 	INT16 size = size_tab[mode];
34 	INT16 diff, name;
35 	UINT8 *sa, *sg;
36 	tms_sprite *p;
37 
38 	/* Reset # of sprites found */
39 	sprites_found = 0;
40 
41 	if (line >= vdp.height) return; // avoid glitches (3dragon story)
42 
43 	/* Parse sprites */
44 	for(i = 0; i < 32; i++)
45 	{
46 		/* Point to current sprite in SA and our current sprite record */
47 		p = &sprites[sprites_found];
48 		sa = &vdp.vram[vdp.sa + (i << 2)];
49 
50 		/* Fetch Y coordinate */
51 		yp = sa[0];
52 
53 		/* Check for end marker */
54 		if(yp == 0xD0)
55 			goto parse_end;
56 
57 		/* Wrap Y position */
58 		if(yp > 0xE0)
59 			yp -= 256;
60 
61 		/* Check if sprite falls on following line */
62 		if(line >= yp && line < (yp + size))
63 		{
64 			/* Sprite overflow on this line */
65 			if(sprites_found == 4 && !vdp.no_spr_limit)
66 			{
67 				/* Set 5S and abort parsing */
68 				vdp.status |= 0x40;
69 				goto parse_end;
70 			}
71 
72 			/* Fetch X position */
73 			p->xpos = sa[1];
74 
75 			/* Fetch name */
76 			name = sa[2] & name_mask[mode];
77 
78 			/* Load attribute into attribute storage */
79 			p->attr = sa[3];
80 
81 			/* Apply early clock bit */
82 			if(p->attr & 0x80)
83 				p->xpos -= 32;
84 
85 			/* Calculate offset in pattern */
86 			diff = ((line - yp) >> diff_shift[mode]) & diff_mask[mode];
87 
88 			/* Insert additional name bit for 16-pixel tall sprites */
89 			if(diff & 8)
90 				name |= 1;
91 
92 			/* Fetch SG data */
93 			sg = &vdp.vram[vdp.sg | (name << 3) | (diff & 7)];
94 			p->sg[0] = sg[0x00];
95 			p->sg[1] = sg[0x10];
96 
97 			/* Bump found sprite count */
98 			++sprites_found;
99 		}
100 	}
101 parse_end:
102 	/* Insert number of last sprite entry processed */
103 	vdp.status = (vdp.status & 0xE0) | (i & 0x1F);
104 }
105 
render_obj_tms(INT16 line)106 void render_obj_tms(INT16 line)
107 {
108 	INT16 i, x = 0;
109 	INT16 size, start, end, mode;
110 	UINT8 *lb, *lut, *ex[2];
111 	tms_sprite *p;
112 
113 	mode = vdp.reg[1] & 3;
114 	size = size_tab[mode];
115 
116 	/* Render sprites */
117 	for(i = 0; i < sprites_found; i++)
118 	{
119 		p = &sprites[i];
120 		lb = &linebuf[p->xpos];
121 		lut = &tms_obj_lut[(p->attr & 0x0F) << 8];
122 
123 		/* Point to expanded PG data */
124 		ex[0] = bp_expand[p->sg[0]];
125 		ex[1] = bp_expand[p->sg[1]];
126 
127 		/* Clip left edge */
128 		if(p->xpos < 0)
129 			start = 0 - p->xpos;
130 		else
131 			start = 0;
132 
133 		/* Clip right edge */
134 		if(p->xpos > 256 - size)
135 			end = 256 - p->xpos;
136 		else
137 			end = size;
138 
139 		/* Render sprite line */
140 		switch(mode)
141 		{
142 			case 0: /* 8x8 */
143 				for(x = start; x < end; x++) {
144 					if(ex[0][x])
145 					{
146 						/* Check sprite collision */
147 						if ((lb[x] & 0x40) && !(vdp.status & 0x20))
148 						{
149 							/* pixel-accurate SPR_COL flag */
150 							vdp.status |= 0x20;
151 							vdp.spr_col = (line << 8) | ((p->xpos + x + 13) >> 1);
152 						}
153 						lb[x] = lut[lb[x]];
154 					}
155 				}
156 				break;
157 
158 			case 1: /* 8x8 zoomed */
159 				for(x = start; x < end; x++) {
160 					if(ex[0][x >> 1])
161 					{
162 						/* Check sprite collision */
163 						if ((lb[x] & 0x40) && !(vdp.status & 0x20))
164 						{
165 							/* pixel-accurate SPR_COL flag */
166 							vdp.status |= 0x20;
167 							vdp.spr_col = (line << 8) | ((p->xpos + x + 13) >> 1);
168 						}
169 						lb[x] = lut[lb[x]];
170 					}
171 				}
172 				break;
173 
174 			case 2: /* 16x16 */
175 				for(x = start; x < end; x++) {
176 					if(ex[(x >> 3) & 1][x & 7])
177 					{
178 						/* Check sprite collision */
179 						if ((lb[x] & 0x40) && !(vdp.status & 0x20))
180 						{
181 							/* pixel-accurate SPR_COL flag */
182 							vdp.status |= 0x20;
183 							vdp.spr_col = (line << 8) | ((p->xpos + x + 13) >> 1);
184 						}
185 						lb[x] = lut[lb[x]];
186 					}
187 				}
188 				break;
189 
190 			case 3: /* 16x16 zoomed */
191 				for(x = start; x < end; x++) {
192 					if(ex[(x >> 4) & 1][(x >> 1) & 7])
193 					{
194 						/* Check sprite collision */
195 						if ((lb[x] & 0x40) && !(vdp.status & 0x20))
196 						{
197 							/* pixel-accurate SPR_COL flag */
198 							vdp.status |= 0x20;
199 							vdp.spr_col = (line << 8) | ((p->xpos + x + 13) >> 1);
200 						}
201 						lb[x] = lut[lb[x]];
202 					}
203 				}
204 				break;
205 		}
206 	}
207 }
208 
209 /****
210  1.) NOTE: xpos can be negative, but the 'start' value that is added
211  to xpos will ensure it is positive.
212 
213  For an EC sprite that is offscreen, 'start' will be larger
214  than 'end' and the for-loop used for rendering will abort
215  on the first pass.
216  ***/
217 
218 
219 #define RENDER_TX_LINE \
220 	*lb++ = 0x10 | clut[ *bpex++ ]; \
221 	*lb++ = 0x10 | clut[ *bpex++ ]; \
222 	*lb++ = 0x10 | clut[ *bpex++ ]; \
223 	*lb++ = 0x10 | clut[ *bpex++ ]; \
224 	*lb++ = 0x10 | clut[ *bpex++ ]; \
225 	*lb++ = 0x10 | clut[ *bpex++ ];
226 
227 #define RENDER_TX_BORDER \
228 	*lb++ = 0x10 | clut[0]; \
229 	*lb++ = 0x10 | clut[0]; \
230 	*lb++ = 0x10 | clut[0]; \
231 	*lb++ = 0x10 | clut[0]; \
232 	*lb++ = 0x10 | clut[0]; \
233 	*lb++ = 0x10 | clut[0]; \
234 	*lb++ = 0x10 | clut[0]; \
235 	*lb++ = 0x10 | clut[0]; \
236 	*lb++ = 0x10 | clut[0]; \
237 	*lb++ = 0x10 | clut[0]; \
238 	*lb++ = 0x10 | clut[0]; \
239 	*lb++ = 0x10 | clut[0]; \
240 	*lb++ = 0x10 | clut[0]; \
241 	*lb++ = 0x10 | clut[0]; \
242 	*lb++ = 0x10 | clut[0]; \
243 	*lb++ = 0x10 | clut[0];
244 
245 #define RENDER_GR_LINE \
246 	*lb++ = 0x10 | clut[ *bpex++ ]; \
247 	*lb++ = 0x10 | clut[ *bpex++ ]; \
248 	*lb++ = 0x10 | clut[ *bpex++ ]; \
249 	*lb++ = 0x10 | clut[ *bpex++ ]; \
250 	*lb++ = 0x10 | clut[ *bpex++ ]; \
251 	*lb++ = 0x10 | clut[ *bpex++ ]; \
252 	*lb++ = 0x10 | clut[ *bpex++ ]; \
253 	*lb++ = 0x10 | clut[ *bpex++ ];
254 
255 #define RENDER_MC_LINE \
256 	*lb++ = 0x10 | *mcex++; \
257 	*lb++ = 0x10 | *mcex++; \
258 	*lb++ = 0x10 | *mcex++; \
259 	*lb++ = 0x10 | *mcex++; \
260 	*lb++ = 0x10 | *mcex++; \
261 	*lb++ = 0x10 | *mcex++; \
262 	*lb++ = 0x10 | *mcex++; \
263 	*lb++ = 0x10 | *mcex++;
264 
make_tms_tables(void)265 void make_tms_tables(void)
266 {
267 	INT16 i, j, x;
268 	INT16 bd, pg, ct;
269 	INT16 sx, bx;
270 
271 	for(sx = 0; sx < 16; sx++)
272 	{
273 		for(bx = 0; bx < 256; bx++)
274 		{
275 			//          UINT8 bd = (bx & 0x0F);
276 			UINT8 bs = (bx & 0x40);
277 			//          UINT8 bt = (bd == 0) ? 1 : 0;
278 			UINT8 sd = (sx & 0x0F);
279 			//          UINT8 st = (sd == 0) ? 1 : 0;
280 
281 			// opaque sprite pixel, choose 2nd pal and set sprite marker
282 			if(sd && !bs)
283 			{
284 				tms_obj_lut[(sx<<8)|(bx)] = sd | 0x10 | 0x40;
285 			}
286 			else
287 				if(sd && bs)
288 				{
289 					// writing over a sprite
290 					tms_obj_lut[(sx<<8)|(bx)] = bx;
291 				}
292 				else
293 				{
294 					tms_obj_lut[(sx<<8)|(bx)] = bx;
295 				}
296 		}
297 	}
298 
299 
300 	/* Text lookup table */
301 	for(bd = 0; bd < 256; bd++)
302 	{
303 		UINT8 bg = (bd >> 0) & 0x0F;
304 		UINT8 fg = (bd >> 4) & 0x0F;
305 
306 		/* If foreground is transparent, use background color */
307 		if(fg == 0) fg = bg;
308 
309 		txt_lookup[bd][0] = bg;
310 		txt_lookup[bd][1] = fg;
311 	}
312 
313 	/* Multicolor lookup table */
314 	for(bd = 0; bd < 16; bd++)
315 	{
316 		for(pg = 0; pg < 256; pg++)
317 		{
318 			INT16 l = (pg >> 4) & 0x0F;
319 			INT16 r = (pg >> 0) & 0x0F;
320 
321 			/* If foreground is transparent, use background color */
322 			if(l == 0) l = bd;
323 			if(r == 0) r = bd;
324 
325 			/* Unpack 2 nibbles across eight pixels */
326 			for(x = 0; x < 8; x++)
327 			{
328 				INT16 c = (x & 4) ? r : l;
329 
330 				mc_lookup[bd][pg][x] = c;
331 			}
332 		}
333 	}
334 
335 	/* Make bitmap data expansion table */
336 	memset(bp_expand, 0, sizeof(bp_expand));
337 	for(i = 0; i < 256; i++)
338 	{
339 		for(j = 0; j < 8; j++)
340 		{
341 			INT16 c = (i >> (j ^ 7)) & 1;
342 			bp_expand[i][j] = c;
343 		}
344 	}
345 
346 	/* Graphics I/II lookup table */
347 	for(bd = 0; bd < 0x10; bd++)
348 	{
349 		for(ct = 0; ct < 0x100; ct++)
350 		{
351 			INT16 backdrop = (bd & 0x0F);
352 			INT16 background = (ct >> 0) & 0x0F;
353 			INT16 foreground = (ct >> 4) & 0x0F;
354 
355 			/* If foreground is transparent, use background color */
356 			if(background == 0) background = backdrop;
357 			if(foreground == 0) foreground = backdrop;
358 
359 			tms_lookup[bd][ct][0] = background;
360 			tms_lookup[bd][ct][1] = foreground;
361 		}
362 	}
363 }
364 
365 
render_bg_tms(INT16 line)366 void render_bg_tms(INT16 line)
367 {
368 	switch(vdp.mode & 7)
369 	{
370 		case 0: /* Graphics I */
371 			render_bg_m0(line);
372 			break;
373 
374 		case 1: /* Text */
375 			render_bg_m1(line);
376 			break;
377 
378 		case 2: /* Graphics II */
379 			render_bg_m2(line);
380 			break;
381 
382 		case 3: /* Text (Extended PG) */
383 			render_bg_m1x(line);
384 			break;
385 
386 		case 4: /* Multicolor */
387 			render_bg_m3(line);
388 			break;
389 
390 		case 5: /* Invalid (1+3) */
391 			render_bg_inv(line);
392 			break;
393 
394 		case 6: /* Multicolor (Extended PG) */
395 			render_bg_m3x(line);
396 			break;
397 
398 		case 7: /* Invalid (1+2+3) */
399 			render_bg_inv(line);
400 			break;
401 	}
402 }
403 
404 /* Graphics I */
render_bg_m0(INT16 line)405 void render_bg_m0(INT16 line)
406 {
407 	INT16 v_row  = (line & 7);
408 	INT16 column;
409 	INT16 name;
410 
411 	UINT8 *clut;
412 	UINT8 *bpex;
413 	UINT8 *lb = &linebuf[0];
414 	UINT8 *pn = &vdp.vram[vdp.pn + ((line >> 3) << 5)];
415 	UINT8 *ct = &vdp.vram[vdp.ct];
416 	UINT8 *pg = &vdp.vram[vdp.pg | (v_row)];
417 
418 	for(column = 0; column < 32; column++)
419 	{
420 		name = pn[column];
421 		clut = &tms_lookup[vdp.bd][ct[name >> 3]][0];
422 		bpex = &bp_expand[pg[name << 3]][0];
423 		RENDER_GR_LINE
424 	}
425 }
426 
427 /* Text */
render_bg_m1(INT16 line)428 void render_bg_m1(INT16 line)
429 {
430 	INT16 v_row  = (line & 7);
431 	INT16 column;
432 
433 	UINT8 *clut;
434 	UINT8 *bpex;
435 	UINT8 *lb = &linebuf[0];
436 	//  UINT8 *pn = &vdp.vram[vdp.pn + ((line >> 3) * 40)];
437 
438 	UINT8 *pn = &vdp.vram[vdp.pn + text_counter];
439 
440 	UINT8 *pg = &vdp.vram[vdp.pg | (v_row)];
441 	UINT8 bk = vdp.reg[7];
442 
443 	clut = &txt_lookup[bk][0];
444 
445 	for(column = 0; column < 40; column++)
446 	{
447 		bpex = &bp_expand[pg[pn[column] << 3]][0];
448 		RENDER_TX_LINE
449 	}
450 
451 	/* V3 */
452 	if((vdp.line & 7) == 7)
453 		text_counter += 40;
454 
455 	RENDER_TX_BORDER
456 }
457 
458 /* Text + extended PG */
render_bg_m1x(INT16 line)459 void render_bg_m1x(INT16 line)
460 {
461 	INT16 v_row  = (line & 7);
462 	INT16 column;
463 
464 	UINT8 *clut;
465 	UINT8 *bpex;
466 	UINT8 *lb = &linebuf[0];
467 	UINT8 *pn = &vdp.vram[vdp.pn + ((line >> 3) * 40)];
468 	UINT8 *pg = &vdp.vram[vdp.pg + (v_row) + ((line & 0xC0) << 5)];
469 	UINT8 bk = vdp.reg[7];
470 
471 	clut = &tms_lookup[0][bk][0];
472 
473 	for(column = 0; column < 40; column++)
474 	{
475 		bpex = &bp_expand[pg[pn[column] << 3]][0];
476 		RENDER_TX_LINE
477 	}
478 	RENDER_TX_BORDER
479 }
480 
481 /* Invalid (2+3/1+2+3) */
render_bg_inv(INT16)482 void render_bg_inv(INT16 /*line*/)
483 {
484 	INT16 column;
485 	UINT8 *clut;
486 	UINT8 *bpex;
487 	UINT8 *lb = &linebuf[0];
488 	UINT8 bk = vdp.reg[7];
489 
490 	clut = &txt_lookup[bk][0];
491 
492 	for(column = 0; column < 40; column++)
493 	{
494 		bpex = &bp_expand[0xF0][0];
495 		RENDER_TX_LINE
496 	}
497 }
498 
499 /* Multicolor */
render_bg_m3(INT16 line)500 void render_bg_m3(INT16 line)
501 {
502 	INT16 column;
503 	UINT8 *mcex;
504 	UINT8 *lb = &linebuf[0];
505 
506 	UINT8 *pn = &vdp.vram[vdp.pn + ((line >> 3) << 5)];
507 	UINT8 *pg = &vdp.vram[vdp.pg + ((line >> 2) & 7)];
508 
509 	for(column = 0; column < 32; column++)
510 	{
511 		mcex = &mc_lookup[vdp.bd][pg[pn[column]<<3]][0];
512 		RENDER_MC_LINE
513 	}
514 }
515 
516 /* Multicolor + extended PG */
render_bg_m3x(INT16 line)517 void render_bg_m3x(INT16 line)
518 {
519 	INT16 column;
520 	UINT8 *mcex;
521 	UINT8 *lb = &linebuf[0];
522 	UINT8 *pn = &vdp.vram[vdp.pn + ((line >> 3) << 5)];
523 	UINT8 *pg = &vdp.vram[vdp.pg + ((line >> 2) & 7) + ((line & 0xC0) << 5)];
524 
525 	for(column = 0; column < 32; column++)
526 	{
527 		mcex = &mc_lookup[vdp.bd][pg[pn[column]<<3]][0];
528 		RENDER_MC_LINE
529 	}
530 }
531 
532 /* Graphics II */
render_bg_m2(INT16 line)533 void render_bg_m2(INT16 line)
534 {
535 	INT16 v_row  = (line & 7);
536 	INT16 column;
537 	INT16 name;
538 
539 	UINT8 *clut;
540 	UINT8 *bpex;
541 	UINT8 *lb = &linebuf[0];
542 	UINT8 *pn = &vdp.vram[vdp.pn | ((line & 0xF8) << 2)];
543 	UINT8 *ct = &vdp.vram[(vdp.ct & 0x2000) | (v_row) | ((line & 0xC0) << 5)];
544 	UINT8 *pg = &vdp.vram[(vdp.pg & 0x2000) | (v_row) | ((line & 0xC0) << 5)];
545 
546 	for(column = 0; column < 32; column++)
547 	{
548 		name = pn[column] << 3;
549 		clut = &tms_lookup[vdp.bd][ct[name]][0];
550 		bpex = &bp_expand[pg[name]][0];
551 		RENDER_GR_LINE
552 	}
553 }
554 
555