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