feat(core): add video/display frame buffer
This commit is contained in:
1 parent
87ac07c68e
commit
bd40840d67
1 file changed
+192
@@ -0,0 +1,192 @@
|
||||
using System.IO;
|
||||
|
||||
namespace Chip8.Core;
|
||||
|
||||
public sealed class Video
|
||||
{
|
||||
public const int loWidth = 64;
|
||||
public const int loHeight = 32;
|
||||
public const int hiWidth = 128;
|
||||
public const int hiHeight = 64;
|
||||
public const int maxPixels = hiWidth * hiHeight;
|
||||
public const int planeCount = 2;
|
||||
public const int scrollStep = 4;
|
||||
|
||||
public int width { get; private set; } = loWidth;
|
||||
public int height { get; private set; } = loHeight;
|
||||
public bool hires => width == hiWidth;
|
||||
|
||||
readonly bool[][] plane =
|
||||
{
|
||||
new bool[maxPixels],
|
||||
new bool[maxPixels],
|
||||
};
|
||||
|
||||
public void reset()
|
||||
{
|
||||
width = loWidth;
|
||||
height = loHeight;
|
||||
foreach (var buffer in plane)
|
||||
{
|
||||
System.Array.Clear(buffer, 0, buffer.Length);
|
||||
}
|
||||
}
|
||||
|
||||
public void setResolution(bool high)
|
||||
{
|
||||
width = high ? hiWidth : loWidth;
|
||||
height = high ? hiHeight : loHeight;
|
||||
foreach (var buffer in plane)
|
||||
{
|
||||
System.Array.Clear(buffer, 0, buffer.Length);
|
||||
}
|
||||
}
|
||||
|
||||
public bool pixel(int planeIndex, int x, int y) => plane[planeIndex][y * width + x];
|
||||
|
||||
internal void writeTo(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(width);
|
||||
writer.Write(height);
|
||||
for (int p = 0; p < planeCount; p++)
|
||||
{
|
||||
for (int cell = 0; cell < maxPixels; cell++)
|
||||
{
|
||||
writer.Write(plane[p][cell]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void readFrom(BinaryReader reader)
|
||||
{
|
||||
width = reader.ReadInt32();
|
||||
height = reader.ReadInt32();
|
||||
for (int p = 0; p < planeCount; p++)
|
||||
{
|
||||
for (int cell = 0; cell < maxPixels; cell++)
|
||||
{
|
||||
plane[p][cell] = reader.ReadBoolean();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int colorAt(int x, int y)
|
||||
{
|
||||
int index = y * width + x;
|
||||
int color = 0;
|
||||
for (int p = 0; p < planeCount; p++)
|
||||
{
|
||||
if (plane[p][index])
|
||||
{
|
||||
color |= 1 << p;
|
||||
}
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
public void clear(int planeMask)
|
||||
{
|
||||
for (int p = 0; p < planeCount; p++)
|
||||
{
|
||||
if ((planeMask & (1 << p)) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for (int cell = 0; cell < width * height; cell++)
|
||||
{
|
||||
plane[p][cell] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void scrollDown(int rows, int planeMask) => shift(0, rows, planeMask);
|
||||
|
||||
public void scrollUp(int rows, int planeMask) => shift(0, -rows, planeMask);
|
||||
|
||||
public void scrollRight(int columns, int planeMask) => shift(columns, 0, planeMask);
|
||||
|
||||
public void scrollLeft(int columns, int planeMask) => shift(-columns, 0, planeMask);
|
||||
|
||||
void shift(int dx, int dy, int planeMask)
|
||||
{
|
||||
for (int p = 0; p < planeCount; p++)
|
||||
{
|
||||
if ((planeMask & (1 << p)) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bool[] buffer = plane[p];
|
||||
var source = (bool[])buffer.Clone();
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
int sx = x - dx;
|
||||
int sy = y - dy;
|
||||
bool value = sx >= 0 && sx < width && sy >= 0 && sy < height
|
||||
&& source[sy * width + sx];
|
||||
buffer[y * width + x] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool drawSprite(byte[] memory, int address, int originX, int originY, int rows, int planeMask, bool wrap)
|
||||
{
|
||||
int spriteRows = rows == 0 ? 16 : rows;
|
||||
int bytesPerRow = rows == 0 ? 2 : 1;
|
||||
int spriteWidth = bytesPerRow * 8;
|
||||
int startX = originX % width;
|
||||
int startY = originY % height;
|
||||
bool collision = false;
|
||||
int addr = address;
|
||||
|
||||
for (int p = 0; p < planeCount; p++)
|
||||
{
|
||||
if ((planeMask & (1 << p)) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bool[] buffer = plane[p];
|
||||
for (int row = 0; row < spriteRows; row++)
|
||||
{
|
||||
int rowBits = 0;
|
||||
for (int b = 0; b < bytesPerRow; b++)
|
||||
{
|
||||
rowBits = (rowBits << 8) | memory[addr++ & (memory.Length - 1)];
|
||||
}
|
||||
|
||||
for (int col = 0; col < spriteWidth; col++)
|
||||
{
|
||||
if ((rowBits & (1 << (spriteWidth - 1 - col))) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int px = startX + col;
|
||||
int py = startY + row;
|
||||
if (wrap)
|
||||
{
|
||||
px %= width;
|
||||
py %= height;
|
||||
}
|
||||
else if (px >= width || py >= height)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int index = py * width + px;
|
||||
if (buffer[index])
|
||||
{
|
||||
collision = true;
|
||||
}
|
||||
buffer[index] ^= true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return collision;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user