Merge branch 'master' into feature/optional-libslirp

This commit is contained in:
Nadia Holmquist Pedersen 2021-12-04 12:19:45 +01:00 committed by GitHub
commit e465ceff78
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
143 changed files with 40563 additions and 2783 deletions

View File

@ -0,0 +1,26 @@
trigger:
- master
pool:
name: Default
demands:
- agent.name -equals MacStadium-ARM64-Mac
workspace:
clean: all
steps:
- script: mkdir $(Pipeline.Workspace)/build
displayName: 'Create build environment'
- script: arch -arm64 cmake $(Build.SourcesDirectory) -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_PREFIX_PATH="$(brew --prefix qt@6);$(brew --prefix libarchive)" -DMACOS_BUNDLE_LIBS=ON -DMACOS_BUILD_DMG=ON -DUSE_QT6=ON
displayName: 'Configure'
workingDirectory: $(Pipeline.Workspace)/build
- script: arch -arm64 make -j$(sysctl -n hw.logicalcpu)
displayName: 'Make'
workingDirectory: $(Pipeline.Workspace)/build
- publish: $(Pipeline.Workspace)/build/melonDS.dmg
artifact: melonDS.dmg

View File

@ -5,13 +5,13 @@ pool:
vmImage: macOS-10.14
steps:
- script: brew install sdl2 qt@6 libslirp libarchive libepoxy
- script: brew install llvm sdl2 qt@6 libslirp libarchive libepoxy
displayName: 'Install dependencies'
- script: mkdir $(Pipeline.Workspace)/build
displayName: 'Create build environment'
- script: cmake $(Build.SourcesDirectory) -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_PREFIX_PATH="$(brew --prefix qt@6);$(brew --prefix libarchive)" -DMACOS_BUNDLE_LIBS=ON -DMACOS_BUILD_DMG=ON -DUSE_QT6=ON
- script: cmake $(Build.SourcesDirectory) -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_PREFIX_PATH="$(brew --prefix qt@6);$(brew --prefix libarchive)" -DMACOS_BUNDLE_LIBS=ON -DMACOS_BUILD_DMG=ON -DUSE_QT6=ON -DCMAKE_TOOLCHAIN_FILE=$(Build.SourcesDirectory)/cmake/Toolchain-Homebrew-LLVM.cmake
displayName: 'Configure'
workingDirectory: $(Pipeline.Workspace)/build

2
.gitignore vendored
View File

@ -9,7 +9,9 @@ melon_grc.h
melon.rc
cmake-build
cmake-build-debug
compile_commands.json
.idea
.cache
*.exe

View File

@ -16,7 +16,7 @@ set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
set(MELONDS_VERSION "0.9.2")
set(MELONDS_VERSION "0.9.3")
add_compile_definitions(MELONDS_VERSION="${MELONDS_VERSION}")
string(REPLACE "." ";" VERSION_LIST ${MELONDS_VERSION})
# For the melon.rc file used on Windows
@ -67,6 +67,13 @@ endif()
if (ENABLE_JIT)
add_definitions(-DJIT_ENABLED)
option(ENABLE_JIT_PROFILING "Enable JIT profiling with VTune" OFF)
if (ENABLE_JIT_PROFILING)
include(cmake/FindVTune.cmake)
add_definitions(-DJIT_PROFILING_ENABLED)
endif()
endif()
if (CMAKE_BUILD_TYPE STREQUAL Release)
@ -116,10 +123,19 @@ if (ENABLE_LTO)
if (NOT LLD STREQUAL "LLD-NOTFOUND")
add_link_options(-fuse-ld=lld)
endif()
if (NOT APPLE)
set(CMAKE_AR "llvm-ar")
set(CMAKE_RANLIB "llvm-ranlib")
endif()
endif()
endif()
find_program(CCACHE "ccache")
if (CCACHE)
message(STATUS "Using CCache to speed up compilation")
set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE})
set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE})
endif()
option(BUILD_QT_SDL "Build Qt/SDL frontend" ON)

150
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,150 @@
# Contributor guide for melonDS
Please follow a style as documented here. Note that this guide was not always enforced, so some parts of the code violate it.
Files should use 4 spaces for indentation.
```c++
// for single line comments prefer C++ style
/*
for multiline comments
both C style comments
*/
// as well as
// C++ style comments are possible
// when including headers from the C standard library use their C names (so don't use cstdio/cstring, ...)
#include <stdio.h>
// namespaces in PascalCase
namespace Component
{ // for all constructs curly braces go onto the following line
// the content of namespaces should not be indented
int GlobalVariable; // in PascalCase
// function names should use PascalCase, parameters camelCase:
void Test(int someParam)
{
int variable = someParam * 2; // local variables in camelCase
// you can slightly vary the spacing around operators:
int variable2 = someParam*2 + 1;
// but avoid something like this: someParam* 2+ 3
for (int i = 0; i < variable; i++) // always a space between if/for/while and the braces
{
// not using curly braces is allowed
// if the body of the if/for/while is simple:
if ((i % 2) == 0)
printf("%d\n", i); // no space between the function name and the braces
}
}
// defines should always be in CAPITALISED_SNAKE_CASE
#ifdef SOME_CONFIGURATION
// the content of #ifdef sections should not be indented
// the only exception being otherwise hard to read nested sections of #ifdef/#if/#defines
// as e.g. in ARMJIT_Memory.cpp
// prefer #ifdef/#ifndef, only use if defined(...) for complex expressions like this:
#if defined(THIS) || defined(THAT)
// ...
#endif
class MyClass // PascalCase
{
public: // access specfications are not indented
void Test(int param) // for methods the same rules apply as for functions
{
}
private:
int MemberVariable; // PascalCase, no prefix
};
#endif
#endif
enum
{
// enums should always have a common prefix in camelCase
// separated by an underscore with the item name
// which has to be in PascalCase
enumPrefix_FirstElement,
enumPrefix_SecondElement,
enumPrefix_ThirdElement,
enumPrefix_FourthElement,
};
}
```
Some additional notes:
* Keep the definition and initialisation of local variables in one place and keep the scope of local variables as small as possible.
**That means avoid code like this**:
```cpp
void ColorConvert(u32* dst, u16* vram)
{
u16 color;
u8 r, g, b;
int i;
for (i = 0; i < 256; i++)
{
color = vram[i];
r = (color & 0x001F) << 1;
g = (color & 0x03E0) >> 4;
b = (color & 0x7C00) >> 9;
dst[i] = r | (g << 8) | (b << 16);
}
}
```
**Do this instead:**
```cpp
void ColorConvert(u32* dst, u16* vram)
{
for (int i = 0; i < 256; i++)
{
u16 color = vram[i];
u8 r = (color & 0x001F) << 1;
u8 g = (color & 0x03E0) >> 4;
u8 b = (color & 0x7C00) >> 9;
dst[i] = r | (g << 8) | (b << 16);
}
}
```
* For integer types preferably use the explictly typed ones. We have short aliases for them defined in types.h (for unsigned types: `u8`, `u16`, `u32`, `u16`. For signed `s8`, `s16`, `s32`, `s64`). In some situations like loop variables, using `int` is possible as well.
* Don't overdo object oriented programming. Always try to use a simpler construct first, only use a polymorphic class if a namespace with functions in it doesn't cut it.
* In doubt put a namespace around your part of the code.
* C style strings (and the associated functions from the C standard library) are used in most places. We are thinking about changing this, as C strings are a bit of a hassle to deal with, but for the time being this is what we use.
* Only the C standard IO is used (so use `printf`, `fopen`, … Do not use `std::cout`/`std::ostream`, …).
* Complex C++ containers can be used (`std::vector`, `std::list`, `std::map`, …). `std::array` is usually not used, unless necessary so that the container can be used with other C++ constructs (e.g. `<algorithm>`). Only use them if a C array doesn't cut it.
* File names should be in PascalCase
* If a header file is called MyHeader.h it should be guarded with an ifdef like this:
```cpp
#ifndef MYHEADER_H
#define MYHEADER_H
// ...
#endif
```
* And at last, if you have any questions, visit us on IRC (see the readme)!

View File

@ -1,15 +1,16 @@
<p align="center"><img src="https://raw.githubusercontent.com/StapleButter/melonDS/master/icon/melon_128x128.png"></p>
<p align="center"><img src="https://raw.githubusercontent.com/Arisotura/melonDS/master/res/icon/melon_128x128.png"></p>
<h2 align="center"><b>melonDS</b></h2>
<p align="center">
<a href="http://melonds.kuribo64.net/" alt="melonDS website"><img src="https://img.shields.io/badge/website-melonds.kuribo64.net-%2331352e.svg"></a>
<a href="http://melonds.kuribo64.net/downloads.php" alt="Release: 0.9.2"><img src="https://img.shields.io/badge/release-0.9.2-%235c913b.svg"></a>
<a href="http://melonds.kuribo64.net/downloads.php" alt="Release: 0.9.3"><img src="https://img.shields.io/badge/release-0.9.3-%235c913b.svg"></a>
<a href="https://www.gnu.org/licenses/gpl-3.0" alt="License: GPLv3"><img src="https://img.shields.io/badge/License-GPL%20v3-%23ff554d.svg"></a>
<a href="https://kiwiirc.com/client/irc.badnik.net/?nick=IRC-Source_?#melonds" alt="IRC channel: #melonds"><img src="https://img.shields.io/badge/IRC%20chat-%23melonds-%23dd2e44.svg"></a>
<br>
<a href="https://github.com/Arisotura/melonDS/actions?query=workflow%3A%22CMake+Build+%28Windows+x86-64%29%22+event%3Apush"><img src="https://img.shields.io/github/workflow/status/Arisotura/melonDS/CMake%20Build%20(Windows%20x86-64)?label=Windows%20x86-64&logo=GitHub"></img></a>
<a href="https://github.com/Arisotura/melonDS/actions?query=workflow%3A%22CMake+Build+%28Ubuntu+x86-64%29%22+event%3Apush"><img src="https://img.shields.io/github/workflow/status/Arisotura/melonDS/CMake%20Build%20(Ubuntu%20x86-64)?label=Linux%20x86-64&logo=GitHub"></img></a>
<a href="https://dev.azure.com/melonDS/melonDS/_build?definitionId=1&repositoryFilter=1&branchFilter=2%2C2%2C2%2C2%2C2%2C2%2C2%2C2%2C2%2C2%2C2%2C2%2C2"><img src="https://img.shields.io/azure-devops/build/melonDS/7c9c08a1-669f-42a4-bef4-a6c74eadf723/1/master?label=macOS%20x86-64&logo=Azure%20Pipelines"></img></a>
<a href="https://github.com/Arisotura/melonDS/actions?query=workflow%3A%22CMake+Build+%28Ubuntu+aarch64%29%22+event%3Apush"><img src="https://img.shields.io/github/workflow/status/Arisotura/melonDS/CMake%20Build%20(Ubuntu%20aarch64)?label=Linux%20ARM64&logo=GitHub"></img></a>
<a href="https://dev.azure.com/melonDS/melonDS/_build?definitionId=1&repositoryFilter=1&branchFilter=2%2C2%2C2%2C2%2C2%2C2%2C2%2C2%2C2%2C2%2C2%2C2%2C2"><img src="https://img.shields.io/azure-devops/build/melonDS/7c9c08a1-669f-42a4-bef4-a6c74eadf723/1/master?label=macOS%20x86-64&logo=Azure%20Pipelines"></img></a>
<a href="https://dev.azure.com/melonDS/melonDS/_build?definitionId=2&_a=summary&repositoryFilter=1&branchFilter=2%2C2%2C2%2C2%2C2"><img src="https://img.shields.io/azure-devops/build/melonDS/7c9c08a1-669f-42a4-bef4-a6c74eadf723/2/master?label=macOS%20ARM64&logo=Azure%20Pipelines"></img></a>
</p>
DS emulator, sorta
@ -98,7 +99,7 @@ If everything went well, melonDS should now be in the `dist` folder.
cmake .. -DCMAKE_PREFIX_PATH="$(brew --prefix qt@6);$(brew --prefix libarchive)" -DUSE_QT6=ON -DMACOS_BUNDLE_LIBS=ON
make -j$(sysctl -n hw.logicalcpu)
```
If everything went well, melonDS.app should now be in the curent directory.
If everything went well, melonDS.app should now be in the current directory.
## TODO LIST
@ -125,10 +126,14 @@ If everything went well, melonDS.app should now be in the curent directory.
* limittox for the icon
* All of you comrades who have been testing melonDS, reporting issues, suggesting shit, etc
## License
## Licenses
[![GNU GPLv3 Image](https://www.gnu.org/graphics/gplv3-127x51.png)](http://www.gnu.org/licenses/gpl-3.0.en.html)
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.
### External
* Images used in the Input Config Dialog - see `src/frontend/qt_sdl/InputConfig/resources/LICENSE.md`

5
cmake/FindVTune.cmake Normal file
View File

@ -0,0 +1,5 @@
find_path(VTUNE_PATH "")
include_directories("${VTUNE_PATH}/include")
link_directories("${VTUNE_PATH}/lib64")

View File

@ -0,0 +1,12 @@
# Toolchain file for building with Homebrew's LLVM on macOS
# This is useful on 10.14 where std::filesystem is not supported.
set(CMAKE_C_COMPILER /usr/local/opt/llvm/bin/clang)
set(CMAKE_CXX_COMPILER /usr/local/opt/llvm/bin/clang++)
add_link_options(-L/usr/local/opt/llvm/lib)
# LLVM in Homebrew is built with latest Xcode which has a newer linker than
# what is bundled in the default install of Xcode Command Line Tools, so we
# override it to prevent it passing flags not supported by the system's ld.
add_link_options(-mlinker-version=450)

17
freebios/Makefile Executable file
View File

@ -0,0 +1,17 @@
TC_PREFIX = /home/exophase/pandora-dev
PREFIX = $(TC_PREFIX)/arm-2011.03
AS = $(PREFIX)/bin/arm-none-linux-gnueabi-gcc
OBJCOPY = $(PREFIX)/bin/arm-none-linux-gnueabi-objcopy
BIN_ARM7 = drastic_bios_arm7
BIN_ARM9 = drastic_bios_arm9
all:
$(AS) bios_common.S -DBIOS_ARM7 -march=armv4 -c -Wa,-asl=$(BIN_ARM7).list -o $(BIN_ARM7).o
$(AS) bios_common.S -DBIOS_ARM9 -march=armv5 -c -Wa,-asl=$(BIN_ARM9).list -o $(BIN_ARM9).o
$(OBJCOPY) -O binary $(BIN_ARM7).o $(BIN_ARM7).bin
$(OBJCOPY) -O binary $(BIN_ARM9).o $(BIN_ARM9).bin
clean:
rm -f $(BIN_ARM7).bin $(BIN_ARM7).o $(BIN_ARM9).bin $(BIN_ARM9).o

1135
freebios/bios_common.S Executable file

File diff suppressed because it is too large Load Diff

BIN
freebios/drastic_bios_arm7.bin Executable file

Binary file not shown.

BIN
freebios/drastic_bios_arm9.bin Executable file

Binary file not shown.

View File

@ -0,0 +1,36 @@
Custom NDS ARM7/ARM9 BIOS replacement
Copyright (c) 2013, Gilead Kutnick
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1) Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2) Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
-- Info --
This archive contains source code and assembly for a custom BIOS replacement
for the Nintendo DS system. This code is in no way affiliated with Nintendo
and is not derived from Nintendo's BIOS implementation but has been implemented
using publically available documentation.
It can be assembled using the included Makefile along with a proper ARM gcc
toolchain. Change the first four lines to point to the proper toolchain of your
choice.

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/org/kuriboland/melonDS">
<file preprocess="to-pixdata">icon/melon_16x16.png</file>
<file preprocess="to-pixdata">icon/melon_32x32.png</file>
<file preprocess="to-pixdata">icon/melon_48x48.png</file>
<file preprocess="to-pixdata">icon/melon_64x64.png</file>
<file preprocess="to-pixdata">icon/melon_128x128.png</file>
<file preprocess="to-pixdata">icon/melon_256x256.png</file>
</gresource>
</gresources>

View File

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View File

Before

Width:  |  Height:  |  Size: 730 B

After

Width:  |  Height:  |  Size: 730 B

View File

Before

Width:  |  Height:  |  Size: 146 KiB

After

Width:  |  Height:  |  Size: 146 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

Before

Width:  |  Height:  |  Size: 121 KiB

After

Width:  |  Height:  |  Size: 121 KiB

View File

@ -22,5 +22,17 @@
<true/>
<key>NSMicrophoneUsageDescription</key>
<string>We need microphone access so you can use the emulated DS microphone</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>nds</string>
<string>srl</string>
</array>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
</array>
</dict>
</plist>

View File

@ -2,7 +2,7 @@
#define VFT_APP 0x00000001L
//this will set your .exe icon
100 ICON MOVEABLE PURE LOADONCALL DISCARDABLE "melon.ico"
100 ICON MOVEABLE PURE LOADONCALL DISCARDABLE "res/melon.ico"
//include version information in .exe, modify these values to match your needs
1 VERSIONINFO
@ -16,9 +16,9 @@ FILETYPE VFT_APP
{
VALUE "CompanyName", "Melon Factory of Kuribo64"
VALUE "FileVersion", "${MELONDS_VERSION}"
VALUE "FileDescription", "DS emulator, sorta. also 1st quality melon."
VALUE "FileDescription", "melonDS emulator"
VALUE "InternalName", "SDnolem"
VALUE "LegalCopyright", "2016-2021 Arisotura & co."
VALUE "LegalCopyright", "2016-2021 melonDS team"
VALUE "LegalTrademarks", ""
VALUE "OriginalFilename", "zafkflzdasd.exe"
VALUE "ProductName", "melonDS"
@ -31,4 +31,4 @@ FILETYPE VFT_APP
}
}
1 24 "xp.manifest"
1 24 "res/xp.manifest"

View File

@ -2,7 +2,7 @@
Name=melonDS
GenericName=Nintendo DS Emulator
Comment=A fast and accurate Nintendo DS emulator.
Exec=melonDS
Exec=melonDS %f
Type=Application
Categories=Game;Emulator;
Terminal=false

View File

@ -17,14 +17,13 @@
*/
#include <stdio.h>
#include <algorithm>
#include "NDS.h"
#include "DSi.h"
#include "ARM.h"
#include "ARMInterpreter.h"
#include "Config.h"
#include "AREngine.h"
#include "ARMJIT.h"
#include "Config.h"
#ifdef JIT_ENABLED
#include "ARMJIT.h"
@ -82,6 +81,8 @@ ARMv5::ARMv5() : ARM(0)
#ifndef JIT_ENABLED
DTCM = new u8[DTCMPhysicalSize];
#endif
PU_Map = PU_PrivMap;
}
ARMv4::ARMv4() : ARM(1)
@ -108,6 +109,22 @@ void ARM::Reset()
CPSR = 0x000000D3;
for (int i = 0; i < 7; i++)
R_FIQ[i] = 0;
for (int i = 0; i < 2; i++)
{
R_SVC[i] = 0;
R_ABT[i] = 0;
R_IRQ[i] = 0;
R_UND[i] = 0;
}
R_FIQ[7] = 0x00000010;
R_SVC[2] = 0x00000010;
R_ABT[2] = 0x00000010;
R_IRQ[2] = 0x00000010;
R_UND[2] = 0x00000010;
ExceptionBase = Num ? 0x00000000 : 0xFFFF0000;
CodeMem.Mem = NULL;
@ -145,6 +162,8 @@ void ARMv5::Reset()
GetMemRegion = NDS::ARM9GetMemRegion;
}
PU_Map = PU_PrivMap;
ARM::Reset();
}
@ -194,7 +213,7 @@ void ARM::DoSavestate(Savestate* file)
file->VarArray(R_UND, 3*sizeof(u32));
file->Var32(&CurInstr);
#ifdef JIT_ENABLED
if (!file->Saving && Config::JIT_Enable)
if (!file->Saving && NDS::EnableJIT)
{
// hack, the JIT doesn't really pipeline
// but we still want JIT save states to be
@ -208,10 +227,22 @@ void ARM::DoSavestate(Savestate* file)
if (!file->Saving)
{
CPSR |= 0x00000010;
R_FIQ[7] |= 0x00000010;
R_SVC[2] |= 0x00000010;
R_ABT[2] |= 0x00000010;
R_IRQ[2] |= 0x00000010;
R_UND[2] |= 0x00000010;
if (!Num)
{
SetupCodeMem(R[15]); // should fix it
((ARMv5*)this)->RegionCodeCycles = ((ARMv5*)this)->MemTimings[R[15] >> 12][0];
if ((CPSR & 0x1F) == 0x10)
((ARMv5*)this)->PU_Map = ((ARMv5*)this)->PU_UserMap;
else
((ARMv5*)this)->PU_Map = ((ARMv5*)this)->PU_PrivMap;
}
else
{
@ -302,12 +333,11 @@ void ARMv5::JumpTo(u32 addr, bool restorecpsr)
CPSR &= ~0x20;
}
/*if (!(PU_Map[addr>>12] & 0x04))
if (!(PU_Map[addr>>12] & 0x04))
{
printf("jumped to %08X. very bad\n", addr);
PrefetchAbort();
return;
}*/
}
NDS::MonitorARM9Jump(addr);
}
@ -374,10 +404,16 @@ void ARM::RestoreCPSR()
CPSR = R_SVC[2];
break;
case 0x14:
case 0x15:
case 0x16:
case 0x17:
CPSR = R_ABT[2];
break;
case 0x18:
case 0x19:
case 0x1A:
case 0x1B:
CPSR = R_UND[2];
break;
@ -387,91 +423,87 @@ void ARM::RestoreCPSR()
break;
}
CPSR |= 0x00000010;
UpdateMode(oldcpsr, CPSR);
}
void ARM::UpdateMode(u32 oldmode, u32 newmode)
void ARM::UpdateMode(u32 oldmode, u32 newmode, bool phony)
{
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]);
std::swap(R[8], R_FIQ[0]);
std::swap(R[9], R_FIQ[1]);
std::swap(R[10], R_FIQ[2]);
std::swap(R[11], R_FIQ[3]);
std::swap(R[12], R_FIQ[4]);
std::swap(R[13], R_FIQ[5]);
std::swap(R[14], R_FIQ[6]);
break;
case 0x12:
SWAP(R[13], R_IRQ[0]);
SWAP(R[14], R_IRQ[1]);
std::swap(R[13], R_IRQ[0]);
std::swap(R[14], R_IRQ[1]);
break;
case 0x13:
SWAP(R[13], R_SVC[0]);
SWAP(R[14], R_SVC[1]);
std::swap(R[13], R_SVC[0]);
std::swap(R[14], R_SVC[1]);
break;
case 0x17:
SWAP(R[13], R_ABT[0]);
SWAP(R[14], R_ABT[1]);
std::swap(R[13], R_ABT[0]);
std::swap(R[14], R_ABT[1]);
break;
case 0x1B:
SWAP(R[13], R_UND[0]);
SWAP(R[14], R_UND[1]);
std::swap(R[13], R_UND[0]);
std::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]);
std::swap(R[8], R_FIQ[0]);
std::swap(R[9], R_FIQ[1]);
std::swap(R[10], R_FIQ[2]);
std::swap(R[11], R_FIQ[3]);
std::swap(R[12], R_FIQ[4]);
std::swap(R[13], R_FIQ[5]);
std::swap(R[14], R_FIQ[6]);
break;
case 0x12:
SWAP(R[13], R_IRQ[0]);
SWAP(R[14], R_IRQ[1]);
std::swap(R[13], R_IRQ[0]);
std::swap(R[14], R_IRQ[1]);
break;
case 0x13:
SWAP(R[13], R_SVC[0]);
SWAP(R[14], R_SVC[1]);
std::swap(R[13], R_SVC[0]);
std::swap(R[14], R_SVC[1]);
break;
case 0x17:
SWAP(R[13], R_ABT[0]);
SWAP(R[14], R_ABT[1]);
std::swap(R[13], R_ABT[0]);
std::swap(R[14], R_ABT[1]);
break;
case 0x1B:
SWAP(R[13], R_UND[0]);
SWAP(R[14], R_UND[1]);
std::swap(R[13], R_UND[0]);
std::swap(R[14], R_UND[1]);
break;
}
#undef SWAP
if (Num == 0)
if ((!phony) && (Num == 0))
{
/*if ((newmode & 0x1F) == 0x10)
if ((newmode & 0x1F) == 0x10)
((ARMv5*)this)->PU_Map = ((ARMv5*)this)->PU_UserMap;
else
((ARMv5*)this)->PU_Map = ((ARMv5*)this)->PU_PrivMap;*/
//if ((newmode & 0x1F) == 0x10) printf("!! USER MODE\n");
((ARMv5*)this)->PU_Map = ((ARMv5*)this)->PU_PrivMap;
}
}
@ -500,7 +532,7 @@ void ARM::TriggerIRQ()
void ARMv5::PrefetchAbort()
{
printf("prefetch abort\n");
printf("ARM9: prefetch abort (%08X)\n", R[15]);
u32 oldcpsr = CPSR;
CPSR &= ~0xBF;
@ -511,7 +543,7 @@ void ARMv5::PrefetchAbort()
// so better take care of it
if (!(PU_Map[ExceptionBase>>12] & 0x04))
{
printf("!!!!! EXCEPTION REGION NOT READABLE. THIS IS VERY BAD!!\n");
printf("!!!!! EXCEPTION REGION NOT EXECUTABLE. THIS IS VERY BAD!!\n");
NDS::Stop();
return;
}
@ -523,7 +555,7 @@ void ARMv5::PrefetchAbort()
void ARMv5::DataAbort()
{
printf("data abort\n");
printf("ARM9: data abort (%08X)\n", R[15]);
u32 oldcpsr = CPSR;
CPSR &= ~0xBF;
@ -531,7 +563,7 @@ void ARMv5::DataAbort()
UpdateMode(oldcpsr, CPSR);
R_ABT[2] = oldcpsr;
R[14] = R[15] + (oldcpsr & 0x20 ? 6 : 4);
R[14] = R[15] + (oldcpsr & 0x20 ? 4 : 0);
JumpTo(ExceptionBase + 0x10);
}

View File

@ -93,7 +93,7 @@ public:
if (v) CPSR |= 0x10000000;
}
void UpdateMode(u32 oldmode, u32 newmode);
void UpdateMode(u32 oldmode, u32 newmode, bool phony = false);
void TriggerIRQ();
@ -269,7 +269,7 @@ public:
// for aarch64 JIT they need to go up here
// to be addressable by a 12-bit immediate
u32 ITCMSize;
u32 DTCMBase, DTCMSize;
u32 DTCMBase, DTCMMask;
s32 RegionCodeCycles;
u8 ITCM[ITCMPhysicalSize];
@ -293,7 +293,8 @@ public:
u8 PU_UserMap[0x100000];
// games operate under system mode, generally
#define PU_Map PU_PrivMap
//#define PU_Map PU_PrivMap
u8* PU_Map;
// code/16N/32N/32S
u8 MemTimings[0x100000][4];

View File

@ -69,9 +69,17 @@ void A_MSR_IMM(ARM* cpu)
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 0x14:
case 0x15:
case 0x16:
case 0x17: psr = &cpu->R_ABT[2]; break;
case 0x18:
case 0x19:
case 0x1A:
case 0x1B: psr = &cpu->R_UND[2]; break;
default: printf("bad CPU mode %08X\n", cpu->CPSR); return;
default:
cpu->AddCycles_C();
return;
}
}
else
@ -92,6 +100,9 @@ void A_MSR_IMM(ARM* cpu)
u32 val = ROR((cpu->CurInstr & 0xFF), ((cpu->CurInstr >> 7) & 0x1E));
// bit4 is forced to 1
val |= 0x00000010;
*psr &= ~mask;
*psr |= (val & mask);
@ -111,9 +122,17 @@ void A_MSR_REG(ARM* cpu)
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 0x14:
case 0x15:
case 0x16:
case 0x17: psr = &cpu->R_ABT[2]; break;
case 0x18:
case 0x19:
case 0x1A:
case 0x1B: psr = &cpu->R_UND[2]; break;
default: printf("bad CPU mode %08X\n", cpu->CPSR); return;
default:
cpu->AddCycles_C();
return;
}
}
else
@ -134,6 +153,9 @@ void A_MSR_REG(ARM* cpu)
u32 val = cpu->R[cpu->CurInstr & 0xF];
// bit4 is forced to 1
val |= 0x00000010;
*psr &= ~mask;
*psr |= (val & mask);
@ -153,9 +175,15 @@ void A_MRS(ARM* cpu)
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 0x14:
case 0x15:
case 0x16:
case 0x17: psr = cpu->R_ABT[2]; break;
case 0x18:
case 0x19:
case 0x1A:
case 0x1B: psr = cpu->R_UND[2]; break;
default: printf("bad CPU mode %08X\n", cpu->CPSR); return;
default: psr = cpu->CPSR; break;
}
}
else
@ -168,6 +196,9 @@ void A_MRS(ARM* cpu)
void A_MCR(ARM* cpu)
{
if ((cpu->CPSR & 0x1F) == 0x10)
return A_UNK(cpu);
u32 cp = (cpu->CurInstr >> 8) & 0xF;
//u32 op = (cpu->CurInstr >> 21) & 0x7;
u32 cn = (cpu->CurInstr >> 16) & 0xF;
@ -193,6 +224,9 @@ void A_MCR(ARM* cpu)
void A_MRC(ARM* cpu)
{
if ((cpu->CPSR & 0x1F) == 0x10)
return A_UNK(cpu);
u32 cp = (cpu->CurInstr >> 8) & 0xF;
//u32 op = (cpu->CurInstr >> 21) & 0x7;
u32 cn = (cpu->CurInstr >> 16) & 0xF;

View File

@ -410,7 +410,7 @@ void A_LDM(ARM* cpu)
}
if ((cpu->CurInstr & (1<<22)) && !(cpu->CurInstr & (1<<15)))
cpu->UpdateMode(cpu->CPSR, (cpu->CPSR&~0x1F)|0x10);
cpu->UpdateMode(cpu->CPSR, (cpu->CPSR&~0x1F)|0x10, true);
for (int i = 0; i < 15; i++)
{
@ -439,7 +439,7 @@ void A_LDM(ARM* cpu)
}
if ((cpu->CurInstr & (1<<22)) && !(cpu->CurInstr & (1<<15)))
cpu->UpdateMode((cpu->CPSR&~0x1F)|0x10, cpu->CPSR);
cpu->UpdateMode((cpu->CPSR&~0x1F)|0x10, cpu->CPSR, true);
if (cpu->CurInstr & (1<<21))
{
@ -494,7 +494,7 @@ void A_STM(ARM* cpu)
else if (mode != 0x10 && mode != 0x1F)
isbanked = (baseid >= 13 && baseid < 15);
cpu->UpdateMode(cpu->CPSR, (cpu->CPSR&~0x1F)|0x10);
cpu->UpdateMode(cpu->CPSR, (cpu->CPSR&~0x1F)|0x10, true);
}
for (u32 i = 0; i < 16; i++)
@ -520,7 +520,7 @@ void A_STM(ARM* cpu)
}
if (cpu->CurInstr & (1<<22))
cpu->UpdateMode((cpu->CPSR&~0x1F)|0x10, cpu->CPSR);
cpu->UpdateMode((cpu->CPSR&~0x1F)|0x10, cpu->CPSR, true);
if ((cpu->CurInstr & (1<<23)) && (cpu->CurInstr & (1<<21)))
cpu->R[baseid] = base;

View File

@ -25,7 +25,7 @@
#define XXH_STATIC_LINKING_ONLY
#include "xxhash/xxhash.h"
#include "Config.h"
#include "Platform.h"
#include "ARMJIT_Internal.h"
#include "ARMJIT_Memory.h"
@ -43,9 +43,6 @@
#include "Wifi.h"
#include "NDSCart.h"
#if defined(__APPLE__) && defined(__aarch64__)
#include <pthread.h>
#endif
#include "ARMJIT_x64/ARMJIT_Offsets.h"
static_assert(offsetof(ARM, CPSR) == ARM_CPSR_offset, "");
@ -60,6 +57,11 @@ namespace ARMJIT
Compiler* JITCompiler;
int MaxBlockSize;
bool LiteralOptimizations;
bool BranchOptimizations;
bool FastMemory;
std::unordered_map<u32, JitBlock*> JitBlocks9;
std::unordered_map<u32, JitBlock*> JitBlocks7;
@ -182,8 +184,8 @@ T SlowRead9(u32 addr, ARMv5* cpu)
T val;
if (addr < cpu->ITCMSize)
val = *(T*)&cpu->ITCM[addr & 0x7FFF];
else if (addr >= cpu->DTCMBase && addr < (cpu->DTCMBase + cpu->DTCMSize))
val = *(T*)&cpu->DTCM[(addr - cpu->DTCMBase) & 0x3FFF];
else if ((addr & cpu->DTCMMask) == cpu->DTCMBase)
val = *(T*)&cpu->DTCM[addr & 0x3FFF];
else if (std::is_same<T, u32>::value)
val = (ConsoleType == 0 ? NDS::ARM9Read32 : DSi::ARM9Read32)(addr);
else if (std::is_same<T, u16>::value)
@ -207,9 +209,9 @@ void SlowWrite9(u32 addr, ARMv5* cpu, u32 val)
CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr);
*(T*)&cpu->ITCM[addr & 0x7FFF] = val;
}
else if (addr >= cpu->DTCMBase && addr < (cpu->DTCMBase + cpu->DTCMSize))
else if ((addr & cpu->DTCMMask) == cpu->DTCMBase)
{
*(T*)&cpu->DTCM[(addr - cpu->DTCMBase) & 0x3FFF] = val;
*(T*)&cpu->DTCM[addr & 0x3FFF] = val;
}
else if (std::is_same<T, u32>::value)
{
@ -320,9 +322,7 @@ void Init()
void DeInit()
{
#if defined(__APPLE__) && defined(__aarch64__)
pthread_jit_write_protect_np(false);
#endif
JitEnableWrite();
ResetBlockCache();
ARMJIT_Memory::DeInit();
@ -331,9 +331,17 @@ void DeInit()
void Reset()
{
#if defined(__APPLE__) && defined(__aarch64__)
pthread_jit_write_protect_np(false);
#endif
MaxBlockSize = Platform::GetConfigInt(Platform::JIT_MaxBlockSize);
LiteralOptimizations = Platform::GetConfigBool(Platform::JIT_LiteralOptimizations);
BranchOptimizations = Platform::GetConfigBool(Platform::JIT_BranchOptimizations);
FastMemory = Platform::GetConfigBool(Platform::JIT_FastMemory);
if (MaxBlockSize < 1)
MaxBlockSize = 1;
if (MaxBlockSize > 32)
MaxBlockSize = 32;
JitEnableWrite();
ResetBlockCache();
ARMJIT_Memory::Reset();
@ -581,11 +589,6 @@ void CompileBlock(ARM* cpu)
{
bool thumb = cpu->CPSR & 0x20;
if (Config::JIT_MaxBlockSize < 1)
Config::JIT_MaxBlockSize = 1;
if (Config::JIT_MaxBlockSize > 32)
Config::JIT_MaxBlockSize = 32;
u32 blockAddr = cpu->R[15] - (thumb ? 2 : 4);
u32 localAddr = LocaliseCodeAddress(cpu->Num, blockAddr);
@ -618,23 +621,26 @@ void CompileBlock(ARM* cpu)
map.erase(existingBlockIt);
}
FetchedInstr instrs[Config::JIT_MaxBlockSize];
FetchedInstr instrs[MaxBlockSize];
int i = 0;
u32 r15 = cpu->R[15];
u32 addressRanges[Config::JIT_MaxBlockSize];
u32 addressMasks[Config::JIT_MaxBlockSize];
memset(addressMasks, 0, Config::JIT_MaxBlockSize * sizeof(u32));
u32 addressRanges[MaxBlockSize];
u32 addressMasks[MaxBlockSize];
memset(addressMasks, 0, MaxBlockSize * sizeof(u32));
u32 numAddressRanges = 0;
u32 numLiterals = 0;
u32 literalLoadAddrs[Config::JIT_MaxBlockSize];
u32 literalLoadAddrs[MaxBlockSize];
// they are going to be hashed
u32 literalValues[Config::JIT_MaxBlockSize];
u32 instrValues[Config::JIT_MaxBlockSize];
u32 literalValues[MaxBlockSize];
u32 instrValues[MaxBlockSize];
// due to instruction merging i might not reflect the amount of actual instructions
u32 numInstrs = 0;
u32 writeAddrs[MaxBlockSize];
u32 numWriteAddrs = 0, writeAddrsTranslated = 0;
cpu->FillPipeline();
u32 nextInstr[2] = {cpu->NextInstr[0], cpu->NextInstr[1]};
u32 nextInstrAddr[2] = {blockAddr, r15};
@ -645,6 +651,8 @@ void CompileBlock(ARM* cpu)
u32 lr;
bool hasLink = false;
bool hasMemoryInstr = false;
do
{
r15 += thumb ? 2 : 4;
@ -707,6 +715,10 @@ void CompileBlock(ARM* cpu)
}
instrs[i].Info = ARMInstrInfo::Decode(thumb, cpu->Num, instrs[i].Instr);
hasMemoryInstr |= thumb
? (instrs[i].Info.Kind >= ARMInstrInfo::tk_LDR_PCREL && instrs[i].Info.Kind <= ARMInstrInfo::tk_STMIA)
: (instrs[i].Info.Kind >= ARMInstrInfo::ak_STR_REG_LSL && instrs[i].Info.Kind <= ARMInstrInfo::ak_STM);
cpu->R[15] = r15;
cpu->CurInstr = instrs[i].Instr;
cpu->CodeCycles = instrs[i].CodeCycles;
@ -745,7 +757,7 @@ void CompileBlock(ARM* cpu)
instrs[i].DataRegion = cpu->DataRegion;
u32 literalAddr;
if (Config::JIT_LiteralOptimisations
if (LiteralOptimizations
&& instrs[i].Info.SpecialKind == ARMInstrInfo::special_LoadLiteral
&& DecodeLiteral(thumb, instrs[i], literalAddr))
{
@ -754,6 +766,8 @@ void CompileBlock(ARM* cpu)
{
printf("literal in non executable memory?\n");
}
if (InvalidLiterals.Find(translatedAddr) == -1)
{
u32 translatedAddrRounded = translatedAddr & ~0x1FF;
u32 j = 0;
@ -767,8 +781,10 @@ void CompileBlock(ARM* cpu)
cpu->DataRead32(literalAddr, &literalValues[numLiterals]);
literalLoadAddrs[numLiterals++] = translatedAddr;
}
if (thumb && instrs[i].Info.Kind == ARMInstrInfo::tk_BL_LONG_2 && i > 0
}
else if (instrs[i].Info.SpecialKind == ARMInstrInfo::special_WriteMem)
writeAddrs[numWriteAddrs++] = instrs[i].DataRegion;
else if (thumb && instrs[i].Info.Kind == ARMInstrInfo::tk_BL_LONG_2 && i > 0
&& instrs[i - 1].Info.Kind == ARMInstrInfo::tk_BL_LONG_1)
{
i--;
@ -780,7 +796,8 @@ void CompileBlock(ARM* cpu)
JIT_DEBUGPRINT("merged BL\n");
}
if (instrs[i].Info.Branches() && Config::JIT_BranchOptimisations)
if (instrs[i].Info.Branches() && BranchOptimizations
&& instrs[i].Info.Kind != (thumb ? ARMInstrInfo::tk_SVC : ARMInstrInfo::ak_SVC))
{
bool hasBranched = cpu->R[15] != r15;
@ -816,7 +833,7 @@ void CompileBlock(ARM* cpu)
JIT_DEBUGPRINT("found %s idle loop %d in block %08x\n", thumb ? "thumb" : "arm", cpu->Num, blockAddr);
}
}
else if (hasBranched && !isBackJump && i + 1 < Config::JIT_MaxBlockSize)
else if (hasBranched && !isBackJump && i + 1 < MaxBlockSize)
{
if (link)
{
@ -844,8 +861,9 @@ void CompileBlock(ARM* cpu)
}
}
if (!hasBranched && cond < 0xE && i + 1 < Config::JIT_MaxBlockSize)
if (!hasBranched && cond < 0xE && i + 1 < MaxBlockSize)
{
JIT_DEBUGPRINT("block lengthened by untaken branch\n");
instrs[i].Info.EndBlock = false;
instrs[i].BranchFlags |= branch_FollowCondNotTaken;
}
@ -857,7 +875,27 @@ void CompileBlock(ARM* cpu)
bool secondaryFlagReadCond = !canCompile || (instrs[i - 1].BranchFlags & (branch_FollowCondTaken | branch_FollowCondNotTaken));
if (instrs[i - 1].Info.ReadFlags != 0 || secondaryFlagReadCond)
FloodFillSetFlags(instrs, i - 2, !secondaryFlagReadCond ? instrs[i - 1].Info.ReadFlags : 0xF);
} while(!instrs[i - 1].Info.EndBlock && i < Config::JIT_MaxBlockSize && !cpu->Halted && (!cpu->IRQ || (cpu->CPSR & 0x80)));
} while(!instrs[i - 1].Info.EndBlock && i < MaxBlockSize && !cpu->Halted && (!cpu->IRQ || (cpu->CPSR & 0x80)));
if (numLiterals)
{
for (u32 j = 0; j < numWriteAddrs; j++)
{
u32 translatedAddr = LocaliseCodeAddress(cpu->Num, writeAddrs[j]);
if (translatedAddr)
{
for (u32 k = 0; k < numLiterals; k++)
{
if (literalLoadAddrs[k] == translatedAddr)
{
if (InvalidLiterals.Find(translatedAddr) == -1)
InvalidLiterals.Add(translatedAddr);
break;
}
}
}
}
}
u32 literalHash = (u32)XXH3_64bits(literalValues, numLiterals * 4);
u32 instrHash = (u32)XXH3_64bits(instrValues, numInstrs * 4);
@ -912,13 +950,10 @@ void CompileBlock(ARM* cpu)
block->StartAddrLocal = localAddr;
FloodFillSetFlags(instrs, i - 1, 0xF);
#if defined(__APPLE__) && defined(__aarch64__)
pthread_jit_write_protect_np(false);
#endif
block->EntryPoint = JITCompiler->CompileBlock(cpu, thumb, instrs, i);
#if defined(__APPLE__) && defined(__aarch64__)
pthread_jit_write_protect_np(true);
#endif
JitEnableWrite();
block->EntryPoint = JITCompiler->CompileBlock(cpu, thumb, instrs, i, hasMemoryInstr);
JitEnableExecute();
JIT_DEBUGPRINT("block start %p\n", block->EntryPoint);
}
@ -1001,7 +1036,7 @@ void InvalidateByAddr(u32 localAddr)
u32 addr = block->Literals()[j];
if (addr == localAddr)
{
if (InvalidLiterals.Find(localAddr) != -1)
if (InvalidLiterals.Find(localAddr) == -1)
{
InvalidLiterals.Add(localAddr);
JIT_DEBUGPRINT("found invalid literal %d\n", InvalidLiterals.Length);
@ -1162,4 +1197,20 @@ void ResetBlockCache()
JITCompiler->Reset();
}
void JitEnableWrite()
{
#if defined(__APPLE__) && defined(__aarch64__)
if (__builtin_available(macOS 11.0, *))
pthread_jit_write_protect_np(false);
#endif
}
void JitEnableExecute()
{
#if defined(__APPLE__) && defined(__aarch64__)
if (__builtin_available(macOS 11.0, *))
pthread_jit_write_protect_np(true);
#endif
}
}

View File

@ -24,11 +24,20 @@
#include "ARM.h"
#include "ARM_InstrInfo.h"
#if defined(__APPLE__) && defined(__aarch64__)
#include <pthread.h>
#endif
namespace ARMJIT
{
typedef void (*JitBlockEntry)();
extern int MaxBlockSize;
extern bool LiteralOptimizations;
extern bool BranchOptimizations;
extern bool FastMemory;
void Init();
void DeInit();
@ -48,6 +57,8 @@ void ResetBlockCache();
JitBlockEntry LookUpBlock(u32 num, u64* entries, u32 offset, u32 addr);
bool SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32& size);
void JitEnableWrite();
void JitEnableExecute();
}
extern "C" void ARM_Dispatch(ARM* cpu, ARMJIT::JitBlockEntry entry);

View File

@ -323,6 +323,8 @@ void Compiler::Comp_Arithmetic(int op, bool S, ARM64Reg rd, ARM64Reg rn, Op2 op2
case 0x5: // ADC
UBFX(W2, RCPSR, 29, 1);
if (S)
{
if (op2.IsImm)
{
CVInGPR = true;
ADDS(W1, rn, W2);
@ -336,6 +338,17 @@ void Compiler::Comp_Arithmetic(int op, bool S, ARM64Reg rd, ARM64Reg rn, Op2 op2
CSINC(W3, W3, WZR, CC_VC);
}
else
{
if (op2.Reg.ShiftAmount > 0)
{
MOV(W0, op2.Reg.Rm, op2.ToArithOption());
op2 = Op2(W0, ST_LSL, 0);
}
CMP(W2, 1);
ADCS(rd, rn, op2.Reg.Rm);
}
}
else
{
ADD(W1, rn, W2);
if (op2.IsImm)
@ -346,6 +359,18 @@ void Compiler::Comp_Arithmetic(int op, bool S, ARM64Reg rd, ARM64Reg rn, Op2 op2
break;
case 0x6: // SBC
UBFX(W2, RCPSR, 29, 1);
if (S && !op2.IsImm)
{
if (op2.Reg.ShiftAmount > 0)
{
MOV(W0, op2.Reg.Rm, op2.ToArithOption());
op2 = Op2(W0, ST_LSL, 0);
}
CMP(W2, 1);
SBCS(rd, rn, op2.Reg.Rm);
}
else
{
// W1 = -op2 - 1
if (op2.IsImm)
MOVI2R(W1, ~op2.Imm);
@ -366,6 +391,7 @@ void Compiler::Comp_Arithmetic(int op, bool S, ARM64Reg rd, ARM64Reg rn, Op2 op2
ADD(W1, W2, W1);
ADD(rd, rn, W1);
}
}
break;
case 0x7: // RSC
UBFX(W2, RCPSR, 29, 1);
@ -532,24 +558,10 @@ void Compiler::A_Comp_ALUMovOp()
MOVI2R(rd, op2.Imm);
}
else
{
// ORR with shifted operand has cycles latency
if (op2.Reg.ShiftAmount > 0)
{
switch (op2.Reg.ShiftType)
{
case ST_LSL: LSL(rd, op2.Reg.Rm, op2.Reg.ShiftAmount); break;
case ST_LSR: LSR(rd, op2.Reg.Rm, op2.Reg.ShiftAmount); break;
case ST_ASR: ASR(rd, op2.Reg.Rm, op2.Reg.ShiftAmount); break;
case ST_ROR: ROR(rd, op2.Reg.Rm, op2.Reg.ShiftAmount); break;
}
}
else
{
MOV(rd, op2.Reg.Rm, op2.ToArithOption());
}
}
}
if (S)
{

View File

@ -27,7 +27,7 @@ namespace ARMJIT
{
template <typename T>
void jumpToTrampoline(T* cpu, u32 addr, bool changeCPSR)
void JumpToTrampoline(T* cpu, u32 addr, bool changeCPSR)
{
cpu->JumpTo(addr, changeCPSR);
}
@ -301,7 +301,7 @@ void Compiler::Comp_JumpTo(Arm64Gen::ARM64Reg addr, bool switchThumb, bool resto
bool cpsrDirty = CPSRDirty;
SaveCPSR();
SaveCycles();
PushRegs(restoreCPSR);
PushRegs(restoreCPSR, true);
if (switchThumb)
MOV(W1, addr);
@ -315,11 +315,11 @@ void Compiler::Comp_JumpTo(Arm64Gen::ARM64Reg addr, bool switchThumb, bool resto
MOV(X0, RCPU);
MOVI2R(W2, restoreCPSR);
if (Num == 0)
QuickCallFunction(X3, jumpToTrampoline<ARMv5>);
QuickCallFunction(X3, JumpToTrampoline<ARMv5>);
else
QuickCallFunction(X3, jumpToTrampoline<ARMv4>);
QuickCallFunction(X3, JumpToTrampoline<ARMv4>);
PopRegs(restoreCPSR);
PopRegs(restoreCPSR, true);
LoadCycles();
LoadCPSR();
if (CurInstr.Cond() < 0xE)

View File

@ -20,7 +20,6 @@
#include "../ARMJIT_Internal.h"
#include "../ARMInterpreter.h"
#include "../Config.h"
#ifdef __SWITCH__
#include <switch.h>
@ -33,10 +32,6 @@ extern char __start__;
#include <stdlib.h>
#ifdef __APPLE__
#include <pthread.h>
#endif
using namespace Arm64Gen;
extern "C" void ARM_Ret();
@ -58,9 +53,14 @@ namespace ARMJIT
template <>
const ARM64Reg RegisterCache<Compiler, ARM64Reg>::NativeRegAllocOrder[] =
{W19, W20, W21, W22, W23, W24, W25, W26};
{
W19, W20, W21, W22, W23, W24, W25,
W8, W9, W10, W11, W12, W13, W14, W15
};
template <>
const int RegisterCache<Compiler, ARM64Reg>::NativeRegsAvailable = 8;
const int RegisterCache<Compiler, ARM64Reg>::NativeRegsAvailable = 15;
const BitSet32 CallerSavedPushRegs({W8, W9, W10, W11, W12, W13, W14, W15});
const int JitMemSize = 16 * 1024 * 1024;
#ifndef __SWITCH__
@ -164,43 +164,54 @@ void Compiler::A_Comp_MSR()
MOV(W2, RCPSR);
MOV(X0, RCPU);
PushRegs(true);
QuickCallFunction(X3, (void*)&UpdateModeTrampoline);
PopRegs(true);
PushRegs(true, true);
QuickCallFunction(X3, UpdateModeTrampoline);
PopRegs(true, true);
}
}
}
void Compiler::PushRegs(bool saveHiRegs)
void Compiler::PushRegs(bool saveHiRegs, bool saveRegsToBeChanged, bool allowUnload)
{
BitSet32 loadedRegs(RegCache.LoadedRegs);
if (saveHiRegs)
{
if (Thumb || CurInstr.Cond() == 0xE)
{
BitSet16 hiRegsLoaded(RegCache.LoadedRegs & 0x7F00);
BitSet32 hiRegsLoaded(RegCache.LoadedRegs & 0x7F00);
for (int reg : hiRegsLoaded)
RegCache.UnloadRegister(reg);
}
else
{
BitSet16 hiRegsDirty(RegCache.LoadedRegs & 0x7F00);
for (int reg : hiRegsDirty)
if (Thumb || CurInstr.Cond() == 0xE)
RegCache.UnloadRegister(reg);
else
SaveReg(reg, RegCache.Mapping[reg]);
// prevent saving the register twice
loadedRegs[reg] = false;
}
}
for (int reg : loadedRegs)
{
if (CallerSavedPushRegs[RegCache.Mapping[reg]]
&& (saveRegsToBeChanged || !((1<<reg) & CurInstr.Info.DstRegs && !((1<<reg) & CurInstr.Info.SrcRegs))))
{
if ((Thumb || CurInstr.Cond() == 0xE) && !((1 << reg) & (CurInstr.Info.DstRegs|CurInstr.Info.SrcRegs)) && allowUnload)
RegCache.UnloadRegister(reg);
else
SaveReg(reg, RegCache.Mapping[reg]);
}
}
}
void Compiler::PopRegs(bool saveHiRegs)
void Compiler::PopRegs(bool saveHiRegs, bool saveRegsToBeChanged)
{
if (saveHiRegs)
BitSet32 loadedRegs(RegCache.LoadedRegs);
for (int reg : loadedRegs)
{
if (!Thumb && CurInstr.Cond() != 0xE)
if ((saveHiRegs && reg >= 8 && reg < 15)
|| (CallerSavedPushRegs[RegCache.Mapping[reg]]
&& (saveRegsToBeChanged || !((1<<reg) & CurInstr.Info.DstRegs && !((1<<reg) & CurInstr.Info.SrcRegs)))))
{
BitSet16 hiRegsLoaded(RegCache.LoadedRegs & 0x7F00);
for (int reg : hiRegsLoaded)
LoadReg(reg, RegCache.Mapping[reg]);
}
}
@ -250,7 +261,7 @@ Compiler::Compiler()
u64 alignedSize = (((u64)JitMem + sizeof(JitMem)) & ~(pageSize - 1)) - (u64)pageAligned;
#ifdef __APPLE__
pageAligned = (u8*)mmap(NULL, 1024*1024*16, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_JIT,-1, 0);
pthread_jit_write_protect_np(false);
JitEnableWrite();
#else
mprotect(pageAligned, alignedSize, PROT_EXEC | PROT_READ | PROT_WRITE);
#endif
@ -267,6 +278,7 @@ Compiler::Compiler()
}
/*
W4 - whether the register was written to
W5 - mode
W1 - reg num
W3 - in/out value of reg
@ -358,7 +370,7 @@ Compiler::Compiler()
{
for (int reg = 0; reg < 32; reg++)
{
if (!(reg == W4 || (reg >= W19 && reg <= W26)))
if (!(reg == W4 || (reg >= W8 && reg <= W15) || (reg >= W19 && reg <= W25)))
continue;
ARM64Reg rdMapped = (ARM64Reg)reg;
PatchedStoreFuncs[consoleType][num][size][reg] = GetRXPtr();
@ -371,7 +383,7 @@ Compiler::Compiler()
{
MOV(W1, rdMapped);
}
ABI_PushRegisters({30});
ABI_PushRegisters(BitSet32({30}) | CallerSavedPushRegs);
if (consoleType == 0)
{
switch ((8 << size) | num)
@ -397,7 +409,7 @@ Compiler::Compiler()
}
}
ABI_PopRegisters({30});
ABI_PopRegisters(BitSet32({30}) | CallerSavedPushRegs);
RET();
for (int signextend = 0; signextend < 2; signextend++)
@ -405,7 +417,7 @@ Compiler::Compiler()
PatchedLoadFuncs[consoleType][num][size][signextend][reg] = GetRXPtr();
if (num == 0)
MOV(X1, RCPU);
ABI_PushRegisters({30});
ABI_PushRegisters(BitSet32({30}) | CallerSavedPushRegs);
if (consoleType == 0)
{
switch ((8 << size) | num)
@ -430,7 +442,7 @@ Compiler::Compiler()
case 9: QuickCallFunction(X3, SlowRead7<u8, 1>); break;
}
}
ABI_PopRegisters({30});
ABI_PopRegisters(BitSet32({30}) | CallerSavedPushRegs);
if (size == 32)
MOV(rdMapped, W0);
else if (signextend)
@ -668,12 +680,13 @@ void Compiler::Comp_BranchSpecialBehaviour(bool taken)
{
RegCache.PrepareExit();
if (ConstantCycles)
ADD(RCycles, RCycles, ConstantCycles);
QuickTailCall(X0, ARM_Ret);
}
}
JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[], int instrsCount)
JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[], int instrsCount, bool hasMemInstr)
{
if (JitMemMainSize - GetCodeOffset() < 1024 * 16)
{
@ -695,6 +708,9 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
RegCache = RegisterCache<Compiler, ARM64Reg>(this, instrs, instrsCount, true);
CPSRDirty = false;
if (hasMemInstr)
MOVP2R(RMemBase, Num == 0 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start);
for (int i = 0; i < instrsCount; i++)
{
CurInstr = instrs[i];
@ -816,6 +832,7 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
RegCache.Flush();
if (ConstantCycles)
ADD(RCycles, RCycles, ConstantCycles);
QuickTailCall(X0, ARM_Ret);

View File

@ -32,6 +32,7 @@
namespace ARMJIT
{
const Arm64Gen::ARM64Reg RMemBase = Arm64Gen::X26;
const Arm64Gen::ARM64Reg RCPSR = Arm64Gen::W27;
const Arm64Gen::ARM64Reg RCycles = Arm64Gen::W28;
const Arm64Gen::ARM64Reg RCPU = Arm64Gen::X29;
@ -99,8 +100,8 @@ public:
Compiler();
~Compiler();
void PushRegs(bool saveHiRegs);
void PopRegs(bool saveHiRegs);
void PushRegs(bool saveHiRegs, bool saveRegsToBeChanged, bool allowUnload = true);
void PopRegs(bool saveHiRegs, bool saveRegsToBeChanged);
Arm64Gen::ARM64Reg MapReg(int reg)
{
@ -108,7 +109,7 @@ public:
return RegCache.Mapping[reg];
}
JitBlockEntry CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[], int instrsCount);
JitBlockEntry CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[], int instrsCount, bool hasMemInstr);
bool CanCompile(bool thumb, u16 kind);
@ -184,7 +185,7 @@ public:
void T_Comp_BL_LONG_2();
void T_Comp_BL_Merged();
s32 Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode);
s32 Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode, bool skipLoadingRn);
void Comp_Mul_Mla(bool S, bool mla, Arm64Gen::ARM64Reg rd, Arm64Gen::ARM64Reg rm, Arm64Gen::ARM64Reg rs, Arm64Gen::ARM64Reg rn);

View File

@ -18,7 +18,7 @@
#include "ARMJIT_Compiler.h"
#include "../Config.h"
#include "../ARMJIT.h"
#include "../ARMJIT_Memory.h"
@ -67,7 +67,6 @@ bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr)
int invalidLiteralIdx = InvalidLiterals.Find(localAddr);
if (invalidLiteralIdx != -1)
{
InvalidLiterals.Remove(invalidLiteralIdx);
return false;
}
@ -112,7 +111,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
if (size == 16)
addressMask = ~1;
if (Config::JIT_LiteralOptimisations && rn == 15 && rd != 15 && offset.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback)))
if (ARMJIT::LiteralOptimizations && rn == 15 && rd != 15 && offset.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback)))
{
u32 addr = R15 + offset.Imm * ((flags & memop_SubtractOffset) ? -1 : 1);
@ -147,7 +146,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
MOV(W0, rnMapped);
}
bool addrIsStatic = Config::JIT_LiteralOptimisations
bool addrIsStatic = ARMJIT::LiteralOptimizations
&& RegCache.IsLiteral(rn) && offset.IsImm && !(flags & (memop_Writeback|memop_Post));
u32 staticAddress;
if (addrIsStatic)
@ -189,18 +188,16 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
? ARMJIT_Memory::ClassifyAddress9(addrIsStatic ? staticAddress : CurInstr.DataRegion)
: ARMJIT_Memory::ClassifyAddress7(addrIsStatic ? staticAddress : CurInstr.DataRegion);
if (Config::JIT_FastMemory && ((!Thumb && CurInstr.Cond() != 0xE) || ARMJIT_Memory::IsFastmemCompatible(expectedTarget)))
if (ARMJIT::FastMemory && ((!Thumb && CurInstr.Cond() != 0xE) || ARMJIT_Memory::IsFastmemCompatible(expectedTarget)))
{
ptrdiff_t memopStart = GetCodeOffset();
LoadStorePatch patch;
assert((rdMapped >= W19 && rdMapped <= W26) || rdMapped == W4);
assert((rdMapped >= W8 && rdMapped <= W15) || (rdMapped >= W19 && rdMapped <= W25) || rdMapped == W4);
patch.PatchFunc = flags & memop_Store
? PatchedStoreFuncs[NDS::ConsoleType][Num][__builtin_ctz(size) - 3][rdMapped]
: PatchedLoadFuncs[NDS::ConsoleType][Num][__builtin_ctz(size) - 3][!!(flags & memop_SignExtend)][rdMapped];
MOVP2R(X7, Num == 0 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start);
// take a chance at fastmem
if (size > 8)
ANDI2R(W1, W0, addressMask);
@ -208,11 +205,11 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
ptrdiff_t loadStorePosition = GetCodeOffset();
if (flags & memop_Store)
{
STRGeneric(size, rdMapped, size > 8 ? X1 : X0, X7);
STRGeneric(size, rdMapped, size > 8 ? X1 : X0, RMemBase);
}
else
{
LDRGeneric(size, flags & memop_SignExtend, rdMapped, size > 8 ? X1 : X0, X7);
LDRGeneric(size, flags & memop_SignExtend, rdMapped, size > 8 ? X1 : X0, RMemBase);
if (size == 32 && !addrIsStatic)
{
UBFIZ(W0, W0, 3, 2);
@ -230,12 +227,16 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
if (addrIsStatic)
func = ARMJIT_Memory::GetFuncForAddr(CurCPU, staticAddress, flags & memop_Store, size);
PushRegs(false, false);
if (func)
{
if (flags & memop_Store)
MOV(W1, rdMapped);
QuickCallFunction(X2, (void (*)())func);
PopRegs(false, false);
if (!(flags & memop_Store))
{
if (size == 32)
@ -314,6 +315,8 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
}
}
PopRegs(false, false);
if (!(flags & memop_Store))
{
if (size == 32)
@ -449,7 +452,7 @@ void Compiler::T_Comp_LoadPCRel()
u32 offset = ((CurInstr.Instr & 0xFF) << 2);
u32 addr = (R15 & ~0x2) + offset;
if (!Config::JIT_LiteralOptimisations || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr))
if (!ARMJIT::LiteralOptimizations || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr))
Comp_MemAccess(CurInstr.T_Reg(8), 15, Op2(offset), 32, 0);
}
@ -461,7 +464,7 @@ void Compiler::T_Comp_MemSPRel()
Comp_MemAccess(CurInstr.T_Reg(8), 13, Op2(offset), 32, load ? 0 : memop_Store);
}
s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode)
s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode, bool skipLoadingRn)
{
IrregularCycles = true;
@ -470,7 +473,8 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
if (regsCount == 0)
return 0; // actually not the right behaviour TODO: fix me
if (regsCount == 1 && !usermode && RegCache.LoadedRegs & (1 << *regs.begin()))
int firstReg = *regs.begin();
if (regsCount == 1 && !usermode && RegCache.LoadedRegs & (1 << firstReg) && !(firstReg == rn && skipLoadingRn))
{
int flags = 0;
if (store)
@ -479,7 +483,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
flags |= memop_SubtractOffset;
Op2 offset = preinc ? Op2(4) : Op2(0);
Comp_MemAccess(*regs.begin(), rn, offset, 32, flags);
Comp_MemAccess(firstReg, rn, offset, 32, flags);
return decrement ? -4 : 4;
}
@ -493,7 +497,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
? ARMJIT_Memory::ClassifyAddress9(CurInstr.DataRegion)
: ARMJIT_Memory::ClassifyAddress7(CurInstr.DataRegion);
bool compileFastPath = Config::JIT_FastMemory
bool compileFastPath = ARMJIT::FastMemory
&& store && !usermode && (CurInstr.Cond() < 0xE || ARMJIT_Memory::IsFastmemCompatible(expectedTarget));
{
@ -515,8 +519,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
ptrdiff_t fastPathStart = GetCodeOffset();
ptrdiff_t loadStoreOffsets[8];
MOVP2R(X1, Num == 0 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start);
ADD(X1, X1, X0);
ADD(X1, RMemBase, X0);
u32 offset = 0;
BitSet16::Iterator it = regs.begin();
@ -536,12 +539,16 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
loadStoreOffsets[i++] = GetCodeOffset();
if (store)
{
STR(INDEX_UNSIGNED, first, X1, offset);
else
}
else if (!(reg == rn && skipLoadingRn))
{
LDR(INDEX_UNSIGNED, first, X1, offset);
if (!(RegCache.LoadedRegs & (1 << reg)) && !store)
if (!(RegCache.LoadedRegs & (1 << reg)))
SaveReg(reg, first);
}
offset += 4;
}
@ -555,13 +562,23 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
ARM64Reg first = W3, second = W4;
if (RegCache.LoadedRegs & (1 << reg))
{
if (!(reg == rn && skipLoadingRn))
first = MapReg(reg);
}
else if (store)
{
LoadReg(reg, first);
}
if (RegCache.LoadedRegs & (1 << nextReg))
{
if (!(nextReg == rn && skipLoadingRn))
second = MapReg(nextReg);
}
else if (store)
{
LoadReg(nextReg, second);
}
loadStoreOffsets[i++] = GetCodeOffset();
if (store)
@ -655,6 +672,8 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
}
}
PushRegs(false, false, !compileFastPath);
ADD(X1, SP, 0);
MOVI2R(W2, regsCount);
@ -680,6 +699,8 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
}
}
PopRegs(false, false);
if (!store)
{
if (usermode && !regs[15] && (regs & BitSet16(0x7f00)))
@ -698,6 +719,8 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
LDR(INDEX_UNSIGNED, W3, SP, i * 8);
MOVI2R(W1, reg - 8);
BL(WriteBanked);
if (!(reg == rn && skipLoadingRn))
{
FixupBranch alreadyWritten = CBNZ(W4);
if (RegCache.LoadedRegs & (1 << reg))
MOV(MapReg(reg), W3);
@ -705,13 +728,14 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
SaveReg(reg, W3);
SetJumpTarget(alreadyWritten);
}
}
else if (!usermode && nextReg != regs.end())
{
ARM64Reg first = W3, second = W4;
if (RegCache.LoadedRegs & (1 << reg))
if (RegCache.LoadedRegs & (1 << reg) && !(reg == rn && skipLoadingRn))
first = MapReg(reg);
if (RegCache.LoadedRegs & (1 << *nextReg))
if (RegCache.LoadedRegs & (1 << *nextReg) && !(*nextReg == rn && skipLoadingRn))
second = MapReg(*nextReg);
LDP(INDEX_SIGNED, EncodeRegTo64(first), EncodeRegTo64(second), SP, i * 8);
@ -725,10 +749,13 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
i++;
}
else if (RegCache.LoadedRegs & (1 << reg))
{
if (!(reg == rn && skipLoadingRn))
{
ARM64Reg mapped = MapReg(reg);
LDR(INDEX_UNSIGNED, mapped, SP, i * 8);
}
}
else
{
LDR(INDEX_UNSIGNED, W3, SP, i * 8);
@ -771,13 +798,13 @@ void Compiler::A_Comp_LDM_STM()
ARM64Reg rn = MapReg(CurInstr.A_Reg(16));
s32 offset = Comp_MemAccessBlock(CurInstr.A_Reg(16), regs, !load, pre, !add, usermode);
if (load && writeback && regs[CurInstr.A_Reg(16)])
writeback = Num == 0
? (!(regs & ~BitSet16(1 << CurInstr.A_Reg(16)))) || (regs & ~BitSet16((2 << CurInstr.A_Reg(16)) - 1))
: false;
if (writeback)
&& (!(regs & ~BitSet16(1 << CurInstr.A_Reg(16)))) || (regs & ~BitSet16((2 << CurInstr.A_Reg(16)) - 1));
s32 offset = Comp_MemAccessBlock(CurInstr.A_Reg(16), regs, !load, pre, !add, usermode, load && writeback);
if (writeback && offset)
{
if (offset > 0)
ADD(rn, rn, offset);
@ -799,13 +826,16 @@ void Compiler::T_Comp_PUSH_POP()
}
ARM64Reg sp = MapReg(13);
s32 offset = Comp_MemAccessBlock(13, regs, !load, !load, !load, false);
s32 offset = Comp_MemAccessBlock(13, regs, !load, !load, !load, false, false);
if (offset)
{
if (offset > 0)
ADD(sp, sp, offset);
else
SUB(sp, sp, -offset);
}
}
void Compiler::T_Comp_LDMIA_STMIA()
{
@ -814,9 +844,11 @@ void Compiler::T_Comp_LDMIA_STMIA()
bool load = CurInstr.Instr & (1 << 11);
u32 regsCount = regs.Count();
s32 offset = Comp_MemAccessBlock(CurInstr.T_Reg(8), regs, !load, false, false, false);
bool writeback = !load || !regs[CurInstr.T_Reg(8)];
if (!load || !regs[CurInstr.T_Reg(8)])
s32 offset = Comp_MemAccessBlock(CurInstr.T_Reg(8), regs, !load, false, false, false, load && writeback);
if (writeback && offset)
{
if (offset > 0)
ADD(rb, rb, offset);

View File

@ -372,7 +372,7 @@ struct Mapping
void Unmap(int region)
{
u32 dtcmStart = NDS::ARM9->DTCMBase;
u32 dtcmSize = NDS::ARM9->DTCMSize;
u32 dtcmSize = ~NDS::ARM9->DTCMMask + 1;
bool skipDTCM = Num == 0 && region != memregion_DTCM;
u8* statuses = Num == 0 ? MappingStatus9 : MappingStatus7;
u32 offset = 0;
@ -453,8 +453,7 @@ void SetCodeProtection(int region, u32 offset, bool protect)
u32 effectiveAddr = mapping.Addr + (offset - mapping.LocalOffset);
if (mapping.Num == 0
&& region != memregion_DTCM
&& effectiveAddr >= NDS::ARM9->DTCMBase
&& effectiveAddr < (NDS::ARM9->DTCMBase + NDS::ARM9->DTCMSize))
&& (effectiveAddr & NDS::ARM9->DTCMMask) == NDS::ARM9->DTCMBase)
continue;
u8* states = (u8*)(mapping.Num == 0 ? MappingStatus9 : MappingStatus7);
@ -481,11 +480,12 @@ void RemapDTCM(u32 newBase, u32 newSize)
// this first part could be made more efficient
// by unmapping DTCM first and then map the holes
u32 oldDTCMBase = NDS::ARM9->DTCMBase;
u32 oldDTCBEnd = oldDTCMBase + NDS::ARM9->DTCMSize;
u32 oldDTCMSize = ~NDS::ARM9->DTCMMask + 1;
u32 oldDTCMEnd = oldDTCMBase + NDS::ARM9->DTCMMask;
u32 newEnd = newBase + newSize;
printf("remapping DTCM %x %x %x %x\n", newBase, newEnd, oldDTCMBase, oldDTCBEnd);
printf("remapping DTCM %x %x %x %x\n", newBase, newEnd, oldDTCMBase, oldDTCMEnd);
// unmap all regions containing the old or the current DTCM mapping
for (int region = 0; region < memregions_Count; region++)
{
@ -501,7 +501,7 @@ void RemapDTCM(u32 newBase, u32 newSize)
printf("unmapping %d %x %x %x %x\n", region, mapping.Addr, mapping.Size, mapping.Num, mapping.LocalOffset);
bool overlap = (NDS::ARM9->DTCMSize > 0 && oldDTCMBase < end && oldDTCBEnd > start)
bool overlap = (oldDTCMSize > 0 && oldDTCMBase < end && oldDTCMEnd > start)
|| (newSize > 0 && newBase < end && newEnd > start);
if (mapping.Num == 0 && overlap)
@ -588,7 +588,7 @@ bool MapAtAddress(u32 addr)
bool isExecutable = ARMJIT::CodeMemRegions[region];
u32 dtcmStart = NDS::ARM9->DTCMBase;
u32 dtcmSize = NDS::ARM9->DTCMSize;
u32 dtcmSize = ~NDS::ARM9->DTCMMask + 1;
u32 dtcmEnd = dtcmStart + dtcmSize;
#ifndef __SWITCH__
#ifndef _WIN32
@ -1067,7 +1067,7 @@ int ClassifyAddress9(u32 addr)
{
return memregion_ITCM;
}
else if (addr >= NDS::ARM9->DTCMBase && addr < (NDS::ARM9->DTCMBase + NDS::ARM9->DTCMSize))
else if ((addr & NDS::ARM9->DTCMMask) == NDS::ARM9->DTCMBase)
{
return memregion_DTCM;
}

View File

@ -165,7 +165,7 @@ void Compiler::Comp_JumpTo(Gen::X64Reg addr, bool restoreCPSR)
bool cpsrDirty = CPSRDirty;
SaveCPSR();
PushRegs(restoreCPSR);
PushRegs(restoreCPSR, true);
MOV(64, R(ABI_PARAM1), R(RCPU));
MOV(32, R(ABI_PARAM2), R(addr));
@ -178,7 +178,7 @@ void Compiler::Comp_JumpTo(Gen::X64Reg addr, bool restoreCPSR)
else
CALL((void*)&ARMv4JumpToTrampoline);
PopRegs(restoreCPSR);
PopRegs(restoreCPSR, true);
LoadCPSR();
// in case this instruction is skipped

View File

@ -19,9 +19,9 @@
#include "ARMJIT_Compiler.h"
#include "../ARMInterpreter.h"
#include "../Config.h"
#include <assert.h>
#include <stdarg.h>
#include "../dolphin/CommonFuncs.h"
@ -64,7 +64,7 @@ const BitSet32 CallerSavedPushRegs({R10, R11});
const BitSet32 CallerSavedPushRegs({R9, R10, R11});
#endif
void Compiler::PushRegs(bool saveHiRegs)
void Compiler::PushRegs(bool saveHiRegs, bool saveRegsToBeChanged, bool allowUnload)
{
BitSet32 loadedRegs(RegCache.LoadedRegs);
@ -83,17 +83,26 @@ void Compiler::PushRegs(bool saveHiRegs)
}
for (int reg : loadedRegs)
if (BitSet32(1 << RegCache.Mapping[reg]) & ABI_ALL_CALLER_SAVED)
{
if (CallerSavedPushRegs[RegCache.Mapping[reg]]
&& (saveRegsToBeChanged || !((1<<reg) & CurInstr.Info.DstRegs && !((1<<reg) & CurInstr.Info.SrcRegs))))
{
if ((Thumb || CurInstr.Cond() == 0xE) && !((1 << reg) & (CurInstr.Info.DstRegs|CurInstr.Info.SrcRegs)) && allowUnload)
RegCache.UnloadRegister(reg);
else
SaveReg(reg, RegCache.Mapping[reg]);
}
}
}
void Compiler::PopRegs(bool saveHiRegs)
void Compiler::PopRegs(bool saveHiRegs, bool saveRegsToBeChanged)
{
BitSet32 loadedRegs(RegCache.LoadedRegs);
for (int reg : loadedRegs)
{
if ((saveHiRegs && reg >= 8 && reg < 15)
|| BitSet32(1 << RegCache.Mapping[reg]) & ABI_ALL_CALLER_SAVED)
|| (CallerSavedPushRegs[RegCache.Mapping[reg]]
&& (saveRegsToBeChanged || !((1<<reg) & CurInstr.Info.DstRegs && !((1<<reg) & CurInstr.Info.SrcRegs)))))
{
LoadReg(reg, RegCache.Mapping[reg]);
}
@ -205,14 +214,14 @@ void Compiler::A_Comp_MSR()
AND(32, R(RSCRATCH2), val);
OR(32, R(RCPSR), R(RSCRATCH2));
PushRegs(true);
PushRegs(true, true);
MOV(32, R(ABI_PARAM3), R(RCPSR));
MOV(32, R(ABI_PARAM2), R(RSCRATCH3));
MOV(64, R(ABI_PARAM1), R(RCPU));
CALL((void*)&UpdateModeTrampoline);
PopRegs(true);
PopRegs(true, true);
}
}
}
@ -289,6 +298,10 @@ Compiler::Compiler()
SetJumpTarget(und);
MOV(32, R(RSCRATCH3), MComplex(RCPU, RSCRATCH2, SCALE_4, offsetof(ARM, R_UND)));
RET();
#ifdef JIT_PROFILING_ENABLED
CreateMethod("ReadBanked", ReadBanked);
#endif
}
{
// RSCRATCH mode
@ -332,6 +345,10 @@ Compiler::Compiler()
MOV(32, MComplex(RCPU, RSCRATCH2, SCALE_4, offsetof(ARM, R_UND)), R(RSCRATCH3));
CLC();
RET();
#ifdef JIT_PROFILING_ENABLED
CreateMethod("WriteBanked", WriteBanked);
#endif
}
for (int consoleType = 0; consoleType < 2; consoleType++)
@ -392,6 +409,10 @@ Compiler::Compiler()
ABI_PopRegistersAndAdjustStack(CallerSavedPushRegs, 8);
RET();
#ifdef JIT_PROFILING_ENABLED
CreateMethod("FastMemStorePatch%d_%d_%d", PatchedStoreFuncs[consoleType][num][size][reg], num, size, reg);
#endif
for (int signextend = 0; signextend < 2; signextend++)
{
PatchedLoadFuncs[consoleType][num][size][signextend][reg] = GetWritableCodePtr();
@ -430,6 +451,10 @@ Compiler::Compiler()
else
MOVZX(32, 8 << size, rdMapped, R(RSCRATCH));
RET();
#ifdef JIT_PROFILING_ENABLED
CreateMethod("FastMemLoadPatch%d_%d_%d_%d", PatchedLoadFuncs[consoleType][num][size][signextend][reg], num, size, reg, signextend);
#endif
}
}
}
@ -654,12 +679,35 @@ void Compiler::Comp_SpecialBranchBehaviour(bool taken)
{
RegCache.PrepareExit();
if (ConstantCycles)
ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm32(ConstantCycles));
JMP((u8*)&ARM_Ret, true);
}
}
JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[], int instrsCount)
#ifdef JIT_PROFILING_ENABLED
void Compiler::CreateMethod(const char* namefmt, void* start, ...)
{
if (iJIT_IsProfilingActive())
{
va_list args;
va_start(args, start);
char name[64];
vsprintf(name, namefmt, args);
va_end(args);
iJIT_Method_Load method = {0};
method.method_id = iJIT_GetNewMethodID();
method.method_name = name;
method.method_load_address = start;
method.method_size = GetWritableCodePtr() - (u8*)start;
iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, (void*)&method);
}
}
#endif
JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[], int instrsCount, bool hasMemoryInstr)
{
if (NearSize - (GetCodePtr() - NearStart) < 1024 * 32) // guess...
{
@ -793,9 +841,14 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
RegCache.Flush();
if (ConstantCycles)
ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm32(ConstantCycles));
JMP((u8*)ARM_Ret, true);
#ifdef JIT_PROFILING_ENABLED
CreateMethod("JIT_Block_%d_%d_%08X", (void*)res, Num, Thumb, instrs[0].Addr);
#endif
/*FILE* codeout = fopen("codeout", "a");
fprintf(codeout, "beginning block argargarg__ %x!!!", instrs[0].Addr);
fwrite((u8*)res, GetWritableCodePtr() - (u8*)res, 1, codeout);

View File

@ -25,6 +25,10 @@
#include "../ARMJIT_Internal.h"
#include "../ARMJIT_RegisterCache.h"
#ifdef JIT_PROFILING_ENABLED
#include <jitprofiling.h>
#endif
#include <unordered_map>
namespace ARMJIT
@ -79,7 +83,7 @@ public:
void Reset();
JitBlockEntry CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[], int instrsCount);
JitBlockEntry CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[], int instrsCount, bool hasMemoryInstr);
void LoadReg(int reg, Gen::X64Reg nativeReg);
void SaveReg(int reg, Gen::X64Reg nativeReg);
@ -163,7 +167,7 @@ public:
memop_SubtractOffset = 1 << 4
};
void Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flags);
s32 Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode);
s32 Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode, bool skipLoadingRn);
bool Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr);
void Comp_ArithTriOp(void (Compiler::*op)(int, const Gen::OpArg&, const Gen::OpArg&),
@ -192,8 +196,8 @@ public:
Gen::FixupBranch CheckCondition(u32 cond);
void PushRegs(bool saveHiRegs);
void PopRegs(bool saveHiRegs);
void PushRegs(bool saveHiRegs, bool saveRegsToBeChanged, bool allowUnload = true);
void PopRegs(bool saveHiRegs, bool saveRegsToBeChanged);
Gen::OpArg MapReg(int reg)
{
@ -230,6 +234,10 @@ public:
u8* RewriteMemAccess(u8* pc);
#ifdef JIT_PROFILING_ENABLED
void CreateMethod(const char* namefmt, void* start, ...);
#endif
u8* FarCode;
u8* NearCode;
u32 FarSize;

View File

@ -18,8 +18,6 @@
#include "ARMJIT_Compiler.h"
#include "../Config.h"
using namespace Gen;
namespace ARMJIT
@ -74,7 +72,6 @@ bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr)
int invalidLiteralIdx = InvalidLiterals.Find(localAddr);
if (invalidLiteralIdx != -1)
{
InvalidLiterals.Remove(invalidLiteralIdx);
return false;
}
@ -120,7 +117,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
if (size == 16)
addressMask = ~1;
if (Config::JIT_LiteralOptimisations && rn == 15 && rd != 15 && op2.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback)))
if (LiteralOptimizations && rn == 15 && rd != 15 && op2.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback)))
{
u32 addr = R15 + op2.Imm * ((flags & memop_SubtractOffset) ? -1 : 1);
@ -137,7 +134,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
Comp_AddCycles_CDI();
}
bool addrIsStatic = Config::JIT_LiteralOptimisations
bool addrIsStatic = LiteralOptimizations
&& RegCache.IsLiteral(rn) && op2.IsImm && !(flags & (memop_Writeback|memop_Post));
u32 staticAddress;
if (addrIsStatic)
@ -201,7 +198,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
? ARMJIT_Memory::ClassifyAddress9(CurInstr.DataRegion)
: ARMJIT_Memory::ClassifyAddress7(CurInstr.DataRegion);
if (Config::JIT_FastMemory && ((!Thumb && CurInstr.Cond() != 0xE) || ARMJIT_Memory::IsFastmemCompatible(expectedTarget)))
if (ARMJIT::FastMemory && ((!Thumb && CurInstr.Cond() != 0xE) || ARMJIT_Memory::IsFastmemCompatible(expectedTarget)))
{
if (rdMapped.IsImm())
{
@ -266,7 +263,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
}
else
{
PushRegs(false);
PushRegs(false, false);
void* func = NULL;
if (addrIsStatic)
@ -283,7 +280,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
ABI_CallFunction((void (*)())func);
PopRegs(false);
PopRegs(false, false);
if (!(flags & memop_Store))
{
@ -370,7 +367,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
}
}
PopRegs(false);
PopRegs(false, false);
if (!(flags & memop_Store))
{
@ -399,14 +396,15 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
}
}
s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode)
s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode, bool skipLoadingRn)
{
int regsCount = regs.Count();
if (regsCount == 0)
return 0; // actually not the right behaviour TODO: fix me
if (regsCount == 1 && !usermode && RegCache.LoadedRegs & (1 << *regs.begin()))
int firstReg = *regs.begin();
if (regsCount == 1 && !usermode && RegCache.LoadedRegs & (1 << firstReg) && !(firstReg == rn && skipLoadingRn))
{
int flags = 0;
if (store)
@ -415,7 +413,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
flags |= memop_SubtractOffset;
Op2 offset = preinc ? Op2(4) : Op2(0);
Comp_MemAccess(*regs.begin(), rn, offset, 32, flags);
Comp_MemAccess(firstReg, rn, offset, 32, flags);
return decrement ? -4 : 4;
}
@ -431,7 +429,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
else
Comp_AddCycles_CD();
bool compileFastPath = Config::JIT_FastMemory
bool compileFastPath = FastMemory
&& !usermode && (CurInstr.Cond() < 0xE || ARMJIT_Memory::IsFastmemCompatible(expectedTarget));
// we need to make sure that the stack stays aligned to 16 bytes
@ -482,7 +480,10 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
{
if (RegCache.LoadedRegs & (1 << reg))
{
if (!(reg == rn && skipLoadingRn))
MOV(32, MapReg(reg), mem);
else
MOV(32, R(RSCRATCH), mem); // just touch the memory
}
else
{
@ -508,7 +509,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
if (!store)
{
PushRegs(false);
PushRegs(false, false, !compileFastPath);
MOV(32, R(ABI_PARAM1), R(RSCRATCH4));
MOV(32, R(ABI_PARAM3), Imm32(regsCount));
@ -529,7 +530,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
case 3: CALL((void*)&SlowBlockTransfer7<false, 1>); break;
}
PopRegs(false);
PopRegs(false, false);
if (allocOffset)
ADD(64, R(RSP), Imm8(allocOffset));
@ -548,6 +549,8 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
MOV(32, R(RSCRATCH2), Imm32(reg - 8));
POP(RSCRATCH3);
CALL(WriteBanked);
if (!(reg == rn && skipLoadingRn))
{
FixupBranch sucessfulWritten = J_CC(CC_NC);
if (RegCache.LoadedRegs & (1 << reg))
MOV(32, R(RegCache.Mapping[reg]), R(RSCRATCH3));
@ -555,6 +558,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
SaveReg(reg, RSCRATCH3);
SetJumpTarget(sucessfulWritten);
}
}
else if (!(RegCache.LoadedRegs & (1 << reg)))
{
assert(reg != 15);
@ -562,6 +566,10 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
POP(RSCRATCH);
SaveReg(reg, RSCRATCH);
}
else if (reg == rn && skipLoadingRn)
{
ADD(64, R(RSP), Imm8(8));
}
else
{
POP(MapReg(reg).GetSimpleReg());
@ -606,7 +614,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
if (allocOffset)
SUB(64, R(RSP), Imm8(allocOffset));
PushRegs(false);
PushRegs(false, false, !compileFastPath);
MOV(32, R(ABI_PARAM1), R(RSCRATCH4));
if (allocOffset)
@ -628,7 +636,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
ADD(64, R(RSP), stackAlloc <= INT8_MAX ? Imm8(stackAlloc) : Imm32(stackAlloc));
PopRegs(false);
PopRegs(false, false);
}
if (compileFastPath)
@ -748,14 +756,14 @@ void Compiler::A_Comp_LDM_STM()
OpArg rn = MapReg(CurInstr.A_Reg(16));
s32 offset = Comp_MemAccessBlock(CurInstr.A_Reg(16), regs, !load, pre, !add, usermode);
if (load && writeback && regs[CurInstr.A_Reg(16)])
writeback = Num == 0
? (!(regs & ~BitSet16(1 << CurInstr.A_Reg(16)))) || (regs & ~BitSet16((2 << CurInstr.A_Reg(16)) - 1))
: false;
if (writeback)
ADD(32, rn, offset >= INT8_MIN && offset < INT8_MAX ? Imm8(offset) : Imm32(offset));
&& (!(regs & ~BitSet16(1 << CurInstr.A_Reg(16)))) || (regs & ~BitSet16((2 << CurInstr.A_Reg(16)) - 1));
s32 offset = Comp_MemAccessBlock(CurInstr.A_Reg(16), regs, !load, pre, !add, usermode, load && writeback);
if (writeback && offset)
ADD(32, rn, Imm32(offset));
}
void Compiler::T_Comp_MemImm()
@ -799,7 +807,7 @@ void Compiler::T_Comp_LoadPCRel()
{
u32 offset = (CurInstr.Instr & 0xFF) << 2;
u32 addr = (R15 & ~0x2) + offset;
if (!Config::JIT_LiteralOptimisations || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr))
if (!LiteralOptimizations || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr))
Comp_MemAccess(CurInstr.T_Reg(8), 15, Op2(offset), 32, 0);
}
@ -825,8 +833,9 @@ void Compiler::T_Comp_PUSH_POP()
}
OpArg sp = MapReg(13);
s32 offset = Comp_MemAccessBlock(13, regs, !load, !load, !load, false);
s32 offset = Comp_MemAccessBlock(13, regs, !load, !load, !load, false, false);
if (offset)
ADD(32, sp, Imm8(offset)); // offset will be always be in range since PUSH accesses 9 regs max
}
@ -836,9 +845,11 @@ void Compiler::T_Comp_LDMIA_STMIA()
OpArg rb = MapReg(CurInstr.T_Reg(8));
bool load = CurInstr.Instr & (1 << 11);
s32 offset = Comp_MemAccessBlock(CurInstr.T_Reg(8), regs, !load, false, false, false);
bool writeback = !load || !regs[CurInstr.T_Reg(8)];
if (!load || !regs[CurInstr.T_Reg(8)])
s32 offset = Comp_MemAccessBlock(CurInstr.T_Reg(8), regs, !load, false, false, false, load && writeback);
if (writeback && offset)
ADD(32, rb, Imm8(offset));
}

View File

@ -20,7 +20,7 @@
#include <stdio.h>
#include "Config.h"
#include "ARMJIT.h"
namespace ARMInstrInfo
{
@ -386,7 +386,7 @@ Info Decode(bool thumb, u32 num, u32 instr)
{
if (res.Kind == tk_LDR_PCREL)
{
if (!Config::JIT_LiteralOptimisations)
if (!ARMJIT::LiteralOptimizations)
res.SrcRegs |= 1 << 15;
res.SpecialKind = special_LoadLiteral;
}
@ -536,6 +536,11 @@ Info Decode(bool thumb, u32 num, u32 instr)
u16 set = (instr & 0xFFFF);
res.NotStrictlyNeeded |= set & ~(res.SrcRegs|res.DstRegs|(1<<15));
res.DstRegs |= set;
// when the instruction is executed not in usermode a banked register in memory will be written to
// but the unbanked register will still be allocated, so it is expected to carry the proper value
// thus it is a source register
if (instr & (1<<22))
res.SrcRegs |= set & 0x7F00;
}
if (res.Kind == ak_STM)
{

View File

@ -11,19 +11,21 @@ add_library(core STATIC
ARMInterpreter_ALU.cpp
ARMInterpreter_Branch.cpp
ARMInterpreter_LoadStore.cpp
Config.cpp
CP15.cpp
CRC32.cpp
DMA.cpp
DMA_Timings.h
DSi.cpp
DSi_AES.cpp
DSi_Camera.cpp
DSi_DSP.cpp
DSi_I2C.cpp
DSi_NAND.cpp
DSi_NDMA.cpp
DSi_NWifi.cpp
DSi_SD.cpp
DSi_SPI_TSC.cpp
FATStorage.cpp
FIFO.h
GBACart.cpp
GPU.cpp
@ -37,6 +39,7 @@ add_library(core STATIC
NDSCart_SRAMManager.cpp
Platform.h
ROMList.h
FreeBIOS.h
RTC.cpp
Savestate.cpp
SPI.cpp
@ -46,6 +49,13 @@ add_library(core STATIC
Wifi.cpp
WifiAP.cpp
fatfs/diskio.c
fatfs/ff.c
fatfs/ffsystem.c
fatfs/ffunicode.c
fatfs/ffconf.h
sha1/sha1.c
tiny-AES-c/aes.c
xxhash/xxhash.c
)
@ -124,3 +134,7 @@ else()
target_link_libraries(core rt)
endif()
endif()
if (ENABLE_JIT_PROFILING)
target_link_libraries(core jitprofiling)
endif()

View File

@ -50,7 +50,7 @@ void ARMv5::CP15Reset()
ITCMSize = 0;
DTCMBase = 0xFFFFFFFF;
DTCMSize = 0;
DTCMMask = 0;
memset(ICache, 0, 0x2000);
ICacheInvalidateAll();
@ -102,26 +102,30 @@ void ARMv5::CP15DoSavestate(Savestate* file)
void ARMv5::UpdateDTCMSetting()
{
u32 newDTCMBase;
u32 newDTCMMask;
u32 newDTCMSize;
if (CP15Control & (1<<16))
{
newDTCMBase = DTCMSetting & 0xFFFFF000;
newDTCMSize = 0x200 << ((DTCMSetting >> 1) & 0x1F);
//printf("DTCM [%08X] enabled at %08X, size %X\n", DTCMSetting, newDTCMBase, newDTCMSize);
if (newDTCMSize < 0x1000) newDTCMSize = 0x1000;
newDTCMMask = 0xFFFFF000 & ~(newDTCMSize-1);
newDTCMBase = DTCMSetting & newDTCMMask;
}
else
{
newDTCMBase = 0xFFFFFFFF;
newDTCMSize = 0;
//printf("DTCM disabled\n");
newDTCMBase = 0xFFFFFFFF;
newDTCMMask = 0;
}
if (newDTCMBase != DTCMBase || newDTCMSize != DTCMSize)
if (newDTCMBase != DTCMBase || newDTCMMask != DTCMMask)
{
#ifdef JIT_ENABLED
ARMJIT_Memory::RemapDTCM(newDTCMBase, newDTCMSize);
#endif
DTCMBase = newDTCMBase;
DTCMSize = newDTCMSize;
DTCMMask = newDTCMMask;
}
}
@ -130,12 +134,10 @@ void ARMv5::UpdateITCMSetting()
if (CP15Control & (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");
}
}
@ -144,6 +146,9 @@ void ARMv5::UpdateITCMSetting()
// (not to the region range/enabled status)
void ARMv5::UpdatePURegion(u32 n)
{
if (!(CP15Control & (1<<0)))
return;
u32 coderw = (PU_CodeRW >> (4*n)) & 0xF;
u32 datarw = (PU_DataRW >> (4*n)) & 0xF;
@ -225,7 +230,8 @@ void ARMv5::UpdatePURegion(u32 n)
usermask |= 0x40;
}
//printf("PU region %d: %08X-%08X, user=%02X priv=%02X\n", n, start<<12, end<<12, usermask, privmask);
printf("PU region %d: %08X-%08X, user=%02X priv=%02X\n", n, start<<12, end<<12, usermask, privmask);
printf("%08X/%08X\n", PU_DataRW, PU_CodeRW);
for (u32 i = start; i < end; i++)
{
@ -233,7 +239,7 @@ void ARMv5::UpdatePURegion(u32 n)
PU_PrivMap[i] = privmask;
}
UpdateRegionTimings(start<<12, end<<12);
UpdateRegionTimings(start, end);
}
void ARMv5::UpdatePURegions(bool update_all)
@ -249,7 +255,7 @@ void ARMv5::UpdatePURegions(bool update_all)
memset(PU_UserMap, mask, 0x100000);
memset(PU_PrivMap, mask, 0x100000);
UpdateRegionTimings(0x00000000, 0xFFFFFFFF);
UpdateRegionTimings(0x00000, 0x100000);
return;
}
@ -266,16 +272,13 @@ void ARMv5::UpdatePURegions(bool update_all)
// TODO: this is way unoptimized
// should be okay unless the game keeps changing shit, tho
if (update_all) UpdateRegionTimings(0x00000000, 0xFFFFFFFF);
if (update_all) UpdateRegionTimings(0x00000, 0x100000);
// TODO: throw exception if the region we're running in has become non-executable, I guess
}
void ARMv5::UpdateRegionTimings(u32 addrstart, u32 addrend)
{
addrstart >>= 12;
addrend >>= 12;
if (addrend == 0xFFFFF) addrend++;
for (u32 i = addrstart; i < addrend; i++)
{
u8 pu = PU_Map[i];
@ -420,7 +423,7 @@ void ARMv5::ICacheInvalidateAll()
void ARMv5::CP15Write(u32 id, u32 val)
{
//printf("CP15 write op %03X %08X %08X\n", id, val, R[15]);
//if(id!=0x704)printf("CP15 write op %03X %08X %08X\n", id, val, R[15]);
switch (id)
{
@ -604,12 +607,12 @@ void ARMv5::CP15Write(u32 id, u32 val)
case 0x910:
DTCMSetting = val;
DTCMSetting = val & 0xFFFFF03E;
UpdateDTCMSetting();
return;
case 0x911:
ITCMSetting = val;
ITCMSetting = val & 0x0000003E;
UpdateITCMSetting();
return;
@ -745,6 +748,15 @@ u32 ARMv5::CP15Read(u32 id)
u32 ARMv5::CodeRead32(u32 addr, bool branch)
{
/*if (branch || (!(addr & 0xFFF)))
{
if (!(PU_Map[addr>>12] & 0x04))
{
PrefetchAbort();
return 0;
}
}*/
if (addr < ITCMSize)
{
CodeCycles = 1;
@ -770,6 +782,12 @@ u32 ARMv5::CodeRead32(u32 addr, bool branch)
void ARMv5::DataRead8(u32 addr, u32* val)
{
if (!(PU_Map[addr>>12] & 0x01))
{
DataAbort();
return;
}
DataRegion = addr;
if (addr < ITCMSize)
@ -778,10 +796,10 @@ void ARMv5::DataRead8(u32 addr, u32* val)
*val = *(u8*)&ITCM[addr & (ITCMPhysicalSize - 1)];
return;
}
if (addr >= DTCMBase && addr < (DTCMBase + DTCMSize))
if ((addr & DTCMMask) == DTCMBase)
{
DataCycles = 1;
*val = *(u8*)&DTCM[(addr - DTCMBase) & (DTCMPhysicalSize - 1)];
*val = *(u8*)&DTCM[addr & (DTCMPhysicalSize - 1)];
return;
}
@ -791,6 +809,12 @@ void ARMv5::DataRead8(u32 addr, u32* val)
void ARMv5::DataRead16(u32 addr, u32* val)
{
if (!(PU_Map[addr>>12] & 0x01))
{
DataAbort();
return;
}
DataRegion = addr;
addr &= ~1;
@ -801,10 +825,10 @@ void ARMv5::DataRead16(u32 addr, u32* val)
*val = *(u16*)&ITCM[addr & (ITCMPhysicalSize - 1)];
return;
}
if (addr >= DTCMBase && addr < (DTCMBase + DTCMSize))
if ((addr & DTCMMask) == DTCMBase)
{
DataCycles = 1;
*val = *(u16*)&DTCM[(addr - DTCMBase) & (DTCMPhysicalSize - 1)];
*val = *(u16*)&DTCM[addr & (DTCMPhysicalSize - 1)];
return;
}
@ -814,6 +838,12 @@ void ARMv5::DataRead16(u32 addr, u32* val)
void ARMv5::DataRead32(u32 addr, u32* val)
{
if (!(PU_Map[addr>>12] & 0x01))
{
DataAbort();
return;
}
DataRegion = addr;
addr &= ~3;
@ -824,10 +854,10 @@ void ARMv5::DataRead32(u32 addr, u32* val)
*val = *(u32*)&ITCM[addr & (ITCMPhysicalSize - 1)];
return;
}
if (addr >= DTCMBase && addr < (DTCMBase + DTCMSize))
if ((addr & DTCMMask) == DTCMBase)
{
DataCycles = 1;
*val = *(u32*)&DTCM[(addr - DTCMBase) & (DTCMPhysicalSize - 1)];
*val = *(u32*)&DTCM[addr & (DTCMPhysicalSize - 1)];
return;
}
@ -845,10 +875,10 @@ void ARMv5::DataRead32S(u32 addr, u32* val)
*val = *(u32*)&ITCM[addr & (ITCMPhysicalSize - 1)];
return;
}
if (addr >= DTCMBase && addr < (DTCMBase + DTCMSize))
if ((addr & DTCMMask) == DTCMBase)
{
DataCycles += 1;
*val = *(u32*)&DTCM[(addr - DTCMBase) & (DTCMPhysicalSize - 1)];
*val = *(u32*)&DTCM[addr & (DTCMPhysicalSize - 1)];
return;
}
@ -858,6 +888,12 @@ void ARMv5::DataRead32S(u32 addr, u32* val)
void ARMv5::DataWrite8(u32 addr, u8 val)
{
if (!(PU_Map[addr>>12] & 0x02))
{
DataAbort();
return;
}
DataRegion = addr;
if (addr < ITCMSize)
@ -869,10 +905,10 @@ void ARMv5::DataWrite8(u32 addr, u8 val)
#endif
return;
}
if (addr >= DTCMBase && addr < (DTCMBase + DTCMSize))
if ((addr & DTCMMask) == DTCMBase)
{
DataCycles = 1;
*(u8*)&DTCM[(addr - DTCMBase) & (DTCMPhysicalSize - 1)] = val;
*(u8*)&DTCM[addr & (DTCMPhysicalSize - 1)] = val;
return;
}
@ -882,6 +918,12 @@ void ARMv5::DataWrite8(u32 addr, u8 val)
void ARMv5::DataWrite16(u32 addr, u16 val)
{
if (!(PU_Map[addr>>12] & 0x02))
{
DataAbort();
return;
}
DataRegion = addr;
addr &= ~1;
@ -895,10 +937,10 @@ void ARMv5::DataWrite16(u32 addr, u16 val)
#endif
return;
}
if (addr >= DTCMBase && addr < (DTCMBase + DTCMSize))
if ((addr & DTCMMask) == DTCMBase)
{
DataCycles = 1;
*(u16*)&DTCM[(addr - DTCMBase) & (DTCMPhysicalSize - 1)] = val;
*(u16*)&DTCM[addr & (DTCMPhysicalSize - 1)] = val;
return;
}
@ -908,6 +950,12 @@ void ARMv5::DataWrite16(u32 addr, u16 val)
void ARMv5::DataWrite32(u32 addr, u32 val)
{
if (!(PU_Map[addr>>12] & 0x02))
{
DataAbort();
return;
}
DataRegion = addr;
addr &= ~3;
@ -921,10 +969,10 @@ void ARMv5::DataWrite32(u32 addr, u32 val)
#endif
return;
}
if (addr >= DTCMBase && addr < (DTCMBase + DTCMSize))
if ((addr & DTCMMask) == DTCMBase)
{
DataCycles = 1;
*(u32*)&DTCM[(addr - DTCMBase) & (DTCMPhysicalSize - 1)] = val;
*(u32*)&DTCM[addr & (DTCMPhysicalSize - 1)] = val;
return;
}
@ -945,10 +993,10 @@ void ARMv5::DataWrite32S(u32 addr, u32 val)
#endif
return;
}
if (addr >= DTCMBase && addr < (DTCMBase + DTCMSize))
if ((addr & DTCMMask) == DTCMBase)
{
DataCycles += 1;
*(u32*)&DTCM[(addr - DTCMBase) & (DTCMPhysicalSize - 1)] = val;
*(u32*)&DTCM[addr & (DTCMPhysicalSize - 1)] = val;
return;
}

View File

@ -1,187 +0,0 @@
/*
Copyright 2016-2021 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 <stdlib.h>
#include "Config.h"
#include "Platform.h"
namespace Config
{
const char* kConfigFile = "melonDS.ini";
char BIOS9Path[1024];
char BIOS7Path[1024];
char FirmwarePath[1024];
int DLDIEnable;
char DLDISDPath[1024];
char DSiBIOS9Path[1024];
char DSiBIOS7Path[1024];
char DSiFirmwarePath[1024];
char DSiNANDPath[1024];
int DSiSDEnable;
char DSiSDPath[1024];
int RandomizeMAC;
#ifdef JIT_ENABLED
int JIT_Enable = false;
int JIT_MaxBlockSize = 32;
int JIT_BranchOptimisations = true;
int JIT_LiteralOptimisations = true;
int JIT_FastMemory = true;
#endif
ConfigEntry ConfigFile[] =
{
{"BIOS9Path", 1, BIOS9Path, 0, "", 1023},
{"BIOS7Path", 1, BIOS7Path, 0, "", 1023},
{"FirmwarePath", 1, FirmwarePath, 0, "", 1023},
{"DLDIEnable", 0, &DLDIEnable, 0, NULL, 0},
{"DLDISDPath", 1, DLDISDPath, 0, "", 1023},
{"DSiBIOS9Path", 1, DSiBIOS9Path, 0, "", 1023},
{"DSiBIOS7Path", 1, DSiBIOS7Path, 0, "", 1023},
{"DSiFirmwarePath", 1, DSiFirmwarePath, 0, "", 1023},
{"DSiNANDPath", 1, DSiNANDPath, 0, "", 1023},
{"DSiSDEnable", 0, &DSiSDEnable, 0, NULL, 0},
{"DSiSDPath", 1, DSiSDPath, 0, "", 1023},
{"RandomizeMAC", 0, &RandomizeMAC, 0, NULL, 0},
#ifdef JIT_ENABLED
{"JIT_Enable", 0, &JIT_Enable, 0, NULL, 0},
{"JIT_MaxBlockSize", 0, &JIT_MaxBlockSize, 32, NULL, 0},
{"JIT_BranchOptimisations", 0, &JIT_BranchOptimisations, 1, NULL, 0},
{"JIT_LiteralOptimisations", 0, &JIT_LiteralOptimisations, 1, NULL, 0},
#ifdef __APPLE__
{"JIT_FastMemory", 0, &JIT_FastMemory, 0, NULL, 0},
#else
{"JIT_FastMemory", 0, &JIT_FastMemory, 1, NULL, 0},
#endif
#endif
{"", -1, NULL, 0, NULL, 0}
};
extern ConfigEntry PlatformConfigFile[];
void Load()
{
ConfigEntry* entry = &ConfigFile[0];
int c = 0;
for (;;)
{
if (!entry->Value)
{
if (c > 0) break;
entry = &PlatformConfigFile[0];
if (!entry->Value) break;
c++;
}
if (entry->Type == 0)
*(int*)entry->Value = entry->DefaultInt;
else
{
strncpy((char*)entry->Value, entry->DefaultStr, entry->StrLength);
((char*)entry->Value)[entry->StrLength] = '\0';
}
entry++;
}
FILE* f = Platform::OpenLocalFile(kConfigFile, "r");
if (!f) return;
char linebuf[1024];
char entryname[32];
char entryval[1024];
while (!feof(f))
{
if (fgets(linebuf, 1024, f) == nullptr)
break;
int ret = sscanf(linebuf, "%31[A-Za-z_0-9]=%[^\t\r\n]", entryname, entryval);
entryname[31] = '\0';
if (ret < 2) continue;
ConfigEntry* entry = &ConfigFile[0];
c = 0;
for (;;)
{
if (!entry->Value)
{
if (c > 0) break;
entry = &PlatformConfigFile[0];
if (!entry->Value) break;
c++;
}
if (!strncmp(entry->Name, entryname, 32))
{
if (entry->Type == 0)
*(int*)entry->Value = strtol(entryval, NULL, 10);
else
strncpy((char*)entry->Value, entryval, entry->StrLength);
break;
}
entry++;
}
}
fclose(f);
}
void Save()
{
FILE* f = Platform::OpenLocalFile(kConfigFile, "w");
if (!f) return;
ConfigEntry* entry = &ConfigFile[0];
int c = 0;
for (;;)
{
if (!entry->Value)
{
if (c > 0) break;
entry = &PlatformConfigFile[0];
if (!entry->Value) break;
c++;
}
if (entry->Type == 0)
fprintf(f, "%s=%d\r\n", entry->Name, *(int*)entry->Value);
else
fprintf(f, "%s=%s\r\n", entry->Name, (char*)entry->Value);
entry++;
}
fclose(f);
}
}

View File

@ -1,69 +0,0 @@
/*
Copyright 2016-2021 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 CONFIG_H
#define CONFIG_H
#include <stdio.h>
#include "types.h"
namespace Config
{
struct ConfigEntry
{
char Name[32];
int Type;
void* Value;
int DefaultInt;
const char* DefaultStr;
int StrLength; // should be set to actual array length minus one
};
FILE* GetConfigFile(const char* fileName, const char* permissions);
bool HasConfigFile(const char* fileName);
void Load();
void Save();
extern char BIOS9Path[1024];
extern char BIOS7Path[1024];
extern char FirmwarePath[1024];
extern int DLDIEnable;
extern char DLDISDPath[1024];
extern char DSiBIOS9Path[1024];
extern char DSiBIOS7Path[1024];
extern char DSiFirmwarePath[1024];
extern char DSiNANDPath[1024];
extern int DSiSDEnable;
extern char DSiSDPath[1024];
extern int RandomizeMAC;
#ifdef JIT_ENABLED
extern int JIT_Enable;
extern int JIT_MaxBlockSize;
extern int JIT_BranchOptimisations;
extern int JIT_LiteralOptimisations;
extern int JIT_FastMemory;
#endif
}
#endif // CONFIG_H

View File

@ -21,6 +21,7 @@
#include "DSi.h"
#include "DMA.h"
#include "GPU.h"
#include "DMA_Timings.h"
@ -77,6 +78,7 @@ void DMA::Reset()
Running = false;
InProgress = false;
MRAMBurstCount = 0;
}
void DMA::DoSavestate(Savestate* file)
@ -94,12 +96,13 @@ void DMA::DoSavestate(Savestate* file)
file->Var32(&CurDstAddr);
file->Var32(&RemCount);
file->Var32(&IterCount);
file->Var32(&SrcAddrInc);
file->Var32(&DstAddrInc);
file->Var32((u32*)&SrcAddrInc);
file->Var32((u32*)&DstAddrInc);
file->Var32(&Running);
file->Bool32(&InProgress);
file->Bool32(&IsGXFIFODMA);
file->Var32(&MRAMBurstCount);
}
void DMA::WriteCnt(u32 val)
@ -125,7 +128,7 @@ void DMA::WriteCnt(u32 val)
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;
case 0x01800000: SrcAddrInc = 1; break;
}
if (CPU == 0)
@ -165,6 +168,9 @@ void DMA::Start()
else
IterCount = RemCount;
if ((Cnt & 0x01800000) == 0x01800000)
CurSrcAddr = SrcAddr;
if ((Cnt & 0x00600000) == 0x00600000)
CurDstAddr = DstAddr;
@ -174,15 +180,369 @@ void DMA::Start()
// TODO eventually: not stop if we're running code in ITCM
if (NDS::DMAsRunning(CPU))
Running = 1;
else
Running = 2;
// safety measure
MRAMBurstTable = DMATiming::MRAMDummy;
InProgress = true;
NDS::StopCPU(CPU, 1<<Num);
}
u32 DMA::UnitTimings9_16(bool burststart)
{
u32 src_id = CurSrcAddr >> 14;
u32 dst_id = CurDstAddr >> 14;
u32 src_rgn = NDS::ARM9Regions[src_id];
u32 dst_rgn = NDS::ARM9Regions[dst_id];
u32 src_n, src_s, dst_n, dst_s;
src_n = NDS::ARM9MemTimings[src_id][4];
src_s = NDS::ARM9MemTimings[src_id][5];
dst_n = NDS::ARM9MemTimings[dst_id][4];
dst_s = NDS::ARM9MemTimings[dst_id][5];
if (src_rgn == NDS::Mem9_MainRAM)
{
if (dst_rgn == NDS::Mem9_MainRAM)
return 16;
if (SrcAddrInc > 0)
{
if (burststart || MRAMBurstTable[MRAMBurstCount] == 0)
{
MRAMBurstCount = 0;
if (dst_rgn == NDS::Mem9_GBAROM)
{
if (dst_s == 4)
MRAMBurstTable = DMATiming::MRAMRead16Bursts[1];
else
MRAMBurstTable = DMATiming::MRAMRead16Bursts[2];
}
else
MRAMBurstTable = DMATiming::MRAMRead16Bursts[0];
}
u32 ret = MRAMBurstTable[MRAMBurstCount++];
return ret;
}
else
{
// TODO: not quite right for GBA slot
return (((CurSrcAddr & 0x1F) == 0x1E) ? 7 : 8) +
(burststart ? dst_n : dst_s);
}
}
else if (dst_rgn == NDS::Mem9_MainRAM)
{
if (DstAddrInc > 0)
{
if (burststart || MRAMBurstTable[MRAMBurstCount] == 0)
{
MRAMBurstCount = 0;
if (src_rgn == NDS::Mem9_GBAROM)
{
if (src_s == 4)
MRAMBurstTable = DMATiming::MRAMWrite16Bursts[1];
else
MRAMBurstTable = DMATiming::MRAMWrite16Bursts[2];
}
else
MRAMBurstTable = DMATiming::MRAMWrite16Bursts[0];
}
u32 ret = MRAMBurstTable[MRAMBurstCount++];
return ret;
}
else
{
return (burststart ? src_n : src_s) + 7;
}
}
else if (src_rgn & dst_rgn)
{
return src_n + dst_n + 1;
}
else
{
if (burststart)
return src_n + dst_n;
else
return src_s + dst_s;
}
}
u32 DMA::UnitTimings9_32(bool burststart)
{
u32 src_id = CurSrcAddr >> 14;
u32 dst_id = CurDstAddr >> 14;
u32 src_rgn = NDS::ARM9Regions[src_id];
u32 dst_rgn = NDS::ARM9Regions[dst_id];
u32 src_n, src_s, dst_n, dst_s;
src_n = NDS::ARM9MemTimings[src_id][6];
src_s = NDS::ARM9MemTimings[src_id][7];
dst_n = NDS::ARM9MemTimings[dst_id][6];
dst_s = NDS::ARM9MemTimings[dst_id][7];
if (src_rgn == NDS::Mem9_MainRAM)
{
if (dst_rgn == NDS::Mem9_MainRAM)
return 18;
if (SrcAddrInc > 0)
{
if (burststart || MRAMBurstTable[MRAMBurstCount] == 0)
{
MRAMBurstCount = 0;
if (dst_rgn == NDS::Mem9_GBAROM)
{
if (dst_s == 8)
MRAMBurstTable = DMATiming::MRAMRead32Bursts[2];
else
MRAMBurstTable = DMATiming::MRAMRead32Bursts[3];
}
else if (dst_n == 2)
MRAMBurstTable = DMATiming::MRAMRead32Bursts[0];
else
MRAMBurstTable = DMATiming::MRAMRead32Bursts[1];
}
u32 ret = MRAMBurstTable[MRAMBurstCount++];
return ret;
}
else
{
// TODO: not quite right for GBA slot
return (((CurSrcAddr & 0x1F) == 0x1C) ? (dst_n==2 ? 7:8) : 9) +
(burststart ? dst_n : dst_s);
}
}
else if (dst_rgn == NDS::Mem9_MainRAM)
{
if (DstAddrInc > 0)
{
if (burststart || MRAMBurstTable[MRAMBurstCount] == 0)
{
MRAMBurstCount = 0;
if (src_rgn == NDS::Mem9_GBAROM)
{
if (src_s == 8)
MRAMBurstTable = DMATiming::MRAMWrite32Bursts[2];
else
MRAMBurstTable = DMATiming::MRAMWrite32Bursts[3];
}
else if (src_n == 2)
MRAMBurstTable = DMATiming::MRAMWrite32Bursts[0];
else
MRAMBurstTable = DMATiming::MRAMWrite32Bursts[1];
}
u32 ret = MRAMBurstTable[MRAMBurstCount++];
return ret;
}
else
{
return (burststart ? src_n : src_s) + 8;
}
}
else if (src_rgn & dst_rgn)
{
return src_n + dst_n + 1;
}
else
{
if (burststart)
return src_n + dst_n;
else
return src_s + dst_s;
}
}
// TODO: the ARM7 ones don't take into account that the two wifi regions have different timings
u32 DMA::UnitTimings7_16(bool burststart)
{
u32 src_id = CurSrcAddr >> 15;
u32 dst_id = CurDstAddr >> 15;
u32 src_rgn = NDS::ARM7Regions[src_id];
u32 dst_rgn = NDS::ARM7Regions[dst_id];
u32 src_n, src_s, dst_n, dst_s;
src_n = NDS::ARM7MemTimings[src_id][0];
src_s = NDS::ARM7MemTimings[src_id][1];
dst_n = NDS::ARM7MemTimings[dst_id][0];
dst_s = NDS::ARM7MemTimings[dst_id][1];
if (src_rgn == NDS::Mem7_MainRAM)
{
if (dst_rgn == NDS::Mem7_MainRAM)
return 16;
if (SrcAddrInc > 0)
{
if (burststart || MRAMBurstTable[MRAMBurstCount] == 0)
{
MRAMBurstCount = 0;
if (dst_rgn == NDS::Mem7_GBAROM || dst_rgn == NDS::Mem7_Wifi0 || dst_rgn == NDS::Mem7_Wifi1)
{
if (dst_s == 4)
MRAMBurstTable = DMATiming::MRAMRead16Bursts[1];
else
MRAMBurstTable = DMATiming::MRAMRead16Bursts[2];
}
else
MRAMBurstTable = DMATiming::MRAMRead16Bursts[0];
}
u32 ret = MRAMBurstTable[MRAMBurstCount++];
return ret;
}
else
{
// TODO: not quite right for GBA slot
return (((CurSrcAddr & 0x1F) == 0x1E) ? 7 : 8) +
(burststart ? dst_n : dst_s);
}
}
else if (dst_rgn == NDS::Mem7_MainRAM)
{
if (DstAddrInc > 0)
{
if (burststart || MRAMBurstTable[MRAMBurstCount] == 0)
{
MRAMBurstCount = 0;
if (src_rgn == NDS::Mem7_GBAROM || src_rgn == NDS::Mem7_Wifi0 || src_rgn == NDS::Mem7_Wifi1)
{
if (src_s == 4)
MRAMBurstTable = DMATiming::MRAMWrite16Bursts[1];
else
MRAMBurstTable = DMATiming::MRAMWrite16Bursts[2];
}
else
MRAMBurstTable = DMATiming::MRAMWrite16Bursts[0];
}
u32 ret = MRAMBurstTable[MRAMBurstCount++];
return ret;
}
else
{
return (burststart ? src_n : src_s) + 7;
}
}
else if (src_rgn & dst_rgn)
{
return src_n + dst_n + 1;
}
else
{
if (burststart)
return src_n + dst_n;
else
return src_s + dst_s;
}
}
u32 DMA::UnitTimings7_32(bool burststart)
{
u32 src_id = CurSrcAddr >> 15;
u32 dst_id = CurDstAddr >> 15;
u32 src_rgn = NDS::ARM7Regions[src_id];
u32 dst_rgn = NDS::ARM7Regions[dst_id];
u32 src_n, src_s, dst_n, dst_s;
src_n = NDS::ARM7MemTimings[src_id][2];
src_s = NDS::ARM7MemTimings[src_id][3];
dst_n = NDS::ARM7MemTimings[dst_id][2];
dst_s = NDS::ARM7MemTimings[dst_id][3];
if (src_rgn == NDS::Mem7_MainRAM)
{
if (dst_rgn == NDS::Mem7_MainRAM)
return 18;
if (SrcAddrInc > 0)
{
if (burststart || MRAMBurstTable[MRAMBurstCount] == 0)
{
MRAMBurstCount = 0;
if (dst_rgn == NDS::Mem7_GBAROM || dst_rgn == NDS::Mem7_Wifi0 || dst_rgn == NDS::Mem7_Wifi1)
{
if (dst_s == 8)
MRAMBurstTable = DMATiming::MRAMRead32Bursts[2];
else
MRAMBurstTable = DMATiming::MRAMRead32Bursts[3];
}
else if (dst_n == 2)
MRAMBurstTable = DMATiming::MRAMRead32Bursts[0];
else
MRAMBurstTable = DMATiming::MRAMRead32Bursts[1];
}
u32 ret = MRAMBurstTable[MRAMBurstCount++];
return ret;
}
else
{
// TODO: not quite right for GBA slot
return (((CurSrcAddr & 0x1F) == 0x1C) ? (dst_n==2 ? 7:8) : 9) +
(burststart ? dst_n : dst_s);
}
}
else if (dst_rgn == NDS::Mem7_MainRAM)
{
if (DstAddrInc > 0)
{
if (burststart || MRAMBurstTable[MRAMBurstCount] == 0)
{
MRAMBurstCount = 0;
if (src_rgn == NDS::Mem7_GBAROM || src_rgn == NDS::Mem7_Wifi0 || src_rgn == NDS::Mem7_Wifi1)
{
if (src_s == 8)
MRAMBurstTable = DMATiming::MRAMWrite32Bursts[2];
else
MRAMBurstTable = DMATiming::MRAMWrite32Bursts[3];
}
else if (src_n == 2)
MRAMBurstTable = DMATiming::MRAMWrite32Bursts[0];
else
MRAMBurstTable = DMATiming::MRAMWrite32Bursts[1];
}
u32 ret = MRAMBurstTable[MRAMBurstCount++];
return ret;
}
else
{
return (burststart ? src_n : src_s) + 8;
}
}
else if (src_rgn & dst_rgn)
{
return src_n + dst_n + 1;
}
else
{
if (burststart)
return src_n + dst_n;
else
return src_s + dst_s;
}
}
template <int ConsoleType>
void DMA::Run9()
{
@ -194,32 +554,12 @@ void DMA::Run9()
bool burststart = (Running == 2);
Running = 1;
s32 unitcycles;
//s32 lastcycles = cycles;
if (!(Cnt & (1<<26)))
{
if ((CurSrcAddr >> 24) == 0x02 && (CurDstAddr >> 24) == 0x02)
{
unitcycles = NDS::ARM9MemTimings[CurSrcAddr >> 14][0] + NDS::ARM9MemTimings[CurDstAddr >> 14][0];
}
else
{
unitcycles = NDS::ARM9MemTimings[CurSrcAddr >> 14][1] + NDS::ARM9MemTimings[CurDstAddr >> 14][1];
if ((CurSrcAddr >> 24) == (CurDstAddr >> 24))
unitcycles++;
/*if (burststart)
{
cycles -= 2;
cycles -= (NDS::ARM9MemTimings[CurSrcAddr >> 14][0] + NDS::ARM9MemTimings[CurDstAddr >> 14][0]);
cycles += unitcycles;
}*/
}
while (IterCount > 0 && !Stall)
{
NDS::ARM9Timestamp += (unitcycles << NDS::ARM9ClockShift);
NDS::ARM9Timestamp += (UnitTimings9_16(burststart) << NDS::ARM9ClockShift);
burststart = false;
if (ConsoleType == 1)
DSi::ARM9Write16(CurDstAddr, DSi::ARM9Read16(CurSrcAddr));
@ -236,29 +576,10 @@ void DMA::Run9()
}
else
{
if ((CurSrcAddr >> 24) == 0x02 && (CurDstAddr >> 24) == 0x02)
{
unitcycles = NDS::ARM9MemTimings[CurSrcAddr >> 14][2] + NDS::ARM9MemTimings[CurDstAddr >> 14][2];
}
else
{
unitcycles = NDS::ARM9MemTimings[CurSrcAddr >> 14][3] + NDS::ARM9MemTimings[CurDstAddr >> 14][3];
if ((CurSrcAddr >> 24) == (CurDstAddr >> 24))
unitcycles++;
else if ((CurSrcAddr >> 24) == 0x02)
unitcycles--;
/*if (burststart)
{
cycles -= 2;
cycles -= (NDS::ARM9MemTimings[CurSrcAddr >> 14][2] + NDS::ARM9MemTimings[CurDstAddr >> 14][2]);
cycles += unitcycles;
}*/
}
while (IterCount > 0 && !Stall)
{
NDS::ARM9Timestamp += (unitcycles << NDS::ARM9ClockShift);
NDS::ARM9Timestamp += (UnitTimings9_32(burststart) << NDS::ARM9ClockShift);
burststart = false;
if (ConsoleType == 1)
DSi::ARM9Write32(CurDstAddr, DSi::ARM9Read32(CurSrcAddr));
@ -313,32 +634,12 @@ void DMA::Run7()
bool burststart = (Running == 2);
Running = 1;
s32 unitcycles;
//s32 lastcycles = cycles;
if (!(Cnt & (1<<26)))
{
if ((CurSrcAddr >> 24) == 0x02 && (CurDstAddr >> 24) == 0x02)
{
unitcycles = NDS::ARM7MemTimings[CurSrcAddr >> 15][0] + NDS::ARM7MemTimings[CurDstAddr >> 15][0];
}
else
{
unitcycles = NDS::ARM7MemTimings[CurSrcAddr >> 15][1] + NDS::ARM7MemTimings[CurDstAddr >> 15][1];
if ((CurSrcAddr >> 23) == (CurDstAddr >> 23))
unitcycles++;
/*if (burststart)
{
cycles -= 2;
cycles -= (NDS::ARM7MemTimings[CurSrcAddr >> 15][0] + NDS::ARM7MemTimings[CurDstAddr >> 15][0]);
cycles += unitcycles;
}*/
}
while (IterCount > 0 && !Stall)
{
NDS::ARM7Timestamp += unitcycles;
NDS::ARM7Timestamp += UnitTimings7_16(burststart);
burststart = false;
if (ConsoleType == 1)
DSi::ARM7Write16(CurDstAddr, DSi::ARM7Read16(CurSrcAddr));
@ -355,29 +656,10 @@ void DMA::Run7()
}
else
{
if ((CurSrcAddr >> 24) == 0x02 && (CurDstAddr >> 24) == 0x02)
{
unitcycles = NDS::ARM7MemTimings[CurSrcAddr >> 15][2] + NDS::ARM7MemTimings[CurDstAddr >> 15][2];
}
else
{
unitcycles = NDS::ARM7MemTimings[CurSrcAddr >> 15][3] + NDS::ARM7MemTimings[CurDstAddr >> 15][3];
if ((CurSrcAddr >> 23) == (CurDstAddr >> 23))
unitcycles++;
else if ((CurSrcAddr >> 24) == 0x02)
unitcycles--;
/*if (burststart)
{
cycles -= 2;
cycles -= (NDS::ARM7MemTimings[CurSrcAddr >> 15][2] + NDS::ARM7MemTimings[CurDstAddr >> 15][2]);
cycles += unitcycles;
}*/
}
while (IterCount > 0 && !Stall)
{
NDS::ARM7Timestamp += unitcycles;
NDS::ARM7Timestamp += UnitTimings7_32(burststart);
burststart = false;
if (ConsoleType == 1)
DSi::ARM7Write32(CurDstAddr, DSi::ARM7Read32(CurSrcAddr));

View File

@ -34,6 +34,11 @@ public:
void WriteCnt(u32 val);
void Start();
u32 UnitTimings9_16(bool burststart);
u32 UnitTimings9_32(bool burststart);
u32 UnitTimings7_16(bool burststart);
u32 UnitTimings7_32(bool burststart);
template <int ConsoleType>
void Run();
@ -78,8 +83,8 @@ private:
u32 CurDstAddr;
u32 RemCount;
u32 IterCount;
u32 SrcAddrInc;
u32 DstAddrInc;
s32 SrcAddrInc;
s32 DstAddrInc;
u32 CountMask;
u32 Running;
@ -89,6 +94,9 @@ private:
bool Stall;
bool IsGXFIFODMA;
u32 MRAMBurstCount;
u8* MRAMBurstTable;
};
#endif

245
src/DMA_Timings.h Normal file
View File

@ -0,0 +1,245 @@
/*
Copyright 2016-2021 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 DMA_TIMINGS_H
#define DMA_TIMINGS_H
namespace DMATiming
{
// DMA timing tables
//
// DMA timings on the DS are normally straightforward, except in one case: when
// main RAM is involved.
// Main RAM to main RAM is the easy case: 16c/unit in 16bit mode, 18c/unit in 32bit
// mode.
// It gets more complicated when transferring from main RAM to somewhere else, or
// vice versa: main RAM supports burst accesses, but the rules dictating how long
// bursts can be are weird and inconsistent. Main RAM also supports parallel
// memory operations, to some extent.
// I haven't figured out the full logic behind it, let alone how to emulate it
// efficiently, so for now we will use these tables.
// A zero denotes the end of a burst pattern.
//
// Note: burst patterns only apply when the main RAM address is incrementing.
// A fixed or decrementing address results in nonsequential accesses.
//
// Note about GBA slot/wifi timings: these take into account the sequential timing
// setting. Timings are such that the nonseq setting only matters for the first
// access, and minor edge cases (like the last of a 0x20000-byte block).
u8 MRAMDummy[1] = {0};
u8 MRAMRead16Bursts[][256] =
{
// main RAM to regular 16bit or 32bit bus (similar)
{7, 3, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2,
7, 3, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2,
7, 3,
0},
// main RAM to GBA/wifi, seq=4
{8, 6, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5,
0},
// main RAM to GBA/wifi, seq=6
{10, 8, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7,
12, 8, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7,
12, 8, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7,
12, 8, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7,
12, 8, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7,
12, 8, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7,
12, 8, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7,
12, 8,
0},
};
u8 MRAMRead32Bursts[][256] =
{
// main RAM to regular 16bit bus
{9, 4, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 9,
0},
// main RAM to regular 32bit bus
{9, 3, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2,
0},
// main RAM to GBA/wifi, seq=4
{14, 10, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9,
13, 10, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9,
13, 10, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9,
13, 10, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9,
13, 10, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9,
13,
0},
// main RAM to GBA/wifi, seq=6
{18, 14, 13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 13,
17, 14, 13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 13,
17, 14, 13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 13,
17, 14, 13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 13,
17, 14, 13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 13,
17,
0},
};
u8 MRAMWrite16Bursts[][256] =
{
// regular 16bit or 32bit bus to main RAM (similar)
{8, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
0},
// GBA/wifi to main RAM, seq=4
{10, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5,
0},
// GBA/wifi to main RAM, seq=6
{9, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7,
0},
};
u8 MRAMWrite32Bursts[][256] =
{
// regular 16bit bus to main RAM
{9, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
0},
// regular 32bit bus to main RAM
{9, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
0},
// GBA/wifi to main RAM, seq=4
{15, 10, 10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10,
0},
// GBA/wifi to main RAM, seq=6
{16, 14, 14, 14, 14, 14, 14, 14, 14, 14,
14, 14, 14, 14, 14, 14, 14, 14,
0},
};
}
#endif // DMA_TIMINGS_H

File diff suppressed because it is too large Load Diff

View File

@ -40,7 +40,6 @@ extern DSi_SDHost* SDMMC;
extern DSi_SDHost* SDIO;
extern FILE* SDMMCFile;
extern FILE* SDIOFile;
const u32 NWRAMSize = 0x40000;
@ -60,6 +59,7 @@ bool Init();
void DeInit();
void Reset();
void SetupDirectBoot();
void SoftReset();
bool LoadBIOS();

View File

@ -31,6 +31,7 @@ namespace DSi_AES
u32 Cnt;
u32 BlkCnt;
u32 RemExtra;
u32 RemBlocks;
bool OutputFlush;
@ -106,6 +107,7 @@ void Reset()
Cnt = 0;
BlkCnt = 0;
RemExtra = 0;
RemBlocks = 0;
OutputFlush = false;
@ -154,6 +156,22 @@ void Reset()
}
void ProcessBlock_CCM_Extra()
{
u8 data[16];
u8 data_rev[16];
*(u32*)&data[0] = InputFIFO.Read();
*(u32*)&data[4] = InputFIFO.Read();
*(u32*)&data[8] = InputFIFO.Read();
*(u32*)&data[12] = InputFIFO.Read();
Swap16(data_rev, data);
for (int i = 0; i < 16; i++) CurMAC[i] ^= data_rev[i];
AES_ECB_encrypt(&Ctx, CurMAC);
}
void ProcessBlock_CCM_Decrypt()
{
u8 data[16];
@ -272,13 +290,14 @@ void WriteCnt(u32 val)
if (!(oldcnt & (1<<31)) && (val & (1<<31)))
{
// transfer start (checkme)
RemExtra = (AESMode < 2) ? (BlkCnt & 0xFFFF) : 0;
RemBlocks = BlkCnt >> 16;
OutputMACDue = false;
if (AESMode == 0 && (!(val & (1<<20)))) printf("AES: CCM-DECRYPT MAC FROM WRFIFO, TODO\n");
if (RemBlocks > 0)
if ((RemBlocks > 0) || (RemExtra > 0))
{
u8 key[16];
u8 iv[16];
@ -288,8 +307,6 @@ void WriteCnt(u32 val)
if (AESMode < 2)
{
if (BlkCnt & 0xFFFF) printf("AES: CCM EXTRA LEN TODO\n");
u32 maclen = (val >> 16) & 0x7;
if (maclen < 1) maclen = 1;
@ -325,8 +342,8 @@ void WriteCnt(u32 val)
}
}
//printf("AES CNT: %08X / mode=%d key=%d inDMA=%d outDMA=%d blocks=%d\n",
// val, AESMode, (val >> 26) & 0x3, InputDMASize, OutputDMASize, RemBlocks);
//printf("AES CNT: %08X / mode=%d key=%d inDMA=%d outDMA=%d blocks=%d (BLKCNT=%08X)\n",
// val, AESMode, (val >> 26) & 0x3, InputDMASize, OutputDMASize, RemBlocks, BlkCnt);
}
void WriteBlkCnt(u32 val)
@ -380,7 +397,7 @@ void WriteInputFIFO(u32 val)
void CheckInputDMA()
{
if (RemBlocks == 0) return;
if (RemBlocks == 0 && RemExtra == 0) return;
if (InputFIFO.Level() <= InputDMASize)
{
@ -401,6 +418,17 @@ void CheckOutputDMA()
}
void Update()
{
if (RemExtra > 0)
{
while (InputFIFO.Level() >= 4 && RemExtra > 0)
{
ProcessBlock_CCM_Extra();
RemExtra--;
}
}
if (RemExtra == 0)
{
while (InputFIFO.Level() >= 4 && OutputFIFO.Level() <= 12 && RemBlocks > 0)
{
@ -414,10 +442,11 @@ void Update()
RemBlocks--;
}
}
CheckOutputDMA();
if (RemBlocks == 0)
if (RemBlocks == 0 && RemExtra == 0)
{
if (AESMode == 0)
{
@ -443,6 +472,15 @@ void Update()
AES_CTR_xcrypt_buffer(&Ctx, CurMAC, 16);
Swap16(OutputMAC, CurMAC);
if (OutputFIFO.Level() <= 12)
{
OutputFIFO.Write(*(u32*)&OutputMAC[0]);
OutputFIFO.Write(*(u32*)&OutputMAC[4]);
OutputFIFO.Write(*(u32*)&OutputMAC[8]);
OutputFIFO.Write(*(u32*)&OutputMAC[12]);
}
else
OutputMACDue = true;
// CHECKME
@ -485,16 +523,13 @@ void WriteMAC(u32 offset, u32 val, u32 mask)
//printf("AES: MAC: "); _printhex(MAC, 16);
}
void DeriveNormalKey(u32 slot)
void DeriveNormalKey(u8* keyX, u8* keyY, u8* normalkey)
{
const u8 key_const[16] = {0xFF, 0xFE, 0xFB, 0x4E, 0x29, 0x59, 0x02, 0x58, 0x2A, 0x68, 0x0F, 0x5F, 0x1A, 0x4F, 0x3E, 0x79};
u8 tmp[16];
//printf("slot%d keyX: ", slot); _printhex(KeyX[slot], 16);
//printf("slot%d keyY: ", slot); _printhex(KeyY[slot], 16);
for (int i = 0; i < 16; i++)
tmp[i] = KeyX[slot][i] ^ KeyY[slot][i];
tmp[i] = keyX[i] ^ keyY[i];
u32 carry = 0;
for (int i = 0; i < 16; i++)
@ -506,9 +541,7 @@ void DeriveNormalKey(u32 slot)
ROL16(tmp, 42);
//printf("derive normalkey %d\n", slot); _printhex(tmp, 16);
memcpy(KeyNormal[slot], tmp, 16);
memcpy(normalkey, tmp, 16);
}
void WriteKeyNormal(u32 slot, u32 offset, u32 val, u32 mask)
@ -539,67 +572,8 @@ void WriteKeyY(u32 slot, u32 offset, u32 val, u32 mask)
if (offset >= 0xC)
{
DeriveNormalKey(slot);
DeriveNormalKey(KeyX[slot], KeyY[slot], KeyNormal[slot]);
}
}
// utility
void GetModcryptKey(u8* romheader, u8* key)
{
if ((romheader[0x01C] & 0x04) || (romheader[0x1BF] & 0x80))
{
// dev key
memcpy(key, &romheader[0x000], 16);
return;
}
u8 oldkeys[16*3];
memcpy(&oldkeys[16*0], KeyX[0], 16);
memcpy(&oldkeys[16*1], KeyY[0], 16);
memcpy(&oldkeys[16*2], KeyNormal[0], 16);
KeyX[0][8] = romheader[0x00C];
KeyX[0][9] = romheader[0x00D];
KeyX[0][10] = romheader[0x00E];
KeyX[0][11] = romheader[0x00F];
KeyX[0][12] = romheader[0x00F];
KeyX[0][13] = romheader[0x00E];
KeyX[0][14] = romheader[0x00D];
KeyX[0][15] = romheader[0x00C];
memcpy(KeyY[0], &romheader[0x350], 16);
DeriveNormalKey(0);
memcpy(key, KeyNormal[0], 16);
memcpy(KeyX[0], &oldkeys[16*0], 16);
memcpy(KeyY[0], &oldkeys[16*1], 16);
memcpy(KeyNormal[0], &oldkeys[16*2], 16);
}
void ApplyModcrypt(u8* data, u32 len, u8* key, u8* iv)
{
u8 key_rev[16], iv_rev[16];
u8 data_rev[16];
u8 oldkeys[16*2];
memcpy(&oldkeys[16*0], Ctx.RoundKey, 16);
memcpy(&oldkeys[16*1], Ctx.Iv, 16);
Swap16(key_rev, key);
Swap16(iv_rev, iv);
AES_init_ctx_iv(&Ctx, key_rev, iv_rev);
for (u32 i = 0; i < len; i += 16)
{
Swap16(data_rev, &data[i]);
AES_CTR_xcrypt_buffer(&Ctx, data_rev, 16);
Swap16(&data[i], data_rev);
}
memcpy(Ctx.RoundKey, &oldkeys[16*0], 16);
memcpy(Ctx.Iv, &oldkeys[16*1], 16);
}
}

View File

@ -46,8 +46,8 @@ void WriteKeyNormal(u32 slot, u32 offset, u32 val, u32 mask);
void WriteKeyX(u32 slot, u32 offset, u32 val, u32 mask);
void WriteKeyY(u32 slot, u32 offset, u32 val, u32 mask);
void GetModcryptKey(u8* romheader, u8* key);
void ApplyModcrypt(u8* data, u32 len, u8* key, u8* iv);
void Swap16(u8* dst, u8* src);
void DeriveNormalKey(u8* keyX, u8* keyY, u8* normalkey);
}

View File

@ -206,7 +206,7 @@ void OnMBKCfg(char bank, u32 slot, u8 oldcfg, u8 newcfg, u8* nwrambacking)
inline bool IsDSPCoreEnabled()
{
return (DSi::SCFG_Clock9 & (1<<1)) && SCFG_RST && (DSP_PCFG & (1<<0));
return (DSi::SCFG_Clock9 & (1<<1)) && SCFG_RST && (!(DSP_PCFG & (1<<0)));
}
bool DSPCatchUp()
@ -560,6 +560,7 @@ void Run(u32 cycles)
DSPTimestamp += cycles;
NDS::CancelEvent(NDS::Event_DSi_DSP);
NDS::ScheduleEvent(NDS::Event_DSi_DSP, false,
16384/*from citra (TeakraSlice)*/, DSPCatchUpU32, 0);
}

View File

@ -72,6 +72,8 @@ void Reset()
Registers[0x81] = 0x64;
}
u8 GetBootFlag() { return Registers[0x70]; }
void Start()
{
//printf("BPTWL: start\n");

View File

@ -19,6 +19,13 @@
#ifndef DSI_I2C_H
#define DSI_I2C_H
namespace DSi_BPTWL
{
u8 GetBootFlag();
}
namespace DSi_I2C
{

1165
src/DSi_NAND.cpp Normal file

File diff suppressed because it is too large Load Diff

59
src/DSi_NAND.h Normal file
View File

@ -0,0 +1,59 @@
/*
Copyright 2016-2021 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 DSI_NAND_H
#define DSI_NAND_H
#include "types.h"
#include "NDS_Header.h"
#include <vector>
#include <string>
namespace DSi_NAND
{
enum
{
TitleData_PublicSav,
TitleData_PrivateSav,
TitleData_BannerSav,
};
bool Init(FILE* nand, u8* es_keyY);
void DeInit();
void GetIDs(u8* emmc_cid, u64& consoleid);
void ReadHardwareInfo(u8* dataS, u8* dataN);
void ReadUserData(u8* data);
void PatchUserData();
void ListTitles(u32 category, std::vector<u32>& titlelist);
bool TitleExists(u32 category, u32 titleid);
void GetTitleInfo(u32 category, u32 titleid, u32& version, NDSHeader* header, NDSBanner* banner);
bool ImportTitle(const char* appfile, u8* tmd, bool readonly);
void DeleteTitle(u32 category, u32 titleid);
u32 GetTitleDataMask(u32 category, u32 titleid);
bool ImportTitleData(u32 category, u32 titleid, int type, const char* file);
bool ExportTitleData(u32 category, u32 titleid, int type, const char* file);
}
#endif // DSI_NAND_H

View File

@ -22,7 +22,6 @@
#include "DSi_SD.h"
#include "DSi_NWifi.h"
#include "Platform.h"
#include "Config.h"
// observed IRQ behavior during transfers
@ -112,9 +111,20 @@ void DSi_SDHost::Reset()
DSi_MMCStorage* sd;
DSi_MMCStorage* mmc;
if (Config::DSiSDEnable)
if (Platform::GetConfigBool(Platform::DSiSD_Enable))
{
sd = new DSi_MMCStorage(this, false, DSi::SDIOFile);
std::string folderpath;
if (Platform::GetConfigBool(Platform::DSiSD_FolderSync))
folderpath = Platform::GetConfigString(Platform::DSiSD_FolderPath);
else
folderpath = "";
sd = new DSi_MMCStorage(this,
false,
Platform::GetConfigString(Platform::DSiSD_ImagePath),
(u64)Platform::GetConfigInt(Platform::DSiSD_ImageSize) * 1024 * 1024,
Platform::GetConfigBool(Platform::DSiSD_ReadOnly),
folderpath);
u8 sd_cid[16] = {0xBD, 0x12, 0x34, 0x56, 0x78, 0x03, 0x4D, 0x30, 0x30, 0x46, 0x50, 0x41, 0x00, 0x00, 0x15, 0x00};
sd->SetCID(sd_cid);
}
@ -427,7 +437,10 @@ u16 DSi_SDHost::Read(u32 addr)
if (!Num)
{
if (Ports[0]) // basic check of whether the SD card is inserted
ret |= 0x00B0;
{
ret |= 0x0030;
if (!Ports[0]->ReadOnly) ret |= 0x0080;
}
else
ret |= 0x0008;
}
@ -714,14 +727,34 @@ void DSi_SDHost::CheckSwapFIFO()
#define MMC_DESC (Internal?"NAND":"SDcard")
DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, bool internal, FILE* file) : DSi_SDDevice(host)
DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, bool internal, FILE* file)
: DSi_SDDevice(host)
{
Internal = internal;
File = file;
SD = nullptr;
}
DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, bool internal, std::string filename, u64 size, bool readonly, std::string sourcedir)
: DSi_SDDevice(host)
{
Internal = internal;
File = nullptr;
SD = new FATStorage(filename, size, readonly, sourcedir);
SD->Open();
ReadOnly = readonly;
}
DSi_MMCStorage::~DSi_MMCStorage()
{}
{
if (SD)
{
SD->Close();
delete SD;
}
}
void DSi_MMCStorage::Reset()
{
@ -947,13 +980,17 @@ u32 DSi_MMCStorage::ReadBlock(u64 addr)
len = Host->GetTransferrableLen(len);
u8 data[0x200];
if (File)
if (SD)
{
SD->ReadSectors((u32)(addr >> 9), 1, data);
}
else if (File)
{
fseek(File, addr, SEEK_SET);
fread(data, 1, len, File);
fread(&data[addr & 0x1FF], 1, len, File);
}
return Host->DataRX(data, len);
return Host->DataRX(&data[addr & 0x1FF], len);
}
u32 DSi_MMCStorage::WriteBlock(u64 addr)
@ -962,12 +999,26 @@ u32 DSi_MMCStorage::WriteBlock(u64 addr)
len = Host->GetTransferrableLen(len);
u8 data[0x200];
if ((len = Host->DataTX(data, len)))
if (len < 0x200)
{
if (File)
if (SD)
{
SD->ReadSectors((u32)(addr >> 9), 1, data);
}
}
if ((len = Host->DataTX(&data[addr & 0x1FF], len)))
{
if (!ReadOnly)
{
if (SD)
{
SD->WriteSectors((u32)(addr >> 9), 1, data);
}
else if (File)
{
fseek(File, addr, SEEK_SET);
fwrite(data, 1, len, File);
fwrite(&data[addr & 0x1FF], 1, len, File);
}
}
}

View File

@ -21,6 +21,7 @@
#include <string.h>
#include "FIFO.h"
#include "FATStorage.h"
class DSi_SDDevice;
@ -102,7 +103,7 @@ private:
class DSi_SDDevice
{
public:
DSi_SDDevice(DSi_SDHost* host) { Host = host; IRQ = false; }
DSi_SDDevice(DSi_SDHost* host) { Host = host; IRQ = false; ReadOnly = false; }
virtual ~DSi_SDDevice() {}
virtual void Reset() = 0;
@ -111,6 +112,7 @@ public:
virtual void ContinueTransfer() = 0;
bool IRQ;
bool ReadOnly;
protected:
DSi_SDHost* Host;
@ -121,6 +123,7 @@ class DSi_MMCStorage : public DSi_SDDevice
{
public:
DSi_MMCStorage(DSi_SDHost* host, bool internal, FILE* file);
DSi_MMCStorage(DSi_SDHost* host, bool internal, std::string filename, u64 size, bool readonly, std::string sourcedir);
~DSi_MMCStorage();
void Reset();
@ -135,6 +138,7 @@ public:
private:
bool Internal;
FILE* File;
FATStorage* SD;
u8 CID[16];
u8 CSD[16];

View File

@ -81,6 +81,11 @@ void DoSavestate(Savestate* file)
// TODO!!
}
void SetMode(u8 mode)
{
TSCMode = mode;
}
void SetTouchCoords(u16 x, u16 y)
{
if (TSCMode == 0x00)

View File

@ -29,6 +29,9 @@ void DeInit();
void Reset();
void DoSavestate(Savestate* file);
// 00=DS-mode 01=normal
void SetMode(u8 mode);
void SetTouchCoords(u16 x, u16 y);
void MicInputFrame(s16* data, int samples);

1123
src/FATStorage.cpp Normal file

File diff suppressed because it is too large Load Diff

101
src/FATStorage.h Normal file
View File

@ -0,0 +1,101 @@
/*
Copyright 2016-2021 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 FATSTORAGE_H
#define FATSTORAGE_H
#include <stdio.h>
#include <string>
#include <map>
#include <filesystem>
#include "types.h"
#include "fatfs/ff.h"
class FATStorage
{
public:
FATStorage(std::string filename, u64 size, bool readonly, std::string sourcedir);
~FATStorage();
bool Open();
void Close();
bool InjectFile(std::string path, u8* data, u32 len);
u32 ReadSectors(u32 start, u32 num, u8* data);
u32 WriteSectors(u32 start, u32 num, u8* data);
private:
std::string FilePath;
std::string IndexPath;
std::string SourceDir;
bool ReadOnly;
FILE* File;
u64 FileSize;
static FILE* FF_File;
static u64 FF_FileSize;
static UINT FF_ReadStorage(BYTE* buf, LBA_t sector, UINT num);
static UINT FF_WriteStorage(BYTE* buf, LBA_t sector, UINT num);
static u32 ReadSectorsInternal(FILE* file, u64 filelen, u32 start, u32 num, u8* data);
static u32 WriteSectorsInternal(FILE* file, u64 filelen, u32 start, u32 num, u8* data);
void LoadIndex();
void SaveIndex();
bool ExportFile(std::string path, std::filesystem::path out);
void ExportDirectory(std::string path, std::string outbase, int level);
bool DeleteHostDirectory(std::string path, std::string outbase, int level);
void ExportChanges(std::string outbase);
bool CanFitFile(u32 len);
bool DeleteDirectory(std::string path, int level);
void CleanupDirectory(std::string sourcedir, std::string path, int level);
bool ImportFile(std::string path, std::filesystem::path in);
bool ImportDirectory(std::string sourcedir);
u64 GetDirectorySize(std::filesystem::path sourcedir);
bool Load(std::string filename, u64 size, std::string sourcedir);
bool Save();
typedef struct
{
std::string Path;
bool IsReadOnly;
} DirIndexEntry;
typedef struct
{
std::string Path;
bool IsReadOnly;
u64 Size;
s64 LastModified;
u32 LastModifiedInternal;
} FileIndexEntry;
std::map<std::string, DirIndexEntry> DirIndex;
std::map<std::string, FileIndexEntry> FileIndex;
};
#endif // FATSTORAGE_H

1747
src/FreeBIOS.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1061,6 +1061,12 @@ void FinishFrame(u32 lines)
AssignFramebuffers();
TotalScanlines = lines;
if (GPU3D::AbortFrame)
{
GPU3D::RestartFrame();
GPU3D::AbortFrame = false;
}
}
void StartScanline(u32 line)
@ -1180,6 +1186,7 @@ void SetVCount(u16 val)
// 3D engine seems to give up on the current frame in that situation, repeating the last two scanlines
// TODO: also check the various DMA types that can be involved
GPU3D::AbortFrame |= NextVCount != val;
NextVCount = val;
}

View File

@ -22,7 +22,6 @@
#include "NDS.h"
#include "GPU.h"
#include "FIFO.h"
#include "Config.h"
// 3D engine notes
@ -275,6 +274,8 @@ u32 FlushAttributes;
std::unique_ptr<GPU3D::Renderer3D> CurrentRenderer = {};
bool AbortFrame;
bool Init()
{
return true;
@ -380,6 +381,8 @@ void Reset()
ResetRenderingState();
RenderXPos = 0;
AbortFrame = false;
}
void DoSavestate(Savestate* file)
@ -621,6 +624,8 @@ void DoSavestate(Savestate* file)
file->Bool32(&UseShininessTable);
file->VarArray(ShininessTable, 128*sizeof(u8));
file->Bool32(&AbortFrame);
}
@ -2610,6 +2615,8 @@ void SetRenderXPos(u16 xpos)
u32 ScrolledLine[256];
u32* GetLine(int line)
{
if (!AbortFrame)
{
u32* rawline = CurrentRenderer->GetLine(line);
@ -2633,6 +2640,11 @@ u32* GetLine(int line)
for (; i < 256; i++)
ScrolledLine[i] = 0;
}
}
else
{
memset(ScrolledLine, 0, 256*4);
}
return ScrolledLine;
}

View File

@ -97,6 +97,8 @@ extern u16 RenderXPos;
extern std::array<Polygon*,2048> RenderPolygonRAM;
extern u32 RenderNumPolygons;
extern bool AbortFrame;
extern u64 Timestamp;
bool Init();

View File

@ -22,7 +22,6 @@
#include <string.h>
#include "NDS.h"
#include "GPU.h"
#include "Config.h"
#include "GPU3D_OpenGL_shaders.h"
namespace GPU3D

View File

@ -18,11 +18,11 @@
#include "GPU3D_Soft.h"
#include <algorithm>
#include <stdio.h>
#include <string.h>
#include "NDS.h"
#include "GPU.h"
#include "Config.h"
namespace GPU3D
@ -756,11 +756,10 @@ void SoftRenderer::RenderShadowMaskScanline(RendererPolygon* rp, s32 y)
rp->SlopeR.EdgeParams_YMajor(&l_edgelen, &l_edgecov);
rp->SlopeL.EdgeParams_YMajor(&r_edgelen, &r_edgecov);
s32 tmp;
tmp = xstart; xstart = xend; xend = tmp;
tmp = wl; wl = wr; wr = tmp;
tmp = zl; zl = zr; zr = tmp;
tmp = (s32)l_filledge; l_filledge = r_filledge; r_filledge = (bool)tmp;
std::swap(xstart, xend);
std::swap(wl, wr);
std::swap(zl, zr);
std::swap(l_filledge, r_filledge);
}
else
{
@ -977,11 +976,10 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y)
rp->SlopeR.EdgeParams_YMajor(&l_edgelen, &l_edgecov);
rp->SlopeL.EdgeParams_YMajor(&r_edgelen, &r_edgecov);
s32 tmp;
tmp = xstart; xstart = xend; xend = tmp;
tmp = wl; wl = wr; wr = tmp;
tmp = zl; zl = zr; zr = tmp;
tmp = (s32)l_filledge; l_filledge = r_filledge; r_filledge = (bool)tmp;
std::swap(xstart, xend);
std::swap(wl, wr);
std::swap(zl, zr);
std::swap(l_filledge, r_filledge);
}
else
{
@ -1646,7 +1644,7 @@ void SoftRenderer::RenderPolygons(bool threaded, Polygon** polygons, int npolys)
void SoftRenderer::VCount144()
{
if (RenderThreadRunning.load(std::memory_order_relaxed))
if (RenderThreadRunning.load(std::memory_order_relaxed) && !GPU3D::AbortFrame)
Platform::Semaphore_Wait(Sema_RenderDone);
}

View File

@ -23,7 +23,6 @@
#include "NDS.h"
#include "GPU.h"
#include "Config.h"
#include "OpenGLSupport.h"
#include "GPU_OpenGL_shaders.h"

View File

@ -19,7 +19,6 @@
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "Config.h"
#include "NDS.h"
#include "ARM.h"
#include "NDSCart.h"
@ -34,6 +33,7 @@
#include "AREngine.h"
#include "Platform.h"
#include "NDSCart_SRAMManager.h"
#include "FreeBIOS.h"
#ifdef JIT_ENABLED
#include "ARMJIT.h"
@ -71,12 +71,18 @@ namespace NDS
int ConsoleType;
u8 ARM9MemTimings[0x40000][4];
u8 ARM9MemTimings[0x40000][8];
u32 ARM9Regions[0x40000];
u8 ARM7MemTimings[0x20000][4];
u32 ARM7Regions[0x20000];
ARMv5* ARM9;
ARMv4* ARM7;
#ifdef JIT_ENABLED
bool EnableJIT;
#endif
u32 NumFrames;
u32 NumLagFrames;
bool LagFrameFlag;
@ -86,6 +92,7 @@ u64 FrameStartTimestamp;
int CurCPU;
const s32 kMaxIterationCycles = 64;
const s32 kIterationCycleMargin = 8;
u32 ARM9ClockShift;
@ -164,7 +171,6 @@ bool Running;
bool RunningGame;
void DivDone(u32 param);
void SqrtDone(u32 param);
void RunTimer(u32 tid, s32 cycles);
@ -237,14 +243,12 @@ void DeInit()
}
void SetARM9RegionTimings(u32 addrstart, u32 addrend, int buswidth, int nonseq, int seq)
void SetARM9RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, int nonseq, int seq)
{
addrstart >>= 14;
addrend >>= 14;
addrstart >>= 2;
addrend >>= 2;
if (addrend == 0x3FFFF) addrend++;
int N16, S16, N32, S32;
int N16, S16, N32, S32, cpuN;
N16 = nonseq;
S16 = seq;
if (buswidth == 16)
@ -258,25 +262,33 @@ void SetARM9RegionTimings(u32 addrstart, u32 addrend, int buswidth, int nonseq,
S32 = S16;
}
// nonseq accesses on the CPU get a 3-cycle penalty for all regions except main RAM
cpuN = (region == Mem9_MainRAM) ? 0 : 3;
for (u32 i = addrstart; i < addrend; i++)
{
ARM9MemTimings[i][0] = N16;
// CPU timings
ARM9MemTimings[i][0] = N16 + cpuN;
ARM9MemTimings[i][1] = S16;
ARM9MemTimings[i][2] = N32;
ARM9MemTimings[i][2] = N32 + cpuN;
ARM9MemTimings[i][3] = S32;
// DMA timings
ARM9MemTimings[i][4] = N16;
ARM9MemTimings[i][5] = S16;
ARM9MemTimings[i][6] = N32;
ARM9MemTimings[i][7] = S32;
ARM9Regions[i] = region;
}
ARM9->UpdateRegionTimings(addrstart<<14, addrend == 0x40000
? 0xFFFFFFFF
: (addrend<<14));
ARM9->UpdateRegionTimings(addrstart<<2, addrend<<2);
}
void SetARM7RegionTimings(u32 addrstart, u32 addrend, int buswidth, int nonseq, int seq)
void SetARM7RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, int nonseq, int seq)
{
addrstart >>= 15;
addrend >>= 15;
if (addrend == 0x1FFFF) addrend++;
addrstart >>= 3;
addrend >>= 3;
int N16, S16, N32, S32;
N16 = nonseq;
@ -294,10 +306,13 @@ void SetARM7RegionTimings(u32 addrstart, u32 addrend, int buswidth, int nonseq,
for (u32 i = addrstart; i < addrend; i++)
{
// CPU and DMA timings are the same
ARM7MemTimings[i][0] = N16;
ARM7MemTimings[i][1] = S16;
ARM7MemTimings[i][2] = N32;
ARM7MemTimings[i][3] = S32;
ARM7Regions[i] = region;
}
}
@ -308,32 +323,32 @@ void InitTimings()
// Similarly for any unmapped VRAM area.
// Need to check whether supporting these timing characteristics would impact performance
// (especially wrt VRAM mirroring and overlapping and whatnot).
// Also, each VRAM bank is its own memory region. This would matter when DMAing from a VRAM
// bank to another (if this is a thing) for example.
// ARM9
// TODO: +3c nonseq waitstate doesn't apply to DMA!
// but of course mainRAM always gets 8c nonseq waitstate
// TODO: check in detail how WRAM works, although it seems to be one region.
// TODO: DSi-specific timings!!
SetARM9RegionTimings(0x00000000, 0xFFFFFFFF, 32, 1 + 3, 1); // void
SetARM9RegionTimings(0x00000, 0x100000, 0, 32, 1, 1); // void
SetARM9RegionTimings(0xFFFF0000, 0xFFFFFFFF, 32, 1 + 3, 1); // BIOS
SetARM9RegionTimings(0x02000000, 0x03000000, 16, 8, 1); // main RAM
SetARM9RegionTimings(0x03000000, 0x04000000, 32, 1 + 3, 1); // ARM9/shared WRAM
SetARM9RegionTimings(0x04000000, 0x05000000, 32, 1 + 3, 1); // IO
SetARM9RegionTimings(0x05000000, 0x06000000, 16, 1 + 3, 1); // palette
SetARM9RegionTimings(0x06000000, 0x07000000, 16, 1 + 3, 1); // VRAM
SetARM9RegionTimings(0x07000000, 0x08000000, 32, 1 + 3, 1); // OAM
SetARM9RegionTimings(0xFFFF0, 0x100000, Mem9_BIOS, 32, 1, 1); // BIOS
SetARM9RegionTimings(0x02000, 0x03000, Mem9_MainRAM, 16, 8, 1); // main RAM
SetARM9RegionTimings(0x03000, 0x04000, Mem9_WRAM, 32, 1, 1); // ARM9/shared WRAM
SetARM9RegionTimings(0x04000, 0x05000, Mem9_IO, 32, 1, 1); // IO
SetARM9RegionTimings(0x05000, 0x06000, Mem9_Pal, 16, 1, 1); // palette
SetARM9RegionTimings(0x06000, 0x07000, Mem9_VRAM, 16, 1, 1); // VRAM
SetARM9RegionTimings(0x07000, 0x08000, Mem9_OAM, 32, 1, 1); // OAM
// ARM7
SetARM7RegionTimings(0x00000000, 0xFFFFFFFF, 32, 1, 1); // void
SetARM7RegionTimings(0x00000, 0x100000, 0, 32, 1, 1); // void
SetARM7RegionTimings(0x00000000, 0x00010000, 32, 1, 1); // BIOS
SetARM7RegionTimings(0x02000000, 0x03000000, 16, 8, 1); // main RAM
SetARM7RegionTimings(0x03000000, 0x04000000, 32, 1, 1); // ARM7/shared WRAM
SetARM7RegionTimings(0x04000000, 0x04800000, 32, 1, 1); // IO
SetARM7RegionTimings(0x06000000, 0x07000000, 16, 1, 1); // ARM7 VRAM
SetARM7RegionTimings(0x00000, 0x00010, Mem7_BIOS, 32, 1, 1); // BIOS
SetARM7RegionTimings(0x02000, 0x03000, Mem7_MainRAM, 16, 8, 1); // main RAM
SetARM7RegionTimings(0x03000, 0x04000, Mem7_WRAM, 32, 1, 1); // ARM7/shared WRAM
SetARM7RegionTimings(0x04000, 0x04800, Mem7_IO, 32, 1, 1); // IO
SetARM7RegionTimings(0x06000, 0x07000, Mem7_VRAM, 16, 1, 1); // ARM7 VRAM
// handled later: GBA slot, wifi
}
@ -342,47 +357,39 @@ void SetupDirectBoot()
{
if (ConsoleType == 1)
{
printf("!! DIRECT BOOT NOT SUPPORTED IN DSI MODE\n");
return;
DSi::SetupDirectBoot();
}
u32 bootparams[8];
memcpy(bootparams, &NDSCart::CartROM[0x20], 8*4);
printf("ARM9: offset=%08X entry=%08X RAM=%08X size=%08X\n",
bootparams[0], bootparams[1], bootparams[2], bootparams[3]);
printf("ARM7: offset=%08X entry=%08X RAM=%08X size=%08X\n",
bootparams[4], bootparams[5], bootparams[6], bootparams[7]);
else
{
MapSharedWRAM(3);
u32 arm9start = 0;
// load the ARM9 secure area
if (bootparams[0] >= 0x4000 && bootparams[0] < 0x8000)
if (NDSCart::Header.ARM9ROMOffset >= 0x4000 && NDSCart::Header.ARM9ROMOffset < 0x8000)
{
u8 securearea[0x800];
NDSCart::DecryptSecureArea(securearea);
for (u32 i = 0; i < 0x800; i+=4)
{
ARM9Write32(bootparams[2]+i, *(u32*)&securearea[i]);
ARM9Write32(NDSCart::Header.ARM9RAMAddress+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)
for (u32 i = arm9start; i < NDSCart::Header.ARM9Size; i+=4)
{
u32 tmp = *(u32*)&NDSCart::CartROM[bootparams[0]+i];
ARM9Write32(bootparams[2]+i, tmp);
u32 tmp = *(u32*)&NDSCart::CartROM[NDSCart::Header.ARM9ROMOffset+i];
ARM9Write32(NDSCart::Header.ARM9RAMAddress+i, tmp);
}
for (u32 i = 0; i < bootparams[7]; i+=4)
for (u32 i = 0; i < NDSCart::Header.ARM7Size; i+=4)
{
u32 tmp = *(u32*)&NDSCart::CartROM[bootparams[4]+i];
ARM7Write32(bootparams[6]+i, tmp);
u32 tmp = *(u32*)&NDSCart::CartROM[NDSCart::Header.ARM7ROMOffset+i];
ARM7Write32(NDSCart::Header.ARM7RAMAddress+i, tmp);
}
for (u32 i = 0; i < 0x170; i+=4)
@ -393,38 +400,64 @@ void SetupDirectBoot()
ARM9Write32(0x027FF800, NDSCart::CartID);
ARM9Write32(0x027FF804, NDSCart::CartID);
ARM9Write16(0x027FF808, *(u16*)&NDSCart::CartROM[0x15E]);
ARM9Write16(0x027FF80A, *(u16*)&NDSCart::CartROM[0x6C]);
ARM9Write16(0x027FF808, NDSCart::Header.HeaderCRC16);
ARM9Write16(0x027FF80A, NDSCart::Header.SecureAreaCRC16);
ARM9Write16(0x027FF850, 0x5835);
ARM9Write32(0x027FFC00, NDSCart::CartID);
ARM9Write32(0x027FFC04, NDSCart::CartID);
ARM9Write16(0x027FFC08, *(u16*)&NDSCart::CartROM[0x15E]);
ARM9Write16(0x027FFC0A, *(u16*)&NDSCart::CartROM[0x6C]);
ARM9Write16(0x027FFC08, NDSCart::Header.HeaderCRC16);
ARM9Write16(0x027FFC0A, NDSCart::Header.SecureAreaCRC16);
ARM9Write16(0x027FFC10, 0x5835);
ARM9Write16(0x027FFC30, 0xFFFF);
ARM9Write16(0x027FFC40, 0x0001);
ARM7BIOSProt = 0x1204;
SPI_Firmware::SetupDirectBoot(false);
ARM9->CP15Write(0x100, 0x00012078);
ARM9->CP15Write(0x200, 0x00000042);
ARM9->CP15Write(0x201, 0x00000042);
ARM9->CP15Write(0x300, 0x00000002);
ARM9->CP15Write(0x502, 0x15111011);
ARM9->CP15Write(0x503, 0x05100011);
ARM9->CP15Write(0x600, 0x04000033);
ARM9->CP15Write(0x601, 0x04000033);
ARM9->CP15Write(0x610, 0x0200002B);
ARM9->CP15Write(0x611, 0x0200002B);
ARM9->CP15Write(0x620, 0x00000000);
ARM9->CP15Write(0x621, 0x00000000);
ARM9->CP15Write(0x630, 0x08000035);
ARM9->CP15Write(0x631, 0x08000035);
ARM9->CP15Write(0x640, 0x0300001B);
ARM9->CP15Write(0x641, 0x0300001B);
ARM9->CP15Write(0x650, 0x00000000);
ARM9->CP15Write(0x651, 0x00000000);
ARM9->CP15Write(0x660, 0xFFFF001D);
ARM9->CP15Write(0x661, 0xFFFF001D);
ARM9->CP15Write(0x670, 0x027FF017);
ARM9->CP15Write(0x671, 0x027FF017);
ARM9->CP15Write(0x910, 0x0300000A);
ARM9->CP15Write(0x911, 0x00000020);
ARM9->CP15Write(0x100, ARM9->CP15Read(0x100) | 0x00050000);
}
ARM9->R[12] = bootparams[1];
ARM9->R[12] = NDSCart::Header.ARM9EntryAddress;
ARM9->R[13] = 0x03002F7C;
ARM9->R[14] = bootparams[1];
ARM9->R[14] = NDSCart::Header.ARM9EntryAddress;
ARM9->R_IRQ[0] = 0x03003F80;
ARM9->R_SVC[0] = 0x03003FC0;
ARM7->R[12] = bootparams[5];
ARM7->R[12] = NDSCart::Header.ARM7EntryAddress;
ARM7->R[13] = 0x0380FD80;
ARM7->R[14] = bootparams[5];
ARM7->R[14] = NDSCart::Header.ARM7EntryAddress;
ARM7->R_IRQ[0] = 0x0380FF80;
ARM7->R_SVC[0] = 0x0380FFC0;
ARM9->JumpTo(bootparams[1]);
ARM7->JumpTo(bootparams[5]);
ARM9->JumpTo(NDSCart::Header.ARM9EntryAddress);
ARM7->JumpTo(NDSCart::Header.ARM7EntryAddress);
PostFlag9 = 0x01;
PostFlag7 = 0x01;
@ -440,10 +473,6 @@ void SetupDirectBoot()
SPU::SetBias(0x200);
SetWifiWaitCnt(0x0030);
ARM7BIOSProt = 0x1204;
SPI_Firmware::SetupDirectBoot();
}
void Reset()
@ -451,6 +480,10 @@ void Reset()
FILE* f;
u32 i;
#ifdef JIT_ENABLED
EnableJIT = Platform::GetConfigBool(Platform::JIT_Enable);
#endif
RunningGame = false;
LastSysClockCycles = 0;
@ -460,7 +493,9 @@ void Reset()
// DS BIOSes are always loaded, even in DSi mode
// we need them for DS-compatible mode
f = Platform::OpenLocalFile(Config::BIOS9Path, "rb");
if (Platform::GetConfigBool(Platform::ExternalBIOSEnable))
{
f = Platform::OpenLocalFile(Platform::GetConfigString(Platform::BIOS9Path), "rb");
if (!f)
{
printf("ARM9 BIOS not found\n");
@ -477,7 +512,7 @@ void Reset()
fclose(f);
}
f = Platform::OpenLocalFile(Config::BIOS7Path, "rb");
f = Platform::OpenLocalFile(Platform::GetConfigString(Platform::BIOS7Path), "rb");
if (!f)
{
printf("ARM7 BIOS not found\n");
@ -493,6 +528,12 @@ void Reset()
printf("ARM7 BIOS loaded\n");
fclose(f);
}
}
else
{
memcpy(ARM9BIOS, bios_arm9_bin, bios_arm9_bin_len);
memcpy(ARM7BIOS, bios_arm7_bin, bios_arm7_bin_len);
}
#ifdef JIT_ENABLED
ARMJIT::Reset();
@ -592,12 +633,28 @@ void Reset()
RTC::Reset();
Wifi::Reset();
// TODO: move the SOUNDBIAS/degrade logic to SPU?
// The SOUNDBIAS register does nothing on DSi
SPU::SetApplyBias(ConsoleType == 0);
bool degradeAudio = true;
if (ConsoleType == 1)
{
DSi::Reset();
KeyInput &= ~(1 << (16+6));
degradeAudio = false;
}
int bitrate = Platform::GetConfigInt(Platform::AudioBitrate);
if (bitrate == 1) // Always 10-bit
degradeAudio = true;
else if (bitrate == 2) // Always 16-bit
degradeAudio = false;
SPU::SetDegrade10Bit(degradeAudio);
AREngine::Reset();
}
@ -901,7 +958,7 @@ void RelocateSave(const char* path, bool write)
u64 NextTarget()
{
u64 ret = SysTimestamp + kMaxIterationCycles;
u64 minEvent = UINT64_MAX;
u32 mask = SchedListMask;
for (int i = 0; i < Event_MAX; i++)
@ -909,14 +966,19 @@ u64 NextTarget()
if (!mask) break;
if (mask & 0x1)
{
if (SchedList[i].Timestamp < ret)
ret = SchedList[i].Timestamp;
if (SchedList[i].Timestamp < minEvent)
minEvent = SchedList[i].Timestamp;
}
mask >>= 1;
}
return ret;
u64 max = SysTimestamp + kMaxIterationCycles;
if (minEvent < max + kIterationCycleMargin)
return minEvent;
return max;
}
void RunSystem(u64 timestamp)
@ -953,7 +1015,6 @@ u32 RunFrame()
while (Running && GPU::TotalScanlines==0)
{
// TODO: give it some margin, so it can directly do 17 cycles instead of 16 then 1
u64 target = NextTarget();
ARM9Target = target << ARM9ClockShift;
CurCPU = 0;
@ -1052,7 +1113,7 @@ u32 RunFrame()
u32 RunFrame()
{
#ifdef JIT_ENABLED
if (Config::JIT_Enable)
if (EnableJIT)
return NDS::ConsoleType == 1
? RunFrame<true, 1>()
: RunFrame<true, 0>();
@ -1237,8 +1298,8 @@ void SetWifiWaitCnt(u16 val)
WifiWaitCnt = val;
const int ntimings[4] = {10, 8, 6, 18};
SetARM7RegionTimings(0x04800000, 0x04808000, 16, ntimings[val & 0x3], (val & 0x4) ? 4 : 6);
SetARM7RegionTimings(0x04808000, 0x04810000, 16, ntimings[(val>>3) & 0x3], (val & 0x20) ? 4 : 10);
SetARM7RegionTimings(0x04800, 0x04808, Mem7_Wifi0, 16, ntimings[val & 0x3], (val & 0x4) ? 4 : 6);
SetARM7RegionTimings(0x04808, 0x04810, Mem7_Wifi1, 16, ntimings[(val>>3) & 0x3], (val & 0x20) ? 4 : 10);
}
void SetGBASlotTimings()
@ -1246,31 +1307,36 @@ void SetGBASlotTimings()
const int ntimings[4] = {10, 8, 6, 18};
const u16 openbus[4] = {0xFE08, 0x0000, 0x0000, 0xFFFF};
u16 curcnt;
int ramN, romN, romS;
u16 curcpu = (ExMemCnt[0] >> 7) & 0x1;
u16 curcnt = ExMemCnt[curcpu];
int ramN = ntimings[curcnt & 0x3];
int romN = ntimings[(curcnt>>2) & 0x3];
int romS = (curcnt & 0x10) ? 4 : 6;
curcnt = ExMemCnt[0];
ramN = ntimings[curcnt & 0x3];
romN = ntimings[(curcnt>>2) & 0x3];
romS = (curcnt & 0x10) ? 4 : 6;
// GBA slot timings only apply on the selected side
SetARM9RegionTimings(0x08000000, 0x0A000000, 16, romN + 3, romS);
SetARM9RegionTimings(0x0A000000, 0x0B000000, 8, ramN + 3, ramN);
if (curcpu == 0)
{
SetARM9RegionTimings(0x08000, 0x0A000, Mem9_GBAROM, 16, romN, romS);
SetARM9RegionTimings(0x0A000, 0x0B000, Mem9_GBARAM, 8, ramN, ramN);
curcnt = ExMemCnt[1];
ramN = ntimings[curcnt & 0x3];
romN = ntimings[(curcnt>>2) & 0x3];
romS = (curcnt & 0x10) ? 4 : 6;
SetARM7RegionTimings(0x08000, 0x0A000, 0, 32, 1, 1);
SetARM7RegionTimings(0x0A000, 0x0B000, 0, 32, 1, 1);
}
else
{
SetARM9RegionTimings(0x08000, 0x0A000, 0, 32, 1, 1);
SetARM9RegionTimings(0x0A000, 0x0B000, 0, 32, 1, 1);
SetARM7RegionTimings(0x08000000, 0x0A000000, 16, romN, romS);
SetARM7RegionTimings(0x0A000000, 0x0B000000, 8, ramN, ramN);
SetARM7RegionTimings(0x08000, 0x0A000, Mem7_GBAROM, 16, romN, romS);
SetARM7RegionTimings(0x0A000, 0x0B000, Mem7_GBARAM, 8, ramN, ramN);
}
// this open-bus implementation is a rough way of simulating the way values
// lingering on the bus decay after a while, which is visible at higher waitstates
// for example, the Cartridge Construction Kit relies on this to determine that
// the GBA slot is empty
curcnt = ExMemCnt[(ExMemCnt[0]>>7) & 0x1];
GBACart::SetOpenBusDecay(openbus[(curcnt>>2) & 0x3]);
}
@ -1551,10 +1617,7 @@ void RunTimer(u32 tid, s32 cycles)
{
Timer* timer = &Timers[tid];
u32 oldcount = timer->Counter;
timer->Counter += (cycles << timer->CycleShift);
//if (timer->Counter < oldcount)
// HandleTimerOverflow(tid);
while (timer->Counter >> 26)
{
timer->Counter -= (1 << 26);
@ -1580,6 +1643,38 @@ void RunTimers(u32 cpu)
TimerTimestamp[cpu] += cycles;
}
const s32 TimerPrescaler[4] = {0, 6, 8, 10};
u16 TimerGetCounter(u32 timer)
{
RunTimers(timer>>2);
u32 ret = Timers[timer].Counter;
return ret >> 10;
}
void TimerStart(u32 id, u16 cnt)
{
Timer* timer = &Timers[id];
u16 curstart = timer->Cnt & (1<<7);
u16 newstart = cnt & (1<<7);
RunTimers(id>>2);
timer->Cnt = cnt;
timer->CycleShift = 10 - TimerPrescaler[cnt & 0x03];
if ((!curstart) && newstart)
{
timer->Counter = timer->Reload << 10;
}
if ((cnt & 0x84) == 0x80)
TimerCheckMask[id>>2] |= 0x01 << (id&0x3);
else
TimerCheckMask[id>>2] &= ~(0x01 << (id&0x3));
}
// matching NDMA modes for DSi
@ -1668,55 +1763,6 @@ void StopDMAs(u32 cpu, u32 mode)
const s32 TimerPrescaler[4] = {0, 6, 8, 10};
u16 TimerGetCounter(u32 timer)
{
RunTimers(timer>>2);
u32 ret = Timers[timer].Counter;
return ret >> 10;
}
void TimerStart(u32 id, u16 cnt)
{
Timer* timer = &Timers[id];
u16 curstart = timer->Cnt & (1<<7);
u16 newstart = cnt & (1<<7);
timer->Cnt = cnt;
timer->CycleShift = 10 - TimerPrescaler[cnt & 0x03];
if ((!curstart) && newstart)
{
timer->Counter = timer->Reload << 10;
/*if ((cnt & 0x84) == 0x80)
{
u32 delay = (0x10000 - timer->Reload) << TimerPrescaler[cnt & 0x03];
printf("timer%d IRQ: start %d, reload=%04X cnt=%08X\n", id, delay, timer->Reload, timer->Counter);
CancelEvent(Event_TimerIRQ_0 + id);
ScheduleEvent(Event_TimerIRQ_0 + id, false, delay, HandleTimerOverflow, id);
}*/
}
if ((cnt & 0x84) == 0x80)
{
u32 tmask;
//if ((cnt & 0x03) == 0)
tmask = 0x01 << (id&0x3);
//else
// tmask = 0x10 << (id&0x3);
TimerCheckMask[id>>2] |= tmask;
}
else
TimerCheckMask[id>>2] &= ~(0x11 << (id&0x3));
}
void DivDone(u32 param)
{
DivCnt &= ~0xC000;
@ -1863,7 +1909,7 @@ void debug(u32 param)
//for (int i = 0; i < 9; i++)
// printf("VRAM %c: %02X\n", 'A'+i, GPU::VRAMCNT[i]);
FILE*
/*FILE*
shit = fopen("debug/construct.bin", "wb");
fwrite(ARM9->ITCM, 0x8000, 1, shit);
for (u32 i = 0x02000000; i < 0x02400000; i+=4)
@ -1876,23 +1922,23 @@ void debug(u32 param)
u32 val = ARM7Read32(i);
fwrite(&val, 4, 1, shit);
}
fclose(shit);
fclose(shit);*/
/*FILE*
shit = fopen("debug/power9.bin", "wb");
FILE*
shit = fopen("debug/directboot9.bin", "wb");
for (u32 i = 0x02000000; i < 0x04000000; i+=4)
{
u32 val = DSi::ARM9Read32(i);
fwrite(&val, 4, 1, shit);
}
fclose(shit);
shit = fopen("debug/power7.bin", "wb");
shit = fopen("debug/directboot7.bin", "wb");
for (u32 i = 0x02000000; i < 0x04000000; i+=4)
{
u32 val = DSi::ARM7Read32(i);
fwrite(&val, 4, 1, shit);
}
fclose(shit);*/
fclose(shit);
}
@ -2009,7 +2055,7 @@ u16 ARM9Read16(u32 addr)
(GBACart::SRAMRead(addr+1) << 8);
}
if (addr) printf("unknown arm9 read16 %08X %08X\n", addr, ARM9->R[15]);
//if (addr) printf("unknown arm9 read16 %08X %08X\n", addr, ARM9->R[15]);
return 0;
}
@ -2070,7 +2116,7 @@ u32 ARM9Read32(u32 addr)
(GBACart::SRAMRead(addr+3) << 24);
}
printf("unknown arm9 read32 %08X | %08X %08X\n", addr, ARM9->R[15], ARM9->R[12]);
//printf("unknown arm9 read32 %08X | %08X %08X\n", addr, ARM9->R[15], ARM9->R[12]);
return 0;
}
@ -2178,7 +2224,7 @@ void ARM9Write16(u32 addr, u16 val)
return;
}
if (addr) printf("unknown arm9 write16 %08X %04X\n", addr, val);
//if (addr) printf("unknown arm9 write16 %08X %04X\n", addr, val);
}
void ARM9Write32(u32 addr, u32 val)
@ -2245,7 +2291,7 @@ void ARM9Write32(u32 addr, u32 val)
return;
}
printf("unknown arm9 write32 %08X %08X | %08X\n", addr, val, ARM9->R[15]);
//printf("unknown arm9 write32 %08X %08X | %08X\n", addr, val, ARM9->R[15]);
}
bool ARM9GetMemRegion(u32 addr, bool write, MemRegion* region)
@ -2470,7 +2516,7 @@ u32 ARM7Read32(u32 addr)
(GBACart::SRAMRead(addr+3) << 24);
}
printf("unknown arm7 read32 %08X | %08X\n", addr, ARM7->R[15]);
//printf("unknown arm7 read32 %08X | %08X\n", addr, ARM7->R[15]);
return 0;
}
@ -2978,6 +3024,12 @@ u16 ARM9IORead16(u32 addr)
case 0x04000300: return PostFlag9;
case 0x04000304: return PowerControl9;
case 0x04004000:
case 0x04004004:
case 0x04004010:
// shut up logging for DSi registers
return 0;
}
if ((addr >= 0x04000000 && addr < 0x04000060) || (addr == 0x0400006C))
@ -3111,6 +3163,12 @@ u32 ARM9IORead32(u32 addr)
if (!(ExMemCnt[0] & (1<<11))) return NDSCart::ReadROMData();
return 0;
case 0x04004000:
case 0x04004004:
case 0x04004010:
// shut up logging for DSi registers
return 0;
// NO$GBA debug register "Clock Cycles"
// Since it's a 64 bit reg. the CPU will access it in two parts:
case 0x04FFFA20: return (u32)(GetSysClockCycles(0) & 0xFFFFFFFF);
@ -3329,10 +3387,14 @@ void ARM9IOWrite16(u32 addr, u16 val)
case 0x040001BA: ROMSeed1[4] = val & 0x7F; return;
case 0x04000204:
{
u16 oldVal = ExMemCnt[0];
ExMemCnt[0] = val;
ExMemCnt[1] = (ExMemCnt[1] & 0x007F) | (val & 0xFF80);
if ((oldVal ^ ExMemCnt[0]) & 0xFF)
SetGBASlotTimings();
return;
}
case 0x04000208: IME[0] = val & 0x1; UpdateIRQ(0); return;
case 0x04000210: IE[0] = (IE[0] & 0xFFFF0000) | val; UpdateIRQ(0); return;
@ -4038,9 +4100,13 @@ void ARM7IOWrite16(u32 addr, u16 val)
return;
case 0x04000204:
{
u16 oldVal = ExMemCnt[1];
ExMemCnt[1] = (ExMemCnt[1] & 0xFF80) | (val & 0x007F);
if ((ExMemCnt[1] ^ oldVal) & 0xFF)
SetGBASlotTimings();
return;
}
case 0x04000206:
SetWifiWaitCnt(val);
return;

View File

@ -129,17 +129,49 @@ struct Timer
u32 CycleShift;
};
enum
{
Mem9_ITCM = 0x00000001,
Mem9_DTCM = 0x00000002,
Mem9_BIOS = 0x00000004,
Mem9_MainRAM = 0x00000008,
Mem9_WRAM = 0x00000010,
Mem9_IO = 0x00000020,
Mem9_Pal = 0x00000040,
Mem9_OAM = 0x00000080,
Mem9_VRAM = 0x00000100,
Mem9_GBAROM = 0x00020000,
Mem9_GBARAM = 0x00040000,
Mem7_BIOS = 0x00000001,
Mem7_MainRAM = 0x00000002,
Mem7_WRAM = 0x00000004,
Mem7_IO = 0x00000008,
Mem7_Wifi0 = 0x00000010,
Mem7_Wifi1 = 0x00000020,
Mem7_VRAM = 0x00000040,
Mem7_GBAROM = 0x00000100,
Mem7_GBARAM = 0x00000200,
// TODO: add DSi regions!
};
struct MemRegion
{
u8* Mem;
u32 Mask;
};
#ifdef JIT_ENABLED
extern bool EnableJIT;
#endif
extern int ConsoleType;
extern int CurCPU;
extern u8 ARM9MemTimings[0x40000][4];
extern u8 ARM9MemTimings[0x40000][8];
extern u32 ARM9Regions[0x40000];
extern u8 ARM7MemTimings[0x20000][4];
extern u32 ARM7Regions[0x20000];
extern u32 NumFrames;
extern u32 NumLagFrames;
@ -191,8 +223,8 @@ void Stop();
bool DoSavestate(Savestate* file);
void SetARM9RegionTimings(u32 addrstart, u32 addrend, int buswidth, int nonseq, int seq);
void SetARM7RegionTimings(u32 addrstart, u32 addrend, int buswidth, int nonseq, int seq);
void SetARM9RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, int nonseq, int seq);
void SetARM7RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, int nonseq, int seq);
// 0=DS 1=DSi
void SetConsoleType(int type);

View File

@ -24,7 +24,6 @@
#include "ARM.h"
#include "DSi_AES.h"
#include "Platform.h"
#include "Config.h"
#include "ROMList.h"
#include "melonDLDI.h"
#include "NDSCart_SRAMManager.h"
@ -52,12 +51,16 @@ u32 TransferDir;
u8 TransferCmd[8];
bool CartInserted;
char CartName[256];
u8* CartROM;
u32 CartROMSize;
u32 CartID;
bool CartIsHomebrew;
bool CartIsDSi;
NDSHeader Header;
NDSBanner Banner;
CartCommon* Cart;
u32 Key1_KeyBuf[0x412];
@ -173,15 +176,6 @@ void Key2_Encrypt(u8* data, u32 len)
}
void ApplyModcrypt(u32 addr, u32 len, u8* iv)
{return;
u8 key[16];
DSi_AES::GetModcryptKey(&CartROM[0], key);
DSi_AES::ApplyModcrypt(&CartROM[addr], len, key, iv);
}
CartCommon::CartCommon(u8* rom, u32 len, u32 chipid)
{
ROM = rom;
@ -190,6 +184,7 @@ CartCommon::CartCommon(u8* rom, u32 len, u32 chipid)
u8 unitcode = ROM[0x12];
IsDSi = (unitcode & 0x02) != 0;
DSiBase = *(u16*)&ROM[0x92] << 19;
}
CartCommon::~CartCommon()
@ -200,12 +195,14 @@ void CartCommon::Reset()
{
CmdEncMode = 0;
DataEncMode = 0;
DSiMode = false;
}
void CartCommon::SetupDirectBoot()
{
CmdEncMode = 2;
DataEncMode = 2;
DSiMode = IsDSi && NDS::ConsoleType==1;
}
void CartCommon::DoSavestate(Savestate* file)
@ -214,6 +211,7 @@ void CartCommon::DoSavestate(Savestate* file)
file->Var32(&CmdEncMode);
file->Var32(&DataEncMode);
file->Bool32(&DSiMode);
}
void CartCommon::LoadSave(const char* path, u32 type)
@ -263,13 +261,15 @@ int CartCommon::ROMCommandStart(u8* cmd, u8* data, u32 len)
case 0x3C:
CmdEncMode = 1;
Key1_InitKeycode(false, *(u32*)&ROM[0xC], 2, 2);
DSiMode = false;
return 0;
case 0x3D:
if (IsDSi)
{
CmdEncMode = 11;
CmdEncMode = 1;
Key1_InitKeycode(true, *(u32*)&ROM[0xC], 1, 2);
DSiMode = true;
}
return 0;
@ -277,7 +277,7 @@ int CartCommon::ROMCommandStart(u8* cmd, u8* data, u32 len)
return 0;
}
}
else if (CmdEncMode == 1 || CmdEncMode == 11)
else if (CmdEncMode == 1)
{
// decrypt the KEY1 command as needed
// (KEY2 commands do not need decrypted because KEY2 is handled entirely by hardware,
@ -306,14 +306,13 @@ int CartCommon::ROMCommandStart(u8* cmd, u8* data, u32 len)
case 0x20:
{
u32 addr = (cmddec[2] & 0xF0) << 8;
if (CmdEncMode == 11)
if (DSiMode)
{
// the DSi region starts with 0x3000 unreadable bytes
// similarly to how the DS region starts at 0x1000 with 0x3000 unreadable bytes
// these contain data for KEY1 crypto
u32 dsiregion = *(u16*)&ROM[0x92] << 19;
addr -= 0x1000;
addr += dsiregion;
addr += DSiBase;
}
ReadROM(addr, 0x1000, data, 0);
}
@ -594,9 +593,15 @@ void CartRetail::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset)
if (addr < 0x8000)
addr = 0x8000 + (addr & 0x1FF);
// TODO: protect DSi secure area
// also protect DSi region if not unlocked
// and other security shenanigans
if (IsDSi && (addr >= DSiBase))
{
// for DSi carts:
// * in DSi mode: block the first 0x3000 bytes of the DSi area
// * in DS mode: block the entire DSi area
if ((!DSiMode) || (addr < (DSiBase+0x3000)))
addr = 0x8000 + (addr & 0x1FF);
}
memcpy(data+offset, ROM+addr, len);
}
@ -1118,6 +1123,8 @@ u8 CartRetailIR::SPIWrite(u8 val, u32 pos, bool last)
case 0x08: // ID
return 0xAA;
}
return 0;
}
@ -1157,30 +1164,73 @@ u8 CartRetailBT::SPIWrite(u8 val, u32 pos, bool last)
CartHomebrew::CartHomebrew(u8* rom, u32 len, u32 chipid) : CartCommon(rom, len, chipid)
{
if (Config::DLDIEnable)
ReadOnly = Platform::GetConfigBool(Platform::DLDI_ReadOnly);
if (Platform::GetConfigBool(Platform::DLDI_Enable))
{
ApplyDLDIPatch(melonDLDI, sizeof(melonDLDI));
SDFile = Platform::OpenLocalFile(Config::DLDISDPath, "r+b");
std::string folderpath;
if (Platform::GetConfigBool(Platform::DLDI_FolderSync))
folderpath = Platform::GetConfigString(Platform::DLDI_FolderPath);
else
folderpath = "";
ApplyDLDIPatch(melonDLDI, sizeof(melonDLDI), ReadOnly);
SD = new FATStorage(Platform::GetConfigString(Platform::DLDI_ImagePath),
(u64)Platform::GetConfigInt(Platform::DLDI_ImageSize) * 1024 * 1024,
ReadOnly,
folderpath);
SD->Open();
}
else
SDFile = nullptr;
SD = nullptr;
}
CartHomebrew::~CartHomebrew()
{
if (SDFile) fclose(SDFile);
if (SD)
{
SD->Close();
delete SD;
}
}
void CartHomebrew::Reset()
{
CartCommon::Reset();
}
if (SDFile) fclose(SDFile);
void CartHomebrew::SetupDirectBoot()
{
CartCommon::SetupDirectBoot();
if (Config::DLDIEnable)
SDFile = Platform::OpenLocalFile(Config::DLDISDPath, "r+b");
else
SDFile = nullptr;
if (SD)
{
// add the ROM to the SD volume
if (!SD->InjectFile(CartName, CartROM, CartROMSize))
return;
// setup argv command line
char argv[512] = {0};
int argvlen;
strncpy(argv, "fat:/", 511);
strncat(argv, CartName, 511);
argvlen = strlen(argv);
void (*writefn)(u32,u32) = (NDS::ConsoleType==1) ? DSi::ARM9Write32 : NDS::ARM9Write32;
u32 argvbase = Header.ARM9RAMAddress + Header.ARM9Size;
argvbase = (argvbase + 0xF) & ~0xF;
for (u32 i = 0; i <= argvlen; i+=4)
writefn(argvbase+i, *(u32*)&argv[i]);
writefn(0x02FFFE70, 0x5F617267);
writefn(0x02FFFE74, argvbase);
writefn(0x02FFFE78, argvlen+1);
}
}
void CartHomebrew::DoSavestate(Savestate* file)
@ -1213,13 +1263,7 @@ int CartHomebrew::ROMCommandStart(u8* cmd, u8* data, u32 len)
case 0xC0: // SD read
{
u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
u64 addr = sector * 0x200ULL;
if (SDFile)
{
fseek(SDFile, addr, SEEK_SET);
fread(data, len, 1, SDFile);
}
if (SD) SD->ReadSectors(sector, len>>9, data);
}
return 0;
@ -1242,13 +1286,7 @@ void CartHomebrew::ROMCommandFinish(u8* cmd, u8* data, u32 len)
case 0xC1:
{
u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
u64 addr = sector * 0x200ULL;
if (SDFile)
{
fseek(SDFile, addr, SEEK_SET);
fwrite(data, len, 1, SDFile);
}
if (SD && (!ReadOnly)) SD->WriteSectors(sector, len>>9, data);
}
break;
@ -1257,7 +1295,7 @@ void CartHomebrew::ROMCommandFinish(u8* cmd, u8* data, u32 len)
}
}
void CartHomebrew::ApplyDLDIPatch(const u8* patch, u32 patchlen)
void CartHomebrew::ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly)
{
u32 offset = *(u32*)&ROM[0x20];
u32 size = *(u32*)&ROM[0x2C];
@ -1374,6 +1412,19 @@ void CartHomebrew::ApplyDLDIPatch(const u8* patch, u32 patchlen)
memset(&binary[dldioffset+fixstart], 0, fixend-fixstart);
}
if (readonly)
{
// clear the can-write feature flag
binary[dldioffset+0x64] &= ~0x02;
// make writeSectors() return failure
u32 writesec_addr = *(u32*)&binary[dldioffset+0x74];
writesec_addr -= memaddr;
writesec_addr += dldioffset;
*(u32*)&binary[writesec_addr+0x00] = 0xE3A00000; // mov r0, #0
*(u32*)&binary[writesec_addr+0x04] = 0xE12FFF1E; // bx lr
}
printf("applied DLDI patch\n");
}
@ -1496,8 +1547,11 @@ void DecryptSecureArea(u8* out)
// * .srl ROMs (VC dumps) have encrypted secure areas but have precomputed
// decryption data at 0x1000 (and at the beginning of the DSi region if any)
u32 gamecode = *(u32*)&CartROM[0x0C];
u32 arm9base = *(u32*)&CartROM[0x20];
u32 gamecode = (u32)Header.GameCode[3] << 24 |
(u32)Header.GameCode[2] << 16 |
(u32)Header.GameCode[1] << 8 |
(u32)Header.GameCode[0];
u32 arm9base = Header.ARM9ROMOffset;
memcpy(out, &CartROM[arm9base], 0x800);
@ -1524,11 +1578,17 @@ void DecryptSecureArea(u8* out)
bool LoadROMCommon(u32 filelength, const char *sram, bool direct)
{
u32 gamecode;
memcpy(&gamecode, CartROM + 0x0C, 4);
printf("Game code: %c%c%c%c\n", gamecode&0xFF, (gamecode>>8)&0xFF, (gamecode>>16)&0xFF, gamecode>>24);
memcpy(&Header, CartROM, sizeof(Header));
memcpy(&Banner, CartROM + Header.BannerOffset, sizeof(Banner));
u8 unitcode = CartROM[0x12];
printf("Game code: %.4s\n", Header.GameCode);
u32 gamecode = (u32)Header.GameCode[3] << 24 |
(u32)Header.GameCode[2] << 16 |
(u32)Header.GameCode[1] << 8 |
(u32)Header.GameCode[0];
u8 unitcode = Header.UnitCode;
CartIsDSi = (unitcode & 0x02) != 0;
ROMListEntry romparams;
@ -1657,6 +1717,16 @@ bool LoadROM(const char* path, const char* sram, bool direct)
NDS::Reset();
char* romname = strrchr((char*)path, '/');
if (!romname)
{
romname = strrchr((char*)path, '\\');
if (!romname)
romname = (char*)&path[-1];
}
romname++;
strncpy(CartName, romname, 255); CartName[255] = '\0';
fseek(f, 0, SEEK_END);
u32 len = (u32)ftell(f);
@ -1678,6 +1748,9 @@ bool LoadROM(const u8* romdata, u32 filelength, const char *sram, bool direct)
{
NDS::Reset();
// TODO: make it more meaningful?
strncpy(CartName, "rom.nds", 256);
u32 len = filelength;
CartROMSize = 0x200;
while (CartROMSize < len)

View File

@ -20,6 +20,8 @@
#define NDSCART_H
#include "types.h"
#include "NDS_Header.h"
#include "FATStorage.h"
namespace NDSCart
{
@ -55,6 +57,8 @@ protected:
u32 ROMLength;
u32 ChipID;
bool IsDSi;
bool DSiMode;
u32 DSiBase;
u32 CmdEncMode;
u32 DataEncMode;
@ -168,6 +172,7 @@ public:
~CartHomebrew() override;
void Reset() override;
void SetupDirectBoot() override;
void DoSavestate(Savestate* file) override;
@ -175,10 +180,11 @@ public:
void ROMCommandFinish(u8* cmd, u8* data, u32 len) override;
private:
void ApplyDLDIPatch(const u8* patch, u32 len);
void ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly);
void ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset);
FILE* SDFile;
FATStorage* SD;
bool ReadOnly;
};
extern u16 SPICnt;
@ -191,6 +197,9 @@ extern u32 CartROMSize;
extern u32 CartID;
extern NDSHeader Header;
extern NDSBanner Banner;
bool Init();
void DeInit();
void Reset();

213
src/NDS_Header.h Normal file
View File

@ -0,0 +1,213 @@
/*
Copyright 2016-2021 Arisotura, WaluigiWare64
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_HEADER_H
#define NDS_HEADER_H
#include "types.h"
// Consult GBATEK for info on what these are
struct NDSHeader
{
char GameTitle[12];
char GameCode[4];
char MakerCode[2];
u8 UnitCode;
u8 EncryptionSeedSelect;
u8 CardSize;
u8 Reserved1[7];
u8 DSiCryptoFlags;
u8 NDSRegion;
u8 ROMVersion;
u8 Autostart;
u32 ARM9ROMOffset;
u32 ARM9EntryAddress;
u32 ARM9RAMAddress;
u32 ARM9Size;
u32 ARM7ROMOffset;
u32 ARM7EntryAddress;
u32 ARM7RAMAddress;
u32 ARM7Size;
u32 FNTOffset;
u32 FNTSize;
u32 FATOffset;
u32 FATSize;
u32 ARM9OverlayOffset;
u32 ARM9OverlaySize;
u32 ARM7OverlayOffset;
u32 ARM7OverlaySize;
u32 NormalCommandSettings;
u32 Key1CommandSettings;
u32 BannerOffset;
u16 SecureAreaCRC16;
u16 SecureAreaDelay;
// GBATEK lists the following two with a question mark
u32 ARM9AutoLoadListAddress;
u32 ARM7AutoLoadListAddress;
u64 SecureAreaDisable;
u32 ROMSize; // excluding DSi area
u32 HeaderSize;
// GBATEK lists the following two with a question mark
u32 DSiARM9ParamTableOffset;
u32 DSiARM7ParamTableOffset;
// expressed in 0x80000-byte units
u16 NDSRegionEnd;
u16 DSiRegionStart;
// specific to NAND games
u16 NANDROMEnd;
u16 NANDRWStart;
u8 Reserved2[40];
u8 NintendoLogo[156];
u16 NintendoLogoCRC16;
u16 HeaderCRC16;
u32 DebugROMOffset;
u32 DebugSize;
u32 DebugRAMAddress;
u32 Reserved4;
u8 Reserved5[16];
u32 DSiMBKSlots[5]; // global MBK1..MBK5 settings
u32 DSiARM9MBKAreas[3]; // local MBK6..MBK8 settings for ARM9
u32 DSiARM7MBKAreas[3]; // local MBK6..MBK8 settings for ARM7
u8 DSiMBKWriteProtect[3]; // global MBK9 setting
u8 DSiWRAMCntSetting; // global WRAMCNT setting
u32 DSiRegionMask;
u32 DSiPermissions[2];
u8 Reserved6[3];
u8 AppFlags; // flags at 1BF
u32 DSiARM9iROMOffset;
u32 Reserved7;
u32 DSiARM9iRAMAddress;
u32 DSiARM9iSize;
u32 DSiARM7iROMOffset;
u32 DSiSDMMCDeviceList;
u32 DSiARM7iRAMAddress;
u32 DSiARM7iSize;
u32 DSiDigestNTROffset;
u32 DSiDigestNTRSize;
u32 DSiDigestTWLOffset;
u32 DSiDigestTWLSize;
u32 DSiDigestSecHashtblOffset;
u32 DSiDigestSecHashtblSize;
u32 DSiDigestBlkHashtblOffset;
u32 DSiDigestBlkHashtblSize;
u32 DSiDigestSecSize; // sector size in bytes
u32 DSiDigestBlkSecCount; // sectors per block
u32 DSiBannerSize;
// ???
u8 DSiShared0Size;
u8 DSiShared1Size;
u8 DSiEULARatings;
u8 DSiUseRatings;
u32 DSiTotalROMSize;
u8 DSiShared2Size;
u8 DSiShared3Size;
u8 DSiShared4Size;
u8 DSiShared5Size;
// ???
u32 DSiARM9iParamTableOffset;
u32 DSiARM7iParamTableOffset;
u32 DSiModcrypt1Offset;
u32 DSiModcrypt1Size;
u32 DSiModcrypt2Offset;
u32 DSiModcrypt2Size;
u32 DSiTitleIDLow;
u32 DSiTitleIDHigh;
u32 DSiPublicSavSize;
u32 DSiPrivateSavSize;
u8 Reserved8[176];
u8 DSiAgeRatingFlags[16];
// 0x300 - hashes (SHA1-HMAC)
u8 DSiARM9Hash[20];
u8 DSiARM7Hash[20];
u8 DSiDigestMasterHash[20];
u8 BannerHash[20];
u8 DSiARM9iHash[20];
u8 DSiARM7iHash[20];
u8 HeaderBinariesHash[20]; // 0x160-byte header + ARM9/ARM7 binaries
u8 ARM9OverlayHash[20]; // ARM9 overlay and NitroFAT
u8 DSiARM9NoSecureHash[20]; // ARM9 binary without secure area
u8 Reserved9[2636];
// reserved and unchecked region at 0xE00
u8 Reserved10[384];
u8 HeaderSignature[128]; // RSA-SHA1 across 0x000..0xDFF
};
static_assert(sizeof(NDSHeader) == 4096, "NDSHeader is not 4096 bytes!");
struct NDSBanner
{
u16 Version;
u16 CRC16[4];
u8 Reserved1[22];
u8 Icon[512];
u16 Palette[16];
char16_t JapaneseTitle[128];
char16_t EnglishTitle[128];
char16_t FrenchTitle[128];
char16_t GermanTitle[128];
char16_t ItalianTitle[128];
char16_t SpanishTitle[128];
char16_t ChineseTitle[128];
char16_t KoreanTitle[128];
u8 Reserved2[2048];
u8 DSiIcon[8][512];
u16 DSiPalette[8][16];
u16 DSiSequence[64];
};
static_assert(sizeof(NDSBanner) == 9152, "NDSBanner is not 9152 bytes!");
#endif //NDS_HEADER_H

View File

@ -22,6 +22,7 @@
#include "types.h"
#include <functional>
#include <string>
namespace Platform
{
@ -31,6 +32,61 @@ void DeInit();
void StopEmu();
// configuration values
enum ConfigEntry
{
#ifdef JIT_ENABLED
JIT_Enable,
JIT_MaxBlockSize,
JIT_LiteralOptimizations,
JIT_BranchOptimizations,
JIT_FastMemory,
#endif
ExternalBIOSEnable,
BIOS9Path,
BIOS7Path,
FirmwarePath,
DSi_BIOS9Path,
DSi_BIOS7Path,
DSi_FirmwarePath,
DSi_NANDPath,
DLDI_Enable,
DLDI_ImagePath,
DLDI_ImageSize,
DLDI_ReadOnly,
DLDI_FolderSync,
DLDI_FolderPath,
DSiSD_Enable,
DSiSD_ImagePath,
DSiSD_ImageSize,
DSiSD_ReadOnly,
DSiSD_FolderSync,
DSiSD_FolderPath,
Firm_OverrideSettings,
Firm_Username,
Firm_Language,
Firm_BirthdayMonth,
Firm_BirthdayDay,
Firm_Color,
Firm_Message,
Firm_MAC,
Firm_RandomizeMAC,
AudioBitrate,
};
int GetConfigInt(ConfigEntry entry);
bool GetConfigBool(ConfigEntry entry);
std::string GetConfigString(ConfigEntry entry);
bool GetConfigArray(ConfigEntry entry, void* data);
// fopen() wrappers
// * OpenFile():
// simple fopen() wrapper that supports UTF8.
@ -49,11 +105,11 @@ void StopEmu();
// 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);
FILE* OpenFile(std::string path, std::string mode, bool mustexist=false);
FILE* OpenLocalFile(std::string path, std::string mode);
FILE* OpenDataFile(std::string path);
inline bool FileExists(const char* name)
inline bool FileExists(std::string name)
{
FILE* f = OpenFile(name, "rb");
if (!f) return false;
@ -61,7 +117,7 @@ inline bool FileExists(const char* name)
return true;
}
inline bool LocalFileExists(const char* name)
inline bool LocalFileExists(std::string name)
{
FILE* f = OpenLocalFile(name, "rb");
if (!f) return false;

View File

@ -19,8 +19,12 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "Config.h"
#include <string>
#include <algorithm>
#include <codecvt>
#include <locale>
#include "NDS.h"
#include "DSi.h"
#include "SPI.h"
#include "DSi_SPI_TSC.h"
#include "Platform.h"
@ -29,7 +33,7 @@
namespace SPI_Firmware
{
char FirmwarePath[1024];
std::string FirmwarePath;
u8* Firmware;
u32 FirmwareLength;
u32 FirmwareMask;
@ -78,8 +82,8 @@ bool VerifyCRC16(u32 start, u32 offset, u32 len, u32 crcoffset)
bool Init()
{
memset(FirmwarePath, 0, sizeof(FirmwarePath));
Firmware = NULL;
FirmwarePath = "";
Firmware = nullptr;
return true;
}
@ -111,25 +115,142 @@ u32 FixFirmwareLength(u32 originalLength)
return originalLength;
}
void Reset()
void LoadDefaultFirmware()
{
if (Firmware) delete[] Firmware;
Firmware = NULL;
FirmwareLength = 0x20000;
Firmware = new u8[FirmwareLength];
memset(Firmware, 0xFF, FirmwareLength);
FirmwareMask = FirmwareLength - 1;
memset(Firmware, 0, 0x1D);
if (NDS::ConsoleType == 1)
strncpy(FirmwarePath, Config::DSiFirmwarePath, 1023);
else
strncpy(FirmwarePath, Config::FirmwarePath, 1023);
FILE* f = Platform::OpenLocalFile(FirmwarePath, "rb");
if (!f)
{
printf("Firmware not found\n");
// TODO: generate default firmware
return;
Firmware[0x1D] = 0x57; // DSi
Firmware[0x2F] = 0x18;
Firmware[0x1FD] = 0x02;
Firmware[0x1FE] = 0x20;
}
else
{
Firmware[0x1D] = 0x20; // DS Lite (TODO: make configurable?)
Firmware[0x2F] = 0x06;
}
// wifi calibration
const u8 defaultmac[6] = {0x00, 0x09, 0xBF, 0x11, 0x22, 0x33};
const u8 bbinit[0x69] =
{
0x03, 0x17, 0x40, 0x00, 0x1B, 0x6C, 0x48, 0x80, 0x38, 0x00, 0x35, 0x07, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC7, 0xBB, 0x01, 0x24, 0x7F,
0x5A, 0x01, 0x3F, 0x01, 0x3F, 0x36, 0x1D, 0x00, 0x78, 0x35, 0x55, 0x12, 0x34, 0x1C, 0x00, 0x01,
0x0E, 0x38, 0x03, 0x70, 0xC5, 0x2A, 0x0A, 0x08, 0x04, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFE,
0xFE, 0xFE, 0xFE, 0xFC, 0xFC, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xF8, 0xF8, 0xF6, 0x00, 0x12, 0x14,
0x12, 0x41, 0x23, 0x03, 0x04, 0x70, 0x35, 0x0E, 0x2C, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x12, 0x28, 0x1C
};
const u8 rfinit[0x29] =
{
0x31, 0x4C, 0x4F, 0x21, 0x00, 0x10, 0xB0, 0x08, 0xFA, 0x15, 0x26, 0xE6, 0xC1, 0x01, 0x0E, 0x50,
0x05, 0x00, 0x6D, 0x12, 0x00, 0x00, 0x01, 0xFF, 0x0E, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x06,
0x06, 0x00, 0x00, 0x00, 0x18, 0x00, 0x02, 0x00, 0x00
};
const u8 chandata[0x3C] =
{
0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x16,
0x26, 0x1C, 0x1C, 0x1C, 0x1D, 0x1D, 0x1D, 0x1E, 0x1E, 0x1E, 0x1E, 0x1F, 0x1E, 0x1F, 0x18,
0x01, 0x4B, 0x4B, 0x4B, 0x4B, 0x4C, 0x4C, 0x4C, 0x4C, 0x4C, 0x4C, 0x4C, 0x4D, 0x4D, 0x4D,
0x02, 0x6C, 0x71, 0x76, 0x5B, 0x40, 0x45, 0x4A, 0x2F, 0x34, 0x39, 0x3E, 0x03, 0x08, 0x14
};
*(u16*)&Firmware[0x2C] = 0x138;
Firmware[0x2E] = 0;
*(u32*)&Firmware[0x30] = 0xFFFFFFFF;
*(u16*)&Firmware[0x34] = 0x00FF;
memcpy(&Firmware[0x36], defaultmac, 6);
*(u16*)&Firmware[0x3C] = 0x3FFE;
*(u16*)&Firmware[0x3E] = 0xFFFF;
Firmware[0x40] = 0x03;
Firmware[0x41] = 0x94;
Firmware[0x42] = 0x29;
Firmware[0x43] = 0x02;
*(u16*)&Firmware[0x44] = 0x0002;
*(u16*)&Firmware[0x46] = 0x0017;
*(u16*)&Firmware[0x48] = 0x0026;
*(u16*)&Firmware[0x4A] = 0x1818;
*(u16*)&Firmware[0x4C] = 0x0048;
*(u16*)&Firmware[0x4E] = 0x4840;
*(u16*)&Firmware[0x50] = 0x0058;
*(u16*)&Firmware[0x52] = 0x0042;
*(u16*)&Firmware[0x54] = 0x0146;
*(u16*)&Firmware[0x56] = 0x8064;
*(u16*)&Firmware[0x58] = 0xE6E6;
*(u16*)&Firmware[0x5A] = 0x2443;
*(u16*)&Firmware[0x5C] = 0x000E;
*(u16*)&Firmware[0x5E] = 0x0001;
*(u16*)&Firmware[0x60] = 0x0001;
*(u16*)&Firmware[0x62] = 0x0402;
memcpy(&Firmware[0x64], bbinit, 0x69);
Firmware[0xCD] = 0;
memcpy(&Firmware[0xCE], rfinit, 0x29);
Firmware[0xF7] = 0x02;
memcpy(&Firmware[0xF8], chandata, 0x3C);
*(u16*)&Firmware[0x2A] = CRC16(&Firmware[0x2C], *(u16*)&Firmware[0x2C], 0x0000);
// user data
u32 userdata = 0x7FE00 & FirmwareMask;
*(u16*)&Firmware[0x20] = userdata >> 3;
memset(Firmware + userdata, 0, 0x74);
Firmware[userdata+0x00] = 5; // version
Firmware[userdata+0x03] = 1;
Firmware[userdata+0x04] = 1;
*(u16*)&Firmware[userdata+0x64] = 0x0031;
*(u16*)&Firmware[userdata+0x72] = CRC16(&Firmware[userdata], 0x70, 0xFFFF);
// wifi access points
// TODO: WFC ID??
u32 apdata = userdata - 0x400;
memset(&Firmware[apdata], 0, 0x300);
strcpy((char*)&Firmware[apdata+0x40], "melonAP");
if (NDS::ConsoleType == 1) *(u16*)&Firmware[apdata+0xEA] = 1400;
Firmware[apdata+0xEF] = 0x01;
*(u16*)&Firmware[apdata+0xFE] = CRC16(&Firmware[apdata], 0xFE, 0x0000);
apdata += 0x100;
Firmware[apdata+0xE7] = 0xFF;
Firmware[apdata+0xEF] = 0x01;
*(u16*)&Firmware[apdata+0xFE] = CRC16(&Firmware[apdata], 0xFE, 0x0000);
apdata += 0x100;
Firmware[apdata+0xE7] = 0xFF;
Firmware[apdata+0xEF] = 0x01;
*(u16*)&Firmware[apdata+0xFE] = CRC16(&Firmware[apdata], 0xFE, 0x0000);
if (NDS::ConsoleType == 1)
{
apdata = userdata - 0xA00;
Firmware[apdata+0xE7] = 0xFF;
*(u16*)&Firmware[apdata+0xFE] = CRC16(&Firmware[apdata], 0xFE, 0x0000);
apdata += 0x200;
Firmware[apdata+0xE7] = 0xFF;
*(u16*)&Firmware[apdata+0xFE] = CRC16(&Firmware[apdata], 0xFE, 0x0000);
apdata += 0x200;
Firmware[apdata+0xE7] = 0xFF;
*(u16*)&Firmware[apdata+0xFE] = CRC16(&Firmware[apdata], 0xFE, 0x0000);
}
}
void LoadFirmwareFromFile(FILE* f)
{
fseek(f, 0, SEEK_END);
FirmwareLength = FixFirmwareLength((u32)ftell(f));
@ -139,22 +260,91 @@ void Reset()
fseek(f, 0, SEEK_SET);
fread(Firmware, 1, FirmwareLength, f);
fclose(f);
// take a backup
char firmbkp[1028];
int fplen = strlen(FirmwarePath);
strncpy(&firmbkp[0], FirmwarePath, fplen);
strncpy(&firmbkp[fplen], ".bak", 1028-fplen);
firmbkp[fplen+4] = '\0';
f = Platform::OpenLocalFile(firmbkp, "rb");
if (f) fclose(f);
std::string fwBackupPath = FirmwarePath + ".bak";
FILE* bf = Platform::OpenLocalFile(fwBackupPath, "rb");
if (!bf)
{
bf = Platform::OpenLocalFile(fwBackupPath, "wb");
if (bf)
{
fwrite(Firmware, 1, FirmwareLength, bf);
fclose(bf);
}
else
{
f = Platform::OpenLocalFile(firmbkp, "wb");
fwrite(Firmware, 1, FirmwareLength, f);
printf("Could not write firmware backup!\n");
}
}
else
{
fclose(bf);
}
}
void LoadUserSettingsFromConfig()
{
// setting up username
std::string orig_username = Platform::GetConfigString(Platform::Firm_Username);
std::u16string username = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(orig_username);
size_t usernameLength = std::min(username.length(), (size_t) 10);
memcpy(Firmware + UserSettings + 0x06, username.data(), usernameLength * sizeof(char16_t));
Firmware[UserSettings+0x1A] = usernameLength;
// setting language
Firmware[UserSettings+0x64] = Platform::GetConfigInt(Platform::Firm_Language);
// setting up color
Firmware[UserSettings+0x02] = Platform::GetConfigInt(Platform::Firm_Color);
// setting up birthday
Firmware[UserSettings+0x03] = Platform::GetConfigInt(Platform::Firm_BirthdayMonth);
Firmware[UserSettings+0x04] = Platform::GetConfigInt(Platform::Firm_BirthdayDay);
// setup message
std::string orig_message = Platform::GetConfigString(Platform::Firm_Message);
std::u16string message = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(orig_message);
size_t messageLength = std::min(message.length(), (size_t) 26);
memcpy(Firmware + UserSettings + 0x1C, message.data(), messageLength * sizeof(char16_t));
Firmware[UserSettings+0x50] = messageLength;
}
void Reset()
{
if (Firmware) delete[] Firmware;
Firmware = nullptr;
FirmwarePath = "";
bool firmoverride = false;
if (Platform::GetConfigBool(Platform::ExternalBIOSEnable))
{
if (NDS::ConsoleType == 1)
FirmwarePath = Platform::GetConfigString(Platform::DSi_FirmwarePath);
else
FirmwarePath = Platform::GetConfigString(Platform::FirmwarePath);
FILE* f = Platform::OpenLocalFile(FirmwarePath, "rb");
if (!f)
{
printf("Firmware not found! Generating default firmware.\n");
FirmwarePath = "";
}
else
{
LoadFirmwareFromFile(f);
fclose(f);
}
}
if (FirmwarePath.empty())
{
LoadDefaultFirmware();
firmoverride = true;
}
else
{
firmoverride = Platform::GetConfigBool(Platform::Firm_OverrideSettings);
}
FirmwareMask = FirmwareLength - 1;
@ -167,9 +357,9 @@ void Reset()
UserSettings = userdata;
// TODO evetually: do this in DSi mode
if (NDS::ConsoleType == 0)
{
if (firmoverride)
LoadUserSettingsFromConfig();
// fix touchscreen coords
*(u16*)&Firmware[userdata+0x58] = 0;
*(u16*)&Firmware[userdata+0x5A] = 0;
@ -185,15 +375,29 @@ void Reset()
*(u16*)&Firmware[userdata+0x72] = CRC16(&Firmware[userdata], 0x70, 0xFFFF);
if (Config::RandomizeMAC)
if (firmoverride)
{
// replace MAC address with random address
Firmware[0x36] = 0x00;
Firmware[0x37] = 0x09;
Firmware[0x38] = 0xBF;
Firmware[0x39] = rand()&0xFF;
Firmware[0x3A] = rand()&0xFF;
Firmware[0x3B] = rand()&0xFF;
u8 mac[6];
bool rep;
if (Platform::GetConfigBool(Platform::Firm_RandomizeMAC))
{
mac[0] = 0x00;
mac[1] = 0x09;
mac[2] = 0xBF;
mac[3] = rand()&0xFF;
mac[4] = rand()&0xFF;
mac[5] = rand()&0xFF;
rep = true;
}
else
{
rep = Platform::GetConfigArray(Platform::Firm_MAC, mac);
}
if (rep)
{
memcpy(&Firmware[0x36], mac, 6);
*(u16*)&Firmware[0x2A] = CRC16(&Firmware[0x2C], *(u16*)&Firmware[0x2C], 0x0000);
}
@ -233,17 +437,31 @@ void DoSavestate(Savestate* file)
file->Var32(&Addr);
}
void SetupDirectBoot()
void SetupDirectBoot(bool dsi)
{
if (dsi)
{
for (u32 i = 0; i < 6; i += 2)
DSi::ARM9Write16(0x02FFFCF4, *(u16*)&Firmware[0x36+i]); // MAC address
// checkme
DSi::ARM9Write16(0x02FFFCFA, *(u16*)&Firmware[0x3C]); // enabled channels
for (u32 i = 0; i < 0x70; i += 4)
DSi::ARM9Write32(0x02FFFC80+i, *(u32*)&Firmware[UserSettings+i]);
}
else
{
NDS::ARM9Write32(0x027FF864, 0);
NDS::ARM9Write32(0x027FF868, *(u16*)&Firmware[0x20] << 3);
NDS::ARM9Write32(0x027FF868, *(u16*)&Firmware[0x20] << 3); // user settings offset
NDS::ARM9Write16(0x027FF874, *(u16*)&Firmware[0x26]);
NDS::ARM9Write16(0x027FF876, *(u16*)&Firmware[0x04]);
NDS::ARM9Write16(0x027FF874, *(u16*)&Firmware[0x26]); // CRC16 for data/gfx
NDS::ARM9Write16(0x027FF876, *(u16*)&Firmware[0x04]); // CRC16 for GUI/wifi code
for (u32 i = 0; i < 0x70; i += 4)
NDS::ARM9Write32(0x027FFC80+i, *(u32*)&Firmware[UserSettings+i]);
}
}
u8 GetConsoleType() { return Firmware[0x1D]; }
u8 GetWifiVersion() { return Firmware[0x2F]; }
@ -349,6 +567,8 @@ void Write(u8 val, u32 hold)
}
if (!hold && (CurCmd == 0x02 || CurCmd == 0x0A))
{
if (!FirmwarePath.empty())
{
FILE* f = Platform::OpenLocalFile(FirmwarePath, "r+b");
if (f)
@ -360,6 +580,7 @@ void Write(u8 val, u32 hold)
}
}
}
}
}

View File

@ -24,7 +24,7 @@
namespace SPI_Firmware
{
void SetupDirectBoot();
void SetupDirectBoot(bool dsi);
u32 FixFirmwareLength(u32 originalLength);

View File

@ -18,6 +18,7 @@
#include <stdio.h>
#include <string.h>
#include <cmath>
#include "Platform.h"
#include "NDS.h"
#include "DSi.h"
@ -27,7 +28,6 @@
// SPU TODO
// * capture addition modes, overflow bugs
// * channel hold
// * 'length less than 4' glitch
namespace SPU
{
@ -62,6 +62,12 @@ const s16 PSGTable[8][8] =
{-0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF}
};
// audio interpolation is an improvement upon the original hardware
// (which performs no interpolation)
int InterpType;
s16 InterpCos[0x100];
s16 InterpCubic[0x100][4];
const u32 OutputBufferSize = 2*2048;
s16 OutputBackbuffer[2 * OutputBufferSize];
u32 OutputBackbufferWritePosition;
@ -75,6 +81,8 @@ Platform::Mutex* AudioLock;
u16 Cnt;
u8 MasterVolume;
u16 Bias;
bool ApplyBias;
bool Degrade10Bit;
Channel* Channels[16];
CaptureUnit* Capture[2];
@ -90,6 +98,34 @@ bool Init()
AudioLock = Platform::Mutex_Create();
InterpType = 0;
ApplyBias = true;
Degrade10Bit = false;
// generate interpolation tables
// values are 1:1:14 fixed-point
float m_pi = std::acos(-1.0f);
for (int i = 0; i < 0x100; i++)
{
float ratio = (i * m_pi) / 255.0f;
ratio = 1.0f - std::cos(ratio);
InterpCos[i] = (s16)(ratio * 0x2000);
}
for (int i = 0; i < 0x100; i++)
{
s32 i1 = i << 6;
s32 i2 = (i * i) >> 2;
s32 i3 = (i * i * i) >> 10;
InterpCubic[i][0] = -i3 + 2*i2 - i1;
InterpCubic[i][1] = i3 - 2*i2 + 0x4000;
InterpCubic[i][2] = -i3 + i2 + i1;
InterpCubic[i][3] = i3 - i2;
}
return true;
}
@ -148,11 +184,26 @@ void DoSavestate(Savestate* file)
}
void SetInterpolation(int type)
{
InterpType = type;
}
void SetBias(u16 bias)
{
Bias = bias;
}
void SetApplyBias(bool enable)
{
ApplyBias = enable;
}
void SetDegrade10Bit(bool enable)
{
Degrade10Bit = enable;
}
Channel::Channel(u32 num)
{
@ -202,6 +253,7 @@ void Channel::DoSavestate(Savestate* file)
file->Var8((u8*)&KeyOn);
file->Var32(&Timer);
file->Var32((u32*)&Pos);
file->VarArray(PrevSample, sizeof(PrevSample));
file->Var16((u16*)&CurSample);
file->Var16(&NoiseVal);
@ -215,7 +267,7 @@ void Channel::DoSavestate(Savestate* file)
file->Var32(&FIFOWritePos);
file->Var32(&FIFOReadOffset);
file->Var32(&FIFOLevel);
file->VarArray(FIFO, 8*4);
file->VarArray(FIFO, sizeof(FIFO));
}
void Channel::FIFO_BufferData()
@ -233,6 +285,9 @@ void Channel::FIFO_BufferData()
if ((FIFOReadOffset + 16) > totallen)
burstlen = totallen - FIFOReadOffset;
// sound DMA can't read from the ARM7 BIOS
if ((SrcAddr + FIFOReadOffset) >= 0x00004000)
{
for (u32 i = 0; i < burstlen; i += 4)
{
FIFO[FIFOWritePos] = BusRead32(SrcAddr + FIFOReadOffset);
@ -240,6 +295,17 @@ void Channel::FIFO_BufferData()
FIFOWritePos++;
FIFOWritePos &= 0x7;
}
}
else
{
for (u32 i = 0; i < burstlen; i += 4)
{
FIFO[FIFOWritePos] = 0;
FIFOReadOffset += 4;
FIFOWritePos++;
FIFOWritePos &= 0x7;
}
}
FIFOLevel += burstlen;
}
@ -269,6 +335,9 @@ void Channel::Start()
Pos = -3;
NoiseVal = 0x7FFF;
PrevSample[0] = 0;
PrevSample[1] = 0;
PrevSample[2] = 0;
CurSample = 0;
FIFOReadPos = 0;
@ -339,7 +408,7 @@ void Channel::NextSample_ADPCM()
{
// setup ADPCM
u32 header = FIFO_ReadData<u32>();
ADPCMVal = header & 0xFFFF;
ADPCMVal = (s32)(s16)(header & 0xFFFF);
ADPCMIndex = (header >> 16) & 0x7F;
if (ADPCMIndex > 88) ADPCMIndex = 88;
@ -444,6 +513,16 @@ s32 Channel::Run()
{
Timer = TimerReload + (Timer - 0x10000);
// for optional interpolation: save previous samples
// the interpolated audio will be delayed by a couple samples,
// but it's easier to deal with this way
if ((type < 3) && (InterpType != 0))
{
PrevSample[2] = PrevSample[1];
PrevSample[1] = PrevSample[0];
PrevSample[0] = CurSample;
}
switch (type)
{
case 0: NextSample_PCM8(); break;
@ -455,6 +534,34 @@ s32 Channel::Run()
}
s32 val = (s32)CurSample;
// interpolation (emulation improvement, not a hardware feature)
if ((type < 3) && (InterpType != 0))
{
s32 samplepos = ((Timer - TimerReload) * 0x100) / (0x10000 - TimerReload);
if (samplepos > 0xFF) samplepos = 0xFF;
switch (InterpType)
{
case 1: // linear
val = ((val * samplepos) +
(PrevSample[0] * (0xFF-samplepos))) >> 8;
break;
case 2: // cosine
val = ((val * InterpCos[samplepos]) +
(PrevSample[0] * InterpCos[0xFF-samplepos])) >> 14;
break;
case 3: // cubic
val = ((PrevSample[2] * InterpCubic[samplepos][0]) +
(PrevSample[1] * InterpCubic[samplepos][1]) +
(PrevSample[0] * InterpCubic[samplepos][2]) +
(val * InterpCubic[samplepos][3])) >> 14;
break;
}
}
val <<= VolumeShift;
val *= Volume;
return val;
@ -710,12 +817,28 @@ void Mix(u32 dummy)
rightoutput = ((s64)rightoutput * MasterVolume) >> 7;
leftoutput >>= 8;
rightoutput >>= 8;
// Add SOUNDBIAS value
// The value used by all commercial games is 0x200, so we subtract that so it won't offset the final sound output.
if (ApplyBias)
{
leftoutput += (Bias << 6) - 0x8000;
rightoutput += (Bias << 6) - 0x8000;
}
if (leftoutput < -0x8000) leftoutput = -0x8000;
else if (leftoutput > 0x7FFF) leftoutput = 0x7FFF;
rightoutput >>= 8;
if (rightoutput < -0x8000) rightoutput = -0x8000;
else if (rightoutput > 0x7FFF) rightoutput = 0x7FFF;
// The original DS and DS lite degrade the output from 16 to 10 bit before output
if (Degrade10Bit)
{
leftoutput &= 0xFFFFFFC0;
rightoutput &= 0xFFFFFFC0;
}
// OutputBufferFrame can never get full because it's
// transfered to OutputBuffer at the end of the frame
OutputBackbuffer[OutputBackbufferWritePosition ] = leftoutput >> 1;

View File

@ -31,7 +31,12 @@ void Stop();
void DoSavestate(Savestate* file);
// 0=none 1=linear 2=cosine 3=cubic
void SetInterpolation(int type);
void SetBias(u16 bias);
void SetDegrade10Bit(bool enable);
void SetApplyBias(bool enable);
void Mix(u32 dummy);
@ -73,6 +78,7 @@ public:
bool KeyOn;
u32 Timer;
s32 Pos;
s16 PrevSample[3];
s16 CurSample;
u16 NoiseVal;

View File

@ -22,7 +22,7 @@
#include <stdio.h>
#include "types.h"
#define SAVESTATE_MAJOR 8
#define SAVESTATE_MAJOR 9
#define SAVESTATE_MINOR 0
class Savestate

View File

@ -1606,9 +1606,23 @@ void ARM64XEmitter::BICS(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm, ArithOption Shif
}
void ARM64XEmitter::MOV(ARM64Reg Rd, ARM64Reg Rm, ArithOption Shift)
{
if (Shift.GetType() == ArithOption::TYPE_SHIFTEDREG)
{
switch (Shift.GetShiftType())
{
case ST_LSL: LSL(Rd, Rm, Shift.GetShiftAmount()); break;
case ST_LSR: LSR(Rd, Rm, Shift.GetShiftAmount()); break;
case ST_ASR: ASR(Rd, Rm, Shift.GetShiftAmount()); break;
case ST_ROR: ROR(Rd, Rm, Shift.GetShiftAmount()); break;
default: ASSERT_MSG(DYNA_REC, false, "Invalid shift type"); break;
}
}
else
{
ORR(Rd, Is64Bit(Rd) ? ZR : WZR, Rm, Shift);
}
}
void ARM64XEmitter::MOV(ARM64Reg Rd, ARM64Reg Rm)
{

View File

@ -469,6 +469,8 @@ public:
}
TypeSpecifier GetType() const { return m_type; }
ARM64Reg GetReg() const { return m_destReg; }
ShiftType GetShiftType() const { return m_shifttype; }
u32 GetShiftAmount() const { return m_shift; }
u32 GetData() const
{
switch (m_type)

359
src/fatfs/00history.txt Normal file
View File

@ -0,0 +1,359 @@
----------------------------------------------------------------------------
Revision history of FatFs module
----------------------------------------------------------------------------
R0.00 (February 26, 2006)
Prototype.
R0.01 (April 29, 2006)
The first release.
R0.02 (June 01, 2006)
Added FAT12 support.
Removed unbuffered mode.
Fixed a problem on small (<32M) partition.
R0.02a (June 10, 2006)
Added a configuration option (_FS_MINIMUM).
R0.03 (September 22, 2006)
Added f_rename().
Changed option _FS_MINIMUM to _FS_MINIMIZE.
R0.03a (December 11, 2006)
Improved cluster scan algorithm to write files fast.
Fixed f_mkdir() creates incorrect directory on FAT32.
R0.04 (February 04, 2007)
Added f_mkfs().
Supported multiple drive system.
Changed some interfaces for multiple drive system.
Changed f_mountdrv() to f_mount().
R0.04a (April 01, 2007)
Supported multiple partitions on a physical drive.
Added a capability of extending file size to f_lseek().
Added minimization level 3.
Fixed an endian sensitive code in f_mkfs().
R0.04b (May 05, 2007)
Added a configuration option _USE_NTFLAG.
Added FSINFO support.
Fixed DBCS name can result FR_INVALID_NAME.
Fixed short seek (<= csize) collapses the file object.
R0.05 (August 25, 2007)
Changed arguments of f_read(), f_write() and f_mkfs().
Fixed f_mkfs() on FAT32 creates incorrect FSINFO.
Fixed f_mkdir() on FAT32 creates incorrect directory.
R0.05a (February 03, 2008)
Added f_truncate() and f_utime().
Fixed off by one error at FAT sub-type determination.
Fixed btr in f_read() can be mistruncated.
Fixed cached sector is not flushed when create and close without write.
R0.06 (April 01, 2008)
Added fputc(), fputs(), fprintf() and fgets().
Improved performance of f_lseek() on moving to the same or following cluster.
R0.07 (April 01, 2009)
Merged Tiny-FatFs as a configuration option. (_FS_TINY)
Added long file name feature. (_USE_LFN)
Added multiple code page feature. (_CODE_PAGE)
Added re-entrancy for multitask operation. (_FS_REENTRANT)
Added auto cluster size selection to f_mkfs().
Added rewind option to f_readdir().
Changed result code of critical errors.
Renamed string functions to avoid name collision.
R0.07a (April 14, 2009)
Septemberarated out OS dependent code on reentrant cfg.
Added multiple sector size feature.
R0.07c (June 21, 2009)
Fixed f_unlink() can return FR_OK on error.
Fixed wrong cache control in f_lseek().
Added relative path feature.
Added f_chdir() and f_chdrive().
Added proper case conversion to extended character.
R0.07e (November 03, 2009)
Septemberarated out configuration options from ff.h to ffconf.h.
Fixed f_unlink() fails to remove a sub-directory on _FS_RPATH.
Fixed name matching error on the 13 character boundary.
Added a configuration option, _LFN_UNICODE.
Changed f_readdir() to return the SFN with always upper case on non-LFN cfg.
R0.08 (May 15, 2010)
Added a memory configuration option. (_USE_LFN = 3)
Added file lock feature. (_FS_SHARE)
Added fast seek feature. (_USE_FASTSEEK)
Changed some types on the API, XCHAR->TCHAR.
Changed .fname in the FILINFO structure on Unicode cfg.
String functions support UTF-8 encoding files on Unicode cfg.
R0.08a (August 16, 2010)
Added f_getcwd(). (_FS_RPATH = 2)
Added sector erase feature. (_USE_ERASE)
Moved file lock semaphore table from fs object to the bss.
Fixed f_mkfs() creates wrong FAT32 volume.
R0.08b (January 15, 2011)
Fast seek feature is also applied to f_read() and f_write().
f_lseek() reports required table size on creating CLMP.
Extended format syntax of f_printf().
Ignores duplicated directory separators in given path name.
R0.09 (September 06, 2011)
f_mkfs() supports multiple partition to complete the multiple partition feature.
Added f_fdisk().
R0.09a (August 27, 2012)
Changed f_open() and f_opendir() reject null object pointer to avoid crash.
Changed option name _FS_SHARE to _FS_LOCK.
Fixed assertion failure due to OS/2 EA on FAT12/16 volume.
R0.09b (January 24, 2013)
Added f_setlabel() and f_getlabel().
R0.10 (October 02, 2013)
Added selection of character encoding on the file. (_STRF_ENCODE)
Added f_closedir().
Added forced full FAT scan for f_getfree(). (_FS_NOFSINFO)
Added forced mount feature with changes of f_mount().
Improved behavior of volume auto detection.
Improved write throughput of f_puts() and f_printf().
Changed argument of f_chdrive(), f_mkfs(), disk_read() and disk_write().
Fixed f_write() can be truncated when the file size is close to 4GB.
Fixed f_open(), f_mkdir() and f_setlabel() can return incorrect value on error.
R0.10a (January 15, 2014)
Added arbitrary strings as drive number in the path name. (_STR_VOLUME_ID)
Added a configuration option of minimum sector size. (_MIN_SS)
2nd argument of f_rename() can have a drive number and it will be ignored.
Fixed f_mount() with forced mount fails when drive number is >= 1. (appeared at R0.10)
Fixed f_close() invalidates the file object without volume lock.
Fixed f_closedir() returns but the volume lock is left acquired. (appeared at R0.10)
Fixed creation of an entry with LFN fails on too many SFN collisions. (appeared at R0.07)
R0.10b (May 19, 2014)
Fixed a hard error in the disk I/O layer can collapse the directory entry.
Fixed LFN entry is not deleted when delete/rename an object with lossy converted SFN. (appeared at R0.07)
R0.10c (November 09, 2014)
Added a configuration option for the platforms without RTC. (_FS_NORTC)
Changed option name _USE_ERASE to _USE_TRIM.
Fixed volume label created by Mac OS X cannot be retrieved with f_getlabel(). (appeared at R0.09b)
Fixed a potential problem of FAT access that can appear on disk error.
Fixed null pointer dereference on attempting to delete the root direcotry. (appeared at R0.08)
R0.11 (February 09, 2015)
Added f_findfirst(), f_findnext() and f_findclose(). (_USE_FIND)
Fixed f_unlink() does not remove cluster chain of the file. (appeared at R0.10c)
Fixed _FS_NORTC option does not work properly. (appeared at R0.10c)
R0.11a (September 05, 2015)
Fixed wrong media change can lead a deadlock at thread-safe configuration.
Added code page 771, 860, 861, 863, 864, 865 and 869. (_CODE_PAGE)
Removed some code pages actually not exist on the standard systems. (_CODE_PAGE)
Fixed errors in the case conversion teble of code page 437 and 850 (ff.c).
Fixed errors in the case conversion teble of Unicode (cc*.c).
R0.12 (April 12, 2016)
Added support for exFAT file system. (_FS_EXFAT)
Added f_expand(). (_USE_EXPAND)
Changed some members in FINFO structure and behavior of f_readdir().
Added an option _USE_CHMOD.
Removed an option _WORD_ACCESS.
Fixed errors in the case conversion table of Unicode (cc*.c).
R0.12a (July 10, 2016)
Added support for creating exFAT volume with some changes of f_mkfs().
Added a file open method FA_OPEN_APPEND. An f_lseek() following f_open() is no longer needed.
f_forward() is available regardless of _FS_TINY.
Fixed f_mkfs() creates wrong volume. (appeared at R0.12)
Fixed wrong memory read in create_name(). (appeared at R0.12)
Fixed compilation fails at some configurations, _USE_FASTSEEK and _USE_FORWARD.
R0.12b (September 04, 2016)
Made f_rename() be able to rename objects with the same name but case.
Fixed an error in the case conversion teble of code page 866. (ff.c)
Fixed writing data is truncated at the file offset 4GiB on the exFAT volume. (appeared at R0.12)
Fixed creating a file in the root directory of exFAT volume can fail. (appeared at R0.12)
Fixed f_mkfs() creating exFAT volume with too small cluster size can collapse unallocated memory. (appeared at R0.12)
Fixed wrong object name can be returned when read directory at Unicode cfg. (appeared at R0.12)
Fixed large file allocation/removing on the exFAT volume collapses allocation bitmap. (appeared at R0.12)
Fixed some internal errors in f_expand() and f_lseek(). (appeared at R0.12)
R0.12c (March 04, 2017)
Improved write throughput at the fragmented file on the exFAT volume.
Made memory usage for exFAT be able to be reduced as decreasing _MAX_LFN.
Fixed successive f_getfree() can return wrong count on the FAT12/16 volume. (appeared at R0.12)
Fixed configuration option _VOLUMES cannot be set 10. (appeared at R0.10c)
R0.13 (May 21, 2017)
Changed heading character of configuration keywords "_" to "FF_".
Removed ASCII-only configuration, FF_CODE_PAGE = 1. Use FF_CODE_PAGE = 437 instead.
Added f_setcp(), run-time code page configuration. (FF_CODE_PAGE = 0)
Improved cluster allocation time on stretch a deep buried cluster chain.
Improved processing time of f_mkdir() with large cluster size by using FF_USE_LFN = 3.
Improved NoFatChain flag of the fragmented file to be set after it is truncated and got contiguous.
Fixed archive attribute is left not set when a file on the exFAT volume is renamed. (appeared at R0.12)
Fixed exFAT FAT entry can be collapsed when write or lseek operation to the existing file is done. (appeared at R0.12c)
Fixed creating a file can fail when a new cluster allocation to the exFAT directory occures. (appeared at R0.12c)
R0.13a (October 14, 2017)
Added support for UTF-8 encoding on the API. (FF_LFN_UNICODE = 2)
Added options for file name output buffer. (FF_LFN_BUF, FF_SFN_BUF).
Added dynamic memory allocation option for working buffer of f_mkfs() and f_fdisk().
Fixed f_fdisk() and f_mkfs() create the partition table with wrong CHS parameters. (appeared at R0.09)
Fixed f_unlink() can cause lost clusters at fragmented file on the exFAT volume. (appeared at R0.12c)
Fixed f_setlabel() rejects some valid characters for exFAT volume. (appeared at R0.12)
R0.13b (April 07, 2018)
Added support for UTF-32 encoding on the API. (FF_LFN_UNICODE = 3)
Added support for Unix style volume ID. (FF_STR_VOLUME_ID = 2)
Fixed accesing any object on the exFAT root directory beyond the cluster boundary can fail. (appeared at R0.12c)
Fixed f_setlabel() does not reject some invalid characters. (appeared at R0.09b)
R0.13c (October 14, 2018)
Supported stdint.h for C99 and later. (integer.h was included in ff.h)
Fixed reading a directory gets infinite loop when the last directory entry is not empty. (appeared at R0.12)
Fixed creating a sub-directory in the fragmented sub-directory on the exFAT volume collapses FAT chain of the parent directory. (appeared at R0.12)
Fixed f_getcwd() cause output buffer overrun when the buffer has a valid drive number. (appeared at R0.13b)
R0.14 (October 14, 2019)
Added support for 64-bit LBA and GUID partition table (FF_LBA64 = 1)
Changed some API functions, f_mkfs() and f_fdisk().
Fixed f_open() function cannot find the file with file name in length of FF_MAX_LFN characters.
Fixed f_readdir() function cannot retrieve long file names in length of FF_MAX_LFN - 1 characters.
Fixed f_readdir() function returns file names with wrong case conversion. (appeared at R0.12)
Fixed f_mkfs() function can fail to create exFAT volume in the second partition. (appeared at R0.12)
R0.14a (December 5, 2020)
Limited number of recursive calls in f_findnext().
Fixed old floppy disks formatted with MS-DOS 2.x and 3.x cannot be mounted.
Fixed some compiler warnings.
R0.14b (April 17, 2021)
Made FatFs uses standard library <string.h> for copy, compare and search instead of built-in string functions.
Added support for long long integer and floating point to f_printf(). (FF_STRF_LLI and FF_STRF_FP)
Made path name parser ignore the terminating separator to allow "dir/".
Improved the compatibility in Unix style path name feature.
Fixed the file gets dead-locked when f_open() failed with some conditions. (appeared at R0.12a)
Fixed f_mkfs() can create wrong exFAT volume due to a timing dependent error. (appeared at R0.12)
Fixed code page 855 cannot be set by f_setcp().
Fixed some compiler warnings.

21
src/fatfs/00readme.txt Normal file
View File

@ -0,0 +1,21 @@
FatFs Module Source Files R0.14b
FILES
00readme.txt This file.
00history.txt Revision history.
ff.c FatFs module.
ffconf.h Configuration file of FatFs module.
ff.h Common include file for FatFs and application module.
diskio.h Common include file for FatFs and disk I/O module.
diskio.c An example of glue function to attach existing disk I/O module to FatFs.
ffunicode.c Optional Unicode utility functions.
ffsystem.c An example of optional O/S related functions.
Low level disk I/O module is not included in this archive because the FatFs
module is only a generic file system layer and it does not depend on any specific
storage device. You need to provide a low level disk I/O module written to
control the storage device that attached to the target system.

24
src/fatfs/LICENSE.txt Normal file
View File

@ -0,0 +1,24 @@
FatFs License
FatFs has being developped as a personal project of the author, ChaN. It is free from the code anyone else wrote at current release. Following code block shows a copy of the FatFs license document that heading the source files.
/*----------------------------------------------------------------------------/
/ FatFs - Generic FAT Filesystem Module Rx.xx /
/-----------------------------------------------------------------------------/
/
/ Copyright (C) 20xx, ChaN, all right reserved.
/
/ FatFs module is an open source software. Redistribution and use of FatFs in
/ source and binary forms, with or without modification, are permitted provided
/ that the following condition is met:
/
/ 1. Redistributions of source code must retain the above copyright notice,
/ this condition and the following disclaimer.
/
/ This software is provided by the copyright holder and contributors "AS IS"
/ and any warranties related to this software are DISCLAIMED.
/ The copyright owner or contributors be NOT LIABLE for any damages caused
/ by use of this software.
/----------------------------------------------------------------------------*/
Therefore FatFs license is one of the BSD-style licenses, but there is a significant feature. FatFs is mainly intended for embedded systems. In order to extend the usability for commercial products, the redistributions of FatFs in binary form, such as embedded code, binary library and any forms without source code, do not need to include about FatFs in the documentations. This is equivalent to the 1-clause BSD license. Of course FatFs is compatible with the most of open source software licenses include GNU GPL. When you redistribute the FatFs source code with changes or create a fork, the license can also be changed to GNU GPL, BSD-style license or any open source software license that not conflict with FatFs license.

153
src/fatfs/diskio.c Normal file
View File

@ -0,0 +1,153 @@
/*-----------------------------------------------------------------------*/
/* Low level disk I/O module SKELETON for FatFs (C)ChaN, 2019 */
/*-----------------------------------------------------------------------*/
/* If a working storage control module is available, it should be */
/* attached to the FatFs via a glue function rather than modifying it. */
/* This is an example of glue functions to attach various exsisting */
/* storage control modules to the FatFs module with a defined API. */
/*-----------------------------------------------------------------------*/
#include "ff.h" /* Obtains integer types */
#include "diskio.h" /* Declarations of disk functions */
static ff_disk_read_cb ReadCb;
static ff_disk_write_cb WriteCb;
static LBA_t SectorCount;
static DSTATUS Status = STA_NOINIT | STA_NODISK;
void ff_disk_open(ff_disk_read_cb readcb, ff_disk_write_cb writecb, LBA_t seccnt)
{
if (!readcb) return;
ReadCb = readcb;
WriteCb = writecb;
SectorCount = seccnt;
Status &= ~STA_NODISK;
if (!writecb) Status |= STA_PROTECT;
else Status &= ~STA_PROTECT;
}
void ff_disk_close()
{
ReadCb = (void*)0;
WriteCb = (void*)0;
SectorCount = 0;
Status &= ~STA_PROTECT;
Status |= STA_NODISK;
}
/*-----------------------------------------------------------------------*/
/* Get Drive Status */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
return Status;
}
/*-----------------------------------------------------------------------*/
/* Inidialize a Drive */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
Status &= ~STA_NOINIT;
return Status;
}
/*-----------------------------------------------------------------------*/
/* Read Sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT disk_read (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
BYTE *buff, /* Data buffer to store read data */
LBA_t sector, /* Start sector in LBA */
UINT count /* Number of sectors to read */
)
{
if (Status & (STA_NOINIT | STA_NODISK)) return RES_NOTRDY;
if (!ReadCb) return RES_ERROR;
UINT res = ReadCb(buff, sector, count);
if (res != count) return RES_ERROR;
return RES_OK;
}
/*-----------------------------------------------------------------------*/
/* Write Sector(s) */
/*-----------------------------------------------------------------------*/
#if FF_FS_READONLY == 0
DRESULT disk_write (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
const BYTE *buff, /* Data to be written */
LBA_t sector, /* Start sector in LBA */
UINT count /* Number of sectors to write */
)
{
if (Status & (STA_NOINIT | STA_NODISK)) return RES_NOTRDY;
if (Status & STA_PROTECT) return RES_WRPRT;
if (!WriteCb) return RES_ERROR;
UINT res = WriteCb(buff, sector, count);
if (res != count) return RES_ERROR;
return RES_OK;
}
#endif
/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions */
/*-----------------------------------------------------------------------*/
DRESULT disk_ioctl (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
switch (cmd)
{
case CTRL_SYNC:
// TODO: fflush?
return RES_OK;
case GET_SECTOR_COUNT:
*(LBA_t*)buff = SectorCount;
return RES_OK;
case GET_SECTOR_SIZE:
*(WORD*)buff = 0x200;
return RES_OK;
case GET_BLOCK_SIZE:
*(DWORD*)buff = 1;
return RES_OK;
case CTRL_TRIM:
// TODO??
return RES_OK;
}
//printf("FatFS: unknown disk_ioctl(%02X, %02X, %p)\n", pdrv, cmd, buff);
return RES_PARERR;
}

77
src/fatfs/diskio.h Normal file
View File

@ -0,0 +1,77 @@
/*-----------------------------------------------------------------------/
/ Low level disk interface modlue include file (C)ChaN, 2019 /
/-----------------------------------------------------------------------*/
#ifndef _DISKIO_DEFINED
#define _DISKIO_DEFINED
#ifdef __cplusplus
extern "C" {
#endif
/* Status of Disk Functions */
typedef BYTE DSTATUS;
/* Results of Disk Functions */
typedef enum {
RES_OK = 0, /* 0: Successful */
RES_ERROR, /* 1: R/W Error */
RES_WRPRT, /* 2: Write Protected */
RES_NOTRDY, /* 3: Not Ready */
RES_PARERR /* 4: Invalid Parameter */
} DRESULT;
/*---------------------------------------*/
/* Prototypes for disk control functions */
DSTATUS disk_initialize (BYTE pdrv);
DSTATUS disk_status (BYTE pdrv);
DRESULT disk_read (BYTE pdrv, BYTE* buff, LBA_t sector, UINT count);
DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count);
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
/* Disk Status Bits (DSTATUS) */
#define STA_NOINIT 0x01 /* Drive not initialized */
#define STA_NODISK 0x02 /* No medium in the drive */
#define STA_PROTECT 0x04 /* Write protected */
/* Command code for disk_ioctrl fucntion */
/* Generic command (Used by FatFs) */
#define CTRL_SYNC 0 /* Complete pending write process (needed at FF_FS_READONLY == 0) */
#define GET_SECTOR_COUNT 1 /* Get media size (needed at FF_USE_MKFS == 1) */
#define GET_SECTOR_SIZE 2 /* Get sector size (needed at FF_MAX_SS != FF_MIN_SS) */
#define GET_BLOCK_SIZE 3 /* Get erase block size (needed at FF_USE_MKFS == 1) */
#define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at FF_USE_TRIM == 1) */
/* Generic command (Not used by FatFs) */
#define CTRL_POWER 5 /* Get/Set power status */
#define CTRL_LOCK 6 /* Lock/Unlock media removal */
#define CTRL_EJECT 7 /* Eject media */
#define CTRL_FORMAT 8 /* Create physical format on the media */
/* MMC/SDC specific ioctl command */
#define MMC_GET_TYPE 10 /* Get card type */
#define MMC_GET_CSD 11 /* Get CSD */
#define MMC_GET_CID 12 /* Get CID */
#define MMC_GET_OCR 13 /* Get OCR */
#define MMC_GET_SDSTAT 14 /* Get SD status */
#define ISDIO_READ 55 /* Read data form SD iSDIO register */
#define ISDIO_WRITE 56 /* Write data to SD iSDIO register */
#define ISDIO_MRITE 57 /* Masked write data to SD iSDIO register */
/* ATA/CF specific ioctl command */
#define ATA_GET_REV 20 /* Get F/W revision */
#define ATA_GET_MODEL 21 /* Get model name */
#define ATA_GET_SN 22 /* Get serial number */
#ifdef __cplusplus
}
#endif
#endif

6982
src/fatfs/ff.c Normal file

File diff suppressed because it is too large Load Diff

431
src/fatfs/ff.h Normal file
View File

@ -0,0 +1,431 @@
/*----------------------------------------------------------------------------/
/ FatFs - Generic FAT Filesystem module R0.14b /
/-----------------------------------------------------------------------------/
/
/ Copyright (C) 2021, ChaN, all right reserved.
/
/ FatFs module is an open source software. Redistribution and use of FatFs in
/ source and binary forms, with or without modification, are permitted provided
/ that the following condition is met:
/ 1. Redistributions of source code must retain the above copyright notice,
/ this condition and the following disclaimer.
/
/ This software is provided by the copyright holder and contributors "AS IS"
/ and any warranties related to this software are DISCLAIMED.
/ The copyright owner or contributors be NOT LIABLE for any damages caused
/ by use of this software.
/
/----------------------------------------------------------------------------*/
#ifndef FF_DEFINED
#define FF_DEFINED 86631 /* Revision ID */
#ifdef __cplusplus
extern "C" {
#endif
#include "ffconf.h" /* FatFs configuration options */
#if FF_DEFINED != FFCONF_DEF
#error Wrong configuration file (ffconf.h).
#endif
/* Integer types used for FatFs API */
#if defined(_WIN32) /* Windows VC++ (for development only) */
#define FF_INTDEF 2
#include <windows.h>
typedef unsigned __int64 QWORD;
#include <float.h>
#define isnan(v) _isnan(v)
#define isinf(v) (!_finite(v))
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus) /* C99 or later */
#define FF_INTDEF 2
#include <stdint.h>
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
typedef unsigned char BYTE; /* char must be 8-bit */
typedef uint16_t WORD; /* 16-bit unsigned integer */
typedef uint32_t DWORD; /* 32-bit unsigned integer */
typedef uint64_t QWORD; /* 64-bit unsigned integer */
typedef WORD WCHAR; /* UTF-16 character type */
#else /* Earlier than C99 */
#define FF_INTDEF 1
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
typedef unsigned char BYTE; /* char must be 8-bit */
typedef unsigned short WORD; /* 16-bit unsigned integer */
typedef unsigned long DWORD; /* 32-bit unsigned integer */
typedef WORD WCHAR; /* UTF-16 character type */
#endif
/* Type of file size and LBA variables */
#if FF_FS_EXFAT
#if FF_INTDEF != 2
#error exFAT feature wants C99 or later
#endif
typedef QWORD FSIZE_t;
#if FF_LBA64
typedef QWORD LBA_t;
#else
typedef DWORD LBA_t;
#endif
#else
#if FF_LBA64
#error exFAT needs to be enabled when enable 64-bit LBA
#endif
typedef DWORD FSIZE_t;
typedef DWORD LBA_t;
#endif
/* Type of path name strings on FatFs API (TCHAR) */
#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */
typedef WCHAR TCHAR;
#define _T(x) L ## x
#define _TEXT(x) L ## x
#elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 encoding */
typedef char TCHAR;
#define _T(x) u8 ## x
#define _TEXT(x) u8 ## x
#elif FF_USE_LFN && FF_LFN_UNICODE == 3 /* Unicode in UTF-32 encoding */
typedef DWORD TCHAR;
#define _T(x) U ## x
#define _TEXT(x) U ## x
#elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3)
#error Wrong FF_LFN_UNICODE setting
#else /* ANSI/OEM code in SBCS/DBCS */
typedef char TCHAR;
#define _T(x) x
#define _TEXT(x) x
#endif
/* Definitions of volume management */
#if FF_MULTI_PARTITION /* Multiple partition configuration */
typedef struct {
BYTE pd; /* Physical drive number */
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
} PARTITION;
extern PARTITION VolToPart[]; /* Volume - Partition mapping table */
#endif
#if FF_STR_VOLUME_ID
#ifndef FF_VOLUME_STRS
extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */
#endif
#endif
/* Filesystem object structure (FATFS) */
typedef struct {
BYTE fs_type; /* Filesystem type (0:not mounted) */
BYTE pdrv; /* Associated physical drive */
BYTE n_fats; /* Number of FATs (1 or 2) */
BYTE wflag; /* win[] flag (b0:dirty) */
BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */
WORD id; /* Volume mount ID */
WORD n_rootdir; /* Number of root directory entries (FAT12/16) */
WORD csize; /* Cluster size [sectors] */
#if FF_MAX_SS != FF_MIN_SS
WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */
#endif
#if FF_USE_LFN
WCHAR* lfnbuf; /* LFN working buffer */
#endif
#if FF_FS_EXFAT
BYTE* dirbuf; /* Directory entry block scratchpad buffer for exFAT */
#endif
#if FF_FS_REENTRANT
FF_SYNC_t sobj; /* Identifier of sync object */
#endif
#if !FF_FS_READONLY
DWORD last_clst; /* Last allocated cluster */
DWORD free_clst; /* Number of free clusters */
#endif
#if FF_FS_RPATH
DWORD cdir; /* Current directory start cluster (0:root) */
#if FF_FS_EXFAT
DWORD cdc_scl; /* Containing directory start cluster (invalid when cdir is 0) */
DWORD cdc_size; /* b31-b8:Size of containing directory, b7-b0: Chain status */
DWORD cdc_ofs; /* Offset in the containing directory (invalid when cdir is 0) */
#endif
#endif
DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */
DWORD fsize; /* Size of an FAT [sectors] */
LBA_t volbase; /* Volume base sector */
LBA_t fatbase; /* FAT base sector */
LBA_t dirbase; /* Root directory base sector/cluster */
LBA_t database; /* Data base sector */
#if FF_FS_EXFAT
LBA_t bitbase; /* Allocation bitmap base sector */
#endif
LBA_t winsect; /* Current sector appearing in the win[] */
BYTE win[FF_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */
} FATFS;
/* Object ID and allocation information (FFOBJID) */
typedef struct {
FATFS* fs; /* Pointer to the hosting volume of this object */
WORD id; /* Hosting volume mount ID */
BYTE attr; /* Object attribute */
BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous, =3:fragmented in this session, b2:sub-directory stretched) */
DWORD sclust; /* Object data start cluster (0:no cluster or root directory) */
FSIZE_t objsize; /* Object size (valid when sclust != 0) */
#if FF_FS_EXFAT
DWORD n_cont; /* Size of first fragment - 1 (valid when stat == 3) */
DWORD n_frag; /* Size of last fragment needs to be written to FAT (valid when not zero) */
DWORD c_scl; /* Containing directory start cluster (valid when sclust != 0) */
DWORD c_size; /* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */
DWORD c_ofs; /* Offset in the containing directory (valid when file object and sclust != 0) */
#endif
#if FF_FS_LOCK
UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */
#endif
} FFOBJID;
/* File object structure (FIL) */
typedef struct {
FFOBJID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */
BYTE flag; /* File status flags */
BYTE err; /* Abort flag (error code) */
FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */
DWORD clust; /* Current cluster of fpter (invalid when fptr is 0) */
LBA_t sect; /* Sector number appearing in buf[] (0:invalid) */
#if !FF_FS_READONLY
LBA_t dir_sect; /* Sector number containing the directory entry (not used at exFAT) */
BYTE* dir_ptr; /* Pointer to the directory entry in the win[] (not used at exFAT) */
#endif
#if FF_USE_FASTSEEK
DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */
#endif
#if !FF_FS_TINY
BYTE buf[FF_MAX_SS]; /* File private data read/write window */
#endif
} FF_FIL;
/* Directory object structure (DIR) */
typedef struct {
FFOBJID obj; /* Object identifier */
DWORD dptr; /* Current read/write offset */
DWORD clust; /* Current cluster */
LBA_t sect; /* Current sector (0:Read operation has terminated) */
BYTE* dir; /* Pointer to the directory item in the win[] */
BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */
#if FF_USE_LFN
DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */
#endif
#if FF_USE_FIND
const TCHAR* pat; /* Pointer to the name matching pattern */
#endif
} FF_DIR;
/* File information structure (FILINFO) */
typedef struct {
FSIZE_t fsize; /* File size */
WORD fdate; /* Modified date */
WORD ftime; /* Modified time */
BYTE fattrib; /* File attribute */
#if FF_USE_LFN
TCHAR altname[FF_SFN_BUF + 1];/* Altenative file name */
TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */
#else
TCHAR fname[12 + 1]; /* File name */
#endif
} FF_FILINFO;
/* Format parameter structure (MKFS_PARM) */
typedef struct {
BYTE fmt; /* Format option (FM_FAT, FM_FAT32, FM_EXFAT and FM_SFD) */
BYTE n_fat; /* Number of FATs */
UINT align; /* Data area alignment (sector) */
UINT n_root; /* Number of root directory entries */
DWORD au_size; /* Cluster size (byte) */
} FF_MKFS_PARM;
/* File function return code (FRESULT) */
typedef enum {
FR_OK = 0, /* (0) Succeeded */
FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */
FR_INT_ERR, /* (2) Assertion failed */
FR_NOT_READY, /* (3) The physical drive cannot work */
FR_NO_FILE, /* (4) Could not find the file */
FR_NO_PATH, /* (5) Could not find the path */
FR_INVALID_NAME, /* (6) The path name format is invalid */
FR_DENIED, /* (7) Access denied due to prohibited access or directory full */
FR_EXIST, /* (8) Access denied due to prohibited access */
FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */
FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */
FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */
FR_NOT_ENABLED, /* (12) The volume has no work area */
FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */
FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any problem */
FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */
FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */
FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */
FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > FF_FS_LOCK */
FR_INVALID_PARAMETER /* (19) Given parameter is invalid */
} FRESULT;
/*--------------------------------------------------------------*/
/* FatFs module application interface */
FRESULT f_open (FF_FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */
FRESULT f_close (FF_FIL* fp); /* Close an open file object */
FRESULT f_read (FF_FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */
FRESULT f_write (FF_FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */
FRESULT f_lseek (FF_FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */
FRESULT f_truncate (FF_FIL* fp); /* Truncate the file */
FRESULT f_sync (FF_FIL* fp); /* Flush cached data of the writing file */
FRESULT f_opendir (FF_DIR* dp, const TCHAR* path); /* Open a directory */
FRESULT f_closedir (FF_DIR* dp); /* Close an open directory */
FRESULT f_readdir (FF_DIR* dp, FF_FILINFO* fno); /* Read a directory item */
FRESULT f_findfirst (FF_DIR* dp, FF_FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */
FRESULT f_findnext (FF_DIR* dp, FF_FILINFO* fno); /* Find next file */
FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */
FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */
FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */
FRESULT f_stat (const TCHAR* path, FF_FILINFO* fno); /* Get file status */
FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */
FRESULT f_utime (const TCHAR* path, const FF_FILINFO* fno); /* Change timestamp of a file/dir */
FRESULT f_chdir (const TCHAR* path); /* Change current directory */
FRESULT f_chdrive (const TCHAR* path); /* Change current drive */
FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */
FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */
FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */
FRESULT f_setlabel (const TCHAR* label); /* Set volume label */
FRESULT f_forward (FF_FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */
FRESULT f_expand (FF_FIL* fp, FSIZE_t fsz, BYTE opt); /* Allocate a contiguous block to the file */
FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */
FRESULT f_mkfs (const TCHAR* path, const FF_MKFS_PARM* opt, void* work, UINT len); /* Create a FAT volume */
FRESULT f_fdisk (BYTE pdrv, const LBA_t ptbl[], void* work); /* Divide a physical drive into some partitions */
FRESULT f_setcp (WORD cp); /* Set current code page */
int f_putc (TCHAR c, FF_FIL* fp); /* Put a character to the file */
int f_puts (const TCHAR* str, FF_FIL* cp); /* Put a string to the file */
int f_printf (FF_FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */
TCHAR* f_gets (TCHAR* buff, int len, FF_FIL* fp); /* Get a string from the file */
#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize))
#define f_error(fp) ((fp)->err)
#define f_tell(fp) ((fp)->fptr)
#define f_size(fp) ((fp)->obj.objsize)
#define f_rewind(fp) f_lseek((fp), 0)
#define f_rewinddir(dp) f_readdir((dp), 0)
#define f_rmdir(path) f_unlink(path)
#define f_unmount(path) f_mount(0, path, 0)
/*--------------------------------------------------------------*/
/* Additional user defined functions */
/* RTC function */
#if !FF_FS_READONLY && !FF_FS_NORTC
DWORD get_fattime (void);
#endif
/* LFN support functions */
#if FF_USE_LFN >= 1 /* Code conversion (defined in unicode.c) */
WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode conversion */
WCHAR ff_uni2oem (DWORD uni, WORD cp); /* Unicode to OEM code conversion */
DWORD ff_wtoupper (DWORD uni); /* Unicode upper-case conversion */
#endif
#if FF_USE_LFN == 3 /* Dynamic memory allocation */
void* ff_memalloc (UINT msize); /* Allocate memory block */
void ff_memfree (void* mblock); /* Free memory block */
#endif
/* Sync functions */
#if FF_FS_REENTRANT
int ff_cre_syncobj (BYTE vol, FF_SYNC_t* sobj); /* Create a sync object */
int ff_req_grant (FF_SYNC_t sobj); /* Lock sync object */
void ff_rel_grant (FF_SYNC_t sobj); /* Unlock sync object */
int ff_del_syncobj (FF_SYNC_t sobj); /* Delete a sync object */
#endif
/*--------------------------------------------------------------*/
/* Flags and offset address */
/* File access mode and open method flags (3rd argument of f_open) */
#define FA_READ 0x01
#define FA_WRITE 0x02
#define FA_OPEN_EXISTING 0x00
#define FA_CREATE_NEW 0x04
#define FA_CREATE_ALWAYS 0x08
#define FA_OPEN_ALWAYS 0x10
#define FA_OPEN_APPEND 0x30
/* Fast seek controls (2nd argument of f_lseek) */
#define CREATE_LINKMAP ((FSIZE_t)0 - 1)
/* Format options (2nd argument of f_mkfs) */
#define FM_FAT 0x01
#define FM_FAT32 0x02
#define FM_EXFAT 0x04
#define FM_ANY 0x07
#define FM_SFD 0x08
/* Filesystem type (FATFS.fs_type) */
#define FS_FAT12 1
#define FS_FAT16 2
#define FS_FAT32 3
#define FS_EXFAT 4
/* File attribute bits for directory entry (FILINFO.fattrib) */
#define AM_RDO 0x01 /* Read only */
#define AM_HID 0x02 /* Hidden */
#define AM_SYS 0x04 /* System */
#define AM_DIR 0x10 /* Directory */
#define AM_ARC 0x20 /* Archive */
// extra additions for interfacing with melonDS
typedef UINT (*ff_disk_read_cb)(BYTE* buff, LBA_t sector, UINT count);
typedef UINT (*ff_disk_write_cb)(BYTE* buff, LBA_t sector, UINT count);
void ff_disk_open(ff_disk_read_cb readcb, ff_disk_write_cb writecb, LBA_t seccnt);
void ff_disk_close();
#ifdef __cplusplus
}
#endif
#endif /* FF_DEFINED */

301
src/fatfs/ffconf.h Normal file
View File

@ -0,0 +1,301 @@
/*---------------------------------------------------------------------------/
/ FatFs Functional Configurations
/---------------------------------------------------------------------------*/
#define FFCONF_DEF 86631 /* Revision ID */
/*---------------------------------------------------------------------------/
/ Function Configurations
/---------------------------------------------------------------------------*/
#define FF_FS_READONLY 0
/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
/ Read-only configuration removes writing API functions, f_write(), f_sync(),
/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
/ and optional writing functions as well. */
#define FF_FS_MINIMIZE 0
/* This option defines minimization level to remove some basic API functions.
/
/ 0: Basic functions are fully enabled.
/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()
/ are removed.
/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
/ 3: f_lseek() function is removed in addition to 2. */
#define FF_USE_FIND 0
/* This option switches filtered directory read functions, f_findfirst() and
/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
#define FF_USE_MKFS 1
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
#define FF_USE_FASTSEEK 0
/* This option switches fast seek function. (0:Disable or 1:Enable) */
#define FF_USE_EXPAND 0
/* This option switches f_expand function. (0:Disable or 1:Enable) */
#define FF_USE_CHMOD 1
/* This option switches attribute manipulation functions, f_chmod() and f_utime().
/ (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */
#define FF_USE_LABEL 0
/* This option switches volume label functions, f_getlabel() and f_setlabel().
/ (0:Disable or 1:Enable) */
#define FF_USE_FORWARD 0
/* This option switches f_forward() function. (0:Disable or 1:Enable) */
#define FF_USE_STRFUNC 0
#define FF_PRINT_LLI 0
#define FF_PRINT_FLOAT 0
#define FF_STRF_ENCODE 0
/* FF_USE_STRFUNC switches string functions, f_gets(), f_putc(), f_puts() and
/ f_printf().
/
/ 0: Disable. FF_PRINT_LLI, FF_PRINT_FLOAT and FF_STRF_ENCODE have no effect.
/ 1: Enable without LF-CRLF conversion.
/ 2: Enable with LF-CRLF conversion.
/
/ FF_PRINT_LLI = 1 makes f_printf() support long long argument and FF_PRINT_FLOAT = 1/2
makes f_printf() support floating point argument. These features want C99 or later.
/ When FF_LFN_UNICODE >= 1 with LFN enabled, string functions convert the character
/ encoding in it. FF_STRF_ENCODE selects assumption of character encoding ON THE FILE
/ to be read/written via those functions.
/
/ 0: ANSI/OEM in current CP
/ 1: Unicode in UTF-16LE
/ 2: Unicode in UTF-16BE
/ 3: Unicode in UTF-8
*/
/*---------------------------------------------------------------------------/
/ Locale and Namespace Configurations
/---------------------------------------------------------------------------*/
#define FF_CODE_PAGE 932
/* This option specifies the OEM code page to be used on the target system.
/ Incorrect code page setting can cause a file open failure.
/
/ 437 - U.S.
/ 720 - Arabic
/ 737 - Greek
/ 771 - KBL
/ 775 - Baltic
/ 850 - Latin 1
/ 852 - Latin 2
/ 855 - Cyrillic
/ 857 - Turkish
/ 860 - Portuguese
/ 861 - Icelandic
/ 862 - Hebrew
/ 863 - Canadian French
/ 864 - Arabic
/ 865 - Nordic
/ 866 - Russian
/ 869 - Greek 2
/ 932 - Japanese (DBCS)
/ 936 - Simplified Chinese (DBCS)
/ 949 - Korean (DBCS)
/ 950 - Traditional Chinese (DBCS)
/ 0 - Include all code pages above and configured by f_setcp()
*/
#define FF_USE_LFN 1
#define FF_MAX_LFN 255
/* The FF_USE_LFN switches the support for LFN (long file name).
/
/ 0: Disable LFN. FF_MAX_LFN has no effect.
/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
/ 2: Enable LFN with dynamic working buffer on the STACK.
/ 3: Enable LFN with dynamic working buffer on the HEAP.
/
/ To enable the LFN, ffunicode.c needs to be added to the project. The LFN function
/ requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
/ additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
/ The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
/ be in range of 12 to 255. It is recommended to be set it 255 to fully support LFN
/ specification.
/ When use stack for the working buffer, take care on stack overflow. When use heap
/ memory for the working buffer, memory management functions, ff_memalloc() and
/ ff_memfree() exemplified in ffsystem.c, need to be added to the project. */
#define FF_LFN_UNICODE 2
/* This option switches the character encoding on the API when LFN is enabled.
/
/ 0: ANSI/OEM in current CP (TCHAR = char)
/ 1: Unicode in UTF-16 (TCHAR = WCHAR)
/ 2: Unicode in UTF-8 (TCHAR = char)
/ 3: Unicode in UTF-32 (TCHAR = DWORD)
/
/ Also behavior of string I/O functions will be affected by this option.
/ When LFN is not enabled, this option has no effect. */
#define FF_LFN_BUF 255
#define FF_SFN_BUF 12
/* This set of options defines size of file name members in the FILINFO structure
/ which is used to read out directory items. These values should be suffcient for
/ the file names to read. The maximum possible length of the read file name depends
/ on character encoding. When LFN is not enabled, these options have no effect. */
#define FF_FS_RPATH 0
/* This option configures support for relative path.
/
/ 0: Disable relative path and remove related functions.
/ 1: Enable relative path. f_chdir() and f_chdrive() are available.
/ 2: f_getcwd() function is available in addition to 1.
*/
/*---------------------------------------------------------------------------/
/ Drive/Volume Configurations
/---------------------------------------------------------------------------*/
#define FF_VOLUMES 1
/* Number of volumes (logical drives) to be used. (1-10) */
#define FF_STR_VOLUME_ID 0
#define FF_VOLUME_STRS "fat"
/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings.
/ When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive
/ number in the path name. FF_VOLUME_STRS defines the volume ID strings for each
/ logical drives. Number of items must not be less than FF_VOLUMES. Valid
/ characters for the volume ID strings are A-Z, a-z and 0-9, however, they are
/ compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is
/ not defined, a user defined volume string table needs to be defined as:
/
/ const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",...
*/
#define FF_MULTI_PARTITION 0
/* This option switches support for multiple volumes on the physical drive.
/ By default (0), each logical drive number is bound to the same physical drive
/ number and only an FAT volume found on the physical drive will be mounted.
/ When this function is enabled (1), each logical drive number can be bound to
/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
/ funciton will be available. */
#define FF_MIN_SS 512
#define FF_MAX_SS 512
/* This set of options configures the range of sector size to be supported. (512,
/ 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
/ harddisk, but a larger value may be required for on-board flash memory and some
/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured
/ for variable sector size mode and disk_ioctl() function needs to implement
/ GET_SECTOR_SIZE command. */
#define FF_LBA64 0
/* This option switches support for 64-bit LBA. (0:Disable or 1:Enable)
/ To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */
#define FF_MIN_GPT 0x10000000
/* Minimum number of sectors to switch GPT as partitioning format in f_mkfs and
/ f_fdisk function. 0x100000000 max. This option has no effect when FF_LBA64 == 0. */
#define FF_USE_TRIM 0
/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable)
/ To enable Trim function, also CTRL_TRIM command should be implemented to the
/ disk_ioctl() function. */
/*---------------------------------------------------------------------------/
/ System Configurations
/---------------------------------------------------------------------------*/
#define FF_FS_TINY 0
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
/ At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes.
/ Instead of private sector buffer eliminated from the file object, common sector
/ buffer in the filesystem object (FATFS) is used for the file data transfer. */
#define FF_FS_EXFAT 0
/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable)
/ To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1)
/ Note that enabling exFAT discards ANSI C (C89) compatibility. */
#define FF_FS_NORTC 0
#define FF_NORTC_MON 1
#define FF_NORTC_MDAY 1
#define FF_NORTC_YEAR 2020
/* The option FF_FS_NORTC switches timestamp functiton. If the system does not have
/ any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable
/ the timestamp function. Every object modified by FatFs will have a fixed timestamp
/ defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.
/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be
/ added to the project to read current time form real-time clock. FF_NORTC_MON,
/ FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.
/ These options have no effect in read-only configuration (FF_FS_READONLY = 1). */
#define FF_FS_NOFSINFO 0
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
/ option, and f_getfree() function at first time after volume mount will force
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
/
/ bit0=0: Use free cluster count in the FSINFO if available.
/ bit0=1: Do not trust free cluster count in the FSINFO.
/ bit1=0: Use last allocated cluster number in the FSINFO if available.
/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
*/
#define FF_FS_LOCK 0
/* The option FF_FS_LOCK switches file lock function to control duplicated file open
/ and illegal operation to open objects. This option must be 0 when FF_FS_READONLY
/ is 1.
/
/ 0: Disable file lock function. To avoid volume corruption, application program
/ should avoid illegal open, remove and rename to the open objects.
/ >0: Enable file lock function. The value defines how many files/sub-directories
/ can be opened simultaneously under file lock control. Note that the file
/ lock control is independent of re-entrancy. */
/* #include <somertos.h> // O/S definitions */
#define FF_FS_REENTRANT 0
#define FF_FS_TIMEOUT 1000
#define FF_SYNC_t HANDLE
/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
/ module itself. Note that regardless of this option, file access to different
/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
/ and f_fdisk() function, are always not re-entrant. Only file/directory access
/ to the same volume is under control of this function.
/
/ 0: Disable re-entrancy. FF_FS_TIMEOUT and FF_SYNC_t have no effect.
/ 1: Enable re-entrancy. Also user provided synchronization handlers,
/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
/ function, must be added to the project. Samples are available in
/ option/syscall.c.
/
/ The FF_FS_TIMEOUT defines timeout period in unit of time tick.
/ The FF_SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
/ SemaphoreHandle_t and etc. A header file for O/S definitions needs to be
/ included somewhere in the scope of ff.h. */
/*--- End of configuration options ---*/

124
src/fatfs/ffsystem.c Normal file
View File

@ -0,0 +1,124 @@
/*------------------------------------------------------------------------*/
/* Sample Code of OS Dependent Functions for FatFs */
/* (C)ChaN, 2018 */
/*------------------------------------------------------------------------*/
#define _POSIX_THREAD_SAFE_FUNCTIONS
#include <time.h>
#include "ff.h"
#if FF_USE_LFN == 3 /* Dynamic memory allocation */
/*------------------------------------------------------------------------*/
/* Allocate a memory block */
/*------------------------------------------------------------------------*/
void* ff_memalloc ( /* Returns pointer to the allocated memory block (null if not enough core) */
UINT msize /* Number of bytes to allocate */
)
{
return malloc(msize); /* Allocate a new memory block with POSIX API */
}
/*------------------------------------------------------------------------*/
/* Free a memory block */
/*------------------------------------------------------------------------*/
void ff_memfree (
void* mblock /* Pointer to the memory block to free (nothing to do if null) */
)
{
free(mblock); /* Free the memory block with POSIX API */
}
#endif
#if FF_FS_REENTRANT /* Mutal exclusion */
/*------------------------------------------------------------------------*/
/* Create a Synchronization Object */
/*------------------------------------------------------------------------*/
/* This function is called in f_mount() function to create a new
/ synchronization object for the volume, such as semaphore and mutex.
/ When a 0 is returned, the f_mount() function fails with FR_INT_ERR.
*/
//const osMutexDef_t Mutex[FF_VOLUMES]; /* Table of CMSIS-RTOS mutex */
int ff_cre_syncobj ( /* 1:Function succeeded, 0:Could not create the sync object */
BYTE vol, /* Corresponding volume (logical drive number) */
FF_SYNC_t* sobj /* Pointer to return the created sync object */
)
{
}
/*------------------------------------------------------------------------*/
/* Delete a Synchronization Object */
/*------------------------------------------------------------------------*/
/* This function is called in f_mount() function to delete a synchronization
/ object that created with ff_cre_syncobj() function. When a 0 is returned,
/ the f_mount() function fails with FR_INT_ERR.
*/
int ff_del_syncobj ( /* 1:Function succeeded, 0:Could not delete due to an error */
FF_SYNC_t sobj /* Sync object tied to the logical drive to be deleted */
)
{
}
/*------------------------------------------------------------------------*/
/* Request Grant to Access the Volume */
/*------------------------------------------------------------------------*/
/* This function is called on entering file functions to lock the volume.
/ When a 0 is returned, the file function fails with FR_TIMEOUT.
*/
int ff_req_grant ( /* 1:Got a grant to access the volume, 0:Could not get a grant */
FF_SYNC_t sobj /* Sync object to wait */
)
{
}
/*------------------------------------------------------------------------*/
/* Release Grant to Access the Volume */
/*------------------------------------------------------------------------*/
/* This function is called on leaving file functions to unlock the volume.
*/
void ff_rel_grant (
FF_SYNC_t sobj /* Sync object to be signaled */
)
{
}
#endif
DWORD get_fattime()
{
// TODO: return melonDS time instead of RTC??
time_t timestamp = time(NULL);
struct tm timedata;
localtime_r(&timestamp, &timedata);
DWORD ret;
ret = (timedata.tm_sec >> 1);
ret |= (timedata.tm_min << 5);
ret |= (timedata.tm_hour << 11);
ret |= (timedata.tm_mday << 16);
ret |= ((timedata.tm_mon + 1) << 21);
ret |= ((timedata.tm_year - 80) << 25);
return ret;
}

15593
src/fatfs/ffunicode.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -21,6 +21,8 @@
#include "types.h"
#include <vector>
namespace Frontend
{
@ -84,6 +86,9 @@ int LoadROM(const u8 *romdata, u32 romlength, const char *archivefilename, const
// simulating ejection of the cartridge
void UnloadROM(int slot);
void ROMIcon(u8 (&data)[512], u16 (&palette)[16], u32* iconRef);
void AnimatedROMIcon(u8 (&data)[8][512], u16 (&palette)[8][16], u16 (&sequence)[64], u32 (&animatedTexRef)[32 * 32 * 64], std::vector<int> &animatedSequenceRef);
// reset execution of the current ROM
int Reset();
@ -151,7 +156,7 @@ int GetScreenTransforms(float* out, int* kind);
// de-transform the provided host display coordinates to get coordinates
// on the bottom screen
bool GetTouchCoords(int& x, int& y);
bool GetTouchCoords(int& x, int& y, bool clamp);
// initialize the audio utility

View File

@ -26,6 +26,17 @@ extern int ConsoleType;
extern int DirectBoot;
extern int SavestateRelocSRAM;
extern int ExternalBIOSEnable;
extern char BIOS9Path[1024];
extern char BIOS7Path[1024];
extern char FirmwarePath[1024];
extern char DSiBIOS9Path[1024];
extern char DSiBIOS7Path[1024];
extern char DSiFirmwarePath[1024];
extern char DSiNANDPath[1024];
}
#endif

View File

@ -19,11 +19,12 @@
#include <stdio.h>
#include <string.h>
#include <utility>
#ifdef ARCHIVE_SUPPORT_ENABLED
#include "ArchiveUtil.h"
#endif
#include "FrontendUtil.h"
#include "Config.h"
#include "SharedConfig.h"
#include "Platform.h"
@ -90,6 +91,8 @@ int VerifyDSBIOS()
FILE* f;
long len;
if (!Config::ExternalBIOSEnable) return Load_OK;
f = Platform::OpenLocalFile(Config::BIOS9Path, "rb");
if (!f) return Load_BIOS9Missing;
@ -160,8 +163,10 @@ int VerifyDSFirmware()
FILE* f;
long len;
if (!Config::ExternalBIOSEnable) return Load_FirmwareNotBootable;
f = Platform::OpenLocalFile(Config::FirmwarePath, "rb");
if (!f) return Load_FirmwareMissing;
if (!f) return Load_FirmwareNotBootable;
fseek(f, 0, SEEK_END);
len = ftell(f);
@ -218,15 +223,6 @@ int SetupDSiNAND()
DSi::SDMMCFile = f;
if (Config::DSiSDEnable)
{
f = Platform::OpenLocalFile(Config::DSiSDPath, "r+b");
if (f)
DSi::SDIOFile = f;
else
DSi::SDIOFile = Platform::OpenLocalFile(Config::DSiSDPath, "w+b");
}
return Load_OK;
}
@ -461,6 +457,72 @@ int LoadROM(const char* file, int slot)
}
}
void ROMIcon(u8 (&data)[512], u16 (&palette)[16], u32* iconRef)
{
int index = 0;
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
for (int k = 0; k < 8; k++)
{
for (int l = 0; l < 8; l++)
{
u8 pal_index = index % 2 ? data[index/2] >> 4 : data[index/2] & 0x0F;
u8 r = ((palette[pal_index] >> 0) & 0x1F) * 255 / 31;
u8 g = ((palette[pal_index] >> 5) & 0x1F) * 255 / 31;
u8 b = ((palette[pal_index] >> 10) & 0x1F) * 255 / 31;
u8 a = pal_index ? 255: 0;
u32* row = &iconRef[256 * i + 32 * k + 8 * j];
row[l] = (a << 24) | (r << 16) | (g << 8) | b;
index++;
}
}
}
}
}
#define SEQ_FLIPV(i) ((i & 0b1000000000000000) >> 15)
#define SEQ_FLIPH(i) ((i & 0b0100000000000000) >> 14)
#define SEQ_PAL(i) ((i & 0b0011100000000000) >> 11)
#define SEQ_BMP(i) ((i & 0b0000011100000000) >> 8)
#define SEQ_DUR(i) ((i & 0b0000000011111111) >> 0)
void AnimatedROMIcon(u8 (&data)[8][512], u16 (&palette)[8][16], u16 (&sequence)[64], u32 (&animatedTexRef)[32 * 32 * 64], std::vector<int> &animatedSequenceRef)
{
for (int i = 0; i < 64; i++)
{
if (!sequence[i])
break;
u32* frame = &animatedTexRef[32 * 32 * i];
ROMIcon(data[SEQ_BMP(sequence[i])], palette[SEQ_PAL(sequence[i])], frame);
if (SEQ_FLIPH(sequence[i]))
{
for (int x = 0; x < 32; x++)
{
for (int y = 0; y < 32/2; y++)
{
std::swap(frame[x * 32 + y], frame[x * 32 + (32 - 1 - y)]);
}
}
}
if (SEQ_FLIPV(sequence[i]))
{
for (int x = 0; x < 32/2; x++)
{
for (int y = 0; y < 32; y++)
{
std::swap(frame[x * 32 + y], frame[(32 - 1 - x) * 32 + y]);
}
}
}
for (int j = 0; j < SEQ_DUR(sequence[i]); j++)
animatedSequenceRef.push_back(i);
}
}
void UnloadROM(int slot)
{
if (slot == ROMSlot_NDS)

View File

@ -37,6 +37,7 @@ bool TopEnable;
bool BotEnable;
bool HybEnable;
int HybScreen;
int HybPrevTouchScreen; // 0:unknown, 1:buttom screen, 2:hybrid screen
void M23_Identity(float* m)
{
@ -138,6 +139,7 @@ void SetupScreenLayout(int screenWidth, int screenHeight,
HybScreen = swapScreens ? 1 : 0;
swapScreens = false;
topAspect = botAspect = 1;
HybPrevTouchScreen = 0;
}
float refpoints[6][2] =
@ -467,34 +469,82 @@ int GetScreenTransforms(float* out, int* kind)
return num;
}
bool GetTouchCoords(int& x, int& y)
bool GetTouchCoords(int& x, int& y, bool clamp)
{
if (BotEnable)
if (HybEnable && HybScreen == 1)
{
float vx = x;
float vy = y;
float hvx = x;
float hvy = y;
M23_Transform(TouchMtx, vx, vy);
M23_Transform(HybTouchMtx, hvx, hvy);
if (clamp)
{
if (HybPrevTouchScreen == 1)
{
x = std::clamp((int)vx, 0, 255);
y = std::clamp((int)vy, 0, 191);
return true;
}
if (HybPrevTouchScreen == 2)
{
x = std::clamp((int)hvx, 0, 255);
y = std::clamp((int)hvy, 0, 191);
return true;
}
}
else
{
if (vx >= 0 && vx < 256 && vy >= 0 && vy < 192)
{
HybPrevTouchScreen = 1;
x = (int)vx;
y = (int)vy;
return true;
}
if (hvx >= 0 && hvx < 256 && hvy >= 0 && hvy < 192)
{
HybPrevTouchScreen = 2;
x = (int)hvx;
y = (int)hvy;
return true;
}
}
}
else if (BotEnable)
{
float vx = x;
float vy = y;
M23_Transform(TouchMtx, vx, vy);
x = (int)vx;
y = (int)vy;
if (clamp)
{
x = std::clamp((int)vx, 0, 255);
y = std::clamp((int)vy, 0, 191);
if (vx >= 0 && vx < 256 && vy >= 0 && vy < 192)
return true;
}
else if (HybEnable && HybScreen == 1)
else
{
if (vx >= 0 && vx < 256 && vy >= 0 && vy < 192)
{
float vx = x;
float vy = y;
M23_Transform(HybTouchMtx, vx, vy);
x = (int)vx;
y = (int)vy;
if (x >= 0 && x < 256 && y >= 0 && y < 192)
return true;
}
}
}
return false;
}

Some files were not shown because too many files have changed in this diff Show More