mirror of
https://git.checksum.fail/alec/bahamut.git
synced 2026-05-26 11:58:55 +00:00
1397 lines
38 KiB
HolyC
1397 lines
38 KiB
HolyC
U16 *vram = MAlloc(0x8000*2);
|
|
|
|
U16 *cgram = MAlloc(0x100*2);
|
|
|
|
U16 *oam = MAlloc(0x100*2);
|
|
U16 *highOam = MAlloc(0x10*2);
|
|
|
|
U8 *spriteLineBuffer = MAlloc(256);
|
|
U8 *spritePrioBuffer = MAlloc(256);
|
|
|
|
I32 *mode7Xcoords = MAlloc(256*4);
|
|
I32 *mode7Ycoords = MAlloc(256*4);
|
|
|
|
U16 *pixelOutput = MAlloc((512*3*240)*2);
|
|
|
|
I32 layersPerMode[120] = {
|
|
4, 0, 1, 4, 0, 1, 4, 2, 3, 4, 2, 3,
|
|
4, 0, 1, 4, 0, 1, 4, 2, 4, 2, 5, 5,
|
|
4, 0, 4, 1, 4, 0, 4, 1, 5, 5, 5, 5,
|
|
4, 0, 4, 1, 4, 0, 4, 1, 5, 5, 5, 5,
|
|
4, 0, 4, 1, 4, 0, 4, 1, 5, 5, 5, 5,
|
|
4, 0, 4, 1, 4, 0, 4, 1, 5, 5, 5, 5,
|
|
4, 0, 4, 4, 0, 4, 5, 5, 5, 5, 5, 5,
|
|
4, 4, 4, 0, 4, 5, 5, 5, 5, 5, 5, 5,
|
|
2, 4, 0, 1, 4, 0, 1, 4, 2, 4, 5, 5,
|
|
4, 4, 1, 4, 0, 4, 1, 5, 5, 5, 5, 5
|
|
};
|
|
|
|
I32 prioPerMode[120] = {
|
|
3, 1, 1, 2, 0, 0, 1, 1, 1, 0, 0, 0,
|
|
3, 1, 1, 2, 0, 0, 1, 1, 0, 0, 5, 5,
|
|
3, 1, 2, 1, 1, 0, 0, 0, 5, 5, 5, 5,
|
|
3, 1, 2, 1, 1, 0, 0, 0, 5, 5, 5, 5,
|
|
3, 1, 2, 1, 1, 0, 0, 0, 5, 5, 5, 5,
|
|
3, 1, 2, 1, 1, 0, 0, 0, 5, 5, 5, 5,
|
|
3, 1, 2, 1, 0, 0, 5, 5, 5, 5, 5, 5,
|
|
3, 2, 1, 0, 0, 5, 5, 5, 5, 5, 5, 5,
|
|
1, 3, 1, 1, 2, 0, 0, 1, 0, 0, 5, 5,
|
|
3, 2, 1, 1, 0, 0, 0, 5, 5, 5, 5, 5
|
|
};
|
|
|
|
I32 bitPerMode[40] = {
|
|
2, 2, 2, 2,
|
|
4, 4, 2, 5,
|
|
4, 4, 5, 5,
|
|
8, 4, 5, 5,
|
|
8, 2, 5, 5,
|
|
4, 2, 5, 5,
|
|
4, 5, 5, 5,
|
|
8, 5, 5, 5,
|
|
4, 4, 2, 5,
|
|
8, 7, 5, 5
|
|
};
|
|
|
|
I32 layercountPerMode[10] = {12, 10, 8, 8, 8, 8, 6, 5, 10, 7};
|
|
|
|
F64 brightnessMults[16] = {
|
|
0.1, 0.5, 1.1, 1.6, 2.2, 2.7, 3.3, 3.8, 4.4, 4.9, 5.5, 6, 6.6, 7.1, 7.6, 8.2
|
|
;
|
|
|
|
I32 spriteTileOffsets[64] = {
|
|
0, 1, 2, 3, 4, 5, 6, 7,
|
|
16, 17, 18, 19, 20, 21, 22, 23,
|
|
32, 33, 34, 35, 36, 37, 38, 39,
|
|
48, 49, 50, 51, 52, 53, 54, 55,
|
|
64, 65, 66, 67, 68, 69, 70, 71,
|
|
80, 81, 82, 83, 84, 85, 86, 87,
|
|
96, 97, 98, 99, 100, 101, 102, 103,
|
|
112, 113, 114, 115, 116, 117, 118, 119
|
|
};
|
|
|
|
I32 spriteSizes[16] = {
|
|
1, 1, 1, 2, 2, 4, 2, 2,
|
|
2, 4, 8, 4, 8, 8, 4, 4
|
|
};
|
|
|
|
|
|
U0 ppu_reset()
|
|
{
|
|
I32 ii;
|
|
/*
|
|
clearArray(vram);
|
|
clearArray(cgram);
|
|
clearArray(oam);
|
|
clearArray(highOam);
|
|
|
|
clearArray(spriteLineBuffer);
|
|
clearArray(spritePrioBuffer);
|
|
|
|
clearArray(pixelOutput);
|
|
|
|
clearArray(mode7Xcoords);
|
|
clearArray(mode7Ycoords);
|
|
*/
|
|
|
|
MemSet(vram, NULL, 0x8000*2);
|
|
MemSet(cgram, NULL, 0x100*2);
|
|
MemSet(oam, NULL, 0x100*2);
|
|
MemSet(highOam, NULL,0x10*2);
|
|
MemSet(spriteLineBuffer, NULL, 256);
|
|
MemSet(spritePrioBuffer, NULL, 256);
|
|
|
|
MemSet(mode7Xcoords, NULL, 256*4);
|
|
MemSet(mode7Ycoords, NULL, 256*4);
|
|
|
|
MemSet(pixelOutput, NULL, ((512*3*240)*2));
|
|
|
|
cgramAdr = 0;
|
|
cgramSecond = FALSE;
|
|
cgramBuffer = 0;
|
|
|
|
vramInc = 0;
|
|
vramRemap = 0;
|
|
vramIncOnHigh = FALSE;
|
|
vramAdr = 0;
|
|
vramReadBuffer = 0;
|
|
|
|
for (ii=0;ii<4;ii++)
|
|
{
|
|
tilemapWider[ii]=FALSE;
|
|
tilemapHigher[ii]=FALSE;
|
|
tilemapAdr[ii]=0;
|
|
tileAdr[ii]=0;
|
|
}
|
|
|
|
for (ii=0;ii<5;ii++)
|
|
{
|
|
bgHoff[ii]=0;
|
|
bgVoff[ii]=0;
|
|
}
|
|
offPrev1 = 0;
|
|
offPrev2 = 0;
|
|
|
|
mode = 0;
|
|
layer3Prio = FALSE;
|
|
for (ii=0;ii<4;ii++)
|
|
{
|
|
bigTiles[ii]=FALSE;
|
|
}
|
|
|
|
for (ii=0;ii<5;ii++)
|
|
{
|
|
mosaicEnabled[ii]=FALSE;
|
|
}
|
|
mosaicSize = 1;
|
|
mosaicStartLine = 1;
|
|
|
|
for (ii=0;ii<5;ii++)
|
|
{
|
|
mainScreenEnabled[ii]=FALSE;
|
|
subScreenEnabled[ii]=FALSE;
|
|
}
|
|
|
|
forcedBlank = TRUE;
|
|
brightness = 0;
|
|
|
|
oamAdr = 0;
|
|
oamRegAdr = 0;
|
|
oamInHigh = FALSE;
|
|
oamRegInHigh = FALSE;
|
|
objPriority = FALSE;
|
|
oamSecond = FALSE;
|
|
oamBuffer = FALSE;
|
|
|
|
sprAdr1 = 0;
|
|
sprAdr2 = 0;
|
|
objSize = 0;
|
|
|
|
rangeOver = FALSE;
|
|
timeOver = FALSE;
|
|
|
|
mode7ExBg = FALSE;
|
|
pseudoHires = FALSE;
|
|
overscan = FALSE;
|
|
objInterlace = FALSE;
|
|
interlace = FALSE;
|
|
|
|
frameOverscan = FALSE;
|
|
frameInterlace = FALSE;
|
|
evenFrame = FALSE;
|
|
|
|
latchedHpos = 0;
|
|
latchedVpos = 0;
|
|
latchHsecond = FALSE;
|
|
latchVsecond = FALSE;
|
|
countersLatched = FALSE;
|
|
|
|
mode7Hoff = 0;
|
|
mode7Voff = 0;
|
|
mode7A = 0;
|
|
mode7B = 0;
|
|
mode7C = 0;
|
|
mode7D = 0;
|
|
mode7X = 0;
|
|
mode7Y = 0;
|
|
mode7Prev = 0;
|
|
multResult = 0;
|
|
|
|
mode7LargeField = FALSE;
|
|
mode7Char0fill = FALSE;
|
|
mode7FlipX = FALSE;
|
|
mode7FlipY = FALSE;
|
|
|
|
for (ii=0;ii<6;ii++)
|
|
{
|
|
window1Inversed[ii]=FALSE;
|
|
window1Enabled[ii]=FALSE;
|
|
window2Inversed[ii]=FALSE;
|
|
window2Enabled[ii]=FALSE;
|
|
windowMaskLogic[ii]=0;
|
|
}
|
|
|
|
window1Left = 0;
|
|
window1Right = 0;
|
|
window2Left = 0;
|
|
window2Right = 0;
|
|
|
|
for (ii=0;ii<5;ii++)
|
|
{
|
|
mainScreenWindow[ii]=FALSE;
|
|
subScreenWindow[ii]=FALSE;
|
|
}
|
|
|
|
colorClip = 0;
|
|
preventMath = 0;
|
|
addSub = FALSE;
|
|
directColor = FALSE;
|
|
|
|
subtractColors = FALSE;
|
|
halfColors = FALSE;
|
|
|
|
for (ii=0;ii<6;ii++)
|
|
{
|
|
mathEnabled[ii]=FALSE;
|
|
}
|
|
fixedColorB = 0;
|
|
fixedColorG = 0;
|
|
fixedColorR = 0;
|
|
|
|
for (ii=0;ii<4;ii++)
|
|
{
|
|
tilemapBuffer[ii]=0;
|
|
tileBufferP1[ii]=0;
|
|
tileBufferP2[ii]=0;
|
|
tileBufferP3[ii]=0;
|
|
tileBufferP4[ii]=0;
|
|
lastTileFetchedX[ii]=-1;
|
|
lastTileFetchedY[ii]=-1;
|
|
}
|
|
optHorBuffer[0] = 0;
|
|
optHorBuffer[1] = 0;
|
|
optVerBuffer[0] = 0;
|
|
optVerBuffer[1] = 0;
|
|
lastOrigTileX[0] = -1;
|
|
lastOrigTileX[1] = -1;
|
|
}
|
|
|
|
ppu_reset;
|
|
|
|
// TODO: better mode 2/4/6 offset-per-tile (especially mode 6), color math
|
|
// when subscreen is visible (especially how to handle the subscreen pixels),
|
|
// mosaic with hires/interlace, mosaic on mode 7, rectangular sprites,
|
|
// oddities with sprite X-position being -256, mosaic with offset-per-tile,
|
|
// offset-per-tile with interlace
|
|
|
|
U0 setPixels(U8 *arr) {
|
|
// arr = 32bpp framebuffer
|
|
I32 i, x, y, ind, r, g, b, addY;
|
|
|
|
/*
|
|
if(!frameOverscan) {
|
|
// clear the top 8 and bottom 8 lines to transarent
|
|
for(i = 0; i < 512*16; i++) {
|
|
x = i % 512;
|
|
y = (i >> 9);
|
|
ind = (y * 512 + x) * 4;
|
|
arr[ind + 3] = 0;
|
|
}
|
|
for(i = 0; i < 512*16; i++) {
|
|
x = i % 512;
|
|
y = (i >> 9);
|
|
ind = ((y + 464) * 512 + x) * 4;
|
|
arr[ind + 3] = 0;
|
|
}
|
|
}
|
|
*/
|
|
|
|
addY = cond(frameOverscan, 0, 14);
|
|
|
|
for(i = 512; i < 512 * cond(frameOverscan, 240, 225); i++) {
|
|
x = i % 512;
|
|
y = (i >> 9) * 2;
|
|
ind = ((y + addY) * 512 + x) * 4;
|
|
r = pixelOutput[i * 3];
|
|
g = pixelOutput[i * 3 + 1];
|
|
b = pixelOutput[i * 3 + 2];
|
|
if(!frameInterlace || evenFrame) {
|
|
arr[ind] = b;
|
|
arr[ind + 1] = g;
|
|
arr[ind + 2] = r;
|
|
arr[ind + 3] = 255;
|
|
}
|
|
ind += 512 * 4;
|
|
if(!frameInterlace || !evenFrame) {
|
|
arr[ind] = b;
|
|
arr[ind + 1] = g;
|
|
arr[ind + 2] = r;
|
|
arr[ind + 3] = 255;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
U0 evaluateSprites(I32 line) {
|
|
I32 spriteCount, sliverCount, index, i, j, k, x, y, tile, ex, big, size, sprRow;
|
|
I32 adr, tileRow, tileColumn, tileNum, tileP1, tileP2, shift, tileData, color, xInd;
|
|
spriteCount = 0;
|
|
sliverCount = 0;
|
|
// search through oam, backwards
|
|
index = cond(objPriority, ((oamAdr & 0xfe) - 2) & 0xff, 254);
|
|
for(i = 0; i < 128; i++) {
|
|
x = oam[index] & 0xff;
|
|
y = (oam[index] & 0xff00) >> 8;
|
|
tile = oam[index + 1] & 0xff;
|
|
ex = (oam[index + 1] & 0xff00) >> 8;
|
|
x |= (highOam[index >> 4] >> (index & 0xf) & 0x1) << 8;
|
|
big = (highOam[index >> 4] >> (index & 0xf) & 0x2) > 0;
|
|
x = cond(x > 255, -(512 - x), x);
|
|
|
|
// check for being on this line
|
|
size = spriteSizes[objSize + (cond(big, 8, 0))];
|
|
sprRow = line - y;
|
|
if(sprRow < 0 || sprRow >= size * (cond(objInterlace, 4, 8))) {
|
|
// check if it is a sprite from the top of the screen
|
|
sprRow = line + (256 - y);
|
|
}
|
|
if(
|
|
sprRow >= 0 && sprRow < size * (cond(objInterlace, 4, 8)) &&
|
|
x > -(size * 8)
|
|
) {
|
|
// in range, show it
|
|
if(spriteCount == 32) {
|
|
// this would be the 33th sprite, exit the loop
|
|
rangeOver = TRUE;
|
|
break;
|
|
}
|
|
sprRow = cond(objInterlace, sprRow * 2 + (
|
|
cond(evenFrame, 1, 0)
|
|
), sprRow);
|
|
// fetch the tile(s)
|
|
adr = sprAdr1 + (cond((ex & 0x1) > 0, sprAdr2, 0));
|
|
sprRow = cond(((ex & 0x80) > 0), (size * 8) - 1 - sprRow, sprRow);
|
|
tileRow = sprRow >> 3;
|
|
sprRow &= 0x7;
|
|
for(k = 0; k < size; k++) {
|
|
if((x + k * 8) > -7 && (x + k * 8) < 256) {
|
|
if(sliverCount == 34) {
|
|
sliverCount = 35;
|
|
break; // exit tile fetch loop, maximum slivers
|
|
}
|
|
tileColumn = cond(((ex & 0x40) > 0), size - 1 - k, k);
|
|
tileNum = tile + spriteTileOffsets[
|
|
tileRow * 8 + tileColumn
|
|
];
|
|
tileNum &= 0xff;
|
|
tileP1 = vram[
|
|
(adr + tileNum * 16 + sprRow) & 0x7fff
|
|
];
|
|
tileP2 = vram[
|
|
(adr + tileNum * 16 + sprRow + 8) & 0x7fff
|
|
];
|
|
// and draw it in the line buffer
|
|
for(j = 0; j < 8; j++) {
|
|
shift = cond(((ex & 0x40) > 0), j, 7 - j);
|
|
tileData = (tileP1 >> shift) & 0x1;
|
|
tileData |= ((tileP1 >> (8 + shift)) & 0x1) << 1;
|
|
tileData |= ((tileP2 >> shift) & 0x1) << 2;
|
|
tileData |= ((tileP2 >> (8 + shift)) & 0x1) << 3;
|
|
color = tileData + 16 * ((ex & 0xe) >> 1);
|
|
xInd = x + k * 8 + j;
|
|
if(tileData > 0 && xInd < 256 && xInd >= 0) {
|
|
spriteLineBuffer[xInd] = 0x80 + color;
|
|
spritePrioBuffer[xInd] = (ex & 0x30) >> 4;
|
|
}
|
|
}
|
|
sliverCount++;
|
|
}
|
|
}
|
|
if(sliverCount == 35) {
|
|
// we exited the tile fetch loop because we reached max slivers
|
|
// se we can stop evaluating sprites
|
|
timeOver = TRUE;
|
|
break;
|
|
}
|
|
|
|
spriteCount++;
|
|
}
|
|
|
|
index = (index - 2) & 0xff;
|
|
}
|
|
}
|
|
|
|
U0 generateMode7Coords(I32 y) {
|
|
I32 i, rY, clippedH, clippedV, lineStartX, lineStartY;
|
|
rY = cond(mode7FlipY, 255 - y, y);
|
|
|
|
clippedH = mode7Hoff - mode7X;
|
|
clippedH = cond((clippedH & 0x2000) > 0, (clippedH | ~0x3ff), (clippedH & 0x3ff));
|
|
clippedV = mode7Voff - mode7Y;
|
|
clippedV = cond((clippedV & 0x2000) > 0, (clippedV | ~0x3ff), (clippedV & 0x3ff));
|
|
|
|
lineStartX = (
|
|
((mode7A * clippedH) & ~63) +
|
|
((mode7B * rY) & ~63) + ((mode7B * clippedV) & ~63) +
|
|
(mode7X << 8)
|
|
);
|
|
lineStartY = (
|
|
((mode7C * clippedH) & ~63) +
|
|
((mode7D * rY) & ~63) + ((mode7D * clippedV) & ~63) +
|
|
(mode7Y << 8)
|
|
);
|
|
|
|
mode7Xcoords[0] = lineStartX;
|
|
mode7Ycoords[0] = lineStartY;
|
|
|
|
for(i = 1; i < 256; i++) {
|
|
mode7Xcoords[i] = mode7Xcoords[i - 1] + mode7A;
|
|
mode7Ycoords[i] = mode7Ycoords[i - 1] + mode7C;
|
|
}
|
|
}
|
|
|
|
Bool getWindowState(I32 x, I32 l) {
|
|
Bool test, w1test, w2test;
|
|
if(!window1Enabled[l] && !window2Enabled[l]) {
|
|
return FALSE;
|
|
}
|
|
if(window1Enabled[l] && !window2Enabled[l]) {
|
|
test = x >= window1Left && x <= window1Right;
|
|
return cond(window1Inversed[l], !test, test);
|
|
}
|
|
if(!window1Enabled[l] && window2Enabled[l]) {
|
|
test = x >= window2Left && x <= window2Right;
|
|
return cond(window2Inversed[l], !test, test);
|
|
}
|
|
// both window enabled
|
|
w1test = x >= window1Left && x <= window1Right;
|
|
w1test = cond(window1Inversed[l], !w1test, w1test);
|
|
w2test = x >= window2Left && x <= window2Right;
|
|
w2test = cond(window2Inversed[l], !w2test, w2test);
|
|
switch(windowMaskLogic[l]) {
|
|
case 0: {
|
|
return w1test || w2test;
|
|
}
|
|
case 1: {
|
|
return w1test && w2test;
|
|
}
|
|
case 2: {
|
|
return w1test != w2test;
|
|
}
|
|
case 3: {
|
|
return w1test == w2test;
|
|
}
|
|
}
|
|
}
|
|
|
|
Bool getMathEnabled(I32 x, I32 l, I32 pal) {
|
|
if(
|
|
preventMath == 3 ||
|
|
(preventMath == 2 && getWindowState(x, 5)) ||
|
|
(preventMath == 1 && !getWindowState(x, 5))
|
|
) {
|
|
return FALSE;
|
|
}
|
|
if(mathEnabled[l] && (l != 4 || pal >= 0xc0)) {
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
U0 fetchTileInBuffer(I32 x, I32 y, I32 l, I32 offset) {
|
|
I32 rx, ry, adr, yFlip, xFlip, yRow, tileNum, bits;
|
|
Bool useXbig;
|
|
rx = x;
|
|
ry = y;
|
|
useXbig = bigTiles[l] | mode == 5 | mode == 6;
|
|
x >>= cond(useXbig, 1, 0);
|
|
y >>= cond(bigTiles[l], 1, 0);
|
|
|
|
adr = tilemapAdr[l] + (
|
|
((y & 0xff) >> 3) << 5 | ((x & 0xff) >> 3)
|
|
);
|
|
adr += cond(((x & 0x100) > 0 && tilemapWider[l]), 1024, 0);
|
|
adr += cond(((y & 0x100) > 0 && tilemapHigher[l]), (
|
|
cond(tilemapWider[l], 2048, 1024)
|
|
), 0);
|
|
tilemapBuffer[l] = vram[adr & 0x7fff];
|
|
if(offset) {
|
|
// for offset-per-tile, we only nees the tilemap byte,
|
|
// don't fetch the tiles themselves
|
|
return;
|
|
}
|
|
yFlip = (tilemapBuffer[l] & 0x8000) > 0;
|
|
xFlip = (tilemapBuffer[l] & 0x4000) > 0;
|
|
yRow = cond(yFlip, 7 - (ry & 0x7), (ry & 0x7));
|
|
tileNum = tilemapBuffer[l] & 0x3ff;
|
|
|
|
tileNum += cond(useXbig && (rx & 0x8) == cond(xFlip, 0, 8), 1, 0);
|
|
tileNum += cond(bigTiles[l] && (ry & 0x8) == cond(yFlip, 0, 8), 0x10, 0);
|
|
|
|
bits = bitPerMode[mode * 4 + l];
|
|
|
|
tileBufferP1[l] = vram[
|
|
(tileAdr[l] + tileNum * 4 * bits + yRow) & 0x7fff
|
|
];
|
|
if(bits > 2) {
|
|
tileBufferP2[l] = vram[
|
|
(tileAdr[l] + tileNum * 4 * bits + yRow + 8) & 0x7fff
|
|
];
|
|
}
|
|
if(bits > 4) {
|
|
tileBufferP3[l] = vram[
|
|
(tileAdr[l] + tileNum * 4 * bits + yRow + 16) & 0x7fff
|
|
];
|
|
tileBufferP4[l] = vram[
|
|
(tileAdr[l] + tileNum * 4 * bits + yRow + 24) & 0x7fff
|
|
];
|
|
}
|
|
}
|
|
|
|
I32 getMode7Pixel(I32 x, I32 y, I32 l, I32 p) {
|
|
I32 pixelData, rX, px, py, tileX, tileY, tileByte;
|
|
Bool pixelIsTransparent;
|
|
pixelData = tilemapBuffer[0];
|
|
if(x != lastTileFetchedX[0] || y != lastTileFetchedY[0]) {
|
|
rX = cond(mode7FlipX, 255 - x, x);
|
|
|
|
px = mode7Xcoords[rX] >> 8;
|
|
py = mode7Ycoords[rX] >> 8;
|
|
|
|
pixelIsTransparent = FALSE;
|
|
|
|
if(mode7LargeField && (px < 0 || px >= 1024 || py < 0 || py >= 1024)) {
|
|
if(mode7Char0fill) {
|
|
// always use tile 0
|
|
px &= 0x7;
|
|
py &= 0x7;
|
|
} else {
|
|
// act as transparent
|
|
pixelIsTransparent = TRUE;
|
|
}
|
|
}
|
|
// fetch the right tilemap byte
|
|
tileX = (px & 0x3f8) >> 3;
|
|
tileY = (py & 0x3f8) >> 3;
|
|
|
|
tileByte = vram[(tileY * 128 + tileX)] & 0xff;
|
|
// fetch the tile
|
|
pixelData = vram[tileByte * 64 + (py & 0x7) * 8 + (px & 0x7)];
|
|
pixelData >>= 8;
|
|
pixelData = cond(pixelIsTransparent, 0, pixelData);
|
|
tilemapBuffer[0] = pixelData;
|
|
lastTileFetchedX[0] = x;
|
|
lastTileFetchedY[0] = y;
|
|
}
|
|
|
|
if(l == 1 && (pixelData >> 7) != p) {
|
|
// wrong priority
|
|
return 0;
|
|
} else if(l == 1) {
|
|
return pixelData & 0x7f;
|
|
}
|
|
|
|
return pixelData;
|
|
}
|
|
|
|
|
|
|
|
I32 getPixelForLayer(I32 x, I32 y, I32 l, I32 p) {
|
|
I32 mapWord, paletteNum, xShift, bits, mul, tileData;
|
|
if(l > 3) {
|
|
if(spritePrioBuffer[x] != p) {
|
|
return 0;
|
|
}
|
|
return spriteLineBuffer[x];
|
|
}
|
|
|
|
if(mode == 7) {
|
|
return getMode7Pixel(x, y, l, p);
|
|
}
|
|
|
|
if(
|
|
(x >> 3) != lastTileFetchedX[l] ||
|
|
y != lastTileFetchedY[l]
|
|
) {
|
|
fetchTileInBuffer(x, y, l, FALSE);
|
|
lastTileFetchedX[l] = (x >> 3);
|
|
lastTileFetchedY[l] = y;
|
|
}
|
|
|
|
mapWord = tilemapBuffer[l];
|
|
if(((mapWord & 0x2000) >> 13) != p) {
|
|
// not the right priority
|
|
return 0;
|
|
}
|
|
paletteNum = (mapWord & 0x1c00) >> 10;
|
|
xShift = cond((mapWord & 0x4000) > 0, (x & 0x7), 7 - (x & 0x7));
|
|
|
|
paletteNum += cond(mode == 0, l * 8, 0);
|
|
|
|
bits = bitPerMode[mode * 4 + l];
|
|
mul = 4;
|
|
tileData = (tileBufferP1[l] >> xShift) & 0x1;
|
|
tileData |= ((tileBufferP1[l] >> (8 + xShift)) & 0x1) << 1;
|
|
|
|
if(bits > 2) {
|
|
mul = 16;
|
|
tileData |= ((tileBufferP2[l] >> xShift) & 0x1) << 2;
|
|
tileData |= ((tileBufferP2[l] >> (8 + xShift)) & 0x1) << 3;
|
|
}
|
|
|
|
if(bits > 4) {
|
|
mul = 256;
|
|
tileData |= ((tileBufferP3[l] >> xShift) & 0x1) << 4;
|
|
tileData |= ((tileBufferP3[l] >> (8 + xShift)) & 0x1) << 5;
|
|
tileData |= ((tileBufferP4[l] >> xShift) & 0x1) << 6;
|
|
tileData |= ((tileBufferP4[l] >> (8 + xShift)) & 0x1) << 7;
|
|
}
|
|
|
|
return cond(tileData > 0, (paletteNum * mul + tileData), 0);
|
|
}
|
|
|
|
U0 getColor(Bool sub, I32 x, I32 y, I32* lay) {
|
|
I32 modeIndex, count, j, pixel, layer, lx, ly, optX, andVal, tileStartX, add, color, r, g, b;
|
|
|
|
modeIndex = cond(layer3Prio && mode == 1, 96, 12 * mode);
|
|
modeIndex = cond(mode7ExBg && mode == 7, 108, modeIndex);
|
|
count = layercountPerMode[mode];
|
|
|
|
pixel = 0;
|
|
layer = 5;
|
|
if(interlace && (mode == 5 || mode == 6)) {
|
|
y = y * 2 + cond(evenFrame, 1, 0);
|
|
}
|
|
for(j = 0; j < count; j++) {
|
|
lx = x;
|
|
ly = y;
|
|
layer = layersPerMode[modeIndex + j];
|
|
if(
|
|
(
|
|
!sub && mainScreenEnabled[layer] &&
|
|
(!mainScreenWindow[layer] || !getWindowState(lx, layer))
|
|
) || (
|
|
sub && subScreenEnabled[layer] &&
|
|
(!subScreenWindow[layer] || !getWindowState(lx, layer))
|
|
)
|
|
) {
|
|
if(mosaicEnabled[layer]) {
|
|
lx -= lx % mosaicSize;
|
|
ly -= (ly - mosaicStartLine) % mosaicSize;
|
|
}
|
|
lx += cond(mode == 7, 0, bgHoff[layer]);
|
|
ly += cond(mode == 7, 0, bgVoff[layer]);
|
|
optX = lx - bgHoff[layer];
|
|
if((mode == 5 || mode == 6) && layer < 4) {
|
|
lx = lx * 2 + cond(sub, 0, 1);
|
|
optX = optX * 2 + cond(sub, 0, 1);
|
|
}
|
|
|
|
// origLx = lx;
|
|
|
|
if((mode == 2 || mode == 4 || mode == 6) && layer < 2) {
|
|
andVal = cond(layer == 0, 0x2000, 0x4000);
|
|
if(x == 0) {
|
|
lastOrigTileX[layer] = lx >> 3;
|
|
}
|
|
// where the relevant tile started
|
|
// TODO: lx can be above 0xffff (e.g. if scroll is 0xffff, and x > 0)
|
|
tileStartX = optX - (lx - (lx & 0xfff8));
|
|
if((lx >> 3) != lastOrigTileX[layer] && x > 0) {
|
|
// we are fetching a new tile for the layer, get a new OPT-tile
|
|
// if(logging && y == 32 && (mode == 2 || mode == 4 || mode == 6) && layer == 0) {
|
|
// log("at X = " + x + ", lx: " + getWordRep(lx) + ", fetched new tile for OPT");
|
|
// }
|
|
fetchTileInBuffer(
|
|
bgHoff[2] + ((tileStartX - 1) & 0x1f8),
|
|
bgVoff[2], 2, TRUE
|
|
);
|
|
optHorBuffer[layer] = tilemapBuffer[2];
|
|
if(mode == 4) {
|
|
if((optHorBuffer[layer] & 0x8000) > 0) {
|
|
optVerBuffer[layer] = optHorBuffer[layer];
|
|
optHorBuffer[layer] = 0;
|
|
} else {
|
|
optVerBuffer[layer] = 0;
|
|
}
|
|
} else {
|
|
fetchTileInBuffer(
|
|
bgHoff[2] + ((tileStartX - 1) & 0x1f8),
|
|
bgVoff[2] + 8, 2, TRUE
|
|
);
|
|
optVerBuffer[layer] = tilemapBuffer[2];
|
|
}
|
|
lastOrigTileX[layer] = lx >> 3;
|
|
}
|
|
if((optHorBuffer[layer] & andVal) > 0) {
|
|
//origLx = lx;
|
|
add = ((tileStartX + 7) & 0x1f8);
|
|
lx = (lx & 0x7) + ((optHorBuffer[layer] + add) & 0x1ff8);
|
|
}
|
|
if((optVerBuffer[layer] & andVal) > 0) {
|
|
ly = (optVerBuffer[layer] & 0x1fff) + (ly - bgVoff[layer]);
|
|
}
|
|
}
|
|
// if(logging && y == 32 && (mode == 2 || mode == 4 || mode == 6) && layer == 0) {
|
|
// log("at X = " + x + ", lx: " + getWordRep(lx) + ", ly: " + getWordRep(ly) + ", optHB: " + getWordRep(optHorBuffer[layer]) + ", orig lx: " + getWordRep(origLx));
|
|
// }
|
|
|
|
pixel = getPixelForLayer(
|
|
lx, ly,
|
|
layer,
|
|
prioPerMode[modeIndex + j]
|
|
);
|
|
}
|
|
if((pixel & 0xff) > 0) {
|
|
break;
|
|
}
|
|
}
|
|
layer = cond(j == count, 5, layer);
|
|
color = cgram[pixel & 0xff];
|
|
if(
|
|
directColor && layer < 4 &&
|
|
bitPerMode[mode * 4 + layer] == 8
|
|
) {
|
|
r = ((pixel & 0x7) << 2) | ((pixel & 0x100) >> 7);
|
|
g = ((pixel & 0x38) >> 1) | ((pixel & 0x200) >> 8);
|
|
b = ((pixel & 0xc0) >> 3) | ((pixel & 0x400) >> 8);
|
|
color = (b << 10) | (g << 5) | r;
|
|
}
|
|
|
|
lay[0] = color;
|
|
lay[1] = layer;
|
|
lay[2] = pixel;
|
|
}
|
|
|
|
U0 renderLine(I32 line) {
|
|
I32 r1, g1, b1, r2, g2, b2, i, bMult, color;
|
|
I32 colLay[3];
|
|
I32 secondLay[3];
|
|
if(line == 225 && overscan) {
|
|
frameOverscan = TRUE;
|
|
}
|
|
if(line == 0) {
|
|
// pre-render line
|
|
rangeOver = FALSE;
|
|
timeOver = FALSE;
|
|
frameOverscan = FALSE;
|
|
frameInterlace = FALSE;
|
|
MemSet(spriteLineBuffer, NULL, 256);
|
|
if(!forcedBlank) {
|
|
evaluateSprites(0);
|
|
}
|
|
} else if(line == (cond(frameOverscan,240, 225))) {
|
|
// beginning of Vblank
|
|
if(!forcedBlank) {
|
|
oamAdr = oamRegAdr;
|
|
oamInHigh = oamRegInHigh;
|
|
}
|
|
frameInterlace = interlace;
|
|
evenFrame = !evenFrame;
|
|
} else if(line > 0 && line < (cond(frameOverscan, 240, 225))) {
|
|
// visible line
|
|
if(line == 1) {
|
|
mosaicStartLine = 1;
|
|
}
|
|
if(mode == 7) {
|
|
generateMode7Coords(line);
|
|
}
|
|
|
|
lastTileFetchedX[0] = -1;
|
|
lastTileFetchedX[1] = -1;
|
|
lastTileFetchedX[2] = -1;
|
|
lastTileFetchedX[3] = -1;
|
|
lastTileFetchedY[0] = -1;
|
|
lastTileFetchedY[1] = -1;
|
|
lastTileFetchedY[2] = -1;
|
|
lastTileFetchedY[3] = -1;
|
|
optHorBuffer[0] = 0;
|
|
optHorBuffer[1] = 0;
|
|
optVerBuffer[0] = 0;
|
|
optVerBuffer[1] = 0;
|
|
lastOrigTileX[0] = -1;
|
|
lastOrigTileX[1] = -1;
|
|
bMult = brightnessMults[brightness];
|
|
i = 0;
|
|
while(i < 256) {
|
|
// for each pixel
|
|
|
|
r1 = 0;
|
|
g1 = 0;
|
|
b1 = 0;
|
|
r2 = 0;
|
|
g2 = 0;
|
|
b2 = 0;
|
|
|
|
if(!forcedBlank) {
|
|
|
|
getColor(FALSE, i, line, &colLay);
|
|
color = colLay[0];
|
|
|
|
r2 = color & 0x1f;
|
|
g2 = (color & 0x3e0) >> 5;
|
|
b2 = (color & 0x7c00) >> 10;
|
|
|
|
// TODO: docs day that this clips before math, but it seems to simply
|
|
// always clip the pixels to black?
|
|
if(
|
|
colorClip == 3 ||
|
|
(colorClip == 2 && getWindowState(i, 5)) ||
|
|
(colorClip == 1 && !getWindowState(i, 5))
|
|
) {
|
|
r2 = 0;
|
|
g2 = 0;
|
|
b2 = 0;
|
|
}
|
|
|
|
secondLay[0] = 0;
|
|
secondLay[1] = 5;
|
|
secondLay[2] = 0;
|
|
if(
|
|
mode == 5 || mode == 6 || pseudoHires ||
|
|
(getMathEnabled(i, colLay[1], colLay[2]) && addSub)
|
|
) {
|
|
getColor(TRUE, i, line, &secondLay);
|
|
r1 = secondLay[0] & 0x1f;
|
|
g1 = (secondLay[0] & 0x3e0) >> 5;
|
|
b1 = (secondLay[0] & 0x7c00) >> 10;
|
|
}
|
|
|
|
if(getMathEnabled(i, colLay[1], colLay[2])) {
|
|
if(subtractColors) {
|
|
r2 -= cond((addSub && secondLay[1] < 5), r1, fixedColorR);
|
|
g2 -= cond((addSub && secondLay[1] < 5), g1, fixedColorG);
|
|
b2 -= cond((addSub && secondLay[1] < 5), b1, fixedColorB);
|
|
} else {
|
|
r2 += cond((addSub && secondLay[1] < 5), r1, fixedColorR);
|
|
g2 += cond((addSub && secondLay[1] < 5), g1, fixedColorG);
|
|
b2 += cond((addSub && secondLay[1] < 5), b1, fixedColorB);
|
|
}
|
|
// TODO: docs say that halfing should not happen if adding the
|
|
// direct color, but that makes some effects in the SNES character
|
|
// test look wrong
|
|
if(halfColors && (secondLay[1] < 5 || !addSub)) {
|
|
r2 >>= 1;
|
|
g2 >>= 1;
|
|
b2 >>= 1;
|
|
}
|
|
r2 = cond(r2 > 31, 31, r2);
|
|
r2 = cond(r2 < 0, 0, r2);
|
|
g2 = cond(g2 > 31, 31, g2);
|
|
g2 = cond(g2 < 0, 0, g2);
|
|
b2 = cond(b2 > 31, 31, b2);
|
|
b2 = cond(b2 < 0, 0, b2);
|
|
}
|
|
|
|
if(!(mode == 5 || mode == 6 || pseudoHires)) {
|
|
r1 = r2;
|
|
g1 = g2;
|
|
b1 = b2;
|
|
}
|
|
|
|
}
|
|
pixelOutput[line * 1536 + 6 * i] = (r1 * bMult) & 0xff;
|
|
pixelOutput[line * 1536 + 6 * i + 1] = (g1 * bMult) & 0xff;
|
|
pixelOutput[line * 1536 + 6 * i + 2] = (b1 * bMult) & 0xff;
|
|
pixelOutput[line * 1536 + 6 * i + 3] = (r2 * bMult) & 0xff;
|
|
pixelOutput[line * 1536 + 6 * i + 4] = (g2 * bMult) & 0xff;
|
|
pixelOutput[line * 1536 + 6 * i + 5] = (b2 * bMult) & 0xff;
|
|
|
|
i++;
|
|
|
|
}
|
|
MemSet(spriteLineBuffer, NULL, 256);
|
|
if(!forcedBlank) {
|
|
evaluateSprites(line);
|
|
}
|
|
}
|
|
}
|
|
|
|
I32 getVramRemap() {
|
|
I32 adr;
|
|
adr = vramAdr & 0x7fff;
|
|
if(vramRemap == 1) {
|
|
adr = (adr & 0xff00) | ((adr & 0xe0) >> 5) | ((adr & 0x1f) << 3);
|
|
} else if(vramRemap == 2) {
|
|
adr = (adr & 0xfe00) | ((adr & 0x1c0) >> 6) | ((adr & 0x3f) << 3);
|
|
} else if(vramRemap == 3) {
|
|
adr = (adr & 0xfc00) | ((adr & 0x380) >> 7) | ((adr & 0x7f) << 3);
|
|
}
|
|
return adr;
|
|
}
|
|
|
|
I32 get13Signed(I32 val) {
|
|
if((val & 0x1000) > 0) {
|
|
return -(8192 - (val & 0xfff));
|
|
}
|
|
return (val & 0xfff);
|
|
}
|
|
|
|
I32 get16Signed(I32 val) {
|
|
if((val & 0x8000) > 0) {
|
|
return -(65536 - val);
|
|
}
|
|
return val;
|
|
}
|
|
|
|
I32 getMultResult(I32 a, I32 b) {
|
|
b = cond(b < 0, 65536 + b, b);
|
|
b >>= 8;
|
|
b = cond(((b & 0x80) > 0), -(256 - b), b);
|
|
I32 _ans = a * b;
|
|
if(_ans < 0) {
|
|
return 16777216 + _ans;
|
|
}
|
|
return _ans;
|
|
}
|
|
|
|
I32 ppu_read(I32 adr) {
|
|
I32 val;
|
|
switch(adr) {
|
|
case 0x34: {
|
|
return multResult & 0xff;
|
|
}
|
|
case 0x35: {
|
|
return (multResult & 0xff00) >> 8;
|
|
}
|
|
case 0x36: {
|
|
return (multResult & 0xff0000) >> 16;
|
|
}
|
|
case 0x37: {
|
|
// TODO: docs say this should only happen if bit 7 of the IO port is
|
|
// set, but always doing it makes The Legend of Zelda: A Link to the
|
|
// Past work
|
|
//if(ppuLatch) {
|
|
latchedHpos = xPos >> 2;
|
|
latchedVpos = yPos;
|
|
//}
|
|
countersLatched = TRUE;
|
|
return openBus;
|
|
}
|
|
case 0x38: {
|
|
if(!oamSecond) {
|
|
if(oamInHigh) {
|
|
val = highOam[oamAdr & 0xf] & 0xff;
|
|
} else {
|
|
val = oam[oamAdr] & 0xff;
|
|
}
|
|
oamSecond = TRUE;
|
|
} else {
|
|
if(oamInHigh) {
|
|
val = highOam[oamAdr & 0xf] >> 8;
|
|
} else {
|
|
val = oam[oamAdr] >> 8;
|
|
}
|
|
oamAdr++;
|
|
oamAdr &= 0xff;
|
|
oamInHigh = cond((
|
|
oamAdr == 0
|
|
), !oamInHigh, oamInHigh);
|
|
oamSecond = FALSE;
|
|
}
|
|
return val;
|
|
}
|
|
case 0x39: {
|
|
val = vramReadBuffer;
|
|
vramReadBuffer = vram[getVramRemap()];
|
|
if(!vramIncOnHigh) {
|
|
vramAdr += vramInc;
|
|
vramAdr &= 0xffff;
|
|
}
|
|
return val & 0xff;
|
|
}
|
|
case 0x3a: {
|
|
val = vramReadBuffer;
|
|
vramReadBuffer = vram[getVramRemap()];
|
|
if(vramIncOnHigh) {
|
|
vramAdr += vramInc;
|
|
vramAdr &= 0xffff;
|
|
}
|
|
return (val & 0xff00) >> 8;
|
|
}
|
|
case 0x3b: {
|
|
|
|
if(!cgramSecond) {
|
|
val = cgram[cgramAdr] & 0xff;
|
|
cgramSecond = TRUE;
|
|
} else {
|
|
val = cgram[cgramAdr++] >> 8;
|
|
cgramAdr &= 0xff;
|
|
cgramSecond = FALSE;
|
|
}
|
|
return val;
|
|
}
|
|
case 0x3c: {
|
|
|
|
if(!latchHsecond) {
|
|
val = latchedHpos & 0xff;
|
|
latchHsecond = TRUE;
|
|
} else {
|
|
val = (latchedHpos & 0xff00) >> 8;
|
|
latchHsecond = FALSE;
|
|
}
|
|
return val;
|
|
}
|
|
case 0x3d: {
|
|
|
|
if(!latchVsecond) {
|
|
val = latchedVpos & 0xff;
|
|
latchVsecond = TRUE;
|
|
} else {
|
|
val = (latchedVpos & 0xff00) >> 8;
|
|
latchVsecond = FALSE;
|
|
}
|
|
return val;
|
|
}
|
|
case 0x3e: {
|
|
val = cond(timeOver, 0x80, 0);
|
|
val |= cond(rangeOver, 0x40, 0);
|
|
return val | 0x1;
|
|
}
|
|
case 0x3f: {
|
|
val = cond(evenFrame, 0x80, 0);
|
|
val |= cond(countersLatched, 0x40, 0);
|
|
if(ppuLatch) {
|
|
countersLatched = FALSE;
|
|
}
|
|
latchHsecond = FALSE;
|
|
latchVsecond = FALSE;
|
|
/* PAL: val |= 0x10; */
|
|
return val | 0x2;
|
|
}
|
|
}
|
|
return openBus;
|
|
}
|
|
|
|
U0 ppu_write(I32 adr, I32 value) {
|
|
I32 incVal, _adr;
|
|
switch(adr) {
|
|
case 0x00: {
|
|
forcedBlank = (value & 0x80) > 0;
|
|
brightness = value & 0xf;
|
|
return;
|
|
}
|
|
case 0x01: {
|
|
sprAdr1 = (value & 0x7) << 13;
|
|
sprAdr2 = ((value & 0x18) + 8) << 9;
|
|
objSize = (value & 0xe0) >> 5;
|
|
return;
|
|
}
|
|
case 0x02: {
|
|
oamAdr = value;
|
|
oamRegAdr = oamAdr;
|
|
oamInHigh = oamRegInHigh;
|
|
oamSecond = FALSE;
|
|
return;
|
|
}
|
|
case 0x03: {
|
|
oamInHigh = (value & 0x1) > 0;
|
|
objPriority = (value & 0x80) > 0;
|
|
oamAdr = oamRegAdr;
|
|
oamRegInHigh = oamInHigh;
|
|
oamSecond = FALSE;
|
|
return;
|
|
}
|
|
case 0x04: {
|
|
if(!oamSecond) {
|
|
if(oamInHigh) {
|
|
highOam[
|
|
oamAdr & 0xf
|
|
] = (highOam[oamAdr & 0xf] & 0xff00) | value;
|
|
} else {
|
|
oamBuffer = (oamBuffer & 0xff00) | value;
|
|
}
|
|
oamSecond = TRUE;
|
|
} else {
|
|
if(oamInHigh) {
|
|
highOam[
|
|
oamAdr & 0xf
|
|
] = (highOam[oamAdr & 0xf] & 0xff) | (value << 8);
|
|
} else {
|
|
oamBuffer = (oamBuffer & 0xff) | (value << 8);
|
|
oam[oamAdr] = oamBuffer;
|
|
}
|
|
oamAdr++;
|
|
oamAdr &= 0xff;
|
|
oamInHigh = cond((
|
|
oamAdr == 0
|
|
), !oamInHigh, oamInHigh);
|
|
oamSecond = FALSE;
|
|
}
|
|
return;
|
|
}
|
|
case 0x05: {
|
|
mode = value & 0x7;
|
|
layer3Prio = (value & 0x08) > 0;
|
|
bigTiles[0] = (value & 0x10) > 0;
|
|
bigTiles[1] = (value & 0x20) > 0;
|
|
bigTiles[2] = (value & 0x40) > 0;
|
|
bigTiles[3] = (value & 0x80) > 0;
|
|
return;
|
|
}
|
|
case 0x06: {
|
|
mosaicEnabled[0] = (value & 0x1) > 0;
|
|
mosaicEnabled[1] = (value & 0x2) > 0;
|
|
mosaicEnabled[2] = (value & 0x4) > 0;
|
|
mosaicEnabled[3] = (value & 0x8) > 0;
|
|
mosaicSize = ((value & 0xf0) >> 4) + 1;
|
|
mosaicStartLine = yPos;
|
|
return;
|
|
}
|
|
case 0x07:
|
|
case 0x08:
|
|
case 0x09:
|
|
case 0x0a: {
|
|
tilemapWider[adr - 7] = (value & 0x1) > 0;
|
|
tilemapHigher[adr - 7] = (value & 0x2) > 0;
|
|
tilemapAdr[adr - 7] = (value & 0xfc) << 8;
|
|
return;
|
|
}
|
|
case 0x0b: {
|
|
tileAdr[0] = (value & 0xf) << 12;
|
|
tileAdr[1] = (value & 0xf0) << 8;
|
|
return;
|
|
}
|
|
case 0x0c: {
|
|
tileAdr[2] = (value & 0xf) << 12;
|
|
tileAdr[3] = (value & 0xf0) << 8;
|
|
return;
|
|
}
|
|
case 0x0d: {
|
|
mode7Hoff = get13Signed((value << 8) | mode7Prev);
|
|
mode7Prev = value;
|
|
// fall through to also set normal layer bgHoff
|
|
}
|
|
case 0x0f:
|
|
case 0x11:
|
|
case 0x13: {
|
|
bgHoff[
|
|
(adr - 0xd) >> 1
|
|
] = (value << 8) | (offPrev1 & 0xf8) | (offPrev2 & 0x7);
|
|
offPrev1 = value;
|
|
offPrev2 = value;
|
|
return;
|
|
}
|
|
case 0x0e: {
|
|
mode7Voff = get13Signed((value << 8) | mode7Prev);
|
|
mode7Prev = value;
|
|
// fall through to also set normal layer bgVoff
|
|
}
|
|
case 0x10:
|
|
case 0x12:
|
|
case 0x14: {
|
|
bgVoff[
|
|
(adr - 0xe) >> 1
|
|
] = (value << 8) | (offPrev1 & 0xff);
|
|
offPrev1 = value;
|
|
return;
|
|
}
|
|
case 0x15: {
|
|
incVal = value & 0x3;
|
|
if(incVal == 0) {
|
|
vramInc = 1;
|
|
} else if(incVal == 1) {
|
|
vramInc = 32;
|
|
} else {
|
|
vramInc = 128;
|
|
}
|
|
vramRemap = (value & 0xc0) >> 2;
|
|
vramIncOnHigh = (value & 0x80) > 0;
|
|
return;
|
|
}
|
|
case 0x16: {
|
|
vramAdr = (vramAdr & 0xff00) | value;
|
|
vramReadBuffer = vram[getVramRemap()];
|
|
return;
|
|
}
|
|
case 0x17: {
|
|
vramAdr = (vramAdr & 0xff) | (value << 8);
|
|
vramReadBuffer = vram[getVramRemap()];
|
|
return;
|
|
}
|
|
case 0x18: {
|
|
_adr = getVramRemap();
|
|
vram[_adr] = (vram[_adr] & 0xff00) | value;
|
|
if(!vramIncOnHigh) {
|
|
vramAdr += vramInc;
|
|
vramAdr &= 0xffff;
|
|
}
|
|
return;
|
|
}
|
|
case 0x19: {
|
|
_adr = getVramRemap();
|
|
vram[_adr] = (vram[_adr] & 0xff) | (value << 8);
|
|
if(vramIncOnHigh) {
|
|
vramAdr += vramInc;
|
|
vramAdr &= 0xffff;
|
|
}
|
|
return;
|
|
}
|
|
case 0x1a: {
|
|
mode7LargeField = (value & 0x80) > 0;
|
|
mode7Char0fill = (value & 0x40) > 0;
|
|
mode7FlipY = (value & 0x2) > 0;
|
|
mode7FlipX = (value & 0x1) > 0;
|
|
return;
|
|
}
|
|
case 0x1b: {
|
|
mode7A = get16Signed((value << 8) | mode7Prev);
|
|
mode7Prev = value;
|
|
multResult = getMultResult(mode7A, mode7B);
|
|
return;
|
|
}
|
|
case 0x1c: {
|
|
mode7B = get16Signed((value << 8) | mode7Prev);
|
|
mode7Prev = value;
|
|
multResult = getMultResult(mode7A, mode7B);
|
|
return;
|
|
}
|
|
case 0x1d: {
|
|
mode7C = get16Signed((value << 8) | mode7Prev);
|
|
mode7Prev = value;
|
|
return;
|
|
}
|
|
case 0x1e: {
|
|
mode7D = get16Signed((value << 8) | mode7Prev);
|
|
mode7Prev = value;
|
|
return;
|
|
}
|
|
case 0x1f: {
|
|
mode7X = get13Signed((value << 8) | mode7Prev);
|
|
mode7Prev = value;
|
|
return;
|
|
}
|
|
case 0x20: {
|
|
mode7Y = get13Signed((value << 8) | mode7Prev);
|
|
mode7Prev = value;
|
|
return;
|
|
}
|
|
case 0x21: {
|
|
cgramAdr = value;
|
|
cgramSecond = FALSE;
|
|
return;
|
|
}
|
|
case 0x22: {
|
|
if(!cgramSecond) {
|
|
cgramBuffer = (cgramBuffer & 0xff00) | value;
|
|
cgramSecond = TRUE;
|
|
} else {
|
|
cgramBuffer = (cgramBuffer & 0xff) | (value << 8);
|
|
cgram[cgramAdr++] = cgramBuffer;
|
|
cgramAdr &= 0xff;
|
|
cgramSecond = FALSE;
|
|
}
|
|
return;
|
|
}
|
|
case 0x23: {
|
|
window1Inversed[0] = (value & 0x01) > 0;
|
|
window1Enabled[0] = (value & 0x02) > 0;
|
|
window2Inversed[0] = (value & 0x04) > 0;
|
|
window2Enabled[0] = (value & 0x08) > 0;
|
|
window1Inversed[1] = (value & 0x10) > 0;
|
|
window1Enabled[1] = (value & 0x20) > 0;
|
|
window2Inversed[1] = (value & 0x40) > 0;
|
|
window2Enabled[1] = (value & 0x80) > 0;
|
|
return;
|
|
}
|
|
case 0x24: {
|
|
window1Inversed[2] = (value & 0x01) > 0;
|
|
window1Enabled[2] = (value & 0x02) > 0;
|
|
window2Inversed[2] = (value & 0x04) > 0;
|
|
window2Enabled[2] = (value & 0x08) > 0;
|
|
window1Inversed[3] = (value & 0x10) > 0;
|
|
window1Enabled[3] = (value & 0x20) > 0;
|
|
window2Inversed[3] = (value & 0x40) > 0;
|
|
window2Enabled[3] = (value & 0x80) > 0;
|
|
return;
|
|
}
|
|
case 0x25: {
|
|
window1Inversed[4] = (value & 0x01) > 0;
|
|
window1Enabled[4] = (value & 0x02) > 0;
|
|
window2Inversed[4] = (value & 0x04) > 0;
|
|
window2Enabled[4] = (value & 0x08) > 0;
|
|
window1Inversed[5] = (value & 0x10) > 0;
|
|
window1Enabled[5] = (value & 0x20) > 0;
|
|
window2Inversed[5] = (value & 0x40) > 0;
|
|
window2Enabled[5] = (value & 0x80) > 0;
|
|
return;
|
|
}
|
|
case 0x26: {
|
|
window1Left = value;
|
|
return;
|
|
}
|
|
case 0x27: {
|
|
window1Right = value;
|
|
return;
|
|
}
|
|
case 0x28: {
|
|
window2Left = value;
|
|
return;
|
|
}
|
|
case 0x29: {
|
|
window2Right = value;
|
|
return;
|
|
}
|
|
case 0x2a: {
|
|
windowMaskLogic[0] = value & 0x3;
|
|
windowMaskLogic[1] = (value & 0xc) >> 2;
|
|
windowMaskLogic[2] = (value & 0x30) >> 4;
|
|
windowMaskLogic[3] = (value & 0xc0) >> 6;
|
|
return;
|
|
}
|
|
case 0x2b: {
|
|
windowMaskLogic[4] = value & 0x3;
|
|
windowMaskLogic[5] = (value & 0xc) >> 2;
|
|
return;
|
|
}
|
|
case 0x2c: {
|
|
mainScreenEnabled[0] = (value & 0x1) > 0;
|
|
mainScreenEnabled[1] = (value & 0x2) > 0;
|
|
mainScreenEnabled[2] = (value & 0x4) > 0;
|
|
mainScreenEnabled[3] = (value & 0x8) > 0;
|
|
mainScreenEnabled[4] = (value & 0x10) > 0;
|
|
return;
|
|
}
|
|
case 0x2d: {
|
|
subScreenEnabled[0] = (value & 0x1) > 0;
|
|
subScreenEnabled[1] = (value & 0x2) > 0;
|
|
subScreenEnabled[2] = (value & 0x4) > 0;
|
|
subScreenEnabled[3] = (value & 0x8) > 0;
|
|
subScreenEnabled[4] = (value & 0x10) > 0;
|
|
return;
|
|
}
|
|
case 0x2e: {
|
|
mainScreenWindow[0] = (value & 0x1) > 0;
|
|
mainScreenWindow[1] = (value & 0x2) > 0;
|
|
mainScreenWindow[2] = (value & 0x4) > 0;
|
|
mainScreenWindow[3] = (value & 0x8) > 0;
|
|
mainScreenWindow[4] = (value & 0x10) > 0;
|
|
return;
|
|
}
|
|
case 0x2f: {
|
|
subScreenWindow[0] = (value & 0x1) > 0;
|
|
subScreenWindow[1] = (value & 0x2) > 0;
|
|
subScreenWindow[2] = (value & 0x4) > 0;
|
|
subScreenWindow[3] = (value & 0x8) > 0;
|
|
subScreenWindow[4] = (value & 0x10) > 0;
|
|
return;
|
|
}
|
|
case 0x30: {
|
|
colorClip = (value & 0xc0) >> 6;
|
|
preventMath = (value & 0x30) >> 4;
|
|
addSub = (value & 0x2) > 0;
|
|
directColor = (value & 0x1) > 0;
|
|
return;
|
|
}
|
|
case 0x31: {
|
|
subtractColors = (value & 0x80) > 0;
|
|
halfColors = (value & 0x40) > 0;
|
|
mathEnabled[0] = (value & 0x1) > 0;
|
|
mathEnabled[1] = (value & 0x2) > 0;
|
|
mathEnabled[2] = (value & 0x4) > 0;
|
|
mathEnabled[3] = (value & 0x8) > 0;
|
|
mathEnabled[4] = (value & 0x10) > 0;
|
|
mathEnabled[5] = (value & 0x20) > 0;
|
|
return;
|
|
}
|
|
case 0x32: {
|
|
if((value & 0x80) > 0) {
|
|
fixedColorB = value & 0x1f;
|
|
}
|
|
if((value & 0x40) > 0) {
|
|
fixedColorG = value & 0x1f;
|
|
}
|
|
if((value & 0x20) > 0) {
|
|
fixedColorR = value & 0x1f;
|
|
}
|
|
return;
|
|
}
|
|
case 0x33: {
|
|
mode7ExBg = (value & 0x40) > 0;
|
|
pseudoHires = (value & 0x08) > 0;
|
|
overscan = (value & 0x04) > 0;
|
|
objInterlace = (value & 0x02) > 0;
|
|
interlace = (value & 0x01) > 0;
|
|
return;
|
|
}
|
|
}
|
|
} |