mirror of
https://github.com/libretro/smsplus-gx.git
synced 2024-12-12 11:35:40 +00:00
830 lines
20 KiB
C
830 lines
20 KiB
C
/******************************************************************************
|
|
* Sega Master System / GameGear Emulator
|
|
* Copyright (C) 1998-2007 Charles MacDonald
|
|
*
|
|
* additionnal code by Eke-Eke (SMS Plus GX)
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
* VDP rendering core
|
|
*
|
|
******************************************************************************/
|
|
|
|
#include "shared.h"
|
|
/*** Vertical Counter Tables ***/
|
|
extern uint8_t *vc_table[2][3];
|
|
|
|
static struct
|
|
{
|
|
uint16_t yrange;
|
|
uint16_t xpos;
|
|
uint16_t attr;
|
|
} object_info[64];
|
|
|
|
/* Background drawing function */
|
|
void (*render_bg)(int32_t line) = NULL;
|
|
void (*render_obj)(int32_t line) = NULL;
|
|
|
|
/* Pointer to output buffer */
|
|
uint8_t *linebuf;
|
|
|
|
/* Pixel 8-bit color tables */
|
|
uint8_t sms_cram_expand_table[4];
|
|
uint8_t gg_cram_expand_table[16];
|
|
|
|
/* Dirty pattern info */
|
|
uint8_t bg_name_dirty[0x200]; /* 1= This pattern is dirty */
|
|
uint16_t bg_name_list[0x200]; /* List of modified pattern indices */
|
|
uint16_t bg_list_index; /* # of modified patterns in list */
|
|
|
|
/* Internal buffer for drawing non 8-bit displays */
|
|
static uint8_t internal_buffer[0x200];
|
|
|
|
/* Precalculated pixel table */
|
|
static uint16_t pixel[PALETTE_SIZE];
|
|
|
|
static uint8_t bg_pattern_cache[0x20000];/* Cached and flipped patterns */
|
|
|
|
/* Pixel look-up table */
|
|
static uint8_t lut[0x10000];
|
|
|
|
static uint8_t object_index_count;
|
|
|
|
/* Top Border area height */
|
|
static uint8_t active_border[2][3] =
|
|
{
|
|
{24, 8, 0}, /* NTSC VDP */
|
|
{24, 8, 0} /* PAL VDP */
|
|
};
|
|
|
|
/* Active Scan Area height */
|
|
static uint16_t active_range[2] =
|
|
{
|
|
243, /* NTSC VDP */
|
|
267 /* PAL VDP */
|
|
};
|
|
|
|
/* CRAM palette in TMS compatibility mode */
|
|
static const uint8_t tms_crom[] =
|
|
{
|
|
0x00, 0x00, 0x08, 0x0C,
|
|
0x10, 0x30, 0x01, 0x3C,
|
|
0x02, 0x03, 0x05, 0x0F,
|
|
0x04, 0x33, 0x15, 0x3F
|
|
};
|
|
|
|
/* original TMS palette for SG-1000 & Colecovision */
|
|
static uint8_t tms_palette[16*3][3] =
|
|
{
|
|
/* from Sean Young (http://www.smspower.org/dev/docs/tms9918a.txt) */
|
|
{ 0, 0, 0},
|
|
{ 0, 0, 0},
|
|
{ 33,200, 66},
|
|
{ 94,220,120},
|
|
{ 84, 85,237},
|
|
{125,118,252},
|
|
{212, 82, 77},
|
|
{ 66,235,245},
|
|
{252, 85, 84},
|
|
{255,121,120},
|
|
{212,193, 84},
|
|
{230,206,128},
|
|
{ 33,176, 59},
|
|
{201, 91,186},
|
|
{204,204,204},
|
|
{255,255,255},
|
|
/* from Omar Cornut (http://www.smspower.org/dev/docs/sg1000.txt) */
|
|
{ 0, 0, 0},
|
|
{ 0, 0, 0},
|
|
{ 32,192, 30},
|
|
{ 96,224, 96},
|
|
{ 32, 32,224},
|
|
{ 64, 96,224},
|
|
{160, 32, 32},
|
|
{ 64,192,224},
|
|
{224, 32, 32},
|
|
{224, 64, 64},
|
|
{192,192, 32},
|
|
{192,192,128},
|
|
{ 32,128, 32},
|
|
{192, 64,160},
|
|
{160,160,160},
|
|
{224,224,224},
|
|
/* from Richard F. Drushel (http://users.stargate.net/~drushel/pub/coleco/twwmca/wk961118.html) */
|
|
{ 0, 0, 0},
|
|
{ 0, 0, 0},
|
|
{ 71,183, 59},
|
|
{124,207,111},
|
|
{ 93, 78,255},
|
|
{128,114,255},
|
|
{182, 98, 71},
|
|
{ 93,200,237},
|
|
{215,107, 72},
|
|
{251,143,108},
|
|
{195,205, 65},
|
|
{211,218,118},
|
|
{ 62,159, 47},
|
|
{182,100,199},
|
|
{204,204,204},
|
|
{255,255,255}
|
|
};
|
|
|
|
/* Attribute expansion table */
|
|
static const uint32_t atex[4] =
|
|
{
|
|
0x00000000,
|
|
0x10101010,
|
|
0x20202020,
|
|
0x30303030,
|
|
};
|
|
|
|
/* Bitplane to packed pixel LUT */
|
|
static uint32_t bp_lut[0x10000];
|
|
|
|
static void parse_satb(int32_t line);
|
|
static void update_bg_pattern_cache(void);
|
|
static void remap_8_to_16(int32_t line);
|
|
|
|
/* Macros to access memory 32-bits at a time (from MAME's drawgfx.c) */
|
|
|
|
#ifdef ALIGN_DWORD
|
|
|
|
static __inline__ uint32_t read_dword(void *address)
|
|
{
|
|
if ((uint32_t)address & 3)
|
|
{
|
|
#ifdef LSB_FIRST /* little endian version */
|
|
return ( *((uint8_t *)address) +
|
|
(*((uint8_t *)address+1) << 8) +
|
|
(*((uint8_t *)address+2) << 16) +
|
|
(*((uint8_t *)address+3) << 24) );
|
|
#else /* big endian version */
|
|
return ( *((uint8_t *)address+3) +
|
|
(*((uint8_t *)address+2) << 8) +
|
|
(*((uint8_t *)address+1) << 16) +
|
|
(*((uint8_t *)address) << 24) );
|
|
#endif
|
|
}
|
|
else
|
|
return *(uint32_t *)address;
|
|
}
|
|
|
|
|
|
static __inline__ void write_dword(void *address, uint32_t data)
|
|
{
|
|
if ((uint32_t)address & 3)
|
|
{
|
|
#ifdef LSB_FIRST
|
|
*((uint8_t *)address) = data;
|
|
*((uint8_t *)address+1) = (data >> 8);
|
|
*((uint8_t *)address+2) = (data >> 16);
|
|
*((uint8_t *)address+3) = (data >> 24);
|
|
#else
|
|
*((uint8_t *)address+3) = data;
|
|
*((uint8_t *)address+2) = (data >> 8);
|
|
*((uint8_t *)address+1) = (data >> 16);
|
|
*((uint8_t *)address) = (data >> 24);
|
|
#endif
|
|
return;
|
|
}
|
|
else
|
|
*(uint32_t *)address = data;
|
|
}
|
|
#else
|
|
#define read_dword(address) *(uint32_t *)address
|
|
#define write_dword(address,data) *(uint32_t *)address=data
|
|
#endif
|
|
|
|
|
|
/****************************************************************************/
|
|
|
|
|
|
void render_shutdown(void)
|
|
{
|
|
}
|
|
|
|
/* Initialize the rendering data */
|
|
void render_init(void)
|
|
{
|
|
int32_t j;
|
|
int32_t bx, sx, b, s, bp, bf, sf, c;
|
|
|
|
make_tms_tables();
|
|
|
|
/* Generate 64k of data for the look up table */
|
|
for(bx = 0; bx < 0x100; bx++)
|
|
{
|
|
for(sx = 0; sx < 0x100; sx++)
|
|
{
|
|
/* Background pixel */
|
|
b = (bx & 0x0F);
|
|
|
|
/* Background priority */
|
|
bp = (bx & 0x20) ? 1 : 0;
|
|
|
|
/* Full background pixel + priority + sprite marker */
|
|
bf = (bx & 0x7F);
|
|
|
|
/* Sprite pixel */
|
|
s = (sx & 0x0F);
|
|
|
|
/* Full sprite pixel, w/ palette and marker bits added */
|
|
sf = (sx & 0x0F) | 0x10 | 0x40;
|
|
|
|
/* Overwriting a sprite pixel ? */
|
|
if(bx & 0x40)
|
|
{
|
|
/* Return the input */
|
|
c = bf;
|
|
}
|
|
else
|
|
{
|
|
/* Work out priority and transparency for both pixels */
|
|
if(bp)
|
|
{
|
|
/* Underlying pixel is high priority */
|
|
if(b)
|
|
{
|
|
c = bf | 0x40;
|
|
}
|
|
else
|
|
{
|
|
if(s) c = sf;
|
|
else c = bf;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Underlying pixel is low priority */
|
|
if(s) c = sf;
|
|
else c = bf;
|
|
}
|
|
}
|
|
/* Store result */
|
|
lut[(bx << 8) | (sx)] = c;
|
|
}
|
|
}
|
|
|
|
/* Make bitplane to pixel lookup table */
|
|
for(int32_t i = 0; i < 0x100; i++)
|
|
for(j = 0; j < 0x100; j++)
|
|
{
|
|
int32_t x;
|
|
uint32_t out = 0;
|
|
for(x = 0; x < 8; x++)
|
|
{
|
|
out |= (j & (0x80 >> x)) ? (uint32_t)(8 << (x << 2)) : 0;
|
|
out |= (i & (0x80 >> x)) ? (uint32_t)(4 << (x << 2)) : 0;
|
|
}
|
|
#if LSB_FIRST
|
|
bp_lut[(j << 8) | (i)] = out;
|
|
#else
|
|
bp_lut[(i << 8) | (j)] = out;
|
|
#endif
|
|
}
|
|
|
|
sms_cram_expand_table[0] = 0;
|
|
sms_cram_expand_table[1] = 0x55;
|
|
sms_cram_expand_table[2] = 0xAA;
|
|
sms_cram_expand_table[3] = 0xFF;
|
|
|
|
for(uint8_t i = 0; i < 16; i++)
|
|
{
|
|
gg_cram_expand_table[i] = i << 4 | i;
|
|
}
|
|
}
|
|
|
|
|
|
/* Reset the rendering data */
|
|
void render_reset(void)
|
|
{
|
|
int32_t i;
|
|
|
|
/* Clear display bitmap */
|
|
memset(bitmap.data, 0, bitmap.pitch * bitmap.height);
|
|
|
|
/* Clear palette */
|
|
for(i = 0; i < PALETTE_SIZE; i++)
|
|
{
|
|
palette_sync(i);
|
|
}
|
|
|
|
/* Invalidate pattern cache */
|
|
memset(bg_name_dirty, 0, sizeof(bg_name_dirty));
|
|
memset(bg_name_list, 0, sizeof(bg_name_list));
|
|
bg_list_index = 0;
|
|
memset(bg_pattern_cache, 0, sizeof(bg_pattern_cache));
|
|
|
|
/* Pick default render routine */
|
|
if (vdp.reg[0] & 4)
|
|
{
|
|
render_bg = render_bg_sms;
|
|
render_obj = render_obj_sms;
|
|
}
|
|
else
|
|
{
|
|
render_bg = render_bg_tms;
|
|
render_obj = render_obj_tms;
|
|
}
|
|
}
|
|
|
|
static int32_t prev_line = -1;
|
|
|
|
/* Draw a line of the display */
|
|
void render_line(int32_t line)
|
|
{
|
|
int32_t view = 1;
|
|
|
|
/* Ensure we have not already rendered this line */
|
|
if (prev_line == line) return;
|
|
prev_line = line;
|
|
|
|
/* Ensure we're within the VDP active area (incl. overscan) */
|
|
int32_t top_border = active_border[sms.display][vdp.extended];
|
|
int32_t vline = (line + top_border) % vdp.lpf;
|
|
|
|
if (vline >= active_range[sms.display]) return;
|
|
|
|
/* adjust for Game Gear screen */
|
|
top_border = top_border + (vdp.height - bitmap.viewport.h) / 2;
|
|
|
|
/* Point to current line in output buffer */
|
|
linebuf = &internal_buffer[0];
|
|
|
|
/* Sprite limit flag is set at the beginning of the line */
|
|
if (vdp.spr_ovr)
|
|
{
|
|
vdp.spr_ovr = 0;
|
|
vdp.status |= 0x40;
|
|
}
|
|
|
|
/* Vertical borders */
|
|
if ((vline < top_border) || (vline >= (bitmap.viewport.h + top_border)))
|
|
{
|
|
/* Sprites are still processed offscreen */
|
|
if ((vdp.mode > 7) && (vdp.reg[1] & 0x40))
|
|
render_obj(line);
|
|
|
|
/* Line is only displayed where overscan is emulated */
|
|
view = 0;
|
|
}
|
|
/* Active display */
|
|
else
|
|
{
|
|
/* Display enabled ? */
|
|
if (vdp.reg[1] & 0x40)
|
|
{
|
|
/* Update pattern cache */
|
|
update_bg_pattern_cache();
|
|
|
|
/* Draw background */
|
|
render_bg(line);
|
|
|
|
/* Draw sprites */
|
|
render_obj(line);
|
|
|
|
/* This can take some CPU time. Some targets like the Retromini RS-90 code will crop this section
|
|
* as to make sure it fits on the screen and to avoid less artifacts from downscaling.
|
|
* This can be useful for other targets as well but adding a test case
|
|
* to check this might make it more expensive so best to do this only for the RS-90.
|
|
* */
|
|
#ifndef NOBLANKING_LEFTCOLUM
|
|
/* Blank leftmost column of display */
|
|
if((vdp.reg[0] & 0x20) && (IS_SMS || IS_MD))
|
|
memset(linebuf, BACKDROP_COLOR, 8);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
/* Background color */
|
|
memset(linebuf, BACKDROP_COLOR, bitmap.viewport.w + 2*bitmap.viewport.x);
|
|
}
|
|
}
|
|
|
|
/* Parse Sprites for next line */
|
|
if (vdp.mode > 7)
|
|
parse_satb(line);
|
|
else
|
|
parse_line(line);
|
|
|
|
/* LightGun mark */
|
|
#ifdef LIGHTGUN_ENABLED
|
|
/* Putting this in an ifdef because the condition loop is executed every render_line */
|
|
if (sms.device[0] == DEVICE_LIGHTGUN)
|
|
{
|
|
int32_t dy = vdp.line - input.analog[0][1];
|
|
if (abs(dy) < 6)
|
|
{
|
|
int32_t i;
|
|
int32_t start = input.analog[0][0] - 4;
|
|
int32_t end = input.analog[0][0] + 4;
|
|
if (start < 0) start = 0;
|
|
if (end > 255) end = 255;
|
|
for (i=start; i<end+1; i++)
|
|
{
|
|
linebuf[i] = 0xFF;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Only draw lines within the video output range ! */
|
|
if (view)
|
|
{
|
|
/* adjust output line */
|
|
vline -= top_border;
|
|
remap_8_to_16(vline);
|
|
}
|
|
}
|
|
|
|
/* Draw the Master System background */
|
|
void render_bg_sms(int32_t line)
|
|
{
|
|
int32_t locked = 0;
|
|
int32_t yscroll_mask = (vdp.extended) ? 256 : 224;
|
|
int32_t v_line = (line + vdp.vscroll) % yscroll_mask;
|
|
int32_t v_row = (v_line & 7) << 3;
|
|
int32_t hscroll = ((vdp.reg[0] & 0x40) && (line < 0x10) && (sms.console != CONSOLE_GG)) ? 0 : (0x100 - vdp.reg[8]);
|
|
int32_t column = 0;
|
|
uint16_t attr;
|
|
uint16_t SMS_VDP_BUG = (((sms.console == CONSOLE_SMS) && !(vdp.reg[2] & 1)) ? ~0x400 :0xFFFF);
|
|
uint16_t nt_addr = (vdp.ntab + ((v_line >> 3) << 6)) & SMS_VDP_BUG;
|
|
uint16_t nt_addr_stop_verticalscroll = (vdp.ntab + ((line >> 3) << 6)) & SMS_VDP_BUG;
|
|
uint16_t *nt = (uint16_t *)&vdp.vram[nt_addr];
|
|
int32_t nt_scroll = (hscroll >> 3);
|
|
int32_t shift = (hscroll & 7);
|
|
uint32_t atex_mask;
|
|
uint32_t *cache_ptr;
|
|
uint32_t *linebuf_ptr = (uint32_t *)&linebuf[0 - shift];
|
|
|
|
/* Draw first column (clipped) */
|
|
if(shift)
|
|
{
|
|
int32_t x;
|
|
for(x = shift; x < 8; x++)
|
|
linebuf[(0 - shift) + (x)] = 0;
|
|
column++;
|
|
}
|
|
|
|
/* Draw a line of the background */
|
|
for(; column < 32; column++)
|
|
{
|
|
/* Stop vertical scrolling for leftmost eight columns */
|
|
if((vdp.reg[0] & 0x80) && (!locked) && (column >= 24))
|
|
{
|
|
locked = 1;
|
|
v_row = (line & 7) << 3;
|
|
nt = (uint16_t *)&vdp.vram[nt_addr_stop_verticalscroll];
|
|
}
|
|
|
|
/* Get name table attribute word */
|
|
attr = nt[(column + nt_scroll) & 0x1F];
|
|
|
|
#ifndef LSB_FIRST
|
|
attr = (((attr & 0xFF) << 8) | ((attr & 0xFF00) >> 8));
|
|
#endif
|
|
/* Expand priority and palette bits */
|
|
atex_mask = atex[(attr >> 11) & 3];
|
|
|
|
/* Point to a line of pattern data in cache */
|
|
cache_ptr = (uint32_t *)&bg_pattern_cache[((attr & 0x7FF) << 6) | (v_row)];
|
|
|
|
/* Copy the left half, adding the attribute bits in */
|
|
write_dword( &linebuf_ptr[(column << 1)] , read_dword( &cache_ptr[0] ) | (atex_mask));
|
|
|
|
/* Copy the right half, adding the attribute bits in */
|
|
write_dword( &linebuf_ptr[(column << 1) | (1)], read_dword( &cache_ptr[1] ) | (atex_mask));
|
|
}
|
|
|
|
/* Draw last column (clipped) */
|
|
if(shift)
|
|
{
|
|
int32_t x;
|
|
uint8_t c, a;
|
|
uint8_t *p = &linebuf[(0 - shift)+(column << 3)];
|
|
attr = nt[(column + nt_scroll) & 0x1F];
|
|
#ifndef LSB_FIRST
|
|
attr = (((attr & 0xFF) << 8) | ((attr & 0xFF00) >> 8));
|
|
#endif
|
|
a = (attr >> 7) & 0x30;
|
|
|
|
for(x = 0; x < shift; x++)
|
|
{
|
|
c = bg_pattern_cache[((attr & 0x7FF) << 6) | (v_row) | (x)];
|
|
p[x] = ((c) | (a));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Draw sprites */
|
|
void render_obj_sms(int32_t line)
|
|
{
|
|
int32_t i,x,start,end,xp,yp,n;
|
|
uint8_t sp,bg;
|
|
uint8_t *linebuf_ptr;
|
|
uint8_t *cache_ptr;
|
|
|
|
int32_t width = 8;
|
|
|
|
/* Adjust dimensions for double size sprites */
|
|
if(vdp.reg[1] & 0x01)
|
|
width *= 2;
|
|
|
|
/* Draw sprites in front-to-back order */
|
|
for(i = 0; i < object_index_count; i++)
|
|
{
|
|
/* Width of sprite */
|
|
start = 0;
|
|
end = width;
|
|
|
|
/* Sprite X position */
|
|
xp = object_info[i].xpos;
|
|
|
|
/* Sprite Y range */
|
|
yp = object_info[i].yrange;
|
|
|
|
/* Pattern name */
|
|
n = object_info[i].attr;
|
|
|
|
/* X position shift */
|
|
if(vdp.reg[0] & 0x08) xp -= 8;
|
|
|
|
/* Add MSB of pattern name */
|
|
if(vdp.reg[6] & 0x04) n |= 0x0100;
|
|
|
|
/* Mask LSB for 8x16 sprites */
|
|
if(vdp.reg[1] & 0x02) n &= 0x01FE;
|
|
|
|
/* Point to offset in line buffer */
|
|
linebuf_ptr = (uint8_t *)&linebuf[xp];
|
|
|
|
/* Clip sprites on left edge */
|
|
if(xp < 0)
|
|
start = (0 - xp);
|
|
|
|
/* Clip sprites on right edge */
|
|
if((xp + width) > 256)
|
|
end = (256 - xp);
|
|
|
|
/* Draw double size sprite */
|
|
if(vdp.reg[1] & 0x01)
|
|
{
|
|
/* Retrieve tile data from cached nametable */
|
|
cache_ptr = (uint8_t *)&bg_pattern_cache[(n << 6) | ((yp >> 1) << 3)];
|
|
|
|
/* Draw sprite line (at 1/2 dot rate) */
|
|
for(x = start; x < end; x+=2)
|
|
{
|
|
/* Source pixel from cache */
|
|
sp = cache_ptr[(x >> 1)];
|
|
|
|
/* Only draw opaque sprite pixels */
|
|
if(sp)
|
|
{
|
|
/* Background pixel from line buffer */
|
|
bg = linebuf_ptr[x];
|
|
|
|
/* Look up result */
|
|
linebuf_ptr[x] = linebuf_ptr[x+1] = lut[(bg << 8) | (sp)];
|
|
|
|
/* Check sprite collision */
|
|
/* No sprite collision for 9th sprite. This passes Flubba's test */
|
|
if ((bg & 0x40) && !(vdp.status & 0x20) && object_index_count != 9)
|
|
{
|
|
/* pixel-accurate SPR_COL flag */
|
|
vdp.status |= 0x20;
|
|
vdp.spr_col = (line << 8) | ((xp + x + 13) >> 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else /* Regular size sprite (8x8 / 8x16) */
|
|
{
|
|
/* Retrieve tile data from cached nametable */
|
|
cache_ptr = (uint8_t *)&bg_pattern_cache[(n << 6) | (yp << 3)];
|
|
|
|
/* Draw sprite line */
|
|
for(x = start; x < end; x++)
|
|
{
|
|
/* Source pixel from cache */
|
|
sp = cache_ptr[x];
|
|
|
|
/* Only draw opaque sprite pixels */
|
|
if(sp)
|
|
{
|
|
/* Background pixel from line buffer */
|
|
bg = linebuf_ptr[x];
|
|
|
|
/* Look up result */
|
|
linebuf_ptr[x] = lut[(bg << 8) | (sp)];
|
|
|
|
/* Check sprite collision */
|
|
/* No sprite collision for 9th sprite. This passes Flubba's test */
|
|
if ((bg & 0x40) && !(vdp.status & 0x20) && object_index_count != 9)
|
|
{
|
|
/* pixel-accurate SPR_COL flag */
|
|
vdp.status |= 0x20;
|
|
vdp.spr_col = (line << 8) | ((xp + x + 13) >> 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Update a palette entry */
|
|
void palette_sync(int32_t index)
|
|
{
|
|
int32_t r, g, b;
|
|
|
|
/* VDP Mode */
|
|
if ((vdp.reg[0] & 4) || IS_GG)
|
|
{
|
|
/* Mode 4 or Game Gear TMS mode*/
|
|
if(sms.console == CONSOLE_GG)
|
|
{
|
|
/* GG palette */
|
|
/* ----BBBBGGGGRRRR */
|
|
r = (vdp.cram[(index << 1) | (0)] >> 0) & 0x0F;
|
|
g = (vdp.cram[(index << 1) | (0)] >> 4) & 0x0F;
|
|
b = (vdp.cram[(index << 1) | (1)] >> 0) & 0x0F;
|
|
|
|
r = gg_cram_expand_table[r];
|
|
g = gg_cram_expand_table[g];
|
|
b = gg_cram_expand_table[b];
|
|
pixel[index] = MAKE_PIXEL(r, g, b);
|
|
}
|
|
else
|
|
{
|
|
/* SMS palette */
|
|
/* --BBGGRR */
|
|
r = (vdp.cram[index] >> 0) & 3;
|
|
g = (vdp.cram[index] >> 2) & 3;
|
|
b = (vdp.cram[index] >> 4) & 3;
|
|
|
|
r = sms_cram_expand_table[r];
|
|
g = sms_cram_expand_table[g];
|
|
b = sms_cram_expand_table[b];
|
|
|
|
pixel[index] = MAKE_PIXEL((r), (g), (b));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* TMS Mode (16 colors only) */
|
|
int32_t color = index & 0x0F;
|
|
|
|
if (sms.console < CONSOLE_SMS)
|
|
{
|
|
/* pick one of the original TMS9918 palettes */
|
|
color += option.tms_pal * 16;
|
|
|
|
r = tms_palette[color][0];
|
|
g = tms_palette[color][1];
|
|
b = tms_palette[color][2];
|
|
}
|
|
else
|
|
{
|
|
/* fixed CRAM palette in TMS mode */
|
|
r = (tms_crom[color] >> 0) & 3;
|
|
g = (tms_crom[color] >> 2) & 3;
|
|
b = (tms_crom[color] >> 4) & 3;
|
|
|
|
r = sms_cram_expand_table[r];
|
|
g = sms_cram_expand_table[g];
|
|
b = sms_cram_expand_table[b];
|
|
}
|
|
pixel[index] = MAKE_PIXEL(r, g, b);
|
|
}
|
|
|
|
}
|
|
|
|
static void parse_satb(int32_t line)
|
|
{
|
|
/* Pointer to sprite attribute table */
|
|
uint8_t *st = (uint8_t *)&vdp.vram[vdp.satb];
|
|
|
|
/* Sprite counter (64 max.) */
|
|
int32_t i = 0;
|
|
|
|
/* Line counter value */
|
|
uint8_t vc = vc_table[sms.display][vdp.extended][line];
|
|
|
|
/* Sprite height (8x8 by default) */
|
|
uint8_t yp;
|
|
uint8_t height = 8;
|
|
|
|
/* Adjust height for 8x16 sprites */
|
|
if(vdp.reg[1] & 0x02)
|
|
height <<= 1;
|
|
|
|
/* Adjust height for zoomed sprites */
|
|
if(vdp.reg[1] & 0x01)
|
|
height <<= 1;
|
|
|
|
/* Sprite count for current line (8 max.) */
|
|
object_index_count = 0;
|
|
|
|
for(i = 0; i < 64; i++)
|
|
{
|
|
/* Sprite Y position */
|
|
yp = st[i];
|
|
|
|
/* Found end of sprite list marker for non-extended modes? */
|
|
if(vdp.extended == 0 && yp == 208)
|
|
return;
|
|
|
|
/* Wrap Y coordinate for sprites > 240 */
|
|
if(yp > 240) yp -= 256;
|
|
|
|
/* Compare sprite position with current line counter */
|
|
yp = vc - yp;
|
|
|
|
/* Sprite is within vertical range? */
|
|
if(yp < height)
|
|
{
|
|
/* Sprite limit reached? */
|
|
if (object_index_count == 8)
|
|
{
|
|
/* Flag is set only during active area */
|
|
if (line < vdp.height)
|
|
vdp.spr_ovr = 1;
|
|
|
|
/* End of sprite parsing */
|
|
if (option.spritelimit)
|
|
return;
|
|
}
|
|
|
|
/* Store sprite attributes for later processing */
|
|
object_info[object_index_count].yrange = yp;
|
|
object_info[object_index_count].xpos = st[0x80 + (i << 1)];
|
|
object_info[object_index_count].attr = st[0x81 + (i << 1)];
|
|
|
|
/* Increment Sprite count for current line */
|
|
++object_index_count;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void update_bg_pattern_cache(void)
|
|
{
|
|
int32_t i;
|
|
uint8_t x, y;
|
|
uint16_t name;
|
|
|
|
if(!bg_list_index) return;
|
|
|
|
for(i = 0; i < bg_list_index; i++)
|
|
{
|
|
name = bg_name_list[i];
|
|
bg_name_list[i] = 0;
|
|
|
|
for(y = 0; y < 8; y++)
|
|
{
|
|
if(bg_name_dirty[name] & (1 << y))
|
|
{
|
|
uint8_t *dst = &bg_pattern_cache[name << 6];
|
|
uint16_t bp01 = *(uint16_t *)&vdp.vram[(name << 5) | (y << 2) | (0)];
|
|
uint16_t bp23 = *(uint16_t *)&vdp.vram[(name << 5) | (y << 2) | (2)];
|
|
uint32_t temp = (bp_lut[bp01] >> 2) | (bp_lut[bp23]);
|
|
|
|
for(x = 0; x < 8; x++)
|
|
{
|
|
uint8_t c = (temp >> (x << 2)) & 0x0F;
|
|
dst[0x00000 | (y << 3) | (x)] = (c);
|
|
dst[0x08000 | (y << 3) | (x ^ 7)] = (c);
|
|
dst[0x10000 | ((y ^ 7) << 3) | (x)] = (c);
|
|
dst[0x18000 | ((y ^ 7) << 3) | (x ^ 7)] = (c);
|
|
}
|
|
}
|
|
}
|
|
bg_name_dirty[name] = 0;
|
|
}
|
|
|
|
bg_list_index = 0;
|
|
}
|
|
|
|
static void remap_8_to_16(int32_t line)
|
|
{
|
|
int32_t i;
|
|
uint16_t *p = (uint16_t *)&bitmap.data[(line * bitmap.pitch)];
|
|
int32_t width = (bitmap.viewport.w)+2 * bitmap.viewport.x;
|
|
|
|
for(i = 0; i < width; i++)
|
|
{
|
|
p[i] = pixel[ internal_buffer[i] & PIXEL_MASK ];
|
|
}
|
|
}
|