📄 screen.cpp
字号:
/* SCREEN.CPP - SNES Screen Renderer
Color depths (cdepth): 0=4 colors 1=16 colors 3=256 colors.
*/
#include "Common.hpp"
#include "GrEngine.hpp"
#include "CPU.hpp"
#include "emu65816.hpp"
#include "FileMan.hpp"
#include "GUI.hpp"
#include "SPC700.hpp"
#include "Screen.hpp"
#include <ASSERT.H>
//#define USE_C_SCREEN_CODE
int debugscreen;
int memdumpoffset = 0, vramdumpoffset = 0, oamdumpoffset = 0, cgramdumpoffset = 0;
int vramblitoffset = 0, vramblitcdepth = 0;
char vramcmpfn[40] = "";
byte debug_vram[65536];
union cachetile ctile [4][4096]; // 1MB!
byte cstatus [4096];
byte csolid [2][4096], cempty [2][4096]; // 8 bits, 1 for each scanline of the tile.
// Two sets 0=no vertical flip, 1=vertical flip
struct cachetilemap ctmap [4][128][128], sprmap [128][8][8];
// 1MB! + 64K
// here's the deal with the ctmap (cached tile map): 4 BGs*128 max height*
// 128 max width*2 priorities*8 bytes=1MB. This stores a more useful version of the
// tile maps. The unused priority entry has it's "empty" byte set to 0xFF, so nothing
// is drawn. Every scanline, one line of this cached tile map is built from the tile
// map data in VRAM (except that the same line will not be built two scanlines in a row,
// so it may be up to 8 lines between line copies.)
// Now, if the BG isn't large enough horizontally to fill 128 elements, it is copied so
// that it does. For example if it's 32 tiles wide, those 32 built tiles are copied
// 4 times to fill 128 elements.
#define PRELINESIZE 272
byte bgline[4][2][272]; //4BGs*2Prios*263 Max pixels
byte mustcopybgline[4][2]; // Flags indicating whether bgline[] contains data
// [x][8] = screen pixel 0
byte sprline[4][272]; // 4 Priorities
byte mustcopysprline[4];
byte spr8x8size [128]; // Set to 0 when cache entry is invalidated.
struct bginfo bg[4], *curbg;
union cachetile *translatetile (int taddress, byte cdepth, int flip)
{
byte *tile = (byte*) &(ctile[flip][taddress]), *o_tile = tile;
byte *addr = vram + (taddress << 4), *o_addr = addr, temp;
signed char x, y, incaddr;
boolean issolid = true, vflip = flip >> 1;
byte *solidflags = &csolid[vflip][taddress], *emptyflags = &cempty[vflip][taddress];
taddress &= 4095;
if (flip & 2) {
incaddr = -2;
addr += 14;
} else
incaddr = 2;
//debug0 (" Caching tile; cstatus=%X cdepth=%d address=%d taddress=%d", cstatus[taddress], cdepth, taddress << 4, taddress);
*solidflags = 0;
*emptyflags = 0;
switch (cdepth) {
case 0:
for (y = 0; y < 8; y++, tile += 8, addr += incaddr) {
x = 7;
do {
tile[7-x] = ((addr[0] & (1<<x)) ?1:0) | (addr[1] & (1<<x) ?2:0);
x--;
} while (x >= 0);
if ((addr[0] | addr[1]) == 0xFF) *solidflags |= (1 << y);
else if ((addr[0] | addr[1]) == 0) *emptyflags |= (1 << y);
}
break;
case 1:
for (y = 0; y < 8; y++, tile += 8, addr += incaddr) {
x = 7;
do {
tile[7-x] = (addr[0] & (1<<x) ?1:0) | (addr[1] & (1<<x) ?2:0) | (addr[16] & (1<<x) ?4:0) | (addr[17] & (1<<x) ?8:0);
x--;
} while (x >= 0);
temp = addr[0] | addr[1] | addr[16] | addr[17];
if (temp == 0xFF) *solidflags |= (1 << y);
else if (temp == 0) *emptyflags |= (1 << y);
}
break;
case 3:
for (y = 0; y < 8; y++, tile += 8, addr += incaddr) {
x = 7;
do {
tile[7-x] = (addr[0] & (1<<x) ?1:0) | (addr[1] & (1<<x) ?2:0) | (addr[16] & (1<<x) ?4:0) | (addr[17] & (1<<x) ?8:0) \
| (addr[32]& (1<<x)?16:0) | (addr[33]& (1<<x)?32:0) | (addr[48] & (1<<x)?64:0) | (addr[49] &(1<<x)?128:0);
x--;
} while (x >= 0);
temp = addr[0] | addr[1] | addr[16] | addr[17] | addr[32] | addr[33] | addr[48] | addr[49];
if (temp == 0xFF) *solidflags |= (1 << y);
else if (temp == 0) *emptyflags |= (1 << y);
}
break;
}
cstatus [taddress] = cdepth | (cstatus [taddress] & 0xF0) | (16 << flip);
tile = o_tile;
if (flip & 1) {
for (y = 0; y < 8; y++) {
temp = tile[7]; tile[7] = tile[0]; tile[0] = temp;
temp = tile[6]; tile[6] = tile[1]; tile[1] = temp;
temp = tile[5]; tile[5] = tile[2]; tile[2] = temp;
temp = tile[4]; tile[4] = tile[3]; tile[3] = temp;
tile += 8;
}
}
/*
if ((taddress<<4) >= 0xC000 && (taddress<<4) < 0xC200) { // second row first ball
for (y=0;y<8;y++) {
debug0 (" %d %02X %02X %02X %02X %02X %02X %02X %02X P0:%04X", y,
ctile[taddress].pixel[y*8], ctile[taddress].pixel[y*8+1],
ctile[taddress].pixel[y*8+2], ctile[taddress].pixel[y*8+3],
ctile[taddress].pixel[y*8+4], ctile[taddress].pixel[y*8+5],
ctile[taddress].pixel[y*8+6], ctile[taddress].pixel[y*8+7],
*((word*)&o_addr[y*2]));
}
}
*/
return &ctile [flip][taddress];
}
inline union cachetile *getcachetile (int taddress, byte cdepth, int flip)
{ // COLOR DEPTH: 0=2 bit 1=4 bit 3=8 bit
if ((cstatus[taddress] & (16 << flip)) && (cstatus[taddress] & 0x3) == cdepth) {
return &ctile [flip][taddress];
} else {
return translatetile (taddress, cdepth, flip);
}
}
byte *screen, scanlines;
// screen points to current scanline; scanlines = 224 or 239.
struct snesscreenmode {
int bgs, onmask;
byte cdepth[4];
} snesmode [8] = {
{ 4, 0x1F, 0, 0, 0, 0 },
{ 3, 0x17, 1, 1, 0 },
{ 2, 0x13, 1, 1 },
{ 2, 0x13, 3, 1 },
{ 2, 0x13, 3, 0 },
{ 2, 0x13, 1, 0 },
{ 1, 0x11, 1 },
{ 1, 0x11, 3 }
};
void resetscreenframe ()
{ // To be called at scanline 0...
int x;
//debug0 ("spriteline %P/%P/%P/%P", sprline[0], sprline[1], sprline[2], sprline[3]);
screen = vs + 256*8;
for (x = 0; x < 4; x++) {
getbgframeinfo (x);
bg[x].lastbgrow = -1;
}
for (x = 0; x < 256; x++) {
setsnespalette (x, ((word*)cgram) [x], (*REG2100 & 0xF));
}
if ((*REG212C | *REG212D | curgs->bgs) & 0x10) {
buildspritetable ();
oamposchange = true;
}
}
//--------------------------------------------------------------Info updating funcs
void getbgframeinfo (int plane)
{
bg[plane].bgnum = plane + 1;
}
void getbglineinfo (int plane)
{
bg[plane].cdepth = snesmode[*REG2105 & 7].cdepth[plane];
bg[plane].tcharshift = (bg[plane].cdepth == 3 ? 2 : bg[plane].cdepth);
if (*REG2105 & (16 << plane)) { // 16x16 tiles
bg[plane].tile16x16 = true;
} else {
bg[plane].tile16x16 = false;
}
if (*REG2107(plane) & 2) { // SC Height
bg[plane].height64 = true;
//bg[plane].pixheight = (512 << bg[plane].tile16x16);
//bg[plane].pixheightand = (bg[plane].tile16x16 ? 0x3FF : 0x1FF);
bg[plane].height8x8 = (64 << bg[plane].tile16x16);
} else {
bg[plane].height64 = false;
//bg[plane].pixheight = (256 << bg[plane].tile16x16);
//bg[plane].pixheightand = (bg[plane].tile16x16 ? 0x1FF : 0xFF);
bg[plane].height8x8 = (32 << bg[plane].tile16x16);
}
if (*(REG2107(plane)) & 1) { // SC Width
bg[plane].width64 = true;
//bg[plane].pixwidth = (512 << bg[plane].tile16x16);
//bg[plane].pixwidthand = (bg[plane].tile16x16 ? 0x3FF : 0x1FF);
//bg[plane].pixwidthshift = (9 + bg[plane].tile16x16);
bg[plane].width8x8 = (64 << bg[plane].tile16x16);
} else {
bg[plane].width64 = false;
//bg[plane].pixwidth = (256 << bg[plane].tile16x16);
//bg[plane].pixwidthand = (bg[plane].tile16x16 ? 0x1FF : 0xFF);
//bg[plane].pixwidthshift = (8 + bg[plane].tile16x16);
bg[plane].width8x8 = (32 << bg[plane].tile16x16);
}
bg[plane].width8x8and = bg[plane].width8x8 - 1;
bg[plane].height8x8and = bg[plane].height8x8 - 1;
bg[plane].tilemap = vram + ((*REG2107(plane) & 0x7C) << 9); // Correct in demos
bg[plane].tcharbase = ((*REG210B >> (plane << 2)) & 7) << 9; // Correct in demos
bg[plane].scrollx = (state.scrollreg[plane << 1] & 0x7FF);
bg[plane].scrolly = ((state.scrollreg[(plane<<1)+1] + 1) & 0x7FF);// & bg[plane].pixelheightmask);
if ((*REG2105 & 7) == 0) // Mode 0
bg[plane].cgoffs = (plane << 5);
else
bg[plane].cgoffs = 0;
}
//--------------------------------------------------BG scanline generators/blitters
int getwindowregion (byte fourflags, byte masklogic, int *start, int *end)
{ // Returns: -1 if NONE of the scanline is visible because of windowing
// 0 if all the scanline is visible
// Otherwise, # of regions
byte n = 0;
byte l1 = *REG2126, r1 = *REG2127, l2 = *REG2128, r2 = *REG2129, temp;
if (!(fourflags & 0xA))
return 0;
if (masklogic == 3) {
masklogic = 2;
fourflags ^= 5;
}
if ((fourflags & 0xA) == 8) // Enable Window 2 only, simulate window 1
fourflags >>= 2;
if ((fourflags & 0xA) == 2 || masklogic == 0) { // Window 1 enabled only or OR logic
// debug0 ("%d: n%d %d-%d %d-%d", screen_scanline, n, l1, r1, l2, r2);
if (fourflags & 1) { // Clip outside
if (l1 > r1) {
start[0] = 0;
end[0] = 255;
return 1;
}
if (l1 != 0) {
start[n] = 0;
end[n] = l1 - 1;
n++;
}
if (r1 != 255) {
start [n] = r1 + 1;
end[n] = 255;
n++;
}
// debug0 ("n%d start[0]%d end[0]%d start[1]%d end[1]%d", n, start[0], end[0], start[1], end[1]);
} else { // Clip inside
start [n] = l1;
end [n] = r1;
n++;
}
if (fourflags & 8) { // Two windows, OR logic
if (fourflags & 4) { // Clip outside
if (l2 > r2) {
start[0] = 0;
end[0] = 255;
return 1;
}
if (l2 != 0) {
start[n] = 0;
end[n] = l2 - 1;
n++;
}
if (r2 != 255) {
start [n] = r2 + 1;
end[n] = 255;
n++;
}
} else { // Clip inside
if (l2 == 0 && r2 == 255)
n = 0;
start [n] = l2;
end [n] = r2;
n++;
}
}
} else { // Both windows, apply logic also
if (l2 < l1) {
temp = l1; l1 = l2; l2 = temp;
temp = r1; r1 = r2; r2 = temp;
} // Now window 1 always starts before window 2
if (masklogic == 1) { // AND logic
} else { // XOR logic
}
}
return n;
}
void generatebgscanline (int plane, boolean windowsenabled)
{
int row, offsy, x, y, mosaic;
boolean ismosaic;
byte windowsetup, masklogic, nwin = 0;
int start[4], end[4];
if (curgs->bgs & (1 << plane)) {
curbg = &bg[plane];
ismosaic = (*REG2106 & (1 << plane)) && (*REG2106 & 0xF0) != 0;
if (ismosaic) {
mosaic = ((*REG2106 >> 4) + 1);
curbg->scrolly = curbg->scrolly / mosaic * mosaic;
}
row = ((curbg->scrolly >> 3) & curbg->height8x8and);
offsy = curbg->scrolly & 7;
if (row != curbg->lastbgrow) {
updatetilemaprow (row); // uses curbg
curbg->lastbgrow = row;
}
if (windowsenabled) {
windowsetup = (*(word*)REG2123 >> (plane << 2)) & 0xF;
masklogic = (*REG212A >> (plane << 1)) & 0x3;
if ((nwin = getwindowregion (windowsetup, masklogic, start, end)) == -1)
return;
if (screen_scanline == 16) {
// debug0 ("plane %d sc%d nwin%d %d-%d", screen_scanline, nwin, start[0], end[0]);
}
}
blitbgline (row, offsy, curbg->scrollx >> 3, bgline [plane][0] + 8 - (curbg->scrollx & 7), mustcopybgline[plane]);
if ((*REG2105&7) == 7) {
blitmode7line ();
}
if (!mustcopybgline[plane][0] && !mustcopybgline[plane][1])
return;
if (ismosaic) {
for (x = 8; x < 264; x += mosaic) {
for (y = mosaic - 1; y > 0; y--) {
bgline [plane][0][x+y] = bgline [plane][0][x];
bgline [plane][1][x+y] = bgline [plane][1][x];
}
}
}
if (windowsenabled) {
for (y = 0; y < nwin; y++) {
for (x = start[y]; x <= end[y]; x++) {
bgline [plane][0][8+x] = bgline [plane][1][8+x] = 0;
}
}
}
}
}
#ifndef USE_C_SCREEN_CODE
extern void blitbgline_frontend (struct cachetilemap *ctilemap, int offsy, int col, byte *scrstart, byte *mustcopy);
#pragma aux blitbgline_frontend = \
"push edx"\
"mov ecx, [edx]"\
"call blitbglineasm"\
"pop edx"\
"nop"\
"mov [edx], cx"\
parm [esi] [ebx] [eax] [edi] [edx]\
modify [eax ebx ecx edx esi edi];
#endif
void blitbgline (int row, int offsy, int col, byte *scrstart, byte *mustcopy)
{
struct cachetilemap *ctilemap = &ctmap[curbg->bgnum-1][row][col];
#ifdef USE_C_SCREEN_CODE
int x, flagand = (1 << offsy); //pixoffsy = (offsy << 3);
dword dwpal_or;
offsy <<= 1;
for (x = 33; x != 0; scrstart += 8, ctilemap++, col++, x--) {
if (col & 0xFFFFFF80) { // 0 <= Column < 128
col -= 128;
ctilemap -= 128;
}
if (ctilemap->empty & flagand) {
((dword*)(scrstart + PRELINESIZE))[0] = ((dword*)scrstart)[0] = 0;
((dword*)(scrstart + PRELINESIZE))[1] = ((dword*)scrstart)[1] = 0;
} else {
if (ctilemap->prio) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -