melonDS/GPU3D.cpp

347 lines
7.0 KiB
C++
Raw Normal View History

/*
Copyright 2016-2017 StapleButter
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include <string.h>
#include "NDS.h"
#include "GPU.h"
#include "FIFO.h"
namespace GPU3D
{
const u32 CmdNumParams[256] =
{
// 0x00
0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// 0x10
1, 0, 1, 1, 1, 0, 16, 12, 16, 12, 9, 3, 3,
0, 0, 0,
// 0x20
1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0,
// 0x30
1, 1, 1, 1, 32,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// 0x40
1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// 0x50
1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// 0x60
1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// 0x70
3, 2, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// 0x80+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
const s32 CmdNumCycles[256] =
{
// 0x00
0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// 0x10
1, 17, 36, 17, 36, 19, 34, 30, 35, 31, 28, 22, 22,
0, 0, 0,
// 0x20
1, 9, 1, 9, 8, 8, 8, 8, 8, 1, 1, 1,
0, 0, 0, 0,
// 0x30
4, 4, 6, 1, 32,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// 0x40
1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// 0x50
392,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// 0x60
1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// 0x70
103, 9, 5,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// 0x80+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
typedef struct
{
u8 Command;
u32 Param;
} CmdFIFOEntry;
FIFO<CmdFIFOEntry>* CmdFIFO;
FIFO<CmdFIFOEntry>* CmdPIPE;
u32 NumCommands, CurCommand, ParamCount, TotalParams;
u32 GXStat;
u32 ExecParams[32];
u32 ExecParamCount;
s32 CycleCount;
bool Init()
{
CmdFIFO = new FIFO<CmdFIFOEntry>(256);
CmdPIPE = new FIFO<CmdFIFOEntry>(4);
return true;
}
void DeInit()
{
delete CmdFIFO;
delete CmdPIPE;
}
void Reset()
{
CmdFIFO->Clear();
CmdPIPE->Clear();
NumCommands = 0;
CurCommand = 0;
ParamCount = 0;
TotalParams = 0;
GXStat = 0;
memset(ExecParams, 0, 32*4);
ExecParamCount = 0;
CycleCount = 0;
}
void CmdFIFOWrite(CmdFIFOEntry& entry)
{
if (CmdFIFO->IsEmpty() && !CmdPIPE->IsFull())
{
CmdPIPE->Write(entry);
}
else
{
if (CmdFIFO->IsFull())
{
printf("!!! GX FIFO FULL\n");
return;
}
CmdFIFO->Write(entry);
}
}
CmdFIFOEntry CmdFIFORead()
{
CmdFIFOEntry ret = CmdPIPE->Read();
if (CmdPIPE->Level() <= 2)
{
if (!CmdFIFO->IsEmpty())
CmdPIPE->Write(CmdFIFO->Read());
if (!CmdFIFO->IsEmpty())
CmdPIPE->Write(CmdFIFO->Read());
}
CheckFIFOIRQ();
return ret;
}
void ExecuteCommand()
{
CmdFIFOEntry entry = CmdFIFORead();
ExecParams[ExecParamCount] = entry.Param;
ExecParamCount++;
if (ExecParamCount >= CmdNumParams[entry.Command])
{
CycleCount += CmdNumCycles[entry.Command];
ExecParamCount = 0;
// TODO: actually execute the command, maybe
//printf("3D CMD %02X\n", entry.Command);
switch (entry.Command)
{
case 0x18:
case 0x19:
case 0x1A:
// TODO: more cycles if MTX_MODE=2
break;
case 0x21:
// TODO: more cycles if lights are enabled
break;
case 0x50:
// TODO: make it happen upon VBlank, not right now
break;
}
}
}
void Run(s32 cycles)
{
if (CycleCount <= 0)
{
while (CycleCount <= 0 && !CmdPIPE->IsEmpty())
ExecuteCommand();
if (CmdPIPE->IsEmpty())
CycleCount = 0;
}
else
CycleCount -= cycles;
}
void CheckFIFOIRQ()
{
bool irq = false;
switch (GXStat >> 30)
{
case 1: irq = (CmdFIFO->Level() < 128); break;
case 2: irq = CmdFIFO->IsEmpty(); break;
}
if (irq) NDS::TriggerIRQ(0, NDS::IRQ_GXFIFO);
}
u8 Read8(u32 addr)
{
return 0;
}
u16 Read16(u32 addr)
{
return 0;
}
u32 Read32(u32 addr)
{
switch (addr)
{
case 0x04000320:
return 46; // TODO, eventually
case 0x04000600:
{
u32 fifolevel = CmdFIFO->Level();
return GXStat |
// matrix stack levels, TODO
(fifolevel << 16) |
(fifolevel < 128 ? (1<<25) : 0) |
(fifolevel == 0 ? (1<<26) : 0) |
(CycleCount > 0 ? (1<<27) : 0);
}
}
return 0;
}
void Write8(u32 addr, u8 val)
{
//
}
void Write16(u32 addr, u16 val)
{
//
}
void Write32(u32 addr, u32 val)
{
switch (addr)
{
case 0x04000600:
if (val & 0x8000) GXStat &= ~0x8000;
val &= 0xC0000000;
GXStat &= 0x3FFFFFFF;
GXStat |= val;
return;
}
if (addr >= 0x04000400 && addr < 0x04000440)
{
if (NumCommands == 0)
{
NumCommands = 4;
CurCommand = val;
ParamCount = 0;
TotalParams = CmdNumParams[CurCommand & 0xFF];
}
else
ParamCount++;
while (ParamCount == TotalParams)
{
CmdFIFOEntry entry;
entry.Command = CurCommand & 0xFF;
entry.Param = val;
CmdFIFOWrite(entry);
CurCommand >>= 8;
NumCommands--;
if (NumCommands == 0) break;
ParamCount = 0;
TotalParams = CmdNumParams[CurCommand & 0xFF];
}
}
if (addr >= 0x04000440 && addr < 0x040005CC)
{
CmdFIFOEntry entry;
entry.Command = (addr & 0x1FC) >> 2;
entry.Param = val;
CmdFIFOWrite(entry);
return;
}
}
}