5940 lines
298 KiB
C#
5940 lines
298 KiB
C#
namespace GarboDev
|
|
{
|
|
partial class Renderer
|
|
{
|
|
#region Sprite Drawing
|
|
private void DrawSpritesNormal(int priority)
|
|
{
|
|
byte[] palette = this.memory.PaletteRam;
|
|
byte[] vram = this.memory.VideoRam;
|
|
|
|
// OBJ must be enabled in this.dispCnt
|
|
if ((this.dispCnt & (1 << 12)) == 0) return;
|
|
|
|
byte blendMaskType = (byte)(1 << 4);
|
|
|
|
for (int oamNum = 127; oamNum >= 0; oamNum--)
|
|
{
|
|
ushort attr2 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 4);
|
|
|
|
if (((attr2 >> 10) & 3) != priority) continue;
|
|
|
|
ushort attr0 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 0);
|
|
ushort attr1 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 2);
|
|
|
|
int x = attr1 & 0x1FF;
|
|
int y = attr0 & 0xFF;
|
|
|
|
bool semiTransparent = false;
|
|
|
|
switch ((attr0 >> 10) & 3)
|
|
{
|
|
case 1:
|
|
// Semi-transparent
|
|
semiTransparent = true;
|
|
break;
|
|
case 2:
|
|
// Obj window
|
|
continue;
|
|
case 3:
|
|
continue;
|
|
}
|
|
|
|
if ((this.dispCnt & 0x7) >= 3 && (attr2 & 0x3FF) < 0x200) continue;
|
|
|
|
int width = -1, height = -1;
|
|
switch ((attr0 >> 14) & 3)
|
|
{
|
|
case 0:
|
|
// Square
|
|
switch ((attr1 >> 14) & 3)
|
|
{
|
|
case 0: width = 8; height = 8; break;
|
|
case 1: width = 16; height = 16; break;
|
|
case 2: width = 32; height = 32; break;
|
|
case 3: width = 64; height = 64; break;
|
|
}
|
|
break;
|
|
case 1:
|
|
// Horizontal Rectangle
|
|
switch ((attr1 >> 14) & 3)
|
|
{
|
|
case 0: width = 16; height = 8; break;
|
|
case 1: width = 32; height = 8; break;
|
|
case 2: width = 32; height = 16; break;
|
|
case 3: width = 64; height = 32; break;
|
|
}
|
|
break;
|
|
case 2:
|
|
// Vertical Rectangle
|
|
switch ((attr1 >> 14) & 3)
|
|
{
|
|
case 0: width = 8; height = 16; break;
|
|
case 1: width = 8; height = 32; break;
|
|
case 2: width = 16; height = 32; break;
|
|
case 3: width = 32; height = 64; break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Check double size flag here
|
|
|
|
int rwidth = width, rheight = height;
|
|
if ((attr0 & (1 << 8)) != 0)
|
|
{
|
|
// Rot-scale on
|
|
if ((attr0 & (1 << 9)) != 0)
|
|
{
|
|
rwidth *= 2;
|
|
rheight *= 2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Invalid sprite
|
|
if ((attr0 & (1 << 9)) != 0)
|
|
width = -1;
|
|
}
|
|
|
|
if (width == -1)
|
|
{
|
|
// Invalid sprite
|
|
continue;
|
|
}
|
|
|
|
// Y clipping
|
|
if (y > ((y + rheight) & 0xff))
|
|
{
|
|
if (this.curLine >= ((y + rheight) & 0xff) && !(y < this.curLine)) continue;
|
|
}
|
|
else
|
|
{
|
|
if (this.curLine < y || this.curLine >= ((y + rheight) & 0xff)) continue;
|
|
}
|
|
|
|
int scale = 1;
|
|
if ((attr0 & (1 << 13)) != 0) scale = 2;
|
|
|
|
int spritey = this.curLine - y;
|
|
if (spritey < 0) spritey += 256;
|
|
|
|
if (semiTransparent)
|
|
{
|
|
if ((attr0 & (1 << 8)) == 0)
|
|
{
|
|
if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey;
|
|
|
|
int baseSprite;
|
|
if ((this.dispCnt & (1 << 6)) != 0)
|
|
{
|
|
// 1 dimensional
|
|
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale;
|
|
}
|
|
else
|
|
{
|
|
// 2 dimensional
|
|
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20);
|
|
}
|
|
|
|
int baseInc = scale;
|
|
if ((attr1 & (1 << 12)) != 0)
|
|
{
|
|
baseSprite += ((width / 8) * scale) - scale;
|
|
baseInc = -baseInc;
|
|
}
|
|
|
|
if ((attr0 & (1 << 13)) != 0)
|
|
{
|
|
// 256 colors
|
|
for (int i = x; i < x + width; i++)
|
|
{
|
|
if ((i & 0x1ff) < 240 && true)
|
|
{
|
|
int tx = (i - x) & 7;
|
|
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
|
int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx;
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8)));
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 colors
|
|
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
|
for (int i = x; i < x + width; i++)
|
|
{
|
|
if ((i & 0x1ff) < 240 && true)
|
|
{
|
|
int tx = (i - x) & 7;
|
|
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
|
int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if ((tx & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8)));
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int rotScaleParam = (attr1 >> 9) & 0x1F;
|
|
|
|
short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6);
|
|
short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE);
|
|
short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16);
|
|
short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E);
|
|
|
|
int cx = rwidth / 2;
|
|
int cy = rheight / 2;
|
|
|
|
int baseSprite = attr2 & 0x3FF;
|
|
int pitch;
|
|
|
|
if ((this.dispCnt & (1 << 6)) != 0)
|
|
{
|
|
// 1 dimensional
|
|
pitch = (width / 8) * scale;
|
|
}
|
|
else
|
|
{
|
|
// 2 dimensional
|
|
pitch = 0x20;
|
|
}
|
|
|
|
int rx = (int)((dmx * (spritey - cy)) - (cx * dx) + (width << 7));
|
|
int ry = (int)((dmy * (spritey - cy)) - (cx * dy) + (height << 7));
|
|
|
|
// Draw a rot/scale sprite
|
|
if ((attr0 & (1 << 13)) != 0)
|
|
{
|
|
// 256 colors
|
|
for (int i = x; i < x + rwidth; i++)
|
|
{
|
|
int tx = rx >> 8;
|
|
int ty = ry >> 8;
|
|
|
|
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height
|
|
&& true)
|
|
{
|
|
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8)));
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
|
|
rx += dx;
|
|
ry += dy;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 colors
|
|
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
|
for (int i = x; i < x + rwidth; i++)
|
|
{
|
|
int tx = rx >> 8;
|
|
int ty = ry >> 8;
|
|
|
|
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height
|
|
&& true)
|
|
{
|
|
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if ((tx & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8)));
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
|
|
rx += dx;
|
|
ry += dy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((attr0 & (1 << 8)) == 0)
|
|
{
|
|
if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey;
|
|
|
|
int baseSprite;
|
|
if ((this.dispCnt & (1 << 6)) != 0)
|
|
{
|
|
// 1 dimensional
|
|
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale;
|
|
}
|
|
else
|
|
{
|
|
// 2 dimensional
|
|
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20);
|
|
}
|
|
|
|
int baseInc = scale;
|
|
if ((attr1 & (1 << 12)) != 0)
|
|
{
|
|
baseSprite += ((width / 8) * scale) - scale;
|
|
baseInc = -baseInc;
|
|
}
|
|
|
|
if ((attr0 & (1 << 13)) != 0)
|
|
{
|
|
// 256 colors
|
|
for (int i = x; i < x + width; i++)
|
|
{
|
|
if ((i & 0x1ff) < 240 && true)
|
|
{
|
|
int tx = (i - x) & 7;
|
|
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
|
int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx;
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8)));
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 colors
|
|
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
|
for (int i = x; i < x + width; i++)
|
|
{
|
|
if ((i & 0x1ff) < 240 && true)
|
|
{
|
|
int tx = (i - x) & 7;
|
|
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
|
int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if ((tx & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8)));
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int rotScaleParam = (attr1 >> 9) & 0x1F;
|
|
|
|
short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6);
|
|
short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE);
|
|
short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16);
|
|
short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E);
|
|
|
|
int cx = rwidth / 2;
|
|
int cy = rheight / 2;
|
|
|
|
int baseSprite = attr2 & 0x3FF;
|
|
int pitch;
|
|
|
|
if ((this.dispCnt & (1 << 6)) != 0)
|
|
{
|
|
// 1 dimensional
|
|
pitch = (width / 8) * scale;
|
|
}
|
|
else
|
|
{
|
|
// 2 dimensional
|
|
pitch = 0x20;
|
|
}
|
|
|
|
int rx = (int)((dmx * (spritey - cy)) - (cx * dx) + (width << 7));
|
|
int ry = (int)((dmy * (spritey - cy)) - (cx * dy) + (height << 7));
|
|
|
|
// Draw a rot/scale sprite
|
|
if ((attr0 & (1 << 13)) != 0)
|
|
{
|
|
// 256 colors
|
|
for (int i = x; i < x + rwidth; i++)
|
|
{
|
|
int tx = rx >> 8;
|
|
int ty = ry >> 8;
|
|
|
|
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height
|
|
&& true)
|
|
{
|
|
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8)));
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
|
|
rx += dx;
|
|
ry += dy;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 colors
|
|
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
|
for (int i = x; i < x + rwidth; i++)
|
|
{
|
|
int tx = rx >> 8;
|
|
int ty = ry >> 8;
|
|
|
|
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height
|
|
&& true)
|
|
{
|
|
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if ((tx & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8)));
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
|
|
rx += dx;
|
|
ry += dy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
private void DrawSpritesBlend(int priority)
|
|
{
|
|
byte[] palette = this.memory.PaletteRam;
|
|
byte[] vram = this.memory.VideoRam;
|
|
|
|
// OBJ must be enabled in this.dispCnt
|
|
if ((this.dispCnt & (1 << 12)) == 0) return;
|
|
|
|
byte blendMaskType = (byte)(1 << 4);
|
|
|
|
for (int oamNum = 127; oamNum >= 0; oamNum--)
|
|
{
|
|
ushort attr2 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 4);
|
|
|
|
if (((attr2 >> 10) & 3) != priority) continue;
|
|
|
|
ushort attr0 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 0);
|
|
ushort attr1 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 2);
|
|
|
|
int x = attr1 & 0x1FF;
|
|
int y = attr0 & 0xFF;
|
|
|
|
bool semiTransparent = false;
|
|
|
|
switch ((attr0 >> 10) & 3)
|
|
{
|
|
case 1:
|
|
// Semi-transparent
|
|
semiTransparent = true;
|
|
break;
|
|
case 2:
|
|
// Obj window
|
|
continue;
|
|
case 3:
|
|
continue;
|
|
}
|
|
|
|
if ((this.dispCnt & 0x7) >= 3 && (attr2 & 0x3FF) < 0x200) continue;
|
|
|
|
int width = -1, height = -1;
|
|
switch ((attr0 >> 14) & 3)
|
|
{
|
|
case 0:
|
|
// Square
|
|
switch ((attr1 >> 14) & 3)
|
|
{
|
|
case 0: width = 8; height = 8; break;
|
|
case 1: width = 16; height = 16; break;
|
|
case 2: width = 32; height = 32; break;
|
|
case 3: width = 64; height = 64; break;
|
|
}
|
|
break;
|
|
case 1:
|
|
// Horizontal Rectangle
|
|
switch ((attr1 >> 14) & 3)
|
|
{
|
|
case 0: width = 16; height = 8; break;
|
|
case 1: width = 32; height = 8; break;
|
|
case 2: width = 32; height = 16; break;
|
|
case 3: width = 64; height = 32; break;
|
|
}
|
|
break;
|
|
case 2:
|
|
// Vertical Rectangle
|
|
switch ((attr1 >> 14) & 3)
|
|
{
|
|
case 0: width = 8; height = 16; break;
|
|
case 1: width = 8; height = 32; break;
|
|
case 2: width = 16; height = 32; break;
|
|
case 3: width = 32; height = 64; break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Check double size flag here
|
|
|
|
int rwidth = width, rheight = height;
|
|
if ((attr0 & (1 << 8)) != 0)
|
|
{
|
|
// Rot-scale on
|
|
if ((attr0 & (1 << 9)) != 0)
|
|
{
|
|
rwidth *= 2;
|
|
rheight *= 2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Invalid sprite
|
|
if ((attr0 & (1 << 9)) != 0)
|
|
width = -1;
|
|
}
|
|
|
|
if (width == -1)
|
|
{
|
|
// Invalid sprite
|
|
continue;
|
|
}
|
|
|
|
// Y clipping
|
|
if (y > ((y + rheight) & 0xff))
|
|
{
|
|
if (this.curLine >= ((y + rheight) & 0xff) && !(y < this.curLine)) continue;
|
|
}
|
|
else
|
|
{
|
|
if (this.curLine < y || this.curLine >= ((y + rheight) & 0xff)) continue;
|
|
}
|
|
|
|
int scale = 1;
|
|
if ((attr0 & (1 << 13)) != 0) scale = 2;
|
|
|
|
int spritey = this.curLine - y;
|
|
if (spritey < 0) spritey += 256;
|
|
|
|
if (semiTransparent)
|
|
{
|
|
if ((attr0 & (1 << 8)) == 0)
|
|
{
|
|
if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey;
|
|
|
|
int baseSprite;
|
|
if ((this.dispCnt & (1 << 6)) != 0)
|
|
{
|
|
// 1 dimensional
|
|
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale;
|
|
}
|
|
else
|
|
{
|
|
// 2 dimensional
|
|
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20);
|
|
}
|
|
|
|
int baseInc = scale;
|
|
if ((attr1 & (1 << 12)) != 0)
|
|
{
|
|
baseSprite += ((width / 8) * scale) - scale;
|
|
baseInc = -baseInc;
|
|
}
|
|
|
|
if ((attr0 & (1 << 13)) != 0)
|
|
{
|
|
// 256 colors
|
|
for (int i = x; i < x + width; i++)
|
|
{
|
|
if ((i & 0x1ff) < 240 && true)
|
|
{
|
|
int tx = (i - x) & 7;
|
|
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
|
int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx;
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8)));
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 colors
|
|
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
|
for (int i = x; i < x + width; i++)
|
|
{
|
|
if ((i & 0x1ff) < 240 && true)
|
|
{
|
|
int tx = (i - x) & 7;
|
|
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
|
int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if ((tx & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8)));
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int rotScaleParam = (attr1 >> 9) & 0x1F;
|
|
|
|
short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6);
|
|
short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE);
|
|
short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16);
|
|
short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E);
|
|
|
|
int cx = rwidth / 2;
|
|
int cy = rheight / 2;
|
|
|
|
int baseSprite = attr2 & 0x3FF;
|
|
int pitch;
|
|
|
|
if ((this.dispCnt & (1 << 6)) != 0)
|
|
{
|
|
// 1 dimensional
|
|
pitch = (width / 8) * scale;
|
|
}
|
|
else
|
|
{
|
|
// 2 dimensional
|
|
pitch = 0x20;
|
|
}
|
|
|
|
int rx = (int)((dmx * (spritey - cy)) - (cx * dx) + (width << 7));
|
|
int ry = (int)((dmy * (spritey - cy)) - (cx * dy) + (height << 7));
|
|
|
|
// Draw a rot/scale sprite
|
|
if ((attr0 & (1 << 13)) != 0)
|
|
{
|
|
// 256 colors
|
|
for (int i = x; i < x + rwidth; i++)
|
|
{
|
|
int tx = rx >> 8;
|
|
int ty = ry >> 8;
|
|
|
|
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height
|
|
&& true)
|
|
{
|
|
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8)));
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
|
|
rx += dx;
|
|
ry += dy;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 colors
|
|
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
|
for (int i = x; i < x + rwidth; i++)
|
|
{
|
|
int tx = rx >> 8;
|
|
int ty = ry >> 8;
|
|
|
|
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height
|
|
&& true)
|
|
{
|
|
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if ((tx & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8)));
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
|
|
rx += dx;
|
|
ry += dy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((attr0 & (1 << 8)) == 0)
|
|
{
|
|
if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey;
|
|
|
|
int baseSprite;
|
|
if ((this.dispCnt & (1 << 6)) != 0)
|
|
{
|
|
// 1 dimensional
|
|
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale;
|
|
}
|
|
else
|
|
{
|
|
// 2 dimensional
|
|
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20);
|
|
}
|
|
|
|
int baseInc = scale;
|
|
if ((attr1 & (1 << 12)) != 0)
|
|
{
|
|
baseSprite += ((width / 8) * scale) - scale;
|
|
baseInc = -baseInc;
|
|
}
|
|
|
|
if ((attr0 & (1 << 13)) != 0)
|
|
{
|
|
// 256 colors
|
|
for (int i = x; i < x + width; i++)
|
|
{
|
|
if ((i & 0x1ff) < 240 && true)
|
|
{
|
|
int tx = (i - x) & 7;
|
|
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
|
int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx;
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8)));
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 colors
|
|
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
|
for (int i = x; i < x + width; i++)
|
|
{
|
|
if ((i & 0x1ff) < 240 && true)
|
|
{
|
|
int tx = (i - x) & 7;
|
|
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
|
int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if ((tx & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8)));
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int rotScaleParam = (attr1 >> 9) & 0x1F;
|
|
|
|
short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6);
|
|
short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE);
|
|
short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16);
|
|
short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E);
|
|
|
|
int cx = rwidth / 2;
|
|
int cy = rheight / 2;
|
|
|
|
int baseSprite = attr2 & 0x3FF;
|
|
int pitch;
|
|
|
|
if ((this.dispCnt & (1 << 6)) != 0)
|
|
{
|
|
// 1 dimensional
|
|
pitch = (width / 8) * scale;
|
|
}
|
|
else
|
|
{
|
|
// 2 dimensional
|
|
pitch = 0x20;
|
|
}
|
|
|
|
int rx = (int)((dmx * (spritey - cy)) - (cx * dx) + (width << 7));
|
|
int ry = (int)((dmy * (spritey - cy)) - (cx * dy) + (height << 7));
|
|
|
|
// Draw a rot/scale sprite
|
|
if ((attr0 & (1 << 13)) != 0)
|
|
{
|
|
// 256 colors
|
|
for (int i = x; i < x + rwidth; i++)
|
|
{
|
|
int tx = rx >> 8;
|
|
int ty = ry >> 8;
|
|
|
|
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height
|
|
&& true)
|
|
{
|
|
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8)));
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
|
|
rx += dx;
|
|
ry += dy;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 colors
|
|
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
|
for (int i = x; i < x + rwidth; i++)
|
|
{
|
|
int tx = rx >> 8;
|
|
int ty = ry >> 8;
|
|
|
|
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height
|
|
&& true)
|
|
{
|
|
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if ((tx & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8)));
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
|
|
rx += dx;
|
|
ry += dy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
private void DrawSpritesBrightInc(int priority)
|
|
{
|
|
byte[] palette = this.memory.PaletteRam;
|
|
byte[] vram = this.memory.VideoRam;
|
|
|
|
// OBJ must be enabled in this.dispCnt
|
|
if ((this.dispCnt & (1 << 12)) == 0) return;
|
|
|
|
byte blendMaskType = (byte)(1 << 4);
|
|
|
|
for (int oamNum = 127; oamNum >= 0; oamNum--)
|
|
{
|
|
ushort attr2 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 4);
|
|
|
|
if (((attr2 >> 10) & 3) != priority) continue;
|
|
|
|
ushort attr0 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 0);
|
|
ushort attr1 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 2);
|
|
|
|
int x = attr1 & 0x1FF;
|
|
int y = attr0 & 0xFF;
|
|
|
|
bool semiTransparent = false;
|
|
|
|
switch ((attr0 >> 10) & 3)
|
|
{
|
|
case 1:
|
|
// Semi-transparent
|
|
semiTransparent = true;
|
|
break;
|
|
case 2:
|
|
// Obj window
|
|
continue;
|
|
case 3:
|
|
continue;
|
|
}
|
|
|
|
if ((this.dispCnt & 0x7) >= 3 && (attr2 & 0x3FF) < 0x200) continue;
|
|
|
|
int width = -1, height = -1;
|
|
switch ((attr0 >> 14) & 3)
|
|
{
|
|
case 0:
|
|
// Square
|
|
switch ((attr1 >> 14) & 3)
|
|
{
|
|
case 0: width = 8; height = 8; break;
|
|
case 1: width = 16; height = 16; break;
|
|
case 2: width = 32; height = 32; break;
|
|
case 3: width = 64; height = 64; break;
|
|
}
|
|
break;
|
|
case 1:
|
|
// Horizontal Rectangle
|
|
switch ((attr1 >> 14) & 3)
|
|
{
|
|
case 0: width = 16; height = 8; break;
|
|
case 1: width = 32; height = 8; break;
|
|
case 2: width = 32; height = 16; break;
|
|
case 3: width = 64; height = 32; break;
|
|
}
|
|
break;
|
|
case 2:
|
|
// Vertical Rectangle
|
|
switch ((attr1 >> 14) & 3)
|
|
{
|
|
case 0: width = 8; height = 16; break;
|
|
case 1: width = 8; height = 32; break;
|
|
case 2: width = 16; height = 32; break;
|
|
case 3: width = 32; height = 64; break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Check double size flag here
|
|
|
|
int rwidth = width, rheight = height;
|
|
if ((attr0 & (1 << 8)) != 0)
|
|
{
|
|
// Rot-scale on
|
|
if ((attr0 & (1 << 9)) != 0)
|
|
{
|
|
rwidth *= 2;
|
|
rheight *= 2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Invalid sprite
|
|
if ((attr0 & (1 << 9)) != 0)
|
|
width = -1;
|
|
}
|
|
|
|
if (width == -1)
|
|
{
|
|
// Invalid sprite
|
|
continue;
|
|
}
|
|
|
|
// Y clipping
|
|
if (y > ((y + rheight) & 0xff))
|
|
{
|
|
if (this.curLine >= ((y + rheight) & 0xff) && !(y < this.curLine)) continue;
|
|
}
|
|
else
|
|
{
|
|
if (this.curLine < y || this.curLine >= ((y + rheight) & 0xff)) continue;
|
|
}
|
|
|
|
int scale = 1;
|
|
if ((attr0 & (1 << 13)) != 0) scale = 2;
|
|
|
|
int spritey = this.curLine - y;
|
|
if (spritey < 0) spritey += 256;
|
|
|
|
if (semiTransparent)
|
|
{
|
|
if ((attr0 & (1 << 8)) == 0)
|
|
{
|
|
if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey;
|
|
|
|
int baseSprite;
|
|
if ((this.dispCnt & (1 << 6)) != 0)
|
|
{
|
|
// 1 dimensional
|
|
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale;
|
|
}
|
|
else
|
|
{
|
|
// 2 dimensional
|
|
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20);
|
|
}
|
|
|
|
int baseInc = scale;
|
|
if ((attr1 & (1 << 12)) != 0)
|
|
{
|
|
baseSprite += ((width / 8) * scale) - scale;
|
|
baseInc = -baseInc;
|
|
}
|
|
|
|
if ((attr0 & (1 << 13)) != 0)
|
|
{
|
|
// 256 colors
|
|
for (int i = x; i < x + width; i++)
|
|
{
|
|
if ((i & 0x1ff) < 240 && true)
|
|
{
|
|
int tx = (i - x) & 7;
|
|
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
|
int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx;
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8)));
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 colors
|
|
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
|
for (int i = x; i < x + width; i++)
|
|
{
|
|
if ((i & 0x1ff) < 240 && true)
|
|
{
|
|
int tx = (i - x) & 7;
|
|
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
|
int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if ((tx & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8)));
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int rotScaleParam = (attr1 >> 9) & 0x1F;
|
|
|
|
short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6);
|
|
short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE);
|
|
short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16);
|
|
short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E);
|
|
|
|
int cx = rwidth / 2;
|
|
int cy = rheight / 2;
|
|
|
|
int baseSprite = attr2 & 0x3FF;
|
|
int pitch;
|
|
|
|
if ((this.dispCnt & (1 << 6)) != 0)
|
|
{
|
|
// 1 dimensional
|
|
pitch = (width / 8) * scale;
|
|
}
|
|
else
|
|
{
|
|
// 2 dimensional
|
|
pitch = 0x20;
|
|
}
|
|
|
|
int rx = (int)((dmx * (spritey - cy)) - (cx * dx) + (width << 7));
|
|
int ry = (int)((dmy * (spritey - cy)) - (cx * dy) + (height << 7));
|
|
|
|
// Draw a rot/scale sprite
|
|
if ((attr0 & (1 << 13)) != 0)
|
|
{
|
|
// 256 colors
|
|
for (int i = x; i < x + rwidth; i++)
|
|
{
|
|
int tx = rx >> 8;
|
|
int ty = ry >> 8;
|
|
|
|
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height
|
|
&& true)
|
|
{
|
|
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8)));
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
|
|
rx += dx;
|
|
ry += dy;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 colors
|
|
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
|
for (int i = x; i < x + rwidth; i++)
|
|
{
|
|
int tx = rx >> 8;
|
|
int ty = ry >> 8;
|
|
|
|
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height
|
|
&& true)
|
|
{
|
|
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if ((tx & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8)));
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
|
|
rx += dx;
|
|
ry += dy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((attr0 & (1 << 8)) == 0)
|
|
{
|
|
if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey;
|
|
|
|
int baseSprite;
|
|
if ((this.dispCnt & (1 << 6)) != 0)
|
|
{
|
|
// 1 dimensional
|
|
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale;
|
|
}
|
|
else
|
|
{
|
|
// 2 dimensional
|
|
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20);
|
|
}
|
|
|
|
int baseInc = scale;
|
|
if ((attr1 & (1 << 12)) != 0)
|
|
{
|
|
baseSprite += ((width / 8) * scale) - scale;
|
|
baseInc = -baseInc;
|
|
}
|
|
|
|
if ((attr0 & (1 << 13)) != 0)
|
|
{
|
|
// 256 colors
|
|
for (int i = x; i < x + width; i++)
|
|
{
|
|
if ((i & 0x1ff) < 240 && true)
|
|
{
|
|
int tx = (i - x) & 7;
|
|
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
|
int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx;
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8)));
|
|
uint r = pixelColor & 0xFF;
|
|
uint g = (pixelColor >> 8) & 0xFF;
|
|
uint b = (pixelColor >> 16) & 0xFF;
|
|
r = r + (((0xFF - r) * this.blendY) >> 4);
|
|
g = g + (((0xFF - g) * this.blendY) >> 4);
|
|
b = b + (((0xFF - b) * this.blendY) >> 4);
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 colors
|
|
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
|
for (int i = x; i < x + width; i++)
|
|
{
|
|
if ((i & 0x1ff) < 240 && true)
|
|
{
|
|
int tx = (i - x) & 7;
|
|
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
|
int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if ((tx & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8)));
|
|
uint r = pixelColor & 0xFF;
|
|
uint g = (pixelColor >> 8) & 0xFF;
|
|
uint b = (pixelColor >> 16) & 0xFF;
|
|
r = r + (((0xFF - r) * this.blendY) >> 4);
|
|
g = g + (((0xFF - g) * this.blendY) >> 4);
|
|
b = b + (((0xFF - b) * this.blendY) >> 4);
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int rotScaleParam = (attr1 >> 9) & 0x1F;
|
|
|
|
short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6);
|
|
short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE);
|
|
short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16);
|
|
short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E);
|
|
|
|
int cx = rwidth / 2;
|
|
int cy = rheight / 2;
|
|
|
|
int baseSprite = attr2 & 0x3FF;
|
|
int pitch;
|
|
|
|
if ((this.dispCnt & (1 << 6)) != 0)
|
|
{
|
|
// 1 dimensional
|
|
pitch = (width / 8) * scale;
|
|
}
|
|
else
|
|
{
|
|
// 2 dimensional
|
|
pitch = 0x20;
|
|
}
|
|
|
|
int rx = (int)((dmx * (spritey - cy)) - (cx * dx) + (width << 7));
|
|
int ry = (int)((dmy * (spritey - cy)) - (cx * dy) + (height << 7));
|
|
|
|
// Draw a rot/scale sprite
|
|
if ((attr0 & (1 << 13)) != 0)
|
|
{
|
|
// 256 colors
|
|
for (int i = x; i < x + rwidth; i++)
|
|
{
|
|
int tx = rx >> 8;
|
|
int ty = ry >> 8;
|
|
|
|
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height
|
|
&& true)
|
|
{
|
|
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8)));
|
|
uint r = pixelColor & 0xFF;
|
|
uint g = (pixelColor >> 8) & 0xFF;
|
|
uint b = (pixelColor >> 16) & 0xFF;
|
|
r = r + (((0xFF - r) * this.blendY) >> 4);
|
|
g = g + (((0xFF - g) * this.blendY) >> 4);
|
|
b = b + (((0xFF - b) * this.blendY) >> 4);
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
|
|
rx += dx;
|
|
ry += dy;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 colors
|
|
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
|
for (int i = x; i < x + rwidth; i++)
|
|
{
|
|
int tx = rx >> 8;
|
|
int ty = ry >> 8;
|
|
|
|
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height
|
|
&& true)
|
|
{
|
|
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if ((tx & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8)));
|
|
uint r = pixelColor & 0xFF;
|
|
uint g = (pixelColor >> 8) & 0xFF;
|
|
uint b = (pixelColor >> 16) & 0xFF;
|
|
r = r + (((0xFF - r) * this.blendY) >> 4);
|
|
g = g + (((0xFF - g) * this.blendY) >> 4);
|
|
b = b + (((0xFF - b) * this.blendY) >> 4);
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
|
|
rx += dx;
|
|
ry += dy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
private void DrawSpritesBrightDec(int priority)
|
|
{
|
|
byte[] palette = this.memory.PaletteRam;
|
|
byte[] vram = this.memory.VideoRam;
|
|
|
|
// OBJ must be enabled in this.dispCnt
|
|
if ((this.dispCnt & (1 << 12)) == 0) return;
|
|
|
|
byte blendMaskType = (byte)(1 << 4);
|
|
|
|
for (int oamNum = 127; oamNum >= 0; oamNum--)
|
|
{
|
|
ushort attr2 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 4);
|
|
|
|
if (((attr2 >> 10) & 3) != priority) continue;
|
|
|
|
ushort attr0 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 0);
|
|
ushort attr1 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 2);
|
|
|
|
int x = attr1 & 0x1FF;
|
|
int y = attr0 & 0xFF;
|
|
|
|
bool semiTransparent = false;
|
|
|
|
switch ((attr0 >> 10) & 3)
|
|
{
|
|
case 1:
|
|
// Semi-transparent
|
|
semiTransparent = true;
|
|
break;
|
|
case 2:
|
|
// Obj window
|
|
continue;
|
|
case 3:
|
|
continue;
|
|
}
|
|
|
|
if ((this.dispCnt & 0x7) >= 3 && (attr2 & 0x3FF) < 0x200) continue;
|
|
|
|
int width = -1, height = -1;
|
|
switch ((attr0 >> 14) & 3)
|
|
{
|
|
case 0:
|
|
// Square
|
|
switch ((attr1 >> 14) & 3)
|
|
{
|
|
case 0: width = 8; height = 8; break;
|
|
case 1: width = 16; height = 16; break;
|
|
case 2: width = 32; height = 32; break;
|
|
case 3: width = 64; height = 64; break;
|
|
}
|
|
break;
|
|
case 1:
|
|
// Horizontal Rectangle
|
|
switch ((attr1 >> 14) & 3)
|
|
{
|
|
case 0: width = 16; height = 8; break;
|
|
case 1: width = 32; height = 8; break;
|
|
case 2: width = 32; height = 16; break;
|
|
case 3: width = 64; height = 32; break;
|
|
}
|
|
break;
|
|
case 2:
|
|
// Vertical Rectangle
|
|
switch ((attr1 >> 14) & 3)
|
|
{
|
|
case 0: width = 8; height = 16; break;
|
|
case 1: width = 8; height = 32; break;
|
|
case 2: width = 16; height = 32; break;
|
|
case 3: width = 32; height = 64; break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Check double size flag here
|
|
|
|
int rwidth = width, rheight = height;
|
|
if ((attr0 & (1 << 8)) != 0)
|
|
{
|
|
// Rot-scale on
|
|
if ((attr0 & (1 << 9)) != 0)
|
|
{
|
|
rwidth *= 2;
|
|
rheight *= 2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Invalid sprite
|
|
if ((attr0 & (1 << 9)) != 0)
|
|
width = -1;
|
|
}
|
|
|
|
if (width == -1)
|
|
{
|
|
// Invalid sprite
|
|
continue;
|
|
}
|
|
|
|
// Y clipping
|
|
if (y > ((y + rheight) & 0xff))
|
|
{
|
|
if (this.curLine >= ((y + rheight) & 0xff) && !(y < this.curLine)) continue;
|
|
}
|
|
else
|
|
{
|
|
if (this.curLine < y || this.curLine >= ((y + rheight) & 0xff)) continue;
|
|
}
|
|
|
|
int scale = 1;
|
|
if ((attr0 & (1 << 13)) != 0) scale = 2;
|
|
|
|
int spritey = this.curLine - y;
|
|
if (spritey < 0) spritey += 256;
|
|
|
|
if (semiTransparent)
|
|
{
|
|
if ((attr0 & (1 << 8)) == 0)
|
|
{
|
|
if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey;
|
|
|
|
int baseSprite;
|
|
if ((this.dispCnt & (1 << 6)) != 0)
|
|
{
|
|
// 1 dimensional
|
|
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale;
|
|
}
|
|
else
|
|
{
|
|
// 2 dimensional
|
|
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20);
|
|
}
|
|
|
|
int baseInc = scale;
|
|
if ((attr1 & (1 << 12)) != 0)
|
|
{
|
|
baseSprite += ((width / 8) * scale) - scale;
|
|
baseInc = -baseInc;
|
|
}
|
|
|
|
if ((attr0 & (1 << 13)) != 0)
|
|
{
|
|
// 256 colors
|
|
for (int i = x; i < x + width; i++)
|
|
{
|
|
if ((i & 0x1ff) < 240 && true)
|
|
{
|
|
int tx = (i - x) & 7;
|
|
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
|
int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx;
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8)));
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 colors
|
|
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
|
for (int i = x; i < x + width; i++)
|
|
{
|
|
if ((i & 0x1ff) < 240 && true)
|
|
{
|
|
int tx = (i - x) & 7;
|
|
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
|
int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if ((tx & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8)));
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int rotScaleParam = (attr1 >> 9) & 0x1F;
|
|
|
|
short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6);
|
|
short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE);
|
|
short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16);
|
|
short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E);
|
|
|
|
int cx = rwidth / 2;
|
|
int cy = rheight / 2;
|
|
|
|
int baseSprite = attr2 & 0x3FF;
|
|
int pitch;
|
|
|
|
if ((this.dispCnt & (1 << 6)) != 0)
|
|
{
|
|
// 1 dimensional
|
|
pitch = (width / 8) * scale;
|
|
}
|
|
else
|
|
{
|
|
// 2 dimensional
|
|
pitch = 0x20;
|
|
}
|
|
|
|
int rx = (int)((dmx * (spritey - cy)) - (cx * dx) + (width << 7));
|
|
int ry = (int)((dmy * (spritey - cy)) - (cx * dy) + (height << 7));
|
|
|
|
// Draw a rot/scale sprite
|
|
if ((attr0 & (1 << 13)) != 0)
|
|
{
|
|
// 256 colors
|
|
for (int i = x; i < x + rwidth; i++)
|
|
{
|
|
int tx = rx >> 8;
|
|
int ty = ry >> 8;
|
|
|
|
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height
|
|
&& true)
|
|
{
|
|
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8)));
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
|
|
rx += dx;
|
|
ry += dy;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 colors
|
|
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
|
for (int i = x; i < x + rwidth; i++)
|
|
{
|
|
int tx = rx >> 8;
|
|
int ty = ry >> 8;
|
|
|
|
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height
|
|
&& true)
|
|
{
|
|
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if ((tx & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8)));
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
|
|
rx += dx;
|
|
ry += dy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((attr0 & (1 << 8)) == 0)
|
|
{
|
|
if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey;
|
|
|
|
int baseSprite;
|
|
if ((this.dispCnt & (1 << 6)) != 0)
|
|
{
|
|
// 1 dimensional
|
|
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale;
|
|
}
|
|
else
|
|
{
|
|
// 2 dimensional
|
|
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20);
|
|
}
|
|
|
|
int baseInc = scale;
|
|
if ((attr1 & (1 << 12)) != 0)
|
|
{
|
|
baseSprite += ((width / 8) * scale) - scale;
|
|
baseInc = -baseInc;
|
|
}
|
|
|
|
if ((attr0 & (1 << 13)) != 0)
|
|
{
|
|
// 256 colors
|
|
for (int i = x; i < x + width; i++)
|
|
{
|
|
if ((i & 0x1ff) < 240 && true)
|
|
{
|
|
int tx = (i - x) & 7;
|
|
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
|
int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx;
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8)));
|
|
uint r = pixelColor & 0xFF;
|
|
uint g = (pixelColor >> 8) & 0xFF;
|
|
uint b = (pixelColor >> 16) & 0xFF;
|
|
r = r - ((r * this.blendY) >> 4);
|
|
g = g - ((g * this.blendY) >> 4);
|
|
b = b - ((b * this.blendY) >> 4);
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 colors
|
|
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
|
for (int i = x; i < x + width; i++)
|
|
{
|
|
if ((i & 0x1ff) < 240 && true)
|
|
{
|
|
int tx = (i - x) & 7;
|
|
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
|
int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if ((tx & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8)));
|
|
uint r = pixelColor & 0xFF;
|
|
uint g = (pixelColor >> 8) & 0xFF;
|
|
uint b = (pixelColor >> 16) & 0xFF;
|
|
r = r - ((r * this.blendY) >> 4);
|
|
g = g - ((g * this.blendY) >> 4);
|
|
b = b - ((b * this.blendY) >> 4);
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int rotScaleParam = (attr1 >> 9) & 0x1F;
|
|
|
|
short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6);
|
|
short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE);
|
|
short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16);
|
|
short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E);
|
|
|
|
int cx = rwidth / 2;
|
|
int cy = rheight / 2;
|
|
|
|
int baseSprite = attr2 & 0x3FF;
|
|
int pitch;
|
|
|
|
if ((this.dispCnt & (1 << 6)) != 0)
|
|
{
|
|
// 1 dimensional
|
|
pitch = (width / 8) * scale;
|
|
}
|
|
else
|
|
{
|
|
// 2 dimensional
|
|
pitch = 0x20;
|
|
}
|
|
|
|
int rx = (int)((dmx * (spritey - cy)) - (cx * dx) + (width << 7));
|
|
int ry = (int)((dmy * (spritey - cy)) - (cx * dy) + (height << 7));
|
|
|
|
// Draw a rot/scale sprite
|
|
if ((attr0 & (1 << 13)) != 0)
|
|
{
|
|
// 256 colors
|
|
for (int i = x; i < x + rwidth; i++)
|
|
{
|
|
int tx = rx >> 8;
|
|
int ty = ry >> 8;
|
|
|
|
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height
|
|
&& true)
|
|
{
|
|
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8)));
|
|
uint r = pixelColor & 0xFF;
|
|
uint g = (pixelColor >> 8) & 0xFF;
|
|
uint b = (pixelColor >> 16) & 0xFF;
|
|
r = r - ((r * this.blendY) >> 4);
|
|
g = g - ((g * this.blendY) >> 4);
|
|
b = b - ((b * this.blendY) >> 4);
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
|
|
rx += dx;
|
|
ry += dy;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 colors
|
|
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
|
for (int i = x; i < x + rwidth; i++)
|
|
{
|
|
int tx = rx >> 8;
|
|
int ty = ry >> 8;
|
|
|
|
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height
|
|
&& true)
|
|
{
|
|
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if ((tx & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8)));
|
|
uint r = pixelColor & 0xFF;
|
|
uint g = (pixelColor >> 8) & 0xFF;
|
|
uint b = (pixelColor >> 16) & 0xFF;
|
|
r = r - ((r * this.blendY) >> 4);
|
|
g = g - ((g * this.blendY) >> 4);
|
|
b = b - ((b * this.blendY) >> 4);
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
|
|
rx += dx;
|
|
ry += dy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
private void DrawSpritesWindow(int priority)
|
|
{
|
|
byte[] palette = this.memory.PaletteRam;
|
|
byte[] vram = this.memory.VideoRam;
|
|
|
|
// OBJ must be enabled in this.dispCnt
|
|
if ((this.dispCnt & (1 << 12)) == 0) return;
|
|
|
|
byte blendMaskType = (byte)(1 << 4);
|
|
|
|
for (int oamNum = 127; oamNum >= 0; oamNum--)
|
|
{
|
|
ushort attr2 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 4);
|
|
|
|
if (((attr2 >> 10) & 3) != priority) continue;
|
|
|
|
ushort attr0 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 0);
|
|
ushort attr1 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 2);
|
|
|
|
int x = attr1 & 0x1FF;
|
|
int y = attr0 & 0xFF;
|
|
|
|
bool semiTransparent = false;
|
|
|
|
switch ((attr0 >> 10) & 3)
|
|
{
|
|
case 1:
|
|
// Semi-transparent
|
|
semiTransparent = true;
|
|
break;
|
|
case 2:
|
|
// Obj window
|
|
continue;
|
|
case 3:
|
|
continue;
|
|
}
|
|
|
|
if ((this.dispCnt & 0x7) >= 3 && (attr2 & 0x3FF) < 0x200) continue;
|
|
|
|
int width = -1, height = -1;
|
|
switch ((attr0 >> 14) & 3)
|
|
{
|
|
case 0:
|
|
// Square
|
|
switch ((attr1 >> 14) & 3)
|
|
{
|
|
case 0: width = 8; height = 8; break;
|
|
case 1: width = 16; height = 16; break;
|
|
case 2: width = 32; height = 32; break;
|
|
case 3: width = 64; height = 64; break;
|
|
}
|
|
break;
|
|
case 1:
|
|
// Horizontal Rectangle
|
|
switch ((attr1 >> 14) & 3)
|
|
{
|
|
case 0: width = 16; height = 8; break;
|
|
case 1: width = 32; height = 8; break;
|
|
case 2: width = 32; height = 16; break;
|
|
case 3: width = 64; height = 32; break;
|
|
}
|
|
break;
|
|
case 2:
|
|
// Vertical Rectangle
|
|
switch ((attr1 >> 14) & 3)
|
|
{
|
|
case 0: width = 8; height = 16; break;
|
|
case 1: width = 8; height = 32; break;
|
|
case 2: width = 16; height = 32; break;
|
|
case 3: width = 32; height = 64; break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Check double size flag here
|
|
|
|
int rwidth = width, rheight = height;
|
|
if ((attr0 & (1 << 8)) != 0)
|
|
{
|
|
// Rot-scale on
|
|
if ((attr0 & (1 << 9)) != 0)
|
|
{
|
|
rwidth *= 2;
|
|
rheight *= 2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Invalid sprite
|
|
if ((attr0 & (1 << 9)) != 0)
|
|
width = -1;
|
|
}
|
|
|
|
if (width == -1)
|
|
{
|
|
// Invalid sprite
|
|
continue;
|
|
}
|
|
|
|
// Y clipping
|
|
if (y > ((y + rheight) & 0xff))
|
|
{
|
|
if (this.curLine >= ((y + rheight) & 0xff) && !(y < this.curLine)) continue;
|
|
}
|
|
else
|
|
{
|
|
if (this.curLine < y || this.curLine >= ((y + rheight) & 0xff)) continue;
|
|
}
|
|
|
|
int scale = 1;
|
|
if ((attr0 & (1 << 13)) != 0) scale = 2;
|
|
|
|
int spritey = this.curLine - y;
|
|
if (spritey < 0) spritey += 256;
|
|
|
|
if (semiTransparent)
|
|
{
|
|
if ((attr0 & (1 << 8)) == 0)
|
|
{
|
|
if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey;
|
|
|
|
int baseSprite;
|
|
if ((this.dispCnt & (1 << 6)) != 0)
|
|
{
|
|
// 1 dimensional
|
|
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale;
|
|
}
|
|
else
|
|
{
|
|
// 2 dimensional
|
|
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20);
|
|
}
|
|
|
|
int baseInc = scale;
|
|
if ((attr1 & (1 << 12)) != 0)
|
|
{
|
|
baseSprite += ((width / 8) * scale) - scale;
|
|
baseInc = -baseInc;
|
|
}
|
|
|
|
if ((attr0 & (1 << 13)) != 0)
|
|
{
|
|
// 256 colors
|
|
for (int i = x; i < x + width; i++)
|
|
{
|
|
if ((i & 0x1ff) < 240 && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0)
|
|
{
|
|
int tx = (i - x) & 7;
|
|
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
|
int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx;
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0)
|
|
{
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 colors
|
|
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
|
for (int i = x; i < x + width; i++)
|
|
{
|
|
if ((i & 0x1ff) < 240 && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0)
|
|
{
|
|
int tx = (i - x) & 7;
|
|
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
|
int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if ((tx & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0)
|
|
{
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int rotScaleParam = (attr1 >> 9) & 0x1F;
|
|
|
|
short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6);
|
|
short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE);
|
|
short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16);
|
|
short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E);
|
|
|
|
int cx = rwidth / 2;
|
|
int cy = rheight / 2;
|
|
|
|
int baseSprite = attr2 & 0x3FF;
|
|
int pitch;
|
|
|
|
if ((this.dispCnt & (1 << 6)) != 0)
|
|
{
|
|
// 1 dimensional
|
|
pitch = (width / 8) * scale;
|
|
}
|
|
else
|
|
{
|
|
// 2 dimensional
|
|
pitch = 0x20;
|
|
}
|
|
|
|
int rx = (int)((dmx * (spritey - cy)) - (cx * dx) + (width << 7));
|
|
int ry = (int)((dmy * (spritey - cy)) - (cx * dy) + (height << 7));
|
|
|
|
// Draw a rot/scale sprite
|
|
if ((attr0 & (1 << 13)) != 0)
|
|
{
|
|
// 256 colors
|
|
for (int i = x; i < x + rwidth; i++)
|
|
{
|
|
int tx = rx >> 8;
|
|
int ty = ry >> 8;
|
|
|
|
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height
|
|
&& (this.windowCover[i & 0x1ff] & (1 << 4)) != 0)
|
|
{
|
|
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0)
|
|
{
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
|
|
rx += dx;
|
|
ry += dy;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 colors
|
|
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
|
for (int i = x; i < x + rwidth; i++)
|
|
{
|
|
int tx = rx >> 8;
|
|
int ty = ry >> 8;
|
|
|
|
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height
|
|
&& (this.windowCover[i & 0x1ff] & (1 << 4)) != 0)
|
|
{
|
|
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if ((tx & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0)
|
|
{
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
|
|
rx += dx;
|
|
ry += dy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((attr0 & (1 << 8)) == 0)
|
|
{
|
|
if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey;
|
|
|
|
int baseSprite;
|
|
if ((this.dispCnt & (1 << 6)) != 0)
|
|
{
|
|
// 1 dimensional
|
|
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale;
|
|
}
|
|
else
|
|
{
|
|
// 2 dimensional
|
|
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20);
|
|
}
|
|
|
|
int baseInc = scale;
|
|
if ((attr1 & (1 << 12)) != 0)
|
|
{
|
|
baseSprite += ((width / 8) * scale) - scale;
|
|
baseInc = -baseInc;
|
|
}
|
|
|
|
if ((attr0 & (1 << 13)) != 0)
|
|
{
|
|
// 256 colors
|
|
for (int i = x; i < x + width; i++)
|
|
{
|
|
if ((i & 0x1ff) < 240 && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0)
|
|
{
|
|
int tx = (i - x) & 7;
|
|
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
|
int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx;
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8)));
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 colors
|
|
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
|
for (int i = x; i < x + width; i++)
|
|
{
|
|
if ((i & 0x1ff) < 240 && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0)
|
|
{
|
|
int tx = (i - x) & 7;
|
|
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
|
int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if ((tx & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8)));
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int rotScaleParam = (attr1 >> 9) & 0x1F;
|
|
|
|
short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6);
|
|
short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE);
|
|
short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16);
|
|
short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E);
|
|
|
|
int cx = rwidth / 2;
|
|
int cy = rheight / 2;
|
|
|
|
int baseSprite = attr2 & 0x3FF;
|
|
int pitch;
|
|
|
|
if ((this.dispCnt & (1 << 6)) != 0)
|
|
{
|
|
// 1 dimensional
|
|
pitch = (width / 8) * scale;
|
|
}
|
|
else
|
|
{
|
|
// 2 dimensional
|
|
pitch = 0x20;
|
|
}
|
|
|
|
int rx = (int)((dmx * (spritey - cy)) - (cx * dx) + (width << 7));
|
|
int ry = (int)((dmy * (spritey - cy)) - (cx * dy) + (height << 7));
|
|
|
|
// Draw a rot/scale sprite
|
|
if ((attr0 & (1 << 13)) != 0)
|
|
{
|
|
// 256 colors
|
|
for (int i = x; i < x + rwidth; i++)
|
|
{
|
|
int tx = rx >> 8;
|
|
int ty = ry >> 8;
|
|
|
|
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height
|
|
&& (this.windowCover[i & 0x1ff] & (1 << 4)) != 0)
|
|
{
|
|
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8)));
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
|
|
rx += dx;
|
|
ry += dy;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 colors
|
|
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
|
for (int i = x; i < x + rwidth; i++)
|
|
{
|
|
int tx = rx >> 8;
|
|
int ty = ry >> 8;
|
|
|
|
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height
|
|
&& (this.windowCover[i & 0x1ff] & (1 << 4)) != 0)
|
|
{
|
|
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if ((tx & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8)));
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
|
|
rx += dx;
|
|
ry += dy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
private void DrawSpritesWindowBlend(int priority)
|
|
{
|
|
byte[] palette = this.memory.PaletteRam;
|
|
byte[] vram = this.memory.VideoRam;
|
|
|
|
// OBJ must be enabled in this.dispCnt
|
|
if ((this.dispCnt & (1 << 12)) == 0) return;
|
|
|
|
byte blendMaskType = (byte)(1 << 4);
|
|
|
|
for (int oamNum = 127; oamNum >= 0; oamNum--)
|
|
{
|
|
ushort attr2 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 4);
|
|
|
|
if (((attr2 >> 10) & 3) != priority) continue;
|
|
|
|
ushort attr0 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 0);
|
|
ushort attr1 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 2);
|
|
|
|
int x = attr1 & 0x1FF;
|
|
int y = attr0 & 0xFF;
|
|
|
|
bool semiTransparent = false;
|
|
|
|
switch ((attr0 >> 10) & 3)
|
|
{
|
|
case 1:
|
|
// Semi-transparent
|
|
semiTransparent = true;
|
|
break;
|
|
case 2:
|
|
// Obj window
|
|
continue;
|
|
case 3:
|
|
continue;
|
|
}
|
|
|
|
if ((this.dispCnt & 0x7) >= 3 && (attr2 & 0x3FF) < 0x200) continue;
|
|
|
|
int width = -1, height = -1;
|
|
switch ((attr0 >> 14) & 3)
|
|
{
|
|
case 0:
|
|
// Square
|
|
switch ((attr1 >> 14) & 3)
|
|
{
|
|
case 0: width = 8; height = 8; break;
|
|
case 1: width = 16; height = 16; break;
|
|
case 2: width = 32; height = 32; break;
|
|
case 3: width = 64; height = 64; break;
|
|
}
|
|
break;
|
|
case 1:
|
|
// Horizontal Rectangle
|
|
switch ((attr1 >> 14) & 3)
|
|
{
|
|
case 0: width = 16; height = 8; break;
|
|
case 1: width = 32; height = 8; break;
|
|
case 2: width = 32; height = 16; break;
|
|
case 3: width = 64; height = 32; break;
|
|
}
|
|
break;
|
|
case 2:
|
|
// Vertical Rectangle
|
|
switch ((attr1 >> 14) & 3)
|
|
{
|
|
case 0: width = 8; height = 16; break;
|
|
case 1: width = 8; height = 32; break;
|
|
case 2: width = 16; height = 32; break;
|
|
case 3: width = 32; height = 64; break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Check double size flag here
|
|
|
|
int rwidth = width, rheight = height;
|
|
if ((attr0 & (1 << 8)) != 0)
|
|
{
|
|
// Rot-scale on
|
|
if ((attr0 & (1 << 9)) != 0)
|
|
{
|
|
rwidth *= 2;
|
|
rheight *= 2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Invalid sprite
|
|
if ((attr0 & (1 << 9)) != 0)
|
|
width = -1;
|
|
}
|
|
|
|
if (width == -1)
|
|
{
|
|
// Invalid sprite
|
|
continue;
|
|
}
|
|
|
|
// Y clipping
|
|
if (y > ((y + rheight) & 0xff))
|
|
{
|
|
if (this.curLine >= ((y + rheight) & 0xff) && !(y < this.curLine)) continue;
|
|
}
|
|
else
|
|
{
|
|
if (this.curLine < y || this.curLine >= ((y + rheight) & 0xff)) continue;
|
|
}
|
|
|
|
int scale = 1;
|
|
if ((attr0 & (1 << 13)) != 0) scale = 2;
|
|
|
|
int spritey = this.curLine - y;
|
|
if (spritey < 0) spritey += 256;
|
|
|
|
if (semiTransparent)
|
|
{
|
|
if ((attr0 & (1 << 8)) == 0)
|
|
{
|
|
if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey;
|
|
|
|
int baseSprite;
|
|
if ((this.dispCnt & (1 << 6)) != 0)
|
|
{
|
|
// 1 dimensional
|
|
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale;
|
|
}
|
|
else
|
|
{
|
|
// 2 dimensional
|
|
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20);
|
|
}
|
|
|
|
int baseInc = scale;
|
|
if ((attr1 & (1 << 12)) != 0)
|
|
{
|
|
baseSprite += ((width / 8) * scale) - scale;
|
|
baseInc = -baseInc;
|
|
}
|
|
|
|
if ((attr0 & (1 << 13)) != 0)
|
|
{
|
|
// 256 colors
|
|
for (int i = x; i < x + width; i++)
|
|
{
|
|
if ((i & 0x1ff) < 240 && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0)
|
|
{
|
|
int tx = (i - x) & 7;
|
|
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
|
int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx;
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0)
|
|
{
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 colors
|
|
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
|
for (int i = x; i < x + width; i++)
|
|
{
|
|
if ((i & 0x1ff) < 240 && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0)
|
|
{
|
|
int tx = (i - x) & 7;
|
|
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
|
int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if ((tx & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0)
|
|
{
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int rotScaleParam = (attr1 >> 9) & 0x1F;
|
|
|
|
short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6);
|
|
short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE);
|
|
short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16);
|
|
short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E);
|
|
|
|
int cx = rwidth / 2;
|
|
int cy = rheight / 2;
|
|
|
|
int baseSprite = attr2 & 0x3FF;
|
|
int pitch;
|
|
|
|
if ((this.dispCnt & (1 << 6)) != 0)
|
|
{
|
|
// 1 dimensional
|
|
pitch = (width / 8) * scale;
|
|
}
|
|
else
|
|
{
|
|
// 2 dimensional
|
|
pitch = 0x20;
|
|
}
|
|
|
|
int rx = (int)((dmx * (spritey - cy)) - (cx * dx) + (width << 7));
|
|
int ry = (int)((dmy * (spritey - cy)) - (cx * dy) + (height << 7));
|
|
|
|
// Draw a rot/scale sprite
|
|
if ((attr0 & (1 << 13)) != 0)
|
|
{
|
|
// 256 colors
|
|
for (int i = x; i < x + rwidth; i++)
|
|
{
|
|
int tx = rx >> 8;
|
|
int ty = ry >> 8;
|
|
|
|
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height
|
|
&& (this.windowCover[i & 0x1ff] & (1 << 4)) != 0)
|
|
{
|
|
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0)
|
|
{
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
|
|
rx += dx;
|
|
ry += dy;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 colors
|
|
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
|
for (int i = x; i < x + rwidth; i++)
|
|
{
|
|
int tx = rx >> 8;
|
|
int ty = ry >> 8;
|
|
|
|
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height
|
|
&& (this.windowCover[i & 0x1ff] & (1 << 4)) != 0)
|
|
{
|
|
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if ((tx & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0)
|
|
{
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
|
|
rx += dx;
|
|
ry += dy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((attr0 & (1 << 8)) == 0)
|
|
{
|
|
if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey;
|
|
|
|
int baseSprite;
|
|
if ((this.dispCnt & (1 << 6)) != 0)
|
|
{
|
|
// 1 dimensional
|
|
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale;
|
|
}
|
|
else
|
|
{
|
|
// 2 dimensional
|
|
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20);
|
|
}
|
|
|
|
int baseInc = scale;
|
|
if ((attr1 & (1 << 12)) != 0)
|
|
{
|
|
baseSprite += ((width / 8) * scale) - scale;
|
|
baseInc = -baseInc;
|
|
}
|
|
|
|
if ((attr0 & (1 << 13)) != 0)
|
|
{
|
|
// 256 colors
|
|
for (int i = x; i < x + width; i++)
|
|
{
|
|
if ((i & 0x1ff) < 240 && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0)
|
|
{
|
|
int tx = (i - x) & 7;
|
|
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
|
int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx;
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0)
|
|
{
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 colors
|
|
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
|
for (int i = x; i < x + width; i++)
|
|
{
|
|
if ((i & 0x1ff) < 240 && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0)
|
|
{
|
|
int tx = (i - x) & 7;
|
|
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
|
int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if ((tx & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0)
|
|
{
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int rotScaleParam = (attr1 >> 9) & 0x1F;
|
|
|
|
short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6);
|
|
short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE);
|
|
short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16);
|
|
short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E);
|
|
|
|
int cx = rwidth / 2;
|
|
int cy = rheight / 2;
|
|
|
|
int baseSprite = attr2 & 0x3FF;
|
|
int pitch;
|
|
|
|
if ((this.dispCnt & (1 << 6)) != 0)
|
|
{
|
|
// 1 dimensional
|
|
pitch = (width / 8) * scale;
|
|
}
|
|
else
|
|
{
|
|
// 2 dimensional
|
|
pitch = 0x20;
|
|
}
|
|
|
|
int rx = (int)((dmx * (spritey - cy)) - (cx * dx) + (width << 7));
|
|
int ry = (int)((dmy * (spritey - cy)) - (cx * dy) + (height << 7));
|
|
|
|
// Draw a rot/scale sprite
|
|
if ((attr0 & (1 << 13)) != 0)
|
|
{
|
|
// 256 colors
|
|
for (int i = x; i < x + rwidth; i++)
|
|
{
|
|
int tx = rx >> 8;
|
|
int ty = ry >> 8;
|
|
|
|
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height
|
|
&& (this.windowCover[i & 0x1ff] & (1 << 4)) != 0)
|
|
{
|
|
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0)
|
|
{
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
|
|
rx += dx;
|
|
ry += dy;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 colors
|
|
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
|
for (int i = x; i < x + rwidth; i++)
|
|
{
|
|
int tx = rx >> 8;
|
|
int ty = ry >> 8;
|
|
|
|
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height
|
|
&& (this.windowCover[i & 0x1ff] & (1 << 4)) != 0)
|
|
{
|
|
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if ((tx & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0)
|
|
{
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
|
|
rx += dx;
|
|
ry += dy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
private void DrawSpritesWindowBrightInc(int priority)
|
|
{
|
|
byte[] palette = this.memory.PaletteRam;
|
|
byte[] vram = this.memory.VideoRam;
|
|
|
|
// OBJ must be enabled in this.dispCnt
|
|
if ((this.dispCnt & (1 << 12)) == 0) return;
|
|
|
|
byte blendMaskType = (byte)(1 << 4);
|
|
|
|
for (int oamNum = 127; oamNum >= 0; oamNum--)
|
|
{
|
|
ushort attr2 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 4);
|
|
|
|
if (((attr2 >> 10) & 3) != priority) continue;
|
|
|
|
ushort attr0 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 0);
|
|
ushort attr1 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 2);
|
|
|
|
int x = attr1 & 0x1FF;
|
|
int y = attr0 & 0xFF;
|
|
|
|
bool semiTransparent = false;
|
|
|
|
switch ((attr0 >> 10) & 3)
|
|
{
|
|
case 1:
|
|
// Semi-transparent
|
|
semiTransparent = true;
|
|
break;
|
|
case 2:
|
|
// Obj window
|
|
continue;
|
|
case 3:
|
|
continue;
|
|
}
|
|
|
|
if ((this.dispCnt & 0x7) >= 3 && (attr2 & 0x3FF) < 0x200) continue;
|
|
|
|
int width = -1, height = -1;
|
|
switch ((attr0 >> 14) & 3)
|
|
{
|
|
case 0:
|
|
// Square
|
|
switch ((attr1 >> 14) & 3)
|
|
{
|
|
case 0: width = 8; height = 8; break;
|
|
case 1: width = 16; height = 16; break;
|
|
case 2: width = 32; height = 32; break;
|
|
case 3: width = 64; height = 64; break;
|
|
}
|
|
break;
|
|
case 1:
|
|
// Horizontal Rectangle
|
|
switch ((attr1 >> 14) & 3)
|
|
{
|
|
case 0: width = 16; height = 8; break;
|
|
case 1: width = 32; height = 8; break;
|
|
case 2: width = 32; height = 16; break;
|
|
case 3: width = 64; height = 32; break;
|
|
}
|
|
break;
|
|
case 2:
|
|
// Vertical Rectangle
|
|
switch ((attr1 >> 14) & 3)
|
|
{
|
|
case 0: width = 8; height = 16; break;
|
|
case 1: width = 8; height = 32; break;
|
|
case 2: width = 16; height = 32; break;
|
|
case 3: width = 32; height = 64; break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Check double size flag here
|
|
|
|
int rwidth = width, rheight = height;
|
|
if ((attr0 & (1 << 8)) != 0)
|
|
{
|
|
// Rot-scale on
|
|
if ((attr0 & (1 << 9)) != 0)
|
|
{
|
|
rwidth *= 2;
|
|
rheight *= 2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Invalid sprite
|
|
if ((attr0 & (1 << 9)) != 0)
|
|
width = -1;
|
|
}
|
|
|
|
if (width == -1)
|
|
{
|
|
// Invalid sprite
|
|
continue;
|
|
}
|
|
|
|
// Y clipping
|
|
if (y > ((y + rheight) & 0xff))
|
|
{
|
|
if (this.curLine >= ((y + rheight) & 0xff) && !(y < this.curLine)) continue;
|
|
}
|
|
else
|
|
{
|
|
if (this.curLine < y || this.curLine >= ((y + rheight) & 0xff)) continue;
|
|
}
|
|
|
|
int scale = 1;
|
|
if ((attr0 & (1 << 13)) != 0) scale = 2;
|
|
|
|
int spritey = this.curLine - y;
|
|
if (spritey < 0) spritey += 256;
|
|
|
|
if (semiTransparent)
|
|
{
|
|
if ((attr0 & (1 << 8)) == 0)
|
|
{
|
|
if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey;
|
|
|
|
int baseSprite;
|
|
if ((this.dispCnt & (1 << 6)) != 0)
|
|
{
|
|
// 1 dimensional
|
|
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale;
|
|
}
|
|
else
|
|
{
|
|
// 2 dimensional
|
|
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20);
|
|
}
|
|
|
|
int baseInc = scale;
|
|
if ((attr1 & (1 << 12)) != 0)
|
|
{
|
|
baseSprite += ((width / 8) * scale) - scale;
|
|
baseInc = -baseInc;
|
|
}
|
|
|
|
if ((attr0 & (1 << 13)) != 0)
|
|
{
|
|
// 256 colors
|
|
for (int i = x; i < x + width; i++)
|
|
{
|
|
if ((i & 0x1ff) < 240 && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0)
|
|
{
|
|
int tx = (i - x) & 7;
|
|
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
|
int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx;
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0)
|
|
{
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 colors
|
|
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
|
for (int i = x; i < x + width; i++)
|
|
{
|
|
if ((i & 0x1ff) < 240 && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0)
|
|
{
|
|
int tx = (i - x) & 7;
|
|
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
|
int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if ((tx & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0)
|
|
{
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int rotScaleParam = (attr1 >> 9) & 0x1F;
|
|
|
|
short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6);
|
|
short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE);
|
|
short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16);
|
|
short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E);
|
|
|
|
int cx = rwidth / 2;
|
|
int cy = rheight / 2;
|
|
|
|
int baseSprite = attr2 & 0x3FF;
|
|
int pitch;
|
|
|
|
if ((this.dispCnt & (1 << 6)) != 0)
|
|
{
|
|
// 1 dimensional
|
|
pitch = (width / 8) * scale;
|
|
}
|
|
else
|
|
{
|
|
// 2 dimensional
|
|
pitch = 0x20;
|
|
}
|
|
|
|
int rx = (int)((dmx * (spritey - cy)) - (cx * dx) + (width << 7));
|
|
int ry = (int)((dmy * (spritey - cy)) - (cx * dy) + (height << 7));
|
|
|
|
// Draw a rot/scale sprite
|
|
if ((attr0 & (1 << 13)) != 0)
|
|
{
|
|
// 256 colors
|
|
for (int i = x; i < x + rwidth; i++)
|
|
{
|
|
int tx = rx >> 8;
|
|
int ty = ry >> 8;
|
|
|
|
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height
|
|
&& (this.windowCover[i & 0x1ff] & (1 << 4)) != 0)
|
|
{
|
|
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0)
|
|
{
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
|
|
rx += dx;
|
|
ry += dy;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 colors
|
|
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
|
for (int i = x; i < x + rwidth; i++)
|
|
{
|
|
int tx = rx >> 8;
|
|
int ty = ry >> 8;
|
|
|
|
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height
|
|
&& (this.windowCover[i & 0x1ff] & (1 << 4)) != 0)
|
|
{
|
|
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if ((tx & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0)
|
|
{
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
|
|
rx += dx;
|
|
ry += dy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((attr0 & (1 << 8)) == 0)
|
|
{
|
|
if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey;
|
|
|
|
int baseSprite;
|
|
if ((this.dispCnt & (1 << 6)) != 0)
|
|
{
|
|
// 1 dimensional
|
|
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale;
|
|
}
|
|
else
|
|
{
|
|
// 2 dimensional
|
|
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20);
|
|
}
|
|
|
|
int baseInc = scale;
|
|
if ((attr1 & (1 << 12)) != 0)
|
|
{
|
|
baseSprite += ((width / 8) * scale) - scale;
|
|
baseInc = -baseInc;
|
|
}
|
|
|
|
if ((attr0 & (1 << 13)) != 0)
|
|
{
|
|
// 256 colors
|
|
for (int i = x; i < x + width; i++)
|
|
{
|
|
if ((i & 0x1ff) < 240 && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0)
|
|
{
|
|
int tx = (i - x) & 7;
|
|
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
|
int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx;
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0)
|
|
{
|
|
uint r = pixelColor & 0xFF;
|
|
uint g = (pixelColor >> 8) & 0xFF;
|
|
uint b = (pixelColor >> 16) & 0xFF;
|
|
r = r + (((0xFF - r) * this.blendY) >> 4);
|
|
g = g + (((0xFF - g) * this.blendY) >> 4);
|
|
b = b + (((0xFF - b) * this.blendY) >> 4);
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 colors
|
|
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
|
for (int i = x; i < x + width; i++)
|
|
{
|
|
if ((i & 0x1ff) < 240 && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0)
|
|
{
|
|
int tx = (i - x) & 7;
|
|
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
|
int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if ((tx & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0)
|
|
{
|
|
uint r = pixelColor & 0xFF;
|
|
uint g = (pixelColor >> 8) & 0xFF;
|
|
uint b = (pixelColor >> 16) & 0xFF;
|
|
r = r + (((0xFF - r) * this.blendY) >> 4);
|
|
g = g + (((0xFF - g) * this.blendY) >> 4);
|
|
b = b + (((0xFF - b) * this.blendY) >> 4);
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int rotScaleParam = (attr1 >> 9) & 0x1F;
|
|
|
|
short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6);
|
|
short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE);
|
|
short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16);
|
|
short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E);
|
|
|
|
int cx = rwidth / 2;
|
|
int cy = rheight / 2;
|
|
|
|
int baseSprite = attr2 & 0x3FF;
|
|
int pitch;
|
|
|
|
if ((this.dispCnt & (1 << 6)) != 0)
|
|
{
|
|
// 1 dimensional
|
|
pitch = (width / 8) * scale;
|
|
}
|
|
else
|
|
{
|
|
// 2 dimensional
|
|
pitch = 0x20;
|
|
}
|
|
|
|
int rx = (int)((dmx * (spritey - cy)) - (cx * dx) + (width << 7));
|
|
int ry = (int)((dmy * (spritey - cy)) - (cx * dy) + (height << 7));
|
|
|
|
// Draw a rot/scale sprite
|
|
if ((attr0 & (1 << 13)) != 0)
|
|
{
|
|
// 256 colors
|
|
for (int i = x; i < x + rwidth; i++)
|
|
{
|
|
int tx = rx >> 8;
|
|
int ty = ry >> 8;
|
|
|
|
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height
|
|
&& (this.windowCover[i & 0x1ff] & (1 << 4)) != 0)
|
|
{
|
|
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0)
|
|
{
|
|
uint r = pixelColor & 0xFF;
|
|
uint g = (pixelColor >> 8) & 0xFF;
|
|
uint b = (pixelColor >> 16) & 0xFF;
|
|
r = r + (((0xFF - r) * this.blendY) >> 4);
|
|
g = g + (((0xFF - g) * this.blendY) >> 4);
|
|
b = b + (((0xFF - b) * this.blendY) >> 4);
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
|
|
rx += dx;
|
|
ry += dy;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 colors
|
|
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
|
for (int i = x; i < x + rwidth; i++)
|
|
{
|
|
int tx = rx >> 8;
|
|
int ty = ry >> 8;
|
|
|
|
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height
|
|
&& (this.windowCover[i & 0x1ff] & (1 << 4)) != 0)
|
|
{
|
|
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if ((tx & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0)
|
|
{
|
|
uint r = pixelColor & 0xFF;
|
|
uint g = (pixelColor >> 8) & 0xFF;
|
|
uint b = (pixelColor >> 16) & 0xFF;
|
|
r = r + (((0xFF - r) * this.blendY) >> 4);
|
|
g = g + (((0xFF - g) * this.blendY) >> 4);
|
|
b = b + (((0xFF - b) * this.blendY) >> 4);
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
|
|
rx += dx;
|
|
ry += dy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
private void DrawSpritesWindowBrightDec(int priority)
|
|
{
|
|
byte[] palette = this.memory.PaletteRam;
|
|
byte[] vram = this.memory.VideoRam;
|
|
|
|
// OBJ must be enabled in this.dispCnt
|
|
if ((this.dispCnt & (1 << 12)) == 0) return;
|
|
|
|
byte blendMaskType = (byte)(1 << 4);
|
|
|
|
for (int oamNum = 127; oamNum >= 0; oamNum--)
|
|
{
|
|
ushort attr2 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 4);
|
|
|
|
if (((attr2 >> 10) & 3) != priority) continue;
|
|
|
|
ushort attr0 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 0);
|
|
ushort attr1 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 2);
|
|
|
|
int x = attr1 & 0x1FF;
|
|
int y = attr0 & 0xFF;
|
|
|
|
bool semiTransparent = false;
|
|
|
|
switch ((attr0 >> 10) & 3)
|
|
{
|
|
case 1:
|
|
// Semi-transparent
|
|
semiTransparent = true;
|
|
break;
|
|
case 2:
|
|
// Obj window
|
|
continue;
|
|
case 3:
|
|
continue;
|
|
}
|
|
|
|
if ((this.dispCnt & 0x7) >= 3 && (attr2 & 0x3FF) < 0x200) continue;
|
|
|
|
int width = -1, height = -1;
|
|
switch ((attr0 >> 14) & 3)
|
|
{
|
|
case 0:
|
|
// Square
|
|
switch ((attr1 >> 14) & 3)
|
|
{
|
|
case 0: width = 8; height = 8; break;
|
|
case 1: width = 16; height = 16; break;
|
|
case 2: width = 32; height = 32; break;
|
|
case 3: width = 64; height = 64; break;
|
|
}
|
|
break;
|
|
case 1:
|
|
// Horizontal Rectangle
|
|
switch ((attr1 >> 14) & 3)
|
|
{
|
|
case 0: width = 16; height = 8; break;
|
|
case 1: width = 32; height = 8; break;
|
|
case 2: width = 32; height = 16; break;
|
|
case 3: width = 64; height = 32; break;
|
|
}
|
|
break;
|
|
case 2:
|
|
// Vertical Rectangle
|
|
switch ((attr1 >> 14) & 3)
|
|
{
|
|
case 0: width = 8; height = 16; break;
|
|
case 1: width = 8; height = 32; break;
|
|
case 2: width = 16; height = 32; break;
|
|
case 3: width = 32; height = 64; break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Check double size flag here
|
|
|
|
int rwidth = width, rheight = height;
|
|
if ((attr0 & (1 << 8)) != 0)
|
|
{
|
|
// Rot-scale on
|
|
if ((attr0 & (1 << 9)) != 0)
|
|
{
|
|
rwidth *= 2;
|
|
rheight *= 2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Invalid sprite
|
|
if ((attr0 & (1 << 9)) != 0)
|
|
width = -1;
|
|
}
|
|
|
|
if (width == -1)
|
|
{
|
|
// Invalid sprite
|
|
continue;
|
|
}
|
|
|
|
// Y clipping
|
|
if (y > ((y + rheight) & 0xff))
|
|
{
|
|
if (this.curLine >= ((y + rheight) & 0xff) && !(y < this.curLine)) continue;
|
|
}
|
|
else
|
|
{
|
|
if (this.curLine < y || this.curLine >= ((y + rheight) & 0xff)) continue;
|
|
}
|
|
|
|
int scale = 1;
|
|
if ((attr0 & (1 << 13)) != 0) scale = 2;
|
|
|
|
int spritey = this.curLine - y;
|
|
if (spritey < 0) spritey += 256;
|
|
|
|
if (semiTransparent)
|
|
{
|
|
if ((attr0 & (1 << 8)) == 0)
|
|
{
|
|
if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey;
|
|
|
|
int baseSprite;
|
|
if ((this.dispCnt & (1 << 6)) != 0)
|
|
{
|
|
// 1 dimensional
|
|
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale;
|
|
}
|
|
else
|
|
{
|
|
// 2 dimensional
|
|
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20);
|
|
}
|
|
|
|
int baseInc = scale;
|
|
if ((attr1 & (1 << 12)) != 0)
|
|
{
|
|
baseSprite += ((width / 8) * scale) - scale;
|
|
baseInc = -baseInc;
|
|
}
|
|
|
|
if ((attr0 & (1 << 13)) != 0)
|
|
{
|
|
// 256 colors
|
|
for (int i = x; i < x + width; i++)
|
|
{
|
|
if ((i & 0x1ff) < 240 && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0)
|
|
{
|
|
int tx = (i - x) & 7;
|
|
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
|
int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx;
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0)
|
|
{
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 colors
|
|
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
|
for (int i = x; i < x + width; i++)
|
|
{
|
|
if ((i & 0x1ff) < 240 && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0)
|
|
{
|
|
int tx = (i - x) & 7;
|
|
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
|
int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if ((tx & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0)
|
|
{
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int rotScaleParam = (attr1 >> 9) & 0x1F;
|
|
|
|
short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6);
|
|
short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE);
|
|
short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16);
|
|
short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E);
|
|
|
|
int cx = rwidth / 2;
|
|
int cy = rheight / 2;
|
|
|
|
int baseSprite = attr2 & 0x3FF;
|
|
int pitch;
|
|
|
|
if ((this.dispCnt & (1 << 6)) != 0)
|
|
{
|
|
// 1 dimensional
|
|
pitch = (width / 8) * scale;
|
|
}
|
|
else
|
|
{
|
|
// 2 dimensional
|
|
pitch = 0x20;
|
|
}
|
|
|
|
int rx = (int)((dmx * (spritey - cy)) - (cx * dx) + (width << 7));
|
|
int ry = (int)((dmy * (spritey - cy)) - (cx * dy) + (height << 7));
|
|
|
|
// Draw a rot/scale sprite
|
|
if ((attr0 & (1 << 13)) != 0)
|
|
{
|
|
// 256 colors
|
|
for (int i = x; i < x + rwidth; i++)
|
|
{
|
|
int tx = rx >> 8;
|
|
int ty = ry >> 8;
|
|
|
|
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height
|
|
&& (this.windowCover[i & 0x1ff] & (1 << 4)) != 0)
|
|
{
|
|
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0)
|
|
{
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
|
|
rx += dx;
|
|
ry += dy;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 colors
|
|
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
|
for (int i = x; i < x + rwidth; i++)
|
|
{
|
|
int tx = rx >> 8;
|
|
int ty = ry >> 8;
|
|
|
|
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height
|
|
&& (this.windowCover[i & 0x1ff] & (1 << 4)) != 0)
|
|
{
|
|
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if ((tx & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0)
|
|
{
|
|
if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[(i & 0x1ff)];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
|
|
rx += dx;
|
|
ry += dy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((attr0 & (1 << 8)) == 0)
|
|
{
|
|
if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey;
|
|
|
|
int baseSprite;
|
|
if ((this.dispCnt & (1 << 6)) != 0)
|
|
{
|
|
// 1 dimensional
|
|
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale;
|
|
}
|
|
else
|
|
{
|
|
// 2 dimensional
|
|
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20);
|
|
}
|
|
|
|
int baseInc = scale;
|
|
if ((attr1 & (1 << 12)) != 0)
|
|
{
|
|
baseSprite += ((width / 8) * scale) - scale;
|
|
baseInc = -baseInc;
|
|
}
|
|
|
|
if ((attr0 & (1 << 13)) != 0)
|
|
{
|
|
// 256 colors
|
|
for (int i = x; i < x + width; i++)
|
|
{
|
|
if ((i & 0x1ff) < 240 && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0)
|
|
{
|
|
int tx = (i - x) & 7;
|
|
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
|
int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx;
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0)
|
|
{
|
|
uint r = pixelColor & 0xFF;
|
|
uint g = (pixelColor >> 8) & 0xFF;
|
|
uint b = (pixelColor >> 16) & 0xFF;
|
|
r = r - ((r * this.blendY) >> 4);
|
|
g = g - ((g * this.blendY) >> 4);
|
|
b = b - ((b * this.blendY) >> 4);
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 colors
|
|
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
|
for (int i = x; i < x + width; i++)
|
|
{
|
|
if ((i & 0x1ff) < 240 && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0)
|
|
{
|
|
int tx = (i - x) & 7;
|
|
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
|
int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if ((tx & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0)
|
|
{
|
|
uint r = pixelColor & 0xFF;
|
|
uint g = (pixelColor >> 8) & 0xFF;
|
|
uint b = (pixelColor >> 16) & 0xFF;
|
|
r = r - ((r * this.blendY) >> 4);
|
|
g = g - ((g * this.blendY) >> 4);
|
|
b = b - ((b * this.blendY) >> 4);
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int rotScaleParam = (attr1 >> 9) & 0x1F;
|
|
|
|
short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6);
|
|
short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE);
|
|
short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16);
|
|
short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E);
|
|
|
|
int cx = rwidth / 2;
|
|
int cy = rheight / 2;
|
|
|
|
int baseSprite = attr2 & 0x3FF;
|
|
int pitch;
|
|
|
|
if ((this.dispCnt & (1 << 6)) != 0)
|
|
{
|
|
// 1 dimensional
|
|
pitch = (width / 8) * scale;
|
|
}
|
|
else
|
|
{
|
|
// 2 dimensional
|
|
pitch = 0x20;
|
|
}
|
|
|
|
int rx = (int)((dmx * (spritey - cy)) - (cx * dx) + (width << 7));
|
|
int ry = (int)((dmy * (spritey - cy)) - (cx * dy) + (height << 7));
|
|
|
|
// Draw a rot/scale sprite
|
|
if ((attr0 & (1 << 13)) != 0)
|
|
{
|
|
// 256 colors
|
|
for (int i = x; i < x + rwidth; i++)
|
|
{
|
|
int tx = rx >> 8;
|
|
int ty = ry >> 8;
|
|
|
|
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height
|
|
&& (this.windowCover[i & 0x1ff] & (1 << 4)) != 0)
|
|
{
|
|
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0)
|
|
{
|
|
uint r = pixelColor & 0xFF;
|
|
uint g = (pixelColor >> 8) & 0xFF;
|
|
uint b = (pixelColor >> 16) & 0xFF;
|
|
r = r - ((r * this.blendY) >> 4);
|
|
g = g - ((g * this.blendY) >> 4);
|
|
b = b - ((b * this.blendY) >> 4);
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
|
|
rx += dx;
|
|
ry += dy;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 colors
|
|
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
|
for (int i = x; i < x + rwidth; i++)
|
|
{
|
|
int tx = rx >> 8;
|
|
int ty = ry >> 8;
|
|
|
|
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height
|
|
&& (this.windowCover[i & 0x1ff] & (1 << 4)) != 0)
|
|
{
|
|
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2);
|
|
int lookup = vram[0x10000 + curIdx];
|
|
if ((tx & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0)
|
|
{
|
|
uint r = pixelColor & 0xFF;
|
|
uint g = (pixelColor >> 8) & 0xFF;
|
|
uint b = (pixelColor >> 16) & 0xFF;
|
|
r = r - ((r * this.blendY) >> 4);
|
|
g = g - ((g * this.blendY) >> 4);
|
|
b = b - ((b * this.blendY) >> 4);
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[(i & 0x1ff)] = pixelColor;
|
|
this.blend[(i & 0x1ff)] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
|
|
rx += dx;
|
|
ry += dy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endregion Sprite Drawing
|
|
#region Rot/Scale Bg
|
|
private void RenderRotScaleBgNormal(int bg)
|
|
{
|
|
byte[] palette = this.memory.PaletteRam;
|
|
byte[] vram = this.memory.VideoRam;
|
|
|
|
byte blendMaskType = (byte)(1 << bg);
|
|
|
|
ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)bg);
|
|
|
|
int width = 0, height = 0;
|
|
switch ((bgcnt >> 14) & 0x3)
|
|
{
|
|
case 0: width = 128; height = 128; break;
|
|
case 1: width = 256; height = 256; break;
|
|
case 2: width = 512; height = 512; break;
|
|
case 3: width = 1024; height = 1024; break;
|
|
}
|
|
|
|
int screenBase = ((bgcnt >> 8) & 0x1F) * 0x800;
|
|
int charBase = ((bgcnt >> 2) & 0x3) * 0x4000;
|
|
|
|
int x = this.memory.Bgx[bg - 2];
|
|
int y = this.memory.Bgy[bg - 2];
|
|
|
|
short dx = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PA + (uint)(bg - 2) * 0x10);
|
|
short dy = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PC + (uint)(bg - 2) * 0x10);
|
|
|
|
bool transparent = (bgcnt & (1 << 13)) == 0;
|
|
|
|
for (int i = 0; i < 240; i++)
|
|
{
|
|
if (true)
|
|
{
|
|
int ax = x >> 8;
|
|
int ay = y >> 8;
|
|
|
|
if ((ax >= 0 && ax < width && ay >= 0 && ay < height) || !transparent)
|
|
{
|
|
int tmpTileIdx = (int)(screenBase + ((ay & (height - 1)) / 8) * (width / 8) + ((ax & (width - 1)) / 8));
|
|
int tileChar = vram[tmpTileIdx];
|
|
|
|
int lookup = vram[charBase + (tileChar * 64) + ((ay & 7) * 8) + (ax & 7)];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8)));
|
|
this.scanline[i] = pixelColor; this.blend[i] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
x += dx;
|
|
y += dy;
|
|
}
|
|
}
|
|
private void RenderRotScaleBgBlend(int bg)
|
|
{
|
|
byte[] palette = this.memory.PaletteRam;
|
|
byte[] vram = this.memory.VideoRam;
|
|
|
|
byte blendMaskType = (byte)(1 << bg);
|
|
|
|
ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)bg);
|
|
|
|
int width = 0, height = 0;
|
|
switch ((bgcnt >> 14) & 0x3)
|
|
{
|
|
case 0: width = 128; height = 128; break;
|
|
case 1: width = 256; height = 256; break;
|
|
case 2: width = 512; height = 512; break;
|
|
case 3: width = 1024; height = 1024; break;
|
|
}
|
|
|
|
int screenBase = ((bgcnt >> 8) & 0x1F) * 0x800;
|
|
int charBase = ((bgcnt >> 2) & 0x3) * 0x4000;
|
|
|
|
int x = this.memory.Bgx[bg - 2];
|
|
int y = this.memory.Bgy[bg - 2];
|
|
|
|
short dx = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PA + (uint)(bg - 2) * 0x10);
|
|
short dy = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PC + (uint)(bg - 2) * 0x10);
|
|
|
|
bool transparent = (bgcnt & (1 << 13)) == 0;
|
|
|
|
for (int i = 0; i < 240; i++)
|
|
{
|
|
if (true)
|
|
{
|
|
int ax = x >> 8;
|
|
int ay = y >> 8;
|
|
|
|
if ((ax >= 0 && ax < width && ay >= 0 && ay < height) || !transparent)
|
|
{
|
|
int tmpTileIdx = (int)(screenBase + ((ay & (height - 1)) / 8) * (width / 8) + ((ax & (width - 1)) / 8));
|
|
int tileChar = vram[tmpTileIdx];
|
|
|
|
int lookup = vram[charBase + (tileChar * 64) + ((ay & 7) * 8) + (ax & 7)];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8)));
|
|
if ((this.blend[i] & this.blendTarget) != 0)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[i];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[i] = pixelColor; this.blend[i] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
x += dx;
|
|
y += dy;
|
|
}
|
|
}
|
|
private void RenderRotScaleBgBrightInc(int bg)
|
|
{
|
|
byte[] palette = this.memory.PaletteRam;
|
|
byte[] vram = this.memory.VideoRam;
|
|
|
|
byte blendMaskType = (byte)(1 << bg);
|
|
|
|
ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)bg);
|
|
|
|
int width = 0, height = 0;
|
|
switch ((bgcnt >> 14) & 0x3)
|
|
{
|
|
case 0: width = 128; height = 128; break;
|
|
case 1: width = 256; height = 256; break;
|
|
case 2: width = 512; height = 512; break;
|
|
case 3: width = 1024; height = 1024; break;
|
|
}
|
|
|
|
int screenBase = ((bgcnt >> 8) & 0x1F) * 0x800;
|
|
int charBase = ((bgcnt >> 2) & 0x3) * 0x4000;
|
|
|
|
int x = this.memory.Bgx[bg - 2];
|
|
int y = this.memory.Bgy[bg - 2];
|
|
|
|
short dx = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PA + (uint)(bg - 2) * 0x10);
|
|
short dy = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PC + (uint)(bg - 2) * 0x10);
|
|
|
|
bool transparent = (bgcnt & (1 << 13)) == 0;
|
|
|
|
for (int i = 0; i < 240; i++)
|
|
{
|
|
if (true)
|
|
{
|
|
int ax = x >> 8;
|
|
int ay = y >> 8;
|
|
|
|
if ((ax >= 0 && ax < width && ay >= 0 && ay < height) || !transparent)
|
|
{
|
|
int tmpTileIdx = (int)(screenBase + ((ay & (height - 1)) / 8) * (width / 8) + ((ax & (width - 1)) / 8));
|
|
int tileChar = vram[tmpTileIdx];
|
|
|
|
int lookup = vram[charBase + (tileChar * 64) + ((ay & 7) * 8) + (ax & 7)];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8)));
|
|
uint r = pixelColor & 0xFF;
|
|
uint g = (pixelColor >> 8) & 0xFF;
|
|
uint b = (pixelColor >> 16) & 0xFF;
|
|
r = r + (((0xFF - r) * this.blendY) >> 4);
|
|
g = g + (((0xFF - g) * this.blendY) >> 4);
|
|
b = b + (((0xFF - b) * this.blendY) >> 4);
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
this.scanline[i] = pixelColor; this.blend[i] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
x += dx;
|
|
y += dy;
|
|
}
|
|
}
|
|
private void RenderRotScaleBgBrightDec(int bg)
|
|
{
|
|
byte[] palette = this.memory.PaletteRam;
|
|
byte[] vram = this.memory.VideoRam;
|
|
|
|
byte blendMaskType = (byte)(1 << bg);
|
|
|
|
ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)bg);
|
|
|
|
int width = 0, height = 0;
|
|
switch ((bgcnt >> 14) & 0x3)
|
|
{
|
|
case 0: width = 128; height = 128; break;
|
|
case 1: width = 256; height = 256; break;
|
|
case 2: width = 512; height = 512; break;
|
|
case 3: width = 1024; height = 1024; break;
|
|
}
|
|
|
|
int screenBase = ((bgcnt >> 8) & 0x1F) * 0x800;
|
|
int charBase = ((bgcnt >> 2) & 0x3) * 0x4000;
|
|
|
|
int x = this.memory.Bgx[bg - 2];
|
|
int y = this.memory.Bgy[bg - 2];
|
|
|
|
short dx = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PA + (uint)(bg - 2) * 0x10);
|
|
short dy = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PC + (uint)(bg - 2) * 0x10);
|
|
|
|
bool transparent = (bgcnt & (1 << 13)) == 0;
|
|
|
|
for (int i = 0; i < 240; i++)
|
|
{
|
|
if (true)
|
|
{
|
|
int ax = x >> 8;
|
|
int ay = y >> 8;
|
|
|
|
if ((ax >= 0 && ax < width && ay >= 0 && ay < height) || !transparent)
|
|
{
|
|
int tmpTileIdx = (int)(screenBase + ((ay & (height - 1)) / 8) * (width / 8) + ((ax & (width - 1)) / 8));
|
|
int tileChar = vram[tmpTileIdx];
|
|
|
|
int lookup = vram[charBase + (tileChar * 64) + ((ay & 7) * 8) + (ax & 7)];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8)));
|
|
uint r = pixelColor & 0xFF;
|
|
uint g = (pixelColor >> 8) & 0xFF;
|
|
uint b = (pixelColor >> 16) & 0xFF;
|
|
r = r - ((r * this.blendY) >> 4);
|
|
g = g - ((g * this.blendY) >> 4);
|
|
b = b - ((b * this.blendY) >> 4);
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
this.scanline[i] = pixelColor; this.blend[i] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
x += dx;
|
|
y += dy;
|
|
}
|
|
}
|
|
private void RenderRotScaleBgWindow(int bg)
|
|
{
|
|
byte[] palette = this.memory.PaletteRam;
|
|
byte[] vram = this.memory.VideoRam;
|
|
|
|
byte blendMaskType = (byte)(1 << bg);
|
|
|
|
ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)bg);
|
|
|
|
int width = 0, height = 0;
|
|
switch ((bgcnt >> 14) & 0x3)
|
|
{
|
|
case 0: width = 128; height = 128; break;
|
|
case 1: width = 256; height = 256; break;
|
|
case 2: width = 512; height = 512; break;
|
|
case 3: width = 1024; height = 1024; break;
|
|
}
|
|
|
|
int screenBase = ((bgcnt >> 8) & 0x1F) * 0x800;
|
|
int charBase = ((bgcnt >> 2) & 0x3) * 0x4000;
|
|
|
|
int x = this.memory.Bgx[bg - 2];
|
|
int y = this.memory.Bgy[bg - 2];
|
|
|
|
short dx = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PA + (uint)(bg - 2) * 0x10);
|
|
short dy = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PC + (uint)(bg - 2) * 0x10);
|
|
|
|
bool transparent = (bgcnt & (1 << 13)) == 0;
|
|
|
|
for (int i = 0; i < 240; i++)
|
|
{
|
|
if ((this.windowCover[i] & (1 << bg)) != 0)
|
|
{
|
|
int ax = x >> 8;
|
|
int ay = y >> 8;
|
|
|
|
if ((ax >= 0 && ax < width && ay >= 0 && ay < height) || !transparent)
|
|
{
|
|
int tmpTileIdx = (int)(screenBase + ((ay & (height - 1)) / 8) * (width / 8) + ((ax & (width - 1)) / 8));
|
|
int tileChar = vram[tmpTileIdx];
|
|
|
|
int lookup = vram[charBase + (tileChar * 64) + ((ay & 7) * 8) + (ax & 7)];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8)));
|
|
this.scanline[i] = pixelColor; this.blend[i] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
x += dx;
|
|
y += dy;
|
|
}
|
|
}
|
|
private void RenderRotScaleBgWindowBlend(int bg)
|
|
{
|
|
byte[] palette = this.memory.PaletteRam;
|
|
byte[] vram = this.memory.VideoRam;
|
|
|
|
byte blendMaskType = (byte)(1 << bg);
|
|
|
|
ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)bg);
|
|
|
|
int width = 0, height = 0;
|
|
switch ((bgcnt >> 14) & 0x3)
|
|
{
|
|
case 0: width = 128; height = 128; break;
|
|
case 1: width = 256; height = 256; break;
|
|
case 2: width = 512; height = 512; break;
|
|
case 3: width = 1024; height = 1024; break;
|
|
}
|
|
|
|
int screenBase = ((bgcnt >> 8) & 0x1F) * 0x800;
|
|
int charBase = ((bgcnt >> 2) & 0x3) * 0x4000;
|
|
|
|
int x = this.memory.Bgx[bg - 2];
|
|
int y = this.memory.Bgy[bg - 2];
|
|
|
|
short dx = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PA + (uint)(bg - 2) * 0x10);
|
|
short dy = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PC + (uint)(bg - 2) * 0x10);
|
|
|
|
bool transparent = (bgcnt & (1 << 13)) == 0;
|
|
|
|
for (int i = 0; i < 240; i++)
|
|
{
|
|
if ((this.windowCover[i] & (1 << bg)) != 0)
|
|
{
|
|
int ax = x >> 8;
|
|
int ay = y >> 8;
|
|
|
|
if ((ax >= 0 && ax < width && ay >= 0 && ay < height) || !transparent)
|
|
{
|
|
int tmpTileIdx = (int)(screenBase + ((ay & (height - 1)) / 8) * (width / 8) + ((ax & (width - 1)) / 8));
|
|
int tileChar = vram[tmpTileIdx];
|
|
|
|
int lookup = vram[charBase + (tileChar * 64) + ((ay & 7) * 8) + (ax & 7)];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i] & (1 << 5)) != 0)
|
|
{
|
|
if ((this.blend[i] & this.blendTarget) != 0)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[i];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
}
|
|
this.scanline[i] = pixelColor; this.blend[i] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
x += dx;
|
|
y += dy;
|
|
}
|
|
}
|
|
private void RenderRotScaleBgWindowBrightInc(int bg)
|
|
{
|
|
byte[] palette = this.memory.PaletteRam;
|
|
byte[] vram = this.memory.VideoRam;
|
|
|
|
byte blendMaskType = (byte)(1 << bg);
|
|
|
|
ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)bg);
|
|
|
|
int width = 0, height = 0;
|
|
switch ((bgcnt >> 14) & 0x3)
|
|
{
|
|
case 0: width = 128; height = 128; break;
|
|
case 1: width = 256; height = 256; break;
|
|
case 2: width = 512; height = 512; break;
|
|
case 3: width = 1024; height = 1024; break;
|
|
}
|
|
|
|
int screenBase = ((bgcnt >> 8) & 0x1F) * 0x800;
|
|
int charBase = ((bgcnt >> 2) & 0x3) * 0x4000;
|
|
|
|
int x = this.memory.Bgx[bg - 2];
|
|
int y = this.memory.Bgy[bg - 2];
|
|
|
|
short dx = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PA + (uint)(bg - 2) * 0x10);
|
|
short dy = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PC + (uint)(bg - 2) * 0x10);
|
|
|
|
bool transparent = (bgcnt & (1 << 13)) == 0;
|
|
|
|
for (int i = 0; i < 240; i++)
|
|
{
|
|
if ((this.windowCover[i] & (1 << bg)) != 0)
|
|
{
|
|
int ax = x >> 8;
|
|
int ay = y >> 8;
|
|
|
|
if ((ax >= 0 && ax < width && ay >= 0 && ay < height) || !transparent)
|
|
{
|
|
int tmpTileIdx = (int)(screenBase + ((ay & (height - 1)) / 8) * (width / 8) + ((ax & (width - 1)) / 8));
|
|
int tileChar = vram[tmpTileIdx];
|
|
|
|
int lookup = vram[charBase + (tileChar * 64) + ((ay & 7) * 8) + (ax & 7)];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i] & (1 << 5)) != 0)
|
|
{
|
|
uint r = pixelColor & 0xFF;
|
|
uint g = (pixelColor >> 8) & 0xFF;
|
|
uint b = (pixelColor >> 16) & 0xFF;
|
|
r = r + (((0xFF - r) * this.blendY) >> 4);
|
|
g = g + (((0xFF - g) * this.blendY) >> 4);
|
|
b = b + (((0xFF - b) * this.blendY) >> 4);
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[i] = pixelColor; this.blend[i] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
x += dx;
|
|
y += dy;
|
|
}
|
|
}
|
|
private void RenderRotScaleBgWindowBrightDec(int bg)
|
|
{
|
|
byte[] palette = this.memory.PaletteRam;
|
|
byte[] vram = this.memory.VideoRam;
|
|
|
|
byte blendMaskType = (byte)(1 << bg);
|
|
|
|
ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)bg);
|
|
|
|
int width = 0, height = 0;
|
|
switch ((bgcnt >> 14) & 0x3)
|
|
{
|
|
case 0: width = 128; height = 128; break;
|
|
case 1: width = 256; height = 256; break;
|
|
case 2: width = 512; height = 512; break;
|
|
case 3: width = 1024; height = 1024; break;
|
|
}
|
|
|
|
int screenBase = ((bgcnt >> 8) & 0x1F) * 0x800;
|
|
int charBase = ((bgcnt >> 2) & 0x3) * 0x4000;
|
|
|
|
int x = this.memory.Bgx[bg - 2];
|
|
int y = this.memory.Bgy[bg - 2];
|
|
|
|
short dx = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PA + (uint)(bg - 2) * 0x10);
|
|
short dy = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PC + (uint)(bg - 2) * 0x10);
|
|
|
|
bool transparent = (bgcnt & (1 << 13)) == 0;
|
|
|
|
for (int i = 0; i < 240; i++)
|
|
{
|
|
if ((this.windowCover[i] & (1 << bg)) != 0)
|
|
{
|
|
int ax = x >> 8;
|
|
int ay = y >> 8;
|
|
|
|
if ((ax >= 0 && ax < width && ay >= 0 && ay < height) || !transparent)
|
|
{
|
|
int tmpTileIdx = (int)(screenBase + ((ay & (height - 1)) / 8) * (width / 8) + ((ax & (width - 1)) / 8));
|
|
int tileChar = vram[tmpTileIdx];
|
|
|
|
int lookup = vram[charBase + (tileChar * 64) + ((ay & 7) * 8) + (ax & 7)];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i] & (1 << 5)) != 0)
|
|
{
|
|
uint r = pixelColor & 0xFF;
|
|
uint g = (pixelColor >> 8) & 0xFF;
|
|
uint b = (pixelColor >> 16) & 0xFF;
|
|
r = r - ((r * this.blendY) >> 4);
|
|
g = g - ((g * this.blendY) >> 4);
|
|
b = b - ((b * this.blendY) >> 4);
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[i] = pixelColor; this.blend[i] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
x += dx;
|
|
y += dy;
|
|
}
|
|
}
|
|
#endregion Rot/Scale Bg
|
|
#region Text Bg
|
|
private void RenderTextBgNormal(int bg)
|
|
{
|
|
byte[] palette = this.memory.PaletteRam;
|
|
byte[] vram = this.memory.VideoRam;
|
|
|
|
byte blendMaskType = (byte)(1 << bg);
|
|
|
|
ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)bg);
|
|
|
|
int width = 0, height = 0;
|
|
switch ((bgcnt >> 14) & 0x3)
|
|
{
|
|
case 0: width = 256; height = 256; break;
|
|
case 1: width = 512; height = 256; break;
|
|
case 2: width = 256; height = 512; break;
|
|
case 3: width = 512; height = 512; break;
|
|
}
|
|
|
|
int screenBase = ((bgcnt >> 8) & 0x1F) * 0x800;
|
|
int charBase = ((bgcnt >> 2) & 0x3) * 0x4000;
|
|
|
|
int hofs = Memory.ReadU16(this.memory.IORam, Memory.BG0HOFS + (uint)bg * 4) & 0x1FF;
|
|
int vofs = Memory.ReadU16(this.memory.IORam, Memory.BG0VOFS + (uint)bg * 4) & 0x1FF;
|
|
|
|
if ((bgcnt & (1 << 7)) != 0)
|
|
{
|
|
// 256 color tiles
|
|
int bgy = ((this.curLine + vofs) & (height - 1)) / 8;
|
|
|
|
int tileIdx = screenBase + (((bgy & 31) * 32) * 2);
|
|
switch ((bgcnt >> 14) & 0x3)
|
|
{
|
|
case 2: if (bgy >= 32) tileIdx += 32 * 32 * 2; break;
|
|
case 3: if (bgy >= 32) tileIdx += 32 * 32 * 4; break;
|
|
}
|
|
|
|
int tileY = ((this.curLine + vofs) & 0x7) * 8;
|
|
|
|
for (int i = 0; i < 240; i++)
|
|
{
|
|
if (true)
|
|
{
|
|
int bgx = ((i + hofs) & (width - 1)) / 8;
|
|
int tmpTileIdx = tileIdx + ((bgx & 31) * 2);
|
|
if (bgx >= 32) tmpTileIdx += 32 * 32 * 2;
|
|
int tileChar = vram[tmpTileIdx] | (vram[tmpTileIdx + 1] << 8);
|
|
int x = (i + hofs) & 7;
|
|
int y = tileY;
|
|
if ((tileChar & (1 << 10)) != 0) x = 7 - x;
|
|
if ((tileChar & (1 << 11)) != 0) y = 56 - y;
|
|
int lookup = vram[charBase + ((tileChar & 0x3FF) * 64) + y + x];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8)));
|
|
this.scanline[i] = pixelColor; this.blend[i] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 color tiles
|
|
int bgy = ((this.curLine + vofs) & (height - 1)) / 8;
|
|
|
|
int tileIdx = screenBase + (((bgy & 31) * 32) * 2);
|
|
switch ((bgcnt >> 14) & 0x3)
|
|
{
|
|
case 2: if (bgy >= 32) tileIdx += 32 * 32 * 2; break;
|
|
case 3: if (bgy >= 32) tileIdx += 32 * 32 * 4; break;
|
|
}
|
|
|
|
int tileY = ((this.curLine + vofs) & 0x7) * 4;
|
|
|
|
for (int i = 0; i < 240; i++)
|
|
{
|
|
if (true)
|
|
{
|
|
int bgx = ((i + hofs) & (width - 1)) / 8;
|
|
int tmpTileIdx = tileIdx + ((bgx & 31) * 2);
|
|
if (bgx >= 32) tmpTileIdx += 32 * 32 * 2;
|
|
int tileChar = vram[tmpTileIdx] | (vram[tmpTileIdx + 1] << 8);
|
|
int x = (i + hofs) & 7;
|
|
int y = tileY;
|
|
if ((tileChar & (1 << 10)) != 0) x = 7 - x;
|
|
if ((tileChar & (1 << 11)) != 0) y = 28 - y;
|
|
int lookup = vram[charBase + ((tileChar & 0x3FF) * 32) + y + (x / 2)];
|
|
if ((x & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
int palNum = ((tileChar >> 12) & 0xf) * 16 * 2;
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palNum + lookup * 2] | (palette[palNum + lookup * 2 + 1] << 8)));
|
|
this.scanline[i] = pixelColor; this.blend[i] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
private void RenderTextBgBlend(int bg)
|
|
{
|
|
byte[] palette = this.memory.PaletteRam;
|
|
byte[] vram = this.memory.VideoRam;
|
|
|
|
byte blendMaskType = (byte)(1 << bg);
|
|
|
|
ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)bg);
|
|
|
|
int width = 0, height = 0;
|
|
switch ((bgcnt >> 14) & 0x3)
|
|
{
|
|
case 0: width = 256; height = 256; break;
|
|
case 1: width = 512; height = 256; break;
|
|
case 2: width = 256; height = 512; break;
|
|
case 3: width = 512; height = 512; break;
|
|
}
|
|
|
|
int screenBase = ((bgcnt >> 8) & 0x1F) * 0x800;
|
|
int charBase = ((bgcnt >> 2) & 0x3) * 0x4000;
|
|
|
|
int hofs = Memory.ReadU16(this.memory.IORam, Memory.BG0HOFS + (uint)bg * 4) & 0x1FF;
|
|
int vofs = Memory.ReadU16(this.memory.IORam, Memory.BG0VOFS + (uint)bg * 4) & 0x1FF;
|
|
|
|
if ((bgcnt & (1 << 7)) != 0)
|
|
{
|
|
// 256 color tiles
|
|
int bgy = ((this.curLine + vofs) & (height - 1)) / 8;
|
|
|
|
int tileIdx = screenBase + (((bgy & 31) * 32) * 2);
|
|
switch ((bgcnt >> 14) & 0x3)
|
|
{
|
|
case 2: if (bgy >= 32) tileIdx += 32 * 32 * 2; break;
|
|
case 3: if (bgy >= 32) tileIdx += 32 * 32 * 4; break;
|
|
}
|
|
|
|
int tileY = ((this.curLine + vofs) & 0x7) * 8;
|
|
|
|
for (int i = 0; i < 240; i++)
|
|
{
|
|
if (true)
|
|
{
|
|
int bgx = ((i + hofs) & (width - 1)) / 8;
|
|
int tmpTileIdx = tileIdx + ((bgx & 31) * 2);
|
|
if (bgx >= 32) tmpTileIdx += 32 * 32 * 2;
|
|
int tileChar = vram[tmpTileIdx] | (vram[tmpTileIdx + 1] << 8);
|
|
int x = (i + hofs) & 7;
|
|
int y = tileY;
|
|
if ((tileChar & (1 << 10)) != 0) x = 7 - x;
|
|
if ((tileChar & (1 << 11)) != 0) y = 56 - y;
|
|
int lookup = vram[charBase + ((tileChar & 0x3FF) * 64) + y + x];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8)));
|
|
if ((this.blend[i] & this.blendTarget) != 0)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[i];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[i] = pixelColor; this.blend[i] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 color tiles
|
|
int bgy = ((this.curLine + vofs) & (height - 1)) / 8;
|
|
|
|
int tileIdx = screenBase + (((bgy & 31) * 32) * 2);
|
|
switch ((bgcnt >> 14) & 0x3)
|
|
{
|
|
case 2: if (bgy >= 32) tileIdx += 32 * 32 * 2; break;
|
|
case 3: if (bgy >= 32) tileIdx += 32 * 32 * 4; break;
|
|
}
|
|
|
|
int tileY = ((this.curLine + vofs) & 0x7) * 4;
|
|
|
|
for (int i = 0; i < 240; i++)
|
|
{
|
|
if (true)
|
|
{
|
|
int bgx = ((i + hofs) & (width - 1)) / 8;
|
|
int tmpTileIdx = tileIdx + ((bgx & 31) * 2);
|
|
if (bgx >= 32) tmpTileIdx += 32 * 32 * 2;
|
|
int tileChar = vram[tmpTileIdx] | (vram[tmpTileIdx + 1] << 8);
|
|
int x = (i + hofs) & 7;
|
|
int y = tileY;
|
|
if ((tileChar & (1 << 10)) != 0) x = 7 - x;
|
|
if ((tileChar & (1 << 11)) != 0) y = 28 - y;
|
|
int lookup = vram[charBase + ((tileChar & 0x3FF) * 32) + y + (x / 2)];
|
|
if ((x & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
int palNum = ((tileChar >> 12) & 0xf) * 16 * 2;
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palNum + lookup * 2] | (palette[palNum + lookup * 2 + 1] << 8)));
|
|
if ((this.blend[i] & this.blendTarget) != 0)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[i];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[i] = pixelColor; this.blend[i] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
private void RenderTextBgBrightInc(int bg)
|
|
{
|
|
byte[] palette = this.memory.PaletteRam;
|
|
byte[] vram = this.memory.VideoRam;
|
|
|
|
byte blendMaskType = (byte)(1 << bg);
|
|
|
|
ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)bg);
|
|
|
|
int width = 0, height = 0;
|
|
switch ((bgcnt >> 14) & 0x3)
|
|
{
|
|
case 0: width = 256; height = 256; break;
|
|
case 1: width = 512; height = 256; break;
|
|
case 2: width = 256; height = 512; break;
|
|
case 3: width = 512; height = 512; break;
|
|
}
|
|
|
|
int screenBase = ((bgcnt >> 8) & 0x1F) * 0x800;
|
|
int charBase = ((bgcnt >> 2) & 0x3) * 0x4000;
|
|
|
|
int hofs = Memory.ReadU16(this.memory.IORam, Memory.BG0HOFS + (uint)bg * 4) & 0x1FF;
|
|
int vofs = Memory.ReadU16(this.memory.IORam, Memory.BG0VOFS + (uint)bg * 4) & 0x1FF;
|
|
|
|
if ((bgcnt & (1 << 7)) != 0)
|
|
{
|
|
// 256 color tiles
|
|
int bgy = ((this.curLine + vofs) & (height - 1)) / 8;
|
|
|
|
int tileIdx = screenBase + (((bgy & 31) * 32) * 2);
|
|
switch ((bgcnt >> 14) & 0x3)
|
|
{
|
|
case 2: if (bgy >= 32) tileIdx += 32 * 32 * 2; break;
|
|
case 3: if (bgy >= 32) tileIdx += 32 * 32 * 4; break;
|
|
}
|
|
|
|
int tileY = ((this.curLine + vofs) & 0x7) * 8;
|
|
|
|
for (int i = 0; i < 240; i++)
|
|
{
|
|
if (true)
|
|
{
|
|
int bgx = ((i + hofs) & (width - 1)) / 8;
|
|
int tmpTileIdx = tileIdx + ((bgx & 31) * 2);
|
|
if (bgx >= 32) tmpTileIdx += 32 * 32 * 2;
|
|
int tileChar = vram[tmpTileIdx] | (vram[tmpTileIdx + 1] << 8);
|
|
int x = (i + hofs) & 7;
|
|
int y = tileY;
|
|
if ((tileChar & (1 << 10)) != 0) x = 7 - x;
|
|
if ((tileChar & (1 << 11)) != 0) y = 56 - y;
|
|
int lookup = vram[charBase + ((tileChar & 0x3FF) * 64) + y + x];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8)));
|
|
uint r = pixelColor & 0xFF;
|
|
uint g = (pixelColor >> 8) & 0xFF;
|
|
uint b = (pixelColor >> 16) & 0xFF;
|
|
r = r + (((0xFF - r) * this.blendY) >> 4);
|
|
g = g + (((0xFF - g) * this.blendY) >> 4);
|
|
b = b + (((0xFF - b) * this.blendY) >> 4);
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
this.scanline[i] = pixelColor; this.blend[i] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 color tiles
|
|
int bgy = ((this.curLine + vofs) & (height - 1)) / 8;
|
|
|
|
int tileIdx = screenBase + (((bgy & 31) * 32) * 2);
|
|
switch ((bgcnt >> 14) & 0x3)
|
|
{
|
|
case 2: if (bgy >= 32) tileIdx += 32 * 32 * 2; break;
|
|
case 3: if (bgy >= 32) tileIdx += 32 * 32 * 4; break;
|
|
}
|
|
|
|
int tileY = ((this.curLine + vofs) & 0x7) * 4;
|
|
|
|
for (int i = 0; i < 240; i++)
|
|
{
|
|
if (true)
|
|
{
|
|
int bgx = ((i + hofs) & (width - 1)) / 8;
|
|
int tmpTileIdx = tileIdx + ((bgx & 31) * 2);
|
|
if (bgx >= 32) tmpTileIdx += 32 * 32 * 2;
|
|
int tileChar = vram[tmpTileIdx] | (vram[tmpTileIdx + 1] << 8);
|
|
int x = (i + hofs) & 7;
|
|
int y = tileY;
|
|
if ((tileChar & (1 << 10)) != 0) x = 7 - x;
|
|
if ((tileChar & (1 << 11)) != 0) y = 28 - y;
|
|
int lookup = vram[charBase + ((tileChar & 0x3FF) * 32) + y + (x / 2)];
|
|
if ((x & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
int palNum = ((tileChar >> 12) & 0xf) * 16 * 2;
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palNum + lookup * 2] | (palette[palNum + lookup * 2 + 1] << 8)));
|
|
uint r = pixelColor & 0xFF;
|
|
uint g = (pixelColor >> 8) & 0xFF;
|
|
uint b = (pixelColor >> 16) & 0xFF;
|
|
r = r + (((0xFF - r) * this.blendY) >> 4);
|
|
g = g + (((0xFF - g) * this.blendY) >> 4);
|
|
b = b + (((0xFF - b) * this.blendY) >> 4);
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
this.scanline[i] = pixelColor; this.blend[i] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
private void RenderTextBgBrightDec(int bg)
|
|
{
|
|
byte[] palette = this.memory.PaletteRam;
|
|
byte[] vram = this.memory.VideoRam;
|
|
|
|
byte blendMaskType = (byte)(1 << bg);
|
|
|
|
ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)bg);
|
|
|
|
int width = 0, height = 0;
|
|
switch ((bgcnt >> 14) & 0x3)
|
|
{
|
|
case 0: width = 256; height = 256; break;
|
|
case 1: width = 512; height = 256; break;
|
|
case 2: width = 256; height = 512; break;
|
|
case 3: width = 512; height = 512; break;
|
|
}
|
|
|
|
int screenBase = ((bgcnt >> 8) & 0x1F) * 0x800;
|
|
int charBase = ((bgcnt >> 2) & 0x3) * 0x4000;
|
|
|
|
int hofs = Memory.ReadU16(this.memory.IORam, Memory.BG0HOFS + (uint)bg * 4) & 0x1FF;
|
|
int vofs = Memory.ReadU16(this.memory.IORam, Memory.BG0VOFS + (uint)bg * 4) & 0x1FF;
|
|
|
|
if ((bgcnt & (1 << 7)) != 0)
|
|
{
|
|
// 256 color tiles
|
|
int bgy = ((this.curLine + vofs) & (height - 1)) / 8;
|
|
|
|
int tileIdx = screenBase + (((bgy & 31) * 32) * 2);
|
|
switch ((bgcnt >> 14) & 0x3)
|
|
{
|
|
case 2: if (bgy >= 32) tileIdx += 32 * 32 * 2; break;
|
|
case 3: if (bgy >= 32) tileIdx += 32 * 32 * 4; break;
|
|
}
|
|
|
|
int tileY = ((this.curLine + vofs) & 0x7) * 8;
|
|
|
|
for (int i = 0; i < 240; i++)
|
|
{
|
|
if (true)
|
|
{
|
|
int bgx = ((i + hofs) & (width - 1)) / 8;
|
|
int tmpTileIdx = tileIdx + ((bgx & 31) * 2);
|
|
if (bgx >= 32) tmpTileIdx += 32 * 32 * 2;
|
|
int tileChar = vram[tmpTileIdx] | (vram[tmpTileIdx + 1] << 8);
|
|
int x = (i + hofs) & 7;
|
|
int y = tileY;
|
|
if ((tileChar & (1 << 10)) != 0) x = 7 - x;
|
|
if ((tileChar & (1 << 11)) != 0) y = 56 - y;
|
|
int lookup = vram[charBase + ((tileChar & 0x3FF) * 64) + y + x];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8)));
|
|
uint r = pixelColor & 0xFF;
|
|
uint g = (pixelColor >> 8) & 0xFF;
|
|
uint b = (pixelColor >> 16) & 0xFF;
|
|
r = r - ((r * this.blendY) >> 4);
|
|
g = g - ((g * this.blendY) >> 4);
|
|
b = b - ((b * this.blendY) >> 4);
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
this.scanline[i] = pixelColor; this.blend[i] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 color tiles
|
|
int bgy = ((this.curLine + vofs) & (height - 1)) / 8;
|
|
|
|
int tileIdx = screenBase + (((bgy & 31) * 32) * 2);
|
|
switch ((bgcnt >> 14) & 0x3)
|
|
{
|
|
case 2: if (bgy >= 32) tileIdx += 32 * 32 * 2; break;
|
|
case 3: if (bgy >= 32) tileIdx += 32 * 32 * 4; break;
|
|
}
|
|
|
|
int tileY = ((this.curLine + vofs) & 0x7) * 4;
|
|
|
|
for (int i = 0; i < 240; i++)
|
|
{
|
|
if (true)
|
|
{
|
|
int bgx = ((i + hofs) & (width - 1)) / 8;
|
|
int tmpTileIdx = tileIdx + ((bgx & 31) * 2);
|
|
if (bgx >= 32) tmpTileIdx += 32 * 32 * 2;
|
|
int tileChar = vram[tmpTileIdx] | (vram[tmpTileIdx + 1] << 8);
|
|
int x = (i + hofs) & 7;
|
|
int y = tileY;
|
|
if ((tileChar & (1 << 10)) != 0) x = 7 - x;
|
|
if ((tileChar & (1 << 11)) != 0) y = 28 - y;
|
|
int lookup = vram[charBase + ((tileChar & 0x3FF) * 32) + y + (x / 2)];
|
|
if ((x & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
int palNum = ((tileChar >> 12) & 0xf) * 16 * 2;
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palNum + lookup * 2] | (palette[palNum + lookup * 2 + 1] << 8)));
|
|
uint r = pixelColor & 0xFF;
|
|
uint g = (pixelColor >> 8) & 0xFF;
|
|
uint b = (pixelColor >> 16) & 0xFF;
|
|
r = r - ((r * this.blendY) >> 4);
|
|
g = g - ((g * this.blendY) >> 4);
|
|
b = b - ((b * this.blendY) >> 4);
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
this.scanline[i] = pixelColor; this.blend[i] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
private void RenderTextBgWindow(int bg)
|
|
{
|
|
byte[] palette = this.memory.PaletteRam;
|
|
byte[] vram = this.memory.VideoRam;
|
|
|
|
byte blendMaskType = (byte)(1 << bg);
|
|
|
|
ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)bg);
|
|
|
|
int width = 0, height = 0;
|
|
switch ((bgcnt >> 14) & 0x3)
|
|
{
|
|
case 0: width = 256; height = 256; break;
|
|
case 1: width = 512; height = 256; break;
|
|
case 2: width = 256; height = 512; break;
|
|
case 3: width = 512; height = 512; break;
|
|
}
|
|
|
|
int screenBase = ((bgcnt >> 8) & 0x1F) * 0x800;
|
|
int charBase = ((bgcnt >> 2) & 0x3) * 0x4000;
|
|
|
|
int hofs = Memory.ReadU16(this.memory.IORam, Memory.BG0HOFS + (uint)bg * 4) & 0x1FF;
|
|
int vofs = Memory.ReadU16(this.memory.IORam, Memory.BG0VOFS + (uint)bg * 4) & 0x1FF;
|
|
|
|
if ((bgcnt & (1 << 7)) != 0)
|
|
{
|
|
// 256 color tiles
|
|
int bgy = ((this.curLine + vofs) & (height - 1)) / 8;
|
|
|
|
int tileIdx = screenBase + (((bgy & 31) * 32) * 2);
|
|
switch ((bgcnt >> 14) & 0x3)
|
|
{
|
|
case 2: if (bgy >= 32) tileIdx += 32 * 32 * 2; break;
|
|
case 3: if (bgy >= 32) tileIdx += 32 * 32 * 4; break;
|
|
}
|
|
|
|
int tileY = ((this.curLine + vofs) & 0x7) * 8;
|
|
|
|
for (int i = 0; i < 240; i++)
|
|
{
|
|
if ((this.windowCover[i] & (1 << bg)) != 0)
|
|
{
|
|
int bgx = ((i + hofs) & (width - 1)) / 8;
|
|
int tmpTileIdx = tileIdx + ((bgx & 31) * 2);
|
|
if (bgx >= 32) tmpTileIdx += 32 * 32 * 2;
|
|
int tileChar = vram[tmpTileIdx] | (vram[tmpTileIdx + 1] << 8);
|
|
int x = (i + hofs) & 7;
|
|
int y = tileY;
|
|
if ((tileChar & (1 << 10)) != 0) x = 7 - x;
|
|
if ((tileChar & (1 << 11)) != 0) y = 56 - y;
|
|
int lookup = vram[charBase + ((tileChar & 0x3FF) * 64) + y + x];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8)));
|
|
this.scanline[i] = pixelColor; this.blend[i] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 color tiles
|
|
int bgy = ((this.curLine + vofs) & (height - 1)) / 8;
|
|
|
|
int tileIdx = screenBase + (((bgy & 31) * 32) * 2);
|
|
switch ((bgcnt >> 14) & 0x3)
|
|
{
|
|
case 2: if (bgy >= 32) tileIdx += 32 * 32 * 2; break;
|
|
case 3: if (bgy >= 32) tileIdx += 32 * 32 * 4; break;
|
|
}
|
|
|
|
int tileY = ((this.curLine + vofs) & 0x7) * 4;
|
|
|
|
for (int i = 0; i < 240; i++)
|
|
{
|
|
if ((this.windowCover[i] & (1 << bg)) != 0)
|
|
{
|
|
int bgx = ((i + hofs) & (width - 1)) / 8;
|
|
int tmpTileIdx = tileIdx + ((bgx & 31) * 2);
|
|
if (bgx >= 32) tmpTileIdx += 32 * 32 * 2;
|
|
int tileChar = vram[tmpTileIdx] | (vram[tmpTileIdx + 1] << 8);
|
|
int x = (i + hofs) & 7;
|
|
int y = tileY;
|
|
if ((tileChar & (1 << 10)) != 0) x = 7 - x;
|
|
if ((tileChar & (1 << 11)) != 0) y = 28 - y;
|
|
int lookup = vram[charBase + ((tileChar & 0x3FF) * 32) + y + (x / 2)];
|
|
if ((x & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
int palNum = ((tileChar >> 12) & 0xf) * 16 * 2;
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palNum + lookup * 2] | (palette[palNum + lookup * 2 + 1] << 8)));
|
|
this.scanline[i] = pixelColor; this.blend[i] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
private void RenderTextBgWindowBlend(int bg)
|
|
{
|
|
byte[] palette = this.memory.PaletteRam;
|
|
byte[] vram = this.memory.VideoRam;
|
|
|
|
byte blendMaskType = (byte)(1 << bg);
|
|
|
|
ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)bg);
|
|
|
|
int width = 0, height = 0;
|
|
switch ((bgcnt >> 14) & 0x3)
|
|
{
|
|
case 0: width = 256; height = 256; break;
|
|
case 1: width = 512; height = 256; break;
|
|
case 2: width = 256; height = 512; break;
|
|
case 3: width = 512; height = 512; break;
|
|
}
|
|
|
|
int screenBase = ((bgcnt >> 8) & 0x1F) * 0x800;
|
|
int charBase = ((bgcnt >> 2) & 0x3) * 0x4000;
|
|
|
|
int hofs = Memory.ReadU16(this.memory.IORam, Memory.BG0HOFS + (uint)bg * 4) & 0x1FF;
|
|
int vofs = Memory.ReadU16(this.memory.IORam, Memory.BG0VOFS + (uint)bg * 4) & 0x1FF;
|
|
|
|
if ((bgcnt & (1 << 7)) != 0)
|
|
{
|
|
// 256 color tiles
|
|
int bgy = ((this.curLine + vofs) & (height - 1)) / 8;
|
|
|
|
int tileIdx = screenBase + (((bgy & 31) * 32) * 2);
|
|
switch ((bgcnt >> 14) & 0x3)
|
|
{
|
|
case 2: if (bgy >= 32) tileIdx += 32 * 32 * 2; break;
|
|
case 3: if (bgy >= 32) tileIdx += 32 * 32 * 4; break;
|
|
}
|
|
|
|
int tileY = ((this.curLine + vofs) & 0x7) * 8;
|
|
|
|
for (int i = 0; i < 240; i++)
|
|
{
|
|
if ((this.windowCover[i] & (1 << bg)) != 0)
|
|
{
|
|
int bgx = ((i + hofs) & (width - 1)) / 8;
|
|
int tmpTileIdx = tileIdx + ((bgx & 31) * 2);
|
|
if (bgx >= 32) tmpTileIdx += 32 * 32 * 2;
|
|
int tileChar = vram[tmpTileIdx] | (vram[tmpTileIdx + 1] << 8);
|
|
int x = (i + hofs) & 7;
|
|
int y = tileY;
|
|
if ((tileChar & (1 << 10)) != 0) x = 7 - x;
|
|
if ((tileChar & (1 << 11)) != 0) y = 56 - y;
|
|
int lookup = vram[charBase + ((tileChar & 0x3FF) * 64) + y + x];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i] & (1 << 5)) != 0)
|
|
{
|
|
if ((this.blend[i] & this.blendTarget) != 0)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[i];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
}
|
|
this.scanline[i] = pixelColor; this.blend[i] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 color tiles
|
|
int bgy = ((this.curLine + vofs) & (height - 1)) / 8;
|
|
|
|
int tileIdx = screenBase + (((bgy & 31) * 32) * 2);
|
|
switch ((bgcnt >> 14) & 0x3)
|
|
{
|
|
case 2: if (bgy >= 32) tileIdx += 32 * 32 * 2; break;
|
|
case 3: if (bgy >= 32) tileIdx += 32 * 32 * 4; break;
|
|
}
|
|
|
|
int tileY = ((this.curLine + vofs) & 0x7) * 4;
|
|
|
|
for (int i = 0; i < 240; i++)
|
|
{
|
|
if ((this.windowCover[i] & (1 << bg)) != 0)
|
|
{
|
|
int bgx = ((i + hofs) & (width - 1)) / 8;
|
|
int tmpTileIdx = tileIdx + ((bgx & 31) * 2);
|
|
if (bgx >= 32) tmpTileIdx += 32 * 32 * 2;
|
|
int tileChar = vram[tmpTileIdx] | (vram[tmpTileIdx + 1] << 8);
|
|
int x = (i + hofs) & 7;
|
|
int y = tileY;
|
|
if ((tileChar & (1 << 10)) != 0) x = 7 - x;
|
|
if ((tileChar & (1 << 11)) != 0) y = 28 - y;
|
|
int lookup = vram[charBase + ((tileChar & 0x3FF) * 32) + y + (x / 2)];
|
|
if ((x & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
int palNum = ((tileChar >> 12) & 0xf) * 16 * 2;
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palNum + lookup * 2] | (palette[palNum + lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i] & (1 << 5)) != 0)
|
|
{
|
|
if ((this.blend[i] & this.blendTarget) != 0)
|
|
{
|
|
uint r = ((pixelColor & 0xFF) * this.blendA) >> 4;
|
|
uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4;
|
|
uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4;
|
|
uint sourceValue = this.scanline[i];
|
|
r += ((sourceValue & 0xFF) * this.blendB) >> 4;
|
|
g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4;
|
|
b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4;
|
|
if (r > 0xff) r = 0xff;
|
|
if (g > 0xff) g = 0xff;
|
|
if (b > 0xff) b = 0xff;
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
}
|
|
this.scanline[i] = pixelColor; this.blend[i] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
private void RenderTextBgWindowBrightInc(int bg)
|
|
{
|
|
byte[] palette = this.memory.PaletteRam;
|
|
byte[] vram = this.memory.VideoRam;
|
|
|
|
byte blendMaskType = (byte)(1 << bg);
|
|
|
|
ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)bg);
|
|
|
|
int width = 0, height = 0;
|
|
switch ((bgcnt >> 14) & 0x3)
|
|
{
|
|
case 0: width = 256; height = 256; break;
|
|
case 1: width = 512; height = 256; break;
|
|
case 2: width = 256; height = 512; break;
|
|
case 3: width = 512; height = 512; break;
|
|
}
|
|
|
|
int screenBase = ((bgcnt >> 8) & 0x1F) * 0x800;
|
|
int charBase = ((bgcnt >> 2) & 0x3) * 0x4000;
|
|
|
|
int hofs = Memory.ReadU16(this.memory.IORam, Memory.BG0HOFS + (uint)bg * 4) & 0x1FF;
|
|
int vofs = Memory.ReadU16(this.memory.IORam, Memory.BG0VOFS + (uint)bg * 4) & 0x1FF;
|
|
|
|
if ((bgcnt & (1 << 7)) != 0)
|
|
{
|
|
// 256 color tiles
|
|
int bgy = ((this.curLine + vofs) & (height - 1)) / 8;
|
|
|
|
int tileIdx = screenBase + (((bgy & 31) * 32) * 2);
|
|
switch ((bgcnt >> 14) & 0x3)
|
|
{
|
|
case 2: if (bgy >= 32) tileIdx += 32 * 32 * 2; break;
|
|
case 3: if (bgy >= 32) tileIdx += 32 * 32 * 4; break;
|
|
}
|
|
|
|
int tileY = ((this.curLine + vofs) & 0x7) * 8;
|
|
|
|
for (int i = 0; i < 240; i++)
|
|
{
|
|
if ((this.windowCover[i] & (1 << bg)) != 0)
|
|
{
|
|
int bgx = ((i + hofs) & (width - 1)) / 8;
|
|
int tmpTileIdx = tileIdx + ((bgx & 31) * 2);
|
|
if (bgx >= 32) tmpTileIdx += 32 * 32 * 2;
|
|
int tileChar = vram[tmpTileIdx] | (vram[tmpTileIdx + 1] << 8);
|
|
int x = (i + hofs) & 7;
|
|
int y = tileY;
|
|
if ((tileChar & (1 << 10)) != 0) x = 7 - x;
|
|
if ((tileChar & (1 << 11)) != 0) y = 56 - y;
|
|
int lookup = vram[charBase + ((tileChar & 0x3FF) * 64) + y + x];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i] & (1 << 5)) != 0)
|
|
{
|
|
uint r = pixelColor & 0xFF;
|
|
uint g = (pixelColor >> 8) & 0xFF;
|
|
uint b = (pixelColor >> 16) & 0xFF;
|
|
r = r + (((0xFF - r) * this.blendY) >> 4);
|
|
g = g + (((0xFF - g) * this.blendY) >> 4);
|
|
b = b + (((0xFF - b) * this.blendY) >> 4);
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[i] = pixelColor; this.blend[i] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 color tiles
|
|
int bgy = ((this.curLine + vofs) & (height - 1)) / 8;
|
|
|
|
int tileIdx = screenBase + (((bgy & 31) * 32) * 2);
|
|
switch ((bgcnt >> 14) & 0x3)
|
|
{
|
|
case 2: if (bgy >= 32) tileIdx += 32 * 32 * 2; break;
|
|
case 3: if (bgy >= 32) tileIdx += 32 * 32 * 4; break;
|
|
}
|
|
|
|
int tileY = ((this.curLine + vofs) & 0x7) * 4;
|
|
|
|
for (int i = 0; i < 240; i++)
|
|
{
|
|
if ((this.windowCover[i] & (1 << bg)) != 0)
|
|
{
|
|
int bgx = ((i + hofs) & (width - 1)) / 8;
|
|
int tmpTileIdx = tileIdx + ((bgx & 31) * 2);
|
|
if (bgx >= 32) tmpTileIdx += 32 * 32 * 2;
|
|
int tileChar = vram[tmpTileIdx] | (vram[tmpTileIdx + 1] << 8);
|
|
int x = (i + hofs) & 7;
|
|
int y = tileY;
|
|
if ((tileChar & (1 << 10)) != 0) x = 7 - x;
|
|
if ((tileChar & (1 << 11)) != 0) y = 28 - y;
|
|
int lookup = vram[charBase + ((tileChar & 0x3FF) * 32) + y + (x / 2)];
|
|
if ((x & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
int palNum = ((tileChar >> 12) & 0xf) * 16 * 2;
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palNum + lookup * 2] | (palette[palNum + lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i] & (1 << 5)) != 0)
|
|
{
|
|
uint r = pixelColor & 0xFF;
|
|
uint g = (pixelColor >> 8) & 0xFF;
|
|
uint b = (pixelColor >> 16) & 0xFF;
|
|
r = r + (((0xFF - r) * this.blendY) >> 4);
|
|
g = g + (((0xFF - g) * this.blendY) >> 4);
|
|
b = b + (((0xFF - b) * this.blendY) >> 4);
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[i] = pixelColor; this.blend[i] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
private void RenderTextBgWindowBrightDec(int bg)
|
|
{
|
|
byte[] palette = this.memory.PaletteRam;
|
|
byte[] vram = this.memory.VideoRam;
|
|
|
|
byte blendMaskType = (byte)(1 << bg);
|
|
|
|
ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)bg);
|
|
|
|
int width = 0, height = 0;
|
|
switch ((bgcnt >> 14) & 0x3)
|
|
{
|
|
case 0: width = 256; height = 256; break;
|
|
case 1: width = 512; height = 256; break;
|
|
case 2: width = 256; height = 512; break;
|
|
case 3: width = 512; height = 512; break;
|
|
}
|
|
|
|
int screenBase = ((bgcnt >> 8) & 0x1F) * 0x800;
|
|
int charBase = ((bgcnt >> 2) & 0x3) * 0x4000;
|
|
|
|
int hofs = Memory.ReadU16(this.memory.IORam, Memory.BG0HOFS + (uint)bg * 4) & 0x1FF;
|
|
int vofs = Memory.ReadU16(this.memory.IORam, Memory.BG0VOFS + (uint)bg * 4) & 0x1FF;
|
|
|
|
if ((bgcnt & (1 << 7)) != 0)
|
|
{
|
|
// 256 color tiles
|
|
int bgy = ((this.curLine + vofs) & (height - 1)) / 8;
|
|
|
|
int tileIdx = screenBase + (((bgy & 31) * 32) * 2);
|
|
switch ((bgcnt >> 14) & 0x3)
|
|
{
|
|
case 2: if (bgy >= 32) tileIdx += 32 * 32 * 2; break;
|
|
case 3: if (bgy >= 32) tileIdx += 32 * 32 * 4; break;
|
|
}
|
|
|
|
int tileY = ((this.curLine + vofs) & 0x7) * 8;
|
|
|
|
for (int i = 0; i < 240; i++)
|
|
{
|
|
if ((this.windowCover[i] & (1 << bg)) != 0)
|
|
{
|
|
int bgx = ((i + hofs) & (width - 1)) / 8;
|
|
int tmpTileIdx = tileIdx + ((bgx & 31) * 2);
|
|
if (bgx >= 32) tmpTileIdx += 32 * 32 * 2;
|
|
int tileChar = vram[tmpTileIdx] | (vram[tmpTileIdx + 1] << 8);
|
|
int x = (i + hofs) & 7;
|
|
int y = tileY;
|
|
if ((tileChar & (1 << 10)) != 0) x = 7 - x;
|
|
if ((tileChar & (1 << 11)) != 0) y = 56 - y;
|
|
int lookup = vram[charBase + ((tileChar & 0x3FF) * 64) + y + x];
|
|
if (lookup != 0)
|
|
{
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i] & (1 << 5)) != 0)
|
|
{
|
|
uint r = pixelColor & 0xFF;
|
|
uint g = (pixelColor >> 8) & 0xFF;
|
|
uint b = (pixelColor >> 16) & 0xFF;
|
|
r = r - ((r * this.blendY) >> 4);
|
|
g = g - ((g * this.blendY) >> 4);
|
|
b = b - ((b * this.blendY) >> 4);
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[i] = pixelColor; this.blend[i] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 color tiles
|
|
int bgy = ((this.curLine + vofs) & (height - 1)) / 8;
|
|
|
|
int tileIdx = screenBase + (((bgy & 31) * 32) * 2);
|
|
switch ((bgcnt >> 14) & 0x3)
|
|
{
|
|
case 2: if (bgy >= 32) tileIdx += 32 * 32 * 2; break;
|
|
case 3: if (bgy >= 32) tileIdx += 32 * 32 * 4; break;
|
|
}
|
|
|
|
int tileY = ((this.curLine + vofs) & 0x7) * 4;
|
|
|
|
for (int i = 0; i < 240; i++)
|
|
{
|
|
if ((this.windowCover[i] & (1 << bg)) != 0)
|
|
{
|
|
int bgx = ((i + hofs) & (width - 1)) / 8;
|
|
int tmpTileIdx = tileIdx + ((bgx & 31) * 2);
|
|
if (bgx >= 32) tmpTileIdx += 32 * 32 * 2;
|
|
int tileChar = vram[tmpTileIdx] | (vram[tmpTileIdx + 1] << 8);
|
|
int x = (i + hofs) & 7;
|
|
int y = tileY;
|
|
if ((tileChar & (1 << 10)) != 0) x = 7 - x;
|
|
if ((tileChar & (1 << 11)) != 0) y = 28 - y;
|
|
int lookup = vram[charBase + ((tileChar & 0x3FF) * 32) + y + (x / 2)];
|
|
if ((x & 1) == 0)
|
|
{
|
|
lookup &= 0xf;
|
|
}
|
|
else
|
|
{
|
|
lookup >>= 4;
|
|
}
|
|
if (lookup != 0)
|
|
{
|
|
int palNum = ((tileChar >> 12) & 0xf) * 16 * 2;
|
|
uint pixelColor = Renderer.GbaTo32((ushort)(palette[palNum + lookup * 2] | (palette[palNum + lookup * 2 + 1] << 8)));
|
|
if ((this.windowCover[i] & (1 << 5)) != 0)
|
|
{
|
|
uint r = pixelColor & 0xFF;
|
|
uint g = (pixelColor >> 8) & 0xFF;
|
|
uint b = (pixelColor >> 16) & 0xFF;
|
|
r = r - ((r * this.blendY) >> 4);
|
|
g = g - ((g * this.blendY) >> 4);
|
|
b = b - ((b * this.blendY) >> 4);
|
|
pixelColor = r | (g << 8) | (b << 16);
|
|
}
|
|
this.scanline[i] = pixelColor; this.blend[i] = blendMaskType;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endregion Text Bg
|
|
}
|
|
}
|