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