mirror of
https://github.com/liuk7071/ChonkyStation.git
synced 2024-06-02 11:18:02 -04:00
383 lines
9.1 KiB
C++
383 lines
9.1 KiB
C++
#include "gpu.hpp"
|
|
|
|
|
|
MAKE_LOG_FUNCTION(log, gpuLogger)
|
|
|
|
void GPU::scanlineEvent(void* classptr) {
|
|
GPU* gpu = (GPU*)classptr;
|
|
|
|
if (gpu->currentScanline < GPUConstants::scanlinesPerVdraw) {
|
|
gpu->stat ^= 1 << 31; // Interlacing
|
|
}
|
|
else if (gpu->currentScanline == GPUConstants::scanlinesPerVdraw) {
|
|
gpu->vblank = true;
|
|
}
|
|
else if (gpu->currentScanline == GPUConstants::scanlinesPerFrame) {
|
|
gpu->currentScanline = 0;
|
|
}
|
|
|
|
gpu->currentScanline++;
|
|
|
|
gpu->scheduler->push(scanlineEvent, gpu->scheduler->time + GPUConstants::cyclesPerScanline, gpu);
|
|
}
|
|
|
|
u32 GPU::getStat() {
|
|
// Stubbed
|
|
stat |= 1 << 26; // Ready to receive cmd word
|
|
stat |= 1 << 27; // Ready to send VRAM to CPU
|
|
stat |= 1 << 28; // Ready to receive DMA block
|
|
stat |= 1 << 30; // DMA direction CPU -> GP0
|
|
return stat;
|
|
}
|
|
|
|
u32 GPU::gpuRead() {
|
|
// Stubbed
|
|
return 0;
|
|
}
|
|
|
|
void GPU::writeGp0(u32 data) {
|
|
if (!hasCommand) {
|
|
startCommand(data);
|
|
fifo.push_back(data);
|
|
return;
|
|
}
|
|
|
|
paramsLeft--;
|
|
if (uploadingTexture) {
|
|
backend->textureUploadData(data & 0xffff);
|
|
backend->textureUploadData(data >> 16);
|
|
}
|
|
else {
|
|
fifo.push_back(data);
|
|
}
|
|
|
|
if (paramsLeft == 0) {
|
|
if (!uploadingTexture) {
|
|
switch (fifo[0] >> 24) {
|
|
case (u32)GP0Command::FillVRAM: {
|
|
// TODO
|
|
hasCommand = false;
|
|
break;
|
|
}
|
|
case (u32)GP0Command::UploadTexture: {
|
|
uploadingTexture = true;
|
|
// Calculate size
|
|
const u16 width = fifo[2] & 0xffff;
|
|
const u16 height = fifo[2] >> 16;
|
|
auto size = width * height;
|
|
// Round
|
|
size++;
|
|
size &= ~1;
|
|
// Each word contains two pixels, so we divide the amount of parameters left by 2
|
|
paramsLeft = size / 2;
|
|
uploadingTexture = true;
|
|
|
|
const u16 x = fifo[1] & 0xffff;
|
|
const u16 y = fifo[1] >> 16;
|
|
backend->beginTextureUpload(x, y, width);
|
|
|
|
log(" Width : %d\n", width);
|
|
log(" Height: %d\n", height);
|
|
log(" (x, y): (%d, %d)\n", x, y);
|
|
break;
|
|
}
|
|
case (u32)GP0Command::ReadVRAM: {
|
|
// TODO
|
|
hasCommand = false;
|
|
break;
|
|
}
|
|
default:
|
|
hasCommand = false;
|
|
|
|
DrawCommand drawCommand(fifo[0]);
|
|
if (drawCommand.drawType == DrawCommand::DrawType::Polygon) {
|
|
auto poly = drawCommand.getPolygon();
|
|
auto nVerts = poly.quad ? 4 : 3;
|
|
Vertex verts[4];
|
|
std::memset(verts, 0, sizeof(Vertex) * 4);
|
|
auto idx = 0;
|
|
u32 col = fifo[idx++] & 0xffffff;
|
|
verts[0].writeBGR888(col);
|
|
verts[0].x = fifo[idx] & 0xffff;
|
|
verts[0].y = (fifo[idx++] >> 16) & 0xffff;
|
|
if (poly.textured) {
|
|
const u16 uv = fifo[idx++];
|
|
verts[0].u = uv & 0xff;
|
|
verts[0].v = (uv >> 8) & 0xff;
|
|
}
|
|
|
|
for (int i = 1; i < nVerts; i++) {
|
|
// Colour
|
|
if (poly.shaded)
|
|
verts[i].writeBGR888(fifo[idx++] & 0xffffff);
|
|
else
|
|
verts[i].writeBGR888(col);
|
|
// Coords
|
|
const u32 coords = fifo[idx++];
|
|
verts[i].x = coords & 0xffff;
|
|
verts[i].y = (coords >> 16) & 0xffff;
|
|
// Texcoords
|
|
if (poly.textured) {
|
|
const u16 uv = fifo[idx++];
|
|
verts[i].u = uv & 0xff;
|
|
verts[i].v = (uv >> 8) & 0xff;
|
|
}
|
|
}
|
|
|
|
// Draw
|
|
if (!poly.textured) {
|
|
backend->drawTriUntextured(verts[0], verts[1], verts[2]);
|
|
if (poly.quad)
|
|
backend->drawTriUntextured(verts[1], verts[2], verts[3]);
|
|
}
|
|
else {
|
|
u16 clut = fifo[2] >> 16;
|
|
u16 texpage = fifo[4] >> 16;
|
|
backend->drawTriTextured(verts[0], verts[1], verts[2], clut, texpage);
|
|
if (poly.quad)
|
|
backend->drawTriTextured(verts[1], verts[2], verts[3], clut, texpage);
|
|
}
|
|
}
|
|
else if (drawCommand.drawType == DrawCommand::DrawType::Rectangle) {
|
|
auto rect = drawCommand.getRectangle();
|
|
Vertex vert;
|
|
auto idx = 0;
|
|
u32 col = fifo[idx++] & 0xffffff;
|
|
vert.writeBGR888(col);
|
|
vert.x = fifo[idx] & 0xffff;
|
|
vert.y = (fifo[idx++] >> 16) & 0xffff;
|
|
if (rect.textured) {
|
|
vert.u = fifo[idx] & 0xff;
|
|
vert.v = (fifo[idx++] >> 8) & 0xff;
|
|
}
|
|
|
|
// TODO: textured
|
|
Helpers::debugAssert(!rect.textured, "[FATAL] Unimplemented textured rectangle\n");
|
|
|
|
u16 width = 0;
|
|
u16 height = 0;
|
|
|
|
if (rect.size == 0) {
|
|
width = fifo[idx] & 0xffff;
|
|
height = (fifo[idx++] >> 8) & 0xffff;
|
|
}
|
|
else {
|
|
switch (rect.size) {
|
|
case 1: width = 1; height = 1; break;
|
|
case 2: width = 8; height = 8; break;
|
|
case 3: width = 16; height = 16; break;
|
|
}
|
|
}
|
|
|
|
backend->drawRectUntextured(vert, width, height);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
backend->endTextureUpload();
|
|
uploadingTexture = false;
|
|
hasCommand = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GPU::writeGp1(u32 data) {
|
|
const auto cmd = (data >> 24) & 0xff;
|
|
|
|
switch (cmd) {
|
|
case (u32)GP1Command::ResetGPU: {
|
|
stat = 0x14802000;
|
|
break;
|
|
}
|
|
case (u32)GP1Command::ResetCommandBuffer: {
|
|
// Stubbed
|
|
break;
|
|
}
|
|
case (u32)GP1Command::AcknowledgeIRQ1: {
|
|
stat &= ~(1 << 24);
|
|
break;
|
|
}
|
|
case (u32)GP1Command::DisplayEnable: {
|
|
// Bits 0 is copied to GPUSTAT.23
|
|
stat &= ~(1 << 23);
|
|
stat |= (data & 1) << 23;
|
|
break;
|
|
}
|
|
case (u32)GP1Command::DMADirection: {
|
|
// Bits 0-1 are copied to GPUSTAT.29-30
|
|
stat &= ~(3 << 29);
|
|
stat |= (data & 3) << 29;
|
|
break;
|
|
}
|
|
case (u32)GP1Command::StartOfDisplayArea: {
|
|
// TODO: Stubbed for now
|
|
break;
|
|
}
|
|
case (u32)GP1Command::HorizontalDisplayRange: {
|
|
// TODO: Stubbed for now
|
|
break;
|
|
}
|
|
case (u32)GP1Command::VerticalDisplayRange: {
|
|
// TODO: Stubbed for now
|
|
break;
|
|
}
|
|
case (u32)GP1Command::DisplayMode: {
|
|
// Bits 0-5 are copied to GPUSTAT.17-22
|
|
stat &= ~(0x3f << 17);
|
|
stat |= (data & 0x3f) << 17;
|
|
// Bit 6 is copied to GPUSTAT.16
|
|
stat &= ~(1 << 16);
|
|
stat |= (data & (1 << 6)) << 10;
|
|
// Bit 7 is copied to GPUSTAT.17
|
|
stat &= ~(1 << 14);
|
|
stat |= (data & (1 << 7)) << 7;
|
|
break;
|
|
}
|
|
case (u32)GP1Command::GetGPUInfo: {
|
|
// TODO: STUBBED !!
|
|
log("STUBBED GETGPUINFO!!!\n");
|
|
break;
|
|
}
|
|
default:
|
|
Helpers::panic("[GPU] Unimplemented gp1 command 0x%02x\n", cmd);
|
|
}
|
|
}
|
|
|
|
void GPU::startCommand(u32 rawCommand) {
|
|
// Clear FIFO
|
|
fifo.clear();
|
|
// We handle single-word commands (i.e. all the configuration ones) in this function
|
|
const auto cmd = (rawCommand >> 24) & 0xff;
|
|
switch (cmd) {
|
|
case (u32)GP0Command::NOP: {
|
|
log("NOP\n");
|
|
// NOP
|
|
break;
|
|
}
|
|
case (u32)GP0Command::ClearCache: {
|
|
log("ClearCache\n");
|
|
// Stubbed
|
|
break;
|
|
}
|
|
case (u32)GP0Command::FillVRAM: {
|
|
log("FillVRAM\n");
|
|
paramsLeft = 2;
|
|
hasCommand = true;
|
|
break;
|
|
}
|
|
case (u32)GP0Command::UploadTexture: {
|
|
log("UploadTexture\n");
|
|
paramsLeft = 2;
|
|
hasCommand = true;
|
|
break;
|
|
}
|
|
case (u32)GP0Command::ReadVRAM: {
|
|
log("ReadVRAM\n");
|
|
paramsLeft = 2;
|
|
hasCommand = true;
|
|
break;
|
|
}
|
|
case (u32)GP0Command::DrawModeSetting: {
|
|
log("DrawModeSetting\n");
|
|
// Bits 0-10 are copied into GPUSTAT
|
|
stat &= ~0x7ff;
|
|
stat |= rawCommand & 0x7ff;
|
|
// Bit 11 is copied into GPUSTAT.15
|
|
stat &= ~(1 << 15);
|
|
stat |= (rawCommand & (1 << 11)) << 4;
|
|
break;
|
|
}
|
|
case (u32)GP0Command::TextureWindowSetting: {
|
|
log("TextureWindowSetting\n");
|
|
// TODO: Stubbed for now
|
|
break;
|
|
}
|
|
case (u32)GP0Command::SetDrawingAreaTopLeft: {
|
|
log("SetDrawingAreaTopLeft\n");
|
|
// TODO: Stubbed for now
|
|
break;
|
|
}
|
|
case (u32)GP0Command::SetDrawingAreaBottomRight: {
|
|
log("SetDrawingAreaBottomRight\n");
|
|
// TODO: Stubbed for now
|
|
break;
|
|
}
|
|
case (u32)GP0Command::SetDrawingOffset: {
|
|
log("SetDrawingOffset\n");
|
|
// TODO: Stubbed for now
|
|
break;
|
|
}
|
|
case (u32)GP0Command::MaskBitSetting: {
|
|
log("MaskBitSetting\n");
|
|
// TODO: Stubbed for now
|
|
break;
|
|
}
|
|
default: {
|
|
DrawCommand drawCommand(rawCommand);
|
|
hasCommand = true;
|
|
paramsLeft = drawCommand.getCommandSize();
|
|
}
|
|
}
|
|
}
|
|
|
|
GPU::DrawCommand::DrawCommand(u32 raw) {
|
|
this->raw = raw;
|
|
switch ((raw >> 29) & 7) {
|
|
|
|
// Polygon
|
|
case 0b001: {
|
|
Polygon temp = { .raw = raw };
|
|
log("Polygon:\n");
|
|
log(temp.quad ? " Quad\n" : " Tri\n");
|
|
log(temp.shaded ? " Shaded\n" : " Monochrome\n");
|
|
log(temp.textured ? " Textured\n" : " Untextured\n");
|
|
drawType = DrawType::Polygon;
|
|
break;
|
|
}
|
|
|
|
// Rectangle
|
|
case 0b011: {
|
|
// TODO: Logging
|
|
drawType = DrawType::Rectangle;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
Helpers::panic("[GPU] Unimplemented gp0 command 0x%02x (0x%08x)\n", raw >> 24, raw);
|
|
}
|
|
}
|
|
|
|
// Returns the amount of words required for the command
|
|
u32 GPU::DrawCommand::getCommandSize() {
|
|
u32 size = 1;
|
|
if (drawType == DrawType::Polygon) {
|
|
// Get number of words required per vertex
|
|
Polygon poly = getPolygon();
|
|
if (poly.shaded) size++;
|
|
if (poly.textured) size++;
|
|
// Multiply by number of vertices
|
|
size *= poly.quad ? 4 : 3;
|
|
// First colour is included in the command word
|
|
if (poly.shaded) size--;
|
|
}
|
|
else if (drawType == DrawType::Rectangle) {
|
|
Rectangle rect = getRectangle();
|
|
if (rect.textured) size++;
|
|
if (rect.size == 0) size++;
|
|
}
|
|
else {
|
|
Helpers::panic("[GPU] Tried to get command size for unimplemented command type\n");
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
GPU::DrawCommand::Polygon GPU::DrawCommand::getPolygon() {
|
|
if (drawType != DrawType::Polygon) Helpers::panic("[GPU] Tried to getPolygon but drawType was not polygon\n");
|
|
return { .raw = this->raw };
|
|
}
|
|
|
|
GPU::DrawCommand::Rectangle GPU::DrawCommand::getRectangle() {
|
|
if (drawType != DrawType::Rectangle) Helpers::panic("[GPU] Tried to getRectangle but drawType was not rectangle\n");
|
|
return { .raw = this->raw };
|
|
} |