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