Merge branch 'master' into feature/optional-libslirp
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -9,7 +9,9 @@ melon_grc.h
|
|||
melon.rc
|
||||
cmake-build
|
||||
cmake-build-debug
|
||||
compile_commands.json
|
||||
.idea
|
||||
.cache
|
||||
|
||||
*.exe
|
||||
|
||||
|
|
|
@ -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,11 +123,20 @@ if (ENABLE_LTO)
|
|||
if (NOT LLD STREQUAL "LLD-NOTFOUND")
|
||||
add_link_options(-fuse-ld=lld)
|
||||
endif()
|
||||
set(CMAKE_AR "llvm-ar")
|
||||
set(CMAKE_RANLIB "llvm-ranlib")
|
||||
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)
|
||||
|
||||
add_subdirectory(src)
|
||||
|
|
|
@ -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)!
|
15
README.md
|
@ -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`
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
find_path(VTUNE_PATH "")
|
||||
|
||||
include_directories("${VTUNE_PATH}/include")
|
||||
link_directories("${VTUNE_PATH}/lib64")
|
|
@ -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)
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
|
@ -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>
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 730 B After Width: | Height: | Size: 730 B |
Before Width: | Height: | Size: 146 KiB After Width: | Height: | Size: 146 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 121 KiB |
|
@ -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>
|
|
@ -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"
|
|
@ -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
|
132
src/ARM.cpp
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
167
src/ARMJIT.cpp
|
@ -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();
|
||||
|
@ -384,7 +392,7 @@ bool DecodeLiteral(bool thumb, const FetchedInstr& instr, u32& addr)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool DecodeBranch(bool thumb, const FetchedInstr& instr, u32& cond, bool hasLink, u32 lr, bool& link,
|
||||
bool DecodeBranch(bool thumb, const FetchedInstr& instr, u32& cond, bool hasLink, u32 lr, bool& link,
|
||||
u32& linkAddr, u32& targetAddr)
|
||||
{
|
||||
if (thumb)
|
||||
|
@ -427,7 +435,7 @@ bool DecodeBranch(bool thumb, const FetchedInstr& instr, u32& cond, bool hasLink
|
|||
linkAddr = instr.Addr + 4;
|
||||
|
||||
cond = instr.Cond();
|
||||
if (instr.Info.Kind == ARMInstrInfo::ak_BL
|
||||
if (instr.Info.Kind == ARMInstrInfo::ak_BL
|
||||
|| instr.Info.Kind == ARMInstrInfo::ak_B)
|
||||
{
|
||||
s32 offset = (s32)(instr.Instr << 8) >> 6;
|
||||
|
@ -468,7 +476,7 @@ bool IsIdleLoop(bool thumb, FetchedInstr* instrs, int instrsCount)
|
|||
u16 dstRegs = instrs[i].Info.DstRegs & ~(1 << 15);
|
||||
|
||||
regsDisallowedToWrite |= srcRegs & ~regsWrittenTo;
|
||||
|
||||
|
||||
if (dstRegs & regsDisallowedToWrite)
|
||||
return false;
|
||||
regsWrittenTo |= dstRegs;
|
||||
|
@ -558,7 +566,7 @@ InterpreterFunc InterpretTHUMB[ARMInstrInfo::tk_Count] =
|
|||
F(LDRB_IMM), F(STRH_IMM), F(LDRH_IMM), F(STR_SPREL), F(LDR_SPREL),
|
||||
F(PUSH), F(POP), F(LDMIA), F(STMIA),
|
||||
F(BCOND), F(BX), F(BLX_REG), F(B), F(BL_LONG_1), F(BL_LONG_2),
|
||||
F(UNK), F(SVC),
|
||||
F(UNK), F(SVC),
|
||||
T_BL_LONG // BL_LONG psudo opcode
|
||||
};
|
||||
#undef F
|
||||
|
@ -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);
|
||||
|
@ -614,27 +617,30 @@ void CompileBlock(ARM* cpu)
|
|||
}
|
||||
|
||||
// some memory has been remapped
|
||||
RetireJitBlock(existingBlockIt->second);
|
||||
RetireJitBlock(existingBlockIt->second);
|
||||
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,21 +766,25 @@ void CompileBlock(ARM* cpu)
|
|||
{
|
||||
printf("literal in non executable memory?\n");
|
||||
}
|
||||
u32 translatedAddrRounded = translatedAddr & ~0x1FF;
|
||||
if (InvalidLiterals.Find(translatedAddr) == -1)
|
||||
{
|
||||
u32 translatedAddrRounded = translatedAddr & ~0x1FF;
|
||||
|
||||
u32 j = 0;
|
||||
for (; j < numAddressRanges; j++)
|
||||
if (addressRanges[j] == translatedAddrRounded)
|
||||
break;
|
||||
if (j == numAddressRanges)
|
||||
addressRanges[numAddressRanges++] = translatedAddrRounded;
|
||||
addressMasks[j] |= 1 << ((translatedAddr & 0x1FF) / 16);
|
||||
JIT_DEBUGPRINT("literal loading %08x %08x %08x %08x\n", literalAddr, translatedAddr, addressMasks[j], addressRanges[j]);
|
||||
cpu->DataRead32(literalAddr, &literalValues[numLiterals]);
|
||||
literalLoadAddrs[numLiterals++] = translatedAddr;
|
||||
u32 j = 0;
|
||||
for (; j < numAddressRanges; j++)
|
||||
if (addressRanges[j] == translatedAddrRounded)
|
||||
break;
|
||||
if (j == numAddressRanges)
|
||||
addressRanges[numAddressRanges++] = translatedAddrRounded;
|
||||
addressMasks[j] |= 1 << ((translatedAddr & 0x1FF) / 16);
|
||||
JIT_DEBUGPRINT("literal loading %08x %08x %08x %08x\n", literalAddr, translatedAddr, addressMasks[j], addressRanges[j]);
|
||||
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,14 +833,14 @@ 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)
|
||||
{
|
||||
lr = linkAddr;
|
||||
hasLink = true;
|
||||
}
|
||||
|
||||
|
||||
r15 = target + (thumb ? 2 : 4);
|
||||
assert(r15 == cpu->R[15]);
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
13
src/ARMJIT.h
|
@ -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,8 +57,10 @@ 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);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -324,16 +324,29 @@ void Compiler::Comp_Arithmetic(int op, bool S, ARM64Reg rd, ARM64Reg rn, Op2 op2
|
|||
UBFX(W2, RCPSR, 29, 1);
|
||||
if (S)
|
||||
{
|
||||
CVInGPR = true;
|
||||
ADDS(W1, rn, W2);
|
||||
CSET(W2, CC_CS);
|
||||
CSET(W3, CC_VS);
|
||||
if (op2.IsImm)
|
||||
ADDSI2R(rd, W1, op2.Imm, W0);
|
||||
{
|
||||
CVInGPR = true;
|
||||
ADDS(W1, rn, W2);
|
||||
CSET(W2, CC_CS);
|
||||
CSET(W3, CC_VS);
|
||||
if (op2.IsImm)
|
||||
ADDSI2R(rd, W1, op2.Imm, W0);
|
||||
else
|
||||
ADDS(rd, W1, op2.Reg.Rm, op2.ToArithOption());
|
||||
CSINC(W2, W2, WZR, CC_CC);
|
||||
CSINC(W3, W3, WZR, CC_VC);
|
||||
}
|
||||
else
|
||||
ADDS(rd, W1, op2.Reg.Rm, op2.ToArithOption());
|
||||
CSINC(W2, W2, WZR, CC_CC);
|
||||
CSINC(W3, W3, WZR, CC_VC);
|
||||
{
|
||||
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
|
||||
{
|
||||
|
@ -346,25 +359,38 @@ void Compiler::Comp_Arithmetic(int op, bool S, ARM64Reg rd, ARM64Reg rn, Op2 op2
|
|||
break;
|
||||
case 0x6: // SBC
|
||||
UBFX(W2, RCPSR, 29, 1);
|
||||
// W1 = -op2 - 1
|
||||
if (op2.IsImm)
|
||||
MOVI2R(W1, ~op2.Imm);
|
||||
else
|
||||
ORN(W1, WZR, op2.Reg.Rm, op2.ToArithOption());
|
||||
if (S)
|
||||
if (S && !op2.IsImm)
|
||||
{
|
||||
CVInGPR = true;
|
||||
ADDS(W1, W2, W1);
|
||||
CSET(W2, CC_CS);
|
||||
CSET(W3, CC_VS);
|
||||
ADDS(rd, rn, W1);
|
||||
CSINC(W2, W2, WZR, CC_CC);
|
||||
CSINC(W3, W3, WZR, CC_VC);
|
||||
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
|
||||
{
|
||||
ADD(W1, W2, W1);
|
||||
ADD(rd, rn, W1);
|
||||
// W1 = -op2 - 1
|
||||
if (op2.IsImm)
|
||||
MOVI2R(W1, ~op2.Imm);
|
||||
else
|
||||
ORN(W1, WZR, op2.Reg.Rm, op2.ToArithOption());
|
||||
if (S)
|
||||
{
|
||||
CVInGPR = true;
|
||||
ADDS(W1, W2, W1);
|
||||
CSET(W2, CC_CS);
|
||||
CSET(W3, CC_VS);
|
||||
ADDS(rd, rn, W1);
|
||||
CSINC(W2, W2, WZR, CC_CC);
|
||||
CSINC(W3, W3, WZR, CC_VC);
|
||||
}
|
||||
else
|
||||
{
|
||||
ADD(W1, W2, W1);
|
||||
ADD(rd, rn, W1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x7: // RSC
|
||||
|
@ -533,21 +559,7 @@ void Compiler::A_Comp_ALUMovOp()
|
|||
}
|
||||
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());
|
||||
}
|
||||
MOV(rd, op2.Reg.Rm, op2.ToArithOption());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,44 +164,55 @@ 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)
|
||||
BitSet32 hiRegsLoaded(RegCache.LoadedRegs & 0x7F00);
|
||||
for (int reg : hiRegsLoaded)
|
||||
{
|
||||
BitSet16 hiRegsLoaded(RegCache.LoadedRegs & 0x7F00);
|
||||
for (int reg : hiRegsLoaded)
|
||||
if (Thumb || CurInstr.Cond() == 0xE)
|
||||
RegCache.UnloadRegister(reg);
|
||||
else
|
||||
SaveReg(reg, RegCache.Mapping[reg]);
|
||||
// prevent saving the register twice
|
||||
loadedRegs[reg] = false;
|
||||
}
|
||||
else
|
||||
}
|
||||
|
||||
for (int reg : loadedRegs)
|
||||
{
|
||||
if (CallerSavedPushRegs[RegCache.Mapping[reg]]
|
||||
&& (saveRegsToBeChanged || !((1<<reg) & CurInstr.Info.DstRegs && !((1<<reg) & CurInstr.Info.SrcRegs))))
|
||||
{
|
||||
BitSet16 hiRegsDirty(RegCache.LoadedRegs & 0x7F00);
|
||||
for (int reg : hiRegsDirty)
|
||||
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]);
|
||||
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();
|
||||
|
||||
ADD(RCycles, RCycles, ConstantCycles);
|
||||
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,7 +832,8 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
|
|||
|
||||
RegCache.Flush();
|
||||
|
||||
ADD(RCycles, RCycles, ConstantCycles);
|
||||
if (ConstantCycles)
|
||||
ADD(RCycles, RCycles, ConstantCycles);
|
||||
QuickTailCall(X0, ARM_Ret);
|
||||
|
||||
FlushIcache();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
SaveReg(reg, first);
|
||||
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))
|
||||
first = MapReg(reg);
|
||||
{
|
||||
if (!(reg == rn && skipLoadingRn))
|
||||
first = MapReg(reg);
|
||||
}
|
||||
else if (store)
|
||||
{
|
||||
LoadReg(reg, first);
|
||||
}
|
||||
if (RegCache.LoadedRegs & (1 << nextReg))
|
||||
second = MapReg(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,20 +719,23 @@ 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);
|
||||
FixupBranch alreadyWritten = CBNZ(W4);
|
||||
if (RegCache.LoadedRegs & (1 << reg))
|
||||
MOV(MapReg(reg), W3);
|
||||
else
|
||||
SaveReg(reg, W3);
|
||||
SetJumpTarget(alreadyWritten);
|
||||
if (!(reg == rn && skipLoadingRn))
|
||||
{
|
||||
FixupBranch alreadyWritten = CBNZ(W4);
|
||||
if (RegCache.LoadedRegs & (1 << reg))
|
||||
MOV(MapReg(reg), W3);
|
||||
else
|
||||
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);
|
||||
|
@ -726,8 +750,11 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
|
|||
}
|
||||
else if (RegCache.LoadedRegs & (1 << reg))
|
||||
{
|
||||
ARM64Reg mapped = MapReg(reg);
|
||||
LDR(INDEX_UNSIGNED, mapped, SP, i * 8);
|
||||
if (!(reg == rn && skipLoadingRn))
|
||||
{
|
||||
ARM64Reg mapped = MapReg(reg);
|
||||
LDR(INDEX_UNSIGNED, mapped, SP, i * 8);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -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,12 +826,15 @@ 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 > 0)
|
||||
if (offset)
|
||||
{
|
||||
if (offset > 0)
|
||||
ADD(sp, sp, offset);
|
||||
else
|
||||
SUB(sp, sp, -offset);
|
||||
}
|
||||
}
|
||||
|
||||
void Compiler::T_Comp_LDMIA_STMIA()
|
||||
|
@ -813,10 +843,12 @@ void Compiler::T_Comp_LDMIA_STMIA()
|
|||
ARM64Reg rb = MapReg(CurInstr.T_Reg(8));
|
||||
bool load = CurInstr.Instr & (1 << 11);
|
||||
u32 regsCount = regs.Count();
|
||||
|
||||
s32 offset = Comp_MemAccessBlock(CurInstr.T_Reg(8), regs, !load, false, false, false);
|
||||
|
||||
if (!load || !regs[CurInstr.T_Reg(8)])
|
||||
bool writeback = !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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
SaveReg(reg, RegCache.Mapping[reg]);
|
||||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -612,11 +637,11 @@ const Compiler::CompileFunc T_Comp[ARMInstrInfo::tk_Count] = {
|
|||
// LDR/STR sp rel
|
||||
F(T_Comp_MemSPRel), F(T_Comp_MemSPRel),
|
||||
// PUSH/POP
|
||||
F(T_Comp_PUSH_POP), F(T_Comp_PUSH_POP),
|
||||
F(T_Comp_PUSH_POP), F(T_Comp_PUSH_POP),
|
||||
// LDMIA, STMIA
|
||||
F(T_Comp_LDMIA_STMIA), F(T_Comp_LDMIA_STMIA),
|
||||
F(T_Comp_LDMIA_STMIA), F(T_Comp_LDMIA_STMIA),
|
||||
// Branch
|
||||
F(T_Comp_BCOND), F(T_Comp_BranchXchangeReg), F(T_Comp_BranchXchangeReg), F(T_Comp_B), F(T_Comp_BL_LONG_1), F(T_Comp_BL_LONG_2),
|
||||
F(T_Comp_BCOND), F(T_Comp_BranchXchangeReg), F(T_Comp_BranchXchangeReg), F(T_Comp_B), F(T_Comp_BL_LONG_1), F(T_Comp_BL_LONG_2),
|
||||
// Unk, SVC
|
||||
NULL, NULL,
|
||||
F(T_Comp_BL_Merged)
|
||||
|
@ -654,12 +679,35 @@ void Compiler::Comp_SpecialBranchBehaviour(bool taken)
|
|||
{
|
||||
RegCache.PrepareExit();
|
||||
|
||||
ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm32(ConstantCycles));
|
||||
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...
|
||||
{
|
||||
|
@ -783,7 +831,7 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
|
|||
SetJumpTarget(skipExecute);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -793,9 +841,14 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
|
|||
|
||||
RegCache.Flush();
|
||||
|
||||
ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm32(ConstantCycles));
|
||||
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);
|
||||
|
@ -834,7 +887,7 @@ void Compiler::Comp_AddCycles_CI(Gen::X64Reg i, int add)
|
|||
s32 cycles = Num ?
|
||||
NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]
|
||||
: ((R15 & 0x2) ? 0 : CurInstr.CodeCycles);
|
||||
|
||||
|
||||
if (!Thumb && CurInstr.Cond() < 0xE)
|
||||
{
|
||||
LEA(32, RSCRATCH, MDisp(i, add + cycles));
|
||||
|
@ -879,7 +932,7 @@ void Compiler::Comp_AddCycles_CDI()
|
|||
{
|
||||
cycles = numC + numD + 1;
|
||||
}
|
||||
|
||||
|
||||
if (!Thumb && CurInstr.Cond() < 0xE)
|
||||
ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm8(cycles));
|
||||
else
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -107,7 +104,7 @@ bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr)
|
|||
|
||||
if (Thumb || CurInstr.Cond() == 0xE)
|
||||
RegCache.PutLiteral(rd, val);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -120,10 +117,10 @@ 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);
|
||||
|
||||
|
||||
if (Comp_MemLoadLiteral(size, flags & memop_SignExtend, rd, addr))
|
||||
return;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -173,7 +170,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
|
|||
if (!(flags & memop_SubtractOffset) && rm.IsSimpleReg() && rnMapped.IsSimpleReg()
|
||||
&& op2.Reg.Op == 0 && op2.Reg.Amount > 0 && op2.Reg.Amount <= 3)
|
||||
{
|
||||
LEA(32, finalAddr,
|
||||
LEA(32, finalAddr,
|
||||
MComplex(rnMapped.GetSimpleReg(), rm.GetSimpleReg(), 1 << op2.Reg.Amount, 0));
|
||||
}
|
||||
else
|
||||
|
@ -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,8 +367,8 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
|
|||
}
|
||||
}
|
||||
|
||||
PopRegs(false);
|
||||
|
||||
PopRegs(false, false);
|
||||
|
||||
if (!(flags & memop_Store))
|
||||
{
|
||||
if (flags & memop_SignExtend)
|
||||
|
@ -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))
|
||||
{
|
||||
MOV(32, MapReg(reg), mem);
|
||||
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,12 +549,15 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
|
|||
MOV(32, R(RSCRATCH2), Imm32(reg - 8));
|
||||
POP(RSCRATCH3);
|
||||
CALL(WriteBanked);
|
||||
FixupBranch sucessfulWritten = J_CC(CC_NC);
|
||||
if (RegCache.LoadedRegs & (1 << reg))
|
||||
MOV(32, R(RegCache.Mapping[reg]), R(RSCRATCH3));
|
||||
else
|
||||
SaveReg(reg, RSCRATCH3);
|
||||
SetJumpTarget(sucessfulWritten);
|
||||
if (!(reg == rn && skipLoadingRn))
|
||||
{
|
||||
FixupBranch sucessfulWritten = J_CC(CC_NC);
|
||||
if (RegCache.LoadedRegs & (1 << reg))
|
||||
MOV(32, R(RegCache.Mapping[reg]), R(RSCRATCH3));
|
||||
else
|
||||
SaveReg(reg, RSCRATCH3);
|
||||
SetJumpTarget(sucessfulWritten);
|
||||
}
|
||||
}
|
||||
else if (!(RegCache.LoadedRegs & (1 << reg)))
|
||||
{
|
||||
|
@ -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,14 +614,14 @@ 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)
|
||||
LEA(64, ABI_PARAM2, MDisp(RSP, allocOffset));
|
||||
else
|
||||
MOV(64, R(ABI_PARAM2), R(RSP));
|
||||
|
||||
|
||||
MOV(32, R(ABI_PARAM3), Imm32(regsCount));
|
||||
if (Num == 0)
|
||||
MOV(64, R(ABI_PARAM4), R(RCPU));
|
||||
|
@ -627,8 +635,8 @@ 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)
|
||||
|
@ -658,7 +666,7 @@ void Compiler::A_Comp_MemWB()
|
|||
bool load = CurInstr.Instr & (1 << 20);
|
||||
bool byte = CurInstr.Instr & (1 << 22);
|
||||
int size = byte ? 8 : 32;
|
||||
|
||||
|
||||
int flags = 0;
|
||||
if (!load)
|
||||
flags |= memop_Store;
|
||||
|
@ -732,7 +740,7 @@ void Compiler::T_Comp_MemReg()
|
|||
bool load = op & 0x2;
|
||||
bool byte = op & 0x1;
|
||||
|
||||
Comp_MemAccess(CurInstr.T_Reg(0), CurInstr.T_Reg(3), Op2(CurInstr.T_Reg(6), 0, 0),
|
||||
Comp_MemAccess(CurInstr.T_Reg(0), CurInstr.T_Reg(3), Op2(CurInstr.T_Reg(6), 0, 0),
|
||||
byte ? 8 : 32, load ? 0 : memop_Store);
|
||||
}
|
||||
|
||||
|
@ -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,9 +833,10 @@ 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);
|
||||
|
||||
ADD(32, sp, Imm8(offset)); // offset will be always be in range since PUSH accesses 9 regs max
|
||||
if (offset)
|
||||
ADD(32, sp, Imm8(offset)); // offset will be always be in range since PUSH accesses 9 regs max
|
||||
}
|
||||
|
||||
void Compiler::T_Comp_LDMIA_STMIA()
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "Config.h"
|
||||
#include "ARMJIT.h"
|
||||
|
||||
namespace ARMInstrInfo
|
||||
{
|
||||
|
@ -230,7 +230,7 @@ enum {
|
|||
T_SetMaybeC = 1 << 17,
|
||||
T_ReadC = 1 << 18,
|
||||
T_SetC = 1 << 19,
|
||||
|
||||
|
||||
T_WriteMem = 1 << 20,
|
||||
T_LoadMem = 1 << 21,
|
||||
};
|
||||
|
@ -345,7 +345,7 @@ Info Decode(bool thumb, u32 num, u32 instr)
|
|||
res.DstRegs |= 1 << (instr & 0x7);
|
||||
if (data & T_Write8)
|
||||
res.DstRegs |= 1 << ((instr >> 8) & 0x7);
|
||||
|
||||
|
||||
if (data & T_ReadHi0)
|
||||
res.SrcRegs |= 1 << ((instr & 0x7) | ((instr >> 4) & 0x8));
|
||||
if (data & T_ReadHi3)
|
||||
|
@ -381,12 +381,12 @@ Info Decode(bool thumb, u32 num, u32 instr)
|
|||
|
||||
if (data & T_WriteMem)
|
||||
res.SpecialKind = special_WriteMem;
|
||||
|
||||
|
||||
if (data & T_LoadMem)
|
||||
{
|
||||
if (res.Kind == tk_LDR_PCREL)
|
||||
{
|
||||
if (!Config::JIT_LiteralOptimisations)
|
||||
if (!ARMJIT::LiteralOptimizations)
|
||||
res.SrcRegs |= 1 << 15;
|
||||
res.SpecialKind = special_LoadLiteral;
|
||||
}
|
||||
|
@ -471,18 +471,18 @@ Info Decode(bool thumb, u32 num, u32 instr)
|
|||
res.SrcRegs |= 1 << ((instr >> 8) & 0xF);
|
||||
if (data & A_Read12)
|
||||
res.SrcRegs |= 1 << ((instr >> 12) & 0xF);
|
||||
|
||||
|
||||
if (data & A_Write12)
|
||||
res.DstRegs |= 1 << ((instr >> 12) & 0xF);
|
||||
if (data & A_Write16)
|
||||
res.DstRegs |= 1 << ((instr >> 16) & 0xF);
|
||||
|
||||
|
||||
if (data & A_MemWriteback && instr & (1 << 21))
|
||||
res.DstRegs |= 1 << ((instr >> 16) & 0xF);
|
||||
|
||||
if (data & A_BranchAlways)
|
||||
res.DstRegs |= 1 << 15;
|
||||
|
||||
|
||||
if (data & A_Read12Double)
|
||||
{
|
||||
res.SrcRegs |= 1 << ((instr >> 12) & 0xF);
|
||||
|
@ -526,16 +526,21 @@ Info Decode(bool thumb, u32 num, u32 instr)
|
|||
if (data & A_LoadMem)
|
||||
{
|
||||
if (res.SrcRegs == (1 << 15))
|
||||
res.SpecialKind = special_LoadLiteral;
|
||||
res.SpecialKind = special_LoadLiteral;
|
||||
else
|
||||
res.SpecialKind = special_LoadMem;
|
||||
}
|
||||
|
||||
|
||||
if (res.Kind == ak_LDM)
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -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()
|
122
src/CP15.cpp
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
187
src/Config.cpp
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
69
src/Config.h
|
@ -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
|
468
src/DMA.cpp
|
@ -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;
|
||||
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));
|
||||
|
|
12
src/DMA.h
|
@ -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
|
||||
|
|
|
@ -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
|
1580
src/DSi.cpp
|
@ -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();
|
||||
|
|
140
src/DSi_AES.cpp
|
@ -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)
|
||||
{
|
||||
|
@ -402,22 +419,34 @@ void CheckOutputDMA()
|
|||
|
||||
void Update()
|
||||
{
|
||||
while (InputFIFO.Level() >= 4 && OutputFIFO.Level() <= 12 && RemBlocks > 0)
|
||||
if (RemExtra > 0)
|
||||
{
|
||||
switch (AESMode)
|
||||
while (InputFIFO.Level() >= 4 && RemExtra > 0)
|
||||
{
|
||||
case 0: ProcessBlock_CCM_Decrypt(); break;
|
||||
case 1: ProcessBlock_CCM_Encrypt(); break;
|
||||
case 2:
|
||||
case 3: ProcessBlock_CTR(); break;
|
||||
ProcessBlock_CCM_Extra();
|
||||
RemExtra--;
|
||||
}
|
||||
}
|
||||
|
||||
RemBlocks--;
|
||||
if (RemExtra == 0)
|
||||
{
|
||||
while (InputFIFO.Level() >= 4 && OutputFIFO.Level() <= 12 && RemBlocks > 0)
|
||||
{
|
||||
switch (AESMode)
|
||||
{
|
||||
case 0: ProcessBlock_CCM_Decrypt(); break;
|
||||
case 1: ProcessBlock_CCM_Encrypt(); break;
|
||||
case 2:
|
||||
case 3: ProcessBlock_CTR(); break;
|
||||
}
|
||||
|
||||
RemBlocks--;
|
||||
}
|
||||
}
|
||||
|
||||
CheckOutputDMA();
|
||||
|
||||
if (RemBlocks == 0)
|
||||
if (RemBlocks == 0 && RemExtra == 0)
|
||||
{
|
||||
if (AESMode == 0)
|
||||
{
|
||||
|
@ -443,7 +472,16 @@ void Update()
|
|||
AES_CTR_xcrypt_buffer(&Ctx, CurMAC, 16);
|
||||
|
||||
Swap16(OutputMAC, CurMAC);
|
||||
OutputMACDue = true;
|
||||
|
||||
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
|
||||
Cnt &= ~(1<<21);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -72,6 +72,8 @@ void Reset()
|
|||
Registers[0x81] = 0x64;
|
||||
}
|
||||
|
||||
u8 GetBootFlag() { return Registers[0x70]; }
|
||||
|
||||
void Start()
|
||||
{
|
||||
//printf("BPTWL: start\n");
|
||||
|
|
|
@ -19,6 +19,13 @@
|
|||
#ifndef DSI_I2C_H
|
||||
#define DSI_I2C_H
|
||||
|
||||
namespace DSi_BPTWL
|
||||
{
|
||||
|
||||
u8 GetBootFlag();
|
||||
|
||||
}
|
||||
|
||||
namespace DSi_I2C
|
||||
{
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
{
|
||||
fseek(File, addr, SEEK_SET);
|
||||
fwrite(data, 1, len, File);
|
||||
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[addr & 0x1FF], 1, len, 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];
|
||||
|
|
|
@ -81,6 +81,11 @@ void DoSavestate(Savestate* file)
|
|||
// TODO!!
|
||||
}
|
||||
|
||||
void SetMode(u8 mode)
|
||||
{
|
||||
TSCMode = mode;
|
||||
}
|
||||
|
||||
void SetTouchCoords(u16 x, u16 y)
|
||||
{
|
||||
if (TSCMode == 0x00)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -2158,7 +2163,7 @@ void ExecuteCommand()
|
|||
VertexSlotCounter = 0;
|
||||
VertexSlotsFree = 1;
|
||||
break;
|
||||
|
||||
|
||||
case 0x60: // viewport x1,y1,x2,y2
|
||||
VertexPipelineCmdDelayed8();
|
||||
// note: viewport Y coordinates are upside-down
|
||||
|
@ -2407,7 +2412,7 @@ void ExecuteCommand()
|
|||
CurVertex[2] = ExecParams[1] & 0xFFFF;
|
||||
PosTest();
|
||||
break;
|
||||
|
||||
|
||||
case 0x70: // box test
|
||||
NumTestCommands -= 3;
|
||||
BoxTest(ExecParams);
|
||||
|
@ -2611,27 +2616,34 @@ u32 ScrolledLine[256];
|
|||
|
||||
u32* GetLine(int line)
|
||||
{
|
||||
u32* rawline = CurrentRenderer->GetLine(line);
|
||||
|
||||
if (RenderXPos == 0) return rawline;
|
||||
|
||||
// apply X scroll
|
||||
|
||||
if (RenderXPos & 0x100)
|
||||
if (!AbortFrame)
|
||||
{
|
||||
int i = 0, j = RenderXPos;
|
||||
for (; j < 512; i++, j++)
|
||||
ScrolledLine[i] = 0;
|
||||
for (j = 0; i < 256; i++, j++)
|
||||
ScrolledLine[i] = rawline[j];
|
||||
u32* rawline = CurrentRenderer->GetLine(line);
|
||||
|
||||
if (RenderXPos == 0) return rawline;
|
||||
|
||||
// apply X scroll
|
||||
|
||||
if (RenderXPos & 0x100)
|
||||
{
|
||||
int i = 0, j = RenderXPos;
|
||||
for (; j < 512; i++, j++)
|
||||
ScrolledLine[i] = 0;
|
||||
for (j = 0; i < 256; i++, j++)
|
||||
ScrolledLine[i] = rawline[j];
|
||||
}
|
||||
else
|
||||
{
|
||||
int i = 0, j = RenderXPos;
|
||||
for (; j < 256; i++, j++)
|
||||
ScrolledLine[i] = rawline[j];
|
||||
for (; i < 256; i++)
|
||||
ScrolledLine[i] = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int i = 0, j = RenderXPos;
|
||||
for (; j < 256; i++, j++)
|
||||
ScrolledLine[i] = rawline[j];
|
||||
for (; i < 256; i++)
|
||||
ScrolledLine[i] = 0;
|
||||
memset(ScrolledLine, 0, 256*4);
|
||||
}
|
||||
|
||||
return ScrolledLine;
|
||||
|
|
|
@ -97,6 +97,8 @@ extern u16 RenderXPos;
|
|||
extern std::array<Polygon*,2048> RenderPolygonRAM;
|
||||
extern u32 RenderNumPolygons;
|
||||
|
||||
extern bool AbortFrame;
|
||||
|
||||
extern u64 Timestamp;
|
||||
|
||||
bool Init();
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#include <string.h>
|
||||
#include "NDS.h"
|
||||
#include "GPU.h"
|
||||
#include "Config.h"
|
||||
#include "GPU3D_OpenGL_shaders.h"
|
||||
|
||||
namespace GPU3D
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
|
||||
#include "NDS.h"
|
||||
#include "GPU.h"
|
||||
#include "Config.h"
|
||||
#include "OpenGLSupport.h"
|
||||
#include "GPU_OpenGL_shaders.h"
|
||||
|
||||
|
|
522
src/NDS.cpp
|
@ -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,89 +357,107 @@ 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]);
|
||||
|
||||
MapSharedWRAM(3);
|
||||
|
||||
u32 arm9start = 0;
|
||||
|
||||
// load the ARM9 secure area
|
||||
if (bootparams[0] >= 0x4000 && bootparams[0] < 0x8000)
|
||||
else
|
||||
{
|
||||
u8 securearea[0x800];
|
||||
NDSCart::DecryptSecureArea(securearea);
|
||||
MapSharedWRAM(3);
|
||||
|
||||
for (u32 i = 0; i < 0x800; i+=4)
|
||||
u32 arm9start = 0;
|
||||
|
||||
// load the ARM9 secure area
|
||||
if (NDSCart::Header.ARM9ROMOffset >= 0x4000 && NDSCart::Header.ARM9ROMOffset < 0x8000)
|
||||
{
|
||||
ARM9Write32(bootparams[2]+i, *(u32*)&securearea[i]);
|
||||
arm9start += 4;
|
||||
u8 securearea[0x800];
|
||||
NDSCart::DecryptSecureArea(securearea);
|
||||
|
||||
for (u32 i = 0; i < 0x800; i+=4)
|
||||
{
|
||||
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 < NDSCart::Header.ARM9Size; i+=4)
|
||||
{
|
||||
u32 tmp = *(u32*)&NDSCart::CartROM[NDSCart::Header.ARM9ROMOffset+i];
|
||||
ARM9Write32(NDSCart::Header.ARM9RAMAddress+i, tmp);
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < NDSCart::Header.ARM7Size; i+=4)
|
||||
{
|
||||
u32 tmp = *(u32*)&NDSCart::CartROM[NDSCart::Header.ARM7ROMOffset+i];
|
||||
ARM7Write32(NDSCart::Header.ARM7RAMAddress+i, tmp);
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < 0x170; i+=4)
|
||||
{
|
||||
u32 tmp = *(u32*)&NDSCart::CartROM[i];
|
||||
ARM9Write32(0x027FFE00+i, tmp);
|
||||
}
|
||||
|
||||
ARM9Write32(0x027FF800, NDSCart::CartID);
|
||||
ARM9Write32(0x027FF804, NDSCart::CartID);
|
||||
ARM9Write16(0x027FF808, NDSCart::Header.HeaderCRC16);
|
||||
ARM9Write16(0x027FF80A, NDSCart::Header.SecureAreaCRC16);
|
||||
|
||||
ARM9Write16(0x027FF850, 0x5835);
|
||||
|
||||
ARM9Write32(0x027FFC00, NDSCart::CartID);
|
||||
ARM9Write32(0x027FFC04, NDSCart::CartID);
|
||||
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);
|
||||
}
|
||||
|
||||
// CHECKME: firmware seems to load this in 0x200 byte chunks
|
||||
|
||||
for (u32 i = arm9start; i < bootparams[3]; i+=4)
|
||||
{
|
||||
u32 tmp = *(u32*)&NDSCart::CartROM[bootparams[0]+i];
|
||||
ARM9Write32(bootparams[2]+i, tmp);
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < bootparams[7]; i+=4)
|
||||
{
|
||||
u32 tmp = *(u32*)&NDSCart::CartROM[bootparams[4]+i];
|
||||
ARM7Write32(bootparams[6]+i, tmp);
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < 0x170; i+=4)
|
||||
{
|
||||
u32 tmp = *(u32*)&NDSCart::CartROM[i];
|
||||
ARM9Write32(0x027FFE00+i, tmp);
|
||||
}
|
||||
|
||||
ARM9Write32(0x027FF800, NDSCart::CartID);
|
||||
ARM9Write32(0x027FF804, NDSCart::CartID);
|
||||
ARM9Write16(0x027FF808, *(u16*)&NDSCart::CartROM[0x15E]);
|
||||
ARM9Write16(0x027FF80A, *(u16*)&NDSCart::CartROM[0x6C]);
|
||||
|
||||
ARM9Write16(0x027FF850, 0x5835);
|
||||
|
||||
ARM9Write32(0x027FFC00, NDSCart::CartID);
|
||||
ARM9Write32(0x027FFC04, NDSCart::CartID);
|
||||
ARM9Write16(0x027FFC08, *(u16*)&NDSCart::CartROM[0x15E]);
|
||||
ARM9Write16(0x027FFC0A, *(u16*)&NDSCart::CartROM[0x6C]);
|
||||
|
||||
ARM9Write16(0x027FFC10, 0x5835);
|
||||
ARM9Write16(0x027FFC30, 0xFFFF);
|
||||
ARM9Write16(0x027FFC40, 0x0001);
|
||||
|
||||
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,38 +493,46 @@ 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 (!f)
|
||||
if (Platform::GetConfigBool(Platform::ExternalBIOSEnable))
|
||||
{
|
||||
printf("ARM9 BIOS not found\n");
|
||||
f = Platform::OpenLocalFile(Platform::GetConfigString(Platform::BIOS9Path), "rb");
|
||||
if (!f)
|
||||
{
|
||||
printf("ARM9 BIOS not found\n");
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
((u32*)ARM9BIOS)[i] = 0xE7FFDEFF;
|
||||
for (i = 0; i < 16; i++)
|
||||
((u32*)ARM9BIOS)[i] = 0xE7FFDEFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
fseek(f, 0, SEEK_SET);
|
||||
fread(ARM9BIOS, 0x1000, 1, f);
|
||||
|
||||
printf("ARM9 BIOS loaded\n");
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
f = Platform::OpenLocalFile(Platform::GetConfigString(Platform::BIOS7Path), "rb");
|
||||
if (!f)
|
||||
{
|
||||
printf("ARM7 BIOS not found\n");
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
((u32*)ARM7BIOS)[i] = 0xE7FFDEFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
fseek(f, 0, SEEK_SET);
|
||||
fread(ARM7BIOS, 0x4000, 1, f);
|
||||
|
||||
printf("ARM7 BIOS loaded\n");
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fseek(f, 0, SEEK_SET);
|
||||
fread(ARM9BIOS, 0x1000, 1, f);
|
||||
|
||||
printf("ARM9 BIOS loaded\n");
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
f = Platform::OpenLocalFile(Config::BIOS7Path, "rb");
|
||||
if (!f)
|
||||
{
|
||||
printf("ARM7 BIOS not found\n");
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
((u32*)ARM7BIOS)[i] = 0xE7FFDEFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
fseek(f, 0, SEEK_SET);
|
||||
fread(ARM7BIOS, 0x4000, 1, f);
|
||||
|
||||
printf("ARM7 BIOS loaded\n");
|
||||
fclose(f);
|
||||
memcpy(ARM9BIOS, bios_arm9_bin, bios_arm9_bin_len);
|
||||
memcpy(ARM7BIOS, bios_arm7_bin, bios_arm7_bin_len);
|
||||
}
|
||||
|
||||
#ifdef JIT_ENABLED
|
||||
|
@ -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:
|
||||
ExMemCnt[0] = val;
|
||||
ExMemCnt[1] = (ExMemCnt[1] & 0x007F) | (val & 0xFF80);
|
||||
SetGBASlotTimings();
|
||||
return;
|
||||
{
|
||||
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:
|
||||
ExMemCnt[1] = (ExMemCnt[1] & 0xFF80) | (val & 0x007F);
|
||||
SetGBASlotTimings();
|
||||
return;
|
||||
{
|
||||
u16 oldVal = ExMemCnt[1];
|
||||
ExMemCnt[1] = (ExMemCnt[1] & 0xFF80) | (val & 0x007F);
|
||||
if ((ExMemCnt[1] ^ oldVal) & 0xFF)
|
||||
SetGBASlotTimings();
|
||||
return;
|
||||
}
|
||||
case 0x04000206:
|
||||
SetWifiWaitCnt(val);
|
||||
return;
|
||||
|
|
38
src/NDS.h
|
@ -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);
|
||||
|
|
171
src/NDSCart.cpp
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
|
|
353
src/SPI.cpp
|
@ -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,21 +260,90 @@ 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
|
||||
{
|
||||
printf("Could not write firmware backup!\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
f = Platform::OpenLocalFile(firmbkp, "wb");
|
||||
fwrite(Firmware, 1, FirmwareLength, f);
|
||||
fclose(f);
|
||||
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,33 +357,47 @@ 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;
|
||||
Firmware[userdata+0x5C] = 0;
|
||||
Firmware[userdata+0x5D] = 0;
|
||||
*(u16*)&Firmware[userdata+0x5E] = 255<<4;
|
||||
*(u16*)&Firmware[userdata+0x60] = 191<<4;
|
||||
Firmware[userdata+0x62] = 255;
|
||||
Firmware[userdata+0x63] = 191;
|
||||
|
||||
// disable autoboot
|
||||
//Firmware[userdata+0x64] &= 0xBF;
|
||||
|
||||
*(u16*)&Firmware[userdata+0x72] = CRC16(&Firmware[userdata], 0x70, 0xFFFF);
|
||||
|
||||
if (firmoverride)
|
||||
{
|
||||
// fix touchscreen coords
|
||||
*(u16*)&Firmware[userdata+0x58] = 0;
|
||||
*(u16*)&Firmware[userdata+0x5A] = 0;
|
||||
Firmware[userdata+0x5C] = 0;
|
||||
Firmware[userdata+0x5D] = 0;
|
||||
*(u16*)&Firmware[userdata+0x5E] = 255<<4;
|
||||
*(u16*)&Firmware[userdata+0x60] = 191<<4;
|
||||
Firmware[userdata+0x62] = 255;
|
||||
Firmware[userdata+0x63] = 191;
|
||||
u8 mac[6];
|
||||
bool rep;
|
||||
|
||||
// disable autoboot
|
||||
//Firmware[userdata+0x64] &= 0xBF;
|
||||
|
||||
*(u16*)&Firmware[userdata+0x72] = CRC16(&Firmware[userdata], 0x70, 0xFFFF);
|
||||
|
||||
if (Config::RandomizeMAC)
|
||||
if (Platform::GetConfigBool(Platform::Firm_RandomizeMAC))
|
||||
{
|
||||
// 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;
|
||||
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,16 +437,30 @@ void DoSavestate(Savestate* file)
|
|||
file->Var32(&Addr);
|
||||
}
|
||||
|
||||
void SetupDirectBoot()
|
||||
void SetupDirectBoot(bool dsi)
|
||||
{
|
||||
NDS::ARM9Write32(0x027FF864, 0);
|
||||
NDS::ARM9Write32(0x027FF868, *(u16*)&Firmware[0x20] << 3);
|
||||
if (dsi)
|
||||
{
|
||||
for (u32 i = 0; i < 6; i += 2)
|
||||
DSi::ARM9Write16(0x02FFFCF4, *(u16*)&Firmware[0x36+i]); // MAC address
|
||||
|
||||
NDS::ARM9Write16(0x027FF874, *(u16*)&Firmware[0x26]);
|
||||
NDS::ARM9Write16(0x027FF876, *(u16*)&Firmware[0x04]);
|
||||
// checkme
|
||||
DSi::ARM9Write16(0x02FFFCFA, *(u16*)&Firmware[0x3C]); // enabled channels
|
||||
|
||||
for (u32 i = 0; i < 0x70; i += 4)
|
||||
NDS::ARM9Write32(0x027FFC80+i, *(u32*)&Firmware[UserSettings+i]);
|
||||
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); // user settings offset
|
||||
|
||||
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]; }
|
||||
|
@ -350,13 +568,16 @@ void Write(u8 val, u32 hold)
|
|||
|
||||
if (!hold && (CurCmd == 0x02 || CurCmd == 0x0A))
|
||||
{
|
||||
FILE* f = Platform::OpenLocalFile(FirmwarePath, "r+b");
|
||||
if (f)
|
||||
if (!FirmwarePath.empty())
|
||||
{
|
||||
u32 cutoff = 0x7FA00 & FirmwareMask;
|
||||
fseek(f, cutoff, SEEK_SET);
|
||||
fwrite(&Firmware[cutoff], FirmwareLength-cutoff, 1, f);
|
||||
fclose(f);
|
||||
FILE* f = Platform::OpenLocalFile(FirmwarePath, "r+b");
|
||||
if (f)
|
||||
{
|
||||
u32 cutoff = 0x7FA00 & FirmwareMask;
|
||||
fseek(f, cutoff, SEEK_SET);
|
||||
fwrite(&Firmware[cutoff], FirmwareLength-cutoff, 1, f);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
namespace SPI_Firmware
|
||||
{
|
||||
|
||||
void SetupDirectBoot();
|
||||
void SetupDirectBoot(bool dsi);
|
||||
|
||||
u32 FixFirmwareLength(u32 originalLength);
|
||||
|
||||
|
|
141
src/SPU.cpp
|
@ -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,12 +285,26 @@ void Channel::FIFO_BufferData()
|
|||
if ((FIFOReadOffset + 16) > totallen)
|
||||
burstlen = totallen - FIFOReadOffset;
|
||||
|
||||
for (u32 i = 0; i < burstlen; i += 4)
|
||||
// sound DMA can't read from the ARM7 BIOS
|
||||
if ((SrcAddr + FIFOReadOffset) >= 0x00004000)
|
||||
{
|
||||
FIFO[FIFOWritePos] = BusRead32(SrcAddr + FIFOReadOffset);
|
||||
FIFOReadOffset += 4;
|
||||
FIFOWritePos++;
|
||||
FIFOWritePos &= 0x7;
|
||||
for (u32 i = 0; i < burstlen; i += 4)
|
||||
{
|
||||
FIFO[FIFOWritePos] = BusRead32(SrcAddr + FIFOReadOffset);
|
||||
FIFOReadOffset += 4;
|
||||
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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include <stdio.h>
|
||||
#include "types.h"
|
||||
|
||||
#define SAVESTATE_MAJOR 8
|
||||
#define SAVESTATE_MAJOR 9
|
||||
#define SAVESTATE_MINOR 0
|
||||
|
||||
class Savestate
|
||||
|
|
|
@ -1607,7 +1607,21 @@ void ARM64XEmitter::BICS(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm, ArithOption Shif
|
|||
|
||||
void ARM64XEmitter::MOV(ARM64Reg Rd, ARM64Reg Rm, ArithOption Shift)
|
||||
{
|
||||
ORR(Rd, Is64Bit(Rd) ? ZR : WZR, Rm, 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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
@ -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.
|
||||
|
|
@ -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.
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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 */
|
|
@ -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 ---*/
|
|
@ -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(×tamp, &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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
#endif
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,33 +469,81 @@ 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)
|
||||
{
|
||||
float vx = x;
|
||||
float vy = y;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (vx >= 0 && vx < 256 && vy >= 0 && vy < 192)
|
||||
{
|
||||
x = (int)vx;
|
||||
y = (int)vy;
|
||||
|
||||
M23_Transform(HybTouchMtx, vx, vy);
|
||||
|
||||
x = (int)vx;
|
||||
y = (int)vy;
|
||||
|
||||
if (x >= 0 && x < 256 && y >= 0 && y < 192)
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|