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