reorganize repo, move shit around

This commit is contained in:
StapleButter
2017-03-16 23:01:22 +01:00
parent 10ca9b6f7f
commit 8a4ed8f41c
38 changed files with 717 additions and 43 deletions

411
src/ARM.cpp Normal file
View File

@ -0,0 +1,411 @@
/*
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 "NDS.h"
#include "ARM.h"
#include "ARMInterpreter.h"
#include "GPU3D.h"
u32 ARM::ConditionTable[16] =
{
0xF0F0, // EQ
0x0F0F, // NE
0xCCCC, // CS
0x3333, // CC
0xFF00, // MI
0x00FF, // PL
0xAAAA, // VS
0x5555, // VC
0x0C0C, // HI
0xF3F3, // LS
0xAA55, // GE
0x55AA, // LT
0x0A05, // GT
0xF5FA, // LE
0xFFFF, // AL
0x0000 // NE
};
ARM::ARM(u32 num)
{
// well uh
Num = num;
for (int i = 0; i < 16; i++)
{
Waitstates[0][i] = 1;
Waitstates[1][i] = 1;
Waitstates[2][i] = 1;
Waitstates[3][i] = 1;
}
if (!num)
{
// ARM9
Waitstates[0][0x2] = 1; // main RAM timing, assuming cache hit
Waitstates[0][0x3] = 4;
Waitstates[0][0x4] = 4;
Waitstates[0][0x5] = 5;
Waitstates[0][0x6] = 5;
Waitstates[0][0x7] = 4;
Waitstates[0][0x8] = 19;
Waitstates[0][0x9] = 19;
Waitstates[0][0xF] = 4;
Waitstates[1][0x2] = 1;
Waitstates[1][0x3] = 8;
Waitstates[1][0x4] = 8;
Waitstates[1][0x5] = 10;
Waitstates[1][0x6] = 10;
Waitstates[1][0x7] = 8;
Waitstates[1][0x8] = 38;
Waitstates[1][0x9] = 38;
Waitstates[1][0xF] = 8;
Waitstates[2][0x2] = 1;
Waitstates[2][0x3] = 2;
Waitstates[2][0x4] = 2;
Waitstates[2][0x5] = 2;
Waitstates[2][0x6] = 2;
Waitstates[2][0x7] = 2;
Waitstates[2][0x8] = 12;
Waitstates[2][0x9] = 12;
Waitstates[2][0xA] = 20;
Waitstates[2][0xF] = 2;
Waitstates[3][0x2] = 1;
Waitstates[3][0x3] = 2;
Waitstates[3][0x4] = 2;
Waitstates[3][0x5] = 4;
Waitstates[3][0x6] = 4;
Waitstates[3][0x7] = 2;
Waitstates[3][0x8] = 24;
Waitstates[3][0x9] = 24;
Waitstates[3][0xA] = 20;
Waitstates[3][0xF] = 2;
}
else
{
// ARM7
Waitstates[0][0x0] = 1;
Waitstates[0][0x2] = 1;
Waitstates[0][0x3] = 1;
Waitstates[0][0x4] = 1;
Waitstates[0][0x6] = 1;
Waitstates[0][0x8] = 6;
Waitstates[0][0x9] = 6;
Waitstates[1][0x0] = 1;
Waitstates[1][0x2] = 2;
Waitstates[1][0x3] = 1;
Waitstates[1][0x4] = 1;
Waitstates[1][0x6] = 2;
Waitstates[1][0x8] = 12;
Waitstates[1][0x9] = 12;
Waitstates[2][0x0] = 1;
Waitstates[2][0x2] = 1;
Waitstates[2][0x3] = 1;
Waitstates[2][0x4] = 1;
Waitstates[2][0x6] = 1;
Waitstates[2][0x8] = 6;
Waitstates[2][0x9] = 6;
Waitstates[2][0xA] = 10;
Waitstates[3][0x0] = 1;
Waitstates[3][0x2] = 2;
Waitstates[3][0x3] = 1;
Waitstates[3][0x4] = 1;
Waitstates[3][0x6] = 2;
Waitstates[3][0x8] = 12;
Waitstates[3][0x9] = 12;
Waitstates[3][0xA] = 10;
}
}
ARM::~ARM()
{
// dorp
}
void ARM::Reset()
{
Cycles = 0;
Halted = 0;
for (int i = 0; i < 16; i++)
R[i] = 0;
CPSR = 0x000000D3;
ExceptionBase = Num ? 0x00000000 : 0xFFFF0000;
// zorp
JumpTo(ExceptionBase);
}
void ARM::JumpTo(u32 addr, bool restorecpsr)
{
if (restorecpsr)
{
RestoreCPSR();
if (CPSR & 0x20) addr |= 0x1;
else addr &= ~0x1;
}
if (addr & 0x1)
{
addr &= ~0x1;
R[15] = addr+2;
NextInstr[0] = CodeRead16(addr);
NextInstr[1] = CodeRead16(addr+2);
CPSR |= 0x20;
}
else
{
addr &= ~0x3;
R[15] = addr+4;
NextInstr[0] = CodeRead32(addr);
NextInstr[1] = CodeRead32(addr+4);
CPSR &= ~0x20;
}
}
void ARM::RestoreCPSR()
{
u32 oldcpsr = CPSR;
switch (CPSR & 0x1F)
{
case 0x11:
CPSR = R_FIQ[7];
break;
case 0x12:
CPSR = R_IRQ[2];
break;
case 0x13:
CPSR = R_SVC[2];
break;
case 0x17:
CPSR = R_ABT[2];
break;
case 0x1B:
CPSR = R_UND[2];
break;
default:
printf("!! attempt to restore CPSR under bad mode %02X, %08X\n", CPSR&0x1F, R[15]);
break;
}
UpdateMode(oldcpsr, CPSR);
}
void ARM::UpdateMode(u32 oldmode, u32 newmode)
{
u32 temp;
#define SWAP(a, b) temp = a; a = b; b = temp;
if ((oldmode & 0x1F) == (newmode & 0x1F)) return;
switch (oldmode & 0x1F)
{
case 0x11:
SWAP(R[8], R_FIQ[0]);
SWAP(R[9], R_FIQ[1]);
SWAP(R[10], R_FIQ[2]);
SWAP(R[11], R_FIQ[3]);
SWAP(R[12], R_FIQ[4]);
SWAP(R[13], R_FIQ[5]);
SWAP(R[14], R_FIQ[6]);
break;
case 0x12:
SWAP(R[13], R_IRQ[0]);
SWAP(R[14], R_IRQ[1]);
break;
case 0x13:
SWAP(R[13], R_SVC[0]);
SWAP(R[14], R_SVC[1]);
break;
case 0x17:
SWAP(R[13], R_ABT[0]);
SWAP(R[14], R_ABT[1]);
break;
case 0x1B:
SWAP(R[13], R_UND[0]);
SWAP(R[14], R_UND[1]);
break;
}
switch (newmode & 0x1F)
{
case 0x11:
SWAP(R[8], R_FIQ[0]);
SWAP(R[9], R_FIQ[1]);
SWAP(R[10], R_FIQ[2]);
SWAP(R[11], R_FIQ[3]);
SWAP(R[12], R_FIQ[4]);
SWAP(R[13], R_FIQ[5]);
SWAP(R[14], R_FIQ[6]);
break;
case 0x12:
SWAP(R[13], R_IRQ[0]);
SWAP(R[14], R_IRQ[1]);
break;
case 0x13:
SWAP(R[13], R_SVC[0]);
SWAP(R[14], R_SVC[1]);
break;
case 0x17:
SWAP(R[13], R_ABT[0]);
SWAP(R[14], R_ABT[1]);
break;
case 0x1B:
SWAP(R[13], R_UND[0]);
SWAP(R[14], R_UND[1]);
break;
}
#undef SWAP
}
void ARM::TriggerIRQ()
{
if (CPSR & 0x80)
return;
u32 oldcpsr = CPSR;
CPSR &= ~0xFF;
CPSR |= 0xD2;
UpdateMode(oldcpsr, CPSR);
R_IRQ[2] = oldcpsr;
R[14] = R[15] + (oldcpsr & 0x20 ? 2 : 0);
JumpTo(ExceptionBase + 0x18);
}
s32 ARM::Execute()
{
if (Halted)
{
if (NDS::HaltInterrupted(Num))
{
Halted = 0;
if (NDS::IME[Num]&1)
TriggerIRQ();
}
else
{
Cycles = CyclesToRun;
GPU3D::Run(CyclesToRun >> 1);
return Cycles;
}
}
Cycles = 0;
s32 lastcycles = 0;
u32 addr = R[15] - (CPSR&0x20 ? 4:8);
u32 cpsr = CPSR;
while (Cycles < CyclesToRun)
{
//if(Num==1)printf("%08X %08X\n", R[15] - (CPSR&0x20 ? 4:8), NextInstr);
if (CPSR & 0x20) // THUMB
{
// prefetch
R[15] += 2;
CurInstr = NextInstr[0];
NextInstr[0] = NextInstr[1];
NextInstr[1] = CodeRead16(R[15]);
// actually execute
u32 icode = (CurInstr >> 6);
ARMInterpreter::THUMBInstrTable[icode](this);
}
else
{
// prefetch
R[15] += 4;
CurInstr = NextInstr[0];
NextInstr[0] = NextInstr[1];
NextInstr[1] = CodeRead32(R[15]);
// actually execute
if (CheckCondition(CurInstr >> 28))
{
u32 icode = ((CurInstr >> 4) & 0xF) | ((CurInstr >> 16) & 0xFF0);
ARMInterpreter::ARMInstrTable[icode](this);
}
else if ((CurInstr & 0xFE000000) == 0xFA000000)
{
ARMInterpreter::A_BLX_IMM(this);
}
}
//if (R[15]==0x037F9364) printf("R8=%08X R9=%08X\n", R[8], R[9]);
// gross hack
// TODO, though: move timer code here too?
// quick testing shows that moving this to the NDS loop doesn't really slow things down
if (Num==0)
{
s32 diff = Cycles - lastcycles;
GPU3D::Run(diff >> 1);
lastcycles = Cycles - (diff&1);
}
// TODO optimize this shit!!!
if (Halted)
{
if (Halted == 1)
Cycles = CyclesToRun;
break;
}
if (NDS::HaltInterrupted(Num))
{
if (NDS::IME[Num]&1)
TriggerIRQ();
}
// temp. debug cruft
addr = R[15] - (CPSR&0x20 ? 4:8);
cpsr = CPSR;
}
if (Halted == 2)
Halted = 0;
return Cycles;
}

234
src/ARM.h Normal file
View File

@ -0,0 +1,234 @@
/*
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/.
*/
#ifndef ARM_H
#define ARM_H
#include "types.h"
#include "NDS.h"
#include "CP15.h"
// lame
#define C_S(x) x
#define C_N(x) x
#define C_I(x) x
#define ROR(x, n) (((x) >> (n)) | ((x) << (32-(n))))
class ARM
{
public:
ARM(u32 num);
~ARM(); // destroy shit
void Reset();
void JumpTo(u32 addr, bool restorecpsr = false);
void RestoreCPSR();
void Halt(u32 halt)
{
Halted = halt;
}
s32 Execute();
bool CheckCondition(u32 code)
{
if (code == 0xE) return true;
if (ConditionTable[code] & (1 << (CPSR>>28))) return true;
return false;
}
void SetC(bool c)
{
if (c) CPSR |= 0x20000000;
else CPSR &= ~0x20000000;
}
void SetNZ(bool n, bool z)
{
CPSR &= ~0xC0000000;
if (n) CPSR |= 0x80000000;
if (z) CPSR |= 0x40000000;
}
void SetNZCV(bool n, bool z, bool c, bool v)
{
CPSR &= ~0xF0000000;
if (n) CPSR |= 0x80000000;
if (z) CPSR |= 0x40000000;
if (c) CPSR |= 0x20000000;
if (v) CPSR |= 0x10000000;
}
void UpdateMode(u32 oldmode, u32 newmode);
void TriggerIRQ();
u16 CodeRead16(u32 addr)
{
u16 val;
// TODO eventually: on ARM9, THUMB opcodes are prefetched with 32bit reads
if (!Num)
{
if (!CP15::HandleCodeRead16(addr, &val))
val = NDS::ARM9Read16(addr);
}
else
val = NDS::ARM7Read16(addr);
Cycles += Waitstates[0][(addr>>24)&0xF];
return val;
}
u32 CodeRead32(u32 addr)
{
u32 val;
if (!Num)
{
if (!CP15::HandleCodeRead32(addr, &val))
val = NDS::ARM9Read32(addr);
}
else
val = NDS::ARM7Read32(addr);
Cycles += Waitstates[1][(addr>>24)&0xF];
return val;
}
u8 DataRead8(u32 addr, u32 forceuser=0)
{
u8 val;
if (!Num)
{
if (!CP15::HandleDataRead8(addr, &val, forceuser))
val = NDS::ARM9Read8(addr);
}
else
val = NDS::ARM7Read8(addr);
Cycles += Waitstates[2][(addr>>24)&0xF];
return val;
}
u16 DataRead16(u32 addr, u32 forceuser=0)
{
u16 val;
addr &= ~1;
if (!Num)
{
if (!CP15::HandleDataRead16(addr, &val, forceuser))
val = NDS::ARM9Read16(addr);
}
else
val = NDS::ARM7Read16(addr);
Cycles += Waitstates[2][(addr>>24)&0xF];
return val;
}
u32 DataRead32(u32 addr, u32 forceuser=0)
{
u32 val;
addr &= ~3;
if (!Num)
{
if (!CP15::HandleDataRead32(addr, &val, forceuser))
val = NDS::ARM9Read32(addr);
}
else
val = NDS::ARM7Read32(addr);
Cycles += Waitstates[3][(addr>>24)&0xF];
return val;
}
void DataWrite8(u32 addr, u8 val, u32 forceuser=0)
{
if (!Num)
{
if (!CP15::HandleDataWrite8(addr, val, forceuser))
NDS::ARM9Write8(addr, val);
}
else
NDS::ARM7Write8(addr, val);
Cycles += Waitstates[2][(addr>>24)&0xF];
}
void DataWrite16(u32 addr, u16 val, u32 forceuser=0)
{
addr &= ~1;
if (!Num)
{
if (!CP15::HandleDataWrite16(addr, val, forceuser))
NDS::ARM9Write16(addr, val);
}
else
NDS::ARM7Write16(addr, val);
Cycles += Waitstates[2][(addr>>24)&0xF];
}
void DataWrite32(u32 addr, u32 val, u32 forceuser=0)
{
addr &= ~3;
if (!Num)
{
if (!CP15::HandleDataWrite32(addr, val, forceuser))
NDS::ARM9Write32(addr, val);
}
else
NDS::ARM7Write32(addr, val);
Cycles += Waitstates[3][(addr>>24)&0xF];
}
u32 Num;
// waitstates:
// 0=code16 1=code32 2=data16 3=data32
// TODO eventually: nonsequential waitstates
s32 Waitstates[4][16];
s32 Cycles;
s32 CyclesToRun;
u32 Halted;
u32 R[16]; // heh
u32 CPSR;
u32 R_FIQ[8]; // holding SPSR too
u32 R_SVC[3];
u32 R_ABT[3];
u32 R_IRQ[3];
u32 R_UND[3];
u32 CurInstr;
u32 NextInstr[2];
u32 ExceptionBase;
static u32 ConditionTable[16];
u32 debug;
};
#endif // ARM_H

221
src/ARMInterpreter.cpp Normal file
View File

@ -0,0 +1,221 @@
/*
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 "NDS.h"
#include "CP15.h"
#include "ARMInterpreter.h"
#include "ARMInterpreter_ALU.h"
#include "ARMInterpreter_Branch.h"
#include "ARMInterpreter_LoadStore.h"
namespace ARMInterpreter
{
void A_UNK(ARM* cpu)
{
printf("undefined ARM%d instruction %08X @ %08X\n", cpu->Num?7:9, cpu->CurInstr, cpu->R[15]-8);
for (int i = 0; i < 16; i++) printf("R%d: %08X\n", i, cpu->R[i]);
NDS::Halt();
}
void T_UNK(ARM* cpu)
{
printf("undefined THUMB%d instruction %04X @ %08X\n", cpu->Num?7:9, cpu->CurInstr, cpu->R[15]-4);
NDS::Halt();
}
void A_MSR_IMM(ARM* cpu)
{
u32* psr;
if (cpu->CurInstr & (1<<22))
{
switch (cpu->CPSR & 0x1F)
{
case 0x11: psr = &cpu->R_FIQ[7]; break;
case 0x12: psr = &cpu->R_IRQ[2]; break;
case 0x13: psr = &cpu->R_SVC[2]; break;
case 0x17: psr = &cpu->R_ABT[2]; break;
case 0x1B: psr = &cpu->R_UND[2]; break;
default: printf("bad CPU mode %08X\n", cpu->CPSR); return;
}
}
else
psr = &cpu->CPSR;
u32 oldpsr = *psr;
u32 mask = 0;
if (cpu->CurInstr & (1<<16)) mask |= 0x000000FF;
if (cpu->CurInstr & (1<<17)) mask |= 0x0000FF00;
if (cpu->CurInstr & (1<<18)) mask |= 0x00FF0000;
if (cpu->CurInstr & (1<<19)) mask |= 0xFF000000;
if (!(cpu->CurInstr & (1<<22)))
mask &= 0xFFFFFFDF;
if ((cpu->CPSR & 0x1F) == 0x10) mask &= 0xFFFFFF00;
u32 val = ROR((cpu->CurInstr & 0xFF), ((cpu->CurInstr >> 7) & 0x1E));
*psr &= ~mask;
*psr |= (val & mask);
if (!(cpu->CurInstr & (1<<22)))
cpu->UpdateMode(oldpsr, cpu->CPSR);
}
void A_MSR_REG(ARM* cpu)
{
u32* psr;
if (cpu->CurInstr & (1<<22))
{
switch (cpu->CPSR & 0x1F)
{
case 0x11: psr = &cpu->R_FIQ[7]; break;
case 0x12: psr = &cpu->R_IRQ[2]; break;
case 0x13: psr = &cpu->R_SVC[2]; break;
case 0x17: psr = &cpu->R_ABT[2]; break;
case 0x1B: psr = &cpu->R_UND[2]; break;
default: printf("bad CPU mode %08X\n", cpu->CPSR); return;
}
}
else
psr = &cpu->CPSR;
u32 oldpsr = *psr;
u32 mask = 0;
if (cpu->CurInstr & (1<<16)) mask |= 0x000000FF;
if (cpu->CurInstr & (1<<17)) mask |= 0x0000FF00;
if (cpu->CurInstr & (1<<18)) mask |= 0x00FF0000;
if (cpu->CurInstr & (1<<19)) mask |= 0xFF000000;
if (!(cpu->CurInstr & (1<<22)))
mask &= 0xFFFFFFDF;
if ((cpu->CPSR & 0x1F) == 0x10) mask &= 0xFFFFFF00;
u32 val = cpu->R[cpu->CurInstr & 0xF];
*psr &= ~mask;
*psr |= (val & mask);
if (!(cpu->CurInstr & (1<<22)))
cpu->UpdateMode(oldpsr, cpu->CPSR);
}
void A_MRS(ARM* cpu)
{
u32 psr;
if (cpu->CurInstr & (1<<22))
{
switch (cpu->CPSR & 0x1F)
{
case 0x11: psr = cpu->R_FIQ[7]; break;
case 0x12: psr = cpu->R_IRQ[2]; break;
case 0x13: psr = cpu->R_SVC[2]; break;
case 0x17: psr = cpu->R_ABT[2]; break;
case 0x1B: psr = cpu->R_UND[2]; break;
default: printf("bad CPU mode %08X\n", cpu->CPSR); return;
}
}
else
psr = cpu->CPSR;
cpu->R[(cpu->CurInstr>>12) & 0xF] = psr;
}
void A_MCR(ARM* cpu)
{
u32 cp = (cpu->CurInstr >> 8) & 0xF;
//u32 op = (cpu->CurInstr >> 21) & 0x7;
u32 cn = (cpu->CurInstr >> 16) & 0xF;
u32 cm = cpu->CurInstr & 0xF;
u32 cpinfo = (cpu->CurInstr >> 5) & 0x7;
if (cpu->Num==0 && cp==15)
{
CP15::Write((cn<<8)|(cm<<4)|cpinfo, cpu->R[(cpu->CurInstr>>12)&0xF]);
}
else
{
printf("bad MCR opcode p%d,%d,%d,%d on ARM%d\n", cp, cn, cm, cpinfo, cpu->Num?7:9);
}
cpu->Cycles += 2; // TODO: checkme
}
void A_MRC(ARM* cpu)
{
u32 cp = (cpu->CurInstr >> 8) & 0xF;
//u32 op = (cpu->CurInstr >> 21) & 0x7;
u32 cn = (cpu->CurInstr >> 16) & 0xF;
u32 cm = cpu->CurInstr & 0xF;
u32 cpinfo = (cpu->CurInstr >> 5) & 0x7;
if (cpu->Num==0 && cp==15)
{
cpu->R[(cpu->CurInstr>>12)&0xF] = CP15::Read((cn<<8)|(cm<<4)|cpinfo);
}
else
{
printf("bad MRC opcode p%d,%d,%d,%d on ARM%d\n", cp, cn, cm, cpinfo, cpu->Num?7:9);
}
cpu->Cycles += 3; // TODO: checkme
}
void A_SVC(ARM* cpu)
{
u32 oldcpsr = cpu->CPSR;
cpu->CPSR &= ~0xFF;
cpu->CPSR |= 0xD3;
cpu->UpdateMode(oldcpsr, cpu->CPSR);
cpu->R_SVC[2] = oldcpsr;
cpu->R[14] = cpu->R[15] - 4;
cpu->JumpTo(cpu->ExceptionBase + 0x08);
}
void T_SVC(ARM* cpu)
{
u32 oldcpsr = cpu->CPSR;
cpu->CPSR &= ~0xFF;
cpu->CPSR |= 0xD3;
cpu->UpdateMode(oldcpsr, cpu->CPSR);
cpu->R_SVC[2] = oldcpsr;
cpu->R[14] = cpu->R[15] - 2;
cpu->JumpTo(cpu->ExceptionBase + 0x08);
}
#define INSTRFUNC_PROTO(x) void (*x)(ARM* cpu)
#include "ARM_InstrTable.h"
#undef INSTRFUNC_PROTO
}

35
src/ARMInterpreter.h Normal file
View File

@ -0,0 +1,35 @@
/*
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/.
*/
#ifndef ARMINTERPRETER_H
#define ARMINTERPRETER_H
#include "types.h"
#include "ARM.h"
namespace ARMInterpreter
{
extern void (*ARMInstrTable[4096])(ARM* cpu);
extern void (*THUMBInstrTable[1024])(ARM* cpu);
void A_BLX_IMM(ARM* cpu); // I'm a special one look at me
}
#endif // ARMINTERPRETER_H

1461
src/ARMInterpreter_ALU.cpp Normal file

File diff suppressed because it is too large Load Diff

135
src/ARMInterpreter_ALU.h Normal file
View File

@ -0,0 +1,135 @@
/*
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/.
*/
#ifndef ARMINTERPRETER_ALU_H
#define ARMINTERPRETER_ALU_H
namespace ARMInterpreter
{
#define A_PROTO_ALU_OP(x) \
\
void A_##x##_IMM(ARM* cpu); \
void A_##x##_REG_LSL_IMM(ARM* cpu); \
void A_##x##_REG_LSR_IMM(ARM* cpu); \
void A_##x##_REG_ASR_IMM(ARM* cpu); \
void A_##x##_REG_ROR_IMM(ARM* cpu); \
void A_##x##_REG_LSL_REG(ARM* cpu); \
void A_##x##_REG_LSR_REG(ARM* cpu); \
void A_##x##_REG_ASR_REG(ARM* cpu); \
void A_##x##_REG_ROR_REG(ARM* cpu); \
void A_##x##_IMM_S(ARM* cpu); \
void A_##x##_REG_LSL_IMM_S(ARM* cpu); \
void A_##x##_REG_LSR_IMM_S(ARM* cpu); \
void A_##x##_REG_ASR_IMM_S(ARM* cpu); \
void A_##x##_REG_ROR_IMM_S(ARM* cpu); \
void A_##x##_REG_LSL_REG_S(ARM* cpu); \
void A_##x##_REG_LSR_REG_S(ARM* cpu); \
void A_##x##_REG_ASR_REG_S(ARM* cpu); \
void A_##x##_REG_ROR_REG_S(ARM* cpu);
#define A_PROTO_ALU_TEST(x) \
\
void A_##x##_IMM(ARM* cpu); \
void A_##x##_REG_LSL_IMM(ARM* cpu); \
void A_##x##_REG_LSR_IMM(ARM* cpu); \
void A_##x##_REG_ASR_IMM(ARM* cpu); \
void A_##x##_REG_ROR_IMM(ARM* cpu); \
void A_##x##_REG_LSL_REG(ARM* cpu); \
void A_##x##_REG_LSR_REG(ARM* cpu); \
void A_##x##_REG_ASR_REG(ARM* cpu); \
void A_##x##_REG_ROR_REG(ARM* cpu);
A_PROTO_ALU_OP(AND)
A_PROTO_ALU_OP(EOR)
A_PROTO_ALU_OP(SUB)
A_PROTO_ALU_OP(RSB)
A_PROTO_ALU_OP(ADD)
A_PROTO_ALU_OP(ADC)
A_PROTO_ALU_OP(SBC)
A_PROTO_ALU_OP(RSC)
A_PROTO_ALU_TEST(TST)
A_PROTO_ALU_TEST(TEQ)
A_PROTO_ALU_TEST(CMP)
A_PROTO_ALU_TEST(CMN)
A_PROTO_ALU_OP(ORR)
A_PROTO_ALU_OP(MOV)
A_PROTO_ALU_OP(BIC)
A_PROTO_ALU_OP(MVN)
void A_MUL(ARM* cpu);
void A_MLA(ARM* cpu);
void A_UMULL(ARM* cpu);
void A_UMLAL(ARM* cpu);
void A_SMULL(ARM* cpu);
void A_SMLAL(ARM* cpu);
void A_SMLAxy(ARM* cpu);
void A_SMLAWy(ARM* cpu);
void A_SMULxy(ARM* cpu);
void A_SMULWy(ARM* cpu);
void A_SMLALxy(ARM* cpu);
void A_CLZ(ARM* cpu);
void A_QADD(ARM* cpu);
void A_QSUB(ARM* cpu);
void A_QDADD(ARM* cpu);
void A_QDSUB(ARM* cpu);
void T_LSL_IMM(ARM* cpu);
void T_LSR_IMM(ARM* cpu);
void T_ASR_IMM(ARM* cpu);
void T_ADD_REG_(ARM* cpu);
void T_SUB_REG_(ARM* cpu);
void T_ADD_IMM_(ARM* cpu);
void T_SUB_IMM_(ARM* cpu);
void T_MOV_IMM(ARM* cpu);
void T_CMP_IMM(ARM* cpu);
void T_ADD_IMM(ARM* cpu);
void T_SUB_IMM(ARM* cpu);
void T_AND_REG(ARM* cpu);
void T_EOR_REG(ARM* cpu);
void T_LSL_REG(ARM* cpu);
void T_LSR_REG(ARM* cpu);
void T_ASR_REG(ARM* cpu);
void T_ADC_REG(ARM* cpu);
void T_SBC_REG(ARM* cpu);
void T_ROR_REG(ARM* cpu);
void T_TST_REG(ARM* cpu);
void T_NEG_REG(ARM* cpu);
void T_CMP_REG(ARM* cpu);
void T_CMN_REG(ARM* cpu);
void T_ORR_REG(ARM* cpu);
void T_MUL_REG(ARM* cpu);
void T_BIC_REG(ARM* cpu);
void T_MVN_REG(ARM* cpu);
void T_ADD_HIREG(ARM* cpu);
void T_CMP_HIREG(ARM* cpu);
void T_MOV_HIREG(ARM* cpu);
void T_ADD_PCREL(ARM* cpu);
void T_ADD_SPREL(ARM* cpu);
void T_ADD_SP(ARM* cpu);
}
#endif

View File

@ -0,0 +1,116 @@
/*
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 "ARM.h"
namespace ARMInterpreter
{
void A_B(ARM* cpu)
{
s32 offset = (s32)(cpu->CurInstr << 8) >> 6;
cpu->JumpTo(cpu->R[15] + offset);
}
void A_BL(ARM* cpu)
{
s32 offset = (s32)(cpu->CurInstr << 8) >> 6;
cpu->R[14] = cpu->R[15] - 4;
cpu->JumpTo(cpu->R[15] + offset);
}
void A_BLX_IMM(ARM* cpu)
{
s32 offset = (s32)(cpu->CurInstr << 8) >> 6;
if (cpu->CurInstr & 0x01000000) offset += 2;
cpu->R[14] = cpu->R[15] - 4;
cpu->JumpTo(cpu->R[15] + offset + 1);
}
void A_BX(ARM* cpu)
{
cpu->JumpTo(cpu->R[cpu->CurInstr & 0xF]);
}
void A_BLX_REG(ARM* cpu)
{
u32 lr = cpu->R[15] - 4;
cpu->JumpTo(cpu->R[cpu->CurInstr & 0xF]);
cpu->R[14] = lr;
}
void T_BCOND(ARM* cpu)
{
if (cpu->CheckCondition((cpu->CurInstr >> 8) & 0xF))
{
s32 offset = (s32)(cpu->CurInstr << 24) >> 23;
cpu->JumpTo(cpu->R[15] + offset + 1);
}
}
void T_BX(ARM* cpu)
{
cpu->JumpTo(cpu->R[(cpu->CurInstr >> 3) & 0xF]);
}
void T_BLX_REG(ARM* cpu)
{
if (cpu->Num==1)
{
printf("!! THUMB BLX_REG ON ARM7\n");
return;
}
u32 lr = cpu->R[15] - 1;
cpu->JumpTo(cpu->R[(cpu->CurInstr >> 3) & 0xF]);
cpu->R[14] = lr;
}
void T_B(ARM* cpu)
{
s32 offset = (s32)((cpu->CurInstr & 0x7FF) << 21) >> 20;
cpu->JumpTo(cpu->R[15] + offset + 1);
}
void T_BL_LONG_1(ARM* cpu)
{
s32 offset = (s32)((cpu->CurInstr & 0x7FF) << 21) >> 9;
cpu->R[14] = cpu->R[15] + offset;
}
void T_BL_LONG_2(ARM* cpu)
{
s32 offset = (cpu->CurInstr & 0x7FF) << 1;
u32 pc = cpu->R[14] + offset;
cpu->R[14] = (cpu->R[15] - 2) | 1;
if ((cpu->Num==1) || (cpu->CurInstr & (1<<12)))
pc |= 1;
cpu->JumpTo(pc);
}
}

View File

@ -0,0 +1,39 @@
/*
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/.
*/
#ifndef ARMINTERPRETER_BRANCH_H
#define ARMINTERPRETER_BRANCH_H
namespace ARMInterpreter
{
void A_B(ARM* cpu);
void A_BL(ARM* cpu);
void A_BX(ARM* cpu);
void A_BLX_REG(ARM* cpu);
void T_BCOND(ARM* cpu);
void T_BX(ARM* cpu);
void T_BLX_REG(ARM* cpu);
void T_B(ARM* cpu);
void T_BL_LONG_1(ARM* cpu);
void T_BL_LONG_2(ARM* cpu);
}
#endif

View File

@ -0,0 +1,729 @@
/*
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 "ARM.h"
namespace ARMInterpreter
{
// copypasta from ALU. bad
#define LSL_IMM(x, s) \
x <<= s;
#define LSR_IMM(x, s) \
if (s == 0) x = 0; \
else x >>= s;
#define ASR_IMM(x, s) \
if (s == 0) x = ((s32)x) >> 31; \
else x = ((s32)x) >> s;
#define ROR_IMM(x, s) \
if (s == 0) \
{ \
x = (x >> 1) | ((cpu->CPSR & 0x20000000) << 2); \
} \
else \
{ \
x = ROR(x, s); \
}
#define A_WB_CALC_OFFSET_IMM \
u32 offset = (cpu->CurInstr & 0xFFF); \
if (!(cpu->CurInstr & (1<<23))) offset = -offset;
#define A_WB_CALC_OFFSET_REG(shiftop) \
u32 offset = cpu->R[cpu->CurInstr & 0xF]; \
u32 shift = ((cpu->CurInstr>>7)&0x1F); \
shiftop(offset, shift); \
if (!(cpu->CurInstr & (1<<23))) offset = -offset;
#define A_STR \
offset += cpu->R[(cpu->CurInstr>>16) & 0xF]; \
cpu->DataWrite32(offset, cpu->R[(cpu->CurInstr>>12) & 0xF]); \
if (cpu->CurInstr & (1<<21)) cpu->R[(cpu->CurInstr>>16) & 0xF] = offset;
#define A_STR_POST \
u32 addr = cpu->R[(cpu->CurInstr>>16) & 0xF]; \
cpu->DataWrite32(addr, cpu->R[(cpu->CurInstr>>12) & 0xF], cpu->CurInstr & (1<<21)); \
cpu->R[(cpu->CurInstr>>16) & 0xF] += offset;
#define A_STRB \
offset += cpu->R[(cpu->CurInstr>>16) & 0xF]; \
cpu->DataWrite8(offset, cpu->R[(cpu->CurInstr>>12) & 0xF]); \
if (cpu->CurInstr & (1<<21)) cpu->R[(cpu->CurInstr>>16) & 0xF] = offset;
#define A_STRB_POST \
u32 addr = cpu->R[(cpu->CurInstr>>16) & 0xF]; \
cpu->DataWrite8(addr, cpu->R[(cpu->CurInstr>>12) & 0xF], cpu->CurInstr & (1<<21)); \
cpu->R[(cpu->CurInstr>>16) & 0xF] += offset;
#define A_LDR \
offset += cpu->R[(cpu->CurInstr>>16) & 0xF]; \
u32 val = cpu->DataRead32(offset); val = ROR(val, ((offset&0x3)<<3)); \
if (cpu->CurInstr & (1<<21)) cpu->R[(cpu->CurInstr>>16) & 0xF] = offset; \
cpu->Cycles += 1; \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
if (cpu->Num==1) val &= ~0x1; \
cpu->JumpTo(val); \
} \
else \
{ \
cpu->R[(cpu->CurInstr>>12) & 0xF] = val; \
}
#define A_LDR_POST \
u32 addr = cpu->R[(cpu->CurInstr>>16) & 0xF]; \
u32 val = cpu->DataRead32(addr, cpu->CurInstr & (1<<21)); val = ROR(val, ((addr&0x3)<<3)); \
cpu->R[(cpu->CurInstr>>16) & 0xF] += offset; \
cpu->Cycles += 1; \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
if (cpu->Num==1) val &= ~0x1; \
cpu->JumpTo(val); \
} \
else \
{ \
cpu->R[(cpu->CurInstr>>12) & 0xF] = val; \
}
#define A_LDRB \
offset += cpu->R[(cpu->CurInstr>>16) & 0xF]; \
u32 val = cpu->DataRead8(offset); \
if (cpu->CurInstr & (1<<21)) cpu->R[(cpu->CurInstr>>16) & 0xF] = offset; \
cpu->Cycles += 1; \
cpu->R[(cpu->CurInstr>>12) & 0xF] = val; \
if (((cpu->CurInstr>>12) & 0xF) == 15) printf("!! LDRB PC %08X\n", cpu->R[15]); \
#define A_LDRB_POST \
u32 addr = cpu->R[(cpu->CurInstr>>16) & 0xF]; \
u32 val = cpu->DataRead8(addr, cpu->CurInstr & (1<<21)); \
cpu->R[(cpu->CurInstr>>16) & 0xF] += offset; \
cpu->Cycles += 1; \
cpu->R[(cpu->CurInstr>>12) & 0xF] = val; \
if (((cpu->CurInstr>>12) & 0xF) == 15) printf("!! LDRB PC %08X\n", cpu->R[15]); \
#define A_IMPLEMENT_WB_LDRSTR(x) \
\
void A_##x##_IMM(ARM* cpu) \
{ \
A_WB_CALC_OFFSET_IMM \
A_##x \
} \
\
void A_##x##_REG_LSL(ARM* cpu) \
{ \
A_WB_CALC_OFFSET_REG(LSL_IMM) \
A_##x \
} \
\
void A_##x##_REG_LSR(ARM* cpu) \
{ \
A_WB_CALC_OFFSET_REG(LSR_IMM) \
A_##x \
} \
\
void A_##x##_REG_ASR(ARM* cpu) \
{ \
A_WB_CALC_OFFSET_REG(ASR_IMM) \
A_##x \
} \
\
void A_##x##_REG_ROR(ARM* cpu) \
{ \
A_WB_CALC_OFFSET_REG(ROR_IMM) \
A_##x \
} \
\
void A_##x##_POST_IMM(ARM* cpu) \
{ \
A_WB_CALC_OFFSET_IMM \
A_##x##_POST \
} \
\
void A_##x##_POST_REG_LSL(ARM* cpu) \
{ \
A_WB_CALC_OFFSET_REG(LSL_IMM) \
A_##x##_POST \
} \
\
void A_##x##_POST_REG_LSR(ARM* cpu) \
{ \
A_WB_CALC_OFFSET_REG(LSR_IMM) \
A_##x##_POST \
} \
\
void A_##x##_POST_REG_ASR(ARM* cpu) \
{ \
A_WB_CALC_OFFSET_REG(ASR_IMM) \
A_##x##_POST \
} \
\
void A_##x##_POST_REG_ROR(ARM* cpu) \
{ \
A_WB_CALC_OFFSET_REG(ROR_IMM) \
A_##x##_POST \
}
A_IMPLEMENT_WB_LDRSTR(STR)
A_IMPLEMENT_WB_LDRSTR(STRB)
A_IMPLEMENT_WB_LDRSTR(LDR)
A_IMPLEMENT_WB_LDRSTR(LDRB)
#define A_HD_CALC_OFFSET_IMM \
u32 offset = (cpu->CurInstr & 0xF) | ((cpu->CurInstr >> 4) & 0xF0); \
if (!(cpu->CurInstr & (1<<23))) offset = -offset;
#define A_HD_CALC_OFFSET_REG \
u32 offset = cpu->R[cpu->CurInstr & 0xF]; \
if (!(cpu->CurInstr & (1<<23))) offset = -offset;
#define A_STRH \
offset += cpu->R[(cpu->CurInstr>>16) & 0xF]; \
cpu->DataWrite16(offset, cpu->R[(cpu->CurInstr>>12) & 0xF]); \
if (cpu->CurInstr & (1<<21)) cpu->R[(cpu->CurInstr>>16) & 0xF] = offset; \
#define A_STRH_POST \
u32 addr = cpu->R[(cpu->CurInstr>>16) & 0xF]; \
cpu->DataWrite16(addr, cpu->R[(cpu->CurInstr>>12) & 0xF]); \
cpu->R[(cpu->CurInstr>>16) & 0xF] += offset; \
// TODO: CHECK LDRD/STRD TIMINGS!! also, ARM9-only
#define A_LDRD \
offset += cpu->R[(cpu->CurInstr>>16) & 0xF]; \
if (cpu->CurInstr & (1<<21)) cpu->R[(cpu->CurInstr>>16) & 0xF] = offset; \
cpu->Cycles += 1; \
u32 r = (cpu->CurInstr>>12) & 0xF; \
cpu->R[r ] = cpu->DataRead32(offset ); \
cpu->R[r+1] = cpu->DataRead32(offset+4); \
#define A_LDRD_POST \
u32 addr = cpu->R[(cpu->CurInstr>>16) & 0xF]; \
cpu->R[(cpu->CurInstr>>16) & 0xF] += offset; \
cpu->Cycles += 1; \
u32 r = (cpu->CurInstr>>12) & 0xF; \
cpu->R[r ] = cpu->DataRead32(addr ); \
cpu->R[r+1] = cpu->DataRead32(addr+4); \
#define A_STRD \
offset += cpu->R[(cpu->CurInstr>>16) & 0xF]; \
if (cpu->CurInstr & (1<<21)) cpu->R[(cpu->CurInstr>>16) & 0xF] = offset; \
u32 r = (cpu->CurInstr>>12) & 0xF; \
cpu->DataWrite32(offset , cpu->R[r ]); \
cpu->DataWrite32(offset+4, cpu->R[r+1]); \
#define A_STRD_POST \
cpu->R[(cpu->CurInstr>>16) & 0xF] += offset; \
u32 r = (cpu->CurInstr>>12) & 0xF; \
cpu->DataWrite32(offset , cpu->R[r ]); \
cpu->DataWrite32(offset+4, cpu->R[r+1]); \
#define A_LDRH \
offset += cpu->R[(cpu->CurInstr>>16) & 0xF]; \
if (cpu->CurInstr & (1<<21)) cpu->R[(cpu->CurInstr>>16) & 0xF] = offset; \
cpu->R[(cpu->CurInstr>>12) & 0xF] = cpu->DataRead16(offset); \
if (((cpu->CurInstr>>12) & 0xF) == 15) printf("!! LDRH PC %08X\n", cpu->R[15]); \
#define A_LDRH_POST \
u32 addr = cpu->R[(cpu->CurInstr>>16) & 0xF]; \
cpu->R[(cpu->CurInstr>>16) & 0xF] += offset; \
cpu->R[(cpu->CurInstr>>12) & 0xF] = cpu->DataRead16(addr); \
if (((cpu->CurInstr>>12) & 0xF) == 15) printf("!! LDRH PC %08X\n", cpu->R[15]); \
#define A_LDRSB \
offset += cpu->R[(cpu->CurInstr>>16) & 0xF]; \
if (cpu->CurInstr & (1<<21)) cpu->R[(cpu->CurInstr>>16) & 0xF] = offset; \
cpu->R[(cpu->CurInstr>>12) & 0xF] = (s32)(s8)cpu->DataRead8(offset); \
if (((cpu->CurInstr>>12) & 0xF) == 15) printf("!! LDRSB PC %08X\n", cpu->R[15]); \
#define A_LDRSB_POST \
u32 addr = cpu->R[(cpu->CurInstr>>16) & 0xF]; \
cpu->R[(cpu->CurInstr>>16) & 0xF] += offset; \
cpu->R[(cpu->CurInstr>>12) & 0xF] = (s32)(s8)cpu->DataRead8(addr); \
if (((cpu->CurInstr>>12) & 0xF) == 15) printf("!! LDRSB PC %08X\n", cpu->R[15]); \
#define A_LDRSH \
offset += cpu->R[(cpu->CurInstr>>16) & 0xF]; \
if (cpu->CurInstr & (1<<21)) cpu->R[(cpu->CurInstr>>16) & 0xF] = offset; \
cpu->R[(cpu->CurInstr>>12) & 0xF] = (s32)(s16)cpu->DataRead16(offset); \
if (((cpu->CurInstr>>12) & 0xF) == 15) printf("!! LDRSH PC %08X\n", cpu->R[15]); \
#define A_LDRSH_POST \
u32 addr = cpu->R[(cpu->CurInstr>>16) & 0xF]; \
cpu->R[(cpu->CurInstr>>16) & 0xF] += offset; \
cpu->R[(cpu->CurInstr>>12) & 0xF] = (s32)(s16)cpu->DataRead16(addr); \
if (((cpu->CurInstr>>12) & 0xF) == 15) printf("!! LDRSH PC %08X\n", cpu->R[15]); \
#define A_IMPLEMENT_HD_LDRSTR(x) \
\
void A_##x##_IMM(ARM* cpu) \
{ \
A_HD_CALC_OFFSET_IMM \
A_##x \
} \
\
void A_##x##_REG(ARM* cpu) \
{ \
A_HD_CALC_OFFSET_REG \
A_##x \
} \
void A_##x##_POST_IMM(ARM* cpu) \
{ \
A_HD_CALC_OFFSET_IMM \
A_##x##_POST \
} \
\
void A_##x##_POST_REG(ARM* cpu) \
{ \
A_HD_CALC_OFFSET_REG \
A_##x##_POST \
}
A_IMPLEMENT_HD_LDRSTR(STRH)
A_IMPLEMENT_HD_LDRSTR(LDRD)
A_IMPLEMENT_HD_LDRSTR(STRD)
A_IMPLEMENT_HD_LDRSTR(LDRH)
A_IMPLEMENT_HD_LDRSTR(LDRSB)
A_IMPLEMENT_HD_LDRSTR(LDRSH)
void A_SWP(ARM* cpu)
{
u32 base = cpu->R[(cpu->CurInstr >> 16) & 0xF];
u32 rm = cpu->R[cpu->CurInstr & 0xF];
u32 val = cpu->DataRead32(base);
cpu->R[(cpu->CurInstr >> 12) & 0xF] = ROR(val, 8*(base&0x3));
cpu->DataWrite32(base, rm);
cpu->Cycles += 1;
}
void A_SWPB(ARM* cpu)
{
u32 base = cpu->R[(cpu->CurInstr >> 16) & 0xF];
u32 rm = cpu->R[cpu->CurInstr & 0xF] & 0xFF;
cpu->R[(cpu->CurInstr >> 12) & 0xF] = cpu->DataRead8(base);
cpu->DataWrite8(base, rm);
cpu->Cycles += 1;
}
void A_LDM(ARM* cpu)
{
u32 baseid = (cpu->CurInstr >> 16) & 0xF;
u32 base = cpu->R[baseid];
u32 wbbase;
u32 preinc = (cpu->CurInstr & (1<<24));
if (!(cpu->CurInstr & (1<<23)))
{
for (int i = 0; i < 16; i++)
{
if (cpu->CurInstr & (1<<i))
base -= 4;
}
if (cpu->CurInstr & (1<<21))
{
// pre writeback
wbbase = base;
}
preinc = !preinc;
}
cpu->Cycles += 1;
if ((cpu->CurInstr & (1<<22)) && !(cpu->CurInstr & (1<<15)))
cpu->UpdateMode(cpu->CPSR, (cpu->CPSR&~0x1F)|0x10);
for (int i = 0; i < 15; i++)
{
if (cpu->CurInstr & (1<<i))
{
if (preinc) base += 4;
cpu->R[i] = cpu->DataRead32(base);
if (!preinc) base += 4;
}
}
if (cpu->CurInstr & (1<<15))
{
if (preinc) base += 4;
u32 pc = cpu->DataRead32(base);
if (!preinc) base += 4;
if (cpu->Num == 1)
pc &= ~0x1;
cpu->JumpTo(pc, cpu->CurInstr & (1<<22));
}
if ((cpu->CurInstr & (1<<22)) && !(cpu->CurInstr & (1<<15)))
cpu->UpdateMode((cpu->CPSR&~0x1F)|0x10, cpu->CPSR);
if (cpu->CurInstr & (1<<21))
{
// post writeback
if (cpu->CurInstr & (1<<23))
wbbase = base;
if (cpu->CurInstr & (1 << baseid))
{
if (cpu->Num == 0)
{
u32 rlist = cpu->CurInstr & 0xFFFF;
if ((!(rlist & ~(1 << baseid))) || (rlist & ~((2 << baseid) - 1)))
cpu->R[baseid] = wbbase;
}
}
else
cpu->R[baseid] = wbbase;
}
}
void A_STM(ARM* cpu)
{
u32 baseid = (cpu->CurInstr >> 16) & 0xF;
u32 base = cpu->R[baseid];
u32 oldbase = base;
u32 preinc = (cpu->CurInstr & (1<<24));
if (!(cpu->CurInstr & (1<<23)))
{
for (u32 i = 0; i < 16; i++)
{
if (cpu->CurInstr & (1<<i))
base -= 4;
}
if (cpu->CurInstr & (1<<21))
cpu->R[baseid] = base;
preinc = !preinc;
}
bool isbanked = false;
if (cpu->CurInstr & (1<<22))
{
u32 mode = (cpu->CPSR & 0x1F);
if (mode == 0x11)
isbanked = (baseid >= 8 && baseid < 15);
else if (mode != 0x10 && mode != 0x1F)
isbanked = (baseid >= 13 && baseid < 15);
cpu->UpdateMode(cpu->CPSR, (cpu->CPSR&~0x1F)|0x10);
}
for (u32 i = 0; i < 16; i++)
{
if (cpu->CurInstr & (1<<i))
{
if (preinc) base += 4;
if (i == baseid && !isbanked)
{
if ((cpu->Num == 0) || (!(cpu->CurInstr & (i-1))))
cpu->DataWrite32(base, oldbase);
else
cpu->DataWrite32(base, base); // checkme
}
else
cpu->DataWrite32(base, cpu->R[i]);
if (!preinc) base += 4;
}
}
if (cpu->CurInstr & (1<<22))
cpu->UpdateMode((cpu->CPSR&~0x1F)|0x10, cpu->CPSR);
if ((cpu->CurInstr & (1<<23)) && (cpu->CurInstr & (1<<21)))
cpu->R[baseid] = base;
}
// ---- THUMB -----------------------
void T_LDR_PCREL(ARM* cpu)
{
u32 addr = (cpu->R[15] & ~0x2) + ((cpu->CurInstr & 0xFF) << 2);
cpu->R[(cpu->CurInstr >> 8) & 0x7] = cpu->DataRead32(addr);
cpu->Cycles += 1;
}
void T_STR_REG(ARM* cpu)
{
u32 addr = cpu->R[(cpu->CurInstr >> 3) & 0x7] + cpu->R[(cpu->CurInstr >> 6) & 0x7];
cpu->DataWrite32(addr, cpu->R[cpu->CurInstr & 0x7]);
}
void T_STRB_REG(ARM* cpu)
{
u32 addr = cpu->R[(cpu->CurInstr >> 3) & 0x7] + cpu->R[(cpu->CurInstr >> 6) & 0x7];
cpu->DataWrite8(addr, cpu->R[cpu->CurInstr & 0x7]);
}
void T_LDR_REG(ARM* cpu)
{
u32 addr = cpu->R[(cpu->CurInstr >> 3) & 0x7] + cpu->R[(cpu->CurInstr >> 6) & 0x7];
u32 val = cpu->DataRead32(addr);
cpu->R[cpu->CurInstr & 0x7] = ROR(val, 8*(addr&0x3));
cpu->Cycles += 1;
}
void T_LDRB_REG(ARM* cpu)
{
u32 addr = cpu->R[(cpu->CurInstr >> 3) & 0x7] + cpu->R[(cpu->CurInstr >> 6) & 0x7];
cpu->R[cpu->CurInstr & 0x7] = cpu->DataRead8(addr);
cpu->Cycles += 1;
}
void T_STRH_REG(ARM* cpu)
{
u32 addr = cpu->R[(cpu->CurInstr >> 3) & 0x7] + cpu->R[(cpu->CurInstr >> 6) & 0x7];
cpu->DataWrite16(addr, cpu->R[cpu->CurInstr & 0x7]);
}
void T_LDRSB_REG(ARM* cpu)
{
u32 addr = cpu->R[(cpu->CurInstr >> 3) & 0x7] + cpu->R[(cpu->CurInstr >> 6) & 0x7];
cpu->R[cpu->CurInstr & 0x7] = (s32)(s8)cpu->DataRead8(addr);
cpu->Cycles += 1;
}
void T_LDRH_REG(ARM* cpu)
{
u32 addr = cpu->R[(cpu->CurInstr >> 3) & 0x7] + cpu->R[(cpu->CurInstr >> 6) & 0x7];
cpu->R[cpu->CurInstr & 0x7] = cpu->DataRead16(addr);
cpu->Cycles += 1;
}
void T_LDRSH_REG(ARM* cpu)
{
u32 addr = cpu->R[(cpu->CurInstr >> 3) & 0x7] + cpu->R[(cpu->CurInstr >> 6) & 0x7];
cpu->R[cpu->CurInstr & 0x7] = (s32)(s16)cpu->DataRead16(addr);
cpu->Cycles += 1;
}
void T_STR_IMM(ARM* cpu)
{
u32 offset = (cpu->CurInstr >> 4) & 0x7C;
offset += cpu->R[(cpu->CurInstr >> 3) & 0x7];
cpu->DataWrite32(offset, cpu->R[cpu->CurInstr & 0x7]);
}
void T_LDR_IMM(ARM* cpu)
{
u32 offset = (cpu->CurInstr >> 4) & 0x7C;
offset += cpu->R[(cpu->CurInstr >> 3) & 0x7];
u32 val = cpu->DataRead32(offset);
cpu->R[cpu->CurInstr & 0x7] = ROR(val, 8*(offset&0x3));
cpu->Cycles += 1;
}
void T_STRB_IMM(ARM* cpu)
{
u32 offset = (cpu->CurInstr >> 6) & 0x1F;
offset += cpu->R[(cpu->CurInstr >> 3) & 0x7];
cpu->DataWrite8(offset, cpu->R[cpu->CurInstr & 0x7]);
}
void T_LDRB_IMM(ARM* cpu)
{
u32 offset = (cpu->CurInstr >> 6) & 0x1F;
offset += cpu->R[(cpu->CurInstr >> 3) & 0x7];
cpu->R[cpu->CurInstr & 0x7] = cpu->DataRead8(offset);
cpu->Cycles += 1;
}
void T_STRH_IMM(ARM* cpu)
{
u32 offset = (cpu->CurInstr >> 5) & 0x3E;
offset += cpu->R[(cpu->CurInstr >> 3) & 0x7];
cpu->DataWrite16(offset, cpu->R[cpu->CurInstr & 0x7]);
}
void T_LDRH_IMM(ARM* cpu)
{
u32 offset = (cpu->CurInstr >> 5) & 0x3E;
offset += cpu->R[(cpu->CurInstr >> 3) & 0x7];
cpu->R[cpu->CurInstr & 0x7] = cpu->DataRead16(offset);
cpu->Cycles += 1;
}
void T_STR_SPREL(ARM* cpu)
{
u32 offset = (cpu->CurInstr << 2) & 0x3FC;
offset += cpu->R[13];
cpu->DataWrite32(offset, cpu->R[(cpu->CurInstr >> 8) & 0x7]);
}
void T_LDR_SPREL(ARM* cpu)
{
u32 offset = (cpu->CurInstr << 2) & 0x3FC;
offset += cpu->R[13];
cpu->R[(cpu->CurInstr >> 8) & 0x7] = cpu->DataRead32(offset);
cpu->Cycles += 1;
}
void T_PUSH(ARM* cpu)
{
int nregs = 0;
for (int i = 0; i < 8; i++)
{
if (cpu->CurInstr & (1<<i))
nregs++;
}
if (cpu->CurInstr & (1<<8))
nregs++;
u32 base = cpu->R[13];
base -= (nregs<<2);
cpu->R[13] = base;
for (int i = 0; i < 8; i++)
{
if (cpu->CurInstr & (1<<i))
{
cpu->DataWrite32(base, cpu->R[i]);
base += 4;
}
}
if (cpu->CurInstr & (1<<8))
{
cpu->DataWrite32(base, cpu->R[14]);
}
}
void T_POP(ARM* cpu)
{
u32 base = cpu->R[13];
cpu->Cycles += 1;
for (int i = 0; i < 8; i++)
{
if (cpu->CurInstr & (1<<i))
{
cpu->R[i] = cpu->DataRead32(base);
base += 4;
}
}
if (cpu->CurInstr & (1<<8))
{
u32 pc = cpu->DataRead32(base);
if (cpu->Num==1) pc |= 0x1;
cpu->JumpTo(pc);
base += 4;
}
cpu->R[13] = base;
}
void T_STMIA(ARM* cpu)
{
u32 base = cpu->R[(cpu->CurInstr >> 8) & 0x7];
for (int i = 0; i < 8; i++)
{
if (cpu->CurInstr & (1<<i))
{
cpu->DataWrite32(base, cpu->R[i]);
base += 4;
}
}
// TODO: check "Rb included in Rlist" case
cpu->R[(cpu->CurInstr >> 8) & 0x7] = base;
}
void T_LDMIA(ARM* cpu)
{
u32 base = cpu->R[(cpu->CurInstr >> 8) & 0x7];
cpu->Cycles += 1;
for (int i = 0; i < 8; i++)
{
if (cpu->CurInstr & (1<<i))
{
cpu->R[i] = cpu->DataRead32(base);
base += 4;
}
}
if (!(cpu->CurInstr & (1<<((cpu->CurInstr >> 8) & 0x7))))
cpu->R[(cpu->CurInstr >> 8) & 0x7] = base;
}
}

View File

@ -0,0 +1,95 @@
/*
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/.
*/
#ifndef ARMINTERPRETER_LOADSTORE_H
#define ARMINTERPRETER_LOADSTORE_H
namespace ARMInterpreter
{
#define A_PROTO_WB_LDRSTR(x) \
\
void A_##x##_IMM(ARM* cpu); \
void A_##x##_REG_LSL(ARM* cpu); \
void A_##x##_REG_LSR(ARM* cpu); \
void A_##x##_REG_ASR(ARM* cpu); \
void A_##x##_REG_ROR(ARM* cpu); \
void A_##x##_POST_IMM(ARM* cpu); \
void A_##x##_POST_REG_LSL(ARM* cpu); \
void A_##x##_POST_REG_LSR(ARM* cpu); \
void A_##x##_POST_REG_ASR(ARM* cpu); \
void A_##x##_POST_REG_ROR(ARM* cpu);
A_PROTO_WB_LDRSTR(STR)
A_PROTO_WB_LDRSTR(STRB)
A_PROTO_WB_LDRSTR(LDR)
A_PROTO_WB_LDRSTR(LDRB)
#define A_PROTO_HD_LDRSTR(x) \
\
void A_##x##_IMM(ARM* cpu); \
void A_##x##_REG(ARM* cpu); \
void A_##x##_POST_IMM(ARM* cpu); \
void A_##x##_POST_REG(ARM* cpu);
A_PROTO_HD_LDRSTR(STRH)
A_PROTO_HD_LDRSTR(LDRD)
A_PROTO_HD_LDRSTR(STRD)
A_PROTO_HD_LDRSTR(LDRH)
A_PROTO_HD_LDRSTR(LDRSB)
A_PROTO_HD_LDRSTR(LDRSH)
void A_LDM(ARM* cpu);
void A_STM(ARM* cpu);
void A_SWP(ARM* cpu);
void A_SWPB(ARM* cpu);
void T_LDR_PCREL(ARM* cpu);
void T_STR_REG(ARM* cpu);
void T_STRB_REG(ARM* cpu);
void T_LDR_REG(ARM* cpu);
void T_LDRB_REG(ARM* cpu);
void T_STRH_REG(ARM* cpu);
void T_LDRSB_REG(ARM* cpu);
void T_LDRH_REG(ARM* cpu);
void T_LDRSH_REG(ARM* cpu);
void T_STR_IMM(ARM* cpu);
void T_LDR_IMM(ARM* cpu);
void T_STRB_IMM(ARM* cpu);
void T_LDRB_IMM(ARM* cpu);
void T_STRH_IMM(ARM* cpu);
void T_LDRH_IMM(ARM* cpu);
void T_STR_SPREL(ARM* cpu);
void T_LDR_SPREL(ARM* cpu);
void T_PUSH(ARM* cpu);
void T_POP(ARM* cpu);
void T_STMIA(ARM* cpu);
void T_LDMIA(ARM* cpu);
}
#endif

1979
src/ARM_InstrTable.h Normal file

File diff suppressed because it is too large Load Diff

300
src/CP15.cpp Normal file
View File

@ -0,0 +1,300 @@
/*
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 "ARM.h"
#include "CP15.h"
// derp
namespace NDS
{
extern ARM* ARM9;
}
namespace CP15
{
u32 Control;
u32 DTCMSetting, ITCMSetting;
u8 ITCM[0x8000];
u32 ITCMSize;
u8 DTCM[0x4000];
u32 DTCMBase, DTCMSize;
void Reset()
{
Control = 0x78; // dunno
DTCMSetting = 0;
ITCMSetting = 0;
memset(ITCM, 0, 0x8000);
memset(DTCM, 0, 0x4000);
ITCMSize = 0;
DTCMBase = 0xFFFFFFFF;
DTCMSize = 0;
}
void UpdateDTCMSetting()
{
if (Control & (1<<16))
{
DTCMBase = DTCMSetting & 0xFFFFF000;
DTCMSize = 0x200 << ((DTCMSetting >> 1) & 0x1F);
printf("DTCM [%08X] enabled at %08X, size %X\n", DTCMSetting, DTCMBase, DTCMSize);
}
else
{
DTCMBase = 0xFFFFFFFF;
DTCMSize = 0;
printf("DTCM disabled\n");
}
}
void UpdateITCMSetting()
{
if (Control & (1<<18))
{
ITCMSize = 0x200 << ((ITCMSetting >> 1) & 0x1F);
printf("ITCM [%08X] enabled at %08X, size %X\n", ITCMSetting, 0, ITCMSize);
}
else
{
ITCMSize = 0;
printf("ITCM disabled\n");
}
}
void Write(u32 id, u32 val)
{
//printf("CP15 write op %03X %08X %08X\n", id, val, NDS::ARM9->R[15]);
switch (id)
{
case 0x100:
val &= 0x000FF085;
Control &= ~0x000FF085;
Control |= val;
UpdateDTCMSetting();
UpdateITCMSetting();
return;
case 0x704:
case 0x782:
NDS::ARM9->Halt(1);
return;
case 0x761:
//printf("inval data cache %08X\n", val);
return;
case 0x762:
//printf("inval data cache SI\n");
return;
case 0x7A1:
//printf("flush data cache %08X\n", val);
return;
case 0x7A2:
//printf("flush data cache SI\n");
return;
case 0x910:
DTCMSetting = val;
UpdateDTCMSetting();
return;
case 0x911:
ITCMSetting = val;
UpdateITCMSetting();
return;
}
if ((id&0xF00)!=0x700)
printf("unknown CP15 write op %03X %08X\n", id, val);
}
u32 Read(u32 id)
{
//printf("CP15 read op %03X %08X\n", id, NDS::ARM9->R[15]);
switch (id)
{
case 0x000: // CPU ID
case 0x003:
case 0x004:
case 0x005:
case 0x006:
case 0x007:
return 0x41059461;
case 0x001: // cache type
return 0x0F0D2112;
case 0x002: // TCM size
return (6 << 6) | (5 << 18);
case 0x100: // control reg
return Control;
case 0x910:
return DTCMSetting;
case 0x911:
return ITCMSetting;
}
printf("unknown CP15 read op %03X\n", id);
return 0;
}
// TCM are handled here.
// TODO: later on, handle PU, and maybe caches
bool HandleCodeRead16(u32 addr, u16* val)
{
if (addr < ITCMSize)
{
*val = *(u16*)&ITCM[addr & 0x7FFF];
return true;
}
return false;
}
bool HandleCodeRead32(u32 addr, u32* val)
{
if (addr < ITCMSize)
{
*val = *(u32*)&ITCM[addr & 0x7FFF];
return true;
}
return false;
}
bool HandleDataRead8(u32 addr, u8* val, u32 forceuser)
{
if (addr < ITCMSize)
{
*val = *(u8*)&ITCM[addr & 0x7FFF];
return true;
}
if (addr >= DTCMBase && addr < (DTCMBase + DTCMSize))
{
*val = *(u8*)&DTCM[(addr - DTCMBase) & 0x3FFF];
return true;
}
return false;
}
bool HandleDataRead16(u32 addr, u16* val, u32 forceuser)
{
if (addr < ITCMSize)
{
*val = *(u16*)&ITCM[addr & 0x7FFF];
return true;
}
if (addr >= DTCMBase && addr < (DTCMBase + DTCMSize))
{
*val = *(u16*)&DTCM[(addr - DTCMBase) & 0x3FFF];
return true;
}
return false;
}
bool HandleDataRead32(u32 addr, u32* val, u32 forceuser)
{
if (addr < ITCMSize)
{
*val = *(u32*)&ITCM[addr & 0x7FFF];
return true;
}
if (addr >= DTCMBase && addr < (DTCMBase + DTCMSize))
{
*val = *(u32*)&DTCM[(addr - DTCMBase) & 0x3FFF];
return true;
}
return false;
}
bool HandleDataWrite8(u32 addr, u8 val, u32 forceuser)
{
if (addr < ITCMSize)
{
*(u8*)&ITCM[addr & 0x7FFF] = val;
return true;
}
if (addr >= DTCMBase && addr < (DTCMBase + DTCMSize))
{
*(u8*)&DTCM[(addr - DTCMBase) & 0x3FFF] = val;
return true;
}
return false;
}
bool HandleDataWrite16(u32 addr, u16 val, u32 forceuser)
{
if (addr < ITCMSize)
{
*(u16*)&ITCM[addr & 0x7FFF] = val;
return true;
}
if (addr >= DTCMBase && addr < (DTCMBase + DTCMSize))
{
*(u16*)&DTCM[(addr - DTCMBase) & 0x3FFF] = val;
return true;
}
return false;
}
bool HandleDataWrite32(u32 addr, u32 val, u32 forceuser)
{
if (addr < ITCMSize)
{
*(u32*)&ITCM[addr & 0x7FFF] = val;
return true;
}
if (addr >= DTCMBase && addr < (DTCMBase + DTCMSize))
{
*(u32*)&DTCM[(addr - DTCMBase) & 0x3FFF] = val;
return true;
}
return false;
}
}

44
src/CP15.h Normal file
View File

@ -0,0 +1,44 @@
/*
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/.
*/
#ifndef CP15_H
#define CP15_H
namespace CP15
{
void Reset();
void UpdateDTCMSetting();
void UpdateITCMSetting();
void Write(u32 id, u32 val);
u32 Read(u32 id);
bool HandleCodeRead16(u32 addr, u16* val);
bool HandleCodeRead32(u32 addr, u32* val);
bool HandleDataRead8(u32 addr, u8* val, u32 forceuser=0);
bool HandleDataRead16(u32 addr, u16* val, u32 forceuser=0);
bool HandleDataRead32(u32 addr, u32* val, u32 forceuser=0);
bool HandleDataWrite8(u32 addr, u8 val, u32 forceuser=0);
bool HandleDataWrite16(u32 addr, u16 val, u32 forceuser=0);
bool HandleDataWrite32(u32 addr, u32 val, u32 forceuser=0);
}
#endif

269
src/DMA.cpp Normal file
View File

@ -0,0 +1,269 @@
/*
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 "NDS.h"
#include "DMA.h"
#include "NDSCart.h"
#include "GPU3D.h"
// NOTES ON DMA SHIT
//
// * could use optimized code paths for common types of DMA transfers. for example, VRAM
// * needs to eventually be made more accurate anyway. DMA isn't instant.
DMA::DMA(u32 cpu, u32 num)
{
CPU = cpu;
Num = num;
if (cpu == 0)
CountMask = 0x001FFFFF;
else
CountMask = (num==3 ? 0x0000FFFF : 0x00003FFF);
// TODO: merge with the one in ARM.cpp, somewhere
for (int i = 0; i < 16; i++)
{
Waitstates[0][i] = 1;
Waitstates[1][i] = 1;
}
if (!cpu)
{
// ARM9
// note: 33MHz cycles
Waitstates[0][0x2] = 1;
Waitstates[0][0x3] = 1;
Waitstates[0][0x4] = 1;
Waitstates[0][0x5] = 1;
Waitstates[0][0x6] = 1;
Waitstates[0][0x7] = 1;
Waitstates[0][0x8] = 6;
Waitstates[0][0x9] = 6;
Waitstates[0][0xA] = 10;
Waitstates[0][0xF] = 1;
Waitstates[1][0x2] = 2;
Waitstates[1][0x3] = 1;
Waitstates[1][0x4] = 1;
Waitstates[1][0x5] = 2;
Waitstates[1][0x6] = 2;
Waitstates[1][0x7] = 1;
Waitstates[1][0x8] = 12;
Waitstates[1][0x9] = 12;
Waitstates[1][0xA] = 10;
Waitstates[1][0xF] = 1;
}
else
{
// ARM7
Waitstates[0][0x0] = 1;
Waitstates[0][0x2] = 1;
Waitstates[0][0x3] = 1;
Waitstates[0][0x4] = 1;
Waitstates[0][0x6] = 1;
Waitstates[0][0x8] = 6;
Waitstates[0][0x9] = 6;
Waitstates[0][0xA] = 10;
Waitstates[1][0x0] = 1;
Waitstates[1][0x2] = 2;
Waitstates[1][0x3] = 1;
Waitstates[1][0x4] = 1;
Waitstates[1][0x6] = 2;
Waitstates[1][0x8] = 12;
Waitstates[1][0x9] = 12;
Waitstates[1][0xA] = 10;
}
Reset();
}
DMA::~DMA()
{
}
void DMA::Reset()
{
SrcAddr = 0;
DstAddr = 0;
Cnt = 0;
StartMode = 0;
CurSrcAddr = 0;
CurDstAddr = 0;
RemCount = 0;
IterCount = 0;
SrcAddrInc = 0;
DstAddrInc = 0;
Running = false;
}
void DMA::WriteCnt(u32 val)
{
u32 oldcnt = Cnt;
Cnt = val;
if ((!(oldcnt & 0x80000000)) && (val & 0x80000000))
{
CurSrcAddr = SrcAddr;
CurDstAddr = DstAddr;
switch (Cnt & 0x00600000)
{
case 0x00000000: DstAddrInc = 1; break;
case 0x00200000: DstAddrInc = -1; break;
case 0x00400000: DstAddrInc = 0; break;
case 0x00600000: DstAddrInc = 1; break;
}
switch (Cnt & 0x01800000)
{
case 0x00000000: SrcAddrInc = 1; break;
case 0x00800000: SrcAddrInc = -1; break;
case 0x01000000: SrcAddrInc = 0; break;
case 0x01800000: SrcAddrInc = 1; printf("BAD DMA SRC INC MODE 3\n"); break;
}
if (CPU == 0)
StartMode = (Cnt >> 27) & 0x7;
else
StartMode = ((Cnt >> 28) & 0x3) | 0x10;
if ((StartMode & 0x7) == 0)
Start();
else if (StartMode == 0x07)
GPU3D::CheckFIFODMA();
if ((StartMode&7)!=0x00 && (StartMode&7)!=0x1 && StartMode!=2 && StartMode!=0x05 && StartMode!=0x12 && StartMode!=0x07)
printf("UNIMPLEMENTED ARM%d DMA%d START MODE %02X\n", CPU?7:9, Num, StartMode);
}
}
void DMA::Start()
{
if (Running) return;
u32 countmask;
if (CPU == 0)
countmask = 0x001FFFFF;
else
countmask = (Num==3 ? 0x0000FFFF : 0x00003FFF);
RemCount = Cnt & countmask;
if (!RemCount)
RemCount = countmask+1;
if (StartMode == 0x07 && RemCount > 112)
IterCount = 112;
else
IterCount = RemCount;
if ((Cnt & 0x00600000) == 0x00600000)
CurDstAddr = DstAddr;
//printf("ARM%d DMA%d %08X %02X %08X->%08X %d bytes %dbit\n", CPU?7:9, Num, Cnt, StartMode, CurSrcAddr, CurDstAddr, RemCount*((Cnt&0x04000000)?4:2), (Cnt&0x04000000)?32:16);
// special path for cart DMA. this is a gross hack.
// emulating it properly requires emulating cart transfer delays, so uh... TODO
if (CurSrcAddr==0x04100010 && RemCount==1 && (Cnt & 0x07E00000)==0x07000000 &&
StartMode==0x05 || StartMode==0x12)
{
NDSCart::DMA(CurDstAddr);
Cnt &= ~0x80000000;
if (Cnt & 0x40000000)
NDS::SetIRQ(CPU, NDS::IRQ_DMA0 + Num);
return;
}
// TODO eventually: not stop if we're running code in ITCM
Running = true;
NDS::StopCPU(CPU, 1<<Num);
}
s32 DMA::Run(s32 cycles)
{
if (!Running)
return cycles;
if (!(Cnt & 0x04000000))
{
u16 (*readfn)(u32) = CPU ? NDS::ARM7Read16 : NDS::ARM9Read16;
void (*writefn)(u32,u16) = CPU ? NDS::ARM7Write16 : NDS::ARM9Write16;
while (IterCount > 0 && cycles > 0)
{
writefn(CurDstAddr, readfn(CurSrcAddr));
cycles -= (Waitstates[0][(CurSrcAddr >> 24) & 0xF] + Waitstates[0][(CurDstAddr >> 24) & 0xF]);
CurSrcAddr += SrcAddrInc<<1;
CurDstAddr += DstAddrInc<<1;
IterCount--;
RemCount--;
}
}
else
{
u32 (*readfn)(u32) = CPU ? NDS::ARM7Read32 : NDS::ARM9Read32;
void (*writefn)(u32,u32) = CPU ? NDS::ARM7Write32 : NDS::ARM9Write32;
while (IterCount > 0 && cycles > 0)
{
writefn(CurDstAddr, readfn(CurSrcAddr));
cycles -= (Waitstates[1][(CurSrcAddr >> 24) & 0xF] + Waitstates[1][(CurDstAddr >> 24) & 0xF]);
CurSrcAddr += SrcAddrInc<<2;
CurDstAddr += DstAddrInc<<2;
IterCount--;
RemCount--;
}
}
if (RemCount)
{
Cnt &= ~CountMask;
Cnt |= RemCount;
if (IterCount == 0)
{
Running = false;
NDS::ResumeCPU(CPU, 1<<Num);
if (StartMode == 0x07)
GPU3D::CheckFIFODMA();
}
return cycles;
}
if (!(Cnt & 0x02000000))
Cnt &= ~0x80000000;
if (Cnt & 0x40000000)
NDS::SetIRQ(CPU, NDS::IRQ_DMA0 + Num);
Running = false;
NDS::ResumeCPU(CPU, 1<<Num);
return cycles - 2;
}

64
src/DMA.h Normal file
View File

@ -0,0 +1,64 @@
/*
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/.
*/
#ifndef DMA_H
#define DMA_H
#include "types.h"
class DMA
{
public:
DMA(u32 cpu, u32 num);
~DMA();
void Reset();
void WriteCnt(u32 val);
void Start();
s32 Run(s32 cycles);
void StartIfNeeded(u32 mode)
{
if ((mode == StartMode) && (Cnt & 0x80000000))
Start();
}
u32 SrcAddr;
u32 DstAddr;
u32 Cnt;
private:
u32 CPU, Num;
s32 Waitstates[2][16];
u32 StartMode;
u32 CurSrcAddr;
u32 CurDstAddr;
u32 RemCount;
u32 IterCount;
u32 SrcAddrInc;
u32 DstAddrInc;
u32 CountMask;
bool Running;
};
#endif

93
src/FIFO.h Normal file
View File

@ -0,0 +1,93 @@
/*
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/.
*/
#ifndef FIFO_H
#define FIFO_H
#include "types.h"
template<typename T>
class FIFO
{
public:
FIFO(u32 num)
{
NumEntries = num;
Entries = new T[num];
Clear();
}
~FIFO()
{
delete[] Entries;
}
void Clear()
{
NumOccupied = 0;
ReadPos = 0;
WritePos = 0;
memset(&Entries[ReadPos], 0, sizeof(T));
}
void Write(T val)
{
if (IsFull()) return;
Entries[WritePos] = val;
WritePos++;
if (WritePos >= NumEntries)
WritePos = 0;
NumOccupied++;
}
T Read()
{
T ret = Entries[ReadPos];
if (IsEmpty())
return ret;
ReadPos++;
if (ReadPos >= NumEntries)
ReadPos = 0;
NumOccupied--;
return ret;
}
T Peek()
{
return Entries[ReadPos];
}
u32 Level() { return NumOccupied; }
bool IsEmpty() { return NumOccupied == 0; }
bool IsFull() { return NumOccupied >= NumEntries; }
private:
u32 NumEntries;
T* Entries;
u32 NumOccupied;
u32 ReadPos, WritePos;
};
#endif

732
src/GPU.cpp Normal file
View File

@ -0,0 +1,732 @@
/*
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"
namespace GPU
{
#define LINE_CYCLES (355*6)
#define HBLANK_CYCLES (256*6)
#define FRAME_CYCLES (LINE_CYCLES * 263)
u16 VCount;
u16 DispStat[2], VMatch[2];
u8 Palette[2*1024];
u8 OAM[2*1024];
u8 VRAM_A[128*1024];
u8 VRAM_B[128*1024];
u8 VRAM_C[128*1024];
u8 VRAM_D[128*1024];
u8 VRAM_E[ 64*1024];
u8 VRAM_F[ 16*1024];
u8 VRAM_G[ 16*1024];
u8 VRAM_H[ 32*1024];
u8 VRAM_I[ 16*1024];
u8* VRAM[9] = {VRAM_A, VRAM_B, VRAM_C, VRAM_D, VRAM_E, VRAM_F, VRAM_G, VRAM_H, VRAM_I};
u8 VRAMCNT[9];
u8 VRAMSTAT;
//u32 VRAM_Base[9];
//u32 VRAM_Mask[9];
u32 VRAMMap_LCDC;
u32 VRAMMap_ABG[0x20];
u32 VRAMMap_AOBJ[0x10];
u32 VRAMMap_BBG[0x8];
u32 VRAMMap_BOBJ[0x8];
u32 VRAMMap_ABGExtPal[4];
u32 VRAMMap_AOBJExtPal;
u32 VRAMMap_BBGExtPal[4];
u32 VRAMMap_BOBJExtPal;
u32 VRAMMap_Texture[4];
u32 VRAMMap_TexPal[6];
u32 VRAMMap_ARM7[2];
/*u8* VRAM_ABG[128];
u8* VRAM_AOBJ[128];
u8* VRAM_BBG[128];
u8* VRAM_BOBJ[128];
u8* VRAM_LCD[128];*/
/*u8* VRAM_ARM7[2];
u8* VRAM_ABGExtPal[4];
u8* VRAM_AOBJExtPal;
u8* VRAM_BBGExtPal[4];
u8* VRAM_BOBJExtPal;*/
u32 Framebuffer[256*192*2];
GPU2D* GPU2D_A;
GPU2D* GPU2D_B;
bool Init()
{
GPU2D_A = new GPU2D(0);
GPU2D_B = new GPU2D(1);
if (!GPU3D::Init()) return false;
return true;
}
void DeInit()
{
delete GPU2D_A;
delete GPU2D_B;
GPU3D::DeInit();
}
void Reset()
{
VCount = 0;
DispStat[0] = 0;
DispStat[1] = 0;
VMatch[0] = 0;
VMatch[1] = 0;
memset(Palette, 0, 2*1024);
memset(OAM, 0, 2*1024);
memset(VRAM_A, 0, 128*1024);
memset(VRAM_B, 0, 128*1024);
memset(VRAM_C, 0, 128*1024);
memset(VRAM_D, 0, 128*1024);
memset(VRAM_E, 0, 64*1024);
memset(VRAM_F, 0, 16*1024);
memset(VRAM_G, 0, 16*1024);
memset(VRAM_H, 0, 32*1024);
memset(VRAM_I, 0, 16*1024);
memset(VRAMCNT, 0, 9);
VRAMSTAT = 0;
VRAMMap_LCDC = 0;
memset(VRAMMap_ABG, 0, sizeof(VRAMMap_ABG));
memset(VRAMMap_AOBJ, 0, sizeof(VRAMMap_AOBJ));
memset(VRAMMap_BBG, 0, sizeof(VRAMMap_BBG));
memset(VRAMMap_BOBJ, 0, sizeof(VRAMMap_BOBJ));
memset(VRAMMap_ABGExtPal, 0, sizeof(VRAMMap_ABGExtPal));
VRAMMap_AOBJExtPal = 0;
memset(VRAMMap_BBGExtPal, 0, sizeof(VRAMMap_BBGExtPal));
VRAMMap_BOBJExtPal = 0;
memset(VRAMMap_Texture, 0, sizeof(VRAMMap_Texture));
memset(VRAMMap_TexPal, 0, sizeof(VRAMMap_TexPal));
VRAMMap_ARM7[0] = 0;
VRAMMap_ARM7[1] = 0;
//memset(VRAM_Base, 0, sizeof(VRAM_Base));
//memset(VRAM_Mask, 0, sizeof(VRAM_Mask));
/*memset(VRAM_ABG, 0, sizeof(u8*)*128);
memset(VRAM_AOBJ, 0, sizeof(u8*)*128);
memset(VRAM_BBG, 0, sizeof(u8*)*128);
memset(VRAM_BOBJ, 0, sizeof(u8*)*128);
memset(VRAM_LCD, 0, sizeof(u8*)*128);*/
/*memset(VRAM_ARM7, 0, sizeof(u8*)*2);
memset(VRAM_ABGExtPal, 0, sizeof(u8*)*4);
VRAM_AOBJExtPal = NULL;
memset(VRAM_BBGExtPal, 0, sizeof(u8*)*4);
VRAM_BOBJExtPal = NULL;*/
for (int i = 0; i < 256*192*2; i++)
{
Framebuffer[i] = 0xFFFFFFFF;
}
GPU2D_A->Reset();
GPU2D_B->Reset();
GPU3D::Reset();
GPU2D_A->SetFramebuffer(&Framebuffer[256*192]);
GPU2D_B->SetFramebuffer(&Framebuffer[256*0]);
}
// VRAM mapping notes
//
// mirroring:
// unmapped range reads zero
// LCD is mirrored every 0x100000 bytes, the gap between each mirror reads zero
// ABG:
// bank A,B,C,D,E mirror every 0x80000 bytes
// bank F,G mirror at base+0x8000, mirror every 0x80000 bytes
// AOBJ:
// bank A,B,E mirror every 0x40000 bytes
// bank F,G mirror at base+0x8000, mirror every 0x40000 bytes
// BBG:
// bank C mirrors every 0x20000 bytes
// bank H mirrors every 0x10000 bytes
// bank I mirrors at base+0x4000, mirrors every 0x10000 bytes
// BOBJ:
// bank D mirrors every 0x20000 bytes
// bank I mirrors every 0x4000 bytes
//
// untested:
// ARM7 (TODO)
// extended palette (mirroring doesn't apply)
// texture/texpal (does mirroring apply?)
// -> trying to use extpal/texture/texpal with no VRAM mapped.
// would likely read all black, but has to be tested.
//
// overlap:
// when reading: values are read from each bank and ORed together
// when writing: value is written to each bank
#define MAP_RANGE(map, base, n) for (int i = 0; i < n; i++) map[(base)+i] |= bankmask;
#define UNMAP_RANGE(map, base, n) for (int i = 0; i < n; i++) map[(base)+i] &= ~bankmask;
void MapVRAM_AB(u32 bank, u8 cnt)
{
u8 oldcnt = VRAMCNT[bank];
VRAMCNT[bank] = cnt;
if (oldcnt == cnt) return;
u8 oldofs = (oldcnt >> 3) & 0x3;
u8 ofs = (cnt >> 3) & 0x3;
u32 bankmask = 1 << bank;
if (oldcnt & (1<<7))
{
switch (oldcnt & 0x3)
{
case 0: // LCDC
VRAMMap_LCDC &= ~bankmask;
break;
case 1: // ABG
UNMAP_RANGE(VRAMMap_ABG, oldofs<<3, 8);
break;
case 2: // AOBJ
oldofs &= 0x1;
UNMAP_RANGE(VRAMMap_AOBJ, oldofs<<3, 8);
break;
case 3: // texture
VRAMMap_Texture[oldofs] &= ~bankmask;
break;
}
}
if (cnt & (1<<7))
{
switch (cnt & 0x3)
{
case 0: // LCDC
VRAMMap_LCDC |= bankmask;
break;
case 1: // ABG
MAP_RANGE(VRAMMap_ABG, ofs<<3, 8);
break;
case 2: // AOBJ
ofs &= 0x1;
MAP_RANGE(VRAMMap_AOBJ, ofs<<3, 8);
break;
case 3: // texture
VRAMMap_Texture[ofs] |= bankmask;
break;
}
}
}
void MapVRAM_CD(u32 bank, u8 cnt)
{
u8 oldcnt = VRAMCNT[bank];
VRAMCNT[bank] = cnt;
VRAMSTAT &= ~(1 << (bank-2));
if (oldcnt == cnt) return;
u8 oldofs = (oldcnt >> 3) & 0x7;
u8 ofs = (cnt >> 3) & 0x7;
u32 bankmask = 1 << bank;
if (oldcnt & (1<<7))
{
switch (oldcnt & 0x7)
{
case 0: // LCDC
VRAMMap_LCDC &= ~bankmask;
break;
case 1: // ABG
UNMAP_RANGE(VRAMMap_ABG, oldofs<<3, 8);
break;
case 2: // ARM7 VRAM
oldofs &= 0x1;
VRAMMap_ARM7[oldofs] &= ~bankmask;
break;
case 3: // texture
VRAMMap_Texture[oldofs] &= ~bankmask;
break;
case 4: // BBG/BOBJ
if (bank == 2)
{
UNMAP_RANGE(VRAMMap_BBG, 0, 8);
}
else
{
UNMAP_RANGE(VRAMMap_BOBJ, 0, 8);
}
break;
}
}
if (cnt & (1<<7))
{
switch (cnt & 0x7)
{
case 0: // LCDC
VRAMMap_LCDC |= bankmask;
break;
case 1: // ABG
MAP_RANGE(VRAMMap_ABG, ofs<<3, 8);
break;
case 2: // ARM7 VRAM
ofs &= 0x1;
VRAMMap_ARM7[ofs] |= bankmask;
VRAMSTAT |= (1 << (bank-2));
break;
case 3: // texture
VRAMMap_Texture[ofs] |= bankmask;
break;
case 4: // BBG/BOBJ
if (bank == 2)
{
MAP_RANGE(VRAMMap_BBG, 0, 8);
}
else
{
MAP_RANGE(VRAMMap_BOBJ, 0, 8);
}
break;
}
}
}
void MapVRAM_E(u32 bank, u8 cnt)
{
u8 oldcnt = VRAMCNT[bank];
VRAMCNT[bank] = cnt;
if (oldcnt == cnt) return;
u32 bankmask = 1 << bank;
if (oldcnt & (1<<7))
{
switch (oldcnt & 0x7)
{
case 0: // LCDC
VRAMMap_LCDC &= ~bankmask;
break;
case 1: // ABG
UNMAP_RANGE(VRAMMap_ABG, 0, 4);
break;
case 2: // AOBJ
UNMAP_RANGE(VRAMMap_AOBJ, 0, 4);
break;
case 3: // texture palette
UNMAP_RANGE(VRAMMap_TexPal, 0, 4);
break;
case 4: // ABG ext palette
UNMAP_RANGE(VRAMMap_ABGExtPal, 0, 4);
GPU2D_A->BGExtPalDirty(0);
GPU2D_A->BGExtPalDirty(2);
break;
}
}
if (cnt & (1<<7))
{
switch (cnt & 0x7)
{
case 0: // LCDC
VRAMMap_LCDC |= bankmask;
break;
case 1: // ABG
MAP_RANGE(VRAMMap_ABG, 0, 4);
break;
case 2: // AOBJ
MAP_RANGE(VRAMMap_AOBJ, 0, 4);
break;
case 3: // texture palette
MAP_RANGE(VRAMMap_TexPal, 0, 4);
break;
case 4: // ABG ext palette
MAP_RANGE(VRAMMap_ABGExtPal, 0, 4);
GPU2D_A->BGExtPalDirty(0);
GPU2D_A->BGExtPalDirty(2);
break;
}
}
}
void MapVRAM_FG(u32 bank, u8 cnt)
{
u8 oldcnt = VRAMCNT[bank];
VRAMCNT[bank] = cnt;
if (oldcnt == cnt) return;
u8 oldofs = (oldcnt >> 3) & 0x7;
u8 ofs = (cnt >> 3) & 0x7;
u32 bankmask = 1 << bank;
if (oldcnt & (1<<7))
{
switch (oldcnt & 0x7)
{
case 0: // LCDC
VRAMMap_LCDC &= ~bankmask;
break;
case 1: // ABG
VRAMMap_ABG[(oldofs & 0x1) + ((oldofs & 0x2) << 1)] &= ~bankmask;
VRAMMap_ABG[(oldofs & 0x1) + ((oldofs & 0x2) << 1) + 2] &= ~bankmask;
break;
case 2: // AOBJ
VRAMMap_AOBJ[(oldofs & 0x1) + ((oldofs & 0x2) << 1)] &= ~bankmask;
VRAMMap_AOBJ[(oldofs & 0x1) + ((oldofs & 0x2) << 1) + 2] &= ~bankmask;
break;
case 3: // texture palette
VRAMMap_TexPal[(oldofs & 0x1) + ((oldofs & 0x2) << 1)] &= ~bankmask;
break;
case 4: // ABG ext palette
VRAMMap_ABGExtPal[((oldofs & 0x1) << 1)] &= ~bankmask;
VRAMMap_ABGExtPal[((oldofs & 0x1) << 1) + 1] &= ~bankmask;
GPU2D_A->BGExtPalDirty(0);
GPU2D_A->BGExtPalDirty(2);
break;
case 5: // AOBJ ext palette
VRAMMap_AOBJExtPal &= ~bankmask;
GPU2D_A->OBJExtPalDirty();
break;
}
}
if (cnt & (1<<7))
{
switch (cnt & 0x7)
{
case 0: // LCDC
VRAMMap_LCDC |= bankmask;
break;
case 1: // ABG
VRAMMap_ABG[(ofs & 0x1) + ((ofs & 0x2) << 1)] |= bankmask;
VRAMMap_ABG[(ofs & 0x1) + ((ofs & 0x2) << 1) + 2] |= bankmask;
break;
case 2: // AOBJ
VRAMMap_AOBJ[(ofs & 0x1) + ((ofs & 0x2) << 1)] |= bankmask;
VRAMMap_AOBJ[(ofs & 0x1) + ((ofs & 0x2) << 1) + 2] |= bankmask;
break;
case 3: // texture palette
VRAMMap_TexPal[(ofs & 0x1) + ((ofs & 0x2) << 1)] |= bankmask;
break;
case 4: // ABG ext palette
VRAMMap_ABGExtPal[((ofs & 0x1) << 1)] |= bankmask;
VRAMMap_ABGExtPal[((ofs & 0x1) << 1) + 1] |= bankmask;
GPU2D_A->BGExtPalDirty(0);
GPU2D_A->BGExtPalDirty(2);
break;
case 5: // AOBJ ext palette
VRAMMap_AOBJExtPal |= bankmask;
GPU2D_A->OBJExtPalDirty();
break;
}
}
}
void MapVRAM_H(u32 bank, u8 cnt)
{
u8 oldcnt = VRAMCNT[bank];
VRAMCNT[bank] = cnt;
if (oldcnt == cnt) return;
u32 bankmask = 1 << bank;
if (oldcnt & (1<<7))
{
switch (oldcnt & 0x3)
{
case 0: // LCDC
VRAMMap_LCDC &= ~bankmask;
break;
case 1: // BBG
VRAMMap_BBG[0] &= ~bankmask;
VRAMMap_BBG[1] &= ~bankmask;
VRAMMap_BBG[4] &= ~bankmask;
VRAMMap_BBG[5] &= ~bankmask;
break;
case 2: // BBG ext palette
UNMAP_RANGE(VRAMMap_BBGExtPal, 0, 4);
GPU2D_B->BGExtPalDirty(0);
GPU2D_B->BGExtPalDirty(2);
break;
}
}
if (cnt & (1<<7))
{
switch (cnt & 0x3)
{
case 0: // LCDC
VRAMMap_LCDC |= bankmask;
break;
case 1: // BBG
VRAMMap_BBG[0] |= bankmask;
VRAMMap_BBG[1] |= bankmask;
VRAMMap_BBG[4] |= bankmask;
VRAMMap_BBG[5] |= bankmask;
break;
case 2: // BBG ext palette
MAP_RANGE(VRAMMap_BBGExtPal, 0, 4);
GPU2D_B->BGExtPalDirty(0);
GPU2D_B->BGExtPalDirty(2);
break;
}
}
}
void MapVRAM_I(u32 bank, u8 cnt)
{
u8 oldcnt = VRAMCNT[bank];
VRAMCNT[bank] = cnt;
if (oldcnt == cnt) return;
u32 bankmask = 1 << bank;
if (oldcnt & (1<<7))
{
switch (oldcnt & 0x3)
{
case 0: // LCDC
VRAMMap_LCDC &= ~bankmask;
break;
case 1: // BBG
VRAMMap_BBG[2] &= ~bankmask;
VRAMMap_BBG[3] &= ~bankmask;
VRAMMap_BBG[6] &= ~bankmask;
VRAMMap_BBG[7] &= ~bankmask;
break;
case 2: // BOBJ
UNMAP_RANGE(VRAMMap_BOBJ, 0, 8);
break;
case 3: // BOBJ ext palette
VRAMMap_BOBJExtPal &= ~bankmask;
GPU2D_B->OBJExtPalDirty();
break;
}
}
if (cnt & (1<<7))
{
switch (cnt & 0x3)
{
case 0: // LCDC
VRAMMap_LCDC |= bankmask;
break;
case 1: // BBG
VRAMMap_BBG[2] |= bankmask;
VRAMMap_BBG[3] |= bankmask;
VRAMMap_BBG[6] |= bankmask;
VRAMMap_BBG[7] |= bankmask;
break;
case 2: // BOBJ
MAP_RANGE(VRAMMap_BOBJ, 0, 8);
break;
case 3: // BOBJ ext palette
VRAMMap_BOBJExtPal |= bankmask;
GPU2D_B->OBJExtPalDirty();
break;
}
}
}
void DisplaySwap(u32 val)
{
if (val)
{
GPU2D_A->SetFramebuffer(&Framebuffer[256*0]);
GPU2D_B->SetFramebuffer(&Framebuffer[256*192]);
}
else
{
GPU2D_A->SetFramebuffer(&Framebuffer[256*192]);
GPU2D_B->SetFramebuffer(&Framebuffer[256*0]);
}
}
void StartFrame()
{
StartScanline(0);
}
void StartHBlank(u32 line)
{
DispStat[0] |= (1<<1);
DispStat[1] |= (1<<1);
if (line < 192) NDS::CheckDMAs(0, 0x02);
if (DispStat[0] & (1<<4)) NDS::SetIRQ(0, NDS::IRQ_HBlank);
if (DispStat[1] & (1<<4)) NDS::SetIRQ(1, NDS::IRQ_HBlank);
if (line < 262)
NDS::ScheduleEvent(NDS::Event_LCD, true, (LINE_CYCLES - HBLANK_CYCLES), StartScanline, line+1);
}
void StartScanline(u32 line)
{
VCount = line;
DispStat[0] &= ~(1<<1);
DispStat[1] &= ~(1<<1);
if (line == VMatch[0])
{
DispStat[0] |= (1<<2);
if (DispStat[0] & (1<<5)) NDS::SetIRQ(0, NDS::IRQ_VCount);
}
else
DispStat[0] &= ~(1<<2);
if (line == VMatch[1])
{
DispStat[1] |= (1<<2);
if (DispStat[1] & (1<<5)) NDS::SetIRQ(1, NDS::IRQ_VCount);
}
else
DispStat[1] &= ~(1<<2);
if (line < 192)
{
// draw
GPU2D_A->DrawScanline(line);
GPU2D_B->DrawScanline(line);
//NDS::ScheduleEvent(LINE_CYCLES, StartScanline, line+1);
}
else if (line == 262)
{
// frame end
DispStat[0] &= ~(1<<0);
DispStat[1] &= ~(1<<0);
}
else
{
if (line == 192)
{
// VBlank
DispStat[0] |= (1<<0);
DispStat[1] |= (1<<0);
NDS::CheckDMAs(0, 0x01);
NDS::CheckDMAs(1, 0x11);
if (DispStat[0] & (1<<3)) NDS::SetIRQ(0, NDS::IRQ_VBlank);
if (DispStat[1] & (1<<3)) NDS::SetIRQ(1, NDS::IRQ_VBlank);
GPU2D_A->VBlank();
GPU2D_B->VBlank();
GPU3D::VBlank();
}
//NDS::ScheduleEvent(LINE_CYCLES, StartScanline, line+1);
//NDS::ScheduleEvent(NDS::Event_LCD, true, LINE_CYCLES, StartScanline, line+1);
}
NDS::ScheduleEvent(NDS::Event_LCD, true, HBLANK_CYCLES, StartHBlank, line);
}
void SetDispStat(u32 cpu, u16 val)
{
val &= 0xFFB8;
DispStat[cpu] &= 0x0047;
DispStat[cpu] |= val;
VMatch[cpu] = (val >> 8) | ((val & 0x80) << 1);
}
}

395
src/GPU.h Normal file
View File

@ -0,0 +1,395 @@
/*
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/.
*/
#ifndef GPU_H
#define GPU_H
#include "GPU2D.h"
#include "GPU3D.h"
namespace GPU
{
extern u16 VCount;
extern u16 DispStat[2];
extern u8 VRAMCNT[9];
extern u8 VRAMSTAT;
extern u8 Palette[2*1024];
extern u8 OAM[2*1024];
extern u8 VRAM_A[128*1024];
extern u8 VRAM_B[128*1024];
extern u8 VRAM_C[128*1024];
extern u8 VRAM_D[128*1024];
extern u8 VRAM_E[ 64*1024];
extern u8 VRAM_F[ 16*1024];
extern u8 VRAM_G[ 16*1024];
extern u8 VRAM_H[ 32*1024];
extern u8 VRAM_I[ 16*1024];
extern u8* VRAM[9];
extern u32 VRAMMap_LCDC;
extern u32 VRAMMap_ABG[0x20];
extern u32 VRAMMap_AOBJ[0x10];
extern u32 VRAMMap_BBG[0x8];
extern u32 VRAMMap_BOBJ[0x8];
extern u32 VRAMMap_ABGExtPal[4];
extern u32 VRAMMap_AOBJExtPal;
extern u32 VRAMMap_BBGExtPal[4];
extern u32 VRAMMap_BOBJExtPal;
extern u32 VRAMMap_Texture[4];
extern u32 VRAMMap_TexPal[6];
extern u32 VRAMMap_ARM7[2];
extern u32 Framebuffer[256*192*2];
extern GPU2D* GPU2D_A;
extern GPU2D* GPU2D_B;
bool Init();
void DeInit();
void Reset();
void MapVRAM_AB(u32 bank, u8 cnt);
void MapVRAM_CD(u32 bank, u8 cnt);
void MapVRAM_E(u32 bank, u8 cnt);
void MapVRAM_FG(u32 bank, u8 cnt);
void MapVRAM_H(u32 bank, u8 cnt);
void MapVRAM_I(u32 bank, u8 cnt);
template<typename T>
T ReadVRAM_LCDC(u32 addr)
{
int bank;
switch (addr & 0xFF8FC000)
{
case 0x06800000: case 0x06804000: case 0x06808000: case 0x0680C000:
case 0x06810000: case 0x06814000: case 0x06818000: case 0x0681C000:
bank = 0;
addr &= 0x1FFFF;
break;
case 0x06820000: case 0x06824000: case 0x06828000: case 0x0682C000:
case 0x06830000: case 0x06834000: case 0x06838000: case 0x0683C000:
bank = 1;
addr &= 0x1FFFF;
break;
case 0x06840000: case 0x06844000: case 0x06848000: case 0x0684C000:
case 0x06850000: case 0x06854000: case 0x06858000: case 0x0685C000:
bank = 2;
addr &= 0x1FFFF;
break;
case 0x06860000: case 0x06864000: case 0x06868000: case 0x0686C000:
case 0x06870000: case 0x06874000: case 0x06878000: case 0x0687C000:
bank = 3;
addr &= 0x1FFFF;
break;
case 0x06880000: case 0x06884000: case 0x06888000: case 0x0688C000:
bank = 4;
addr &= 0xFFFF;
break;
case 0x06890000:
bank = 5;
addr &= 0x3FFF;
break;
case 0x06894000:
bank = 6;
addr &= 0x3FFF;
break;
case 0x06898000:
case 0x0689C000:
bank = 7;
addr &= 0x7FFF;
break;
case 0x068A0000:
bank = 8;
addr &= 0x3FFF;
break;
default: return 0;
}
if (VRAMMap_LCDC & (1<<bank)) return *(T*)&VRAM[bank][addr];
return 0;
}
template<typename T>
void WriteVRAM_LCDC(u32 addr, T val)
{
int bank;
switch (addr & 0xFF8FC000)
{
case 0x06800000: case 0x06804000: case 0x06808000: case 0x0680C000:
case 0x06810000: case 0x06814000: case 0x06818000: case 0x0681C000:
bank = 0;
addr &= 0x1FFFF;
break;
case 0x06820000: case 0x06824000: case 0x06828000: case 0x0682C000:
case 0x06830000: case 0x06834000: case 0x06838000: case 0x0683C000:
bank = 1;
addr &= 0x1FFFF;
break;
case 0x06840000: case 0x06844000: case 0x06848000: case 0x0684C000:
case 0x06850000: case 0x06854000: case 0x06858000: case 0x0685C000:
bank = 2;
addr &= 0x1FFFF;
break;
case 0x06860000: case 0x06864000: case 0x06868000: case 0x0686C000:
case 0x06870000: case 0x06874000: case 0x06878000: case 0x0687C000:
bank = 3;
addr &= 0x1FFFF;
break;
case 0x06880000: case 0x06884000: case 0x06888000: case 0x0688C000:
bank = 4;
addr &= 0xFFFF;
break;
case 0x06890000:
bank = 5;
addr &= 0x3FFF;
break;
case 0x06894000:
bank = 6;
addr &= 0x3FFF;
break;
case 0x06898000:
case 0x0689C000:
bank = 7;
addr &= 0x7FFF;
break;
case 0x068A0000:
bank = 8;
addr &= 0x3FFF;
break;
default: return;
}
if (VRAMMap_LCDC & (1<<bank)) *(T*)&VRAM[bank][addr] = val;
}
template<typename T>
T ReadVRAM_ABG(u32 addr)
{
u32 ret = 0;
u32 mask = VRAMMap_ABG[(addr >> 14) & 0x1F];
if (mask & (1<<0)) ret |= *(T*)&VRAM_A[addr & 0x1FFFF];
if (mask & (1<<1)) ret |= *(T*)&VRAM_B[addr & 0x1FFFF];
if (mask & (1<<2)) ret |= *(T*)&VRAM_C[addr & 0x1FFFF];
if (mask & (1<<3)) ret |= *(T*)&VRAM_D[addr & 0x1FFFF];
if (mask & (1<<4)) ret |= *(T*)&VRAM_E[addr & 0xFFFF];
if (mask & (1<<5)) ret |= *(T*)&VRAM_F[addr & 0x3FFF];
if (mask & (1<<6)) ret |= *(T*)&VRAM_G[addr & 0x3FFF];
return ret;
}
template<typename T>
void WriteVRAM_ABG(u32 addr, T val)
{
u32 mask = VRAMMap_ABG[(addr >> 14) & 0x1F];
if (mask & (1<<0)) *(T*)&VRAM_A[addr & 0x1FFFF] = val;
if (mask & (1<<1)) *(T*)&VRAM_B[addr & 0x1FFFF] = val;
if (mask & (1<<2)) *(T*)&VRAM_C[addr & 0x1FFFF] = val;
if (mask & (1<<3)) *(T*)&VRAM_D[addr & 0x1FFFF] = val;
if (mask & (1<<4)) *(T*)&VRAM_E[addr & 0xFFFF] = val;
if (mask & (1<<5)) *(T*)&VRAM_F[addr & 0x3FFF] = val;
if (mask & (1<<6)) *(T*)&VRAM_G[addr & 0x3FFF] = val;
}
template<typename T>
T ReadVRAM_AOBJ(u32 addr)
{
u32 ret = 0;
u32 mask = VRAMMap_AOBJ[(addr >> 14) & 0xF];
if (mask & (1<<0)) ret |= *(T*)&VRAM_A[addr & 0x1FFFF];
if (mask & (1<<1)) ret |= *(T*)&VRAM_B[addr & 0x1FFFF];
if (mask & (1<<4)) ret |= *(T*)&VRAM_E[addr & 0xFFFF];
if (mask & (1<<5)) ret |= *(T*)&VRAM_F[addr & 0x3FFF];
if (mask & (1<<6)) ret |= *(T*)&VRAM_G[addr & 0x3FFF];
return ret;
}
template<typename T>
void WriteVRAM_AOBJ(u32 addr, T val)
{
u32 mask = VRAMMap_AOBJ[(addr >> 14) & 0xF];
if (mask & (1<<0)) *(T*)&VRAM_A[addr & 0x1FFFF] = val;
if (mask & (1<<1)) *(T*)&VRAM_B[addr & 0x1FFFF] = val;
if (mask & (1<<4)) *(T*)&VRAM_E[addr & 0xFFFF] = val;
if (mask & (1<<5)) *(T*)&VRAM_F[addr & 0x3FFF] = val;
if (mask & (1<<6)) *(T*)&VRAM_G[addr & 0x3FFF] = val;
}
template<typename T>
T ReadVRAM_BBG(u32 addr)
{
u32 ret = 0;
u32 mask = VRAMMap_BBG[(addr >> 14) & 0x7];
if (mask & (1<<2)) ret |= *(T*)&VRAM_C[addr & 0x1FFFF];
if (mask & (1<<7)) ret |= *(T*)&VRAM_H[addr & 0x7FFF];
if (mask & (1<<8)) ret |= *(T*)&VRAM_I[addr & 0x3FFF];
return ret;
}
template<typename T>
void WriteVRAM_BBG(u32 addr, T val)
{
u32 mask = VRAMMap_BBG[(addr >> 14) & 0x7];
if (mask & (1<<2)) *(T*)&VRAM_C[addr & 0x1FFFF] = val;
if (mask & (1<<7)) *(T*)&VRAM_H[addr & 0x7FFF] = val;
if (mask & (1<<8)) *(T*)&VRAM_I[addr & 0x3FFF] = val;
}
template<typename T>
T ReadVRAM_BOBJ(u32 addr)
{
u32 ret = 0;
u32 mask = VRAMMap_BOBJ[(addr >> 14) & 0x7];
if (mask & (1<<3)) ret |= *(T*)&VRAM_D[addr & 0x1FFFF];
if (mask & (1<<8)) ret |= *(T*)&VRAM_I[addr & 0x3FFF];
return ret;
}
template<typename T>
void WriteVRAM_BOBJ(u32 addr, T val)
{
u32 mask = VRAMMap_BOBJ[(addr >> 14) & 0x7];
if (mask & (1<<3)) *(T*)&VRAM_D[addr & 0x1FFFF] = val;
if (mask & (1<<8)) *(T*)&VRAM_I[addr & 0x3FFF] = val;
}
template<typename T>
T ReadVRAM_ARM7(u32 addr)
{
u32 ret = 0;
u32 mask = VRAMMap_ARM7[(addr >> 17) & 0x1];
if (mask & (1<<2)) ret |= *(T*)&VRAM_C[addr & 0x1FFFF];
if (mask & (1<<3)) ret |= *(T*)&VRAM_D[addr & 0x1FFFF];
return ret;
}
template<typename T>
void WriteVRAM_ARM7(u32 addr, T val)
{
u32 mask = VRAMMap_ARM7[(addr >> 17) & 0x1];
if (mask & (1<<2)) *(T*)&VRAM_C[addr & 0x1FFFF] = val;
if (mask & (1<<3)) *(T*)&VRAM_D[addr & 0x1FFFF] = val;
}
template<typename T>
T ReadVRAM_BG(u32 addr)
{
if ((addr & 0xFFE00000) == 0x06000000)
return ReadVRAM_ABG<T>(addr);
else
return ReadVRAM_BBG<T>(addr);
}
template<typename T>
T ReadVRAM_OBJ(u32 addr)
{
if ((addr & 0xFFE00000) == 0x06400000)
return ReadVRAM_AOBJ<T>(addr);
else
return ReadVRAM_BOBJ<T>(addr);
}
template<typename T>
T ReadVRAM_Texture(u32 addr)
{
u32 ret = 0;
u32 mask = VRAMMap_Texture[(addr >> 17) & 0x3];
if (mask & (1<<0)) ret |= *(T*)&VRAM_A[addr & 0x1FFFF];
if (mask & (1<<1)) ret |= *(T*)&VRAM_B[addr & 0x1FFFF];
if (mask & (1<<2)) ret |= *(T*)&VRAM_C[addr & 0x1FFFF];
if (mask & (1<<3)) ret |= *(T*)&VRAM_D[addr & 0x1FFFF];
return ret;
}
template<typename T>
T ReadVRAM_TexPal(u32 addr)
{
u32 ret = 0;
if (addr >= 0x18000) return 0;
u32 mask = VRAMMap_TexPal[(addr >> 14) & 0x7];
if (mask & (1<<4)) ret |= *(T*)&VRAM_E[addr & 0xFFFF];
if (mask & (1<<5)) ret |= *(T*)&VRAM_F[addr & 0x3FFF];
if (mask & (1<<6)) ret |= *(T*)&VRAM_G[addr & 0x3FFF];
return ret;
}
void DisplaySwap(u32 val);
void StartFrame();
void StartScanline(u32 line);
void SetDispStat(u32 cpu, u16 val);
}
#endif

1604
src/GPU2D.cpp Normal file

File diff suppressed because it is too large Load Diff

97
src/GPU2D.h Normal file
View File

@ -0,0 +1,97 @@
/*
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/.
*/
#ifndef GPU2D_H
#define GPU2D_H
class GPU2D
{
public:
GPU2D(u32 num);
~GPU2D();
void Reset();
void SetFramebuffer(u32* buf);
u8 Read8(u32 addr);
u16 Read16(u32 addr);
u32 Read32(u32 addr);
void Write8(u32 addr, u8 val);
void Write16(u32 addr, u16 val);
void Write32(u32 addr, u32 val);
void DrawScanline(u32 line);
void VBlank();
void BGExtPalDirty(u32 base);
void OBJExtPalDirty();
u16* GetBGExtPal(u32 slot, u32 pal);
u16* GetOBJExtPal(u32 pal);
private:
u32 Num;
u32* Framebuffer;
u32 DispCnt;
u16 BGCnt[4];
u16 BGXPos[4];
u16 BGYPos[4];
s32 BGXRef[2];
s32 BGYRef[2];
s32 BGXRefInternal[2];
s32 BGYRefInternal[2];
s16 BGRotA[2];
s16 BGRotB[2];
s16 BGRotC[2];
s16 BGRotD[2];
u16 BlendCnt;
u8 EVA, EVB;
u8 EVY;
u32 CaptureCnt;
u16 MasterBrightness;
u16 BGExtPalCache[4][16*256];
u16 OBJExtPalCache[16*256];
u32 BGExtPalStatus[4];
u32 OBJExtPalStatus;
template<u32 bgmode> void DrawScanlineBGMode(u32 line, u32* spritebuf, u32* dst);
void DrawScanline_Mode1(u32 line, u32* dst);
void DrawPixel(u32* dst, u16 color, u32 flag);
void DrawBG_3D(u32 line, u32* dst);
void DrawBG_Text(u32 line, u32* dst, u32 num);
void DrawBG_Extended(u32 line, u32* dst, u32 bgnum);
void InterleaveSprites(u32* buf, u32 prio, u32* dst);
void DrawSprites(u32 line, u32* dst);
void DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32 boundheight, u32 width, u32 height, s32 xpos, u32 ypos, u32* dst);
void DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, u32 ypos, u32* dst);
void DoCapture(u32 line, u32 width, u32* src);
};
#endif

1917
src/GPU3D.cpp Normal file

File diff suppressed because it is too large Load Diff

98
src/GPU3D.h Normal file
View File

@ -0,0 +1,98 @@
/*
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/.
*/
#ifndef GPU3D_H
#define GPU3D_H
namespace GPU3D
{
typedef struct
{
s32 Position[4];
s32 Color[3];
s16 TexCoords[2];
bool Clipped;
// final vertex attributes.
// allows them to be reused in polygon strips.
s32 FinalPosition[4];
s32 FinalColor[3];
} Vertex;
typedef struct
{
Vertex* Vertices[10];
u32 NumVertices;
u32 Attr;
u32 TexParam;
u32 TexPalette;
bool FacingView;
bool Translucent;
u32 VTop, VBottom; // vertex indices
s32 YTop, YBottom; // Y coords
s32 XTop, XBottom; // associated X coords
} Polygon;
extern u32 DispCnt;
extern u32 AlphaRef;
extern s32 Viewport[4];
extern u32 ClearAttr1, ClearAttr2;
bool Init();
void DeInit();
void Reset();
void ExecuteCommand();
void Run(s32 cycles);
void CheckFIFOIRQ();
void CheckFIFODMA();
void VBlank();
u32* GetLine(int line);
u8 Read8(u32 addr);
u16 Read16(u32 addr);
u32 Read32(u32 addr);
void Write8(u32 addr, u8 val);
void Write16(u32 addr, u16 val);
void Write32(u32 addr, u32 val);
namespace SoftRenderer
{
bool Init();
void DeInit();
void Reset();
void RenderFrame(Vertex* vertices, Polygon* polygons, int npolys);
u32* GetLine(int line);
}
}
#endif

853
src/GPU3D_Soft.cpp Normal file
View File

@ -0,0 +1,853 @@
/*
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"
namespace GPU3D
{
namespace SoftRenderer
{
u32 ColorBuffer[256*192];
u32 DepthBuffer[256*192];
u32 AttrBuffer[256*192];
// attribute buffer:
// bit0-5: polygon ID
// bit8: fog enable
bool Init()
{
return true;
}
void DeInit()
{
}
void Reset()
{
memset(ColorBuffer, 0, 256*192 * 4);
memset(DepthBuffer, 0, 256*192 * 4);
memset(AttrBuffer, 0, 256*192 * 4);
}
void TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha)
{
u32 vramaddr = (texparam & 0xFFFF) << 3;
u32 width = 8 << ((texparam >> 20) & 0x7);
u32 height = 8 << ((texparam >> 23) & 0x7);
s >>= 4;
t >>= 4;
// texture wrapping
// TODO: optimize this somehow
if (texparam & (1<<16))
{
if (texparam & (1<<18))
{
if (s & width) s = (width-1) - (s & (width-1));
else s = (s & (width-1));
}
else
s &= width-1;
}
else
{
if (s < 0) s = 0;
else if (s >= width) s = width-1;
}
if (texparam & (1<<17))
{
if (texparam & (1<<19))
{
if (t & height) t = (height-1) - (t & (height-1));
else t = (t & (height-1));
}
else
t &= height-1;
}
else
{
if (t < 0) t = 0;
else if (t >= height) t = height-1;
}
u8 alpha0;
if (texparam & (1<<29)) alpha0 = 0;
else alpha0 = 31;
switch ((texparam >> 26) & 0x7)
{
case 1: // A3I5
{
vramaddr += ((t * width) + s);
u8 pixel = GPU::ReadVRAM_Texture<u8>(vramaddr);
texpal <<= 4;
*color = GPU::ReadVRAM_TexPal<u16>(texpal + ((pixel&0x1F)<<1));
*alpha = ((pixel >> 3) & 0x1C) + (pixel >> 6);
}
break;
case 2: // 4-color
{
vramaddr += (((t * width) + s) >> 2);
u8 pixel = GPU::ReadVRAM_Texture<u8>(vramaddr);
pixel >>= ((s & 0x3) << 1);
pixel &= 0x3;
texpal <<= 3;
*color = GPU::ReadVRAM_TexPal<u16>(texpal + (pixel<<1));
*alpha = (pixel==0) ? alpha0 : 31;
}
break;
case 3: // 16-color
{
vramaddr += (((t * width) + s) >> 1);
u8 pixel = GPU::ReadVRAM_Texture<u8>(vramaddr);
if (s & 0x1) pixel >>= 4;
else pixel &= 0xF;
texpal <<= 4;
*color = GPU::ReadVRAM_TexPal<u16>(texpal + (pixel<<1));
*alpha = (pixel==0) ? alpha0 : 31;
}
break;
case 4: // 256-color
{
vramaddr += ((t * width) + s);
u8 pixel = GPU::ReadVRAM_Texture<u8>(vramaddr);
texpal <<= 4;
*color = GPU::ReadVRAM_TexPal<u16>(texpal + (pixel<<1));
*alpha = (pixel==0) ? alpha0 : 31;
}
break;
case 5: // compressed
{
vramaddr += ((t & 0x3FC) * (width>>2)) + (s & 0x3FC);
vramaddr += (t & 0x3);
u32 slot1addr = 0x20000 + ((vramaddr & 0x1FFFC) >> 1);
if (vramaddr >= 0x40000)
slot1addr += 0x10000;
u8 val = GPU::ReadVRAM_Texture<u8>(vramaddr);
val >>= (2 * (s & 0x3));
u16 palinfo = GPU::ReadVRAM_Texture<u16>(slot1addr);
u32 paloffset = (palinfo & 0x3FFF) << 2;
texpal <<= 4;
switch (val & 0x3)
{
case 0:
*color = GPU::ReadVRAM_TexPal<u16>(texpal + paloffset);
*alpha = 31;
break;
case 1:
*color = GPU::ReadVRAM_TexPal<u16>(texpal + paloffset + 2);
*alpha = 31;
break;
case 2:
if ((palinfo >> 14) == 1)
{
u16 color0 = GPU::ReadVRAM_TexPal<u16>(texpal + paloffset);
u16 color1 = GPU::ReadVRAM_TexPal<u16>(texpal + paloffset + 2);
u32 r0 = color0 & 0x001F;
u32 g0 = color0 & 0x03E0;
u32 b0 = color0 & 0x7C00;
u32 r1 = color1 & 0x001F;
u32 g1 = color1 & 0x03E0;
u32 b1 = color1 & 0x7C00;
u32 r = (r0 + r1) >> 1;
u32 g = ((g0 + g1) >> 1) & 0x03E0;
u32 b = ((b0 + b1) >> 1) & 0x7C00;
*color = r | g | b;
}
else if ((palinfo >> 14) == 3)
{
u16 color0 = GPU::ReadVRAM_TexPal<u16>(texpal + paloffset);
u16 color1 = GPU::ReadVRAM_TexPal<u16>(texpal + paloffset + 2);
u32 r0 = color0 & 0x001F;
u32 g0 = color0 & 0x03E0;
u32 b0 = color0 & 0x7C00;
u32 r1 = color1 & 0x001F;
u32 g1 = color1 & 0x03E0;
u32 b1 = color1 & 0x7C00;
u32 r = (r0*5 + r1*3) >> 3;
u32 g = ((g0*5 + g1*3) >> 3) & 0x03E0;
u32 b = ((b0*5 + b1*3) >> 3) & 0x7C00;
*color = r | g | b;
}
else
*color = GPU::ReadVRAM_TexPal<u16>(texpal + paloffset + 4);
*alpha = 31;
break;
case 3:
if ((palinfo >> 14) == 2)
{
*color = GPU::ReadVRAM_TexPal<u16>(texpal + paloffset + 6);
*alpha = 31;
}
else if ((palinfo >> 14) == 3)
{
u16 color0 = GPU::ReadVRAM_TexPal<u16>(texpal + paloffset);
u16 color1 = GPU::ReadVRAM_TexPal<u16>(texpal + paloffset + 2);
u32 r0 = color0 & 0x001F;
u32 g0 = color0 & 0x03E0;
u32 b0 = color0 & 0x7C00;
u32 r1 = color1 & 0x001F;
u32 g1 = color1 & 0x03E0;
u32 b1 = color1 & 0x7C00;
u32 r = (r0*3 + r1*5) >> 3;
u32 g = ((g0*3 + g1*5) >> 3) & 0x03E0;
u32 b = ((b0*3 + b1*5) >> 3) & 0x7C00;
*color = r | g | b;
*alpha = 31;
}
else
{
*color = 0;
*alpha = 0;
}
break;
}
}
break;
case 6: // A5I3
{
vramaddr += ((t * width) + s);
u8 pixel = GPU::ReadVRAM_Texture<u8>(vramaddr);
texpal <<= 4;
*color = GPU::ReadVRAM_TexPal<u16>(texpal + ((pixel&0x7)<<1));
*alpha = (pixel >> 3);
}
break;
case 7: // direct color
{
vramaddr += (((t * width) + s) << 1);
*color = GPU::ReadVRAM_Texture<u16>(vramaddr);
*alpha = (*color & 0x8000) ? 31 : 0;
}
break;
}
}
bool DepthTest(Polygon* polygon, s32 x, s32 y, s32 z)
{
u32 oldz = DepthBuffer[(256*y) + x];
if (polygon->Attr & (1<<14))
{
s32 diff = oldz - z;
if ((u32)(diff + 0x200) <= 0x400)
return true;
}
else
if (z < oldz)
return true;
return false;
}
u32 RenderPixel(Polygon* polygon, s32 x, s32 y, s32 z, u8 vr, u8 vg, u8 vb, s16 s, s16 t)
{
u32 attr = polygon->Attr;
u8 r, g, b, a;
u32 polyalpha = (polygon->Attr >> 16) & 0x1F;
bool wireframe = (polyalpha == 0);
if ((DispCnt & (1<<0)) && (((polygon->TexParam >> 26) & 0x7) != 0))
{
u8 tr, tg, tb;
u16 tcolor; u8 talpha;
TextureLookup(polygon->TexParam, polygon->TexPalette, s, t, &tcolor, &talpha);
tr = (tcolor << 1) & 0x3E; if (tr) tr++;
tg = (tcolor >> 4) & 0x3E; if (tg) tg++;
tb = (tcolor >> 9) & 0x3E; if (tb) tb++;
// TODO: other blending modes
r = ((tr+1) * (vr+1) - 1) >> 6;
g = ((tg+1) * (vg+1) - 1) >> 6;
b = ((tb+1) * (vb+1) - 1) >> 6;
a = ((talpha+1) * (polyalpha+1) - 1) >> 5;
}
else
{
r = vr;
g = vg;
b = vb;
a = polyalpha;
}
if (wireframe) a = 31;
return r | (g << 8) | (b << 16) | (a << 24);
}
void RenderPolygon(Polygon* polygon)
{
int nverts = polygon->NumVertices;
bool isline = false;
int vtop = polygon->VTop, vbot = polygon->VBottom;
s32 ytop = polygon->YTop, ybot = polygon->YBottom;
s32 xtop = polygon->XTop, xbot = polygon->XBottom;
if (ytop > 191) return;
// draw, line per line
u32 polyalpha = (polygon->Attr >> 16) & 0x1F;
bool wireframe = (polyalpha == 0);
int lcur = vtop, rcur = vtop;
int lnext, rnext;
s32 dxl, dxr;
s32 lslope, rslope;
bool l_xmajor, r_xmajor;
if (ybot == ytop)
{
ybot++;
isline = true;
vtop = 0; vbot = 0;
xtop = 256; xbot = 0;
int i;
i = 1;
if (polygon->Vertices[i]->FinalPosition[0] < polygon->Vertices[vtop]->FinalPosition[0]) vtop = i;
if (polygon->Vertices[i]->FinalPosition[0] > polygon->Vertices[vbot]->FinalPosition[0]) vbot = i;
i = nverts - 1;
if (polygon->Vertices[i]->FinalPosition[0] < polygon->Vertices[vtop]->FinalPosition[0]) vtop = i;
if (polygon->Vertices[i]->FinalPosition[0] > polygon->Vertices[vbot]->FinalPosition[0]) vbot = i;
lcur = vtop; lnext = vtop;
rcur = vbot; rnext = vbot;
lslope = 0; l_xmajor = false;
rslope = 0; r_xmajor = false;
}
else
{
//while (polygon->Vertices[lnext]->FinalPosition[1] )
if (polygon->FacingView)
{
lnext = lcur + 1;
if (lnext >= nverts) lnext = 0;
rnext = rcur - 1;
if (rnext < 0) rnext = nverts - 1;
}
else
{
lnext = lcur - 1;
if (lnext < 0) lnext = nverts - 1;
rnext = rcur + 1;
if (rnext >= nverts) rnext = 0;
}
if (polygon->Vertices[lnext]->FinalPosition[1] == polygon->Vertices[lcur]->FinalPosition[1])
lslope = 0;
else
lslope = ((polygon->Vertices[lnext]->FinalPosition[0] - polygon->Vertices[lcur]->FinalPosition[0]) << 12) /
(polygon->Vertices[lnext]->FinalPosition[1] - polygon->Vertices[lcur]->FinalPosition[1]);
if (polygon->Vertices[rnext]->FinalPosition[1] == polygon->Vertices[rcur]->FinalPosition[1])
rslope = 0;
else
rslope = ((polygon->Vertices[rnext]->FinalPosition[0] - polygon->Vertices[rcur]->FinalPosition[0]) << 12) /
(polygon->Vertices[rnext]->FinalPosition[1] - polygon->Vertices[rcur]->FinalPosition[1]);
l_xmajor = (lslope < -0x1000) || (lslope > 0x1000);
r_xmajor = (rslope < -0x1000) || (rslope > 0x1000);
}
if (l_xmajor) dxl = (lslope > 0) ? 0x800 : (-lslope-0x800)+0x1000;
else if (lslope) dxl = (lslope > 0) ? 0 : 0x1000;
else dxl = 0;
if (r_xmajor) dxr = (rslope > 0) ? rslope-0x800 : 0x800+0x1000;
else if (rslope) dxr = (rslope > 0) ? 0 : 0x1000;
else dxr = 0x1000;
if (ybot > 192) ybot = 192;
for (s32 y = ytop; y < ybot; y++)
{
if (!isline)
{
if (y >= polygon->Vertices[lnext]->FinalPosition[1] && lcur != vbot)
{
while (y >= polygon->Vertices[lnext]->FinalPosition[1] && lcur != vbot)
{
lcur = lnext;
if (polygon->FacingView)
{
lnext = lcur + 1;
if (lnext >= nverts) lnext = 0;
}
else
{
lnext = lcur - 1;
if (lnext < 0) lnext = nverts - 1;
}
}
if (polygon->Vertices[lnext]->FinalPosition[1] == polygon->Vertices[lcur]->FinalPosition[1])
lslope = 0;
else
lslope = ((polygon->Vertices[lnext]->FinalPosition[0] - polygon->Vertices[lcur]->FinalPosition[0]) << 12) /
(polygon->Vertices[lnext]->FinalPosition[1] - polygon->Vertices[lcur]->FinalPosition[1]);
l_xmajor = (lslope < -0x1000) || (lslope > 0x1000);
if (l_xmajor) dxl = (lslope > 0) ? 0x800 : (-lslope-0x800)+0x1000;
else if (lslope) dxl = (lslope > 0) ? 0 : 0x1000;
else dxl = 0;
}
if (y >= polygon->Vertices[rnext]->FinalPosition[1] && rcur != vbot)
{
while (y >= polygon->Vertices[rnext]->FinalPosition[1] && rcur != vbot)
{
rcur = rnext;
if (polygon->FacingView)
{
rnext = rcur - 1;
if (rnext < 0) rnext = nverts - 1;
}
else
{
rnext = rcur + 1;
if (rnext >= nverts) rnext = 0;
}
}
if (polygon->Vertices[rnext]->FinalPosition[1] == polygon->Vertices[rcur]->FinalPosition[1])
rslope = 0;
else
rslope = ((polygon->Vertices[rnext]->FinalPosition[0] - polygon->Vertices[rcur]->FinalPosition[0]) << 12) /
(polygon->Vertices[rnext]->FinalPosition[1] - polygon->Vertices[rcur]->FinalPosition[1]);
r_xmajor = (rslope < -0x1000) || (rslope > 0x1000);
if (r_xmajor) dxr = (rslope > 0) ? rslope-0x800 : 0x800+0x1000;
else if (rslope) dxr = (rslope > 0) ? 0 : 0x1000;
else dxr = 0x1000;
}
}
Vertex *vlcur, *vlnext, *vrcur, *vrnext;
s32 xstart, xend;
s32 xstart_int, xend_int;
s32 slope_start, slope_end;
if (lslope == 0 && rslope == 0 &&
polygon->Vertices[lcur]->FinalPosition[0] == polygon->Vertices[rcur]->FinalPosition[0])
{
xstart = polygon->Vertices[lcur]->FinalPosition[0];
xend = xstart;
}
else
{
if (lslope > 0)
{
xstart = polygon->Vertices[lcur]->FinalPosition[0] + (dxl >> 12);
if (xstart < polygon->Vertices[lcur]->FinalPosition[0])
xstart = polygon->Vertices[lcur]->FinalPosition[0];
else if (xstart > polygon->Vertices[lnext]->FinalPosition[0]-1)
xstart = polygon->Vertices[lnext]->FinalPosition[0]-1;
}
else if (lslope < 0)
{
xstart = polygon->Vertices[lcur]->FinalPosition[0] - (dxl >> 12);
if (xstart < polygon->Vertices[lnext]->FinalPosition[0])
xstart = polygon->Vertices[lnext]->FinalPosition[0];
else if (xstart > polygon->Vertices[lcur]->FinalPosition[0]-1)
xstart = polygon->Vertices[lcur]->FinalPosition[0]-1;
}
else
xstart = polygon->Vertices[lcur]->FinalPosition[0];
if (rslope > 0)
{
xend = polygon->Vertices[rcur]->FinalPosition[0] + (dxr >> 12);
if (xend < polygon->Vertices[rcur]->FinalPosition[0])
xend = polygon->Vertices[rcur]->FinalPosition[0];
else if (xend > polygon->Vertices[rnext]->FinalPosition[0]-1)
xend = polygon->Vertices[rnext]->FinalPosition[0]-1;
}
else if (rslope < 0)
{
xend = polygon->Vertices[rcur]->FinalPosition[0] - (dxr >> 12);
if (xend < polygon->Vertices[rnext]->FinalPosition[0])
xend = polygon->Vertices[rnext]->FinalPosition[0];
else if (xend > polygon->Vertices[rcur]->FinalPosition[0]-1)
xend = polygon->Vertices[rcur]->FinalPosition[0]-1;
}
else
xend = polygon->Vertices[rcur]->FinalPosition[0] - 1;
}
// if the left and right edges are swapped, render backwards.
// note: we 'forget' to swap the xmajor flags, on purpose
// the hardware has the same bug
if (xstart > xend)
{
vlcur = polygon->Vertices[rcur];
vlnext = polygon->Vertices[rnext];
vrcur = polygon->Vertices[lcur];
vrnext = polygon->Vertices[lnext];
slope_start = rslope;
slope_end = lslope;
s32 tmp = xstart; xstart = xend; xend = tmp;
}
else
{
vlcur = polygon->Vertices[lcur];
vlnext = polygon->Vertices[lnext];
vrcur = polygon->Vertices[rcur];
vrnext = polygon->Vertices[rnext];
slope_start = lslope;
slope_end = rslope;
}
// interpolate attributes along Y
s64 lfactor1, lfactor2;
s64 rfactor1, rfactor2;
if (l_xmajor)
{
lfactor1 = (vlnext->FinalPosition[0] - xstart) * vlnext->FinalPosition[3];
lfactor2 = (xstart - vlcur->FinalPosition[0]) * vlcur->FinalPosition[3];
}
else
{
lfactor1 = (vlnext->FinalPosition[1] - y) * vlnext->FinalPosition[3];
lfactor2 = (y - vlcur->FinalPosition[1]) * vlcur->FinalPosition[3];
}
s64 ldenom = lfactor1 + lfactor2;
if (ldenom == 0)
{
lfactor1 = 0x1000;
lfactor2 = 0;
ldenom = 0x1000;
}
if (r_xmajor)
{
rfactor1 = (vrnext->FinalPosition[0] - xend+1) * vrnext->FinalPosition[3];
rfactor2 = (xend+1 - vrcur->FinalPosition[0]) * vrcur->FinalPosition[3];
}
else
{
rfactor1 = (vrnext->FinalPosition[1] - y) * vrnext->FinalPosition[3];
rfactor2 = (y - vrcur->FinalPosition[1]) * vrcur->FinalPosition[3];
}
s64 rdenom = rfactor1 + rfactor2;
if (rdenom == 0)
{
rfactor1 = 0x1000;
rfactor2 = 0;
rdenom = 0x1000;
}
s32 zl = ((lfactor1 * vlcur->FinalPosition[2]) + (lfactor2 * vlnext->FinalPosition[2])) / ldenom;
s32 zr = ((rfactor1 * vrcur->FinalPosition[2]) + (rfactor2 * vrnext->FinalPosition[2])) / rdenom;
s32 wl = ((lfactor1 * vlcur->FinalPosition[3]) + (lfactor2 * vlnext->FinalPosition[3])) / ldenom;
s32 wr = ((rfactor1 * vrcur->FinalPosition[3]) + (rfactor2 * vrnext->FinalPosition[3])) / rdenom;
s32 rl = ((lfactor1 * vlcur->FinalColor[0]) + (lfactor2 * vlnext->FinalColor[0])) / ldenom;
s32 gl = ((lfactor1 * vlcur->FinalColor[1]) + (lfactor2 * vlnext->FinalColor[1])) / ldenom;
s32 bl = ((lfactor1 * vlcur->FinalColor[2]) + (lfactor2 * vlnext->FinalColor[2])) / ldenom;
s32 sl = ((lfactor1 * vlcur->TexCoords[0]) + (lfactor2 * vlnext->TexCoords[0])) / ldenom;
s32 tl = ((lfactor1 * vlcur->TexCoords[1]) + (lfactor2 * vlnext->TexCoords[1])) / ldenom;
s32 rr = ((rfactor1 * vrcur->FinalColor[0]) + (rfactor2 * vrnext->FinalColor[0])) / rdenom;
s32 gr = ((rfactor1 * vrcur->FinalColor[1]) + (rfactor2 * vrnext->FinalColor[1])) / rdenom;
s32 br = ((rfactor1 * vrcur->FinalColor[2]) + (rfactor2 * vrnext->FinalColor[2])) / rdenom;
s32 sr = ((rfactor1 * vrcur->TexCoords[0]) + (rfactor2 * vrnext->TexCoords[0])) / rdenom;
s32 tr = ((rfactor1 * vrcur->TexCoords[1]) + (rfactor2 * vrnext->TexCoords[1])) / rdenom;
// calculate edges
s32 l_edgeend, r_edgestart;
if (l_xmajor)
{
if (slope_start > 0) l_edgeend = vlcur->FinalPosition[0] + ((dxl + slope_start) >> 12);
else l_edgeend = vlcur->FinalPosition[0] - ((dxl - slope_start) >> 12);
if (l_edgeend == xstart) l_edgeend++;
}
else
l_edgeend = xstart + 1;
if (r_xmajor)
{
if (slope_end > 0) r_edgestart = vrcur->FinalPosition[0] + ((dxr + slope_end) >> 12);
else r_edgestart = vrcur->FinalPosition[0] - ((dxr - slope_end) >> 12);
if (r_edgestart == xend_int) r_edgestart--;
}
else
r_edgestart = xend - 1;
// edge fill rules for opaque pixels:
// * right edge is filled if slope > 1
// * left edge is filled if slope <= 1
// * edges with slope = 0 are always filled
// edges are always filled if the pixels are translucent
// in wireframe mode, there are special rules for equal Z (TODO)
for (s32 x = xstart; x <= xend; x++)
{
if (x < 0) continue;
if (x > 255) break;
int edge = 0;
if (y == ytop) edge |= 0x4;
else if (y == ybot-1) edge |= 0x8;
if (x < l_edgeend) edge |= 0x1;
else if (x > r_edgestart) edge |= 0x2;
// wireframe polygons. really ugly, but works
if (wireframe && edge==0) continue;
s64 factor1 = (xend+1 - x) * wr;
s64 factor2 = (x - xstart) * wl;
s64 denom = factor1 + factor2;
if (denom == 0)
{
factor1 = 0x1000;
factor2 = 0;
denom = 0x1000;
}
s32 z = ((factor1 * zl) + (factor2 * zr)) / denom;
if (!DepthTest(polygon, x, y, z)) continue;
u32 vr = ((factor1 * rl) + (factor2 * rr)) / denom;
u32 vg = ((factor1 * gl) + (factor2 * gr)) / denom;
u32 vb = ((factor1 * bl) + (factor2 * br)) / denom;
s16 s = ((factor1 * sl) + (factor2 * sr)) / denom;
s16 t = ((factor1 * tl) + (factor2 * tr)) / denom;
u32 color = RenderPixel(polygon, x, y, z, vr>>3, vg>>3, vb>>3, s, t);
u32 attr = 0;
u32 pixeladdr = (y*256) + x;
u8 alpha = color >> 24;
// alpha test
if (DispCnt & (1<<2))
{
if (alpha <= AlphaRef) continue;
}
else
{
if (alpha == 0) continue;
}
// alpha blending disable
// TODO: check alpha test when blending is disabled
if (!(DispCnt & (1<<3)))
alpha = 31;
u32 dstcolor = ColorBuffer[pixeladdr];
u32 dstalpha = dstcolor >> 24;
if (alpha == 31)
{
// edge fill rules for opaque pixels
// TODO, eventually: antialiasing
if (!wireframe)
{
if ((edge & 0x1) && slope_start > 0x1000)
continue;
if ((edge & 0x2) && (slope_end != 0 && slope_end <= 0x1000))
continue;
}
DepthBuffer[pixeladdr] = z;
}
else if (dstalpha == 0)
{
// TODO: conditional Z-buffer update
DepthBuffer[pixeladdr] = z;
}
else
{
u32 srcR = color & 0x3F;
u32 srcG = (color >> 8) & 0x3F;
u32 srcB = (color >> 16) & 0x3F;
u32 dstR = dstcolor & 0x3F;
u32 dstG = (dstcolor >> 8) & 0x3F;
u32 dstB = (dstcolor >> 16) & 0x3F;
alpha++;
dstR = ((srcR * alpha) + (dstR * (32-alpha))) >> 5;
dstG = ((srcG * alpha) + (dstG * (32-alpha))) >> 5;
dstB = ((srcB * alpha) + (dstB * (32-alpha))) >> 5;
alpha--;
if (alpha > dstalpha) dstalpha = alpha;
color = dstR | (dstG << 8) | (dstB << 16) | (dstalpha << 24);
// TODO: conditional Z-buffer update
DepthBuffer[pixeladdr] = z;
}
ColorBuffer[pixeladdr] = color;
AttrBuffer[pixeladdr] = attr;
}
if (lslope > 0) dxl += lslope;
else dxl -= lslope;
if (rslope > 0) dxr += rslope;
else dxr -= rslope;
}
}
void RenderFrame(Vertex* vertices, Polygon* polygons, int npolys)
{
u32 polyid = (ClearAttr1 >> 24) & 0x3F;
if (DispCnt & (1<<14))
{
u8 xoff = (ClearAttr2 >> 16) & 0xFF;
u8 yoff = (ClearAttr2 >> 24) & 0xFF;
for (int y = 0; y < 256*192; y += 256)
{
for (int x = 0; x < 256; x++)
{
u16 val2 = GPU::ReadVRAM_Texture<u16>(0x40000 + (yoff << 9) + (xoff << 1));
u16 val3 = GPU::ReadVRAM_Texture<u16>(0x60000 + (yoff << 9) + (xoff << 1));
// TODO: confirm color conversion
u32 r = (val2 << 1) & 0x3E; if (r) r++;
u32 g = (val2 >> 4) & 0x3E; if (g) g++;
u32 b = (val2 >> 9) & 0x3E; if (b) b++;
u32 a = (val2 & 0x8000) ? 0x1F000000 : 0;
u32 color = r | (g << 8) | (b << 16) | a;
u32 z = ((val3 & 0x7FFF) * 0x200) + 0x1FF;
if (z >= 0x10000 && z < 0xFFFFFF) z++;
ColorBuffer[y+x] = color;
DepthBuffer[y+x] = z;
AttrBuffer[y+x] = polyid | ((val3 & 0x8000) >> 7);
xoff++;
}
yoff++;
}
}
else
{
// TODO: confirm color conversion
u32 r = (ClearAttr1 << 1) & 0x3E; if (r) r++;
u32 g = (ClearAttr1 >> 4) & 0x3E; if (g) g++;
u32 b = (ClearAttr1 >> 9) & 0x3E; if (b) b++;
u32 a = (ClearAttr1 >> 16) & 0x1F;
u32 color = r | (g << 8) | (b << 16) | (a << 24);
u32 z = ((ClearAttr2 & 0x7FFF) * 0x200) + 0x1FF;
if (z >= 0x10000 && z < 0xFFFFFF) z++;
polyid |= ((ClearAttr1 & 0x8000) >> 7);
for (int i = 0; i < 256*192; i++)
{
ColorBuffer[i] = color;
DepthBuffer[i] = z;
AttrBuffer[i] = polyid;
}
}
// TODO: Y-sorting of translucent polygons
for (int i = 0; i < npolys; i++)
{
if (polygons[i].Translucent) continue;
RenderPolygon(&polygons[i]);
}
for (int i = 0; i < npolys; i++)
{
if (!polygons[i].Translucent) continue;
RenderPolygon(&polygons[i]);
}
}
u32* GetLine(int line)
{
return &ColorBuffer[line * 256];
}
}
}

2192
src/NDS.cpp Normal file

File diff suppressed because it is too large Load Diff

181
src/NDS.h Normal file
View File

@ -0,0 +1,181 @@
/*
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/.
*/
#ifndef NDS_H
#define NDS_H
#include "types.h"
namespace NDS
{
/*#define SCHED_BUF_LEN 64
typedef struct _SchedEvent
{
u32 Delay;
void (*Func)(u32);
u32 Param;
struct _SchedEvent* PrevEvent;
struct _SchedEvent* NextEvent;
} SchedEvent;*/
enum
{
Event_LCD = 0,
Event_Timer9_0,
Event_Timer9_1,
Event_Timer9_2,
Event_Timer9_3,
Event_Timer7_0,
Event_Timer7_1,
Event_Timer7_2,
Event_Timer7_3,
Event_MAX
};
typedef struct
{
void (*Func)(u32 param);
s32 WaitCycles;
u32 Param;
} SchedEvent;
enum
{
IRQ_VBlank = 0,
IRQ_HBlank,
IRQ_VCount,
IRQ_Timer0,
IRQ_Timer1,
IRQ_Timer2,
IRQ_Timer3,
IRQ_RTC,
IRQ_DMA0,
IRQ_DMA1,
IRQ_DMA2,
IRQ_DMA3,
IRQ_Keypad,
IRQ_GBASlot,
IRQ_Unused14,
IRQ_Unused15,
IRQ_IPCSync,
IRQ_IPCSendDone,
IRQ_IPCRecv,
IRQ_CartSendDone,
IRQ_CartIREQMC,
IRQ_GXFIFO,
IRQ_LidOpen,
IRQ_SPI,
IRQ_Wifi
};
typedef struct
{
u16 Reload;
u16 Cnt;
u32 Counter;
u32 CycleShift;
//SchedEvent* Event;
} Timer;
// hax
extern u32 IME[2];
extern u32 IE[2];
extern u32 IF[2];
extern Timer Timers[8];
extern u16 ExMemCnt[2];
extern u8 ROMSeed0[2*8];
extern u8 ROMSeed1[2*8];
extern u8 ARM9BIOS[0x1000];
extern u8 ARM7BIOS[0x4000];
bool Init();
void DeInit();
void Reset();
void SetupDirectBoot();
void RunFrame();
void PressKey(u32 key);
void ReleaseKey(u32 key);
void TouchScreen(u16 x, u16 y);
void ReleaseScreen();
/*SchedEvent* ScheduleEvent(s32 Delay, void (*Func)(u32), u32 Param);
void CancelEvent(SchedEvent* event);
void RunEvents(s32 cycles);*/
void ScheduleEvent(u32 id, bool periodic, s32 delay, void (*func)(u32), u32 param);
void CancelEvent(u32 id);
// DO NOT CALL FROM ARM7!!
void CompensateARM7();
void debug(u32 p);
void Halt();
void MapSharedWRAM(u8 val);
void SetIRQ(u32 cpu, u32 irq);
void ClearIRQ(u32 cpu, u32 irq);
bool HaltInterrupted(u32 cpu);
void StopCPU(u32 cpu, u32 mask);
void ResumeCPU(u32 cpu, u32 mask);
void CheckDMAs(u32 cpu, u32 mode);
u8 ARM9Read8(u32 addr);
u16 ARM9Read16(u32 addr);
u32 ARM9Read32(u32 addr);
void ARM9Write8(u32 addr, u8 val);
void ARM9Write16(u32 addr, u16 val);
void ARM9Write32(u32 addr, u32 val);
u8 ARM7Read8(u32 addr);
u16 ARM7Read16(u32 addr);
u32 ARM7Read32(u32 addr);
void ARM7Write8(u32 addr, u8 val);
void ARM7Write16(u32 addr, u16 val);
void ARM7Write32(u32 addr, u32 val);
u8 ARM9IORead8(u32 addr);
u16 ARM9IORead16(u32 addr);
u32 ARM9IORead32(u32 addr);
void ARM9IOWrite8(u32 addr, u8 val);
void ARM9IOWrite16(u32 addr, u16 val);
void ARM9IOWrite32(u32 addr, u32 val);
u8 ARM7IORead8(u32 addr);
u16 ARM7IORead16(u32 addr);
u32 ARM7IORead32(u32 addr);
void ARM7IOWrite8(u32 addr, u8 val);
void ARM7IOWrite16(u32 addr, u16 val);
void ARM7IOWrite32(u32 addr, u32 val);
}
#endif // NDS_H

939
src/NDSCart.cpp Normal file
View File

@ -0,0 +1,939 @@
/*
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 "NDSCart.h"
namespace NDSCart_SRAM
{
u8* SRAM;
u32 SRAMLength;
char SRAMPath[256];
void (*WriteFunc)(u8 val, bool islast);
u32 Discover_MemoryType;
u32 Discover_Likeliness;
u8* Discover_Buffer;
u32 Discover_DataPos;
u32 Hold;
u8 CurCmd;
u32 DataPos;
u8 Data;
u8 StatusReg;
u32 Addr;
void Write_Null(u8 val, bool islast);
void Write_EEPROMTiny(u8 val, bool islast);
void Write_EEPROM(u8 val, bool islast);
void Write_Flash(u8 val, bool islast);
void Write_Discover(u8 val, bool islast);
bool Init()
{
SRAM = NULL;
Discover_Buffer = NULL;
return true;
}
void DeInit()
{
if (SRAM) delete[] SRAM;
if (Discover_Buffer) delete[] Discover_Buffer;
}
void Reset()
{
}
void LoadSave(char* path)
{
if (SRAM) delete[] SRAM;
if (Discover_Buffer) delete[] Discover_Buffer;
Discover_Buffer = NULL;
strncpy(SRAMPath, path, 255);
SRAMPath[255] = '\0';
FILE* f = fopen(path, "rb");
if (f)
{
fseek(f, 0, SEEK_END);
SRAMLength = (u32)ftell(f);
SRAM = new u8[SRAMLength];
fseek(f, 0, SEEK_SET);
fread(SRAM, SRAMLength, 1, f);
fclose(f);
switch (SRAMLength)
{
case 512: WriteFunc = Write_EEPROMTiny; break;
case 8192:
case 65536: WriteFunc = Write_EEPROM; break;
case 256*1024:
case 512*1024:
case 1024*1024:
case 8192*1024: WriteFunc = Write_Flash; break;
default:
printf("!! BAD SAVE LENGTH %d\n", SRAMLength);
WriteFunc = Write_Null;
break;
}
}
else
{
SRAMLength = 0;
WriteFunc = Write_Discover;
Discover_MemoryType = 2;
Discover_Likeliness = 0;
Discover_DataPos = 0;
Discover_Buffer = new u8[256*1024];
memset(Discover_Buffer, 0, 256*1024);
}
Hold = 0;
CurCmd = 0;
Data = 0;
StatusReg = 0x00;
}
u8 Read()
{
return Data;
}
void SetMemoryType()
{
switch (Discover_MemoryType)
{
case 1:
printf("Save memory type: EEPROM 4k\n");
WriteFunc = Write_EEPROMTiny;
SRAMLength = 512;
break;
case 2:
printf("Save memory type: EEPROM 64k\n");
WriteFunc = Write_EEPROM;
SRAMLength = 8192;
break;
case 3:
printf("Save memory type: EEPROM 512k\n");
WriteFunc = Write_EEPROM;
SRAMLength = 65536;
break;
case 4:
printf("Save memory type: Flash. Hope the size is 256K.\n");
WriteFunc = Write_Flash;
SRAMLength = 256*1024;
break;
case 5:
printf("Save memory type: ...something else\n");
WriteFunc = Write_Null;
SRAMLength = 0;
break;
}
if (!SRAMLength)
return;
SRAM = new u8[SRAMLength];
// replay writes that occured during discovery
u8 prev_cmd = CurCmd;
u32 pos = 0;
while (pos < 256*1024)
{
u32 len = *(u32*)&Discover_Buffer[pos];
pos += 4;
if (len == 0) break;
CurCmd = Discover_Buffer[pos++];
DataPos = 0;
Addr = 0;
Data = 0;
for (u32 i = 1; i < len; i++)
{
WriteFunc(Discover_Buffer[pos++], (i==(len-1)));
DataPos++;
}
}
CurCmd = prev_cmd;
delete[] Discover_Buffer;
}
void Write_Discover(u8 val, bool islast)
{
// attempt at autodetecting the type of save memory.
// we basically hope the game will be nice and clear whole pages of memory.
if (CurCmd == 0x03 || CurCmd == 0x0B)
{
if (Discover_Likeliness)
{
// apply. and pray.
SetMemoryType();
DataPos = 0;
Addr = 0;
Data = 0;
return WriteFunc(val, islast);
}
else
{
Data = 0;
return;
}
}
if (CurCmd == 0x02 || CurCmd == 0x0A)
{
if (DataPos == 0)
Discover_Buffer[Discover_DataPos + 4] = CurCmd;
Discover_Buffer[Discover_DataPos + 5 + DataPos] = val;
if (islast)
{
u32 len = DataPos+1;
*(u32*)&Discover_Buffer[Discover_DataPos] = len+1;
Discover_DataPos += 5+len;
if (Discover_Likeliness <= len)
{
Discover_Likeliness = len;
if (len > 3+256) // bigger Flash, FRAM, whatever
{
Discover_MemoryType = 5;
}
else if (len > 2+128) // Flash
{
Discover_MemoryType = 4;
}
else if (len > 2+32) // EEPROM 512k
{
Discover_MemoryType = 3;
}
else if (len > 1+16 || (len != 1+16 && CurCmd != 0x0A)) // EEPROM 64k
{
Discover_MemoryType = 2;
}
else // EEPROM 4k
{
Discover_MemoryType = 1;
}
}
printf("discover: type=%d likeliness=%d\n", Discover_MemoryType, Discover_Likeliness);
}
}
}
void Write_Null(u8 val, bool islast) {}
void Write_EEPROMTiny(u8 val, bool islast)
{
// TODO
}
void Write_EEPROM(u8 val, bool islast)
{
switch (CurCmd)
{
case 0x02:
if (DataPos < 2)
{
Addr <<= 8;
Addr |= val;
Data = 0;
}
else
{
SRAM[Addr & (SRAMLength-1)] = val;
Addr++;
}
break;
case 0x03:
if (DataPos < 2)
{
Addr <<= 8;
Addr |= val;
Data = 0;
}
else
{
Data = SRAM[Addr & (SRAMLength-1)];
Addr++;
}
break;
case 0x9F:
Data = 0xFF;
break;
default:
if (DataPos==0)
printf("unknown EEPROM save command %02X\n", CurCmd);
break;
}
}
void Write_Flash(u8 val, bool islast)
{
switch (CurCmd)
{
case 0x03:
if (DataPos < 3)
{
Addr <<= 8;
Addr |= val;
Data = 0;
}
else
{
// CHECKME: does Flash also wraparound when the address is out of bounds?
if (Addr >= SRAMLength)
Data = 0;
else
Data = SRAM[Addr];
Addr++;
}
break;
case 0x0A:
if (DataPos < 3)
{
Addr <<= 8;
Addr |= val;
Data = 0;
}
else
{
if (Addr < SRAMLength)
SRAM[Addr] = val;
Addr++;
}
break;
case 0x9F:
Data = 0xFF;
break;
default:
if (DataPos==0)
printf("unknown Flash save command %02X\n", CurCmd);
break;
}
}
void Write(u8 val, u32 hold)
{
bool islast = false;
if (!hold)
{
if (Hold) islast = true;
Hold = 0;
}
if (hold && (!Hold))
{
CurCmd = val;
Hold = 1;
Data = 0;
DataPos = 0;
Addr = 0;
//printf("save SPI command %02X\n", CurCmd);
return;
}
switch (CurCmd)
{
case 0x02:
case 0x03:
case 0x0A:
case 0x0B:
case 0x9F:
WriteFunc(val, islast);
DataPos++;
break;
case 0x04: // write disable
StatusReg &= ~(1<<1);
Data = 0;
break;
case 0x05: // read status reg
Data = StatusReg;
break;
case 0x06: // write enable
StatusReg |= (1<<1);
Data = 0;
break;
default:
if (DataPos==0)
printf("unknown save SPI command %02X\n", CurCmd);
break;
}
if (islast && (CurCmd == 0x02 || CurCmd == 0x0A))
{
FILE* f = fopen(SRAMPath, "wb");
if (f)
{
fwrite(SRAM, SRAMLength, 1, f);
fclose(f);
}
}
}
}
namespace NDSCart
{
u16 SPICnt;
u32 ROMCnt;
u8 ROMCommand[8];
u32 ROMDataOut;
u8 DataOut[0x4000];
u32 DataOutPos;
u32 DataOutLen;
bool CartInserted;
u8* CartROM;
u32 CartROMSize;
u32 CartID;
bool CartIsHomebrew;
u32 CmdEncMode;
u32 DataEncMode;
u32 Key1_KeyBuf[0x412];
u64 Key2_X;
u64 Key2_Y;
u32 ByteSwap(u32 val)
{
return (val >> 24) | ((val >> 8) & 0xFF00) | ((val << 8) & 0xFF0000) | (val << 24);
}
void Key1_Encrypt(u32* data)
{
u32 y = data[0];
u32 x = data[1];
u32 z;
for (u32 i = 0x0; i <= 0xF; i++)
{
z = Key1_KeyBuf[i] ^ x;
x = Key1_KeyBuf[0x012 + (z >> 24) ];
x += Key1_KeyBuf[0x112 + ((z >> 16) & 0xFF)];
x ^= Key1_KeyBuf[0x212 + ((z >> 8) & 0xFF)];
x += Key1_KeyBuf[0x312 + (z & 0xFF)];
x ^= y;
y = z;
}
data[0] = x ^ Key1_KeyBuf[0x10];
data[1] = y ^ Key1_KeyBuf[0x11];
}
void Key1_Decrypt(u32* data)
{
u32 y = data[0];
u32 x = data[1];
u32 z;
for (u32 i = 0x11; i >= 0x2; i--)
{
z = Key1_KeyBuf[i] ^ x;
x = Key1_KeyBuf[0x012 + (z >> 24) ];
x += Key1_KeyBuf[0x112 + ((z >> 16) & 0xFF)];
x ^= Key1_KeyBuf[0x212 + ((z >> 8) & 0xFF)];
x += Key1_KeyBuf[0x312 + (z & 0xFF)];
x ^= y;
y = z;
}
data[0] = x ^ Key1_KeyBuf[0x1];
data[1] = y ^ Key1_KeyBuf[0x0];
}
void Key1_ApplyKeycode(u32* keycode, u32 mod)
{
Key1_Encrypt(&keycode[1]);
Key1_Encrypt(&keycode[0]);
u32 temp[2] = {0,0};
for (u32 i = 0; i <= 0x11; i++)
{
Key1_KeyBuf[i] ^= ByteSwap(keycode[i % mod]);
}
for (u32 i = 0; i <= 0x410; i+=2)
{
Key1_Encrypt(temp);
Key1_KeyBuf[i ] = temp[1];
Key1_KeyBuf[i+1] = temp[0];
}
}
void Key1_InitKeycode(u32 idcode, u32 level, u32 mod)
{
memcpy(Key1_KeyBuf, &NDS::ARM7BIOS[0x30], 0x1048); // hax
u32 keycode[3] = {idcode, idcode>>1, idcode<<1};
if (level >= 1) Key1_ApplyKeycode(keycode, mod);
if (level >= 2) Key1_ApplyKeycode(keycode, mod);
if (level >= 3)
{
keycode[1] <<= 1;
keycode[2] >>= 1;
Key1_ApplyKeycode(keycode, mod);
}
}
void Key2_Encrypt(u8* data, u32 len)
{
for (u32 i = 0; i < len; i++)
{
Key2_X = (((Key2_X >> 5) ^
(Key2_X >> 17) ^
(Key2_X >> 18) ^
(Key2_X >> 31)) & 0xFF)
+ (Key2_X << 8);
Key2_Y = (((Key2_Y >> 5) ^
(Key2_Y >> 23) ^
(Key2_Y >> 18) ^
(Key2_Y >> 31)) & 0xFF)
+ (Key2_Y << 8);
Key2_X &= 0x0000007FFFFFFFFFULL;
Key2_Y &= 0x0000007FFFFFFFFFULL;
}
}
bool Init()
{
if (!NDSCart_SRAM::Init()) return false;
return true;
}
void DeInit()
{
NDSCart_SRAM::DeInit();
}
void Reset()
{
SPICnt = 0;
ROMCnt = 0;
memset(ROMCommand, 0, 8);
ROMDataOut = 0;
Key2_X = 0;
Key2_Y = 0;
memset(DataOut, 0, 0x4000);
DataOutPos = 0;
DataOutLen = 0;
CartInserted = false;
CartROM = NULL;
CartROMSize = 0;
CartID = 0;
CartIsHomebrew = false;
CmdEncMode = 0;
DataEncMode = 0;
NDSCart_SRAM::Reset();
}
bool LoadROM(char* path)
{
// TODO: streaming mode? for really big ROMs or systems with limited RAM
// for now we're lazy
FILE* f = fopen(path, "rb");
if (!f)
{
printf("Failed to open ROM file %s\n", path);
return false;
}
fseek(f, 0, SEEK_END);
u32 len = (u32)ftell(f);
CartROMSize = 0x200;
while (CartROMSize < len)
CartROMSize <<= 1;
u32 gamecode;
fseek(f, 0x0C, SEEK_SET);
fread(&gamecode, 4, 1, f);
CartROM = new u8[CartROMSize];
memset(CartROM, 0, CartROMSize);
fseek(f, 0, SEEK_SET);
fread(CartROM, 1, len, f);
fclose(f);
//CartROM = f;
// temp. TODO: later make this user selectable
// calling this sets up shit for booting from the cart directly.
// normal behavior is booting from the BIOS.
NDS::SetupDirectBoot();
CartInserted = true;
// generate a ROM ID
// note: most games don't check the actual value
// it just has to stay the same throughout gameplay
CartID = 0x00001FC2;
u32 arm9base = *(u32*)&CartROM[0x20];
if (arm9base < 0x8000)
{
if (arm9base >= 0x4000)
{
// reencrypt secure area if needed
if (*(u32*)&CartROM[arm9base] == 0xE7FFDEFF)
{
printf("Re-encrypting cart secure area\n");
strncpy((char*)&CartROM[arm9base], "encryObj", 8);
Key1_InitKeycode(gamecode, 3, 2);
for (u32 i = 0; i < 0x800; i += 8)
Key1_Encrypt((u32*)&CartROM[arm9base + i]);
Key1_InitKeycode(gamecode, 2, 2);
Key1_Encrypt((u32*)&CartROM[arm9base]);
}
}
else
CartIsHomebrew = true;
}
// encryption
Key1_InitKeycode(gamecode, 2, 2);
// save
char savepath[256];
strncpy(savepath, path, 255);
savepath[255] = '\0';
strncpy(savepath + strlen(path) - 3, "sav", 3);
printf("Save file: %s\n", savepath);
NDSCart_SRAM::LoadSave(savepath);
return true;
}
void ReadROM(u32 addr, u32 len, u32 offset)
{
if (!CartInserted) return;
if (addr >= CartROMSize) return;
if ((addr+len) > CartROMSize)
len = CartROMSize - addr;
memcpy(DataOut+offset, CartROM+addr, len);
}
void ReadROM_B7(u32 addr, u32 len, u32 offset)
{
addr &= (CartROMSize-1);
if (!CartIsHomebrew)
{
if (addr < 0x8000)
addr = 0x8000 + (addr & 0x1FF);
}
memcpy(DataOut+offset, CartROM+addr, len);
}
void EndTransfer()
{
ROMCnt &= ~(1<<23);
ROMCnt &= ~(1<<31);
if (SPICnt & (1<<14))
NDS::SetIRQ((NDS::ExMemCnt[0]>>11)&0x1, NDS::IRQ_CartSendDone);
}
void ROMPrepareData(u32 param)
{
if (DataOutPos >= DataOutLen)
ROMDataOut = 0;
else
ROMDataOut = *(u32*)&DataOut[DataOutPos];
DataOutPos += 4;
ROMCnt |= (1<<23);
NDS::CheckDMAs(0, 0x06);
NDS::CheckDMAs(1, 0x12);
//if (DataOutPos < DataOutLen)
// NDS::ScheduleEvent((ROMCnt & (1<<27)) ? 8:5, ROMPrepareData, 0);
}
void WriteROMCnt(u32 val)
{
ROMCnt = val & 0xFF7F7FFF;
if (!(SPICnt & (1<<15))) return;
if (val & (1<<15))
{
u32 snum = (NDS::ExMemCnt[0]>>8)&0x8;
u64 seed0 = *(u32*)&NDS::ROMSeed0[snum] | ((u64)NDS::ROMSeed0[snum+4] << 32);
u64 seed1 = *(u32*)&NDS::ROMSeed1[snum] | ((u64)NDS::ROMSeed1[snum+4] << 32);
Key2_X = 0;
Key2_Y = 0;
for (u32 i = 0; i < 39; i++)
{
if (seed0 & (1ULL << i)) Key2_X |= (1ULL << (38-i));
if (seed1 & (1ULL << i)) Key2_Y |= (1ULL << (38-i));
}
printf("seed0: %02X%08X\n", (u32)(seed0>>32), (u32)seed0);
printf("seed1: %02X%08X\n", (u32)(seed1>>32), (u32)seed1);
printf("key2 X: %02X%08X\n", (u32)(Key2_X>>32), (u32)Key2_X);
printf("key2 Y: %02X%08X\n", (u32)(Key2_Y>>32), (u32)Key2_Y);
}
if (!(ROMCnt & (1<<31))) return;
u32 datasize = (ROMCnt >> 24) & 0x7;
if (datasize == 7)
datasize = 4;
else if (datasize > 0)
datasize = 0x100 << datasize;
DataOutPos = 0;
DataOutLen = datasize;
// handle KEY1 encryption as needed.
// KEY2 encryption is implemented in hardware and doesn't need to be handled.
u8 cmd[8];
if (CmdEncMode == 1)
{
*(u32*)&cmd[0] = ByteSwap(*(u32*)&ROMCommand[4]);
*(u32*)&cmd[4] = ByteSwap(*(u32*)&ROMCommand[0]);
Key1_Decrypt((u32*)cmd);
u32 tmp = ByteSwap(*(u32*)&cmd[4]);
*(u32*)&cmd[4] = ByteSwap(*(u32*)&cmd[0]);
*(u32*)&cmd[0] = tmp;
}
else
{
*(u32*)&cmd[0] = *(u32*)&ROMCommand[0];
*(u32*)&cmd[4] = *(u32*)&ROMCommand[4];
}
/*printf("ROM COMMAND %04X %08X %02X%02X%02X%02X%02X%02X%02X%02X SIZE %04X\n",
SPICnt, ROMCnt,
cmd[0], cmd[1], cmd[2], cmd[3],
cmd[4], cmd[5], cmd[6], cmd[7],
datasize);*/
switch (cmd[0])
{
case 0x9F:
memset(DataOut, 0xFF, DataOutLen);
break;
case 0x00:
memset(DataOut, 0, DataOutLen);
if (DataOutLen > 0x1000)
{
ReadROM(0, 0x1000, 0);
for (u32 pos = 0x1000; pos < DataOutLen; pos += 0x1000)
memcpy(DataOut+pos, DataOut, 0x1000);
}
else
ReadROM(0, DataOutLen, 0);
break;
case 0x90:
case 0xB8:
for (u32 pos = 0; pos < DataOutLen; pos += 4)
*(u32*)&DataOut[pos] = CartID;
break;
case 0x3C:
CmdEncMode = 1;
break;
case 0xB7:
{
u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
memset(DataOut, 0, DataOutLen);
if (((addr + DataOutLen - 1) >> 12) != (addr >> 12))
{
u32 len1 = 0x1000 - (addr & 0xFFF);
ReadROM_B7(addr, len1, 0);
ReadROM_B7(addr+len1, DataOutLen-len1, len1);
}
else
ReadROM_B7(addr, DataOutLen, 0);
}
break;
default:
switch (cmd[0] & 0xF0)
{
case 0x40:
DataEncMode = 2;
break;
case 0x10:
for (u32 pos = 0; pos < DataOutLen; pos += 4)
*(u32*)&DataOut[pos] = CartID;
break;
case 0x20:
{
u32 addr = (cmd[2] & 0xF0) << 8;
ReadROM(addr, 0x1000, 0);
}
break;
case 0xA0:
CmdEncMode = 2;
break;
}
break;
}
//ROMCnt &= ~(1<<23);
ROMCnt |= (1<<23);
if (datasize == 0)
EndTransfer();
else
{
NDS::CheckDMAs(0, 0x05);
NDS::CheckDMAs(1, 0x12);
}
//NDS::ScheduleEvent((ROMCnt & (1<<27)) ? 8:5, ROMPrepareData, 0);
}
u32 ReadROMData()
{
/*if (ROMCnt & (1<<23))
{
ROMCnt &= ~(1<<23);
if (DataOutPos >= DataOutLen)
EndTransfer();
}
return ROMDataOut;*/
u32 ret;
if (DataOutPos >= DataOutLen)
ret = 0;
else
ret = *(u32*)&DataOut[DataOutPos];
DataOutPos += 4;
if (DataOutPos == DataOutLen)
EndTransfer();
return ret;
}
void DMA(u32 addr)
{
void (*writefn)(u32,u32) = (NDS::ExMemCnt[0] & (1<<11)) ? NDS::ARM7Write32 : NDS::ARM9Write32;
for (u32 i = 0; i < DataOutLen; i+=4)
{
writefn(addr+i, *(u32*)&DataOut[i]);
}
EndTransfer();
}
void WriteSPICnt(u16 val)
{
SPICnt = (SPICnt & 0x0080) | (val & 0xE043);
}
u8 ReadSPIData()
{
if (!(SPICnt & (1<<15))) return 0;
if (!(SPICnt & (1<<13))) return 0;
return NDSCart_SRAM::Read();
}
void WriteSPIData(u8 val)
{
if (!(SPICnt & (1<<15))) return;
if (!(SPICnt & (1<<13))) return;
// TODO: take delays into account
NDSCart_SRAM::Write(val, SPICnt&(1<<6));
}
}

55
src/NDSCart.h Normal file
View File

@ -0,0 +1,55 @@
/*
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/.
*/
#ifndef NDSCART_H
#define NDSCART_H
#include "types.h"
namespace NDSCart
{
extern u16 SPICnt;
extern u32 ROMCnt;
extern u8 ROMCommand[8];
extern u32 ROMDataOut;
extern u8 EncSeed0[5];
extern u8 EncSeed1[5];
extern u8* CartROM;
extern u32 CartROMSize;
bool Init();
void DeInit();
void Reset();
bool LoadROM(char* path);
void WriteROMCnt(u32 val);
u32 ReadROMData();
void DMA(u32 addr);
void WriteSPICnt(u16 val);
u8 ReadSPIData();
void WriteSPIData(u8 val);
}
#endif

255
src/RTC.cpp Normal file
View File

@ -0,0 +1,255 @@
/*
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 "RTC.h"
namespace RTC
{
u16 IO;
u8 Input;
u32 InputBit;
u32 InputPos;
u8 Output[8];
u32 OutputBit;
u32 OutputPos;
u8 CurCmd;
u8 StatusReg1;
u8 StatusReg2;
u8 Alarm1[3];
u8 Alarm2[3];
u8 ClockAdjust;
u8 FreeReg;
bool Init()
{
return true;
}
void DeInit()
{
}
void Reset()
{
Input = 0;
InputBit = 0;
InputPos = 0;
memset(Output, 0, sizeof(Output));
OutputPos = 0;
CurCmd = 0;
StatusReg1 = 0;
StatusReg2 = 0;
memset(Alarm1, 0, sizeof(Alarm1));
memset(Alarm2, 0, sizeof(Alarm2));
ClockAdjust = 0;
FreeReg = 0;
}
void ByteIn(u8 val)
{
//printf("RTC IN: %02X\n", val);
if (InputPos == 0)
{
if ((val & 0xF0) == 0x60)
{
u8 rev[16] = {0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6};
CurCmd = rev[val & 0xF];
}
else
CurCmd = val;
if (CurCmd & 0x80)
{
switch (CurCmd & 0x70)
{
case 0x00: Output[0] = StatusReg1; break;
case 0x40: Output[0] = StatusReg2; break;
case 0x20:
// TODO: get actual system time
Output[0] = 0x17;
Output[1] = 0x01;
Output[2] = 0x19;
Output[3] = 0x04; // day of week. checkme. apparently 04=Thursday
Output[4] = 0x06;
Output[5] = 0x30;
Output[6] = 0x30;
break;
case 0x60:
// TODO: get actual system time
Output[0] = 0x06;
Output[1] = 0x30;
Output[2] = 0x30;
break;
case 0x10:
if (StatusReg2 & 0x04)
{
Output[0] = Alarm1[0];
Output[1] = Alarm1[1];
Output[2] = Alarm1[2];
}
else
Output[0] = Alarm1[2];
break;
case 0x50:
Output[0] = Alarm2[0];
Output[1] = Alarm2[1];
Output[2] = Alarm2[2];
break;
case 0x30: Output[0] = ClockAdjust; break;
case 0x70: Output[0] = FreeReg; break;
}
}
InputPos++;
return;
}
switch (CurCmd & 0x70)
{
case 0x00:
if (InputPos == 1) StatusReg1 = val & 0x0E;
break;
case 0x40:
if (InputPos == 1) StatusReg2 = val;
if (StatusReg2 & 0x4F) printf("RTC INTERRUPT ON: %02X\n", StatusReg2);
break;
case 0x20:
// TODO: set time somehow??
break;
case 0x60:
// same shit
break;
case 0x10:
if (StatusReg2 & 0x04)
{
if (InputPos <= 3) Alarm1[InputPos-1] = val;
}
else
{
if (InputPos == 1) Alarm1[2] = val;
}
break;
case 0x50:
if (InputPos <= 3) Alarm2[InputPos-1] = val;
break;
case 0x30:
if (InputPos == 1) ClockAdjust = val;
break;
case 0x70:
if (InputPos == 1) FreeReg = val;
break;
}
InputPos++;
}
u16 Read()
{
//printf("RTC READ %04X\n", IO);
return IO;
}
void Write(u16 val, bool byte)
{
if (byte) val |= (IO & 0xFF00);
//printf("RTC WRITE %04X\n", val);
if (val & 0x0004)
{
if (!(IO & 0x0004))
{
// start transfer
Input = 0;
InputBit = 0;
InputPos = 0;
memset(Output, 0, sizeof(Output));
OutputBit = 0;
OutputPos = 0;
}
else
{
if (!(val & 0x0002)) // clock low
{
if (val & 0x0010)
{
// write
if (val & 0x0001)
Input |= (1<<InputBit);
InputBit++;
if (InputBit >= 8)
{
InputBit = 0;
ByteIn(Input);
Input = 0;
InputPos++;
}
}
else
{
// read
if (Output[OutputPos] & (1<<OutputBit))
IO |= 0x0001;
else
IO &= 0xFFFE;
OutputBit++;
if (OutputBit >= 8)
{
OutputBit = 0;
if (OutputPos < 7)
OutputPos++;
}
}
}
}
}
if (val & 0x0010)
IO = val;
else
IO = (IO & 0x0001) | (val & 0xFFFE);
}
}

36
src/RTC.h Normal file
View File

@ -0,0 +1,36 @@
/*
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/.
*/
#ifndef RTC_H
#define RTC_H
#include "types.h"
namespace RTC
{
bool Init();
void DeInit();
void Reset();
u16 Read();
void Write(u16 val, bool byte);
}
#endif

457
src/SPI.cpp Normal file
View File

@ -0,0 +1,457 @@
/*
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 "SPI.h"
namespace SPI_Firmware
{
u8* Firmware;
u32 FirmwareLength;
u32 Hold;
u8 CurCmd;
u32 DataPos;
u8 Data;
u8 StatusReg;
u32 Addr;
u16 CRC16(u8* data, u32 len, u32 start)
{
u16 blarg[8] = {0xC0C1, 0xC181, 0xC301, 0xC601, 0xCC01, 0xD801, 0xF001, 0xA001};
for (u32 i = 0; i < len; i++)
{
start ^= data[i];
for (int j = 0; j < 8; j++)
{
if (start & 0x1)
{
start >>= 1;
start ^= (blarg[j] << (7-j));
}
else
start >>= 1;
}
}
return start & 0xFFFF;
}
bool VerifyCRC16(u32 start, u32 offset, u32 len, u32 crcoffset)
{
u16 crc_stored = *(u16*)&Firmware[crcoffset];
u16 crc_calced = CRC16(&Firmware[offset], len, start);
//printf("%04X vs %04X\n", crc_stored, crc_calced);
return (crc_stored == crc_calced);
}
bool Init()
{
Firmware = NULL;
return true;
}
void DeInit()
{
if (Firmware) delete[] Firmware;
}
void Reset()
{
if (Firmware) delete[] Firmware;
Firmware = NULL;
FILE* f = fopen("firmware.bin", "rb");
if (!f)
{
printf("firmware.bin not found\n");
// TODO: generate default firmware
return;
}
fseek(f, 0, SEEK_END);
FirmwareLength = (u32)ftell(f);
Firmware = new u8[FirmwareLength];
fseek(f, 0, SEEK_SET);
fread(Firmware, FirmwareLength, 1, f);
fclose(f);
u32 userdata = 0x3FE00;
if (*(u16*)&Firmware[0x3FF70] == ((*(u16*)&Firmware[0x3FE70] + 1) & 0x7F))
{
if (VerifyCRC16(0xFFFF, 0x3FF00, 0x70, 0x3FF72))
userdata = 0x3FF00;
}
// fix touchscreen coords
*(u16*)&Firmware[userdata+0x58] = 0;
*(u16*)&Firmware[userdata+0x5A] = 0;
Firmware[userdata+0x5C] = 1;
Firmware[userdata+0x5D] = 1;
*(u16*)&Firmware[userdata+0x5E] = 254<<4;
*(u16*)&Firmware[userdata+0x60] = 190<<4;
Firmware[userdata+0x62] = 255;
Firmware[userdata+0x63] = 191;
// disable autoboot
//Firmware[userdata+0x64] &= 0xBF;
*(u16*)&Firmware[userdata+0x72] = CRC16(&Firmware[userdata], 0x70, 0xFFFF);
// verify shit
printf("FW: WIFI CRC16 = %s\n", VerifyCRC16(0x0000, 0x2C, *(u16*)&Firmware[0x2C], 0x2A)?"GOOD":"BAD");
printf("FW: AP1 CRC16 = %s\n", VerifyCRC16(0x0000, 0x3FA00, 0xFE, 0x3FAFE)?"GOOD":"BAD");
printf("FW: AP2 CRC16 = %s\n", VerifyCRC16(0x0000, 0x3FB00, 0xFE, 0x3FBFE)?"GOOD":"BAD");
printf("FW: AP3 CRC16 = %s\n", VerifyCRC16(0x0000, 0x3FC00, 0xFE, 0x3FCFE)?"GOOD":"BAD");
printf("FW: USER0 CRC16 = %s\n", VerifyCRC16(0xFFFF, 0x3FE00, 0x70, 0x3FE72)?"GOOD":"BAD");
printf("FW: USER1 CRC16 = %s\n", VerifyCRC16(0xFFFF, 0x3FF00, 0x70, 0x3FF72)?"GOOD":"BAD");
Hold = 0;
CurCmd = 0;
Data = 0;
StatusReg = 0x00;
}
u8 Read()
{
return Data;
}
void Write(u8 val, u32 hold)
{
if (!hold)
{
Hold = 0;
}
if (hold && (!Hold))
{
CurCmd = val;
Hold = 1;
Data = 0;
DataPos = 1;
Addr = 0;
//printf("firmware SPI command %02X\n", CurCmd);
return;
}
switch (CurCmd)
{
case 0x03: // read
{
if (DataPos < 4)
{
Addr <<= 8;
Addr |= val;
Data = 0;
//if (DataPos == 3) printf("firmware SPI read %08X\n", Addr);
}
else
{
if (Addr >= FirmwareLength)
Data = 0;
else
Data = Firmware[Addr];
Addr++;
}
DataPos++;
}
break;
case 0x04: // write disable
StatusReg &= ~(1<<1);
Data = 0;
break;
case 0x05: // read status reg
Data = StatusReg;
break;
case 0x06: // write enable
StatusReg |= (1<<1);
Data = 0;
break;
case 0x9F: // read JEDEC ID
{
switch (DataPos)
{
case 1: Data = 0x20; break;
case 2: Data = 0x40; break;
case 3: Data = 0x12; break;
default: Data = 0; break;
}
DataPos++;
}
break;
default:
printf("unknown firmware SPI command %02X\n", CurCmd);
break;
}
}
}
namespace SPI_Powerman
{
u32 Hold;
u32 DataPos;
u8 Index;
u8 Data;
u8 Registers[8];
u8 RegMasks[8];
bool Init()
{
return true;
}
void DeInit()
{
}
void Reset()
{
Hold = 0;
Index = 0;
Data = 0;
memset(Registers, 0, sizeof(Registers));
memset(RegMasks, 0, sizeof(RegMasks));
Registers[4] = 0x40;
RegMasks[0] = 0x7F;
RegMasks[1] = 0x01;
RegMasks[2] = 0x01;
RegMasks[3] = 0x03;
RegMasks[4] = 0x0F;
}
u8 Read()
{
return Data;
}
void Write(u8 val, u32 hold)
{
if (!hold)
{
Hold = 0;
}
if (hold && (!Hold))
{
Index = val;
Hold = 1;
Data = 0;
DataPos = 1;
return;
}
if (DataPos == 1)
{
if (Index & 0x80)
{
Data = Registers[Index & 0x07];
}
else
{
Registers[Index & 0x07] =
(Registers[Index & 0x07] & ~RegMasks[Index & 0x07]) |
(val & RegMasks[Index & 0x07]);
}
}
else
Data = 0;
}
}
namespace SPI_TSC
{
u32 DataPos;
u8 ControlByte;
u8 Data;
u16 ConvResult;
u16 TouchX, TouchY;
bool Init()
{
return true;
}
void DeInit()
{
}
void Reset()
{
ControlByte = 0;
Data = 0;
ConvResult = 0;
}
void SetTouchCoords(u16 x, u16 y)
{
// scr.x = (adc.x-adc.x1) * (scr.x2-scr.x1) / (adc.x2-adc.x1) + (scr.x1-1)
// scr.y = (adc.y-adc.y1) * (scr.y2-scr.y1) / (adc.y2-adc.y1) + (scr.y1-1)
// adc.x = ((scr.x * ((adc.x2-adc.x1) + (scr.x1-1))) / (scr.x2-scr.x1)) + adc.x1
// adc.y = ((scr.y * ((adc.y2-adc.y1) + (scr.y1-1))) / (scr.y2-scr.y1)) + adc.y1
TouchX = x;
TouchY = y;
if (y == 0xFFF) return;
TouchX <<= 4;
TouchY <<= 4;
}
u8 Read()
{
return Data;
}
void Write(u8 val, u32 hold)
{
if (DataPos == 1)
Data = (ConvResult >> 5) & 0xFF;
else if (DataPos == 2)
Data = (ConvResult << 3) & 0xFF;
else
Data = 0;
if (val & 0x80)
{
ControlByte = val;
DataPos = 1;
switch (ControlByte & 0x70)
{
case 0x10: ConvResult = TouchY; break;
case 0x50: ConvResult = TouchX; break;
default: ConvResult = 0xFFF; break;
}
if (ControlByte & 0x08)
ConvResult &= 0x0FF0; // checkme
}
else
DataPos++;
}
}
namespace SPI
{
u16 Cnt;
u32 CurDevice;
bool Init()
{
if (!SPI_Firmware::Init()) return false;
if (!SPI_Powerman::Init()) return false;
if (!SPI_TSC::Init()) return false;
return true;
}
void DeInit()
{
SPI_Firmware::DeInit();
SPI_Powerman::DeInit();
SPI_TSC::DeInit();
}
void Reset()
{
Cnt = 0;
SPI_Firmware::Reset();
SPI_Powerman::Reset();
SPI_TSC::Init();
}
void WriteCnt(u16 val)
{
Cnt = (Cnt & 0x0080) | (val & 0xCF03);
if (val & 0x0400) printf("!! CRAPOED 16BIT SPI MODE\n");
}
u8 ReadData()
{
if (!(Cnt & (1<<15))) return 0;
switch (Cnt & 0x0300)
{
case 0x0000: return SPI_Powerman::Read();
case 0x0100: return SPI_Firmware::Read();
case 0x0200: return SPI_TSC::Read();
default: return 0;
}
}
void WriteData(u8 val)
{
if (!(Cnt & (1<<15))) return;
// TODO: take delays into account
switch (Cnt & 0x0300)
{
case 0x0000: SPI_Powerman::Write(val, Cnt&(1<<11)); break;
case 0x0100: SPI_Firmware::Write(val, Cnt&(1<<11)); break;
case 0x0200: SPI_TSC::Write(val, Cnt&(1<<11)); break;
default: printf("SPI to unknown device %04X %02X\n", Cnt, val); break;
}
if (Cnt & (1<<14))
NDS::SetIRQ(1, NDS::IRQ_SPI);
}
}

46
src/SPI.h Normal file
View File

@ -0,0 +1,46 @@
/*
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/.
*/
#ifndef SPI_H
#define SPI_H
namespace SPI_TSC
{
void SetTouchCoords(u16 x, u16 y);
}
namespace SPI
{
extern u16 Cnt;
bool Init();
void DeInit();
void Reset();
u16 ReadCnt();
void WriteCnt(u16 val);
u8 ReadData();
void WriteData(u8 val);
}
#endif

120
src/Wifi.cpp Normal file
View File

@ -0,0 +1,120 @@
/*
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 "Wifi.h"
namespace Wifi
{
u16 BBCnt;
u8 BBWrite;
u8 BBRegs[0x100];
u8 BBRegsRO[0x100];
void Reset()
{
BBCnt = 0;
BBWrite = 0;
memset(BBRegs, 0, 0x100);
memset(BBRegsRO, 0, 0x100);
#define BBREG_FIXED(id, val) BBRegs[id] = val; BBRegsRO[id] = 1;
BBREG_FIXED(0x00, 0x6D);
BBREG_FIXED(0x0D, 0x00);
BBREG_FIXED(0x0E, 0x00);
BBREG_FIXED(0x0F, 0x00);
BBREG_FIXED(0x10, 0x00);
BBREG_FIXED(0x11, 0x00);
BBREG_FIXED(0x12, 0x00);
BBREG_FIXED(0x16, 0x00);
BBREG_FIXED(0x17, 0x00);
BBREG_FIXED(0x18, 0x00);
BBREG_FIXED(0x19, 0x00);
BBREG_FIXED(0x1A, 0x00);
BBREG_FIXED(0x27, 0x00);
BBREG_FIXED(0x4D, 0x00); // 00 or BF
BBREG_FIXED(0x5D, 0x01);
BBREG_FIXED(0x5E, 0x00);
BBREG_FIXED(0x5F, 0x00);
BBREG_FIXED(0x60, 0x00);
BBREG_FIXED(0x61, 0x00);
BBREG_FIXED(0x64, 0xFF); // FF or 3F
BBREG_FIXED(0x66, 0x00);
for (int i = 0x69; i < 0x100; i++)
{
BBREG_FIXED(i, 0x00);
}
#undef BBREG_FIXED
}
u16 Read(u32 addr)
{
addr &= 0x7FFF;
switch (addr)
{
case 0x158:
return BBCnt;
case 0x15C:
if ((BBCnt & 0xF000) != 0x6000)
{
printf("WIFI: bad BB read, CNT=%04X\n", BBCnt);
return 0;
}
return BBRegs[BBCnt & 0xFF];
case 0x15E:
return 0; // cheap
}
printf("WIFI: unknown read %08X\n", addr);
return 0;
}
void Write(u32 addr, u16 val)
{
addr &= 0x7FFF;
switch (addr)
{
case 0x158:
BBCnt = val;
if ((BBCnt & 0xF000) == 0x5000)
{
u32 regid = BBCnt & 0xFF;
if (!BBRegsRO[regid])
BBRegs[regid] = val & 0xFF;
}
return;
case 0x15A:
BBWrite = val;
return;
}
printf("WIFI: unknown write %08X %04X\n", addr, val);
}
}

35
src/Wifi.h Normal file
View File

@ -0,0 +1,35 @@
/*
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/.
*/
#ifndef WIFI_H
#define WIFI_H
namespace Wifi
{
//
void Reset();
u16 Read(u32 addr);
void Write(u32 addr, u16 val);
}
#endif

272
src/main.cpp Normal file
View File

@ -0,0 +1,272 @@
/*
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 <windows.h>
#include "NDS.h"
#include "GPU.h"
#define VERSION "0.1"
HINSTANCE instance;
HWND melon;
BITMAPV4HEADER bmp;
bool quit;
bool touching;
LRESULT CALLBACK derpo(HWND window, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch (msg)
{
case WM_CLOSE:
printf("close\n");
{
FILE* f = fopen("debug/wram.bin", "wb");
if (f)
{
for (u32 i = 0x37F8000; i < 0x3808000; i+=4)
{
u32 blarg = NDS::ARM7Read32(i);
fwrite(&blarg, 4, 1, f);
}
fclose(f);
}
f = fopen("debug/arm7vram.bin", "wb");
if (f)
{
for (u32 i = 0x6000000; i < 0x6040000; i+=4)
{
u32 blarg = NDS::ARM7Read32(i);
fwrite(&blarg, 4, 1, f);
}
fclose(f);
}
f = fopen("debug/mainram.bin", "wb");
if (f)
{
for (u32 i = 0x2000000; i < 0x2400000; i+=4)
{
u32 blarg = NDS::ARM9Read32(i);
fwrite(&blarg, 4, 1, f);
}
fclose(f);
}
}
PostQuitMessage(0);
return 0;
case WM_KEYDOWN:
switch (wparam)
{
case VK_RETURN: NDS::PressKey(3); break;
case VK_SPACE: NDS::PressKey(2); break;
case VK_UP: NDS::PressKey(6); break;
case VK_DOWN: NDS::PressKey(7); break;
case VK_LEFT: NDS::PressKey(5); break;
case VK_RIGHT: NDS::PressKey(4); break;
case 'A': NDS::PressKey(0); break;
case 'B': NDS::PressKey(1); break;
case 'X': NDS::PressKey(16); break;
case 'Y': NDS::PressKey(17); break;
case 'L': NDS::PressKey(9); break;
case 'R': NDS::PressKey(8); break;
case 'D': NDS::debug(0); break;
}
return 0;
case WM_KEYUP:
switch (wparam)
{
case VK_RETURN: NDS::ReleaseKey(3); break;
case VK_SPACE: NDS::ReleaseKey(2); break;
case VK_UP: NDS::ReleaseKey(6); break;
case VK_DOWN: NDS::ReleaseKey(7); break;
case VK_LEFT: NDS::ReleaseKey(5); break;
case VK_RIGHT: NDS::ReleaseKey(4); break;
case 'A': NDS::ReleaseKey(0); break;
case 'B': NDS::ReleaseKey(1); break;
case 'X': NDS::ReleaseKey(16); break;
case 'Y': NDS::ReleaseKey(17); break;
case 'L': NDS::ReleaseKey(9); break;
case 'R': NDS::ReleaseKey(8); break;
}
return 0;
case WM_LBUTTONDOWN:
if (!touching)
{
s16 x = (s16)(lparam & 0xFFFF);
s16 y = (s16)(lparam >> 16);
y -= 192;
if (x >= 0 && x < 256 && y >= 0 && y < 192)
{
NDS::TouchScreen(x, y);
NDS::PressKey(16+6);
touching = true;
}
}
return 0;
case WM_LBUTTONUP:
case WM_NCLBUTTONUP:
if (touching)
{
NDS::ReleaseScreen();
NDS::ReleaseKey(16+6);
touching = false;
}
return 0;
case WM_MOUSEMOVE:
if (touching)
{
s16 x = (s16)(lparam & 0xFFFF);
s16 y = (s16)(lparam >> 16);
y -= 192;
if (x >= 0 && x < 256 && y >= 0 && y < 192)
NDS::TouchScreen(x, y);
}
return 0;
case WM_PAINT:
{
PAINTSTRUCT partisocialiste;
HDC dc = BeginPaint(window, &partisocialiste);
SetDIBitsToDevice(dc, 0, 0, 256, 384, 0, 0, 0, 384, GPU::Framebuffer, (BITMAPINFO*)&bmp, DIB_RGB_COLORS);
EndPaint(window, &partisocialiste);
}
return 0;
}
return DefWindowProc(window, msg, wparam, lparam);
}
int main()
{
printf("melonDS version uh... 0.1??\n");
printf("it's a DS emulator!!!\n");
printf("http://melonds.kuribo64.net/\n");
quit = false;
touching = false;
instance = GetModuleHandle(NULL);
//SetThreadAffinityMask(GetCurrentThread(), 0x8);
// god this shit sucks
WNDCLASSEX shit;
shit.cbSize = sizeof(shit);
shit.style = CS_HREDRAW | CS_VREDRAW;
shit.lpfnWndProc = derpo;
shit.cbClsExtra = 0;
shit.cbWndExtra = 0;
shit.hInstance = instance;
shit.hIcon = NULL;
shit.hIconSm = NULL;
shit.hCursor = NULL;
shit.hbrBackground = (HBRUSH)(COLOR_WINDOWFRAME+1);
shit.lpszMenuName = NULL;
shit.lpszClassName = "v0ltmeters";
RegisterClassEx(&shit);
RECT rekt;
rekt.left = 0; rekt.top = 0;
rekt.right = 256; rekt.bottom = 384;
AdjustWindowRect(&rekt, WS_OVERLAPPEDWINDOW, FALSE);
melon = CreateWindow("v0ltmeters",
"melonDS " VERSION,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
rekt.right-rekt.left, rekt.bottom-rekt.top,
NULL,
NULL,
instance,
NULL);
ShowWindow(melon, SW_SHOW);
// more sucky shit!
memset(&bmp, 0, sizeof(bmp));
bmp.bV4Size = sizeof(bmp);
bmp.bV4Width = 256;
bmp.bV4Height = -384;
bmp.bV4Planes = 1;
bmp.bV4BitCount = 32;
bmp.bV4V4Compression = BI_RGB|BI_BITFIELDS;
bmp.bV4RedMask = 0x000000FF;
bmp.bV4GreenMask = 0x0000FF00;
bmp.bV4BlueMask = 0x00FF0000;
NDS::Init();
u32 nframes = 0;
u32 lasttick = GetTickCount();
for (;;)
{
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
quit = true;
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if (quit) break;
NDS::RunFrame();
//HDC dc = GetDC(melon);
//SetDIBitsToDevice(dc, 0, 0, 256, 384, 0, 0, 0, 384, GPU::Framebuffer, (BITMAPINFO*)&bmp, DIB_RGB_COLORS);
InvalidateRect(melon, NULL, false);
UpdateWindow(melon);
nframes++;
if (nframes >= 30)
{
u32 tick = GetTickCount();
u32 diff = tick - lasttick;
lasttick = tick;
u32 fps = (nframes * 1000) / diff;
nframes = 0;
char melontitle[100];
sprintf(melontitle, "melonDS " VERSION " | %d FPS", fps);
SetWindowText(melon, melontitle);
}
}
printf("deinit\n");
NDS::DeInit();
return 0;
}

31
src/types.h Normal file
View File

@ -0,0 +1,31 @@
/*
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/.
*/
#ifndef TYPES_H
#define TYPES_H
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long int u64;
typedef signed char s8;
typedef signed short s16;
typedef signed int s32;
typedef signed long long int s64;
#endif // TYPES_H