mirror of
https://github.com/0ldsk00l/smsplus.git
synced 2025-04-02 10:32:07 -04:00
517 lines
13 KiB
C
517 lines
13 KiB
C
/*
|
|
tms.c --
|
|
TMS9918 and legacy video mode support.
|
|
*/
|
|
#include "shared.h"
|
|
|
|
int text_counter; /* Text offset counter */
|
|
uint8 tms_lookup[16][256][2]; /* Expand BD, PG data into 8-bit pixels (G1,G2) */
|
|
uint8 mc_lookup[16][256][8]; /* Expand BD, PG data into 8-bit pixels (MC) */
|
|
uint8 txt_lookup[256][2]; /* Expand BD, PG data into 8-bit pixels (TX) */
|
|
uint8 bp_expand[256][8]; /* Expand PG data into 8-bit pixels */
|
|
uint8 tms_obj_lut[16*256]; /* Look up priority between SG and display pixels */
|
|
|
|
static const uint8 diff_mask[] = {0x07, 0x07, 0x0F, 0x0F};
|
|
static const uint8 name_mask[] = {0xFF, 0xFF, 0xFC, 0xFC};
|
|
static const uint8 diff_shift[] = {0, 1, 0, 1};
|
|
static const uint8 size_tab[] = {8, 16, 16, 32};
|
|
|
|
/* Internally latched sprite data in the VDP */
|
|
typedef struct {
|
|
int xpos;
|
|
uint8 attr;
|
|
uint8 sg[2];
|
|
} tms_sprite;
|
|
|
|
tms_sprite sprites[4];
|
|
int sprites_found;
|
|
|
|
void parse_line(int line)
|
|
{
|
|
int yp, i;
|
|
int mode = vdp.reg[1] & 3;
|
|
int size = size_tab[mode];
|
|
int diff, name;
|
|
uint8 *sa, *sg;
|
|
tms_sprite *p;
|
|
|
|
/* Reset # of sprites found */
|
|
sprites_found = 0;
|
|
|
|
/* Parse sprites */
|
|
for(i = 0; i < 32; i++)
|
|
{
|
|
/* Point to current sprite in SA and our current sprite record */
|
|
p = &sprites[sprites_found];
|
|
sa = &vdp.vram[vdp.sa + (i << 2)];
|
|
|
|
/* Fetch Y coordinate */
|
|
yp = sa[0];
|
|
|
|
/* Check for end marker */
|
|
if(yp == 0xD0)
|
|
goto parse_end;
|
|
|
|
/* Wrap Y position */
|
|
if(yp > 0xE0)
|
|
yp -= 256;
|
|
|
|
/* Check if sprite falls on following line */
|
|
if(line >= yp && line < (yp + size))
|
|
{
|
|
/* Sprite overflow on this line */
|
|
if(sprites_found == 4)
|
|
{
|
|
/* Set 5S and abort parsing */
|
|
vdp.status |= 0x40;
|
|
goto parse_end;
|
|
}
|
|
|
|
/* Fetch X position */
|
|
p->xpos = sa[1];
|
|
|
|
/* Fetch name */
|
|
name = sa[2] & name_mask[mode];
|
|
|
|
/* Load attribute into attribute storage */
|
|
p->attr = sa[3];
|
|
|
|
/* Apply early clock bit */
|
|
if(p->attr & 0x80)
|
|
p->xpos -= 32;
|
|
|
|
/* Calculate offset in pattern */
|
|
diff = ((line - yp) >> diff_shift[mode]) & diff_mask[mode];
|
|
|
|
/* Insert additional name bit for 16-pixel tall sprites */
|
|
if(diff & 8)
|
|
name |= 1;
|
|
|
|
/* Fetch SG data */
|
|
sg = &vdp.vram[vdp.sg | (name << 3) | (diff & 7)];
|
|
p->sg[0] = sg[0x00];
|
|
p->sg[1] = sg[0x10];
|
|
|
|
/* Bump found sprite count */
|
|
++sprites_found;
|
|
}
|
|
}
|
|
parse_end:
|
|
|
|
/* Insert number of last sprite entry processed */
|
|
vdp.status = (vdp.status & 0xE0) | (i & 0x1F);
|
|
}
|
|
|
|
void render_obj_tms(int line)
|
|
{
|
|
int i, x = 0;
|
|
int size, start, end, mode;
|
|
uint8 *lb, *lut, *ex[2];
|
|
tms_sprite *p;
|
|
|
|
mode = vdp.reg[1] & 3;
|
|
size = size_tab[mode];
|
|
|
|
/* Render sprites */
|
|
for(i = 0; i < sprites_found; i++)
|
|
{
|
|
p = &sprites[i];
|
|
lb = &linebuf[p->xpos];
|
|
lut = &tms_obj_lut[(p->attr & 0x0F) << 8];
|
|
|
|
/* Point to expanded PG data */
|
|
ex[0] = bp_expand[p->sg[0]];
|
|
ex[1] = bp_expand[p->sg[1]];
|
|
|
|
/* Clip left edge */
|
|
if(p->xpos < 0)
|
|
start = 0 - p->xpos;
|
|
else
|
|
start = 0;
|
|
|
|
/* Clip right edge */
|
|
if(p->xpos > 256 - size)
|
|
end = 256 - p->xpos;
|
|
else
|
|
end = size;
|
|
|
|
/* Render sprite line */
|
|
switch(mode)
|
|
{
|
|
case 0: /* 8x8 */
|
|
for(x = start; x < end; x++) {
|
|
if(ex[0][x])
|
|
lb[x] = lut[lb[x]];
|
|
}
|
|
break;
|
|
|
|
case 1: /* 8x8 zoomed */
|
|
for(x = start; x < end; x++) {
|
|
if(ex[0][x >> 1])
|
|
lb[x] = lut[lb[x]];
|
|
}
|
|
break;
|
|
|
|
case 2: /* 16x16 */
|
|
for(x = start; x < end; x++) {
|
|
if(ex[(x >> 3) & 1][x & 7])
|
|
lb[x] = lut[lb[x]];
|
|
}
|
|
break;
|
|
|
|
case 3: /* 16x16 zoomed */
|
|
for(x = start; x < end; x++) {
|
|
if(ex[(x >> 4) & 1][(x >> 1) & 7])
|
|
lb[x] = lut[lb[x]];
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/****
|
|
1.) NOTE: xpos can be negative, but the 'start' value that is added
|
|
to xpos will ensure it is positive.
|
|
|
|
For an EC sprite that is offscreen, 'start' will be larger
|
|
than 'end' and the for-loop used for rendering will abort
|
|
on the first pass.
|
|
***/
|
|
|
|
|
|
#define RENDER_TX_LINE \
|
|
*lb++ = 0x10 | clut[ *bpex++ ]; \
|
|
*lb++ = 0x10 | clut[ *bpex++ ]; \
|
|
*lb++ = 0x10 | clut[ *bpex++ ]; \
|
|
*lb++ = 0x10 | clut[ *bpex++ ]; \
|
|
*lb++ = 0x10 | clut[ *bpex++ ]; \
|
|
*lb++ = 0x10 | clut[ *bpex++ ];
|
|
|
|
#define RENDER_TX_BORDER \
|
|
*lb++ = 0x10 | clut[0]; \
|
|
*lb++ = 0x10 | clut[0]; \
|
|
*lb++ = 0x10 | clut[0]; \
|
|
*lb++ = 0x10 | clut[0]; \
|
|
*lb++ = 0x10 | clut[0]; \
|
|
*lb++ = 0x10 | clut[0]; \
|
|
*lb++ = 0x10 | clut[0]; \
|
|
*lb++ = 0x10 | clut[0]; \
|
|
*lb++ = 0x10 | clut[0]; \
|
|
*lb++ = 0x10 | clut[0]; \
|
|
*lb++ = 0x10 | clut[0]; \
|
|
*lb++ = 0x10 | clut[0]; \
|
|
*lb++ = 0x10 | clut[0]; \
|
|
*lb++ = 0x10 | clut[0]; \
|
|
*lb++ = 0x10 | clut[0]; \
|
|
*lb++ = 0x10 | clut[0];
|
|
|
|
#define RENDER_GR_LINE \
|
|
*lb++ = 0x10 | clut[ *bpex++ ]; \
|
|
*lb++ = 0x10 | clut[ *bpex++ ]; \
|
|
*lb++ = 0x10 | clut[ *bpex++ ]; \
|
|
*lb++ = 0x10 | clut[ *bpex++ ]; \
|
|
*lb++ = 0x10 | clut[ *bpex++ ]; \
|
|
*lb++ = 0x10 | clut[ *bpex++ ]; \
|
|
*lb++ = 0x10 | clut[ *bpex++ ]; \
|
|
*lb++ = 0x10 | clut[ *bpex++ ];
|
|
|
|
#define RENDER_MC_LINE \
|
|
*lb++ = 0x10 | *mcex++; \
|
|
*lb++ = 0x10 | *mcex++; \
|
|
*lb++ = 0x10 | *mcex++; \
|
|
*lb++ = 0x10 | *mcex++; \
|
|
*lb++ = 0x10 | *mcex++; \
|
|
*lb++ = 0x10 | *mcex++; \
|
|
*lb++ = 0x10 | *mcex++; \
|
|
*lb++ = 0x10 | *mcex++;
|
|
|
|
void make_tms_tables(void)
|
|
{
|
|
int i, j, x;
|
|
int bd, pg, ct;
|
|
int sx, bx;
|
|
|
|
for(sx = 0; sx < 16; sx++)
|
|
{
|
|
for(bx = 0; bx < 256; bx++)
|
|
{
|
|
// uint8 bd = (bx & 0x0F);
|
|
uint8 bs = (bx & 0x40);
|
|
// uint8 bt = (bd == 0) ? 1 : 0;
|
|
uint8 sd = (sx & 0x0F);
|
|
// uint8 st = (sd == 0) ? 1 : 0;
|
|
|
|
// opaque sprite pixel, choose 2nd pal and set sprite marker
|
|
if(sd && !bs)
|
|
{
|
|
tms_obj_lut[(sx<<8)|(bx)] = sd | 0x10 | 0x40;
|
|
}
|
|
else
|
|
if(sd && bs)
|
|
{
|
|
// writing over a sprite
|
|
tms_obj_lut[(sx<<8)|(bx)] = bx;
|
|
}
|
|
else
|
|
{
|
|
tms_obj_lut[(sx<<8)|(bx)] = bx;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Text lookup table */
|
|
for(bd = 0; bd < 256; bd++)
|
|
{
|
|
uint8 bg = (bd >> 0) & 0x0F;
|
|
uint8 fg = (bd >> 4) & 0x0F;
|
|
|
|
/* If foreground is transparent, use background color */
|
|
if(fg == 0) fg = bg;
|
|
|
|
txt_lookup[bd][0] = bg;
|
|
txt_lookup[bd][1] = fg;
|
|
}
|
|
|
|
/* Multicolor lookup table */
|
|
for(bd = 0; bd < 16; bd++)
|
|
{
|
|
for(pg = 0; pg < 256; pg++)
|
|
{
|
|
int l = (pg >> 4) & 0x0F;
|
|
int r = (pg >> 0) & 0x0F;
|
|
|
|
/* If foreground is transparent, use background color */
|
|
if(l == 0) l = bd;
|
|
if(r == 0) r = bd;
|
|
|
|
/* Unpack 2 nibbles across eight pixels */
|
|
for(x = 0; x < 8; x++)
|
|
{
|
|
int c = (x & 4) ? r : l;
|
|
|
|
mc_lookup[bd][pg][x] = c;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Make bitmap data expansion table */
|
|
memset(bp_expand, 0, sizeof(bp_expand));
|
|
for(i = 0; i < 256; i++)
|
|
{
|
|
for(j = 0; j < 8; j++)
|
|
{
|
|
int c = (i >> (j ^ 7)) & 1;
|
|
bp_expand[i][j] = c;
|
|
}
|
|
}
|
|
|
|
/* Graphics I/II lookup table */
|
|
for(bd = 0; bd < 0x10; bd++)
|
|
{
|
|
for(ct = 0; ct < 0x100; ct++)
|
|
{
|
|
int backdrop = (bd & 0x0F);
|
|
int background = (ct >> 0) & 0x0F;
|
|
int foreground = (ct >> 4) & 0x0F;
|
|
|
|
/* If foreground is transparent, use background color */
|
|
if(background == 0) background = backdrop;
|
|
if(foreground == 0) foreground = backdrop;
|
|
|
|
tms_lookup[bd][ct][0] = background;
|
|
tms_lookup[bd][ct][1] = foreground;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void render_bg_tms(int line)
|
|
{
|
|
switch(vdp.mode & 7)
|
|
{
|
|
case 0: /* Graphics I */
|
|
render_bg_m0(line);
|
|
break;
|
|
|
|
case 1: /* Text */
|
|
render_bg_m1(line);
|
|
break;
|
|
|
|
case 2: /* Graphics II */
|
|
render_bg_m2(line);
|
|
break;
|
|
|
|
case 3: /* Text (Extended PG) */
|
|
render_bg_m1x(line);
|
|
break;
|
|
|
|
case 4: /* Multicolor */
|
|
render_bg_m3(line);
|
|
break;
|
|
|
|
case 5: /* Invalid (1+3) */
|
|
render_bg_inv(line);
|
|
break;
|
|
|
|
case 6: /* Multicolor (Extended PG) */
|
|
render_bg_m3x(line);
|
|
break;
|
|
|
|
case 7: /* Invalid (1+2+3) */
|
|
render_bg_inv(line);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Graphics I */
|
|
void render_bg_m0(int line)
|
|
{
|
|
int v_row = (line & 7);
|
|
int column;
|
|
int name;
|
|
|
|
uint8 *clut;
|
|
uint8 *bpex;
|
|
uint8 *lb = &linebuf[0];
|
|
uint8 *pn = &vdp.vram[vdp.pn + ((line >> 3) << 5)];
|
|
uint8 *ct = &vdp.vram[vdp.ct];
|
|
uint8 *pg = &vdp.vram[vdp.pg | (v_row)];
|
|
|
|
for(column = 0; column < 32; column++)
|
|
{
|
|
name = pn[column];
|
|
clut = &tms_lookup[vdp.bd][ct[name >> 3]][0];
|
|
bpex = &bp_expand[pg[name << 3]][0];
|
|
RENDER_GR_LINE
|
|
}
|
|
}
|
|
|
|
/* Text */
|
|
void render_bg_m1(int line)
|
|
{
|
|
int v_row = (line & 7);
|
|
int column;
|
|
|
|
uint8 *clut;
|
|
uint8 *bpex;
|
|
uint8 *lb = &linebuf[0];
|
|
// uint8 *pn = &vdp.vram[vdp.pn + ((line >> 3) * 40)];
|
|
|
|
uint8 *pn = &vdp.vram[vdp.pn + text_counter];
|
|
|
|
uint8 *pg = &vdp.vram[vdp.pg | (v_row)];
|
|
uint8 bk = vdp.reg[7];
|
|
|
|
clut = &txt_lookup[bk][0];
|
|
|
|
for(column = 0; column < 40; column++)
|
|
{
|
|
bpex = &bp_expand[pg[pn[column] << 3]][0];
|
|
RENDER_TX_LINE
|
|
}
|
|
|
|
/* V3 */
|
|
if((vdp.line & 7) == 7)
|
|
text_counter += 40;
|
|
|
|
RENDER_TX_BORDER
|
|
}
|
|
|
|
/* Text + extended PG */
|
|
void render_bg_m1x(int line)
|
|
{
|
|
int v_row = (line & 7);
|
|
int column;
|
|
|
|
uint8 *clut;
|
|
uint8 *bpex;
|
|
uint8 *lb = &linebuf[0];
|
|
uint8 *pn = &vdp.vram[vdp.pn + ((line >> 3) * 40)];
|
|
uint8 *pg = &vdp.vram[vdp.pg + (v_row) + ((line & 0xC0) << 5)];
|
|
uint8 bk = vdp.reg[7];
|
|
|
|
clut = &tms_lookup[0][bk][0];
|
|
|
|
for(column = 0; column < 40; column++)
|
|
{
|
|
bpex = &bp_expand[pg[pn[column] << 3]][0];
|
|
RENDER_TX_LINE
|
|
}
|
|
RENDER_TX_BORDER
|
|
}
|
|
|
|
/* Invalid (2+3/1+2+3) */
|
|
void render_bg_inv(int line)
|
|
{
|
|
int column;
|
|
uint8 *clut;
|
|
uint8 *bpex;
|
|
uint8 *lb = &linebuf[0];
|
|
uint8 bk = vdp.reg[7];
|
|
|
|
clut = &txt_lookup[bk][0];
|
|
|
|
for(column = 0; column < 40; column++)
|
|
{
|
|
bpex = &bp_expand[0xF0][0];
|
|
RENDER_TX_LINE
|
|
}
|
|
}
|
|
|
|
/* Multicolor */
|
|
void render_bg_m3(int line)
|
|
{
|
|
int column;
|
|
uint8 *mcex;
|
|
uint8 *lb = &linebuf[0];
|
|
|
|
uint8 *pn = &vdp.vram[vdp.pn + ((line >> 3) << 5)];
|
|
uint8 *pg = &vdp.vram[vdp.pg + ((line >> 2) & 7)];
|
|
|
|
for(column = 0; column < 32; column++)
|
|
{
|
|
mcex = &mc_lookup[vdp.bd][pg[pn[column]<<3]][0];
|
|
RENDER_MC_LINE
|
|
}
|
|
}
|
|
|
|
/* Multicolor + extended PG */
|
|
void render_bg_m3x(int line)
|
|
{
|
|
int column;
|
|
uint8 *mcex;
|
|
uint8 *lb = &linebuf[0];
|
|
uint8 *pn = &vdp.vram[vdp.pn + ((line >> 3) << 5)];
|
|
uint8 *pg = &vdp.vram[vdp.pg + ((line >> 2) & 7) + ((line & 0xC0) << 5)];
|
|
|
|
for(column = 0; column < 32; column++)
|
|
{
|
|
mcex = &mc_lookup[vdp.bd][pg[pn[column]<<3]][0];
|
|
RENDER_MC_LINE
|
|
}
|
|
}
|
|
|
|
/* Graphics II */
|
|
void render_bg_m2(int line)
|
|
{
|
|
int v_row = (line & 7);
|
|
int column;
|
|
int name;
|
|
|
|
uint8 *clut;
|
|
uint8 *bpex;
|
|
uint8 *lb = &linebuf[0];
|
|
uint8 *pn = &vdp.vram[vdp.pn | ((line & 0xF8) << 2)];
|
|
uint8 *ct = &vdp.vram[(vdp.ct & 0x2000) | (v_row) | ((line & 0xC0) << 5)];
|
|
uint8 *pg = &vdp.vram[(vdp.pg & 0x2000) | (v_row) | ((line & 0xC0) << 5)];
|
|
|
|
for(column = 0; column < 32; column++)
|
|
{
|
|
name = pn[column] << 3;
|
|
clut = &tms_lookup[vdp.bd][ct[name]][0];
|
|
bpex = &bp_expand[pg[name]][0];
|
|
RENDER_GR_LINE
|
|
}
|
|
}
|
|
|