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