commodore64- preliminary hardware sprite support

This commit is contained in:
saxxonpike 2012-11-09 06:43:08 +00:00
parent 570bb986c2
commit 66c1fc68f6
1 changed files with 252 additions and 76 deletions

View File

@ -31,8 +31,16 @@ namespace BizHawk.Emulation.Computers.Commodore64
public bool IRST; public bool IRST;
public int LPX; public int LPX;
public int LPY; public int LPY;
public int[] MC = new int[8]; // (internal)
public int[] MCBASE = new int[8]; // (internal)
public bool MCM; public bool MCM;
public bool[] MD = new bool[8]; // (internal)
public bool[] MDMA = new bool[8]; // (internal)
public int[] MMx = new int[2]; public int[] MMx = new int[2];
public int[] MPTR = new int[8]; // (internal)
public Int32[] MSR = new Int32[8]; // (internal)
public bool[] MSRA = new bool[8]; // (internal)
public int[] MSRC = new int[8]; // (internal)
public int[] MxC = new int[8]; public int[] MxC = new int[8];
public bool[] MxD = new bool[8]; public bool[] MxD = new bool[8];
public bool[] MxDP = new bool[8]; public bool[] MxDP = new bool[8];
@ -43,14 +51,15 @@ namespace BizHawk.Emulation.Computers.Commodore64
public bool[] MxXE = new bool[8]; public bool[] MxXE = new bool[8];
public int[] MxY = new int[8]; public int[] MxY = new int[8];
public bool[] MxYE = new bool[8]; public bool[] MxYE = new bool[8];
public bool[] MYE = new bool[8]; // (internal)
public int RASTER; public int RASTER;
public int RC; public int RC; // (internal)
public bool RES; public bool RES;
public bool RSEL; public bool RSEL;
public int VC; public int VC; // (internal)
public int VCBASE; public int VCBASE; // (internal)
public int VM; public int VM;
public int VMLI; public int VMLI; // (internal)
public int XSCROLL; public int XSCROLL;
public int YSCROLL; public int YSCROLL;
@ -489,11 +498,13 @@ namespace BizHawk.Emulation.Computers.Commodore64
public byte[] colorMemory; public byte[] colorMemory;
public int cycle; public int cycle;
public int cycleLeft; public int cycleLeft;
public bool dataForeground;
public bool displayEnabled; public bool displayEnabled;
public bool hBlank; public bool hBlank;
public bool idle; public bool idle;
public int[] pixelBuffer = new int[12]; public int[] pixelBuffer;
public int pixelBufferIndex = 0; public bool[] pixelBufferForeground;
public int pixelBufferIndex;
public int rasterInterruptLine; public int rasterInterruptLine;
public int rasterLineLeft; public int rasterLineLeft;
public int rasterOffset; public int rasterOffset;
@ -502,12 +513,9 @@ namespace BizHawk.Emulation.Computers.Commodore64
public int rasterWidth; public int rasterWidth;
public int refreshAddress; public int refreshAddress;
public int renderOffset; public int renderOffset;
public byte[,] spriteData; public int spriteFetchStartCycle;
public int spriteFetchCycle;
public int spriteFetchIndex; public int spriteFetchIndex;
public bool spriteForeground; public bool spriteForeground;
public ushort[] spritePointers;
public int spriteFetchStartCycle;
public VicIITask task; public VicIITask task;
public int totalCycles; public int totalCycles;
public bool vBlank; public bool vBlank;
@ -539,7 +547,7 @@ namespace BizHawk.Emulation.Computers.Commodore64
rasterTotalLines = 263; rasterTotalLines = 263;
rasterLineLeft = 0x19C; rasterLineLeft = 0x19C;
cycleLeft = 0; cycleLeft = 0;
spriteFetchStartCycle = 55; spriteFetchStartCycle = 59;
visibleLeft = 0x008; visibleLeft = 0x008;
visibleRight = 0x168; visibleRight = 0x168;
visibleTop = 0x023; //0x041; visibleTop = 0x023; //0x041;
@ -566,15 +574,15 @@ namespace BizHawk.Emulation.Computers.Commodore64
buffer = new int[visibleWidth * visibleHeight]; buffer = new int[visibleWidth * visibleHeight];
bufferSize = buffer.Length; bufferSize = buffer.Length;
// initialize sprites
spritePointers = new ushort[8];
spriteData = new byte[8, 64];
// initialize screen buffer // initialize screen buffer
characterMemory = new byte[40]; characterMemory = new byte[40];
colorMemory = new byte[40]; colorMemory = new byte[40];
pixelBuffer = new int[12];
pixelBufferForeground = new bool[12];
pixelBufferIndex = 0;
// initialize registers // initialize registers
spriteFetchIndex = 0;
HardReset(); HardReset();
} }
@ -636,6 +644,7 @@ namespace BizHawk.Emulation.Computers.Commodore64
signal.VicIRQ = false; signal.VicIRQ = false;
UpdateBorder(); UpdateBorder();
UpdatePlotter(); UpdatePlotter();
spriteFetchIndex = 17;
} }
public void PerformCycle() public void PerformCycle()
@ -653,6 +662,11 @@ namespace BizHawk.Emulation.Computers.Commodore64
if (badLine) if (badLine)
idle = false; idle = false;
// sprite Y stretch flipflop
for (int i = 0; i < 8; i++)
if (!regs.MxYE[i])
regs.MYE[i] = true;
// these actions are exclusive // these actions are exclusive
if ((regs.RASTER == 0x000 && cycle == 1) || (regs.RASTER > 0x000 && cycle == 0)) if ((regs.RASTER == 0x000 && cycle == 1) || (regs.RASTER > 0x000 && cycle == 0))
{ {
@ -666,6 +680,21 @@ namespace BizHawk.Emulation.Computers.Commodore64
mem.VicRead((ushort)refreshAddress); mem.VicRead((ushort)refreshAddress);
refreshAddress = (refreshAddress - 1) & 0xFF; refreshAddress = (refreshAddress - 1) & 0xFF;
refreshAddress |= 0x3F00; refreshAddress |= 0x3F00;
// VC reset
if (cycle == 13)
{
regs.VC = regs.VCBASE;
regs.VMLI = 0;
characterColumn = 0;
if (badLine)
{
regs.RC = 0;
}
bitmapData = 0;
colorData = 0;
characterData = 0;
}
} }
else if (cycle >= 15 && cycle < 55) else if (cycle >= 15 && cycle < 55)
{ {
@ -678,12 +707,29 @@ namespace BizHawk.Emulation.Computers.Commodore64
} }
signal.VicAEC = !badLine; signal.VicAEC = !badLine;
} }
else if (cycle == 56) else if (cycle == 55)
{ {
// reenable cpu if disabled
signal.VicAEC = true; signal.VicAEC = true;
// sprite comparison
for (int i = 0; i < 8; i++)
{
if (regs.MxYE[i])
regs.MYE[i] = !regs.MYE[i];
if (regs.MxE[i] == true && regs.MxY[i] == (regs.RASTER & 0xFF) && regs.MDMA[i] == false)
{
regs.MDMA[i] = true;
regs.MCBASE[i] = 0;
if (regs.MxYE[i])
regs.MYE[i] = false;
}
}
} }
else if (cycle == 57) else if (cycle == 57)
{ {
// row counter processing
if (regs.RC == 7) if (regs.RC == 7)
{ {
idle = true; idle = true;
@ -691,38 +737,79 @@ namespace BizHawk.Emulation.Computers.Commodore64
} }
if (!idle) if (!idle)
regs.RC = (regs.RC + 1) & 0x07; regs.RC = (regs.RC + 1) & 0x07;
// sprite MC processing
for (int i = 0; i < 8; i++)
{
regs.MC[i] = regs.MCBASE[i];
if (regs.MDMA[i] && regs.MxY[i] == (regs.RASTER & 0xFF))
regs.MD[i] = true;
}
} }
// these actions can fall within the other ranges // MCBASE reset if applicable
if (cycle == 13) if (cycle == 15)
{ {
regs.VC = regs.VCBASE; for (int i = 0; i < 8; i++)
regs.VMLI = 0;
characterColumn = 0;
if (badLine)
{ {
regs.RC = 0; if (regs.MYE[i])
{
regs.MCBASE[i] += 3;
if (regs.MCBASE[i] == 63)
{
regs.MD[i] = false;
regs.MDMA[i] = false;
}
}
} }
bitmapData = 0;
colorData = 0;
characterData = 0;
}
else if (cycle == spriteFetchStartCycle)
{
spriteFetchIndex = 0;
} }
// sprite fetch // sprite fetch
if (spriteFetchIndex < 8) if (cycle == spriteFetchStartCycle)
{ {
spriteFetchIndex = 0;
}
if (spriteFetchIndex < 16)
{
int spriteIndex = spriteFetchIndex >> 1;
PerformCycleSpriteFetch(spriteFetchIndex, spriteFetchCycle); if ((spriteFetchIndex & 1) == 0)
spriteFetchCycle++;
if (spriteFetchCycle >= 2)
{ {
spriteFetchCycle = 0; // first half of the fetch cycle, always fetch pointer
spriteFetchIndex++; ushort pointerOffset = (ushort)((regs.VM << 10) + 0x3F8 + spriteIndex);
regs.MPTR[spriteIndex] = mem.VicRead(pointerOffset);
// also fetch upper 8 bits if enabled
//signal.VicAEC = regs.MDMA[spriteIndex];
if (regs.MDMA[spriteIndex])
{
regs.MSRC[spriteIndex] = 24;
regs.MSR[spriteIndex] = mem.VicRead((ushort)((regs.MPTR[spriteIndex] << 6) | (regs.MC[spriteIndex])));
regs.MSR[spriteIndex] <<= 8;
regs.MC[spriteIndex]++;
}
} }
else
{
// second half of the fetch cycle
//signal.VicAEC = regs.MDMA[spriteIndex];
if (regs.MDMA[spriteIndex])
{
for (int i = 0; i < 2; i++)
{
regs.MSR[spriteIndex] |= mem.VicRead((ushort)((regs.MPTR[spriteIndex] << 6) | (regs.MC[spriteIndex])));
regs.MSR[spriteIndex] <<= 8;
regs.MC[spriteIndex]++;
}
}
}
spriteFetchIndex++;
}
else if (spriteFetchIndex == 16)
{
// set AEC after sprite fetches
//signal.VicAEC = true;
spriteFetchIndex++;
} }
// border check // border check
@ -772,16 +859,17 @@ namespace BizHawk.Emulation.Computers.Commodore64
private void PerformCycleRender() private void PerformCycleRender()
{ {
int spritePixel;
int inputPixel;
int outputPixel;
int spritePixelOwner;
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
{ {
spritePixelOwner = -1;
// draw screen memory if needed // draw screen memory if needed
if (!idle && cycle >= 15 && cycle < 55) if (!idle && cycle >= 15 && cycle < 55)
{ {
// todo: implement XSCROLL properly
// comparing to (rasterOffsetX & 0x7) seems to align
// properly but smooth scrolling is not possible
// comparing to i works but then the screen
// is not aligned anymore...
if (regs.XSCROLL == i) if (regs.XSCROLL == i)
{ {
characterColumn = 0; characterColumn = 0;
@ -812,6 +900,102 @@ namespace BizHawk.Emulation.Computers.Commodore64
borderOnMain = false; borderOnMain = false;
} }
// render plotter
if (idle)
{
inputPixel = regs.BxC[0];
}
else
{
inputPixel = Plotter();
}
// render sprites
outputPixel = pixelBuffer[pixelBufferIndex];
for (int j = 0; j < 8; j++)
{
int spriteBits;
if (regs.MxX[j] == rasterOffsetX)
{
regs.MSRA[j] = true;
regs.MSRC[j] = 24;
}
if (regs.MD[j] && regs.MSRA[j])
{
// multicolor consumes two bits per pixel
if (regs.MxMC[j])
{
spriteBits = (int)((regs.MSR[j] >> 30) & 0x3);
if ((regs.MxXE[j] == false) || (rasterOffsetX & 0x1) != (regs.MxX[j] & 0x1))
{
regs.MSR[j] <<= 2;
regs.MSRC[j]--;
}
}
else
{
spriteBits = (int)((regs.MSR[j] >> 30) & 0x2);
//
if ((regs.MxXE[j] == false) || (rasterOffsetX & 0x1) != (regs.MxX[j] & 0x1))
{
regs.MSR[j] <<= 1;
regs.MSRC[j]--;
}
}
// if not transparent, process collisions and color
if (spriteBits != 0)
{
switch (spriteBits)
{
case 1:
spritePixel = regs.MMx[0];
break;
case 2:
spritePixel = regs.MxC[j];
break;
case 3:
spritePixel = regs.MMx[1];
break;
default:
// this should never happen but VS needs this
spritePixel = 0;
break;
}
// process collisions if the border is off
if (!borderOnVertical)
{
if (spritePixelOwner == -1)
{
spritePixelOwner = j;
if (regs.MxDP[j] || (!regs.MxDP[j] && !pixelBufferForeground[pixelBufferIndex]))
{
outputPixel = spritePixel;
}
}
else
{
// a sprite already occupies this space
//regs.MxM[spritePixelOwner] = true;
//regs.MxM[j] = true;
//regs.IMMC = true;
}
if (dataForeground)
{
//regs.MxD[j] = true;
//regs.IMBC = true;
}
}
}
if (regs.MSRC[j] == 0)
regs.MSRA[j] = false;
}
}
// send pixel to bitmap // send pixel to bitmap
if (visibleRenderX && visibleRenderY) if (visibleRenderX && visibleRenderY)
{ {
@ -821,19 +1005,13 @@ namespace BizHawk.Emulation.Computers.Commodore64
} }
else else
{ {
WritePixel(pixelBuffer[pixelBufferIndex]); WritePixel(outputPixel);
} }
} }
// process 12 pixel delay // process 12 pixel delay
if (idle) pixelBuffer[pixelBufferIndex] = inputPixel;
{ pixelBufferForeground[pixelBufferIndex] = dataForeground;
pixelBuffer[pixelBufferIndex] = regs.BxC[0];
}
else
{
pixelBuffer[pixelBufferIndex] = Plotter();
}
pixelBufferIndex++; pixelBufferIndex++;
if (pixelBufferIndex == 12) if (pixelBufferIndex == 12)
{ {
@ -852,14 +1030,6 @@ namespace BizHawk.Emulation.Computers.Commodore64
} }
private void PerformCycleSpriteFetch(int index, int fetchCycle)
{
ushort offset = (ushort)((regs.VM << 10) + 0x3F8 + index);
spritePointers[index] = (ushort)((regs.VM << 10) + (mem.VicRead(offset) * 64));
for (ushort i = 0; i < 64; i++)
spriteData[index, i] = mem.VicRead((ushort)(spritePointers[index] + i));
}
// standard text mode // standard text mode
private int Plot000() private int Plot000()
{ {
@ -869,11 +1039,11 @@ namespace BizHawk.Emulation.Computers.Commodore64
charData <<= characterColumn; charData <<= characterColumn;
if ((charData & 0x80) != 0x00) if ((charData & 0x80) != 0x00)
{ {
spriteForeground = true; dataForeground = true;
return colorData; return colorData;
} }
} }
spriteForeground = false; dataForeground = false;
return regs.BxC[0]; return regs.BxC[0];
} }
@ -893,13 +1063,13 @@ namespace BizHawk.Emulation.Computers.Commodore64
switch (charData) switch (charData)
{ {
case 1: case 1:
spriteForeground = false; dataForeground = false;
return regs.BxC[1]; return regs.BxC[1];
case 2: case 2:
spriteForeground = true; dataForeground = true;
return regs.BxC[2]; return regs.BxC[2];
case 3: case 3:
spriteForeground = true; dataForeground = true;
return colorData & 0x07; return colorData & 0x07;
} }
} }
@ -908,7 +1078,7 @@ namespace BizHawk.Emulation.Computers.Commodore64
return Plot000(); return Plot000();
} }
} }
spriteForeground = false; dataForeground = false;
return regs.BxC[0]; return regs.BxC[0];
} }
@ -921,11 +1091,11 @@ namespace BizHawk.Emulation.Computers.Commodore64
charData <<= characterColumn; charData <<= characterColumn;
if ((charData & 0x80) != 0x00) if ((charData & 0x80) != 0x00)
{ {
spriteForeground = true; dataForeground = true;
return characterData >> 4; return characterData >> 4;
} }
} }
spriteForeground = false; dataForeground = false;
return characterData & 0xF; return characterData & 0xF;
} }
@ -943,17 +1113,17 @@ namespace BizHawk.Emulation.Computers.Commodore64
switch (charData) switch (charData)
{ {
case 1: case 1:
spriteForeground = false; dataForeground = false;
return characterData & 0xF; return characterData & 0xF;
case 2: case 2:
spriteForeground = true; dataForeground = true;
return characterData >> 4; return characterData >> 4;
case 3: case 3:
spriteForeground = true; dataForeground = true;
return colorData & 0xF; return colorData & 0xF;
} }
} }
spriteForeground = false; dataForeground = false;
return regs.BxC[0]; return regs.BxC[0];
} }
@ -966,16 +1136,16 @@ namespace BizHawk.Emulation.Computers.Commodore64
charData <<= characterColumn; charData <<= characterColumn;
if ((charData & 0x80) != 0x00) if ((charData & 0x80) != 0x00)
{ {
spriteForeground = true; dataForeground = true;
return colorData; return colorData;
} }
else else
{ {
spriteForeground = false; dataForeground = false;
return regs.BxC[characterData >> 6]; return regs.BxC[characterData >> 6];
} }
} }
spriteForeground = false; dataForeground = false;
return regs.BxC[0]; return regs.BxC[0];
} }
@ -1005,10 +1175,16 @@ namespace BizHawk.Emulation.Computers.Commodore64
switch (addr) switch (addr)
{ {
case 0x1E: case 0x1E:
case 0x1F: // clear after read
// collision regs clear after read
result = regs[addr]; result = regs[addr];
regs[addr] = 0x00; regs[addr] = 0x00;
regs.IMMC = false;
break;
case 0x1F:
// clear after read
result = regs[addr];
regs[addr] = 0x00;
regs.IMBC = false;
break; break;
default: default:
result = regs[addr]; result = regs[addr];