mirror of
https://github.com/melonDS-emu/melonDS.git
synced 2025-07-24 14:49:53 -06:00
reorganize repo, move shit around
This commit is contained in:
411
src/ARM.cpp
Normal file
411
src/ARM.cpp
Normal 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
234
src/ARM.h
Normal 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
221
src/ARMInterpreter.cpp
Normal 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
35
src/ARMInterpreter.h
Normal 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
1461
src/ARMInterpreter_ALU.cpp
Normal file
File diff suppressed because it is too large
Load Diff
135
src/ARMInterpreter_ALU.h
Normal file
135
src/ARMInterpreter_ALU.h
Normal 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
|
116
src/ARMInterpreter_Branch.cpp
Normal file
116
src/ARMInterpreter_Branch.cpp
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
39
src/ARMInterpreter_Branch.h
Normal file
39
src/ARMInterpreter_Branch.h
Normal 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
|
729
src/ARMInterpreter_LoadStore.cpp
Normal file
729
src/ARMInterpreter_LoadStore.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
95
src/ARMInterpreter_LoadStore.h
Normal file
95
src/ARMInterpreter_LoadStore.h
Normal 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
1979
src/ARM_InstrTable.h
Normal file
File diff suppressed because it is too large
Load Diff
300
src/CP15.cpp
Normal file
300
src/CP15.cpp
Normal 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
44
src/CP15.h
Normal 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
269
src/DMA.cpp
Normal 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
64
src/DMA.h
Normal 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
93
src/FIFO.h
Normal 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
732
src/GPU.cpp
Normal 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
395
src/GPU.h
Normal 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
1604
src/GPU2D.cpp
Normal file
File diff suppressed because it is too large
Load Diff
97
src/GPU2D.h
Normal file
97
src/GPU2D.h
Normal 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
1917
src/GPU3D.cpp
Normal file
File diff suppressed because it is too large
Load Diff
98
src/GPU3D.h
Normal file
98
src/GPU3D.h
Normal 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
853
src/GPU3D_Soft.cpp
Normal 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
2192
src/NDS.cpp
Normal file
File diff suppressed because it is too large
Load Diff
181
src/NDS.h
Normal file
181
src/NDS.h
Normal 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
939
src/NDSCart.cpp
Normal 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
55
src/NDSCart.h
Normal 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
255
src/RTC.cpp
Normal 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
36
src/RTC.h
Normal 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
457
src/SPI.cpp
Normal 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
46
src/SPI.h
Normal 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
120
src/Wifi.cpp
Normal 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
35
src/Wifi.h
Normal 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
272
src/main.cpp
Normal 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
31
src/types.h
Normal 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
|
Reference in New Issue
Block a user