Merge commit '4b57416552ec2fa95216e2b044559f215723bf70' into melonDSi

This commit is contained in:
Arisotura 2020-05-30 03:12:42 +02:00
commit 2327de2423
88 changed files with 2428 additions and 350 deletions

41
.github/workflows/build-ubuntu.yml vendored Normal file
View File

@ -0,0 +1,41 @@
name: CMake Build (Ubuntu x86-64)
on: [push, pull_request]
env:
BUILD_TYPE: Release
CMAKE_VERSION: 3.15.2
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Install dependencies
shell: bash
working-directory: ${{runner.workspace}}
run: | # Fetch a new version of CMake, because the default is too old.
wget -nv https://github.com/Kitware/CMake/releases/download/v$CMAKE_VERSION/cmake-$CMAKE_VERSION-Linux-x86_64.tar.gz \
&& tar -zxf cmake-$CMAKE_VERSION-Linux-x86_64.tar.gz \
&& sudo rm -f /etc/apt/sources.list.d/dotnetdev.list /etc/apt/sources.list.d/microsoft-prod.list \
&& sudo apt-get update \
&& sudo apt-get install gtk+-3.0 libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev
- name: Create build environment
run: mkdir ${{runner.workspace}}/build
- name: Configure
shell: bash
working-directory: ${{runner.workspace}}/build
run: ${{runner.workspace}}/cmake-$CMAKE_VERSION-Linux-x86_64/bin/cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE
- name: Make
shell: bash
working-directory: ${{runner.workspace}}/build
run: |
make -j$(nproc --all) \
&& mkdir dist \
&& cp {melonDS,romlist.bin} dist
- uses: actions/upload-artifact@v1
with:
name: melonDS
path: ${{runner.workspace}}/build/dist

39
.github/workflows/build-windows.yml vendored Normal file
View File

@ -0,0 +1,39 @@
name: CMake Build (Windows x86-64)
on: [push, pull_request]
env:
BUILD_TYPE: Release
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v1
- name: Install MSYS2
working-directory: ${{runner.workspace}}
run: | # Fetch MSYS2 build from XQEmu. Official distribution causes a CI failure due to permission errors.
Invoke-WebRequest -Uri "https://github.com/xqemu/ci-environment-msys2/releases/latest/download/msys64.7z" -OutFile "msys64.7z"
7z x -y msys64.7z "-oC:\tools\"
C:\tools\msys64\usr\bin\bash.exe -lc "pacman -Syuq --noconfirm"
- name: Install dependencies
run: C:\tools\msys64\usr\bin\bash.exe -lc "pacman -Sq --noconfirm git make mingw-w64-x86_64-{cmake,mesa,SDL2,toolchain}"
- name: Create build environment
run: |
New-Item -ItemType directory -Path ${{runner.workspace}}\melonDS\build
Copy-Item -Path ${{runner.workspace}}\melonDS -Destination C:\tools\msys64\home\runneradmin -Recurse
- name: Configure
run: |
C:\tools\msys64\usr\bin\bash.exe -lc "export PATH=`"/mingw64/bin:`$PATH`" \
&& cd melonDS/build && cmake .. -G 'MSYS Makefiles' -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}"
- name: Make
run: |
C:\tools\msys64\usr\bin\bash.exe -lc "export PATH=`"/mingw64/bin:`$PATH`" \
&& cd melonDS/build && make -j$(nproc --all) \
&& ../msys-dist.sh"
- uses: actions/upload-artifact@v1
with:
name: melonDS
path: C:\tools\msys64\home\runneradmin\melonDS\build\dist

View File

@ -1,5 +1,5 @@
---
app-id: net.kuribo64.melonds
app-id: net.kuribo64.melonDS
runtime: org.freedesktop.Platform
runtime-version: '18.08'
sdk: org.freedesktop.Sdk
@ -22,10 +22,8 @@ modules:
buildsystem: cmake-ninja
sources:
- type: git
url: https://github.com/StapleButter/melonDS.git
commit: d4d4965b2fffc69958685a25a9d9fc0c78b54567
- type: file
path: net.kuribo64.melonds.desktop
url: https://github.com/Arisotura/melonDS.git
branch: master
post-install:
- "desktop-file-install --dir=/app/share/applications net.kuribo64.melonds.desktop"
- "install -D icon/melon_256x256.png /app/share/icons/hicolor/256x256/apps/net.kuribo64.melonds.png"
- "desktop-file-install --dir=/app/share/applications net.kuribo64.melonDS.desktop"
- "install -D icon/melon_256x256.png /app/share/icons/hicolor/256x256/apps/net.kuribo64.melonDS.png"

View File

@ -1,8 +0,0 @@
[Desktop Entry]
Name=melonDS
Comment=Nintendo DS emulator
Exec=melonDS
Type=Application
Categories=Game;
Terminal=false
Icon=net.kuribo64.melonds

View File

@ -18,7 +18,7 @@ FILETYPE VFT_APP
VALUE "FileVersion", "0.8.3"
VALUE "FileDescription", "DS emulator, sorta. also 1st quality melon."
VALUE "InternalName", "SDnolem"
VALUE "LegalCopyright", "2016-2019 Arisotura & co."
VALUE "LegalCopyright", "2016-2020 Arisotura & co."
VALUE "LegalTrademarks", ""
VALUE "OriginalFilename", "zafkflzdasd.exe"
VALUE "ProductName", "melonDS"

View File

@ -100,6 +100,10 @@
<Unit filename="melon.rc">
<Option compilerVar="WINDRES" />
</Unit>
<Unit filename="src/ARCodeList.cpp" />
<Unit filename="src/ARCodeList.h" />
<Unit filename="src/AREngine.cpp" />
<Unit filename="src/AREngine.h" />
<Unit filename="src/ARM.cpp" />
<Unit filename="src/ARM.h" />
<Unit filename="src/ARMInterpreter.cpp" />
@ -137,6 +141,8 @@
<Unit filename="src/DSi_SPI_TSC.cpp" />
<Unit filename="src/DSi_SPI_TSC.h" />
<Unit filename="src/FIFO.h" />
<Unit filename="src/GBACart.cpp" />
<Unit filename="src/GBACart.h" />
<Unit filename="src/GPU.cpp" />
<Unit filename="src/GPU.h" />
<Unit filename="src/GPU2D.cpp" />

View File

@ -0,0 +1,11 @@
[Desktop Entry]
Name=melonDS
GenericName=Nintendo DS Emulator
Comment=A fast and accurate Nintendo DS emulator.
Exec=melonDS
Type=Application
Categories=Game;Emulator;
Terminal=false
Icon=net.kuribo64.melonDS
MimeType=application/x-nintendo-ds-rom;
Keywords=emulator;Nintendo;DS;NDS;Nintendo DS;

38
src/ARCodeList.cpp Normal file
View File

@ -0,0 +1,38 @@
/*
Copyright 2016-2020 Arisotura
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 "ARCodeList.h"
/*
Action Replay code list format
header:
00 - magic MLAR
04 - version major
06 - version minor
08 - length
0C - number of codes
code header:
00 - magic MLCD
04 - name length
08 - code length
0C - enable flag
10 - code data (UTF8 name then actual code)
*/

33
src/ARCodeList.h Normal file
View File

@ -0,0 +1,33 @@
/*
Copyright 2016-2020 Arisotura
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 ARCODELIST_H
#define ARCODELIST_H
#include "types.h"
#define ARCL_MAJOR 1
#define ARCL_MINOR 1
class ARCodeList
{
public:
//
};
#endif // ARCODELIST_H

490
src/AREngine.cpp Normal file
View File

@ -0,0 +1,490 @@
/*
Copyright 2016-2020 Arisotura
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 "AREngine.h"
namespace AREngine
{
typedef struct
{
u32 Code[2 * 64]; // TODO: more sensible size for this? allocate on demand?
bool Enabled;
} CheatEntry;
// TODO: more sensible size for this? allocate on demand?
CheatEntry CheatCodes[64];
u32 NumCheatCodes;
void ParseTextCode(char* text, int tlen, u32* code, int clen) // or whatever this should be named?
{
u32 cur_word = 0;
u32 ndigits = 0;
u32 nin = 0;
u32 nout = 0;
char c;
while ((c = *text++) != '\0')
{
u32 val;
if (c >= '0' && c <= '9')
val = c - '0';
else if (c >= 'a' && c <= 'f')
val = c - 'a' + 0xA;
else if (c >= 'A' && c <= 'F')
val = c - 'A' + 0xA;
else
continue;
cur_word <<= 4;
cur_word |= val;
ndigits++;
if (ndigits >= 8)
{
if (nout >= clen)
{
printf("AR: code too long!\n");
return;
}
*code++ = cur_word;
nout++;
ndigits = 0;
cur_word = 0;
}
nin++;
if (nin >= tlen) break;
}
if (nout & 1)
{
printf("AR: code was missing one word\n");
if (nout >= clen)
{
printf("AR: code too long!\n");
return;
}
*code++ = 0;
}
}
bool Init()
{
return true;
}
void DeInit()
{
//
}
void Reset()
{
memset(CheatCodes, 0, sizeof(CheatCodes));
NumCheatCodes = 0;
// TODO: acquire codes from a sensible source!
CheatEntry* entry = &CheatCodes[0];
u32* ptr = &entry->Code[0];
/*char* test = R"(9209D09A 00000000
6209B468 00000000
B209B468 00000000
10000672 000003FF
D2000000 00000000
9209D09A 00000000
94000130 FCBF0000
6209B468 00000000
B209B468 00000000
200006B3 00000001
200006B4 00000001
D2000000 00000000
9209D09A 00000000
94000130 FC7F0000
6209B468 00000000
B209B468 00000000
10000672 00000000
D2000000 00000000)";
ParseTextCode(test, entry->Code, 2*64);
printf("PARSED CODE:\n");
for (int i = 0; i < 2*64; i+=2)
{
printf("%08X %08X\n", entry->Code[i], entry->Code[i+1]);
}
entry->Enabled = true;
NumCheatCodes++;*/
}
#define case16(x) \
case ((x)+0x00): case ((x)+0x01): case ((x)+0x02): case ((x)+0x03): \
case ((x)+0x04): case ((x)+0x05): case ((x)+0x06): case ((x)+0x07): \
case ((x)+0x08): case ((x)+0x09): case ((x)+0x0A): case ((x)+0x0B): \
case ((x)+0x0C): case ((x)+0x0D): case ((x)+0x0E): case ((x)+0x0F)
void RunCheat(CheatEntry* entry)
{
u32* code = &entry->Code[0];
u32 offset = 0;
u32 datareg = 0;
u32 cond = 1;
u32 condstack = 0;
u32* loopstart = code;
u32 loopcount = 0;
u32 loopcond = 1;
u32 loopcondstack = 0;
// TODO: does anything reset this??
u32 c5count = 0;
for (;;)
{
u32 a = *code++;
u32 b = *code++;
if ((a|b) == 0) break;
u8 op = a >> 24;
if ((op < 0xD0 && op != 0xC5) || op > 0xD2)
{
if (!cond)
{
if ((op & 0xF0) == 0xE0)
{
for (u32 i = 0; i < b; i += 8)
*code += 2;
}
continue;
}
}
switch (op)
{
case16(0x00): // 32-bit write
NDS::ARM7Write32((a & 0x0FFFFFFF) + offset, b);
break;
case16(0x10): // 16-bit write
NDS::ARM7Write16((a & 0x0FFFFFFF) + offset, b & 0xFFFF);
break;
case16(0x20): // 8-bit write
NDS::ARM7Write8((a & 0x0FFFFFFF) + offset, b & 0xFF);
break;
case16(0x30): // IF b > u32[a]
{
condstack <<= 1;
condstack |= cond;
u32 chk = NDS::ARM7Read32(a & 0x0FFFFFFF);
cond = (b > chk) ? 1:0;
}
break;
case16(0x40): // IF b < u32[a]
{
condstack <<= 1;
condstack |= cond;
u32 chk = NDS::ARM7Read32(a & 0x0FFFFFFF);
cond = (b < chk) ? 1:0;
}
break;
case16(0x50): // IF b == u32[a]
{
condstack <<= 1;
condstack |= cond;
u32 chk = NDS::ARM7Read32(a & 0x0FFFFFFF);
cond = (b == chk) ? 1:0;
}
break;
case16(0x60): // IF b != u32[a]
{
condstack <<= 1;
condstack |= cond;
u32 chk = NDS::ARM7Read32(a & 0x0FFFFFFF);
cond = (b != chk) ? 1:0;
}
break;
case16(0x70): // IF b.l > ((~b.h) & u16[a])
{
condstack <<= 1;
condstack |= cond;
u16 val = NDS::ARM7Read16(a & 0x0FFFFFFF);
u16 chk = ~(b >> 16);
chk &= val;
cond = ((b & 0xFFFF) > chk) ? 1:0;
}
break;
case16(0x80): // IF b.l < ((~b.h) & u16[a])
{
condstack <<= 1;
condstack |= cond;
u16 val = NDS::ARM7Read16(a & 0x0FFFFFFF);
u16 chk = ~(b >> 16);
chk &= val;
cond = ((b & 0xFFFF) < chk) ? 1:0;
}
break;
case16(0x90): // IF b.l == ((~b.h) & u16[a])
{
condstack <<= 1;
condstack |= cond;
u16 val = NDS::ARM7Read16(a & 0x0FFFFFFF);
u16 chk = ~(b >> 16);
chk &= val;
cond = ((b & 0xFFFF) == chk) ? 1:0;
}
break;
case16(0xA0): // IF b.l != ((~b.h) & u16[a])
{
condstack <<= 1;
condstack |= cond;
u16 val = NDS::ARM7Read16(a & 0x0FFFFFFF);
u16 chk = ~(b >> 16);
chk &= val;
cond = ((b & 0xFFFF) != chk) ? 1:0;
}
break;
case16(0xB0): // offset = u32[a + offset]
offset = NDS::ARM7Read32((a & 0x0FFFFFFF) + offset);
break;
case 0xC0: // FOR 0..b
loopstart = code; // points to the first opcode after the FOR
loopcount = b;
loopcond = cond; // checkme
loopcondstack = condstack; // (GBAtek is not very clear there)
break;
case 0xC4: // offset = pointer to C4000000 opcode
// theoretically used for safe storage, by accessing [offset+4]
// in practice could be used for a self-modifying AR code
// could be implemented with some hackery, but, does anything even
// use it??
printf("AR: !! THE FUCKING C4000000 OPCODE. TELL ARISOTURA.\n");
return;
case 0xC5: // count++ / IF (count & b.l) == b.h
{
// with weird condition checking, apparently
// oh well
c5count++;
if (!cond) break;
condstack <<= 1;
condstack |= cond;
u16 mask = b & 0xFFFF;
u16 chk = b >> 16;
cond = ((c5count & mask) == chk) ? 1:0;
}
break;
case 0xC6: // u32[b] = offset
NDS::ARM7Write32(b, offset);
break;
case 0xD0: // ENDIF
cond = condstack & 0x1;
condstack >>= 1;
break;
case 0xD1: // NEXT
if (loopcount > 0)
{
loopcount--;
code = loopstart;
}
else
{
cond = loopcond;
condstack = loopcondstack;
}
break;
case 0xD2: // NEXT+FLUSH
if (loopcount > 0)
{
loopcount--;
code = loopstart;
}
else
{
offset = 0;
datareg = 0;
condstack = 0;
cond = 1;
}
break;
case 0xD3: // offset = b
offset = b;
break;
case 0xD4: // datareg += b
datareg += b;
break;
case 0xD5: // datareg = b
datareg = b;
break;
case 0xD6: // u32[b+offset] = datareg / offset += 4
NDS::ARM7Write32(b + offset, datareg);
offset += 4;
break;
case 0xD7: // u16[b+offset] = datareg / offset += 2
NDS::ARM7Write16(b + offset, datareg & 0xFFFF);
offset += 2;
break;
case 0xD8: // u8[b+offset] = datareg / offset += 1
NDS::ARM7Write8(b + offset, datareg & 0xFF);
offset += 1;
break;
case 0xD9: // datareg = u32[b+offset]
datareg = NDS::ARM7Read32(b + offset);
break;
case 0xDA: // datareg = u16[b+offset]
datareg = NDS::ARM7Read16(b + offset);
break;
case 0xDB: // datareg = u8[b+offset]
datareg = NDS::ARM7Read8(b + offset);
break;
case 0xDC: // offset += b
offset += b;
break;
case16(0xE0): // copy b param bytes to address a+offset
{
// TODO: check for bad alignment of dstaddr
u32 dstaddr = (a & 0x0FFFFFFF) + offset;
u32 bytesleft = b;
while (bytesleft >= 8)
{
NDS::ARM7Write32(dstaddr, *code++); dstaddr += 4;
NDS::ARM7Write32(dstaddr, *code++); dstaddr += 4;
bytesleft -= 8;
}
if (bytesleft > 0)
{
u8* leftover = (u8*)code;
*code += 2;
if (bytesleft >= 4)
{
NDS::ARM7Write32(dstaddr, *(u32*)leftover); dstaddr += 4;
leftover += 4;
bytesleft -= 4;
}
while (bytesleft > 0)
{
NDS::ARM7Write8(dstaddr, *leftover++); dstaddr++;
bytesleft--;
}
}
}
break;
case16(0xF0): // copy b bytes from address offset to address a
{
// TODO: check for bad alignment of srcaddr/dstaddr
u32 srcaddr = offset;
u32 dstaddr = (a & 0x0FFFFFFF);
u32 bytesleft = b;
while (bytesleft >= 4)
{
NDS::ARM7Write32(dstaddr, NDS::ARM7Read32(srcaddr));
srcaddr += 4;
dstaddr += 4;
bytesleft -= 4;
}
while (bytesleft > 0)
{
NDS::ARM7Write8(dstaddr, NDS::ARM7Read8(srcaddr));
srcaddr++;
dstaddr++;
bytesleft--;
}
}
break;
default:
printf("!! bad AR opcode %08X %08X\n", a, b);
return;
}
}
}
void RunCheats()
{
// TODO: make it disableable in general
for (u32 i = 0; i < NumCheatCodes; i++)
{
CheatEntry* entry = &CheatCodes[i];
if (entry->Enabled)
RunCheat(entry);
}
}
}

33
src/AREngine.h Normal file
View File

@ -0,0 +1,33 @@
/*
Copyright 2016-2020 Arisotura
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 ARENGINE_H
#define ARENGINE_H
namespace AREngine
{
bool Init();
void DeInit();
void Reset();
void RunCheats();
}
#endif // ARENGINE_H

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.
@ -20,6 +20,7 @@
#include "NDS.h"
#include "ARM.h"
#include "ARMInterpreter.h"
#include "AREngine.h"
// instruction timing notes
@ -227,6 +228,8 @@ void ARMv5::JumpTo(u32 addr, bool restorecpsr)
PrefetchAbort();
return;
}*/
NDS::MonitorARM9Jump(addr);
}
void ARMv4::JumpTo(u32 addr, bool restorecpsr)
@ -405,6 +408,14 @@ void ARM::TriggerIRQ()
R_IRQ[2] = oldcpsr;
R[14] = R[15] + (oldcpsr & 0x20 ? 2 : 0);
JumpTo(ExceptionBase + 0x18);
// ARDS cheat support
// normally, those work by hijacking the ARM7 VBlank handler
if (Num == 1)
{
if ((NDS::IF[1] & NDS::IE[1]) & (1<<NDS::IRQ_VBlank))
AREngine::RunCheats();
}
}
void ARMv5::PrefetchAbort()

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,6 +1,8 @@
project(core)
add_library(core STATIC
ARCodeList.cpp
AREngine.cpp
ARM.cpp
ARMInterpreter.cpp
ARMInterpreter_ALU.cpp
@ -18,6 +20,7 @@ add_library(core STATIC
DSi_NWifi.cpp
DSi_SD.cpp
DSi_SPI_TSC.cpp
GBACart.cpp
GPU.cpp
GPU2D.cpp
GPU3D.cpp

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

773
src/GBACart.cpp Normal file
View File

@ -0,0 +1,773 @@
/*
Copyright 2019 Arisotura, Raphaël Zumer
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 "GBACart.h"
#include "CRC32.h"
#include "Platform.h"
namespace GBACart_SRAM
{
enum SaveType {
S_NULL,
S_EEPROM4K,
S_EEPROM64K,
S_SRAM256K,
S_FLASH512K,
S_FLASH1M
};
// from DeSmuME
struct FlashProperties
{
u8 state;
u8 cmd;
u8 device;
u8 manufacturer;
u8 bank;
};
u8* SRAM;
FILE* SRAMFile;
u32 SRAMLength;
SaveType SRAMType;
FlashProperties SRAMFlashState;
char SRAMPath[1024];
void (*WriteFunc)(u32 addr, u8 val);
void Write_Null(u32 addr, u8 val);
void Write_EEPROM(u32 addr, u8 val);
void Write_SRAM(u32 addr, u8 val);
void Write_Flash(u32 addr, u8 val);
bool Init()
{
SRAM = NULL;
SRAMFile = NULL;
return true;
}
void DeInit()
{
if (SRAMFile) fclose(SRAMFile);
if (SRAM) delete[] SRAM;
}
void Reset()
{
// do nothing, we don't want to clear GBA SRAM on reset
}
void Eject()
{
if (SRAMFile) fclose(SRAMFile);
if (SRAM) delete[] SRAM;
SRAM = NULL;
SRAMFile = NULL;
SRAMLength = 0;
SRAMType = S_NULL;
SRAMFlashState = {};
}
void DoSavestate(Savestate* file)
{
file->Section("GBCS"); // Game Boy [Advance] Cart Save
// logic mostly copied from NDSCart_SRAM
u32 oldlen = SRAMLength;
file->Var32(&SRAMLength);
if (SRAMLength != oldlen)
{
// reallocate save memory
if (oldlen) delete[] SRAM;
if (SRAMLength) SRAM = new u8[SRAMLength];
}
if (SRAMLength)
{
// fill save memory if data is present
file->VarArray(SRAM, SRAMLength);
}
else
{
// no save data, clear the current state
SRAMType = SaveType::S_NULL;
if (SRAMFile) fclose(SRAMFile);
SRAM = NULL;
SRAMFile = NULL;
return;
}
// persist some extra state info
file->Var8(&SRAMFlashState.bank);
file->Var8(&SRAMFlashState.cmd);
file->Var8(&SRAMFlashState.device);
file->Var8(&SRAMFlashState.manufacturer);
file->Var8(&SRAMFlashState.state);
file->Var8((u8*)&SRAMType);
}
void LoadSave(const char* path)
{
if (SRAM) delete[] SRAM;
strncpy(SRAMPath, path, 1023);
SRAMPath[1023] = '\0';
SRAMLength = 0;
FILE* f = Platform::OpenFile(SRAMPath, "r+b");
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);
SRAMFile = f;
}
switch (SRAMLength)
{
case 512:
SRAMType = S_EEPROM4K;
WriteFunc = Write_EEPROM;
break;
case 8192:
SRAMType = S_EEPROM64K;
WriteFunc = Write_EEPROM;
break;
case 32768:
SRAMType = S_SRAM256K;
WriteFunc = Write_SRAM;
break;
case 65536:
SRAMType = S_FLASH512K;
WriteFunc = Write_Flash;
break;
case 128*1024:
SRAMType = S_FLASH1M;
WriteFunc = Write_Flash;
break;
default:
printf("!! BAD SAVE LENGTH %d\n", SRAMLength);
case 0:
SRAMType = S_NULL;
WriteFunc = Write_Null;
break;
}
if (SRAMType == S_FLASH512K)
{
// Panasonic 64K chip
SRAMFlashState.device = 0x1B;
SRAMFlashState.manufacturer = 0x32;
}
else if (SRAMType == S_FLASH1M)
{
// Sanyo 128K chip
SRAMFlashState.device = 0x13;
SRAMFlashState.manufacturer = 0x62;
}
}
void RelocateSave(const char* path, bool write)
{
if (!write)
{
LoadSave(path); // lazy
return;
}
strncpy(SRAMPath, path, 1023);
SRAMPath[1023] = '\0';
FILE *f = Platform::OpenFile(path, "r+b");
if (!f)
{
printf("GBACart_SRAM::RelocateSave: failed to create new file. fuck\n");
return;
}
SRAMFile = f;
fwrite(SRAM, SRAMLength, 1, SRAMFile);
}
// mostly ported from DeSmuME
u8 Read_Flash(u32 addr)
{
if (SRAMFlashState.cmd == 0) // no cmd
{
return *(u8*)&SRAM[addr + 0x10000 * SRAMFlashState.bank];
}
switch (SRAMFlashState.cmd)
{
case 0x90: // chip ID
if (addr == 0x0000) return SRAMFlashState.manufacturer;
if (addr == 0x0001) return SRAMFlashState.device;
break;
case 0xF0: // terminate command (TODO: break if non-Macronix chip and not at the end of an ID call?)
SRAMFlashState.state = 0;
SRAMFlashState.cmd = 0;
break;
case 0xA0: // write command
break; // ignore here, handled in Write_Flash()
case 0xB0: // bank switching (128K only)
break; // ignore here, handled in Write_Flash()
default:
printf("GBACart_SRAM::Read_Flash: unknown command 0x%02X @ 0x%04X\n", SRAMFlashState.cmd, addr);
break;
}
return 0xFF;
}
void Write_Null(u32 addr, u8 val) {}
void Write_EEPROM(u32 addr, u8 val)
{
// TODO: could be used in homebrew?
}
// mostly ported from DeSmuME
void Write_Flash(u32 addr, u8 val)
{
switch (SRAMFlashState.state)
{
case 0x00:
if (addr == 0x5555)
{
if (val == 0xF0)
{
// reset
SRAMFlashState.state = 0;
SRAMFlashState.cmd = 0;
return;
}
else if (val == 0xAA)
{
SRAMFlashState.state = 1;
return;
}
}
if (addr == 0x0000)
{
if (SRAMFlashState.cmd == 0xB0)
{
// bank switching
SRAMFlashState.bank = val;
SRAMFlashState.cmd = 0;
return;
}
}
break;
case 0x01:
if (addr == 0x2AAA && val == 0x55)
{
SRAMFlashState.state = 2;
return;
}
SRAMFlashState.state = 0;
break;
case 0x02:
if (addr == 0x5555)
{
// send command
switch (val)
{
case 0x80: // erase
SRAMFlashState.state = 0x80;
break;
case 0x90: // chip ID
SRAMFlashState.state = 0x90;
break;
case 0xA0: // write
SRAMFlashState.state = 0;
break;
default:
SRAMFlashState.state = 0;
break;
}
SRAMFlashState.cmd = val;
return;
}
SRAMFlashState.state = 0;
break;
// erase
case 0x80:
if (addr == 0x5555 && val == 0xAA)
{
SRAMFlashState.state = 0x81;
return;
}
SRAMFlashState.state = 0;
break;
case 0x81:
if (addr == 0x2AAA && val == 0x55)
{
SRAMFlashState.state = 0x82;
return;
}
SRAMFlashState.state = 0;
break;
case 0x82:
if (val == 0x30)
{
u32 start_addr = addr + 0x10000 * SRAMFlashState.bank;
memset((u8*)&SRAM[start_addr], 0xFF, 0x1000);
if (SRAMFile)
{
fseek(SRAMFile, start_addr, SEEK_SET);
fwrite((u8*)&SRAM[start_addr], 1, 0x1000, SRAMFile);
}
}
SRAMFlashState.state = 0;
SRAMFlashState.cmd = 0;
return;
// chip ID
case 0x90:
if (addr == 0x5555 && val == 0xAA)
{
SRAMFlashState.state = 0x91;
return;
}
SRAMFlashState.state = 0;
break;
case 0x91:
if (addr == 0x2AAA && val == 0x55)
{
SRAMFlashState.state = 0x92;
return;
}
SRAMFlashState.state = 0;
break;
case 0x92:
SRAMFlashState.state = 0;
SRAMFlashState.cmd = 0;
return;
default:
break;
}
if (SRAMFlashState.cmd == 0xA0) // write
{
Write_SRAM(addr + 0x10000 * SRAMFlashState.bank, val);
SRAMFlashState.state = 0;
SRAMFlashState.cmd = 0;
return;
}
printf("GBACart_SRAM::Write_Flash: unknown write 0x%02X @ 0x%04X (state: 0x%02X)\n",
val, addr, SRAMFlashState.state);
}
void Write_SRAM(u32 addr, u8 val)
{
u8 prev = *(u8*)&SRAM[addr];
if (prev != val)
{
*(u8*)&SRAM[addr] = val;
if (SRAMFile)
{
fseek(SRAMFile, addr, SEEK_SET);
fwrite((u8*)&SRAM[addr], 1, 1, SRAMFile);
}
}
}
u8 Read8(u32 addr)
{
if (SRAMType == S_NULL)
{
return 0xFF;
}
if (SRAMType == S_FLASH512K || SRAMType == S_FLASH1M)
{
return Read_Flash(addr);
}
return *(u8*)&SRAM[addr];
}
u16 Read16(u32 addr)
{
if (SRAMType == S_NULL)
{
return 0xFFFF;
}
if (SRAMType == S_FLASH512K || SRAMType == S_FLASH1M)
{
u16 val = Read_Flash(addr + 0) |
(Read_Flash(addr + 1) << 8);
return val;
}
return *(u16*)&SRAM[addr];
}
u32 Read32(u32 addr)
{
if (SRAMType == S_NULL)
{
return 0xFFFFFFFF;
}
if (SRAMType == S_FLASH512K || SRAMType == S_FLASH1M)
{
u32 val = Read_Flash(addr + 0) |
(Read_Flash(addr + 1) << 8) |
(Read_Flash(addr + 2) << 16) |
(Read_Flash(addr + 3) << 24);
return val;
}
return *(u32*)&SRAM[addr];
}
void Write8(u32 addr, u8 val)
{
u8 prev = *(u8*)&SRAM[addr];
WriteFunc(addr, val);
}
void Write16(u32 addr, u16 val)
{
u16 prev = *(u16*)&SRAM[addr];
WriteFunc(addr + 0, val & 0xFF);
WriteFunc(addr + 1, val >> 8 & 0xFF);
}
void Write32(u32 addr, u32 val)
{
u32 prev = *(u32*)&SRAM[addr];
WriteFunc(addr + 0, val & 0xFF);
WriteFunc(addr + 1, val >> 8 & 0xFF);
WriteFunc(addr + 2, val >> 16 & 0xFF);
WriteFunc(addr + 3, val >> 24 & 0xFF);
}
}
namespace GBACart
{
const char SOLAR_SENSOR_GAMECODES[10][5] =
{
"U3IJ", // Bokura no Taiyou - Taiyou Action RPG (Japan)
"U3IE", // Boktai - The Sun Is in Your Hand (USA)
"U3IP", // Boktai - The Sun Is in Your Hand (Europe)
"U32J", // Zoku Bokura no Taiyou - Taiyou Shounen Django (Japan)
"U32E", // Boktai 2 - Solar Boy Django (USA)
"U32P", // Boktai 2 - Solar Boy Django (Europe)
"U33J", // Shin Bokura no Taiyou - Gyakushuu no Sabata (Japan)
"A3IJ" // Boktai - The Sun Is in Your Hand (USA) (Sample)
};
bool CartInserted;
bool HasSolarSensor;
u8* CartROM;
u32 CartROMSize;
u32 CartCRC;
u32 CartID;
GPIO CartGPIO; // overridden GPIO parameters
bool Init()
{
if (!GBACart_SRAM::Init()) return false;
CartROM = NULL;
return true;
}
void DeInit()
{
if (CartROM) delete[] CartROM;
GBACart_SRAM::DeInit();
}
void Reset()
{
// Do not reset cartridge ROM.
// Prefer keeping the inserted cartridge on reset.
// This allows resetting a DS game without losing GBA state,
// and resetting to firmware without the slot being emptied.
// The Stop function will clear the cartridge state via Eject().
GBACart_SRAM::Reset();
GBACart_SolarSensor::Reset();
}
void Eject()
{
if (CartROM) delete[] CartROM;
CartInserted = false;
HasSolarSensor = false;
CartROM = NULL;
CartROMSize = 0;
CartCRC = NULL;
CartID = NULL;
CartGPIO = {};
GBACart_SRAM::Eject();
Reset();
}
void DoSavestate(Savestate* file)
{
file->Section("GBAC"); // Game Boy Advance Cartridge
// logic mostly copied from NDSCart
// first we need to reload the cart itself,
// since unlike with DS, it's not loaded in advance
file->Var32(&CartROMSize);
if (!CartROMSize) // no GBA cartridge state? nothing to do here
{
// do eject the cartridge if something is inserted
Eject();
return;
}
u32 oldCRC = CartCRC;
file->Var32(&CartCRC);
if (CartCRC != oldCRC)
{
// delete and reallocate ROM so that it is zero-padded to its full length
if (CartROM) delete[] CartROM;
CartROM = new u8[CartROMSize];
// clear the SRAM file handle; writes will not be committed
if (GBACart_SRAM::SRAMFile)
{
fclose(GBACart_SRAM::SRAMFile);
GBACart_SRAM::SRAMFile = NULL;
}
}
// only save/load the cartridge header
//
// GBA connectivity on DS mainly involves identifying the title currently
// inserted, reading save data, and issuing commands intercepted here
// (e.g. solar sensor signals). we don't know of any case where GBA ROM is
// read directly from DS software. therefore, it is more practical, both
// from the development and user experience perspectives, to avoid dealing
// with file dependencies, and store a small portion of ROM data that should
// satisfy the needs of all known software that reads from the GBA slot.
//
// note: in case of a state load, only the cartridge header is restored, but
// the rest of the ROM data is only cleared (zero-initialized) if the CRC
// differs. Therefore, loading the GBA cartridge associated with the save state
// in advance will maintain access to the full ROM contents.
file->VarArray(CartROM, 192);
CartInserted = true; // known, because CartROMSize > 0
file->Var32(&CartCRC);
file->Var32(&CartID);
file->Var8((u8*)&HasSolarSensor);
file->Var16(&CartGPIO.control);
file->Var16(&CartGPIO.data);
file->Var16(&CartGPIO.direction);
// now do the rest
GBACart_SRAM::DoSavestate(file);
if (HasSolarSensor) GBACart_SolarSensor::DoSavestate(file);
}
bool LoadROM(const char* path, const char* sram)
{
FILE* f = Platform::OpenFile(path, "rb");
if (!f)
{
return false;
}
if (CartInserted)
{
Reset();
}
fseek(f, 0, SEEK_END);
u32 len = (u32)ftell(f);
CartROMSize = 0x200;
while (CartROMSize < len)
CartROMSize <<= 1;
char gamecode[5] = { '\0' };
fseek(f, 0xAC, SEEK_SET);
fread(&gamecode, 1, 4, f);
printf("Game code: %s\n", gamecode);
for (int i = 0; i < sizeof(SOLAR_SENSOR_GAMECODES)/sizeof(SOLAR_SENSOR_GAMECODES[0]); i++)
{
if (strcmp(gamecode, SOLAR_SENSOR_GAMECODES[i]) == 0) HasSolarSensor = true;
}
if (HasSolarSensor)
{
printf("GBA solar sensor support detected!\n");
}
CartROM = new u8[CartROMSize];
memset(CartROM, 0, CartROMSize);
fseek(f, 0, SEEK_SET);
fread(CartROM, 1, len, f);
fclose(f);
CartCRC = CRC32(CartROM, CartROMSize);
printf("ROM CRC32: %08X\n", CartCRC);
CartInserted = true;
// save
printf("Save file: %s\n", sram);
GBACart_SRAM::LoadSave(sram);
return true;
}
void RelocateSave(const char* path, bool write)
{
// derp herp
GBACart_SRAM::RelocateSave(path, write);
}
// referenced from mGBA
void WriteGPIO(u32 addr, u16 val)
{
switch (addr)
{
case 0xC4:
CartGPIO.data &= ~CartGPIO.direction;
CartGPIO.data |= val & CartGPIO.direction;
if (HasSolarSensor) GBACart_SolarSensor::Process(&CartGPIO);
break;
case 0xC6:
CartGPIO.direction = val;
break;
case 0xC8:
CartGPIO.control = val;
break;
default:
printf("Unknown GBA GPIO write 0x%02X @ 0x%04X\n", val, addr);
}
// write the GPIO values in the ROM (if writable)
if (CartGPIO.control & 1)
{
*(u16*)&CartROM[0xC4] = CartGPIO.data;
*(u16*)&CartROM[0xC6] = CartGPIO.direction;
*(u16*)&CartROM[0xC8] = CartGPIO.control;
}
else
{
// GBATEK: "in write-only mode, reads return 00h (or [possibly] other data (...))"
// ambiguous, but mGBA sets ROM to 00h when switching to write-only, so do the same
*(u16*)&CartROM[0xC4] = 0;
*(u16*)&CartROM[0xC6] = 0;
*(u16*)&CartROM[0xC8] = 0;
}
}
}
namespace GBACart_SolarSensor
{
bool LightEdge;
u8 LightCounter;
u8 LightSample;
u8 LightLevel; // 0-10 range
// levels from mGBA
const int GBA_LUX_LEVELS[11] = { 0, 5, 11, 18, 27, 42, 62, 84, 109, 139, 183 };
#define LIGHT_VALUE (0xFF - (0x16 + GBA_LUX_LEVELS[LightLevel]))
void Reset()
{
LightEdge = false;
LightCounter = 0;
LightSample = 0xFF;
LightLevel = 0;
}
void DoSavestate(Savestate* file)
{
file->Var8((u8*)&LightEdge);
file->Var8(&LightCounter);
file->Var8(&LightSample);
file->Var8(&LightLevel);
}
void Process(GBACart::GPIO* gpio)
{
if (gpio->data & 4) return; // Boktai chip select
if (gpio->data & 2) // Reset
{
u8 prev = LightSample;
LightCounter = 0;
LightSample = LIGHT_VALUE;
printf("Solar sensor reset (sample: 0x%02X -> 0x%02X)\n", prev, LightSample);
}
if (gpio->data & 1 && LightEdge) LightCounter++;
LightEdge = !(gpio->data & 1);
bool sendBit = LightCounter >= LightSample;
if (gpio->control & 1)
{
gpio->data = (gpio->data & gpio->direction) | ((sendBit << 3) & ~gpio->direction & 0xF);
}
}
}

87
src/GBACart.h Normal file
View File

@ -0,0 +1,87 @@
/*
Copyright 2019 Arisotura, Raphaël Zumer
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 GBACART_H
#define GBACART_H
#include "types.h"
#include "Savestate.h"
namespace GBACart_SRAM
{
extern u8* SRAM;
extern u32 SRAMLength;
void Reset();
void DoSavestate(Savestate* file);
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 GBACart
{
struct GPIO
{
u16 data;
u16 direction;
u16 control;
};
extern bool CartInserted;
extern bool HasSolarSensor;
extern u8* CartROM;
extern u32 CartROMSize;
extern u32 CartCRC;
bool Init();
void DeInit();
void Reset();
void Eject();
void DoSavestate(Savestate* file);
bool LoadROM(const char* path, const char* sram);
void RelocateSave(const char* path, bool write);
void WriteGPIO(u32 addr, u16 val);
}
namespace GBACart_SolarSensor
{
extern u8 LightLevel;
void Reset();
void DoSavestate(Savestate* file);
void Process(GBACart::GPIO* gpio);
}
#endif // GBACART_H

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.
@ -181,6 +181,8 @@ u8 RenderFogDensityTable[34];
u32 RenderClearAttr1, RenderClearAttr2;
u32 ZeroDotWLimit;
u32 GXStat;
u32 ExecParams[32];
@ -332,6 +334,8 @@ void Reset()
DispCnt = 0;
AlphaRef = 0;
ZeroDotWLimit = 0; // CHECKME
GXStat = 0;
memset(ExecParams, 0, 32*4);
@ -409,6 +413,8 @@ void DoSavestate(Savestate* file)
file->Var32(&DispCnt);
file->Var8(&AlphaRef);
file->Var32(&ZeroDotWLimit);
file->Var32(&GXStat);
file->VarArray(ExecParams, 32*4);
@ -569,27 +575,12 @@ void DoSavestate(Savestate* file)
// probably not worth storing the vblank-latched Renderxxxxxx variables
if (file->IsAtleastVersion(2, 1))
{
// command stall queue, only in version 2.1 and up
CmdStallQueue->DoSavestate(file);
file->Var32((u32*)&VertexPipeline);
file->Var32((u32*)&NormalPipeline);
file->Var32((u32*)&PolygonPipeline);
file->Var32((u32*)&VertexSlotCounter);
file->Var32(&VertexSlotsFree);
}
else
{
// for version 2.0, just clear it. not having it doesn't matter
// if this comes from older melonDS revisions.
CmdStallQueue->Clear();
VertexPipeline = 0;
NormalPipeline = 0;
PolygonPipeline = 0;
VertexSlotCounter = 0;
VertexSlotsFree = 1;
}
CmdStallQueue->DoSavestate(file);
file->Var32((u32*)&VertexPipeline);
file->Var32((u32*)&NormalPipeline);
file->Var32((u32*)&PolygonPipeline);
file->Var32((u32*)&VertexSlotCounter);
file->Var32(&VertexSlotsFree);
if (!file->Saving)
{
@ -888,7 +879,7 @@ void StallPolygonPipeline(s32 delay, s32 nonstalldelay)
template<int comp, s32 plane, bool attribs>
void ClipSegment(Vertex* outbuf, Vertex* vout, Vertex* vin)
void ClipSegment(Vertex* outbuf, Vertex* vin, Vertex* vout)
{
s64 factor_num = vin->Position[3] - (plane*vin->Position[comp]);
s32 factor_den = factor_num - (vout->Position[3] - (plane*vout->Position[comp]));
@ -1170,6 +1161,104 @@ void SubmitPolygon()
return;
}
// reject the polygon if it's not going to fit in polygon/vertex RAM
if (NumPolygons >= 2048 || NumVertices+nverts > 6144)
{
LastStripPolygon = NULL;
DispCnt |= (1<<13);
return;
}
// compute screen coordinates
for (int i = clipstart; i < nverts; i++)
{
Vertex* vtx = &clippedvertices[i];
// W is truncated to 24 bits at this point
// if this W is zero, the polygon isn't rendered
vtx->Position[3] &= 0x00FFFFFF;
// viewport transform
// note: the DS performs these divisions using a 32-bit divider
// thus, if W is greater than 0xFFFF, some precision is sacrificed
// to make the numbers fit into the divider
u32 posX, posY;
u32 w = vtx->Position[3];
if (w == 0)
{
posX = 0;
posY = 0;
}
else
{
posX = vtx->Position[0] + w;
posY = -vtx->Position[1] + w;
u32 den = w;
if (w > 0xFFFF)
{
posX >>= 1;
posY >>= 1;
den >>= 1;
}
den <<= 1;
posX = ((posX * Viewport[4]) / den) + Viewport[0];
posY = ((posY * Viewport[5]) / den) + Viewport[3];
}
vtx->FinalPosition[0] = posX & 0x1FF;
vtx->FinalPosition[1] = posY & 0xFF;
// hi-res positions
// to consider: only do this when using the GL renderer? apply the aforementioned quirk to this?
if (w != 0)
{
posX = ((((s64)(vtx->Position[0] + w) * Viewport[4]) << 4) / (((s64)w) << 1)) + (Viewport[0] << 4);
posY = ((((s64)(-vtx->Position[1] + w) * Viewport[5]) << 4) / (((s64)w) << 1)) + (Viewport[3] << 4);
vtx->HiresPosition[0] = posX & 0x1FFF;
vtx->HiresPosition[1] = posY & 0xFFF;
}
}
// zero-dot W check:
// * if the polygon's vertices all have the same screen coordinates, it is considered to be zero-dot
// * if all the vertices have a W greater than the threshold defined in register 0x04000610,
// the polygon is rejected, unless bit13 in the polygon attributes is set
if (!(CurPolygonAttr & (1<<13)))
{
bool zerodot = true;
bool allbehind = true;
for (int i = 0; i < nverts; i++)
{
Vertex* vtx = &clippedvertices[i];
if (vtx->FinalPosition[0] != clippedvertices[0].FinalPosition[0] ||
vtx->FinalPosition[1] != clippedvertices[0].FinalPosition[1])
{
zerodot = false;
break;
}
if (vtx->Position[3] <= ZeroDotWLimit)
{
allbehind = false;
break;
}
}
if (zerodot && allbehind)
{
LastStripPolygon = NULL;
return;
}
}
// build the actual polygon
if (nverts == 4)
@ -1187,13 +1276,6 @@ void SubmitPolygon()
else VertexSlotsFree = 0b1110;
}
if (NumPolygons >= 2048 || NumVertices+nverts > 6144)
{
LastStripPolygon = NULL;
DispCnt |= (1<<13);
return;
}
Polygon* poly = &CurPolygonRAM[NumPolygons++];
poly->NumVertices = 0;
@ -1248,37 +1330,6 @@ void SubmitPolygon()
NumVertices++;
poly->NumVertices++;
// W is truncated to 24 bits at this point
// if this W is zero, the polygon isn't rendered
vtx->Position[3] &= 0x00FFFFFF;
// viewport transform
s32 posX, posY;
s32 w = vtx->Position[3];
if (w == 0)
{
posX = 0;
posY = 0;
}
else
{
posX = (((s64)(vtx->Position[0] + w) * Viewport[4]) / (((s64)w) << 1)) + Viewport[0];
posY = (((s64)(-vtx->Position[1] + w) * Viewport[5]) / (((s64)w) << 1)) + Viewport[3];
}
vtx->FinalPosition[0] = posX & 0x1FF;
vtx->FinalPosition[1] = posY & 0xFF;
// hi-res positions
if (w != 0)
{
posX = ((((s64)(vtx->Position[0] + w) * Viewport[4]) << 4) / (((s64)w) << 1)) + (Viewport[0] << 4);
posY = ((((s64)(-vtx->Position[1] + w) * Viewport[5]) << 4) / (((s64)w) << 1)) + (Viewport[3] << 4);
vtx->HiresPosition[0] = posX & 0x1FFF;
vtx->HiresPosition[1] = posY & 0xFFF;
}
vtx->FinalColor[0] = vtx->Color[0] >> 12;
if (vtx->FinalColor[0]) vtx->FinalColor[0] = ((vtx->FinalColor[0] << 4) + 0xF);
vtx->FinalColor[1] = vtx->Color[1] >> 12;
@ -2716,12 +2767,24 @@ void Write8(u32 addr, u8 val)
return;
}
if (addr >= 0x04000330 && addr < 0x04000340)
{
((u8*)EdgeTable)[addr - 0x04000330] = val;
return;
}
if (addr >= 0x04000360 && addr < 0x04000380)
{
FogDensityTable[addr - 0x04000360] = val & 0x7F;
return;
}
if (addr >= 0x04000380 && addr < 0x040003C0)
{
((u8*)ToonTable)[addr - 0x04000380] = val;
return;
}
printf("unknown GPU3D write8 %08X %02X\n", addr, val);
}
@ -2782,6 +2845,11 @@ void Write16(u32 addr, u16 val)
GXStat |= (val << 16);
CheckFIFOIRQ();
return;
case 0x04000610:
val &= 0x7FFF;
ZeroDotWLimit = (val * 0x200) + 0x1FF;
return;
}
if (addr >= 0x04000330 && addr < 0x04000340)
@ -2853,6 +2921,11 @@ void Write32(u32 addr, u32 val)
GXStat |= val;
CheckFIFOIRQ();
return;
case 0x04000610:
val &= 0x7FFF;
ZeroDotWLimit = (val * 0x200) + 0x1FF;
return;
}
if (addr >= 0x04000400 && addr < 0x04000440)

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.
@ -22,6 +22,7 @@
#include "NDS.h"
#include "ARM.h"
#include "NDSCart.h"
#include "GBACart.h"
#include "DMA.h"
#include "FIFO.h"
#include "GPU.h"
@ -29,6 +30,7 @@
#include "SPI.h"
#include "RTC.h"
#include "Wifi.h"
#include "AREngine.h"
#include "Platform.h"
#include "DSi.h"
@ -103,6 +105,7 @@ u8 ARM7WRAM[0x10000];
u16 ExMemCnt[2];
// TODO: these belong in NDSCart!
u8 ROMSeed0[2*8];
u8 ROMSeed1[2*8];
@ -148,6 +151,8 @@ u16 RCnt;
bool Running;
bool RunningGame;
void DivDone(u32 param);
void SqrtDone(u32 param);
@ -174,6 +179,7 @@ bool Init()
IPCFIFO7 = new FIFO<u32>(16);
if (!NDSCart::Init()) return false;
if (!GBACart::Init()) return false;
if (!GPU::Init()) return false;
if (!SPU::Init()) return false;
if (!SPI::Init()) return false;
@ -182,6 +188,8 @@ bool Init()
if (!DSi::Init()) return false;
if (!AREngine::Init()) return false;
return true;
}
@ -197,6 +205,7 @@ void DeInit()
delete IPCFIFO7;
NDSCart::DeInit();
GBACart::DeInit();
GPU::DeInit();
SPU::DeInit();
SPI::DeInit();
@ -204,6 +213,8 @@ void DeInit()
Wifi::DeInit();
DSi::DeInit();
AREngine::DeInit();
}
@ -320,7 +331,24 @@ void SetupDirectBoot()
MapSharedWRAM(3);
for (u32 i = 0; i < bootparams[3]; i+=4)
u32 arm9start = 0;
// load the ARM9 secure area
if (bootparams[0] >= 0x4000 && bootparams[0] < 0x8000)
{
u8 securearea[0x800];
NDSCart::DecryptSecureArea(securearea);
for (u32 i = 0; i < 0x800; i+=4)
{
ARM9Write32(bootparams[2]+i, *(u32*)&securearea[i]);
arm9start += 4;
}
}
// CHECKME: firmware seems to load this in 0x200 byte chunks
for (u32 i = arm9start; i < bootparams[3]; i+=4)
{
u32 tmp = *(u32*)&NDSCart::CartROM[bootparams[0]+i];
ARM9Write32(bootparams[2]+i, tmp);
@ -398,6 +426,7 @@ void Reset()
FILE* f;
u32 i;
RunningGame = false;
LastSysClockCycles = 0;
memset(ARM9BIOS, 0, 0x1000);
@ -523,6 +552,7 @@ void Reset()
RCnt = 0;
NDSCart::Reset();
GBACart::Reset();
GPU::Reset();
SPU::Reset();
SPI::Reset();
@ -531,6 +561,8 @@ void Reset()
DSi::Reset();
KeyInput &= ~(1 << (16+6)); // TODO
AREngine::Reset();
}
void Stop()
@ -700,21 +732,16 @@ bool DoSavestate(Savestate* file)
file->Var16(&KeyCnt);
file->Var16(&RCnt);
file->Var8(&WRAMCnt);
for (int i = 0; i < 8; i++)
DMAs[i]->DoSavestate(file);
file->Var8(&WRAMCnt); // FIXME!!!!!
file->Var32((u32*)&RunningGame);
if (!file->Saving)
{
// 'dept of redundancy dept'
// but we do need to update the mappings
MapSharedWRAM(WRAMCnt);
}
if (!file->Saving)
{
InitTimings();
SetGBASlotTimings();
@ -723,10 +750,14 @@ bool DoSavestate(Savestate* file)
SetWifiWaitCnt(tmp); // force timing table update
}
for (int i = 0; i < 8; i++)
DMAs[i]->DoSavestate(file);
ARM9->DoSavestate(file);
ARM7->DoSavestate(file);
NDSCart::DoSavestate(file);
GBACart::DoSavestate(file);
GPU::DoSavestate(file);
SPU::DoSavestate(file);
SPI::DoSavestate(file);
@ -755,6 +786,19 @@ bool LoadROM(const char* path, const char* sram, bool direct)
}
}
bool LoadGBAROM(const char* path, const char* sram)
{
if (GBACart::LoadROM(path, sram))
{
return true;
}
else
{
printf("Failed to load ROM %s\n", path);
return false;
}
}
void LoadBIOS()
{
Reset();
@ -1308,6 +1352,22 @@ void NocashPrint(u32 ncpu, u32 addr)
void MonitorARM9Jump(u32 addr)
{
// checkme: can the entrypoint addr be THUMB?
if ((!RunningGame) && NDSCart::CartROM)
{
if (addr == *(u32*)&NDSCart::CartROM[0x24])
{
printf("Game is now booting\n");
RunningGame = true;
}
}
}
void HandleTimerOverflow(u32 tid)
{
Timer* timer = &Timers[tid];
@ -1725,16 +1785,20 @@ u8 ARM9Read8(u32 addr)
case 0x08000000:
case 0x09000000:
if (ExMemCnt[0] & (1<<7)) return 0xFF; // TODO: proper open bus
//return *(u8*)&NDSCart::CartROM[addr & (NDSCart::CartROMSize-1)];
//printf("GBA read8 %08X\n", addr);
// TODO!!!
return 0xFF;
if (ExMemCnt[0] & (1<<7)) return 0x00; // deselected CPU is 00h-filled
if (GBACart::CartInserted)
{
return *(u8*)&GBACart::CartROM[addr & (GBACart::CartROMSize-1)];
}
return 0xFF; // TODO: proper open bus
case 0x0A000000:
if (ExMemCnt[0] & (1<<7)) return 0xFF; // TODO: proper open bus
// TODO!!!
return 0xFF;
if (ExMemCnt[0] & (1<<7)) return 0x00; // deselected CPU is 00h-filled
if (GBACart::CartInserted)
{
return GBACart_SRAM::Read8(addr & (GBACart_SRAM::SRAMLength-1));
}
return 0xFF; // TODO: proper open bus
}
printf("unknown arm9 read8 %08X\n", addr);
@ -1786,16 +1850,20 @@ u16 ARM9Read16(u32 addr)
case 0x08000000:
case 0x09000000:
if (ExMemCnt[0] & (1<<7)) return 0xFFFF; // TODO: proper open bus
//return *(u8*)&NDSCart::CartROM[addr & (NDSCart::CartROMSize-1)];
//printf("GBA read8 %08X\n", addr);
// TODO!!!
return 0xFFFF;
if (ExMemCnt[0] & (1<<7)) return 0x0000; // deselected CPU is 00h-filled
if (GBACart::CartInserted)
{
return *(u16*)&GBACart::CartROM[addr & (GBACart::CartROMSize-1)];
}
return 0xFFFF; // TODO: proper open bus
case 0x0A000000:
if (ExMemCnt[0] & (1<<7)) return 0xFFFF; // TODO: proper open bus
// TODO!!!
return 0xFFFF;
if (ExMemCnt[0] & (1<<7)) return 0x0000; // deselected CPU is 00h-filled
if (GBACart::CartInserted)
{
return GBACart_SRAM::Read16(addr & (GBACart_SRAM::SRAMLength-1));
}
return 0xFFFF; // TODO: proper open bus
}
//printf("unknown arm9 read16 %08X %08X\n", addr, ARM9->R[15]);
@ -1847,16 +1915,20 @@ u32 ARM9Read32(u32 addr)
case 0x08000000:
case 0x09000000:
if (ExMemCnt[0] & (1<<7)) return 0xFFFFFFFF; // TODO: proper open bus
//return *(u8*)&NDSCart::CartROM[addr & (NDSCart::CartROMSize-1)];
//printf("GBA read8 %08X\n", addr);
// TODO!!!
return 0xFFFFFFFF;
if (ExMemCnt[0] & (1<<7)) return 0x00000000; // deselected CPU is 00h-filled
if (GBACart::CartInserted)
{
return *(u32*)&GBACart::CartROM[addr & (GBACart::CartROMSize-1)];
}
return 0xFFFFFFFF; // TODO: proper open bus
case 0x0A000000:
if (ExMemCnt[0] & (1<<7)) return 0xFFFFFFFF; // TODO: proper open bus
// TODO!!!
return 0xFFFFFFFF;
if (ExMemCnt[0] & (1<<7)) return 0x00000000; // deselected CPU is 00h-filled
if (GBACart::CartInserted)
{
return GBACart_SRAM::Read32(addr & (GBACart_SRAM::SRAMLength-1));
}
return 0xFFFFFFFF; // TODO: proper open bus
}
printf("unknown arm9 read32 %08X | %08X %08X\n", addr, ARM9->R[15], ARM9->R[12]);
@ -1887,6 +1959,27 @@ void ARM9Write8(u32 addr, u8 val)
case 0x07000000:
// checkme
return;
case 0x08000000:
case 0x09000000:
if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write
if (GBACart::CartInserted)
{
if ((addr & 0x00FFFFFF) >= 0xC4 && (addr & 0x00FFFFFF) <= 0xC9)
{
GBACart::WriteGPIO(addr & (GBACart::CartROMSize-1), val);
return;
}
}
break;
case 0x0A000000:
if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write
if (GBACart::CartInserted)
{
GBACart_SRAM::Write8(addr & (GBACart_SRAM::SRAMLength-1), val);
}
return;
}
printf("unknown arm9 write8 %08X %02X\n", addr, val);
@ -1930,6 +2023,29 @@ void ARM9Write16(u32 addr, u16 val)
if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return;
*(u16*)&GPU::OAM[addr & 0x7FF] = val;
return;
case 0x08000000:
case 0x09000000:
if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write
if (GBACart::CartInserted)
{
// Note: the lower bound is adjusted such that a write starting
// there will hit the first byte of the GPIO region.
if ((addr & 0x00FFFFFF) >= 0xC3 && (addr & 0x00FFFFFF) <= 0xC9)
{
GBACart::WriteGPIO(addr & (GBACart::CartROMSize-1), val);
return;
}
}
break;
case 0x0A000000:
if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write
if (GBACart::CartInserted)
{
GBACart_SRAM::Write16(addr & (GBACart_SRAM::SRAMLength-1), val);
}
return;
}
//printf("unknown arm9 write16 %08X %04X\n", addr, val);
@ -1973,6 +2089,30 @@ void ARM9Write32(u32 addr, u32 val)
if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return;
*(u32*)&GPU::OAM[addr & 0x7FF] = val;
return;
case 0x08000000:
case 0x09000000:
if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write
if (GBACart::CartInserted)
{
// Note: the lower bound is adjusted such that a write starting
// there will hit the first byte of the GPIO region.
if ((addr & 0x00FFFFFF) >= 0xC1 && (addr & 0x00FFFFFF) <= 0xC9)
{
GBACart::WriteGPIO(addr & (GBACart::CartROMSize-1), val & 0xFF);
GBACart::WriteGPIO((addr + 2) & (GBACart::CartROMSize-1), (val >> 16) & 0xFF);
return;
}
}
break;
case 0x0A000000:
if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write
if (GBACart::CartInserted)
{
GBACart_SRAM::Write32(addr & (GBACart_SRAM::SRAMLength-1), val);
}
return;
}
printf("unknown arm9 write32 %08X %08X | %08X\n", addr, val, ARM9->R[15]);
@ -2051,16 +2191,20 @@ u8 ARM7Read8(u32 addr)
case 0x08000000:
case 0x09000000:
if (!(ExMemCnt[0] & (1<<7))) return 0xFF; // TODO: proper open bus
//return *(u8*)&NDSCart::CartROM[addr & (NDSCart::CartROMSize-1)];
//printf("GBA read8 %08X\n", addr);
// TODO!!!
return 0xFF;
if (!(ExMemCnt[0] & (1<<7))) return 0x00; // deselected CPU is 00h-filled
if (GBACart::CartInserted)
{
return *(u8*)&GBACart::CartROM[addr & (GBACart::CartROMSize-1)];
}
return 0xFF; // TODO: proper open bus
case 0x0A000000:
if (!(ExMemCnt[0] & (1<<7))) return 0xFF; // TODO: proper open bus
// TODO!!!
return 0xFF;
if (!(ExMemCnt[0] & (1<<7))) return 0x00; // deselected CPU is 00h-filled
if (GBACart::CartInserted)
{
return GBACart_SRAM::Read8(addr & (GBACart_SRAM::SRAMLength-1));
}
return 0xFF; // TODO: proper open bus
}
printf("unknown arm7 read8 %08X %08X %08X/%08X\n", addr, ARM7->R[15], ARM7->R[0], ARM7->R[1]);
@ -2114,16 +2258,20 @@ u16 ARM7Read16(u32 addr)
case 0x08000000:
case 0x09000000:
if (!(ExMemCnt[0] & (1<<7))) return 0xFFFF; // TODO: proper open bus
//return *(u8*)&NDSCart::CartROM[addr & (NDSCart::CartROMSize-1)];
//printf("GBA read8 %08X\n", addr);
// TODO!!!
return 0xFFFF;
if (!(ExMemCnt[0] & (1<<7))) return 0x0000; // deselected CPU is 00h-filled
if (GBACart::CartInserted)
{
return *(u16*)&GBACart::CartROM[addr & (GBACart::CartROMSize-1)];
}
return 0xFFFF; // TODO: proper open bus
case 0x0A000000:
if (!(ExMemCnt[0] & (1<<7))) return 0xFFFF; // TODO: proper open bus
// TODO!!!
return 0xFFFF;
if (!(ExMemCnt[0] & (1<<7))) return 0x0000; // deselected CPU is 00h-filled
if (GBACart::CartInserted)
{
return GBACart_SRAM::Read16(addr & (GBACart_SRAM::SRAMLength-1));
}
return 0xFFFF; // TODO: proper open bus
}
printf("unknown arm7 read16 %08X %08X\n", addr, ARM7->R[15]);
@ -2177,16 +2325,20 @@ u32 ARM7Read32(u32 addr)
case 0x08000000:
case 0x09000000:
if (!(ExMemCnt[0] & (1<<7))) return 0xFFFFFFFF; // TODO: proper open bus
//return *(u8*)&NDSCart::CartROM[addr & (NDSCart::CartROMSize-1)];
//printf("GBA read8 %08X\n", addr);
// TODO!!!
return 0xFFFFFFFF;
if (!(ExMemCnt[0] & (1<<7))) return 0x00000000; // deselected CPU is 00h-filled
if (GBACart::CartInserted)
{
return *(u32*)&GBACart::CartROM[addr & (GBACart::CartROMSize-1)];
}
return 0xFFFFFFFF; // TODO: proper open bus
case 0x0A000000:
if (!(ExMemCnt[0] & (1<<7))) return 0xFFFFFFFF; // TODO: proper open bus
// TODO!!!
return 0xFFFFFFFF;
if (!(ExMemCnt[0] & (1<<7))) return 0x00000000; // deselected CPU is 00h-filled
if (GBACart::CartInserted)
{
return GBACart_SRAM::Read32(addr & (GBACart_SRAM::SRAMLength-1));
}
return 0xFFFFFFFF; // TODO: proper open bus
}
printf("unknown arm7 read32 %08X | %08X\n", addr, ARM7->R[15]);
@ -2226,6 +2378,27 @@ void ARM7Write8(u32 addr, u8 val)
case 0x06800000:
GPU::WriteVRAM_ARM7<u8>(addr, val);
return;
case 0x08000000:
case 0x09000000:
if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write
if (GBACart::CartInserted)
{
if ((addr & 0x00FFFFFF) >= 0xC4 && (addr & 0x00FFFFFF) <= 0xC9)
{
GBACart::WriteGPIO(addr & (GBACart::CartROMSize-1), val);
return;
}
}
break;
case 0x0A000000:
if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write
if (GBACart::CartInserted)
{
GBACart_SRAM::Write8(addr & (GBACart_SRAM::SRAMLength-1), val);
}
return;
}
printf("unknown arm7 write8 %08X %02X @ %08X\n", addr, val, ARM7->R[15]);
@ -2272,6 +2445,29 @@ void ARM7Write16(u32 addr, u16 val)
case 0x06800000:
GPU::WriteVRAM_ARM7<u16>(addr, val);
return;
case 0x08000000:
case 0x09000000:
if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write
if (GBACart::CartInserted)
{
// Note: the lower bound is adjusted such that a write starting
// there will hit the first byte of the GPIO region.
if ((addr & 0x00FFFFFF) >= 0xC3 && (addr & 0x00FFFFFF) <= 0xC9)
{
GBACart::WriteGPIO(addr & (GBACart::CartROMSize-1), val);
return;
}
}
break;
case 0x0A000000:
if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write
if (GBACart::CartInserted)
{
GBACart_SRAM::Write16(addr & (GBACart_SRAM::SRAMLength-1), val);
}
return;
}
//printf("unknown arm7 write16 %08X %04X @ %08X\n", addr, val, ARM7->R[15]);
@ -2319,6 +2515,30 @@ void ARM7Write32(u32 addr, u32 val)
case 0x06800000:
GPU::WriteVRAM_ARM7<u32>(addr, val);
return;
case 0x08000000:
case 0x09000000:
if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write
if (GBACart::CartInserted)
{
// Note: the lower bound is adjusted such that a write starting
// there will hit the first byte of the GPIO region.
if ((addr & 0x00FFFFFF) >= 0xC1 && (addr & 0x00FFFFFF) <= 0xC9)
{
GBACart::WriteGPIO(addr & (GBACart::CartROMSize-1), val & 0xFF);
GBACart::WriteGPIO((addr + 2) & (GBACart::CartROMSize-1), (val >> 16) & 0xFF);
return;
}
}
break;
case 0x0A000000:
if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write
if (GBACart::CartInserted)
{
GBACart_SRAM::Write32(addr & (GBACart_SRAM::SRAMLength-1), val);
}
return;
}
//printf("unknown arm7 write32 %08X %08X @ %08X\n", addr, val, ARM7->R[15]);

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.
@ -175,6 +175,7 @@ void SetARM9RegionTimings(u32 addrstart, u32 addrend, int buswidth, int nonseq,
void SetARM7RegionTimings(u32 addrstart, u32 addrend, int buswidth, int nonseq, int seq);
bool LoadROM(const char* path, const char* sram, bool direct);
bool LoadGBAROM(const char* path, const char* sram);
void LoadBIOS();
void SetupDirectBoot();
void RelocateSave(const char* path, bool write);
@ -214,6 +215,8 @@ u32 GetPC(u32 cpu);
u64 GetSysClockCycles(int num);
void NocashPrint(u32 cpu, u32 addr);
void MonitorARM9Jump(u32 addr);
bool DMAsInMode(u32 cpu, u32 mode);
bool DMAsRunning(u32 cpu);
void CheckDMAs(u32 cpu, u32 mode);

View File

@ -50,7 +50,6 @@ 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()
@ -401,7 +400,7 @@ void Write(u8 val, u32 hold)
switch (CurCmd)
{
case 0x00:
// Pokémon carts have an IR transceiver thing, and send this
// Pokémon carts have an IR transceiver thing, and send this
// to bypass it and access SRAM.
// TODO: design better
CurCmd = val;
@ -818,7 +817,7 @@ bool ReadROMParams(u32 gamecode, u32* params)
// [gamecode] [ROM size] [save type] [reserved]
// list must be sorted by gamecode
FILE* f = Platform::OpenLocalFile("romlist.bin", "rb");
FILE* f = Platform::OpenDataFile("romlist.bin");
if (!f) return false;
fseek(f, 0, SEEK_END);
@ -869,6 +868,35 @@ bool ReadROMParams(u32 gamecode, u32* params)
}
void DecryptSecureArea(u8* out)
{
u32 gamecode = *(u32*)&CartROM[0x0C];
u32 arm9base = *(u32*)&CartROM[0x20];
memcpy(out, &CartROM[arm9base], 0x800);
Key1_InitKeycode(gamecode, 2, 2);
Key1_Decrypt((u32*)&out[0]);
Key1_InitKeycode(gamecode, 3, 2);
for (u32 i = 0; i < 0x800; i += 8)
Key1_Decrypt((u32*)&out[i]);
if (!strncmp((const char*)out, "encryObj", 8))
{
printf("Secure area decryption OK\n");
*(u32*)&out[0] = 0xE7FFDEFF;
*(u32*)&out[4] = 0xE7FFDEFF;
}
else
{
printf("Secure area decryption failed\n");
for (u32 i = 0; i < 0x800; i += 4)
*(u32*)&out[i] = 0xE7FFDEFF;
}
}
bool LoadROM(const char* path, const char* sram, bool direct)
{
// TODO: streaming mode? for really big ROMs or systems with limited RAM
@ -945,28 +973,8 @@ bool LoadROM(const char* path, const char* sram, bool direct)
printf("Cart ID: %08X\n", CartID);
if (*(u32*)&CartROM[0x20] < 0x4000)
{
//ApplyDLDIPatch();
}
if (direct)
{
// TODO: in the case of an already-encrypted secure area, direct boot
// needs it decrypted
NDS::SetupDirectBoot();
CmdEncMode = 2;
}
CartInserted = true;
// TODO: support more fancy cart types (homebrew?, flashcarts, etc)
if (CartID & 0x08000000)
ROMCommandHandler = ROMCommand_RetailNAND;
else
ROMCommandHandler = ROMCommand_Retail;
u32 arm9base = *(u32*)&CartROM[0x20];
if (arm9base < 0x8000)
{
if (arm9base >= 0x4000)
@ -987,22 +995,30 @@ bool LoadROM(const char* path, const char* sram, bool direct)
}
}
else
{
CartIsHomebrew = true;
//ApplyDLDIPatch();
}
}
// re-encrypt modcrypt areas if needed
// TODO: somehow detect whether those are already encrypted
if (true)
if (direct)
{
u32 mod1 = *(u32*)&CartROM[0x220];
u32 mod2 = *(u32*)&CartROM[0x228];
printf("Re-encrypting modcrypt areas: %08X, %08X\n", mod1, mod2);
if (mod1) ApplyModcrypt(mod1, *(u32*)&CartROM[0x224], &CartROM[0x300]);
if (mod2) ApplyModcrypt(mod2, *(u32*)&CartROM[0x22C], &CartROM[0x314]);
// TODO: in the case of an already-encrypted secure area, direct boot
// needs it decrypted
NDS::SetupDirectBoot();
CmdEncMode = 2;
}
CartInserted = true;
// TODO: support more fancy cart types (homebrew?, flashcarts, etc)
if (CartID & 0x08000000)
ROMCommandHandler = ROMCommand_RetailNAND;
else
ROMCommandHandler = ROMCommand_Retail;
// encryption
Key1_InitKeycode(gamecode, 2, 2);
// save
printf("Save file: %s\n", sram);

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.
@ -44,6 +44,7 @@ void Reset();
void DoSavestate(Savestate* file);
void DecryptSecureArea(u8* out);
bool LoadROM(const char* path, const char* sram, bool direct);
void RelocateSave(const char* path, bool write);

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.
@ -32,15 +32,21 @@ void StopEmu();
// can be optionally restricted to only opening a file that already exists.
// * OpenLocalFile():
// opens files local to the emulator (melonDS.ini, BIOS, firmware, ...)
// checks, by order of priority:
// For Windows builds, or portable UNIX builds it checks, by order of priority:
// * current working directory
// * emulator directory (essentially where the melonDS executable is) if supported
// * any platform-specific application data directories
// in create mode, if the file doesn't exist, it will be created in the emulator
// directory if supported, or in the current directory otherwise
// For regular UNIX builds, the user's configuration directory is always used.
// * OpenDataFile():
// Opens a file that was installed alongside melonDS on UNIX systems in /usr/share, etc.
// Looks in the user's data directory first, then the system's.
// If on Windows or a portable UNIX build, this simply calls OpenLocalFile().
FILE* OpenFile(const char* path, const char* mode, bool mustexist=false);
FILE* OpenLocalFile(const char* path, const char* mode);
FILE* OpenDataFile(const char* path);
inline bool FileExists(const char* name)
{

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.
@ -28,11 +28,7 @@
04 - version major
06 - version minor
08 - length
0C - game serial
10 - ARM9 binary checksum
14 - ARM7 binary checksum
18 - reserved
1C - reserved
0C - reserved (should be game serial later!)
section header:
00 - section magic

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.
@ -22,8 +22,8 @@
#include <stdio.h>
#include "types.h"
#define SAVESTATE_MAJOR 4
#define SAVESTATE_MINOR 1
#define SAVESTATE_MAJOR 6
#define SAVESTATE_MINOR 0
class Savestate
{

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -22,23 +22,24 @@ option(BUILD_SHARED_LIBS "Whether to build libui as a shared library or a static
set(BUILD_SHARED_LIBS OFF)
add_subdirectory(libui)
find_package(SDL2 REQUIRED)
include_directories(${SDL2_INCLUDE_DIR})
#string(STRIP ${SDL2_LIBRARIES} SDL2_LIBRARIES)
find_package(PkgConfig REQUIRED)
pkg_check_modules(SDL2 REQUIRED sdl2)
add_executable(melonDS ${SOURCES_LIBUI})
target_link_libraries(melonDS
core ${SDL2_LIBRARIES} libui)
target_include_directories(melonDS PRIVATE ${SDL2_INCLUDE_DIRS})
target_link_libraries(melonDS core libui ${SDL2_LIBRARIES})
if (UNIX)
option(UNIX_PORTABLE "Make a portable build that looks for its configuration in the current directory" OFF)
if (UNIX_PORTABLE)
add_definitions(-DUNIX_PORTABLE)
endif()
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK3 REQUIRED gtk+-3.0)
pkg_check_modules(SDL2 REQUIRED sdl2)
target_include_directories(melonDS
PRIVATE ${GTK3_INCLUDE_DIRS} ${SDL2_INCLUDE_DIRS}
)
target_link_libraries(melonDS ${GTK3_LIBRARIES} ${SDL2_LIBRARIES})
target_include_directories(melonDS PRIVATE ${GTK3_INCLUDE_DIRS})
target_link_libraries(melonDS ${GTK3_LIBRARIES})
ADD_DEFINITIONS(${GTK3_CFLAGS_OTHER})
@ -58,7 +59,15 @@ if (UNIX)
elseif (WIN32)
target_sources(melonDS PUBLIC "${CMAKE_SOURCE_DIR}/melon.rc")
target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..")
target_link_libraries(melonDS comctl32 d2d1 dwrite uxtheme ws2_32 iphlpapi)
target_link_libraries(melonDS comctl32 d2d1 dwrite uxtheme ws2_32 iphlpapi gdi32)
endif ()
install(FILES ../../net.kuribo64.melonDS.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications)
install(FILES ../../icon/melon_16x16.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/16x16/apps RENAME net.kuribo64.melonDS.png)
install(FILES ../../icon/melon_32x32.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/32x32/apps RENAME net.kuribo64.melonDS.png)
install(FILES ../../icon/melon_48x48.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/48x48/apps RENAME net.kuribo64.melonDS.png)
install(FILES ../../icon/melon_64x64.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/64x64/apps RENAME net.kuribo64.melonDS.png)
install(FILES ../../icon/melon_128x128.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/128x128/apps RENAME net.kuribo64.melonDS.png)
install(FILES ../../icon/melon_256x256.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/256x256/apps RENAME net.kuribo64.melonDS.png)
install(FILES ../../romlist.bin DESTINATION ${CMAKE_INSTALL_PREFIX}/share/melonDS)
install(TARGETS melonDS RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.
@ -71,7 +71,9 @@ char hotkeylabels[HK_MAX][32] =
"Pause/resume:",
"Reset:",
"Fast forward:",
"Fast forward (toggle):"
"Fast forward (toggle):",
"Decrease sunlight (Boktai):",
"Increase sunlight (Boktai):"
};
int openedmask;
@ -514,7 +516,7 @@ void Open(int type)
memcpy(dlg->keymap, Config::HKKeyMapping, sizeof(int)*HK_MAX);
memcpy(dlg->joymap, Config::HKJoyMapping, sizeof(int)*HK_MAX);
dlg->win = uiNewWindow("Hotkey config - melonDS", 600, 100, 0, 0, 0);
dlg->win = uiNewWindow("Hotkey config - melonDS", 700, 100, 0, 0, 0);
}
uiControl(dlg->win)->UserData = dlg;

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.
@ -133,7 +133,7 @@ bool Init(bool open_adapter)
return false;
}
}
PCapAdapter = NULL;
PacketLen = 0;
RXNum = 0;
@ -172,7 +172,7 @@ bool Init(bool open_adapter)
#else
strncpy(adata->DeviceName, dev->name, 127);
adata->DeviceName[127] = '\0';
strncpy(adata->FriendlyName, adata->DeviceName, 127);
adata->FriendlyName[127] = '\0';
#endif // __WIN32__
@ -250,7 +250,7 @@ bool Init(bool open_adapter)
printf("getifaddrs() shat itself :(\n");
return false;
}
for (int i = 0; i < NumAdapters; i++)
{
adata = &Adapters[i];
@ -262,8 +262,14 @@ bool Init(bool open_adapter)
curaddr = curaddr->ifa_next;
continue;
}
if (!curaddr->ifa_addr) continue;
if (!curaddr->ifa_addr)
{
printf("Device (%s) does not have an address :/\n", curaddr->ifa_name);
curaddr = curaddr->ifa_next;
continue;
}
u16 af = curaddr->ifa_addr->sa_family;
if (af == AF_INET)
{
@ -273,23 +279,23 @@ bool Init(bool open_adapter)
else if (af == AF_PACKET)
{
struct sockaddr_ll* sa = (sockaddr_ll*)curaddr->ifa_addr;
if (sa->sll_halen != 6)
if (sa->sll_halen != 6)
printf("weird MAC length %d for %s\n", sa->sll_halen, curaddr->ifa_name);
else
memcpy(adata->MAC, sa->sll_addr, 6);
}
curaddr = curaddr->ifa_next;
}
}
freeifaddrs(addrs);
#endif // __WIN32__
if (!open_adapter) return true;
if (PCapAdapter) pcap_close(PCapAdapter);
// open pcap device
PCapAdapterData = &Adapters[0];
for (int i = 0; i < NumAdapters; i++)

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.
@ -336,8 +336,8 @@ void Update()
colA &= mask;
colB &= mask;
if (colA == colB) WinBitmapData[(y*768) + x + 512] = 0xFF00FF00;
else WinBitmapData[(y*768) + x + 512] = 0xFFFF0000;
if (colA == colB) WinBitmapData[(y*768) + x + 512] = 0xFF000000;//0xFF00FF00;
else WinBitmapData[(y*768) + x + 512] = 0xFFFFFFFF;//0xFFFF0000;
}
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.
@ -135,6 +135,66 @@ FILE* OpenFile(const char* path, const char* mode, bool mustexist)
return ret;
}
#if !defined(UNIX_PORTABLE) && !defined(__WIN32__)
FILE* OpenLocalFile(const char* path, const char* mode)
{
std::string fullpath;
if (path[0] == '/')
{
// If it's an absolute path, just open that.
fullpath = std::string(path);
}
else
{
// Check user configuration directory
std::string confpath = std::string(g_get_user_config_dir()) + "/melonDS/";
g_mkdir_with_parents(confpath.c_str(), 0755);
fullpath = confpath + path;
}
return OpenFile(fullpath.c_str(), mode, mode[0] != 'w');
}
FILE* OpenDataFile(const char* path)
{
const char* melondir = "melonDS";
const char* const* sys_dirs = g_get_system_data_dirs();
const char* user_dir = g_get_user_data_dir();
// First check the user's data directory
char* fullpath = g_build_path("/", user_dir, melondir, path, NULL);
if (access(fullpath, R_OK) == 0)
{
FILE* f = fopen(fullpath, "r");
g_free(fullpath);
return f;
}
free(fullpath);
// Then check the system data directories
for (size_t i = 0; sys_dirs[i] != NULL; i++)
{
const char* dir = sys_dirs[i];
char* fullpath = g_build_path("/", dir, melondir, path, NULL);
if (access(fullpath, R_OK) == 0)
{
FILE* f = fopen(fullpath, "r");
g_free(fullpath);
return f;
}
free(fullpath);
}
FILE* f = fopen(path, "rb");
if (f) return f;
return NULL;
}
#else
FILE* OpenLocalFile(const char* path, const char* mode)
{
bool relpath = false;
@ -178,7 +238,7 @@ FILE* OpenLocalFile(const char* path, const char* mode)
emudirpath[pathlen] = '\0';
}
// Locations are application directory, and AppData/melonDS on Windows or XDG_CONFIG_HOME/melonds on Linux
// Locations are application directory, and AppData/melonDS on Windows or XDG_CONFIG_HOME/melonDS on Linux
FILE* f;
@ -240,7 +300,7 @@ FILE* OpenLocalFile(const char* path, const char* mode)
{
// Now check XDG_CONFIG_HOME
// TODO: check for memory leak there
std::string fullpath = std::string(g_get_user_config_dir()) + "/melonds/" + path;
std::string fullpath = std::string(g_get_user_config_dir()) + "/melonDS/" + path;
f = OpenFile(fullpath.c_str(), mode, true);
if (f) { delete[] emudirpath; return f; }
}
@ -257,6 +317,13 @@ FILE* OpenLocalFile(const char* path, const char* mode)
return NULL;
}
FILE* OpenDataFile(const char* path)
{
return OpenLocalFile(path, "rb");
}
#endif
void* Thread_Create(void (*func)())
{

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.
@ -93,25 +93,29 @@ ConfigEntry PlatformConfigFile[] =
{"Joy_X", 0, &JoyMapping[10], -1, NULL, 0},
{"Joy_Y", 0, &JoyMapping[11], -1, NULL, 0},
{"HKKey_Lid", 0, &HKKeyMapping[HK_Lid], 0x0D, NULL, 0},
{"HKKey_Mic", 0, &HKKeyMapping[HK_Mic], 0x35, NULL, 0},
{"HKKey_Pause", 0, &HKKeyMapping[HK_Pause], -1, NULL, 0},
{"HKKey_Reset", 0, &HKKeyMapping[HK_Reset], -1, NULL, 0},
{"HKKey_FastForward", 0, &HKKeyMapping[HK_FastForward], 0x0F, NULL, 0},
{"HKKey_FastForwardToggle", 0, &HKKeyMapping[HK_FastForwardToggle], -1, NULL, 0},
{"HKKey_Lid", 0, &HKKeyMapping[HK_Lid], 0x0D, NULL, 0},
{"HKKey_Mic", 0, &HKKeyMapping[HK_Mic], 0x35, NULL, 0},
{"HKKey_Pause", 0, &HKKeyMapping[HK_Pause], -1, NULL, 0},
{"HKKey_Reset", 0, &HKKeyMapping[HK_Reset], -1, NULL, 0},
{"HKKey_FastForward", 0, &HKKeyMapping[HK_FastForward], 0x0F, NULL, 0},
{"HKKey_FastForwardToggle", 0, &HKKeyMapping[HK_FastForwardToggle], -1, NULL, 0},
{"HKKey_SolarSensorDecrease", 0, &HKKeyMapping[HK_SolarSensorDecrease], 0x4B, NULL, 0},
{"HKKey_SolarSensorIncrease", 0, &HKKeyMapping[HK_SolarSensorIncrease], 0x4D, NULL, 0},
{"HKJoy_Lid", 0, &HKJoyMapping[HK_Lid], -1, NULL, 0},
{"HKJoy_Mic", 0, &HKJoyMapping[HK_Mic], -1, NULL, 0},
{"HKJoy_Pause", 0, &HKJoyMapping[HK_Pause], -1, NULL, 0},
{"HKJoy_Reset", 0, &HKJoyMapping[HK_Reset], -1, NULL, 0},
{"HKJoy_FastForward", 0, &HKJoyMapping[HK_FastForward], -1, NULL, 0},
{"HKJoy_FastForwardToggle", 0, &HKJoyMapping[HK_FastForwardToggle], -1, NULL, 0},
{"HKJoy_Lid", 0, &HKJoyMapping[HK_Lid], -1, NULL, 0},
{"HKJoy_Mic", 0, &HKJoyMapping[HK_Mic], -1, NULL, 0},
{"HKJoy_Pause", 0, &HKJoyMapping[HK_Pause], -1, NULL, 0},
{"HKJoy_Reset", 0, &HKJoyMapping[HK_Reset], -1, NULL, 0},
{"HKJoy_FastForward", 0, &HKJoyMapping[HK_FastForward], -1, NULL, 0},
{"HKJoy_FastForwardToggle", 0, &HKJoyMapping[HK_FastForwardToggle], -1, NULL, 0},
{"HKJoy_SolarSensorDecrease", 0, &HKJoyMapping[HK_SolarSensorDecrease], -1, NULL, 0},
{"HKJoy_SolarSensorIncrease", 0, &HKJoyMapping[HK_SolarSensorIncrease], -1, NULL, 0},
{"JoystickID", 0, &JoystickID, 0, NULL, 0},
{"WindowWidth", 0, &WindowWidth, 256, NULL, 0},
{"WindowHeight", 0, &WindowHeight, 384, NULL, 0},
{"WindowMax", 0, &WindowMaximized, 0, NULL, 0},
{"WindowMax", 0, &WindowMaximized, 0, NULL, 0},
{"ScreenRotation", 0, &ScreenRotation, 0, NULL, 0},
{"ScreenGap", 0, &ScreenGap, 0, NULL, 0},

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.
@ -29,6 +29,8 @@ enum
HK_Reset,
HK_FastForward,
HK_FastForwardToggle,
HK_SolarSensorDecrease,
HK_SolarSensorIncrease,
HK_MAX
};

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.
@ -21,6 +21,10 @@
#include <stdio.h>
#include <string.h>
#ifndef __WIN32__
#include <glib.h>
#endif
#include <SDL2/SDL.h>
#include "libui/ui.h"
@ -38,6 +42,7 @@
#include "DlgWifiSettings.h"
#include "../NDS.h"
#include "../GBACart.h"
#include "../GPU.h"
#include "../SPU.h"
#include "../Wifi.h"
@ -105,9 +110,9 @@ int EmuRunning;
volatile int EmuStatus;
bool RunningSomething;
char ROMPath[1024];
char SRAMPath[1024];
char PrevSRAMPath[1024]; // for savestate 'undo load'
char ROMPath[2][1024];
char SRAMPath[2][1024];
char PrevSRAMPath[2][1024]; // for savestate 'undo load'
bool SavestateLoaded;
@ -184,7 +189,7 @@ void SetupScreenRects(int width, int height);
void TogglePause(void* blarg);
void Reset(void* blarg);
void SetupSRAMPath();
void SetupSRAMPath(int slot);
void SaveState(int slot);
void LoadState(int slot);
@ -954,6 +959,24 @@ int EmuThreadFunc(void* burp)
if (HotkeyPressed(HK_Pause)) uiQueueMain(TogglePause, NULL);
if (HotkeyPressed(HK_Reset)) uiQueueMain(Reset, NULL);
if (GBACart::CartInserted && GBACart::HasSolarSensor)
{
if (HotkeyPressed(HK_SolarSensorDecrease))
{
if (GBACart_SolarSensor::LightLevel > 0) GBACart_SolarSensor::LightLevel--;
char msg[64];
sprintf(msg, "Solar sensor level set to %d", GBACart_SolarSensor::LightLevel);
OSD::AddMessage(0, msg);
}
if (HotkeyPressed(HK_SolarSensorIncrease))
{
if (GBACart_SolarSensor::LightLevel < 10) GBACart_SolarSensor::LightLevel++;
char msg[64];
sprintf(msg, "Solar sensor level set to %d", GBACart_SolarSensor::LightLevel);
OSD::AddMessage(0, msg);
}
}
if (EmuRunning == 1)
{
EmuStatus = 1;
@ -1661,12 +1684,18 @@ void Reset(void* blarg)
SavestateLoaded = false;
uiMenuItemDisable(MenuItem_UndoStateLoad);
if (ROMPath[0] == '\0')
if (ROMPath[0][0] == '\0')
NDS::LoadBIOS();
else
{
SetupSRAMPath();
NDS::LoadROM(ROMPath, SRAMPath, Config::DirectBoot);
SetupSRAMPath(0);
NDS::LoadROM(ROMPath[0], SRAMPath[0], Config::DirectBoot);
}
if (ROMPath[1][0] != '\0')
{
SetupSRAMPath(1);
NDS::LoadGBAROM(ROMPath[1], SRAMPath[1]);
}
Run();
@ -1681,6 +1710,10 @@ void Stop(bool internal)
while (EmuStatus != 2);
RunningSomething = false;
// eject any inserted GBA cartridge
GBACart::Eject();
ROMPath[1][0] = '\0';
uiWindowSetTitle(MainWindow, "melonDS " MELONDS_VERSION);
for (int i = 0; i < 9; i++) uiMenuItemDisable(MenuItem_SaveStateSlot[i]);
@ -1701,41 +1734,53 @@ void Stop(bool internal)
OSD::AddMessage(0xFFC040, "Shutdown");
}
void SetupSRAMPath()
void SetupSRAMPath(int slot)
{
strncpy(SRAMPath, ROMPath, 1023);
SRAMPath[1023] = '\0';
strncpy(SRAMPath + strlen(ROMPath) - 3, "sav", 3);
strncpy(SRAMPath[slot], ROMPath[slot], 1023);
SRAMPath[slot][1023] = '\0';
strncpy(SRAMPath[slot] + strlen(ROMPath[slot]) - 3, "sav", 3);
}
void TryLoadROM(char* file, int prevstatus)
void TryLoadROM(char* file, int slot, int prevstatus)
{
char oldpath[1024];
char oldsram[1024];
strncpy(oldpath, ROMPath, 1024);
strncpy(oldsram, SRAMPath, 1024);
strncpy(oldpath, ROMPath[slot], 1024);
strncpy(oldsram, SRAMPath[slot], 1024);
strncpy(ROMPath, file, 1023);
ROMPath[1023] = '\0';
strncpy(ROMPath[slot], file, 1023);
ROMPath[slot][1023] = '\0';
SetupSRAMPath();
SetupSRAMPath(0);
SetupSRAMPath(1);
if (NDS::LoadROM(ROMPath, SRAMPath, Config::DirectBoot))
if (slot == 0 && NDS::LoadROM(ROMPath[slot], SRAMPath[slot], Config::DirectBoot))
{
SavestateLoaded = false;
uiMenuItemDisable(MenuItem_UndoStateLoad);
strncpy(PrevSRAMPath, SRAMPath, 1024); // safety
// Reload the inserted GBA cartridge (if any)
if (ROMPath[1][0] != '\0') NDS::LoadGBAROM(ROMPath[1], SRAMPath[1]);
strncpy(PrevSRAMPath[slot], SRAMPath[slot], 1024); // safety
Run();
}
else if (slot == 1 && NDS::LoadGBAROM(ROMPath[slot], SRAMPath[slot]))
{
SavestateLoaded = false;
uiMenuItemDisable(MenuItem_UndoStateLoad);
strncpy(PrevSRAMPath[slot], SRAMPath[slot], 1024); // safety
if (RunningSomething) Run(); // do not start just from a GBA cart
}
else
{
uiMsgBoxError(MainWindow,
"Failed to load the ROM",
"Make sure the file can be accessed and isn't opened in another application.");
strncpy(ROMPath, oldpath, 1024);
strncpy(SRAMPath, oldsram, 1024);
strncpy(ROMPath[slot], oldpath, 1024);
strncpy(SRAMPath[slot], oldsram, 1024);
EmuRunning = prevstatus;
}
}
@ -1748,22 +1793,22 @@ void GetSavestateName(int slot, char* filename, int len)
{
int pos;
if (ROMPath[0] == '\0') // running firmware, no ROM
if (ROMPath[0][0] == '\0') // running firmware, no ROM
{
strcpy(filename, "firmware");
pos = 8;
}
else
{
int l = strlen(ROMPath);
int l = strlen(ROMPath[0]);
pos = l;
while (ROMPath[pos] != '.' && pos > 0) pos--;
while (ROMPath[0][pos] != '.' && pos > 0) pos--;
if (pos == 0) pos = l;
// avoid buffer overflow. shoddy
if (pos > len-5) pos = len-5;
strncpy(&filename[0], ROMPath, pos);
strncpy(&filename[0], ROMPath[0], pos);
}
strcpy(&filename[pos], ".ml");
filename[pos+3] = '0'+slot;
@ -1807,6 +1852,8 @@ void LoadState(int slot)
return;
}
u32 oldGBACartCRC = GBACart::CartCRC;
// backup
Savestate* backup = new Savestate("timewarp.mln", true);
NDS::DoSavestate(backup);
@ -1831,21 +1878,36 @@ void LoadState(int slot)
if (!failed)
{
if (Config::SavestateRelocSRAM && ROMPath[0]!='\0')
if (Config::SavestateRelocSRAM && ROMPath[0][0]!='\0')
{
strncpy(PrevSRAMPath, SRAMPath, 1024);
strncpy(PrevSRAMPath[0], SRAMPath[0], 1024);
strncpy(SRAMPath, filename, 1019);
int len = strlen(SRAMPath);
strcpy(&SRAMPath[len], ".sav");
SRAMPath[len+4] = '\0';
strncpy(SRAMPath[0], filename, 1019);
int len = strlen(SRAMPath[0]);
strcpy(&SRAMPath[0][len], ".sav");
SRAMPath[0][len+4] = '\0';
NDS::RelocateSave(SRAMPath, false);
NDS::RelocateSave(SRAMPath[0], false);
}
bool loadedPartialGBAROM = false;
// in case we have a GBA cart inserted, and the GBA ROM changes
// due to having loaded a save state, we do not want to reload
// the previous cartridge on reset, or commit writes to any
// loaded save file. therefore, their paths are "nulled".
if (GBACart::CartInserted && GBACart::CartCRC != oldGBACartCRC)
{
ROMPath[1][0] = '\0';
SRAMPath[1][0] = '\0';
loadedPartialGBAROM = true;
}
char msg[64];
if (slot > 0) sprintf(msg, "State loaded from slot %d", slot);
else sprintf(msg, "State loaded from file");
if (slot > 0) sprintf(msg, "State loaded from slot %d%s",
slot, loadedPartialGBAROM ? " (GBA ROM header only)" : "");
else sprintf(msg, "State loaded from file%s",
loadedPartialGBAROM ? " (GBA ROM header only)" : "");
OSD::AddMessage(0, msg);
SavestateLoaded = true;
@ -1896,14 +1958,14 @@ void SaveState(int slot)
if (slot > 0)
uiMenuItemEnable(MenuItem_LoadStateSlot[slot-1]);
if (Config::SavestateRelocSRAM && ROMPath[0]!='\0')
if (Config::SavestateRelocSRAM && ROMPath[0][0]!='\0')
{
strncpy(SRAMPath, filename, 1019);
int len = strlen(SRAMPath);
strcpy(&SRAMPath[len], ".sav");
SRAMPath[len+4] = '\0';
strncpy(SRAMPath[0], filename, 1019);
int len = strlen(SRAMPath[0]);
strcpy(&SRAMPath[0][len], ".sav");
SRAMPath[0][len+4] = '\0';
NDS::RelocateSave(SRAMPath, true);
NDS::RelocateSave(SRAMPath[0], true);
}
}
@ -1930,10 +1992,10 @@ void UndoStateLoad()
NDS::DoSavestate(backup);
delete backup;
if (ROMPath[0]!='\0')
if (ROMPath[0][0]!='\0')
{
strncpy(SRAMPath, PrevSRAMPath, 1024);
NDS::RelocateSave(SRAMPath, false);
strncpy(SRAMPath[0], PrevSRAMPath[0], 1024);
NDS::RelocateSave(SRAMPath[0], false);
}
OSD::AddMessage(0, "State load undone");
@ -1977,7 +2039,11 @@ void OnDropFile(uiWindow* window, char* file, void* blarg)
while (EmuStatus != 2);
}
TryLoadROM(file, prevstatus);
TryLoadROM(file, 0, prevstatus);
}
else if (!strcasecmp(ext, "gba"))
{
TryLoadROM(file, 1, prevstatus);
}
}
@ -2008,7 +2074,7 @@ void OnOpenFile(uiMenuItem* item, uiWindow* window, void* blarg)
EmuRunning = 2;
while (EmuStatus != 2);
char* file = uiOpenFile(window, "DS ROM (*.nds)|*.nds;*.srl|Any file|*.*", Config::LastROMFolder);
char* file = uiOpenFile(window, "DS ROM (*.nds)|*.nds;*.srl|GBA ROM (*.gba)|*.gba|Any file|*.*", Config::LastROMFolder);
if (!file)
{
EmuRunning = prevstatus;
@ -2019,8 +2085,17 @@ void OnOpenFile(uiMenuItem* item, uiWindow* window, void* blarg)
while (file[pos] != '/' && file[pos] != '\\' && pos > 0) pos--;
strncpy(Config::LastROMFolder, file, pos);
Config::LastROMFolder[pos] = '\0';
char* ext = &file[strlen(file)-3];
if (!strcasecmp(ext, "gba"))
{
TryLoadROM(file, 1, prevstatus);
}
else
{
TryLoadROM(file, 0, prevstatus);
}
TryLoadROM(file, prevstatus);
uiFreeText(file);
}
@ -2045,8 +2120,14 @@ void OnRun(uiMenuItem* item, uiWindow* window, void* blarg)
{
if (!RunningSomething)
{
ROMPath[0] = '\0';
ROMPath[0][0] = '\0';
NDS::LoadBIOS();
if (ROMPath[1][0] != '\0')
{
SetupSRAMPath(1);
NDS::LoadGBAROM(ROMPath[1], SRAMPath[1]);
}
}
Run();
@ -2602,6 +2683,7 @@ int main(int argc, char** argv)
printf("melonDS " MELONDS_VERSION "\n");
printf(MELONDS_URL "\n");
#if defined(__WIN32__) || defined(UNIX_PORTABLE)
if (argc > 0 && strlen(argv[0]) > 0)
{
int len = strlen(argv[0]);
@ -2628,6 +2710,13 @@ int main(int argc, char** argv)
EmuDirectory = new char[2];
strcpy(EmuDirectory, ".");
}
#else
const char* confdir = g_get_user_config_dir();
const char* confname = "/melonDS";
EmuDirectory = new char[strlen(confdir) + strlen(confname) + 1];
strcat(EmuDirectory, confdir);
strcat(EmuDirectory, confname);
#endif
// http://stackoverflow.com/questions/14543333/joystick-wont-work-using-sdl
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
@ -2663,15 +2752,23 @@ int main(int argc, char** argv)
!Platform::LocalFileExists("bios9.bin") ||
!Platform::LocalFileExists("firmware.bin"))
{
uiMsgBoxError(
NULL,
"BIOS/Firmware not found",
#if defined(__WIN32__) || defined(UNIX_PORTABLE)
const char* locationName = "the directory you run melonDS from";
#else
char* locationName = EmuDirectory;
#endif
char msgboxtext[512];
sprintf(msgboxtext,
"One or more of the following required files don't exist or couldn't be accessed:\n\n"
"bios7.bin -- ARM7 BIOS\n"
"bios9.bin -- ARM9 BIOS\n"
"firmware.bin -- firmware image\n\n"
"Dump the files from your DS and place them in the directory you run melonDS from.\n"
"Make sure that the files can be accessed.");
"Dump the files from your DS and place them in %s.\n"
"Make sure that the files can be accessed.",
locationName
);
uiMsgBoxError(NULL, "BIOS/Firmware not found", msgboxtext);
uiUninit();
SDL_Quit();
@ -2717,7 +2814,18 @@ int main(int argc, char** argv)
}
}
{
FILE* f = Platform::OpenLocalFile("romlist.bin", "rb");
const char* romlist_missing = "Save memory type detection will not work correctly.\n\n"
"You should use the latest version of romlist.bin (provided in melonDS release packages).";
#if !defined(UNIX_PORTABLE) && !defined(__WIN32__)
std::string missingstr = std::string(romlist_missing) +
"\n\nThe ROM list should be placed in " + g_get_user_data_dir() + "/melonds/, otherwise "
"melonDS will search for it in the current working directory.";
const char* romlist_missing_text = missingstr.c_str();
#else
const char* romlist_missing_text = romlist_missing;
#endif
FILE* f = Platform::OpenDataFile("romlist.bin");
if (f)
{
u32 data;
@ -2726,18 +2834,12 @@ int main(int argc, char** argv)
if ((data >> 24) == 0) // old CRC-based list
{
uiMsgBoxError(NULL,
"Your version of romlist.bin is outdated.",
"Save memory type detection will not work correctly.\n\n"
"You should use the latest version of romlist.bin (provided in melonDS release packages).");
uiMsgBoxError(NULL, "Your version of romlist.bin is outdated.", romlist_missing_text);
}
}
else
{
uiMsgBoxError(NULL,
"romlist.bin not found.",
"Save memory type detection will not work correctly.\n\n"
"You should use the latest version of romlist.bin (provided in melonDS release packages).");
uiMsgBoxError(NULL, "romlist.bin not found.", romlist_missing_text);
}
}
@ -2865,14 +2967,30 @@ int main(int argc, char** argv)
if (!strcasecmp(ext, "nds") || !strcasecmp(ext, "srl"))
{
strncpy(ROMPath, file, 1023);
ROMPath[1023] = '\0';
strncpy(ROMPath[0], file, 1023);
ROMPath[0][1023] = '\0';
SetupSRAMPath();
SetupSRAMPath(0);
if (NDS::LoadROM(ROMPath, SRAMPath, Config::DirectBoot))
if (NDS::LoadROM(ROMPath[0], SRAMPath[0], Config::DirectBoot))
Run();
}
if (argc > 2)
{
file = argv[2];
ext = &file[strlen(file)-3];
if (!strcasecmp(ext, "gba"))
{
strncpy(ROMPath[1], file, 1023);
ROMPath[1][1023] = '\0';
SetupSRAMPath(1);
NDS::LoadGBAROM(ROMPath[1], SRAMPath[1]);
}
}
}
uiMain();

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Arisotura
Copyright 2016-2020 Arisotura
This file is part of melonDS.