mirror of
https://github.com/rafaelvcaetano/melonDS-android-lib.git
synced 2024-11-23 13:50:11 +00:00
Merge branch 'master' into feature/zip-support
This commit is contained in:
commit
df190b0400
39
.github/workflows/build-macos.yml
vendored
Normal file
39
.github/workflows/build-macos.yml
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
name: CMake Build (macOS x86-64)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Install dependencies
|
||||
working-directory: ${{runner.workspace}}
|
||||
run: |
|
||||
brew install cmake sdl2 qt5 libslirp
|
||||
- name: Create build environment
|
||||
run: mkdir ${{runner.workspace}}/build
|
||||
- name: Configure
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DQt5_DIR=$(brew --prefix qt5)/lib/cmake/Qt5
|
||||
- name: Make
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: |
|
||||
make -j$(sysctl -n hw.ncpu)
|
||||
mkdir dist
|
||||
cp -r melonDS.app dist
|
||||
- uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: melonDS.app
|
||||
path: ${{runner.workspace}}/build/dist
|
1
.github/workflows/build-ubuntu-aarch64.yml
vendored
1
.github/workflows/build-ubuntu-aarch64.yml
vendored
@ -29,6 +29,7 @@ jobs:
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}
|
||||
run: |
|
||||
sudo rm -f /etc/apt/sources.list.d/kubernetes.list
|
||||
sudo dpkg --add-architecture arm64
|
||||
sudo sh -c "sed \"s|^deb \([a-z\.:/]*\) \([a-z\-]*\) \(.*\)$|deb [arch=amd64] \1 \2 \3\ndeb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports \2 \3|\" /etc/apt/sources.list > /etc/apt/sources.list.new"
|
||||
sudo rm /etc/apt/sources.list
|
||||
|
24
.github/workflows/build-ubuntu.yml
vendored
24
.github/workflows/build-ubuntu.yml
vendored
@ -8,10 +8,6 @@ on:
|
||||
branches:
|
||||
- master
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
CMAKE_VERSION: 3.15.2
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
@ -20,25 +16,21 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}
|
||||
run: | # Fetch a new version of CMake, because the default is too old.
|
||||
sudo rm -f /etc/apt/sources.list.d/dotnetdev.list /etc/apt/sources.list.d/microsoft-prod.list \
|
||||
&& sudo apt update \
|
||||
&& sudo apt install cmake libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qt5-default libslirp0=4.1.0-2ubuntu2.1 libslirp-dev libarchive-dev --allow-downgrades
|
||||
run: |
|
||||
sudo rm -f /etc/apt/sources.list.d/dotnetdev.list /etc/apt/sources.list.d/microsoft-prod.list
|
||||
sudo apt update
|
||||
sudo apt install cmake libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qt5-default libslirp0 libslirp-dev libarchive-dev --allow-downgrades
|
||||
- name: Create build environment
|
||||
run: mkdir ${{runner.workspace}}/build
|
||||
- name: Configure
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE
|
||||
run: cmake $GITHUB_WORKSPACE
|
||||
- name: Make
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: |
|
||||
make -j$(nproc --all) \
|
||||
&& mkdir dist \
|
||||
&& cp melonDS dist
|
||||
make -j$(nproc --all)
|
||||
mkdir dist
|
||||
cp melonDS dist
|
||||
- uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: melonDS
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -11,3 +11,5 @@ cmake-build-debug
|
||||
.idea
|
||||
|
||||
*.exe
|
||||
|
||||
.DS_Store
|
||||
|
@ -1,20 +1,30 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
include(CheckSymbolExists)
|
||||
include(CheckLibraryExists)
|
||||
|
||||
cmake_policy(VERSION 3.13)
|
||||
if (POLICY CMP0076)
|
||||
cmake_policy(SET CMP0076 NEW)
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "Minimum OS X deployment version")
|
||||
|
||||
project(melonDS CXX)
|
||||
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
||||
project(melonDS)
|
||||
|
||||
if (NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
check_library_exists(m pow "" LIBM)
|
||||
if(LIBM)
|
||||
link_libraries(m)
|
||||
endif()
|
||||
|
||||
if (NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
endif()
|
||||
|
||||
include(CheckSymbolExists)
|
||||
function(detect_architecture symbol arch)
|
||||
if (NOT DEFINED ARCHITECTURE)
|
||||
set(CMAKE_REQUIRED_QUIET 1)
|
||||
@ -65,17 +75,36 @@ if (CMAKE_BUILD_TYPE STREQUAL Release)
|
||||
add_link_options(-s)
|
||||
endif()
|
||||
|
||||
add_compile_options(-fno-pic)
|
||||
add_link_options(-no-pie)
|
||||
|
||||
option(BUILD_QT_SDL "Build Qt/SDL frontend" ON)
|
||||
|
||||
if (WIN32)
|
||||
option(BUILD_STATIC "Statically link dependencies" OFF)
|
||||
endif()
|
||||
|
||||
if (ENABLE_LTO)
|
||||
if (WIN32)
|
||||
add_compile_options(-flto)
|
||||
add_link_options(-flto)
|
||||
else()
|
||||
add_compile_options(-flto -fPIC)
|
||||
add_link_options(-flto -fuse-linker-plugin -pie)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
set(CMAKE_AR "gcc-ar")
|
||||
set(CMAKE_RANLIB "gcc-ranlib")
|
||||
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
find_program(LLD NAMES ld.lld ld64.lld lld-link)
|
||||
if (NOT LLD STREQUAL "LLD-NOTFOUND")
|
||||
add_link_options(-fuse-ld=lld)
|
||||
endif()
|
||||
set(CMAKE_AR "llvm-ar")
|
||||
set(CMAKE_RANLIB "llvm-ranlib")
|
||||
endif()
|
||||
|
||||
option(BUILD_QT_SDL "Build Qt/SDL frontend" ON)
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
if (BUILD_QT_SDL)
|
||||
add_subdirectory(src/frontend/qt_sdl)
|
||||
endif()
|
||||
endif()
|
17
README.md
17
README.md
@ -38,7 +38,7 @@ As for the rest, the interface should be pretty straightforward. If you have a q
|
||||
* Install dependencies:
|
||||
|
||||
```sh
|
||||
sudo apt-get install cmake libgtk-3-dev libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qtbase5-dev qtdeclarative5-dev libslirp-dev libarchive-dev
|
||||
sudo apt-get install cmake libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qtbase5-dev qtdeclarative5-dev libslirp-dev libarchive-dev
|
||||
```
|
||||
|
||||
* Compile:
|
||||
@ -84,6 +84,21 @@ If everything went well, melonDS and the libraries it needs should now be in the
|
||||
```
|
||||
If everything went well, melonDS should now be in the `dist` folder.
|
||||
|
||||
### macOS:
|
||||
1. Install the [Homebrew Package Manager](https://brew.sh)
|
||||
2. Install dependencies: `brew install git pkg-config cmake sdl2 qt5 libslirp libarchive`
|
||||
3. Compile:
|
||||
```zsh
|
||||
git clone https://github.com/Arisotura/melonDS.git
|
||||
cd melonDS
|
||||
mkdir build && cd build
|
||||
cmake .. -DQt5_DIR=$(brew --prefix qt5)/lib/cmake/Qt5
|
||||
make -j$(sysctl -n hw.ncpu)
|
||||
mkdir dist && cp -r melonDS.app dist
|
||||
```
|
||||
If everything went well, melonDS.app should now be in the `dist` folder.
|
||||
|
||||
|
||||
## TODO LIST
|
||||
|
||||
* DSi emulation
|
||||
|
BIN
melonDS.icns
Normal file
BIN
melonDS.icns
Normal file
Binary file not shown.
24
melonDS.plist
Normal file
24
melonDS.plist
Normal file
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>melonDS</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>melonDS.icns</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>net.kuribo64.melonDS</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.9</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.9</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Licensed under GPLv3</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
@ -592,7 +592,7 @@ void ARMv5::Execute()
|
||||
else
|
||||
AddCycles_C();
|
||||
}
|
||||
|
||||
|
||||
// TODO optimize this shit!!!
|
||||
if (Halted)
|
||||
{
|
||||
@ -651,7 +651,7 @@ void ARMv5::ExecuteJIT()
|
||||
return;
|
||||
}
|
||||
|
||||
ARMJIT::JitBlockEntry block = ARMJIT::LookUpBlock(0, FastBlockLookup,
|
||||
ARMJIT::JitBlockEntry block = ARMJIT::LookUpBlock(0, FastBlockLookup,
|
||||
instrAddr - FastBlockLookupStart, instrAddr);
|
||||
if (block)
|
||||
ARM_Dispatch(this, block);
|
||||
@ -802,7 +802,7 @@ void ARMv4::ExecuteJIT()
|
||||
return;
|
||||
}
|
||||
|
||||
ARMJIT::JitBlockEntry block = ARMJIT::LookUpBlock(1, FastBlockLookup,
|
||||
ARMJIT::JitBlockEntry block = ARMJIT::LookUpBlock(1, FastBlockLookup,
|
||||
instrAddr - FastBlockLookupStart, instrAddr);
|
||||
if (block)
|
||||
ARM_Dispatch(this, block);
|
||||
@ -879,4 +879,4 @@ void ARMv4::FillPipeline()
|
||||
NextInstr[0] = CodeRead32(R[15] - 4);
|
||||
NextInstr[1] = CodeRead32(R[15]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -176,7 +176,7 @@ T SlowRead9(u32 addr, ARMv5* cpu)
|
||||
}
|
||||
|
||||
template <typename T, int ConsoleType>
|
||||
void SlowWrite9(u32 addr, ARMv5* cpu, T val)
|
||||
void SlowWrite9(u32 addr, ARMv5* cpu, u32 val)
|
||||
{
|
||||
addr &= ~(sizeof(T) - 1);
|
||||
|
||||
@ -224,7 +224,7 @@ T SlowRead7(u32 addr)
|
||||
}
|
||||
|
||||
template <typename T, int ConsoleType>
|
||||
void SlowWrite7(u32 addr, T val)
|
||||
void SlowWrite7(u32 addr, u32 val)
|
||||
{
|
||||
addr &= ~(sizeof(T) - 1);
|
||||
|
||||
@ -266,16 +266,16 @@ void SlowBlockTransfer7(u32 addr, u64* data, u32 num)
|
||||
|
||||
#define INSTANTIATE_SLOWMEM(consoleType) \
|
||||
template void SlowWrite9<u32, consoleType>(u32, ARMv5*, u32); \
|
||||
template void SlowWrite9<u16, consoleType>(u32, ARMv5*, u16); \
|
||||
template void SlowWrite9<u8, consoleType>(u32, ARMv5*, u8); \
|
||||
template void SlowWrite9<u16, consoleType>(u32, ARMv5*, u32); \
|
||||
template void SlowWrite9<u8, consoleType>(u32, ARMv5*, u32); \
|
||||
\
|
||||
template u32 SlowRead9<u32, consoleType>(u32, ARMv5*); \
|
||||
template u16 SlowRead9<u16, consoleType>(u32, ARMv5*); \
|
||||
template u8 SlowRead9<u8, consoleType>(u32, ARMv5*); \
|
||||
\
|
||||
template void SlowWrite7<u32, consoleType>(u32, u32); \
|
||||
template void SlowWrite7<u16, consoleType>(u32, u16); \
|
||||
template void SlowWrite7<u8, consoleType>(u32, u8); \
|
||||
template void SlowWrite7<u16, consoleType>(u32, u32); \
|
||||
template void SlowWrite7<u8, consoleType>(u32, u32); \
|
||||
\
|
||||
template u32 SlowRead7<u32, consoleType>(u32); \
|
||||
template u16 SlowRead7<u16, consoleType>(u32); \
|
||||
@ -298,6 +298,7 @@ void Init()
|
||||
|
||||
void DeInit()
|
||||
{
|
||||
ResetBlockCache();
|
||||
ARMJIT_Memory::DeInit();
|
||||
|
||||
delete JITCompiler;
|
||||
@ -594,7 +595,8 @@ void CompileBlock(ARM* cpu)
|
||||
u32 r15 = cpu->R[15];
|
||||
|
||||
u32 addressRanges[Config::JIT_MaxBlockSize];
|
||||
u32 addressMasks[Config::JIT_MaxBlockSize] = {0};
|
||||
u32 addressMasks[Config::JIT_MaxBlockSize];
|
||||
memset(addressMasks, 0, Config::JIT_MaxBlockSize * sizeof(u32));
|
||||
u32 numAddressRanges = 0;
|
||||
|
||||
u32 numLiterals = 0;
|
||||
@ -1116,6 +1118,7 @@ void ResetBlockCache()
|
||||
range->Blocks.Clear();
|
||||
range->Code = 0;
|
||||
}
|
||||
delete block;
|
||||
}
|
||||
JitBlocks9.clear();
|
||||
JitBlocks7.clear();
|
||||
|
@ -1,5 +1,11 @@
|
||||
#include "ARMJIT_Compiler.h"
|
||||
|
||||
#include "../ARMJIT_Internal.h"
|
||||
#include "../ARMInterpreter.h"
|
||||
#include "../Config.h"
|
||||
|
||||
#ifdef __SWITCH__
|
||||
#include "../switch/compat_switch.h"
|
||||
#include <switch.h>
|
||||
|
||||
extern char __start__;
|
||||
#else
|
||||
@ -7,13 +13,7 @@ extern char __start__;
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "ARMJIT_Compiler.h"
|
||||
|
||||
#include "../ARMJIT_Internal.h"
|
||||
#include "../ARMInterpreter.h"
|
||||
#include "../Config.h"
|
||||
|
||||
#include <malloc.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
using namespace Arm64Gen;
|
||||
|
||||
@ -68,6 +68,11 @@ void Compiler::A_Comp_MRS()
|
||||
MOV(rd, RCPSR);
|
||||
}
|
||||
|
||||
void UpdateModeTrampoline(ARM* arm, u32 oldmode, u32 newmode)
|
||||
{
|
||||
arm->UpdateMode(oldmode, newmode);
|
||||
}
|
||||
|
||||
void Compiler::A_Comp_MSR()
|
||||
{
|
||||
Comp_AddCycles_C();
|
||||
@ -139,7 +144,7 @@ void Compiler::A_Comp_MSR()
|
||||
|
||||
PushRegs(true);
|
||||
|
||||
QuickCallFunction(X3, (void*)&ARM::UpdateMode);
|
||||
QuickCallFunction(X3, (void*)&UpdateModeTrampoline);
|
||||
|
||||
PopRegs(true);
|
||||
}
|
||||
@ -179,7 +184,7 @@ void Compiler::PopRegs(bool saveHiRegs)
|
||||
Compiler::Compiler()
|
||||
{
|
||||
#ifdef __SWITCH__
|
||||
JitRWBase = memalign(0x1000, JitMemSize);
|
||||
JitRWBase = aligned_alloc(0x1000, JitMemSize);
|
||||
|
||||
JitRXStart = (u8*)&__start__ - JitMemSize - 0x1000;
|
||||
JitRWStart = virtmemReserve(JitMemSize);
|
||||
@ -915,4 +920,4 @@ void Compiler::Comp_AddCycles_CD()
|
||||
ConstantCycles += cycles;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -187,6 +187,7 @@ public:
|
||||
void Comp_RegShiftReg(int op, bool S, Op2& op2, Arm64Gen::ARM64Reg rs);
|
||||
|
||||
bool Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr);
|
||||
|
||||
enum
|
||||
{
|
||||
memop_Writeback = 1 << 0,
|
||||
@ -213,8 +214,8 @@ public:
|
||||
return (u8*)entry - GetRXBase();
|
||||
}
|
||||
|
||||
bool IsJITFault(u64 pc);
|
||||
s64 RewriteMemAccess(u64 pc);
|
||||
bool IsJITFault(u8* pc);
|
||||
u8* RewriteMemAccess(u8* pc);
|
||||
|
||||
void SwapCodeRegion()
|
||||
{
|
||||
|
@ -9,37 +9,34 @@ using namespace Arm64Gen;
|
||||
namespace ARMJIT
|
||||
{
|
||||
|
||||
bool Compiler::IsJITFault(u64 pc)
|
||||
bool Compiler::IsJITFault(u8* pc)
|
||||
{
|
||||
return pc >= (u64)GetRXBase() && pc - (u64)GetRXBase() < (JitMemMainSize + JitMemSecondarySize);
|
||||
return (u64)pc >= (u64)GetRXBase() && (u64)pc - (u64)GetRXBase() < (JitMemMainSize + JitMemSecondarySize);
|
||||
}
|
||||
|
||||
s64 Compiler::RewriteMemAccess(u64 pc)
|
||||
u8* Compiler::RewriteMemAccess(u8* pc)
|
||||
{
|
||||
ptrdiff_t pcOffset = pc - (u64)GetRXBase();
|
||||
ptrdiff_t pcOffset = pc - GetRXBase();
|
||||
|
||||
auto it = LoadStorePatches.find(pcOffset);
|
||||
|
||||
if (it != LoadStorePatches.end())
|
||||
{
|
||||
LoadStorePatch patch = it->second;
|
||||
LoadStorePatches.erase(it);
|
||||
|
||||
ptrdiff_t curCodeOffset = GetCodeOffset();
|
||||
|
||||
SetCodePtrUnsafe(pcOffset + patch.PatchOffset);
|
||||
|
||||
BL(patch.PatchFunc);
|
||||
|
||||
for (int i = 0; i < patch.PatchSize / 4 - 1; i++)
|
||||
HINT(HINT_NOP);
|
||||
|
||||
FlushIcacheSection((u8*)pc + patch.PatchOffset, (u8*)GetRXPtr());
|
||||
|
||||
SetCodePtrUnsafe(curCodeOffset);
|
||||
|
||||
LoadStorePatches.erase(it);
|
||||
|
||||
return patch.PatchOffset;
|
||||
return pc + (ptrdiff_t)patch.PatchOffset;
|
||||
}
|
||||
printf("this is a JIT bug! %08x\n", __builtin_bswap32(*(u32*)pc));
|
||||
abort();
|
||||
@ -192,7 +189,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
|
||||
else
|
||||
{
|
||||
LDRGeneric(size, flags & memop_SignExtend, rdMapped, size > 8 ? X1 : X0, X7);
|
||||
if (size == 32)
|
||||
if (size == 32 && !addrIsStatic)
|
||||
{
|
||||
UBFIZ(W0, W0, 3, 2);
|
||||
RORV(rdMapped, rdMapped, W0);
|
||||
|
@ -216,9 +216,9 @@ template <u32 Num>
|
||||
void LinkBlock(ARM* cpu, u32 codeOffset);
|
||||
|
||||
template <typename T, int ConsoleType> T SlowRead9(u32 addr, ARMv5* cpu);
|
||||
template <typename T, int ConsoleType> void SlowWrite9(u32 addr, ARMv5* cpu, T val);
|
||||
template <typename T, int ConsoleType> void SlowWrite9(u32 addr, ARMv5* cpu, u32 val);
|
||||
template <typename T, int ConsoleType> T SlowRead7(u32 addr);
|
||||
template <typename T, int ConsoleType> void SlowWrite7(u32 addr, T val);
|
||||
template <typename T, int ConsoleType> void SlowWrite7(u32 addr, u32 val);
|
||||
|
||||
template <bool Write, int ConsoleType> void SlowBlockTransfer9(u32 addr, u64* data, u32 num, ARMv5* cpu);
|
||||
template <bool Write, int ConsoleType> void SlowBlockTransfer7(u32 addr, u64* data, u32 num);
|
||||
|
@ -1,5 +1,6 @@
|
||||
#if defined(__SWITCH__)
|
||||
#include "switch/compat_switch.h"
|
||||
#include <switch.h>
|
||||
#include "frontend/switch/FaultHandler.h"
|
||||
#elif defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#else
|
||||
@ -10,6 +11,12 @@
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include <dlfcn.h>
|
||||
#include <linux/ashmem.h>
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
|
||||
#include "ARMJIT_Memory.h"
|
||||
|
||||
#include "ARMJIT_Internal.h"
|
||||
@ -22,7 +29,7 @@
|
||||
#include "NDSCart.h"
|
||||
#include "SPU.h"
|
||||
|
||||
#include <malloc.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/*
|
||||
We're handling fastmem here.
|
||||
@ -40,7 +47,8 @@
|
||||
We handle this by only mapping those regions which are actually
|
||||
used and by praying the games don't go wild.
|
||||
|
||||
Beware, this file is full of platform specific code.
|
||||
Beware, this file is full of platform specific code and copied
|
||||
from Dolphin, so enjoy the copied comments!
|
||||
|
||||
*/
|
||||
|
||||
@ -49,12 +57,16 @@ namespace ARMJIT_Memory
|
||||
struct FaultDescription
|
||||
{
|
||||
u32 EmulatedFaultAddr;
|
||||
u64 FaultPC;
|
||||
u8* FaultPC;
|
||||
};
|
||||
|
||||
bool FaultHandler(FaultDescription* faultDesc, s32& offset);
|
||||
bool FaultHandler(FaultDescription& faultDesc);
|
||||
}
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#define ASHMEM_DEVICE "/dev/ashmem"
|
||||
#endif
|
||||
|
||||
#if defined(__SWITCH__)
|
||||
// with LTO the symbols seem to be not properly overriden
|
||||
// if they're somewhere else
|
||||
@ -75,7 +87,7 @@ void __libnx_exception_handler(ThreadExceptionDump* ctx)
|
||||
ARMJIT_Memory::FaultDescription desc;
|
||||
u8* curArea = (u8*)(NDS::CurCPU == 0 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start);
|
||||
desc.EmulatedFaultAddr = (u8*)ctx->far.x - curArea;
|
||||
desc.FaultPC = ctx->pc.x;
|
||||
desc.FaultPC = (u8*)ctx->pc.x;
|
||||
|
||||
u64 integerRegisters[33];
|
||||
memcpy(integerRegisters, &ctx->cpu_gprs[0].x, 8*29);
|
||||
@ -84,23 +96,14 @@ void __libnx_exception_handler(ThreadExceptionDump* ctx)
|
||||
integerRegisters[31] = ctx->sp.x;
|
||||
integerRegisters[32] = ctx->pc.x;
|
||||
|
||||
s32 offset;
|
||||
if (ARMJIT_Memory::FaultHandler(&desc, offset))
|
||||
if (ARMJIT_Memory::FaultHandler(desc))
|
||||
{
|
||||
integerRegisters[32] += offset;
|
||||
integerRegisters[32] = (u64)desc.FaultPC;
|
||||
|
||||
ARM_RestoreContext(integerRegisters);
|
||||
}
|
||||
|
||||
if (ctx->pc.x >= (u64)&__start__ && ctx->pc.x < (u64)&__rodata_start)
|
||||
{
|
||||
printf("unintentional fault in .text at 0x%x (type %d) (trying to access 0x%x?)\n",
|
||||
ctx->pc.x - (u64)&__start__, ctx->error_desc, ctx->far.x);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("unintentional fault somewhere in deep (address) space at %x (type %d)\n", ctx->pc.x, ctx->error_desc);
|
||||
}
|
||||
HandleFault(ctx->pc.x, ctx->lr.x, ctx->fp.x, ctx->far.x, ctx->error_desc);
|
||||
}
|
||||
|
||||
}
|
||||
@ -117,12 +120,11 @@ static LONG ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo)
|
||||
ARMJIT_Memory::FaultDescription desc;
|
||||
u8* curArea = (u8*)(NDS::CurCPU == 0 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start);
|
||||
desc.EmulatedFaultAddr = (u8*)exceptionInfo->ExceptionRecord->ExceptionInformation[1] - curArea;
|
||||
desc.FaultPC = exceptionInfo->ContextRecord->Rip;
|
||||
desc.FaultPC = (u8*)exceptionInfo->ContextRecord->Rip;
|
||||
|
||||
s32 offset = 0;
|
||||
if (ARMJIT_Memory::FaultHandler(&desc, offset))
|
||||
if (ARMJIT_Memory::FaultHandler(desc))
|
||||
{
|
||||
exceptionInfo->ContextRecord->Rip += offset;
|
||||
exceptionInfo->ContextRecord->Rip = (u64)desc.FaultPC;
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
|
||||
@ -131,50 +133,84 @@ static LONG ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo)
|
||||
|
||||
#else
|
||||
|
||||
struct sigaction NewSa;
|
||||
struct sigaction OldSa;
|
||||
static struct sigaction OldSaSegv;
|
||||
static struct sigaction OldSaBus;
|
||||
|
||||
static void SigsegvHandler(int sig, siginfo_t* info, void* rawContext)
|
||||
{
|
||||
if (sig != SIGSEGV && sig != SIGBUS)
|
||||
{
|
||||
// We are not interested in other signals - handle it as usual.
|
||||
return;
|
||||
}
|
||||
if (info->si_code != SEGV_MAPERR && info->si_code != SEGV_ACCERR)
|
||||
{
|
||||
// Huh? Return.
|
||||
return;
|
||||
}
|
||||
|
||||
ucontext_t* context = (ucontext_t*)rawContext;
|
||||
|
||||
|
||||
ARMJIT_Memory::FaultDescription desc;
|
||||
u8* curArea = (u8*)(NDS::CurCPU == 0 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start);
|
||||
#ifdef __x86_64__
|
||||
desc.EmulatedFaultAddr = (u8*)info->si_addr - curArea;
|
||||
desc.FaultPC = context->uc_mcontext.gregs[REG_RIP];
|
||||
#ifdef __APPLE__
|
||||
desc.FaultPC = (u8*)context->uc_mcontext->__ss.__rip;
|
||||
#else
|
||||
desc.FaultPC = (u8*)context->uc_mcontext.gregs[REG_RIP];
|
||||
#endif
|
||||
|
||||
#else
|
||||
desc.EmulatedFaultAddr = (u8*)context->uc_mcontext.fault_address - curArea;
|
||||
desc.FaultPC = context->uc_mcontext.pc;
|
||||
#ifdef __APPLE__
|
||||
desc.EmulatedFaultAddr = (u8*)context->uc_mcontext->__es.__far - curArea;
|
||||
desc.FaultPC = (u8*)context->uc_mcontext->__ss.__pc;
|
||||
#else
|
||||
desc.EmulatedFaultAddr = (u8*)context->uc_mcontext.fault_address - curArea;
|
||||
desc.FaultPC = (u8*)context->uc_mcontext.pc;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
s32 offset = 0;
|
||||
if (ARMJIT_Memory::FaultHandler(&desc, offset))
|
||||
if (ARMJIT_Memory::FaultHandler(desc))
|
||||
{
|
||||
#ifdef __x86_64__
|
||||
context->uc_mcontext.gregs[REG_RIP] += offset;
|
||||
#ifdef __APPLE__
|
||||
context->uc_mcontext->__ss.__rip = (u64)desc.FaultPC;
|
||||
#else
|
||||
context->uc_mcontext.gregs[REG_RIP] = (u64)desc.FaultPC;
|
||||
#endif
|
||||
#else
|
||||
context->uc_mcontext.pc += offset;
|
||||
#ifdef __APPLE__
|
||||
context->uc_mcontext->__ss.__pc = (u64)desc.FaultPC;
|
||||
#else
|
||||
context->uc_mcontext.pc = (u64)desc.FaultPC;
|
||||
#endif
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if (OldSa.sa_flags & SA_SIGINFO)
|
||||
struct sigaction* oldSa;
|
||||
if (sig == SIGSEGV)
|
||||
oldSa = &OldSaSegv;
|
||||
else
|
||||
oldSa = &OldSaBus;
|
||||
|
||||
if (oldSa->sa_flags & SA_SIGINFO)
|
||||
{
|
||||
OldSa.sa_sigaction(sig, info, rawContext);
|
||||
oldSa->sa_sigaction(sig, info, rawContext);
|
||||
return;
|
||||
}
|
||||
if (OldSa.sa_handler == SIG_DFL)
|
||||
if (oldSa->sa_handler == SIG_DFL)
|
||||
{
|
||||
signal(sig, SIG_DFL);
|
||||
return;
|
||||
}
|
||||
if (OldSa.sa_handler == SIG_IGN)
|
||||
if (oldSa->sa_handler == SIG_IGN)
|
||||
{
|
||||
// Ignore signal
|
||||
return;
|
||||
}
|
||||
OldSa.sa_handler(sig);
|
||||
oldSa->sa_handler(sig);
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -231,7 +267,7 @@ enum
|
||||
{
|
||||
memstate_Unmapped,
|
||||
memstate_MappedRW,
|
||||
// on switch this is unmapped as well
|
||||
// on Switch this is unmapped as well
|
||||
memstate_MappedProtected,
|
||||
};
|
||||
|
||||
@ -314,14 +350,16 @@ struct Mapping
|
||||
|
||||
void Unmap(int region)
|
||||
{
|
||||
u32 dtcmStart = NDS::ARM9->DTCMBase;
|
||||
u32 dtcmSize = NDS::ARM9->DTCMSize;
|
||||
bool skipDTCM = Num == 0 && region != memregion_DTCM;
|
||||
u8* statuses = Num == 0 ? MappingStatus9 : MappingStatus7;
|
||||
u32 offset = 0;
|
||||
while (offset < Size)
|
||||
{
|
||||
if (skipDTCM && Addr + offset == NDS::ARM9->DTCMBase)
|
||||
if (skipDTCM && Addr + offset == dtcmStart)
|
||||
{
|
||||
offset += NDS::ARM9->DTCMSize;
|
||||
offset += dtcmSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -329,7 +367,7 @@ struct Mapping
|
||||
u8 status = statuses[(Addr + offset) >> 12];
|
||||
while (statuses[(Addr + offset) >> 12] == status
|
||||
&& offset < Size
|
||||
&& (!skipDTCM || Addr + offset != NDS::ARM9->DTCMBase))
|
||||
&& (!skipDTCM || Addr + offset != dtcmStart))
|
||||
{
|
||||
assert(statuses[(Addr + offset) >> 12] != memstate_Unmapped);
|
||||
statuses[(Addr + offset) >> 12] = memstate_Unmapped;
|
||||
@ -347,9 +385,33 @@ struct Mapping
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef __SWITCH__
|
||||
bool succeded = UnmapFromRange(Addr, Num, OffsetsPerRegion[region] + LocalOffset, Size);
|
||||
assert(succeded);
|
||||
#ifndef _WIN32
|
||||
u32 dtcmEnd = dtcmStart + dtcmSize;
|
||||
if (Num == 0
|
||||
&& dtcmEnd >= Addr
|
||||
&& dtcmStart < Addr + Size)
|
||||
{
|
||||
bool success;
|
||||
if (dtcmStart > Addr)
|
||||
{
|
||||
success = UnmapFromRange(Addr, 0, OffsetsPerRegion[region] + LocalOffset, dtcmStart - Addr);
|
||||
assert(success);
|
||||
}
|
||||
if (dtcmEnd < Addr + Size)
|
||||
{
|
||||
u32 offset = dtcmStart - Addr + dtcmSize;
|
||||
success = UnmapFromRange(dtcmEnd, 0, OffsetsPerRegion[region] + LocalOffset + offset, Size - offset);
|
||||
assert(success);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
bool succeded = UnmapFromRange(Addr, Num, OffsetsPerRegion[region] + LocalOffset, Size);
|
||||
assert(succeded);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
};
|
||||
@ -418,10 +480,10 @@ void RemapDTCM(u32 newBase, u32 newSize)
|
||||
|
||||
printf("unmapping %d %x %x %x %x\n", region, mapping.Addr, mapping.Size, mapping.Num, mapping.LocalOffset);
|
||||
|
||||
bool oldOverlap = NDS::ARM9->DTCMSize > 0 && !(oldDTCMBase >= end || oldDTCBEnd <= start);
|
||||
bool newOverlap = newSize > 0 && !(newBase >= end || newEnd <= start);
|
||||
bool overlap = (NDS::ARM9->DTCMSize > 0 && oldDTCMBase < end && oldDTCBEnd > start)
|
||||
|| (newSize > 0 && newBase < end && newEnd > start);
|
||||
|
||||
if (mapping.Num == 0 && (oldOverlap || newOverlap))
|
||||
if (mapping.Num == 0 && overlap)
|
||||
{
|
||||
mapping.Unmap(region);
|
||||
Mappings[region].Remove(i);
|
||||
@ -445,8 +507,8 @@ void RemapNWRAM(int num)
|
||||
for (int i = 0; i < Mappings[memregion_SharedWRAM].Length;)
|
||||
{
|
||||
Mapping& mapping = Mappings[memregion_SharedWRAM][i];
|
||||
if (!(DSi::NWRAMStart[mapping.Num][num] >= mapping.Addr + mapping.Size
|
||||
|| DSi::NWRAMEnd[mapping.Num][num] < mapping.Addr))
|
||||
if (DSi::NWRAMStart[mapping.Num][num] < mapping.Addr + mapping.Size
|
||||
&& DSi::NWRAMEnd[mapping.Num][num] > mapping.Addr)
|
||||
{
|
||||
mapping.Unmap(memregion_SharedWRAM);
|
||||
Mappings[memregion_SharedWRAM].Remove(i);
|
||||
@ -469,7 +531,7 @@ void RemapSWRAM()
|
||||
for (int i = 0; i < Mappings[memregion_WRAM7].Length;)
|
||||
{
|
||||
Mapping& mapping = Mappings[memregion_WRAM7][i];
|
||||
if (mapping.Addr + mapping.Size < 0x03800000)
|
||||
if (mapping.Addr + mapping.Size <= 0x03800000)
|
||||
{
|
||||
mapping.Unmap(memregion_WRAM7);
|
||||
Mappings[memregion_WRAM7].Remove(i);
|
||||
@ -501,26 +563,53 @@ bool MapAtAddress(u32 addr)
|
||||
return false;
|
||||
|
||||
u8* states = num == 0 ? MappingStatus9 : MappingStatus7;
|
||||
printf("trying to create mapping %x, %x %x %d %d\n", mirrorStart, mirrorSize, memoryOffset, region, num);
|
||||
printf("mapping mirror %x, %x %x %d %d\n", mirrorStart, mirrorSize, memoryOffset, region, num);
|
||||
bool isExecutable = ARMJIT::CodeMemRegions[region];
|
||||
|
||||
u32 dtcmStart = NDS::ARM9->DTCMBase;
|
||||
u32 dtcmSize = NDS::ARM9->DTCMSize;
|
||||
u32 dtcmEnd = dtcmStart + dtcmSize;
|
||||
#ifndef __SWITCH__
|
||||
bool succeded = MapIntoRange(mirrorStart, num, OffsetsPerRegion[region] + memoryOffset, mirrorSize);
|
||||
assert(succeded);
|
||||
#ifndef _WIN32
|
||||
if (num == 0
|
||||
&& dtcmEnd >= mirrorStart
|
||||
&& dtcmStart < mirrorStart + mirrorSize)
|
||||
{
|
||||
bool success;
|
||||
if (dtcmStart > mirrorStart)
|
||||
{
|
||||
success = MapIntoRange(mirrorStart, 0, OffsetsPerRegion[region] + memoryOffset, dtcmStart - mirrorStart);
|
||||
assert(success);
|
||||
}
|
||||
if (dtcmEnd < mirrorStart + mirrorSize)
|
||||
{
|
||||
u32 offset = dtcmStart - mirrorStart + dtcmSize;
|
||||
success = MapIntoRange(dtcmEnd, 0, OffsetsPerRegion[region] + memoryOffset + offset, mirrorSize - offset);
|
||||
assert(success);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
bool succeded = MapIntoRange(mirrorStart, num, OffsetsPerRegion[region] + memoryOffset, mirrorSize);
|
||||
assert(succeded);
|
||||
}
|
||||
#endif
|
||||
|
||||
ARMJIT::AddressRange* range = ARMJIT::CodeMemRegions[region] + memoryOffset / 512;
|
||||
|
||||
// this overcomplicated piece of code basically just finds whole pieces of code memory
|
||||
// which can be mapped
|
||||
// which can be mapped/protected
|
||||
u32 offset = 0;
|
||||
bool skipDTCM = num == 0 && region != memregion_DTCM;
|
||||
while (offset < mirrorSize)
|
||||
{
|
||||
if (skipDTCM && mirrorStart + offset == NDS::ARM9->DTCMBase)
|
||||
if (skipDTCM && mirrorStart + offset == dtcmStart)
|
||||
{
|
||||
SetCodeProtectionRange(NDS::ARM9->DTCMBase, NDS::ARM9->DTCMSize, 0, 0);
|
||||
offset += NDS::ARM9->DTCMSize;
|
||||
#ifdef _WIN32
|
||||
SetCodeProtectionRange(dtcmStart, dtcmSize, 0, 0);
|
||||
#endif
|
||||
offset += dtcmSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -557,37 +646,36 @@ bool MapAtAddress(u32 addr)
|
||||
Mapping mapping{mirrorStart, mirrorSize, memoryOffset, num};
|
||||
Mappings[region].Add(mapping);
|
||||
|
||||
printf("mapped mirror at %08x-%08x\n", mirrorStart, mirrorStart + mirrorSize - 1);
|
||||
//printf("mapped mirror at %08x-%08x\n", mirrorStart, mirrorStart + mirrorSize - 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FaultHandler(FaultDescription* faultDesc, s32& offset)
|
||||
bool FaultHandler(FaultDescription& faultDesc)
|
||||
{
|
||||
if (ARMJIT::JITCompiler->IsJITFault(faultDesc->FaultPC))
|
||||
if (ARMJIT::JITCompiler->IsJITFault(faultDesc.FaultPC))
|
||||
{
|
||||
bool rewriteToSlowPath = true;
|
||||
|
||||
u32 addr = faultDesc->EmulatedFaultAddr;
|
||||
u8* memStatus = NDS::CurCPU == 0 ? MappingStatus9 : MappingStatus7;
|
||||
|
||||
if ((NDS::CurCPU == 0 ? MappingStatus9 : MappingStatus7)[addr >> 12] == memstate_Unmapped)
|
||||
rewriteToSlowPath = !MapAtAddress(faultDesc->EmulatedFaultAddr);
|
||||
if (memStatus[faultDesc.EmulatedFaultAddr >> 12] == memstate_Unmapped)
|
||||
rewriteToSlowPath = !MapAtAddress(faultDesc.EmulatedFaultAddr);
|
||||
|
||||
if (rewriteToSlowPath)
|
||||
{
|
||||
offset = ARMJIT::JITCompiler->RewriteMemAccess(faultDesc->FaultPC);
|
||||
}
|
||||
faultDesc.FaultPC = ARMJIT::JITCompiler->RewriteMemAccess(faultDesc.FaultPC);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const u64 AddrSpaceSize = 0x100000000;
|
||||
|
||||
void Init()
|
||||
{
|
||||
const u64 AddrSpaceSize = 0x100000000;
|
||||
|
||||
#if defined(__SWITCH__)
|
||||
MemoryBase = (u8*)memalign(0x1000, MemoryTotalSize);
|
||||
MemoryBase = (u8*)aligned_alloc(0x1000, MemoryTotalSize);
|
||||
MemoryBaseCodeMem = (u8*)virtmemReserve(MemoryTotalSize);
|
||||
|
||||
bool succeded = R_SUCCEEDED(svcMapProcessCodeMemory(envGetOwnProcessHandle(), (u64)MemoryBaseCodeMem,
|
||||
@ -624,22 +712,52 @@ void Init()
|
||||
|
||||
u8* basePtr = MemoryBase;
|
||||
#else
|
||||
FastMem9Start = mmap(NULL, AddrSpaceSize, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
FastMem7Start = mmap(NULL, AddrSpaceSize, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
// this used to be allocated with three different mmaps
|
||||
// The idea was to give the OS more freedom where to position the buffers,
|
||||
// but something was bad about this so instead we take this vmem eating monster
|
||||
// which seems to work better.
|
||||
MemoryBase = (u8*)mmap(NULL, AddrSpaceSize*4, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
munmap(MemoryBase, AddrSpaceSize*4);
|
||||
FastMem9Start = MemoryBase;
|
||||
FastMem7Start = MemoryBase + AddrSpaceSize;
|
||||
MemoryBase = MemoryBase + AddrSpaceSize*2;
|
||||
|
||||
MemoryBase = (u8*)mmap(NULL, MemoryTotalSize, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
#if defined(__ANDROID__)
|
||||
static void* libandroid = dlopen("libandroid.so", RTLD_LAZY | RTLD_LOCAL);
|
||||
using type_ASharedMemory_create = int(*)(const char* name, size_t size);
|
||||
static void* symbol = dlsym(libandroid, "ASharedMemory_create");
|
||||
static auto shared_memory_create = reinterpret_cast<type_ASharedMemory_create>(symbol);
|
||||
|
||||
if (shared_memory_create)
|
||||
{
|
||||
MemoryFile = shared_memory_create("melondsfastmem", MemoryTotalSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
int fd = open(ASHMEM_DEVICE, O_RDWR);
|
||||
ioctl(fd, ASHMEM_SET_NAME, "melondsfastmem");
|
||||
ioctl(fd, ASHMEM_SET_SIZE, MemoryTotalSize);
|
||||
MemoryFile = fd;
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
char* fastmemPidName = new char[snprintf(NULL, 0, "melondsfastmem%d", getpid()) + 1];
|
||||
sprintf(fastmemPidName, "melondsfastmem%d", getpid());
|
||||
MemoryFile = shm_open(fastmemPidName, O_RDWR|O_CREAT, 0600);
|
||||
delete[] fastmemPidName;
|
||||
#else
|
||||
MemoryFile = memfd_create("melondsfastmem", 0);
|
||||
#endif
|
||||
ftruncate(MemoryFile, MemoryTotalSize);
|
||||
|
||||
NewSa.sa_flags = SA_SIGINFO;
|
||||
sigemptyset(&NewSa.sa_mask);
|
||||
NewSa.sa_sigaction = SigsegvHandler;
|
||||
sigaction(SIGSEGV, &NewSa, &OldSa);
|
||||
|
||||
munmap(MemoryBase, MemoryTotalSize);
|
||||
munmap(FastMem9Start, AddrSpaceSize);
|
||||
munmap(FastMem7Start, AddrSpaceSize);
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = nullptr;
|
||||
sa.sa_sigaction = &SigsegvHandler;
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sigaction(SIGSEGV, &sa, &OldSaSegv);
|
||||
#ifdef __APPLE__
|
||||
sigaction(SIGBUS, &sa, &OldSaBus);
|
||||
#endif
|
||||
|
||||
mmap(MemoryBase, MemoryTotalSize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, MemoryFile, 0);
|
||||
|
||||
@ -657,17 +775,30 @@ void Init()
|
||||
void DeInit()
|
||||
{
|
||||
#if defined(__SWITCH__)
|
||||
virtmemFree(FastMem9Start, 0x100000000);
|
||||
virtmemFree(FastMem7Start, 0x100000000);
|
||||
virtmemFree(FastMem9Start, AddrSpaceSize);
|
||||
virtmemFree(FastMem7Start, AddrSpaceSize);
|
||||
|
||||
svcUnmapProcessCodeMemory(envGetOwnProcessHandle(), (u64)MemoryBaseCodeMem, (u64)MemoryBase, MemoryTotalSize);
|
||||
virtmemFree(MemoryBaseCodeMem, MemoryTotalSize);
|
||||
free(MemoryBase);
|
||||
#elif defined(__APPLE__)
|
||||
char* fastmemPidName = new char[snprintf(NULL, 0, "melondsfastmem%d", getpid()) + 1];
|
||||
sprintf(fastmemPidName, "melondsfastmem%d", getpid());
|
||||
shm_unlink(fastmemPidName);
|
||||
delete[] fastmemPidName;
|
||||
#elif defined(_WIN32)
|
||||
assert(UnmapViewOfFile(MemoryBase));
|
||||
CloseHandle(MemoryFile);
|
||||
|
||||
RemoveVectoredExceptionHandler(ExceptionHandlerHandle);
|
||||
#else
|
||||
sigaction(SIGSEGV, &OldSaSegv, nullptr);
|
||||
#ifdef __APPLE__
|
||||
sigaction(SIGBUS, &OldSaBus, nullptr);
|
||||
#endif
|
||||
|
||||
munmap(MemoryBase, MemoryTotalSize);
|
||||
close(MemoryFile);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -997,9 +1128,11 @@ int ClassifyAddress7(u32 addr)
|
||||
case 0x06000000:
|
||||
case 0x06800000:
|
||||
return memregion_VWRAM;
|
||||
|
||||
default:
|
||||
return memregion_Other;
|
||||
}
|
||||
}
|
||||
return memregion_Other;
|
||||
}
|
||||
|
||||
void WifiWrite32(u32 addr, u32 val)
|
||||
@ -1176,4 +1309,4 @@ void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -130,6 +130,16 @@ void Compiler::Comp_JumpTo(u32 addr, bool forceNonConstantCycles)
|
||||
ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm8(cycles));
|
||||
}
|
||||
|
||||
void ARMv4JumpToTrampoline(ARMv4* arm, u32 addr, bool restorecpsr)
|
||||
{
|
||||
arm->JumpTo(addr, restorecpsr);
|
||||
}
|
||||
|
||||
void ARMv5JumpToTrampoline(ARMv5* arm, u32 addr, bool restorecpsr)
|
||||
{
|
||||
arm->JumpTo(addr, restorecpsr);
|
||||
}
|
||||
|
||||
void Compiler::Comp_JumpTo(Gen::X64Reg addr, bool restoreCPSR)
|
||||
{
|
||||
IrregularCycles = true;
|
||||
@ -146,9 +156,9 @@ void Compiler::Comp_JumpTo(Gen::X64Reg addr, bool restoreCPSR)
|
||||
else
|
||||
MOV(32, R(ABI_PARAM3), Imm32(true)); // what a waste
|
||||
if (Num == 0)
|
||||
CALL((void*)&ARMv5::JumpTo);
|
||||
CALL((void*)&ARMv5JumpToTrampoline);
|
||||
else
|
||||
CALL((void*)&ARMv4::JumpTo);
|
||||
CALL((void*)&ARMv4JumpToTrampoline);
|
||||
|
||||
PopRegs(restoreCPSR);
|
||||
|
||||
@ -269,4 +279,4 @@ void Compiler::T_Comp_BL_Merged()
|
||||
Comp_JumpTo(target);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -101,6 +101,11 @@ void Compiler::A_Comp_MRS()
|
||||
MOV(32, rd, R(RCPSR));
|
||||
}
|
||||
|
||||
void UpdateModeTrampoline(ARM* arm, u32 oldmode, u32 newmode)
|
||||
{
|
||||
arm->UpdateMode(oldmode, newmode);
|
||||
}
|
||||
|
||||
void Compiler::A_Comp_MSR()
|
||||
{
|
||||
Comp_AddCycles_C();
|
||||
@ -185,7 +190,7 @@ void Compiler::A_Comp_MSR()
|
||||
MOV(32, R(ABI_PARAM3), R(RCPSR));
|
||||
MOV(32, R(ABI_PARAM2), R(RSCRATCH3));
|
||||
MOV(64, R(ABI_PARAM1), R(RCPU));
|
||||
CALL((void*)&ARM::UpdateMode);
|
||||
CALL((void*)&UpdateModeTrampoline);
|
||||
|
||||
PopRegs(true);
|
||||
}
|
||||
@ -216,6 +221,8 @@ Compiler::Compiler()
|
||||
#ifdef _WIN32
|
||||
DWORD dummy;
|
||||
VirtualProtect(pageAligned, alignedSize, PAGE_EXECUTE_READWRITE, &dummy);
|
||||
#elif defined(__APPLE__)
|
||||
pageAligned = (u8*)mmap(NULL, 1024*1024*32, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS ,-1, 0);
|
||||
#else
|
||||
mprotect(pageAligned, alignedSize, PROT_EXEC | PROT_READ | PROT_WRITE);
|
||||
#endif
|
||||
@ -340,7 +347,7 @@ Compiler::Compiler()
|
||||
ABI_PushRegistersAndAdjustStack(CallerSavedPushRegs, 8);
|
||||
if (consoleType == 0)
|
||||
{
|
||||
switch ((8 << size) | num)
|
||||
switch ((8 << size) | num)
|
||||
{
|
||||
case 32: ABI_CallFunction(SlowWrite9<u32, 0>); break;
|
||||
case 33: ABI_CallFunction(SlowWrite7<u32, 0>); break;
|
||||
@ -352,7 +359,7 @@ Compiler::Compiler()
|
||||
}
|
||||
else
|
||||
{
|
||||
switch ((8 << size) | num)
|
||||
switch ((8 << size) | num)
|
||||
{
|
||||
case 32: ABI_CallFunction(SlowWrite9<u32, 1>); break;
|
||||
case 33: ABI_CallFunction(SlowWrite7<u32, 1>); break;
|
||||
@ -375,7 +382,7 @@ Compiler::Compiler()
|
||||
ABI_PushRegistersAndAdjustStack(CallerSavedPushRegs, 8);
|
||||
if (consoleType == 0)
|
||||
{
|
||||
switch ((8 << size) | num)
|
||||
switch ((8 << size) | num)
|
||||
{
|
||||
case 32: ABI_CallFunction(SlowRead9<u32, 0>); break;
|
||||
case 33: ABI_CallFunction(SlowRead7<u32, 0>); break;
|
||||
@ -387,7 +394,7 @@ Compiler::Compiler()
|
||||
}
|
||||
else
|
||||
{
|
||||
switch ((8 << size) | num)
|
||||
switch ((8 << size) | num)
|
||||
{
|
||||
case 32: ABI_CallFunction(SlowRead9<u32, 1>); break;
|
||||
case 33: ABI_CallFunction(SlowRead7<u32, 1>); break;
|
||||
@ -612,9 +619,9 @@ void Compiler::Reset()
|
||||
LoadStorePatches.clear();
|
||||
}
|
||||
|
||||
bool Compiler::IsJITFault(u64 addr)
|
||||
bool Compiler::IsJITFault(u8* addr)
|
||||
{
|
||||
return addr >= (u64)CodeMemory && addr < (u64)CodeMemory + sizeof(CodeMemory);
|
||||
return (u64)addr >= (u64)ResetStart && (u64)addr < (u64)ResetStart + CodeMemSize;
|
||||
}
|
||||
|
||||
void Compiler::Comp_SpecialBranchBehaviour(bool taken)
|
||||
@ -896,5 +903,4 @@ void Compiler::Comp_AddCycles_CD()
|
||||
else
|
||||
ConstantCycles += cycles;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -208,9 +208,9 @@ public:
|
||||
SetCodePtr(FarCode);
|
||||
}
|
||||
|
||||
bool IsJITFault(u64 addr);
|
||||
bool IsJITFault(u8* addr);
|
||||
|
||||
s32 RewriteMemAccess(u64 pc);
|
||||
u8* RewriteMemAccess(u8* pc);
|
||||
|
||||
u8* FarCode;
|
||||
u8* NearCode;
|
||||
|
@ -29,8 +29,13 @@
|
||||
|
||||
.p2align 4,,15
|
||||
|
||||
#ifdef __APPLE__
|
||||
.global _ARM_Dispatch
|
||||
_ARM_Dispatch:
|
||||
#else
|
||||
.global ARM_Dispatch
|
||||
ARM_Dispatch:
|
||||
#endif
|
||||
#ifdef WIN64
|
||||
push rdi
|
||||
push rsi
|
||||
@ -54,8 +59,13 @@ ARM_Dispatch:
|
||||
|
||||
.p2align 4,,15
|
||||
|
||||
#ifdef __APPLE__
|
||||
.global _ARM_Ret
|
||||
_ARM_Ret:
|
||||
#else
|
||||
.global ARM_Ret
|
||||
ARM_Ret:
|
||||
#endif
|
||||
mov [RCPU + ARM_CPSR_offset], RCPSR
|
||||
|
||||
#ifdef WIN64
|
@ -15,28 +15,24 @@ int squeezePointer(T* ptr)
|
||||
return truncated;
|
||||
}
|
||||
|
||||
s32 Compiler::RewriteMemAccess(u64 pc)
|
||||
u8* Compiler::RewriteMemAccess(u8* pc)
|
||||
{
|
||||
auto it = LoadStorePatches.find((u8*)pc);
|
||||
auto it = LoadStorePatches.find(pc);
|
||||
if (it != LoadStorePatches.end())
|
||||
{
|
||||
LoadStorePatch patch = it->second;
|
||||
LoadStorePatches.erase(it);
|
||||
|
||||
u8* curCodePtr = GetWritableCodePtr();
|
||||
u8* rewritePtr = (u8*)pc + (ptrdiff_t)patch.Offset;
|
||||
SetCodePtr(rewritePtr);
|
||||
//printf("rewriting memory access %p %d %d\n", (u8*)pc-ResetStart, patch.Offset, patch.Size);
|
||||
|
||||
CALL(patch.PatchFunc);
|
||||
u32 remainingSize = patch.Size - (GetWritableCodePtr() - rewritePtr);
|
||||
XEmitter emitter(pc + (ptrdiff_t)patch.Offset);
|
||||
emitter.CALL(patch.PatchFunc);
|
||||
ptrdiff_t remainingSize = (ptrdiff_t)patch.Size - 5;
|
||||
assert(remainingSize >= 0);
|
||||
if (remainingSize > 0)
|
||||
NOP(remainingSize);
|
||||
emitter.NOP(remainingSize);
|
||||
|
||||
//printf("rewriting memory access %p %d %d\n", patch.PatchFunc, patch.Offset, patch.Size);
|
||||
|
||||
SetCodePtr(curCodePtr);
|
||||
|
||||
return patch.Offset;
|
||||
return pc + (ptrdiff_t)patch.Offset;
|
||||
}
|
||||
|
||||
printf("this is a JIT bug %llx\n", pc);
|
||||
@ -192,6 +188,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
|
||||
u8* memopStart = GetWritableCodePtr();
|
||||
LoadStorePatch patch;
|
||||
|
||||
assert(rdMapped.GetSimpleReg() >= 0 && rdMapped.GetSimpleReg() < 16);
|
||||
patch.PatchFunc = flags & memop_Store
|
||||
? PatchedStoreFuncs[NDS::ConsoleType][Num][__builtin_ctz(size) - 3][rdMapped.GetSimpleReg()]
|
||||
: PatchedLoadFuncs[NDS::ConsoleType][Num][__builtin_ctz(size) - 3][!!(flags & memop_SignExtend)][rdMapped.GetSimpleReg()];
|
||||
|
@ -27,6 +27,7 @@ add_library(core STATIC
|
||||
GBACart.cpp
|
||||
GPU.cpp
|
||||
GPU2D.cpp
|
||||
GPU2D_Soft.cpp
|
||||
GPU3D.cpp
|
||||
GPU3D_Soft.cpp
|
||||
melonDLDI.h
|
||||
@ -80,9 +81,8 @@ if (ENABLE_JIT)
|
||||
ARMJIT_x64/ARMJIT_LoadStore.cpp
|
||||
ARMJIT_x64/ARMJIT_Branch.cpp
|
||||
|
||||
ARMJIT_x64/ARMJIT_Linkage.s
|
||||
ARMJIT_x64/ARMJIT_Linkage.S
|
||||
)
|
||||
set_source_files_properties(ARMJIT_x64/ARMJIT_Linkage.s PROPERTIES COMPILE_FLAGS "-x assembler-with-cpp")
|
||||
endif()
|
||||
if (ARCHITECTURE STREQUAL ARM64)
|
||||
target_sources(core PRIVATE
|
||||
@ -94,16 +94,22 @@ if (ENABLE_JIT)
|
||||
ARMJIT_A64/ARMJIT_LoadStore.cpp
|
||||
ARMJIT_A64/ARMJIT_Branch.cpp
|
||||
|
||||
ARMJIT_A64/ARMJIT_Linkage.s
|
||||
ARMJIT_A64/ARMJIT_Linkage.S
|
||||
)
|
||||
set_source_files_properties(ARMJIT_A64/ARMJIT_Linkage.s PROPERTIES COMPILE_FLAGS "-x assembler-with-cpp")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
target_include_directories(core PUBLIC /usr/local/include)
|
||||
target_link_directories(core PUBLIC /usr/local/lib)
|
||||
endif()
|
||||
|
||||
if (ENABLE_OGLRENDERER)
|
||||
if (WIN32)
|
||||
target_link_libraries(core ole32 comctl32 ws2_32 opengl32)
|
||||
else()
|
||||
elseif (APPLE)
|
||||
target_link_libraries(core "-framework OpenGL")
|
||||
else()
|
||||
target_link_libraries(core GL EGL)
|
||||
endif()
|
||||
else()
|
||||
|
@ -73,7 +73,11 @@ ConfigEntry ConfigFile[] =
|
||||
{"JIT_MaxBlockSize", 0, &JIT_MaxBlockSize, 32, NULL, 0},
|
||||
{"JIT_BranchOptimisations", 0, &JIT_BranchOptimisations, 1, NULL, 0},
|
||||
{"JIT_LiteralOptimisations", 0, &JIT_LiteralOptimisations, 1, NULL, 0},
|
||||
{"JIT_FastMemory", 0, &JIT_FastMemory, 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}
|
||||
|
55
src/DMA.cpp
55
src/DMA.cpp
@ -77,21 +77,6 @@ void DMA::Reset()
|
||||
|
||||
Running = false;
|
||||
InProgress = false;
|
||||
|
||||
if (NDS::ConsoleType == 1)
|
||||
{
|
||||
BusRead16 = (CPU==0) ? DSi::ARM9Read16 : DSi::ARM7Read16;
|
||||
BusRead32 = (CPU==0) ? DSi::ARM9Read32 : DSi::ARM7Read32;
|
||||
BusWrite16 = (CPU==0) ? DSi::ARM9Write16 : DSi::ARM7Write16;
|
||||
BusWrite32 = (CPU==0) ? DSi::ARM9Write32 : DSi::ARM7Write32;
|
||||
}
|
||||
else
|
||||
{
|
||||
BusRead16 = (CPU==0) ? NDS::ARM9Read16 : NDS::ARM7Read16;
|
||||
BusRead32 = (CPU==0) ? NDS::ARM9Read32 : NDS::ARM7Read32;
|
||||
BusWrite16 = (CPU==0) ? NDS::ARM9Write16 : NDS::ARM7Write16;
|
||||
BusWrite32 = (CPU==0) ? NDS::ARM9Write32 : NDS::ARM7Write32;
|
||||
}
|
||||
}
|
||||
|
||||
void DMA::DoSavestate(Savestate* file)
|
||||
@ -198,13 +183,7 @@ void DMA::Start()
|
||||
NDS::StopCPU(CPU, 1<<Num);
|
||||
}
|
||||
|
||||
void DMA::Run()
|
||||
{
|
||||
if (!Running) return;
|
||||
if (CPU == 0) return Run9();
|
||||
else return Run7();
|
||||
}
|
||||
|
||||
template <int ConsoleType>
|
||||
void DMA::Run9()
|
||||
{
|
||||
if (NDS::ARM9Timestamp >= NDS::ARM9Target) return;
|
||||
@ -242,7 +221,10 @@ void DMA::Run9()
|
||||
{
|
||||
NDS::ARM9Timestamp += (unitcycles << NDS::ARM9ClockShift);
|
||||
|
||||
BusWrite16(CurDstAddr, BusRead16(CurSrcAddr));
|
||||
if (ConsoleType == 1)
|
||||
DSi::ARM9Write16(CurDstAddr, DSi::ARM9Read16(CurSrcAddr));
|
||||
else
|
||||
NDS::ARM9Write16(CurDstAddr, NDS::ARM9Read16(CurSrcAddr));
|
||||
|
||||
CurSrcAddr += SrcAddrInc<<1;
|
||||
CurDstAddr += DstAddrInc<<1;
|
||||
@ -278,7 +260,10 @@ void DMA::Run9()
|
||||
{
|
||||
NDS::ARM9Timestamp += (unitcycles << NDS::ARM9ClockShift);
|
||||
|
||||
BusWrite32(CurDstAddr, BusRead32(CurSrcAddr));
|
||||
if (ConsoleType == 1)
|
||||
DSi::ARM9Write32(CurDstAddr, DSi::ARM9Read32(CurSrcAddr));
|
||||
else
|
||||
NDS::ARM9Write32(CurDstAddr, NDS::ARM9Read32(CurSrcAddr));
|
||||
|
||||
CurSrcAddr += SrcAddrInc<<2;
|
||||
CurDstAddr += DstAddrInc<<2;
|
||||
@ -317,6 +302,7 @@ void DMA::Run9()
|
||||
NDS::ResumeCPU(0, 1<<Num);
|
||||
}
|
||||
|
||||
template <int ConsoleType>
|
||||
void DMA::Run7()
|
||||
{
|
||||
if (NDS::ARM7Timestamp >= NDS::ARM7Target) return;
|
||||
@ -354,7 +340,10 @@ void DMA::Run7()
|
||||
{
|
||||
NDS::ARM7Timestamp += unitcycles;
|
||||
|
||||
BusWrite16(CurDstAddr, BusRead16(CurSrcAddr));
|
||||
if (ConsoleType == 1)
|
||||
DSi::ARM7Write16(CurDstAddr, DSi::ARM7Read16(CurSrcAddr));
|
||||
else
|
||||
NDS::ARM7Write16(CurDstAddr, NDS::ARM7Read16(CurSrcAddr));
|
||||
|
||||
CurSrcAddr += SrcAddrInc<<1;
|
||||
CurDstAddr += DstAddrInc<<1;
|
||||
@ -390,7 +379,10 @@ void DMA::Run7()
|
||||
{
|
||||
NDS::ARM7Timestamp += unitcycles;
|
||||
|
||||
BusWrite32(CurDstAddr, BusRead32(CurSrcAddr));
|
||||
if (ConsoleType == 1)
|
||||
DSi::ARM7Write32(CurDstAddr, DSi::ARM7Read32(CurSrcAddr));
|
||||
else
|
||||
NDS::ARM7Write32(CurDstAddr, NDS::ARM7Read32(CurSrcAddr));
|
||||
|
||||
CurSrcAddr += SrcAddrInc<<2;
|
||||
CurDstAddr += DstAddrInc<<2;
|
||||
@ -425,3 +417,14 @@ void DMA::Run7()
|
||||
InProgress = false;
|
||||
NDS::ResumeCPU(1, 1<<Num);
|
||||
}
|
||||
|
||||
template <int ConsoleType>
|
||||
void DMA::Run()
|
||||
{
|
||||
if (!Running) return;
|
||||
if (CPU == 0) return Run9<ConsoleType>();
|
||||
else return Run7<ConsoleType>();
|
||||
}
|
||||
|
||||
template void DMA::Run<0>();
|
||||
template void DMA::Run<1>();
|
||||
|
@ -34,9 +34,12 @@ public:
|
||||
void WriteCnt(u32 val);
|
||||
void Start();
|
||||
|
||||
template <int ConsoleType>
|
||||
void Run();
|
||||
|
||||
template <int ConsoleType>
|
||||
void Run9();
|
||||
template <int ConsoleType>
|
||||
void Run7();
|
||||
|
||||
bool IsInMode(u32 mode)
|
||||
@ -86,11 +89,6 @@ private:
|
||||
bool Stall;
|
||||
|
||||
bool IsGXFIFODMA;
|
||||
|
||||
u16 (*BusRead16)(u32 addr);
|
||||
u32 (*BusRead32)(u32 addr);
|
||||
void (*BusWrite16)(u32 addr, u16 val);
|
||||
void (*BusWrite32)(u32 addr, u32 val);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
61
src/DSi.cpp
61
src/DSi.cpp
@ -35,6 +35,7 @@
|
||||
#include "DSi_I2C.h"
|
||||
#include "DSi_SD.h"
|
||||
#include "DSi_AES.h"
|
||||
#include "DSi_Camera.h"
|
||||
|
||||
#include "tiny-AES-c/aes.hpp"
|
||||
|
||||
@ -542,15 +543,15 @@ void MapNWRAM_A(u32 num, u8 val)
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef JIT_ENABLED
|
||||
ARMJIT_Memory::RemapNWRAM(0);
|
||||
#endif
|
||||
|
||||
int mbkn = 0, mbks = 8*num;
|
||||
|
||||
u8 oldval = (MBK[0][mbkn] >> mbks) & 0xFF;
|
||||
if (oldval == val) return;
|
||||
|
||||
#ifdef JIT_ENABLED
|
||||
ARMJIT_Memory::RemapNWRAM(0);
|
||||
#endif
|
||||
|
||||
MBK[0][mbkn] &= ~(0xFF << mbks);
|
||||
MBK[0][mbkn] |= (val << mbks);
|
||||
MBK[1][mbkn] = MBK[0][mbkn];
|
||||
@ -577,15 +578,15 @@ void MapNWRAM_B(u32 num, u8 val)
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef JIT_ENABLED
|
||||
ARMJIT_Memory::RemapNWRAM(1);
|
||||
#endif
|
||||
|
||||
int mbkn = 1+(num>>2), mbks = 8*(num&3);
|
||||
|
||||
u8 oldval = (MBK[0][mbkn] >> mbks) & 0xFF;
|
||||
if (oldval == val) return;
|
||||
|
||||
#ifdef JIT_ENABLED
|
||||
ARMJIT_Memory::RemapNWRAM(1);
|
||||
#endif
|
||||
|
||||
MBK[0][mbkn] &= ~(0xFF << mbks);
|
||||
MBK[0][mbkn] |= (val << mbks);
|
||||
MBK[1][mbkn] = MBK[0][mbkn];
|
||||
@ -616,15 +617,15 @@ void MapNWRAM_C(u32 num, u8 val)
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef JIT_ENABLED
|
||||
ARMJIT_Memory::RemapNWRAM(2);
|
||||
#endif
|
||||
|
||||
int mbkn = 3+(num>>2), mbks = 8*(num&3);
|
||||
|
||||
u8 oldval = (MBK[0][mbkn] >> mbks) & 0xFF;
|
||||
if (oldval == val) return;
|
||||
|
||||
#ifdef JIT_ENABLED
|
||||
ARMJIT_Memory::RemapNWRAM(2);
|
||||
#endif
|
||||
|
||||
MBK[0][mbkn] &= ~(0xFF << mbks);
|
||||
MBK[0][mbkn] |= (val << mbks);
|
||||
MBK[1][mbkn] = MBK[0][mbkn];
|
||||
@ -1406,6 +1407,12 @@ u8 ARM9IORead8(u32 addr)
|
||||
CASE_READ8_32BIT(0x04004060, MBK[0][8])
|
||||
}
|
||||
|
||||
if ((addr & 0xFFFFFF00) == 0x04004200)
|
||||
{
|
||||
if (!(SCFG_EXT[0] & (1<<17))) return 0;
|
||||
return DSi_Camera::Read8(addr);
|
||||
}
|
||||
|
||||
return NDS::ARM9IORead8(addr);
|
||||
}
|
||||
|
||||
@ -1428,6 +1435,12 @@ u16 ARM9IORead16(u32 addr)
|
||||
CASE_READ16_32BIT(0x04004060, MBK[0][8])
|
||||
}
|
||||
|
||||
if ((addr & 0xFFFFFF00) == 0x04004200)
|
||||
{
|
||||
if (!(SCFG_EXT[0] & (1<<17))) return 0;
|
||||
return DSi_Camera::Read16(addr);
|
||||
}
|
||||
|
||||
return NDS::ARM9IORead16(addr);
|
||||
}
|
||||
|
||||
@ -1480,6 +1493,12 @@ u32 ARM9IORead32(u32 addr)
|
||||
case 0x04004170: return NDMAs[3]->Cnt;
|
||||
}
|
||||
|
||||
if ((addr & 0xFFFFFF00) == 0x04004200)
|
||||
{
|
||||
if (!(SCFG_EXT[0] & (1<<17))) return 0;
|
||||
return DSi_Camera::Read32(addr);
|
||||
}
|
||||
|
||||
return NDS::ARM9IORead32(addr);
|
||||
}
|
||||
|
||||
@ -1519,6 +1538,12 @@ void ARM9IOWrite8(u32 addr, u8 val)
|
||||
case 0x04004053: MapNWRAM_C(7, val); return;
|
||||
}
|
||||
|
||||
if ((addr & 0xFFFFFF00) == 0x04004200)
|
||||
{
|
||||
if (!(SCFG_EXT[0] & (1<<17))) return;
|
||||
return DSi_Camera::Write8(addr, val);
|
||||
}
|
||||
|
||||
return NDS::ARM9IOWrite8(addr, val);
|
||||
}
|
||||
|
||||
@ -1572,6 +1597,12 @@ void ARM9IOWrite16(u32 addr, u16 val)
|
||||
return;
|
||||
}
|
||||
|
||||
if ((addr & 0xFFFFFF00) == 0x04004200)
|
||||
{
|
||||
if (!(SCFG_EXT[0] & (1<<17))) return;
|
||||
return DSi_Camera::Write16(addr, val);
|
||||
}
|
||||
|
||||
return NDS::ARM9IOWrite16(addr, val);
|
||||
}
|
||||
|
||||
@ -1678,6 +1709,12 @@ void ARM9IOWrite32(u32 addr, u32 val)
|
||||
case 0x04004170: NDMAs[3]->WriteCnt(val); return;
|
||||
}
|
||||
|
||||
if ((addr & 0xFFFFFF00) == 0x04004200)
|
||||
{
|
||||
if (!(SCFG_EXT[0] & (1<<17))) return;
|
||||
return DSi_Camera::Write32(addr, val);
|
||||
}
|
||||
|
||||
return NDS::ARM9IOWrite32(addr, val);
|
||||
}
|
||||
|
||||
|
@ -18,12 +18,28 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "DSi.h"
|
||||
#include "DSi_Camera.h"
|
||||
|
||||
|
||||
DSi_Camera* DSi_Camera0; // 78 / facing outside
|
||||
DSi_Camera* DSi_Camera1; // 7A / selfie cam
|
||||
|
||||
u16 DSi_Camera::ModuleCnt;
|
||||
u16 DSi_Camera::Cnt;
|
||||
|
||||
u8 DSi_Camera::FrameBuffer[640*480*4];
|
||||
u32 DSi_Camera::FrameLength;
|
||||
u32 DSi_Camera::TransferPos;
|
||||
|
||||
// note on camera data/etc intervals
|
||||
// on hardware those are likely affected by several factors
|
||||
// namely, how long cameras take to process frames
|
||||
// camera IRQ is fired at roughly 15FPS with default config
|
||||
|
||||
const u32 kIRQInterval = 1120000; // ~30 FPS
|
||||
const u32 kTransferStart = 60000;
|
||||
|
||||
|
||||
bool DSi_Camera::Init()
|
||||
{
|
||||
@ -43,6 +59,87 @@ void DSi_Camera::Reset()
|
||||
{
|
||||
DSi_Camera0->ResetCam();
|
||||
DSi_Camera1->ResetCam();
|
||||
|
||||
ModuleCnt = 0; // CHECKME
|
||||
Cnt = 0;
|
||||
|
||||
memset(FrameBuffer, 0, 640*480*4);
|
||||
TransferPos = 0;
|
||||
FrameLength = 256*192*2; // TODO: make it check frame size, data type, etc
|
||||
|
||||
NDS::ScheduleEvent(NDS::Event_DSi_CamIRQ, true, kIRQInterval, IRQ, 0);
|
||||
}
|
||||
|
||||
|
||||
void DSi_Camera::IRQ(u32 param)
|
||||
{
|
||||
DSi_Camera* activecam = nullptr;
|
||||
|
||||
// TODO: check which camera has priority if both are activated
|
||||
// (or does it just jumble both data sources together, like it
|
||||
// does for, say, overlapping VRAM?)
|
||||
if (DSi_Camera0->IsActivated()) activecam = DSi_Camera0;
|
||||
else if (DSi_Camera1->IsActivated()) activecam = DSi_Camera1;
|
||||
|
||||
if (activecam)
|
||||
{
|
||||
RequestFrame(activecam->Num);
|
||||
|
||||
if (Cnt & (1<<11))
|
||||
NDS::SetIRQ(0, NDS::IRQ_DSi_Camera);
|
||||
|
||||
if (Cnt & (1<<15))
|
||||
NDS::ScheduleEvent(NDS::Event_DSi_CamTransfer, false, kTransferStart, Transfer, 0);
|
||||
}
|
||||
|
||||
NDS::ScheduleEvent(NDS::Event_DSi_CamIRQ, true, kIRQInterval, IRQ, 0);
|
||||
}
|
||||
|
||||
void DSi_Camera::RequestFrame(u32 cam)
|
||||
{
|
||||
if (!(Cnt & (1<<13))) printf("CAMERA: !! REQUESTING YUV FRAME\n");
|
||||
|
||||
// TODO: picture size, data type, cropping, etc
|
||||
// generate test pattern
|
||||
// TODO: get picture from platform (actual camera, video file, whatever source)
|
||||
for (u32 y = 0; y < 192; y++)
|
||||
{
|
||||
for (u32 x = 0; x < 256; x++)
|
||||
{
|
||||
u16* px = (u16*)&FrameBuffer[((y*256) + x) * 2];
|
||||
|
||||
if ((x & 0x8) ^ (y & 0x8))
|
||||
*px = 0x8000;
|
||||
else
|
||||
*px = 0xFC00 | ((y >> 3) << 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DSi_Camera::Transfer(u32 pos)
|
||||
{
|
||||
u32 numscan = (Cnt & 0x000F) + 1;
|
||||
u32 numpix = numscan * 256; // CHECKME
|
||||
|
||||
// TODO: present data
|
||||
//printf("CAM TRANSFER POS=%d/%d\n", pos, 0x6000*2);
|
||||
|
||||
DSi::CheckNDMAs(0, 0x0B);
|
||||
|
||||
pos += numpix;
|
||||
if (pos >= 0x6000*2) // HACK
|
||||
{
|
||||
// transfer done
|
||||
}
|
||||
else
|
||||
{
|
||||
// keep going
|
||||
|
||||
// TODO: must be tweaked such that each block has enough time to transfer
|
||||
u32 delay = numpix*2 + 16;
|
||||
|
||||
NDS::ScheduleEvent(NDS::Event_DSi_CamTransfer, false, delay, Transfer, pos);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -62,16 +159,28 @@ void DSi_Camera::ResetCam()
|
||||
RegAddr = 0;
|
||||
RegData = 0;
|
||||
|
||||
PLLCnt = 0;
|
||||
PLLDiv = 0x0366;
|
||||
PLLPDiv = 0x00F5;
|
||||
PLLCnt = 0x21F9;
|
||||
ClocksCnt = 0;
|
||||
StandbyCnt = 0x4029; // checkme
|
||||
MiscCnt = 0;
|
||||
}
|
||||
|
||||
bool DSi_Camera::IsActivated()
|
||||
{
|
||||
if (StandbyCnt & (1<<14)) return false; // standby
|
||||
if (!(MiscCnt & (1<<9))) return false; // data transfer not enabled
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void DSi_Camera::Start()
|
||||
void DSi_Camera::I2C_Start()
|
||||
{
|
||||
}
|
||||
|
||||
u8 DSi_Camera::Read(bool last)
|
||||
u8 DSi_Camera::I2C_Read(bool last)
|
||||
{
|
||||
u8 ret;
|
||||
|
||||
@ -89,7 +198,7 @@ u8 DSi_Camera::Read(bool last)
|
||||
}
|
||||
else
|
||||
{
|
||||
RegData = ReadReg(RegAddr);
|
||||
RegData = I2C_ReadReg(RegAddr);
|
||||
ret = RegData >> 8;
|
||||
}
|
||||
}
|
||||
@ -100,7 +209,7 @@ u8 DSi_Camera::Read(bool last)
|
||||
return ret;
|
||||
}
|
||||
|
||||
void DSi_Camera::Write(u8 val, bool last)
|
||||
void DSi_Camera::I2C_Write(u8 val, bool last)
|
||||
{
|
||||
if (DataPos < 2)
|
||||
{
|
||||
@ -116,7 +225,7 @@ void DSi_Camera::Write(u8 val, bool last)
|
||||
if (DataPos & 0x1)
|
||||
{
|
||||
RegData |= val;
|
||||
WriteReg(RegAddr, RegData);
|
||||
I2C_WriteReg(RegAddr, RegData);
|
||||
RegAddr += 2; // checkme
|
||||
}
|
||||
else
|
||||
@ -129,38 +238,172 @@ void DSi_Camera::Write(u8 val, bool last)
|
||||
else DataPos++;
|
||||
}
|
||||
|
||||
u16 DSi_Camera::ReadReg(u16 addr)
|
||||
u16 DSi_Camera::I2C_ReadReg(u16 addr)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case 0x0000: return 0x2280; // chip ID
|
||||
case 0x0010: return PLLDiv;
|
||||
case 0x0012: return PLLPDiv;
|
||||
case 0x0014: return PLLCnt;
|
||||
case 0x0016: return ClocksCnt;
|
||||
case 0x0018: return StandbyCnt;
|
||||
case 0x001A: return MiscCnt;
|
||||
|
||||
case 0x301A: return ((~StandbyCnt) & 0x4000) >> 12;
|
||||
}
|
||||
|
||||
//printf("DSi_Camera%d: unknown read %04X\n", Num, addr);
|
||||
if(Num==1)printf("DSi_Camera%d: unknown read %04X\n", Num, addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DSi_Camera::WriteReg(u16 addr, u16 val)
|
||||
void DSi_Camera::I2C_WriteReg(u16 addr, u16 val)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case 0x0010:
|
||||
PLLDiv = val & 0x3FFF;
|
||||
return;
|
||||
case 0x0012:
|
||||
PLLPDiv = val & 0xBFFF;
|
||||
return;
|
||||
case 0x0014:
|
||||
// shouldn't be instant either?
|
||||
val &= 0x7FFF;
|
||||
val |= ((val & 0x0002) << 14);
|
||||
PLLCnt = val;
|
||||
return;
|
||||
case 0x0016:
|
||||
ClocksCnt = val;
|
||||
printf("ClocksCnt=%04X\n", val);
|
||||
return;
|
||||
case 0x0018:
|
||||
// TODO: this shouldn't be instant, but uh
|
||||
val &= 0x003F;
|
||||
val |= ((val & 0x0001) << 14);
|
||||
StandbyCnt = val;
|
||||
printf("CAM%d STBCNT=%04X (%04X)\n", Num, StandbyCnt, val);
|
||||
return;
|
||||
case 0x001A:
|
||||
MiscCnt = val & 0x0B7B;
|
||||
printf("CAM%d MISCCNT=%04X (%04X)\n", Num, MiscCnt, val);
|
||||
return;
|
||||
}
|
||||
|
||||
//printf("DSi_Camera%d: unknown write %04X %04X\n", Num, addr, val);
|
||||
if(Num==1)printf("DSi_Camera%d: unknown write %04X %04X\n", Num, addr, val);
|
||||
}
|
||||
|
||||
|
||||
u8 DSi_Camera::Read8(u32 addr)
|
||||
{
|
||||
//
|
||||
|
||||
printf("unknown DSi cam read8 %08X\n", addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
u16 DSi_Camera::Read16(u32 addr)
|
||||
{printf("CAM READ %08X %08X\n", addr, NDS::GetPC(0));
|
||||
switch (addr)
|
||||
{
|
||||
case 0x04004200: return ModuleCnt;
|
||||
case 0x04004202: return Cnt;
|
||||
}
|
||||
|
||||
printf("unknown DSi cam read16 %08X\n", addr);
|
||||
return 0;
|
||||
}
|
||||
u32 dorp = 0;
|
||||
u32 DSi_Camera::Read32(u32 addr)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case 0x04004204:
|
||||
{
|
||||
return 0xFC00801F;
|
||||
if (!(Cnt & (1<<15))) return 0; // CHECKME
|
||||
u32 ret = *(u32*)&FrameBuffer[TransferPos];
|
||||
TransferPos += 4;
|
||||
if (TransferPos >= FrameLength) TransferPos = 0;
|
||||
dorp += 4;
|
||||
//if (dorp >= (256*4*2))
|
||||
if (TransferPos == 0)
|
||||
{
|
||||
dorp = 0;
|
||||
Cnt &= ~(1<<4);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
printf("unknown DSi cam read32 %08X\n", addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DSi_Camera::Write8(u32 addr, u8 val)
|
||||
{
|
||||
//
|
||||
|
||||
printf("unknown DSi cam write8 %08X %02X\n", addr, val);
|
||||
}
|
||||
|
||||
void DSi_Camera::Write16(u32 addr, u16 val)
|
||||
{printf("CAM WRITE %08X %04X %08X\n", addr, val, NDS::GetPC(0));
|
||||
switch (addr)
|
||||
{
|
||||
case 0x04004200:
|
||||
{
|
||||
u16 oldcnt = ModuleCnt;
|
||||
ModuleCnt = val;
|
||||
|
||||
if ((ModuleCnt & (1<<1)) && !(oldcnt & (1<<1)))
|
||||
{
|
||||
// reset shit to zero
|
||||
// CHECKME
|
||||
|
||||
Cnt = 0;
|
||||
}
|
||||
|
||||
if ((ModuleCnt & (1<<5)) && !(oldcnt & (1<<5)))
|
||||
{
|
||||
// TODO: reset I2C??
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
case 0x04004202:
|
||||
{
|
||||
// checkme
|
||||
u16 oldmask;
|
||||
if (Cnt & 0x8000)
|
||||
{
|
||||
val &= 0x8F20;
|
||||
oldmask = 0x601F;
|
||||
}
|
||||
else
|
||||
{
|
||||
val &= 0xEF2F;
|
||||
oldmask = 0x0010;
|
||||
}
|
||||
|
||||
Cnt = (Cnt & oldmask) | (val & ~0x0020);
|
||||
if (val & (1<<5)) Cnt &= ~(1<<4);
|
||||
|
||||
if ((val & (1<<15)) && !(Cnt & (1<<15)))
|
||||
{
|
||||
// start transfer
|
||||
//DSi::CheckNDMAs(0, 0x0B);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
printf("unknown DSi cam write16 %08X %04X\n", addr, val);
|
||||
}
|
||||
|
||||
void DSi_Camera::Write32(u32 addr, u32 val)
|
||||
{
|
||||
//
|
||||
|
||||
printf("unknown DSi cam write32 %08X %08X\n", addr, val);
|
||||
}
|
||||
|
@ -28,27 +28,56 @@ public:
|
||||
static void DeInit();
|
||||
static void Reset();
|
||||
|
||||
static void IRQ(u32 param);
|
||||
static void RequestFrame(u32 cam);
|
||||
|
||||
static void Transfer(u32 pos);
|
||||
|
||||
DSi_Camera(u32 num);
|
||||
~DSi_Camera();
|
||||
|
||||
void ResetCam();
|
||||
bool IsActivated();
|
||||
|
||||
void Start();
|
||||
u8 Read(bool last);
|
||||
void Write(u8 val, bool last);
|
||||
void I2C_Start();
|
||||
u8 I2C_Read(bool last);
|
||||
void I2C_Write(u8 val, bool last);
|
||||
|
||||
static u8 Read8(u32 addr);
|
||||
static u16 Read16(u32 addr);
|
||||
static u32 Read32(u32 addr);
|
||||
static void Write8(u32 addr, u8 val);
|
||||
static void Write16(u32 addr, u16 val);
|
||||
static void Write32(u32 addr, u32 val);
|
||||
|
||||
private:
|
||||
u32 Num;
|
||||
|
||||
private:
|
||||
u32 DataPos;
|
||||
u32 RegAddr;
|
||||
u16 RegData;
|
||||
|
||||
u16 ReadReg(u16 addr);
|
||||
void WriteReg(u16 addr, u16 val);
|
||||
u16 I2C_ReadReg(u16 addr);
|
||||
void I2C_WriteReg(u16 addr, u16 val);
|
||||
|
||||
u16 PLLDiv;
|
||||
u16 PLLPDiv;
|
||||
u16 PLLCnt;
|
||||
u16 ClocksCnt;
|
||||
u16 StandbyCnt;
|
||||
u16 MiscCnt;
|
||||
|
||||
u16 MCUAddr;
|
||||
u16* MCUData;
|
||||
|
||||
u8 MCURegs[0x8000];
|
||||
|
||||
static u16 ModuleCnt;
|
||||
static u16 Cnt;
|
||||
|
||||
static u8 FrameBuffer[640*480*4];
|
||||
static u32 TransferPos;
|
||||
static u32 FrameLength;
|
||||
};
|
||||
|
||||
|
||||
|
@ -50,7 +50,7 @@ void Reset()
|
||||
Registers[0x10] = 0x00; // power btn
|
||||
Registers[0x11] = 0x00; // reset
|
||||
Registers[0x12] = 0x00; // power btn tap
|
||||
Registers[0x20] = 0x83; // battery
|
||||
Registers[0x20] = 0x8F; // battery
|
||||
Registers[0x21] = 0x07;
|
||||
Registers[0x30] = 0x13;
|
||||
Registers[0x31] = 0x00; // camera power
|
||||
@ -187,8 +187,10 @@ void WriteCnt(u8 val)
|
||||
switch (Device)
|
||||
{
|
||||
case 0x4A: Data = DSi_BPTWL::Read(islast); break;
|
||||
case 0x78: Data = DSi_Camera0->Read(islast); break;
|
||||
case 0x7A: Data = DSi_Camera1->Read(islast); break;
|
||||
case 0x78: Data = DSi_Camera0->I2C_Read(islast); break;
|
||||
case 0x7A: Data = DSi_Camera1->I2C_Read(islast); break;
|
||||
case 0xA0:
|
||||
case 0xE0: Data = 0xFF; break;
|
||||
default:
|
||||
printf("I2C: read on unknown device %02X, cnt=%02X, data=%02X, last=%d\n", Device, val, 0, islast);
|
||||
Data = 0xFF;
|
||||
@ -211,8 +213,10 @@ void WriteCnt(u8 val)
|
||||
switch (Device)
|
||||
{
|
||||
case 0x4A: DSi_BPTWL::Start(); break;
|
||||
case 0x78: DSi_Camera0->Start(); break;
|
||||
case 0x7A: DSi_Camera1->Start(); break;
|
||||
case 0x78: DSi_Camera0->I2C_Start(); break;
|
||||
case 0x7A: DSi_Camera1->I2C_Start(); break;
|
||||
case 0xA0:
|
||||
case 0xE0: ack = false; break;
|
||||
default:
|
||||
printf("I2C: %s start on unknown device %02X\n", (Data&0x01)?"read":"write", Device);
|
||||
ack = false;
|
||||
@ -226,8 +230,10 @@ void WriteCnt(u8 val)
|
||||
switch (Device)
|
||||
{
|
||||
case 0x4A: DSi_BPTWL::Write(Data, islast); break;
|
||||
case 0x78: DSi_Camera0->Write(Data, islast); break;
|
||||
case 0x7A: DSi_Camera1->Write(Data, islast); break;
|
||||
case 0x78: DSi_Camera0->I2C_Write(Data, islast); break;
|
||||
case 0x7A: DSi_Camera1->I2C_Write(Data, islast); break;
|
||||
case 0xA0:
|
||||
case 0xE0: ack = false; break;
|
||||
default:
|
||||
printf("I2C: write on unknown device %02X, cnt=%02X, data=%02X, last=%d\n", Device, val, Data, islast);
|
||||
ack = false;
|
||||
|
@ -101,7 +101,7 @@ void DSi_NDMA::WriteCnt(u32 val)
|
||||
Start();
|
||||
|
||||
if (StartMode != 0x10 && StartMode != 0x30 &&
|
||||
StartMode != 0x04 && StartMode != 0x06 && StartMode != 0x07 && StartMode != 0x08 && StartMode != 0x09 &&
|
||||
StartMode != 0x04 && StartMode != 0x06 && StartMode != 0x07 && StartMode != 0x08 && StartMode != 0x09 && StartMode != 0x0B &&
|
||||
StartMode != 0x24 && StartMode != 0x26 && StartMode != 0x28 && StartMode != 0x29 && StartMode != 0x2A && StartMode != 0x2B)
|
||||
printf("UNIMPLEMENTED ARM%d NDMA%d START MODE %02X, %08X->%08X LEN=%d BLK=%d CNT=%08X\n",
|
||||
CPU?7:9, Num, StartMode, SrcAddr, DstAddr, TotalLength, BlockLength, Cnt);
|
||||
|
@ -778,6 +778,23 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
|
||||
Host->SendResponse(CSR, true);
|
||||
return;
|
||||
|
||||
case 1: // SEND_OP_COND
|
||||
// CHECKME!!
|
||||
// also TODO: it's different for the SD card
|
||||
if (Internal)
|
||||
{
|
||||
param &= ~(1<<30);
|
||||
OCR &= 0xBF000000;
|
||||
OCR |= (param & 0x40FFFFFF);
|
||||
Host->SendResponse(OCR, true);
|
||||
SetState(0x01);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("CMD1 on SD card!!\n");
|
||||
}
|
||||
return;
|
||||
|
||||
case 2:
|
||||
case 10: // get CID
|
||||
Host->SendResponse(*(u32*)&CID[12], false);
|
||||
@ -801,6 +818,11 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
|
||||
}
|
||||
return;
|
||||
|
||||
case 6: // MMC: 'SWITCH'
|
||||
// TODO!
|
||||
Host->SendResponse(CSR, true);
|
||||
return;
|
||||
|
||||
case 7: // select card (by RCA)
|
||||
Host->SendResponse(CSR, true);
|
||||
return;
|
||||
|
346
src/GPU.cpp
346
src/GPU.cpp
@ -49,8 +49,8 @@ u8 VRAM_F[ 16*1024];
|
||||
u8 VRAM_G[ 16*1024];
|
||||
u8 VRAM_H[ 32*1024];
|
||||
u8 VRAM_I[ 16*1024];
|
||||
u8* VRAM[9] = {VRAM_A, VRAM_B, VRAM_C, VRAM_D, VRAM_E, VRAM_F, VRAM_G, VRAM_H, VRAM_I};
|
||||
u32 VRAMMask[9] = {0x1FFFF, 0x1FFFF, 0x1FFFF, 0x1FFFF, 0xFFFF, 0x3FFF, 0x3FFF, 0x7FFF, 0x3FFF};
|
||||
u8* const VRAM[9] = {VRAM_A, VRAM_B, VRAM_C, VRAM_D, VRAM_E, VRAM_F, VRAM_G, VRAM_H, VRAM_I};
|
||||
u32 const VRAMMask[9] = {0x1FFFF, 0x1FFFF, 0x1FFFF, 0x1FFFF, 0xFFFF, 0x3FFF, 0x3FFF, 0x7FFF, 0x3FFF};
|
||||
|
||||
u8 VRAMCNT[9];
|
||||
u8 VRAMSTAT;
|
||||
@ -85,11 +85,67 @@ bool Accelerated;
|
||||
GPU2D* GPU2D_A;
|
||||
GPU2D* GPU2D_B;
|
||||
|
||||
/*
|
||||
VRAM invalidation tracking
|
||||
|
||||
- we want to know when a VRAM region used for graphics changed
|
||||
- for some regions unmapping is mandatory to modify them (Texture, TexPal and ExtPal) and
|
||||
we don't want to completely invalidate them every time they're unmapped and remapped
|
||||
|
||||
For this reason we don't track the dirtyness per mapping region, but instead per VRAM bank
|
||||
with VRAMDirty. Writes to LCDC go directly into VRAMDirty, while writes via other mapping regions
|
||||
like BG or OBJ are first tracked in VRAMWritten_* and need to be flushed using SyncDirtyFlags.
|
||||
|
||||
This is more or less a description of VRAMTrackingSet::DeriveState
|
||||
Each time before the memory is read two things could have happened
|
||||
to each 16kb piece (16kb is the smallest unit in which mappings can
|
||||
be made thus also the size VRAMMap_* use):
|
||||
- this piece was remapped compared to last time we checked,
|
||||
which means this location in memory is invalid.
|
||||
- this piece wasn't remapped, which means we need to check whether
|
||||
it was changed. This can be archived by checking VRAMDirty.
|
||||
VRAMDirty need to be reset for the respective VRAM bank.
|
||||
*/
|
||||
|
||||
VRAMTrackingSet<512*1024, 16*1024> VRAMDirty_ABG;
|
||||
VRAMTrackingSet<256*1024, 16*1024> VRAMDirty_AOBJ;
|
||||
VRAMTrackingSet<128*1024, 16*1024> VRAMDirty_BBG;
|
||||
VRAMTrackingSet<128*1024, 16*1024> VRAMDirty_BOBJ;
|
||||
|
||||
VRAMTrackingSet<32*1024, 8*1024> VRAMDirty_ABGExtPal;
|
||||
VRAMTrackingSet<32*1024, 8*1024> VRAMDirty_BBGExtPal;
|
||||
VRAMTrackingSet<8*1024, 8*1024> VRAMDirty_AOBJExtPal;
|
||||
VRAMTrackingSet<8*1024, 8*1024> VRAMDirty_BOBJExtPal;
|
||||
|
||||
VRAMTrackingSet<512*1024, 128*1024> VRAMDirty_Texture;
|
||||
VRAMTrackingSet<128*1024, 16*1024> VRAMDirty_TexPal;
|
||||
|
||||
|
||||
NonStupidBitField<512*1024/VRAMDirtyGranularity> VRAMWritten_ABG;
|
||||
NonStupidBitField<256*1024/VRAMDirtyGranularity> VRAMWritten_AOBJ;
|
||||
NonStupidBitField<128*1024/VRAMDirtyGranularity> VRAMWritten_BBG;
|
||||
NonStupidBitField<128*1024/VRAMDirtyGranularity> VRAMWritten_BOBJ;
|
||||
NonStupidBitField<256*1024/VRAMDirtyGranularity> VRAMWritten_ARM7;
|
||||
|
||||
NonStupidBitField<128*1024/VRAMDirtyGranularity> VRAMDirty[9];
|
||||
|
||||
u8 VRAMFlat_ABG[512*1024];
|
||||
u8 VRAMFlat_BBG[128*1024];
|
||||
u8 VRAMFlat_AOBJ[256*1024];
|
||||
u8 VRAMFlat_BOBJ[128*1024];
|
||||
|
||||
u8 VRAMFlat_ABGExtPal[32*1024];
|
||||
u8 VRAMFlat_BBGExtPal[32*1024];
|
||||
u8 VRAMFlat_AOBJExtPal[8*1024];
|
||||
u8 VRAMFlat_BOBJExtPal[8*1024];
|
||||
|
||||
u8 VRAMFlat_Texture[512*1024];
|
||||
u8 VRAMFlat_TexPal[128*1024];
|
||||
|
||||
bool Init()
|
||||
{
|
||||
GPU2D_A = new GPU2D(0);
|
||||
GPU2D_B = new GPU2D(1);
|
||||
GPU2D_A = new GPU2D_Soft(0);
|
||||
GPU2D_B = new GPU2D_Soft(1);
|
||||
if (!GPU3D::Init()) return false;
|
||||
|
||||
FrontBuffer = 0;
|
||||
@ -113,6 +169,34 @@ void DeInit()
|
||||
if (Framebuffer[1][1]) delete[] Framebuffer[1][1];
|
||||
}
|
||||
|
||||
void ResetVRAMCache()
|
||||
{
|
||||
for (int i = 0; i < 9; i++)
|
||||
VRAMDirty[i] = NonStupidBitField<128*1024/VRAMDirtyGranularity>();
|
||||
|
||||
VRAMDirty_ABG.Reset();
|
||||
VRAMDirty_BBG.Reset();
|
||||
VRAMDirty_AOBJ.Reset();
|
||||
VRAMDirty_BOBJ.Reset();
|
||||
VRAMDirty_ABGExtPal.Reset();
|
||||
VRAMDirty_BBGExtPal.Reset();
|
||||
VRAMDirty_AOBJExtPal.Reset();
|
||||
VRAMDirty_BOBJExtPal.Reset();
|
||||
VRAMDirty_Texture.Reset();
|
||||
VRAMDirty_TexPal.Reset();
|
||||
|
||||
memset(VRAMFlat_ABG, 0, sizeof(VRAMFlat_ABG));
|
||||
memset(VRAMFlat_BBG, 0, sizeof(VRAMFlat_BBG));
|
||||
memset(VRAMFlat_AOBJ, 0, sizeof(VRAMFlat_AOBJ));
|
||||
memset(VRAMFlat_BOBJ, 0, sizeof(VRAMFlat_BOBJ));
|
||||
memset(VRAMFlat_ABGExtPal, 0, sizeof(VRAMFlat_ABGExtPal));
|
||||
memset(VRAMFlat_BBGExtPal, 0, sizeof(VRAMFlat_BBGExtPal));
|
||||
memset(VRAMFlat_AOBJExtPal, 0, sizeof(VRAMFlat_AOBJExtPal));
|
||||
memset(VRAMFlat_BOBJExtPal, 0, sizeof(VRAMFlat_BOBJExtPal));
|
||||
memset(VRAMFlat_Texture, 0, sizeof(VRAMFlat_Texture));
|
||||
memset(VRAMFlat_TexPal, 0, sizeof(VRAMFlat_TexPal));
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
VCount = 0;
|
||||
@ -186,6 +270,8 @@ void Reset()
|
||||
GPU2D_B->SetFramebuffer(Framebuffer[backbuf][0]);
|
||||
|
||||
ResetRenderer();
|
||||
|
||||
ResetVRAMCache();
|
||||
}
|
||||
|
||||
void Stop()
|
||||
@ -261,6 +347,8 @@ void DoSavestate(Savestate* file)
|
||||
GPU2D_A->DoSavestate(file);
|
||||
GPU2D_B->DoSavestate(file);
|
||||
GPU3D::DoSavestate(file);
|
||||
|
||||
ResetVRAMCache();
|
||||
}
|
||||
|
||||
void AssignFramebuffers()
|
||||
@ -411,18 +499,8 @@ void SetRenderSettings(int renderer, RenderSettings& settings)
|
||||
|
||||
u8* GetUniqueBankPtr(u32 mask, u32 offset)
|
||||
{
|
||||
if (!mask) return NULL;
|
||||
|
||||
int num = 0;
|
||||
if (!(mask & 0xFF)) { mask >>= 8; num += 8; }
|
||||
else
|
||||
{
|
||||
if (!(mask & 0xF)) { mask >>= 4; num += 4; }
|
||||
if (!(mask & 0x3)) { mask >>= 2; num += 2; }
|
||||
if (!(mask & 0x1)) { mask >>= 1; num += 1; }
|
||||
}
|
||||
if (mask != 1) return NULL;
|
||||
|
||||
if (!mask || (mask & (mask - 1)) != 0) return NULL;
|
||||
int num = __builtin_ctz(mask);
|
||||
return &VRAM[num][offset & VRAMMask[num]];
|
||||
}
|
||||
|
||||
@ -606,8 +684,6 @@ void MapVRAM_E(u32 bank, u8 cnt)
|
||||
|
||||
case 4: // ABG ext palette
|
||||
UNMAP_RANGE(ABGExtPal, 0, 4);
|
||||
GPU2D_A->BGExtPalDirty(0);
|
||||
GPU2D_A->BGExtPalDirty(2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -634,8 +710,6 @@ void MapVRAM_E(u32 bank, u8 cnt)
|
||||
|
||||
case 4: // ABG ext palette
|
||||
MAP_RANGE(ABGExtPal, 0, 4);
|
||||
GPU2D_A->BGExtPalDirty(0);
|
||||
GPU2D_A->BGExtPalDirty(2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -687,12 +761,10 @@ void MapVRAM_FG(u32 bank, u8 cnt)
|
||||
case 4: // ABG ext palette
|
||||
VRAMMap_ABGExtPal[((oldofs & 0x1) << 1)] &= ~bankmask;
|
||||
VRAMMap_ABGExtPal[((oldofs & 0x1) << 1) + 1] &= ~bankmask;
|
||||
GPU2D_A->BGExtPalDirty((oldofs & 0x1) << 1);
|
||||
break;
|
||||
|
||||
case 5: // AOBJ ext palette
|
||||
VRAMMap_AOBJExtPal &= ~bankmask;
|
||||
GPU2D_A->OBJExtPalDirty();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -732,12 +804,10 @@ void MapVRAM_FG(u32 bank, u8 cnt)
|
||||
case 4: // ABG ext palette
|
||||
VRAMMap_ABGExtPal[((ofs & 0x1) << 1)] |= bankmask;
|
||||
VRAMMap_ABGExtPal[((ofs & 0x1) << 1) + 1] |= bankmask;
|
||||
GPU2D_A->BGExtPalDirty((ofs & 0x1) << 1);
|
||||
break;
|
||||
|
||||
case 5: // AOBJ ext palette
|
||||
VRAMMap_AOBJExtPal |= bankmask;
|
||||
GPU2D_A->OBJExtPalDirty();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -773,8 +843,6 @@ void MapVRAM_H(u32 bank, u8 cnt)
|
||||
|
||||
case 2: // BBG ext palette
|
||||
UNMAP_RANGE(BBGExtPal, 0, 4);
|
||||
GPU2D_B->BGExtPalDirty(0);
|
||||
GPU2D_B->BGExtPalDirty(2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -800,8 +868,6 @@ void MapVRAM_H(u32 bank, u8 cnt)
|
||||
|
||||
case 2: // BBG ext palette
|
||||
MAP_RANGE(BBGExtPal, 0, 4);
|
||||
GPU2D_B->BGExtPalDirty(0);
|
||||
GPU2D_B->BGExtPalDirty(2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -841,7 +907,6 @@ void MapVRAM_I(u32 bank, u8 cnt)
|
||||
|
||||
case 3: // BOBJ ext palette
|
||||
VRAMMap_BOBJExtPal &= ~bankmask;
|
||||
GPU2D_B->OBJExtPalDirty();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -871,7 +936,6 @@ void MapVRAM_I(u32 bank, u8 cnt)
|
||||
|
||||
case 3: // BOBJ ext palette
|
||||
VRAMMap_BOBJExtPal |= bankmask;
|
||||
GPU2D_B->OBJExtPalDirty();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -937,6 +1001,8 @@ void StartHBlank(u32 line)
|
||||
DispStat[0] |= (1<<1);
|
||||
DispStat[1] |= (1<<1);
|
||||
|
||||
SyncDirtyFlags();
|
||||
|
||||
if (VCount < 192)
|
||||
{
|
||||
// draw
|
||||
@ -1096,4 +1162,224 @@ void SetVCount(u16 val)
|
||||
NextVCount = val;
|
||||
}
|
||||
|
||||
template <u32 Size, u32 MappingGranularity>
|
||||
NonStupidBitField<Size/VRAMDirtyGranularity> VRAMTrackingSet<Size, MappingGranularity>::DeriveState(u32* currentMappings)
|
||||
{
|
||||
NonStupidBitField<Size/VRAMDirtyGranularity> result;
|
||||
u16 banksToBeZeroed = 0;
|
||||
for (u32 i = 0; i < Size / MappingGranularity; i++)
|
||||
{
|
||||
if (currentMappings[i] != Mapping[i])
|
||||
{
|
||||
result |= NonStupidBitField<Size/VRAMDirtyGranularity>(i*VRAMBitsPerMapping, VRAMBitsPerMapping);
|
||||
banksToBeZeroed |= currentMappings[i];
|
||||
Mapping[i] = currentMappings[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 mapping = Mapping[i];
|
||||
|
||||
banksToBeZeroed |= mapping;
|
||||
|
||||
while (mapping != 0)
|
||||
{
|
||||
u32 num = __builtin_ctz(mapping);
|
||||
mapping &= ~(1 << num);
|
||||
|
||||
// hack for **speed**
|
||||
// this could probably be done less ugly but then we would rely
|
||||
// on the compiler for vectorisation
|
||||
static_assert(VRAMDirtyGranularity == 512);
|
||||
if (MappingGranularity == 16*1024)
|
||||
{
|
||||
u32 dirty = ((u32*)VRAMDirty[num].Data)[i & (VRAMMask[num] >> 14)];
|
||||
((u32*)result.Data)[i] |= dirty;
|
||||
}
|
||||
else if (MappingGranularity == 8*1024)
|
||||
{
|
||||
u16 dirty = ((u16*)VRAMDirty[num].Data)[i & (VRAMMask[num] >> 13)];
|
||||
((u16*)result.Data)[i] |= dirty;
|
||||
}
|
||||
else if (MappingGranularity == 128*1024)
|
||||
{
|
||||
((u64*)result.Data)[i * 4 + 0] |= ((u64*)VRAMDirty[num].Data)[0];
|
||||
((u64*)result.Data)[i * 4 + 1] |= ((u64*)VRAMDirty[num].Data)[1];
|
||||
((u64*)result.Data)[i * 4 + 2] |= ((u64*)VRAMDirty[num].Data)[2];
|
||||
((u64*)result.Data)[i * 4 + 3] |= ((u64*)VRAMDirty[num].Data)[3];
|
||||
}
|
||||
else
|
||||
{
|
||||
// welp
|
||||
abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (banksToBeZeroed != 0)
|
||||
{
|
||||
u32 num = __builtin_ctz(banksToBeZeroed);
|
||||
banksToBeZeroed &= ~(1 << num);
|
||||
memset(VRAMDirty[num].Data, 0, sizeof(VRAMDirty[num].Data));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template NonStupidBitField<32*1024/VRAMDirtyGranularity> VRAMTrackingSet<32*1024, 8*1024>::DeriveState(u32*);
|
||||
template NonStupidBitField<8*1024/VRAMDirtyGranularity> VRAMTrackingSet<8*1024, 8*1024>::DeriveState(u32*);
|
||||
template NonStupidBitField<512*1024/VRAMDirtyGranularity> VRAMTrackingSet<512*1024, 128*1024>::DeriveState(u32*);
|
||||
template NonStupidBitField<128*1024/VRAMDirtyGranularity> VRAMTrackingSet<128*1024, 16*1024>::DeriveState(u32*);
|
||||
template NonStupidBitField<256*1024/VRAMDirtyGranularity> VRAMTrackingSet<256*1024, 16*1024>::DeriveState(u32*);
|
||||
template NonStupidBitField<512*1024/VRAMDirtyGranularity> VRAMTrackingSet<512*1024, 16*1024>::DeriveState(u32*);
|
||||
|
||||
template <u32 Size>
|
||||
void SyncDirtyFlags(u32* mappings, NonStupidBitField<Size>& writtenFlags)
|
||||
{
|
||||
const u32 VRAMWrittenBitsPer16KB = 16*1024/VRAMDirtyGranularity;
|
||||
|
||||
for (typename NonStupidBitField<Size>::Iterator it = writtenFlags.Begin(); it != writtenFlags.End(); it++)
|
||||
{
|
||||
u32 mapping = mappings[*it / VRAMWrittenBitsPer16KB];
|
||||
while (mapping != 0)
|
||||
{
|
||||
u32 num = __builtin_ctz(mapping);
|
||||
|
||||
VRAMDirty[num][*it & (VRAMMask[num] / VRAMDirtyGranularity)] = true;
|
||||
|
||||
mapping &= ~(1 << num);
|
||||
}
|
||||
}
|
||||
memset(writtenFlags.Data, 0, sizeof(writtenFlags.Data));
|
||||
}
|
||||
|
||||
void SyncDirtyFlags()
|
||||
{
|
||||
SyncDirtyFlags(VRAMMap_ABG, VRAMWritten_ABG);
|
||||
SyncDirtyFlags(VRAMMap_AOBJ, VRAMWritten_AOBJ);
|
||||
SyncDirtyFlags(VRAMMap_BBG, VRAMWritten_BBG);
|
||||
SyncDirtyFlags(VRAMMap_BOBJ, VRAMWritten_BOBJ);
|
||||
SyncDirtyFlags(VRAMMap_ARM7, VRAMWritten_ARM7);
|
||||
}
|
||||
|
||||
template <u32 MappingGranularity, u32 Size>
|
||||
inline bool CopyLinearVRAM(u8* flat, u32* mappings, NonStupidBitField<Size>& dirty, u64 (*slowAccess)(u32 addr))
|
||||
{
|
||||
const u32 VRAMBitsPerMapping = MappingGranularity / VRAMDirtyGranularity;
|
||||
|
||||
bool change = false;
|
||||
|
||||
typename NonStupidBitField<Size>::Iterator it = dirty.Begin();
|
||||
while (it != dirty.End())
|
||||
{
|
||||
u32 offset = *it * VRAMDirtyGranularity;
|
||||
u8* dst = flat + offset;
|
||||
u8* fastAccess = GetUniqueBankPtr(mappings[*it / VRAMBitsPerMapping], offset);
|
||||
if (fastAccess)
|
||||
{
|
||||
memcpy(dst, fastAccess, VRAMDirtyGranularity);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (u32 i = 0; i < VRAMDirtyGranularity; i += 8)
|
||||
*(u64*)&dst[i] = slowAccess(offset + i);
|
||||
}
|
||||
change = true;
|
||||
it++;
|
||||
}
|
||||
return change;
|
||||
}
|
||||
|
||||
bool MakeVRAMFlat_TextureCoherent(NonStupidBitField<512*1024/VRAMDirtyGranularity>& dirty)
|
||||
{
|
||||
return CopyLinearVRAM<128*1024>(VRAMFlat_Texture, VRAMMap_Texture, dirty, ReadVRAM_Texture<u64>);
|
||||
}
|
||||
bool MakeVRAMFlat_TexPalCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty)
|
||||
{
|
||||
return CopyLinearVRAM<16*1024>(VRAMFlat_TexPal, VRAMMap_TexPal, dirty, ReadVRAM_TexPal<u64>);
|
||||
}
|
||||
|
||||
bool MakeVRAMFlat_ABGCoherent(NonStupidBitField<512*1024/VRAMDirtyGranularity>& dirty)
|
||||
{
|
||||
return CopyLinearVRAM<16*1024>(VRAMFlat_ABG, VRAMMap_ABG, dirty, ReadVRAM_ABG<u64>);
|
||||
}
|
||||
bool MakeVRAMFlat_BBGCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty)
|
||||
{
|
||||
return CopyLinearVRAM<16*1024>(VRAMFlat_BBG, VRAMMap_BBG, dirty, ReadVRAM_BBG<u64>);
|
||||
}
|
||||
|
||||
bool MakeVRAMFlat_AOBJCoherent(NonStupidBitField<256*1024/VRAMDirtyGranularity>& dirty)
|
||||
{
|
||||
return CopyLinearVRAM<16*1024>(VRAMFlat_AOBJ, VRAMMap_AOBJ, dirty, ReadVRAM_AOBJ<u64>);
|
||||
}
|
||||
bool MakeVRAMFlat_BOBJCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty)
|
||||
{
|
||||
return CopyLinearVRAM<16*1024>(VRAMFlat_BOBJ, VRAMMap_BOBJ, dirty, ReadVRAM_BOBJ<u64>);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T ReadVRAM_ABGExtPal(u32 addr)
|
||||
{
|
||||
u32 mask = VRAMMap_ABGExtPal[(addr >> 13) & 0x3];
|
||||
|
||||
T ret = 0;
|
||||
if (mask & (1<<4)) ret |= *(T*)&VRAM_E[addr & 0x7FFF];
|
||||
if (mask & (1<<5)) ret |= *(T*)&VRAM_F[addr & 0x3FFF];
|
||||
if (mask & (1<<6)) ret |= *(T*)&VRAM_G[addr & 0x3FFF];
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T ReadVRAM_BBGExtPal(u32 addr)
|
||||
{
|
||||
u32 mask = VRAMMap_BBGExtPal[(addr >> 13) & 0x3];
|
||||
|
||||
T ret = 0;
|
||||
if (mask & (1<<7)) ret |= *(T*)&VRAM_H[addr & 0x7FFF];
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T ReadVRAM_AOBJExtPal(u32 addr)
|
||||
{
|
||||
u32 mask = VRAMMap_AOBJExtPal;
|
||||
|
||||
T ret = 0;
|
||||
if (mask & (1<<4)) ret |= *(T*)&VRAM_F[addr & 0x1FFF];
|
||||
if (mask & (1<<5)) ret |= *(T*)&VRAM_G[addr & 0x1FFF];
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T ReadVRAM_BOBJExtPal(u32 addr)
|
||||
{
|
||||
u32 mask = VRAMMap_BOBJExtPal;
|
||||
|
||||
T ret = 0;
|
||||
if (mask & (1<<8)) ret |= *(T*)&VRAM_I[addr & 0x1FFF];
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool MakeVRAMFlat_ABGExtPalCoherent(NonStupidBitField<32*1024/VRAMDirtyGranularity>& dirty)
|
||||
{
|
||||
return CopyLinearVRAM<8*1024>(VRAMFlat_ABGExtPal, VRAMMap_ABGExtPal, dirty, ReadVRAM_ABGExtPal<u64>);
|
||||
}
|
||||
bool MakeVRAMFlat_BBGExtPalCoherent(NonStupidBitField<32*1024/VRAMDirtyGranularity>& dirty)
|
||||
{
|
||||
return CopyLinearVRAM<8*1024>(VRAMFlat_BBGExtPal, VRAMMap_BBGExtPal, dirty, ReadVRAM_BBGExtPal<u64>);
|
||||
}
|
||||
|
||||
bool MakeVRAMFlat_AOBJExtPalCoherent(NonStupidBitField<8*1024/VRAMDirtyGranularity>& dirty)
|
||||
{
|
||||
return CopyLinearVRAM<8*1024>(VRAMFlat_AOBJExtPal, &VRAMMap_AOBJExtPal, dirty, ReadVRAM_AOBJExtPal<u64>);
|
||||
}
|
||||
bool MakeVRAMFlat_BOBJExtPalCoherent(NonStupidBitField<8*1024/VRAMDirtyGranularity>& dirty)
|
||||
{
|
||||
return CopyLinearVRAM<8*1024>(VRAMFlat_BOBJExtPal, &VRAMMap_BOBJExtPal, dirty, ReadVRAM_BOBJExtPal<u64>);
|
||||
}
|
||||
|
||||
}
|
92
src/GPU.h
92
src/GPU.h
@ -20,6 +20,7 @@
|
||||
#define GPU_H
|
||||
|
||||
#include "GPU2D.h"
|
||||
#include "NonStupidBitfield.h"
|
||||
|
||||
namespace GPU
|
||||
{
|
||||
@ -45,7 +46,7 @@ extern u8 VRAM_G[ 16*1024];
|
||||
extern u8 VRAM_H[ 32*1024];
|
||||
extern u8 VRAM_I[ 16*1024];
|
||||
|
||||
extern u8* VRAM[9];
|
||||
extern u8* const VRAM[9];
|
||||
|
||||
extern u32 VRAMMap_LCDC;
|
||||
extern u32 VRAMMap_ABG[0x20];
|
||||
@ -73,6 +74,78 @@ extern GPU2D* GPU2D_B;
|
||||
|
||||
extern int Renderer;
|
||||
|
||||
const u32 VRAMDirtyGranularity = 512;
|
||||
|
||||
extern NonStupidBitField<512*1024/VRAMDirtyGranularity> VRAMWritten_ABG;
|
||||
extern NonStupidBitField<256*1024/VRAMDirtyGranularity> VRAMWritten_AOBJ;
|
||||
extern NonStupidBitField<128*1024/VRAMDirtyGranularity> VRAMWritten_BBG;
|
||||
extern NonStupidBitField<128*1024/VRAMDirtyGranularity> VRAMWritten_BOBJ;
|
||||
extern NonStupidBitField<256*1024/VRAMDirtyGranularity> VRAMWritten_ARM7;
|
||||
|
||||
extern NonStupidBitField<128*1024/VRAMDirtyGranularity> VRAMDirty[9];
|
||||
|
||||
template <u32 Size, u32 MappingGranularity>
|
||||
struct VRAMTrackingSet
|
||||
{
|
||||
u16 Mapping[Size / MappingGranularity];
|
||||
|
||||
const u32 VRAMBitsPerMapping = MappingGranularity / VRAMDirtyGranularity;
|
||||
|
||||
void Reset()
|
||||
{
|
||||
for (int i = 0; i < Size / MappingGranularity; i++)
|
||||
{
|
||||
// this is not a real VRAM bank
|
||||
// so it will always be a mismatch => the bank will be completely invalidated
|
||||
Mapping[i] = 0x8000;
|
||||
}
|
||||
}
|
||||
NonStupidBitField<Size/VRAMDirtyGranularity> DeriveState(u32* currentMappings);
|
||||
};
|
||||
|
||||
extern VRAMTrackingSet<512*1024, 16*1024> VRAMDirty_ABG;
|
||||
extern VRAMTrackingSet<256*1024, 16*1024> VRAMDirty_AOBJ;
|
||||
extern VRAMTrackingSet<128*1024, 16*1024> VRAMDirty_BBG;
|
||||
extern VRAMTrackingSet<128*1024, 16*1024> VRAMDirty_BOBJ;
|
||||
|
||||
extern VRAMTrackingSet<32*1024, 8*1024> VRAMDirty_ABGExtPal;
|
||||
extern VRAMTrackingSet<32*1024, 8*1024> VRAMDirty_BBGExtPal;
|
||||
extern VRAMTrackingSet<8*1024, 8*1024> VRAMDirty_AOBJExtPal;
|
||||
extern VRAMTrackingSet<8*1024, 8*1024> VRAMDirty_BOBJExtPal;
|
||||
|
||||
extern VRAMTrackingSet<512*1024, 128*1024> VRAMDirty_Texture;
|
||||
extern VRAMTrackingSet<128*1024, 16*1024> VRAMDirty_TexPal;
|
||||
|
||||
extern u8 VRAMFlat_ABG[512*1024];
|
||||
extern u8 VRAMFlat_BBG[128*1024];
|
||||
extern u8 VRAMFlat_AOBJ[256*1024];
|
||||
extern u8 VRAMFlat_BOBJ[128*1024];
|
||||
|
||||
extern u8 VRAMFlat_ABGExtPal[32*1024];
|
||||
extern u8 VRAMFlat_BBGExtPal[32*1024];
|
||||
|
||||
extern u8 VRAMFlat_AOBJExtPal[8*1024];
|
||||
extern u8 VRAMFlat_BOBJExtPal[8*1024];
|
||||
|
||||
extern u8 VRAMFlat_Texture[512*1024];
|
||||
extern u8 VRAMFlat_TexPal[128*1024];
|
||||
|
||||
bool MakeVRAMFlat_ABGCoherent(NonStupidBitField<512*1024/VRAMDirtyGranularity>& dirty);
|
||||
bool MakeVRAMFlat_BBGCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty);
|
||||
|
||||
bool MakeVRAMFlat_AOBJCoherent(NonStupidBitField<256*1024/VRAMDirtyGranularity>& dirty);
|
||||
bool MakeVRAMFlat_BOBJCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty);
|
||||
|
||||
bool MakeVRAMFlat_ABGExtPalCoherent(NonStupidBitField<32*1024/VRAMDirtyGranularity>& dirty);
|
||||
bool MakeVRAMFlat_BBGExtPalCoherent(NonStupidBitField<32*1024/VRAMDirtyGranularity>& dirty);
|
||||
|
||||
bool MakeVRAMFlat_AOBJExtPalCoherent(NonStupidBitField<8*1024/VRAMDirtyGranularity>& dirty);
|
||||
bool MakeVRAMFlat_BOBJExtPalCoherent(NonStupidBitField<8*1024/VRAMDirtyGranularity>& dirty);
|
||||
|
||||
bool MakeVRAMFlat_TextureCoherent(NonStupidBitField<512*1024/VRAMDirtyGranularity>& dirty);
|
||||
bool MakeVRAMFlat_TexPalCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty);
|
||||
|
||||
void SyncDirtyFlags();
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@ -233,7 +306,11 @@ void WriteVRAM_LCDC(u32 addr, T val)
|
||||
default: return;
|
||||
}
|
||||
|
||||
if (VRAMMap_LCDC & (1<<bank)) *(T*)&VRAM[bank][addr] = val;
|
||||
if (VRAMMap_LCDC & (1<<bank))
|
||||
{
|
||||
*(T*)&VRAM[bank][addr] = val;
|
||||
VRAMDirty[bank][addr / VRAMDirtyGranularity] = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -262,6 +339,8 @@ void WriteVRAM_ABG(u32 addr, T val)
|
||||
{
|
||||
u32 mask = VRAMMap_ABG[(addr >> 14) & 0x1F];
|
||||
|
||||
VRAMWritten_ABG[(addr & 0x7FFFF) / VRAMDirtyGranularity] = true;
|
||||
|
||||
if (mask & (1<<0)) *(T*)&VRAM_A[addr & 0x1FFFF] = val;
|
||||
if (mask & (1<<1)) *(T*)&VRAM_B[addr & 0x1FFFF] = val;
|
||||
if (mask & (1<<2)) *(T*)&VRAM_C[addr & 0x1FFFF] = val;
|
||||
@ -295,6 +374,8 @@ void WriteVRAM_AOBJ(u32 addr, T val)
|
||||
{
|
||||
u32 mask = VRAMMap_AOBJ[(addr >> 14) & 0xF];
|
||||
|
||||
VRAMWritten_AOBJ[(addr & 0x3FFFF) / VRAMDirtyGranularity] = true;
|
||||
|
||||
if (mask & (1<<0)) *(T*)&VRAM_A[addr & 0x1FFFF] = val;
|
||||
if (mask & (1<<1)) *(T*)&VRAM_B[addr & 0x1FFFF] = val;
|
||||
if (mask & (1<<4)) *(T*)&VRAM_E[addr & 0xFFFF] = val;
|
||||
@ -324,6 +405,8 @@ void WriteVRAM_BBG(u32 addr, T val)
|
||||
{
|
||||
u32 mask = VRAMMap_BBG[(addr >> 14) & 0x7];
|
||||
|
||||
VRAMWritten_BBG[(addr & 0x1FFFF) / VRAMDirtyGranularity] = true;
|
||||
|
||||
if (mask & (1<<2)) *(T*)&VRAM_C[addr & 0x1FFFF] = val;
|
||||
if (mask & (1<<7)) *(T*)&VRAM_H[addr & 0x7FFF] = val;
|
||||
if (mask & (1<<8)) *(T*)&VRAM_I[addr & 0x3FFF] = val;
|
||||
@ -350,11 +433,12 @@ void WriteVRAM_BOBJ(u32 addr, T val)
|
||||
{
|
||||
u32 mask = VRAMMap_BOBJ[(addr >> 14) & 0x7];
|
||||
|
||||
VRAMWritten_BOBJ[(addr & 0x1FFFF) / VRAMDirtyGranularity] = true;
|
||||
|
||||
if (mask & (1<<3)) *(T*)&VRAM_D[addr & 0x1FFFF] = val;
|
||||
if (mask & (1<<8)) *(T*)&VRAM_I[addr & 0x3FFF] = val;
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
T ReadVRAM_ARM7(u32 addr)
|
||||
{
|
||||
@ -372,6 +456,8 @@ void WriteVRAM_ARM7(u32 addr, T val)
|
||||
{
|
||||
u32 mask = VRAMMap_ARM7[(addr >> 17) & 0x1];
|
||||
|
||||
VRAMWritten_ARM7[(addr & 0x1FFFF) / VRAMDirtyGranularity] = true;
|
||||
|
||||
if (mask & (1<<2)) *(T*)&VRAM_C[addr & 0x1FFFF] = val;
|
||||
if (mask & (1<<3)) *(T*)&VRAM_D[addr & 0x1FFFF] = val;
|
||||
}
|
||||
|
2270
src/GPU2D.cpp
2270
src/GPU2D.cpp
File diff suppressed because it is too large
Load Diff
96
src/GPU2D.h
96
src/GPU2D.h
@ -19,11 +19,14 @@
|
||||
#ifndef GPU2D_H
|
||||
#define GPU2D_H
|
||||
|
||||
#include "types.h"
|
||||
#include "Savestate.h"
|
||||
|
||||
class GPU2D
|
||||
{
|
||||
public:
|
||||
GPU2D(u32 num);
|
||||
~GPU2D();
|
||||
virtual ~GPU2D() {}
|
||||
|
||||
void Reset();
|
||||
|
||||
@ -31,7 +34,7 @@ public:
|
||||
|
||||
void SetEnabled(bool enable) { Enabled = enable; }
|
||||
void SetFramebuffer(u32* buf);
|
||||
void SetRenderSettings(bool accel);
|
||||
virtual void SetRenderSettings(bool accel) = 0;
|
||||
|
||||
u8 Read8(u32 addr);
|
||||
u16 Read16(u32 addr);
|
||||
@ -52,36 +55,24 @@ public:
|
||||
|
||||
void SampleFIFO(u32 offset, u32 num);
|
||||
|
||||
void DrawScanline(u32 line);
|
||||
void DrawSprites(u32 line);
|
||||
virtual void DrawScanline(u32 line) = 0;
|
||||
virtual void DrawSprites(u32 line) = 0;
|
||||
void VBlank();
|
||||
void VBlankEnd();
|
||||
virtual void VBlankEnd();
|
||||
|
||||
void CheckWindows(u32 line);
|
||||
|
||||
void BGExtPalDirty(u32 base);
|
||||
void OBJExtPalDirty();
|
||||
|
||||
u16* GetBGExtPal(u32 slot, u32 pal);
|
||||
u16* GetOBJExtPal();
|
||||
|
||||
private:
|
||||
void GetBGVRAM(u8*& data, u32& mask);
|
||||
void GetOBJVRAM(u8*& data, u32& mask);
|
||||
|
||||
protected:
|
||||
u32 Num;
|
||||
bool Enabled;
|
||||
u32* Framebuffer;
|
||||
|
||||
bool Accelerated;
|
||||
|
||||
u32 BGOBJLine[256*3] __attribute__((aligned (8)));
|
||||
u32* _3DLine;
|
||||
|
||||
u8 WindowMask[256] __attribute__((aligned (8)));
|
||||
u32 OBJLine[256] __attribute__((aligned (8)));
|
||||
u8 OBJWindow[256] __attribute__((aligned (8)));
|
||||
u8 OBJIndex[256] __attribute__((aligned (8)));
|
||||
|
||||
u32 NumSprites;
|
||||
|
||||
u16 DispFIFO[16];
|
||||
u32 DispFIFOReadPtr;
|
||||
u32 DispFIFOWritePtr;
|
||||
@ -114,32 +105,61 @@ private:
|
||||
u8 BGMosaicY, BGMosaicYMax;
|
||||
u8 OBJMosaicYCount, OBJMosaicY, OBJMosaicYMax;
|
||||
|
||||
u8 MosaicTable[16][256];
|
||||
u8* CurBGXMosaicTable;
|
||||
u8* CurOBJXMosaicTable;
|
||||
|
||||
u16 BlendCnt;
|
||||
u16 BlendAlpha;
|
||||
u8 EVA, EVB;
|
||||
u8 EVY;
|
||||
|
||||
bool CaptureLatch;
|
||||
u32 CaptureCnt;
|
||||
|
||||
u16 MasterBrightness;
|
||||
|
||||
u16 BGExtPalCache[4][16*256];
|
||||
u16 OBJExtPalCache[16*256];
|
||||
u32 BGExtPalStatus[4];
|
||||
u32 OBJExtPalStatus;
|
||||
u8 WindowMask[256] __attribute__((aligned (8)));
|
||||
u8 OBJWindow[256] __attribute__((aligned (8)));
|
||||
|
||||
void UpdateMosaicCounters(u32 line);
|
||||
void CalculateWindowMask(u32 line);
|
||||
|
||||
virtual void MosaicXSizeChanged() = 0;
|
||||
};
|
||||
|
||||
class GPU2D_Soft : public GPU2D
|
||||
{
|
||||
public:
|
||||
GPU2D_Soft(u32 num);
|
||||
~GPU2D_Soft() override {}
|
||||
|
||||
void SetRenderSettings(bool accel) override;
|
||||
|
||||
void DrawScanline(u32 line) override;
|
||||
void DrawSprites(u32 line) override;
|
||||
void VBlankEnd() override;
|
||||
|
||||
protected:
|
||||
void MosaicXSizeChanged() override;
|
||||
|
||||
private:
|
||||
bool Accelerated;
|
||||
|
||||
u32 BGOBJLine[256*3] __attribute__((aligned (8)));
|
||||
u32* _3DLine;
|
||||
|
||||
u32 OBJLine[256] __attribute__((aligned (8)));
|
||||
u8 OBJIndex[256] __attribute__((aligned (8)));
|
||||
|
||||
u32 NumSprites;
|
||||
|
||||
u8 MosaicTable[16][256];
|
||||
u8* CurBGXMosaicTable;
|
||||
u8* CurOBJXMosaicTable;
|
||||
|
||||
u32 ColorBlend4(u32 val1, u32 val2, u32 eva, u32 evb);
|
||||
u32 ColorBlend5(u32 val1, u32 val2);
|
||||
u32 ColorBrightnessUp(u32 val, u32 factor);
|
||||
u32 ColorBrightnessDown(u32 val, u32 factor);
|
||||
u32 ColorComposite(int i, u32 val1, u32 val2);
|
||||
|
||||
void UpdateMosaicCounters(u32 line);
|
||||
|
||||
template<u32 bgmode> void DrawScanlineBGMode(u32 line);
|
||||
void DrawScanlineBGMode6(u32 line);
|
||||
void DrawScanlineBGMode7(u32 line);
|
||||
@ -147,22 +167,22 @@ private:
|
||||
|
||||
static void DrawPixel_Normal(u32* dst, u16 color, u32 flag);
|
||||
static void DrawPixel_Accel(u32* dst, u16 color, u32 flag);
|
||||
void (*DrawPixel)(u32* dst, u16 color, u32 flag);
|
||||
|
||||
typedef void (*DrawPixel)(u32* dst, u16 color, u32 flag);
|
||||
|
||||
void DrawBG_3D();
|
||||
template<bool mosaic> void DrawBG_Text(u32 line, u32 bgnum);
|
||||
template<bool mosaic> void DrawBG_Affine(u32 line, u32 bgnum);
|
||||
template<bool mosaic> void DrawBG_Extended(u32 line, u32 bgnum);
|
||||
template<bool mosaic> void DrawBG_Large(u32 line);
|
||||
template<bool mosaic, DrawPixel drawPixel> void DrawBG_Text(u32 line, u32 bgnum);
|
||||
template<bool mosaic, DrawPixel drawPixel> void DrawBG_Affine(u32 line, u32 bgnum);
|
||||
template<bool mosaic, DrawPixel drawPixel> void DrawBG_Extended(u32 line, u32 bgnum);
|
||||
template<bool mosaic, DrawPixel drawPixel> void DrawBG_Large(u32 line);
|
||||
|
||||
void ApplySpriteMosaicX();
|
||||
template<DrawPixel drawPixel>
|
||||
void InterleaveSprites(u32 prio);
|
||||
template<bool window> void DrawSprite_Rotscale(u32 num, u32 boundwidth, u32 boundheight, u32 width, u32 height, s32 xpos, s32 ypos);
|
||||
template<bool window> void DrawSprite_Normal(u32 num, u32 width, u32 height, s32 xpos, s32 ypos);
|
||||
|
||||
void DoCapture(u32 line, u32 width);
|
||||
|
||||
void CalculateWindowMask(u32 line);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
2215
src/GPU2D_Soft.cpp
Normal file
2215
src/GPU2D_Soft.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -179,6 +179,10 @@ u8 RenderFogDensityTable[34];
|
||||
|
||||
u32 RenderClearAttr1, RenderClearAttr2;
|
||||
|
||||
bool RenderFrameIdentical;
|
||||
|
||||
u16 RenderXPos;
|
||||
|
||||
u32 ZeroDotWLimit;
|
||||
|
||||
u32 GXStat;
|
||||
@ -383,6 +387,8 @@ void Reset()
|
||||
FlushAttributes = 0;
|
||||
|
||||
ResetRenderingState();
|
||||
|
||||
RenderXPos = 0;
|
||||
}
|
||||
|
||||
void DoSavestate(Savestate* file)
|
||||
@ -428,6 +434,8 @@ void DoSavestate(Savestate* file)
|
||||
file->Var32(&RenderClearAttr1);
|
||||
file->Var32(&RenderClearAttr2);
|
||||
|
||||
file->Var16(&RenderXPos);
|
||||
|
||||
file->Var32(&ZeroDotWLimit);
|
||||
|
||||
file->Var32(&GXStat);
|
||||
@ -585,8 +593,6 @@ void DoSavestate(Savestate* file)
|
||||
}
|
||||
}
|
||||
|
||||
// probably not worth storing the vblank-latched Renderxxxxxx variables
|
||||
|
||||
CmdStallQueue->DoSavestate(file);
|
||||
file->Var32((u32*)&VertexPipeline);
|
||||
file->Var32((u32*)&NormalPipeline);
|
||||
@ -606,6 +612,22 @@ void DoSavestate(Savestate* file)
|
||||
// might cause a blank frame but atleast it won't shit itself
|
||||
RenderNumPolygons = 0;
|
||||
}
|
||||
|
||||
file->VarArray(CurVertex, sizeof(s16)*3);
|
||||
file->VarArray(VertexColor, sizeof(u8)*3);
|
||||
file->VarArray(TexCoords, sizeof(s16)*2);
|
||||
file->VarArray(RawTexCoords, sizeof(s16)*2);
|
||||
file->VarArray(Normal, sizeof(s16)*3);
|
||||
|
||||
file->VarArray(LightDirection, sizeof(s16)*4*3);
|
||||
file->VarArray(LightColor, sizeof(u8)*4*3);
|
||||
file->VarArray(MatDiffuse, sizeof(u8)*3);
|
||||
file->VarArray(MatAmbient, sizeof(u8)*3);
|
||||
file->VarArray(MatSpecular, sizeof(u8)*3);
|
||||
file->VarArray(MatEmission, sizeof(u8)*3);
|
||||
|
||||
file->Bool32(&UseShininessTable);
|
||||
file->VarArray(ShininessTable, 128*sizeof(u8));
|
||||
}
|
||||
|
||||
|
||||
@ -2491,6 +2513,19 @@ void VBlank()
|
||||
}
|
||||
|
||||
RenderNumPolygons = NumPolygons;
|
||||
RenderFrameIdentical = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
RenderFrameIdentical = RenderDispCnt == DispCnt
|
||||
&& RenderAlphaRef == AlphaRef
|
||||
&& RenderClearAttr1 == ClearAttr1
|
||||
&& RenderClearAttr2 == ClearAttr2
|
||||
&& RenderFogColor == FogColor
|
||||
&& RenderFogOffset == FogOffset * 0x200
|
||||
&& memcmp(RenderEdgeTable, EdgeTable, 8*2) == 0
|
||||
&& memcmp(RenderFogDensityTable + 1, FogDensityTable, 32) == 0
|
||||
&& memcmp(RenderToonTable, ToonTable, 32*2) == 0;
|
||||
}
|
||||
|
||||
RenderDispCnt = DispCnt;
|
||||
@ -2533,14 +2568,46 @@ void VCount215()
|
||||
#endif
|
||||
}
|
||||
|
||||
void SetRenderXPos(u16 xpos)
|
||||
{
|
||||
if (!RenderingEnabled) return;
|
||||
|
||||
RenderXPos = xpos & 0x01FF;
|
||||
}
|
||||
|
||||
u32 ScrolledLine[256];
|
||||
|
||||
u32* GetLine(int line)
|
||||
{
|
||||
if (GPU::Renderer == 0) return SoftRenderer::GetLine(line);
|
||||
u32* rawline = NULL;
|
||||
|
||||
if (GPU::Renderer == 0) rawline = SoftRenderer::GetLine(line);
|
||||
#ifdef OGLRENDERER_ENABLED
|
||||
else return GLRenderer::GetLine(line);
|
||||
#else
|
||||
return NULL;
|
||||
else rawline = GLRenderer::GetLine(line);
|
||||
#endif
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return ScrolledLine;
|
||||
}
|
||||
|
||||
|
||||
|
@ -87,6 +87,10 @@ extern u8 RenderFogDensityTable[34];
|
||||
|
||||
extern u32 RenderClearAttr1, RenderClearAttr2;
|
||||
|
||||
extern bool RenderFrameIdentical;
|
||||
|
||||
extern u16 RenderXPos;
|
||||
|
||||
extern std::array<Polygon*,2048> RenderPolygonRAM;
|
||||
extern u32 RenderNumPolygons;
|
||||
|
||||
@ -112,6 +116,8 @@ void CheckFIFODMA();
|
||||
void VCount144();
|
||||
void VBlank();
|
||||
void VCount215();
|
||||
|
||||
void SetRenderXPos(u16 xpos);
|
||||
u32* GetLine(int line);
|
||||
|
||||
void WriteToGXFIFO(u32 val);
|
||||
|
@ -53,17 +53,18 @@ GLuint CurShaderID = -1;
|
||||
GLuint FinalPassEdgeShader[3];
|
||||
GLuint FinalPassFogShader[3];
|
||||
|
||||
// std140 compliant structure
|
||||
struct
|
||||
{
|
||||
float uScreenSize[2];
|
||||
u32 uDispCnt;
|
||||
float uScreenSize[2]; // vec2 0 / 2
|
||||
u32 uDispCnt; // int 2 / 1
|
||||
u32 __pad0;
|
||||
float uToonColors[32][4];
|
||||
float uEdgeColors[8][4];
|
||||
float uFogColor[4];
|
||||
float uFogDensity[34][4];
|
||||
u32 uFogOffset;
|
||||
u32 uFogShift;
|
||||
float uToonColors[32][4]; // vec4[32] 4 / 128
|
||||
float uEdgeColors[8][4]; // vec4[8] 132 / 32
|
||||
float uFogColor[4]; // vec4 164 / 4
|
||||
float uFogDensity[34][4]; // float[34] 168 / 136
|
||||
u32 uFogOffset; // int 304 / 1
|
||||
u32 uFogShift; // int 305 / 1
|
||||
|
||||
} ShaderConfig;
|
||||
|
||||
@ -74,11 +75,11 @@ typedef struct
|
||||
Polygon* PolyData;
|
||||
|
||||
u32 NumIndices;
|
||||
u16* Indices;
|
||||
u32 IndicesOffset;
|
||||
GLuint PrimType;
|
||||
|
||||
u32 NumEdgeIndices;
|
||||
u16* EdgeIndices;
|
||||
u32 EdgeIndicesOffset;
|
||||
|
||||
u32 RenderKey;
|
||||
|
||||
@ -107,7 +108,11 @@ u32 VertexBuffer[10240 * 7];
|
||||
u32 NumVertices;
|
||||
|
||||
GLuint VertexArrayID;
|
||||
GLuint IndexBufferID;
|
||||
u16 IndexBuffer[2048 * 40];
|
||||
u32 NumIndices, NumEdgeIndices;
|
||||
|
||||
const u32 EdgeIndicesOffset = 2048 * 30;
|
||||
|
||||
GLuint TexMemID;
|
||||
GLuint TexPalMemID;
|
||||
@ -280,7 +285,7 @@ bool Init()
|
||||
|
||||
glGenBuffers(1, &ShaderConfigUBO);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, ShaderConfigUBO);
|
||||
glBufferData(GL_UNIFORM_BUFFER, sizeof(ShaderConfig), &ShaderConfig, GL_STATIC_DRAW);
|
||||
glBufferData(GL_UNIFORM_BUFFER, (sizeof(ShaderConfig) + 15) & ~15, &ShaderConfig, GL_STATIC_DRAW);
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, 0, ShaderConfigUBO);
|
||||
|
||||
|
||||
@ -320,6 +325,9 @@ bool Init()
|
||||
glEnableVertexAttribArray(3); // attrib
|
||||
glVertexAttribIPointer(3, 3, GL_UNSIGNED_INT, 7*4, (void*)(4*4));
|
||||
|
||||
glGenBuffers(1, &IndexBufferID);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferID);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(IndexBuffer), NULL, GL_DYNAMIC_DRAW);
|
||||
|
||||
glGenFramebuffers(4, &FramebufferID[0]);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, FramebufferID[0]);
|
||||
@ -563,15 +571,15 @@ void BuildPolygons(RendererPolygon* polygons, int npolys)
|
||||
u32* vptr = &VertexBuffer[0];
|
||||
u32 vidx = 0;
|
||||
|
||||
u16* iptr = &IndexBuffer[0];
|
||||
u16* eiptr = &IndexBuffer[2048*30];
|
||||
u32 iidx = 0;
|
||||
u32 eidx = EdgeIndicesOffset;
|
||||
|
||||
for (int i = 0; i < npolys; i++)
|
||||
{
|
||||
RendererPolygon* rp = &polygons[i];
|
||||
Polygon* poly = rp->PolyData;
|
||||
|
||||
rp->Indices = iptr;
|
||||
rp->IndicesOffset = iidx;
|
||||
rp->NumIndices = 0;
|
||||
|
||||
u32 vidx_first = vidx;
|
||||
@ -606,7 +614,7 @@ void BuildPolygons(RendererPolygon* polygons, int npolys)
|
||||
|
||||
vptr = SetupVertex(poly, j, vtx, vtxattr, vptr);
|
||||
|
||||
*iptr++ = vidx;
|
||||
IndexBuffer[iidx++] = vidx;
|
||||
rp->NumIndices++;
|
||||
|
||||
vidx++;
|
||||
@ -627,9 +635,9 @@ void BuildPolygons(RendererPolygon* polygons, int npolys)
|
||||
}
|
||||
|
||||
// build a triangle
|
||||
*iptr++ = vidx_first;
|
||||
*iptr++ = vidx - 2;
|
||||
*iptr++ = vidx - 1;
|
||||
IndexBuffer[iidx++] = vidx_first;
|
||||
IndexBuffer[iidx++] = vidx - 2;
|
||||
IndexBuffer[iidx++] = vidx - 1;
|
||||
rp->NumIndices += 3;
|
||||
}
|
||||
else // quad, pentagon, etc
|
||||
@ -649,9 +657,9 @@ void BuildPolygons(RendererPolygon* polygons, int npolys)
|
||||
if (j >= 2)
|
||||
{
|
||||
// build a triangle
|
||||
*iptr++ = vidx_first;
|
||||
*iptr++ = vidx - 1;
|
||||
*iptr++ = vidx;
|
||||
IndexBuffer[iidx++] = vidx_first;
|
||||
IndexBuffer[iidx++] = vidx - 1;
|
||||
IndexBuffer[iidx++] = vidx;
|
||||
rp->NumIndices += 3;
|
||||
}
|
||||
|
||||
@ -743,46 +751,50 @@ void BuildPolygons(RendererPolygon* polygons, int npolys)
|
||||
if (j >= 1)
|
||||
{
|
||||
// build a triangle
|
||||
*iptr++ = vidx_first;
|
||||
*iptr++ = vidx - 1;
|
||||
*iptr++ = vidx;
|
||||
IndexBuffer[iidx++] = vidx_first;
|
||||
IndexBuffer[iidx++] = vidx - 1;
|
||||
IndexBuffer[iidx++] = vidx;
|
||||
rp->NumIndices += 3;
|
||||
}
|
||||
|
||||
vidx++;
|
||||
}
|
||||
|
||||
*iptr++ = vidx_first;
|
||||
*iptr++ = vidx - 1;
|
||||
*iptr++ = vidx_first + 1;
|
||||
IndexBuffer[iidx++] = vidx_first;
|
||||
IndexBuffer[iidx++] = vidx - 1;
|
||||
IndexBuffer[iidx++] = vidx_first + 1;
|
||||
rp->NumIndices += 3;
|
||||
}
|
||||
}
|
||||
|
||||
rp->EdgeIndices = eiptr;
|
||||
rp->EdgeIndicesOffset = eidx;
|
||||
rp->NumEdgeIndices = 0;
|
||||
|
||||
u32 vidx_cur = vidx_first;
|
||||
for (int j = 1; j < poly->NumVertices; j++)
|
||||
{
|
||||
*eiptr++ = vidx_cur;
|
||||
*eiptr++ = vidx_cur + 1;
|
||||
IndexBuffer[eidx++] = vidx_cur;
|
||||
IndexBuffer[eidx++] = vidx_cur + 1;
|
||||
vidx_cur++;
|
||||
rp->NumEdgeIndices += 2;
|
||||
}
|
||||
*eiptr++ = vidx_cur;
|
||||
*eiptr++ = vidx_first;
|
||||
IndexBuffer[eidx++] = vidx_cur;
|
||||
IndexBuffer[eidx++] = vidx_first;
|
||||
rp->NumEdgeIndices += 2;
|
||||
}
|
||||
|
||||
NumVertices = vidx;
|
||||
NumIndices = iidx;
|
||||
NumEdgeIndices = eidx - EdgeIndicesOffset;
|
||||
}
|
||||
|
||||
void RenderSinglePolygon(int i)
|
||||
int RenderSinglePolygon(int i)
|
||||
{
|
||||
RendererPolygon* rp = &PolygonList[i];
|
||||
|
||||
glDrawElements(rp->PrimType, rp->NumIndices, GL_UNSIGNED_SHORT, rp->Indices);
|
||||
glDrawElements(rp->PrimType, rp->NumIndices, GL_UNSIGNED_SHORT, (void*)(uintptr_t)(rp->IndicesOffset * 2));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int RenderPolygonBatch(int i)
|
||||
@ -803,7 +815,7 @@ int RenderPolygonBatch(int i)
|
||||
numindices += cur_rp->NumIndices;
|
||||
}
|
||||
|
||||
glDrawElements(primtype, numindices, GL_UNSIGNED_SHORT, rp->Indices);
|
||||
glDrawElements(primtype, numindices, GL_UNSIGNED_SHORT, (void*)(uintptr_t)(rp->IndicesOffset * 2));
|
||||
return numpolys;
|
||||
}
|
||||
|
||||
@ -823,7 +835,7 @@ int RenderPolygonEdgeBatch(int i)
|
||||
numindices += cur_rp->NumEdgeIndices;
|
||||
}
|
||||
|
||||
glDrawElements(GL_LINES, numindices, GL_UNSIGNED_SHORT, rp->EdgeIndices);
|
||||
glDrawElements(GL_LINES, numindices, GL_UNSIGNED_SHORT, (void*)(uintptr_t)(rp->EdgeIndicesOffset * 2));
|
||||
return numpolys;
|
||||
}
|
||||
|
||||
@ -857,6 +869,7 @@ void RenderSceneChunk(int y, int h)
|
||||
RendererPolygon* rp = &PolygonList[i];
|
||||
|
||||
if (rp->PolyData->IsShadowMask) { i++; continue; }
|
||||
if (rp->PolyData->Translucent) { i++; continue; }
|
||||
|
||||
if (rp->PolyData->Attr & (1<<14))
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
@ -874,7 +887,8 @@ void RenderSceneChunk(int y, int h)
|
||||
}
|
||||
|
||||
// if edge marking is enabled, mark all opaque edges
|
||||
if (RenderDispCnt & (1<<5))
|
||||
// TODO BETTER EDGE MARKING!!! THIS SUCKS
|
||||
/*if (RenderDispCnt & (1<<5))
|
||||
{
|
||||
UseRenderShader(flags | RenderFlag_Edge);
|
||||
glLineWidth(1.5);
|
||||
@ -899,7 +913,7 @@ void RenderSceneChunk(int y, int h)
|
||||
}
|
||||
|
||||
glDepthMask(GL_TRUE);
|
||||
}
|
||||
}*/
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendEquationSeparate(GL_FUNC_ADD, GL_MAX);
|
||||
@ -944,15 +958,32 @@ void RenderSceneChunk(int y, int h)
|
||||
}
|
||||
else if (rp->PolyData->Translucent)
|
||||
{
|
||||
UseRenderShader(flags | RenderFlag_Trans);
|
||||
bool needopaque = ((rp->PolyData->Attr & 0x001F0000) == 0x001F0000);
|
||||
|
||||
if (rp->PolyData->Attr & (1<<14))
|
||||
u32 polyattr = rp->PolyData->Attr;
|
||||
u32 polyid = (polyattr >> 24) & 0x3F;
|
||||
|
||||
if (polyattr & (1<<14))
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
else
|
||||
glDepthFunc(GL_LESS);
|
||||
|
||||
u32 polyattr = rp->PolyData->Attr;
|
||||
u32 polyid = (polyattr >> 24) & 0x3F;
|
||||
if (needopaque)
|
||||
{
|
||||
UseRenderShader(flags);
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
glColorMaski(1, GL_TRUE, GL_TRUE, fogenable, GL_FALSE);
|
||||
|
||||
glStencilFunc(GL_ALWAYS, polyid, 0xFF);
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
|
||||
glStencilMask(0xFF);
|
||||
|
||||
RenderSinglePolygon(i);
|
||||
}
|
||||
|
||||
UseRenderShader(flags | RenderFlag_Trans);
|
||||
|
||||
GLboolean transfog;
|
||||
if (!(polyattr & (1<<15))) transfog = fogenable;
|
||||
@ -975,7 +1006,7 @@ void RenderSceneChunk(int y, int h)
|
||||
if (polyattr & (1<<11)) glDepthMask(GL_TRUE);
|
||||
else glDepthMask(GL_FALSE);
|
||||
|
||||
i += RenderPolygonBatch(i);
|
||||
i += needopaque ? RenderSinglePolygon(i) : RenderPolygonBatch(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -989,7 +1020,7 @@ void RenderSceneChunk(int y, int h)
|
||||
if (polyattr & (1<<11)) glDepthMask(GL_TRUE);
|
||||
else glDepthMask(GL_FALSE);
|
||||
|
||||
i += RenderPolygonBatch(i);
|
||||
i += needopaque ? RenderSinglePolygon(i) : RenderPolygonBatch(i);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -1030,20 +1061,37 @@ void RenderSceneChunk(int y, int h)
|
||||
}
|
||||
else if (rp->PolyData->Translucent)
|
||||
{
|
||||
UseRenderShader(flags | RenderFlag_Trans);
|
||||
bool needopaque = ((rp->PolyData->Attr & 0x001F0000) == 0x001F0000);
|
||||
|
||||
u32 polyattr = rp->PolyData->Attr;
|
||||
u32 polyid = (polyattr >> 24) & 0x3F;
|
||||
|
||||
GLboolean transfog;
|
||||
if (!(polyattr & (1<<15))) transfog = fogenable;
|
||||
else transfog = GL_FALSE;
|
||||
|
||||
if (rp->PolyData->Attr & (1<<14))
|
||||
if (polyattr & (1<<14))
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
else
|
||||
glDepthFunc(GL_LESS);
|
||||
|
||||
if (needopaque)
|
||||
{
|
||||
UseRenderShader(flags);
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
glColorMaski(1, GL_TRUE, GL_TRUE, fogenable, GL_FALSE);
|
||||
|
||||
glStencilFunc(GL_ALWAYS, polyid, 0xFF);
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
|
||||
glStencilMask(0xFF);
|
||||
|
||||
RenderSinglePolygon(i);
|
||||
}
|
||||
|
||||
UseRenderShader(flags | RenderFlag_Trans);
|
||||
|
||||
GLboolean transfog;
|
||||
if (!(polyattr & (1<<15))) transfog = fogenable;
|
||||
else transfog = GL_FALSE;
|
||||
|
||||
if (rp->PolyData->IsShadow)
|
||||
{
|
||||
glDisable(GL_BLEND);
|
||||
@ -1067,8 +1115,7 @@ void RenderSceneChunk(int y, int h)
|
||||
if (polyattr & (1<<11)) glDepthMask(GL_TRUE);
|
||||
else glDepthMask(GL_FALSE);
|
||||
|
||||
RenderSinglePolygon(i);
|
||||
i++;
|
||||
i += RenderSinglePolygon(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1083,7 +1130,7 @@ void RenderSceneChunk(int y, int h)
|
||||
if (polyattr & (1<<11)) glDepthMask(GL_TRUE);
|
||||
else glDepthMask(GL_FALSE);
|
||||
|
||||
i += RenderPolygonBatch(i);
|
||||
i += needopaque ? RenderSinglePolygon(i) : RenderPolygonBatch(i);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -1320,6 +1367,11 @@ void RenderFrame()
|
||||
glBindBuffer(GL_ARRAY_BUFFER, VertexBufferID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, NumVertices*7*4, VertexBuffer);
|
||||
|
||||
// bind to access the index buffer
|
||||
glBindVertexArray(VertexArrayID);
|
||||
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, NumIndices * 2, IndexBuffer);
|
||||
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, EdgeIndicesOffset * 2, NumEdgeIndices * 2, IndexBuffer + EdgeIndicesOffset);
|
||||
|
||||
RenderSceneChunk(0, 192);
|
||||
}
|
||||
|
||||
|
@ -58,15 +58,17 @@ bool PrevIsShadowMask;
|
||||
|
||||
bool Enabled;
|
||||
|
||||
bool FrameIdentical;
|
||||
|
||||
// threading
|
||||
|
||||
bool Threaded;
|
||||
void* RenderThread;
|
||||
Platform::Thread* RenderThread;
|
||||
bool RenderThreadRunning;
|
||||
bool RenderThreadRendering;
|
||||
void* Sema_RenderStart;
|
||||
void* Sema_RenderDone;
|
||||
void* Sema_ScanlineCount;
|
||||
Platform::Semaphore* Sema_RenderStart;
|
||||
Platform::Semaphore* Sema_RenderDone;
|
||||
Platform::Semaphore* Sema_ScanlineCount;
|
||||
|
||||
void RenderThreadFunc();
|
||||
|
||||
@ -550,6 +552,16 @@ typedef struct
|
||||
|
||||
RendererPolygon PolygonList[2048];
|
||||
|
||||
template <typename T>
|
||||
inline T ReadVRAM_Texture(u32 addr)
|
||||
{
|
||||
return *(T*)&GPU::VRAMFlat_Texture[addr & 0x7FFFF];
|
||||
}
|
||||
template <typename T>
|
||||
inline T ReadVRAM_TexPal(u32 addr)
|
||||
{
|
||||
return *(T*)&GPU::VRAMFlat_TexPal[addr & 0x1FFFF];
|
||||
}
|
||||
|
||||
void TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha)
|
||||
{
|
||||
@ -606,10 +618,10 @@ void TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha
|
||||
case 1: // A3I5
|
||||
{
|
||||
vramaddr += ((t * width) + s);
|
||||
u8 pixel = GPU::ReadVRAM_Texture<u8>(vramaddr);
|
||||
u8 pixel = ReadVRAM_Texture<u8>(vramaddr);
|
||||
|
||||
texpal <<= 4;
|
||||
*color = GPU::ReadVRAM_TexPal<u16>(texpal + ((pixel&0x1F)<<1));
|
||||
*color = ReadVRAM_TexPal<u16>(texpal + ((pixel&0x1F)<<1));
|
||||
*alpha = ((pixel >> 3) & 0x1C) + (pixel >> 6);
|
||||
}
|
||||
break;
|
||||
@ -617,12 +629,12 @@ void TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha
|
||||
case 2: // 4-color
|
||||
{
|
||||
vramaddr += (((t * width) + s) >> 2);
|
||||
u8 pixel = GPU::ReadVRAM_Texture<u8>(vramaddr);
|
||||
u8 pixel = ReadVRAM_Texture<u8>(vramaddr);
|
||||
pixel >>= ((s & 0x3) << 1);
|
||||
pixel &= 0x3;
|
||||
|
||||
texpal <<= 3;
|
||||
*color = GPU::ReadVRAM_TexPal<u16>(texpal + (pixel<<1));
|
||||
*color = ReadVRAM_TexPal<u16>(texpal + (pixel<<1));
|
||||
*alpha = (pixel==0) ? alpha0 : 31;
|
||||
}
|
||||
break;
|
||||
@ -630,12 +642,12 @@ void TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha
|
||||
case 3: // 16-color
|
||||
{
|
||||
vramaddr += (((t * width) + s) >> 1);
|
||||
u8 pixel = GPU::ReadVRAM_Texture<u8>(vramaddr);
|
||||
u8 pixel = ReadVRAM_Texture<u8>(vramaddr);
|
||||
if (s & 0x1) pixel >>= 4;
|
||||
else pixel &= 0xF;
|
||||
|
||||
texpal <<= 4;
|
||||
*color = GPU::ReadVRAM_TexPal<u16>(texpal + (pixel<<1));
|
||||
*color = ReadVRAM_TexPal<u16>(texpal + (pixel<<1));
|
||||
*alpha = (pixel==0) ? alpha0 : 31;
|
||||
}
|
||||
break;
|
||||
@ -643,10 +655,10 @@ void TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha
|
||||
case 4: // 256-color
|
||||
{
|
||||
vramaddr += ((t * width) + s);
|
||||
u8 pixel = GPU::ReadVRAM_Texture<u8>(vramaddr);
|
||||
u8 pixel = ReadVRAM_Texture<u8>(vramaddr);
|
||||
|
||||
texpal <<= 4;
|
||||
*color = GPU::ReadVRAM_TexPal<u16>(texpal + (pixel<<1));
|
||||
*color = ReadVRAM_TexPal<u16>(texpal + (pixel<<1));
|
||||
*alpha = (pixel==0) ? alpha0 : 31;
|
||||
}
|
||||
break;
|
||||
@ -660,30 +672,30 @@ void TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha
|
||||
if (vramaddr >= 0x40000)
|
||||
slot1addr += 0x10000;
|
||||
|
||||
u8 val = GPU::ReadVRAM_Texture<u8>(vramaddr);
|
||||
u8 val = ReadVRAM_Texture<u8>(vramaddr);
|
||||
val >>= (2 * (s & 0x3));
|
||||
|
||||
u16 palinfo = GPU::ReadVRAM_Texture<u16>(slot1addr);
|
||||
u16 palinfo = ReadVRAM_Texture<u16>(slot1addr);
|
||||
u32 paloffset = (palinfo & 0x3FFF) << 2;
|
||||
texpal <<= 4;
|
||||
|
||||
switch (val & 0x3)
|
||||
{
|
||||
case 0:
|
||||
*color = GPU::ReadVRAM_TexPal<u16>(texpal + paloffset);
|
||||
*color = ReadVRAM_TexPal<u16>(texpal + paloffset);
|
||||
*alpha = 31;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
*color = GPU::ReadVRAM_TexPal<u16>(texpal + paloffset + 2);
|
||||
*color = ReadVRAM_TexPal<u16>(texpal + paloffset + 2);
|
||||
*alpha = 31;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if ((palinfo >> 14) == 1)
|
||||
{
|
||||
u16 color0 = GPU::ReadVRAM_TexPal<u16>(texpal + paloffset);
|
||||
u16 color1 = GPU::ReadVRAM_TexPal<u16>(texpal + paloffset + 2);
|
||||
u16 color0 = ReadVRAM_TexPal<u16>(texpal + paloffset);
|
||||
u16 color1 = ReadVRAM_TexPal<u16>(texpal + paloffset + 2);
|
||||
|
||||
u32 r0 = color0 & 0x001F;
|
||||
u32 g0 = color0 & 0x03E0;
|
||||
@ -700,8 +712,8 @@ void TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha
|
||||
}
|
||||
else if ((palinfo >> 14) == 3)
|
||||
{
|
||||
u16 color0 = GPU::ReadVRAM_TexPal<u16>(texpal + paloffset);
|
||||
u16 color1 = GPU::ReadVRAM_TexPal<u16>(texpal + paloffset + 2);
|
||||
u16 color0 = ReadVRAM_TexPal<u16>(texpal + paloffset);
|
||||
u16 color1 = ReadVRAM_TexPal<u16>(texpal + paloffset + 2);
|
||||
|
||||
u32 r0 = color0 & 0x001F;
|
||||
u32 g0 = color0 & 0x03E0;
|
||||
@ -717,20 +729,20 @@ void TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha
|
||||
*color = r | g | b;
|
||||
}
|
||||
else
|
||||
*color = GPU::ReadVRAM_TexPal<u16>(texpal + paloffset + 4);
|
||||
*color = ReadVRAM_TexPal<u16>(texpal + paloffset + 4);
|
||||
*alpha = 31;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if ((palinfo >> 14) == 2)
|
||||
{
|
||||
*color = GPU::ReadVRAM_TexPal<u16>(texpal + paloffset + 6);
|
||||
*color = ReadVRAM_TexPal<u16>(texpal + paloffset + 6);
|
||||
*alpha = 31;
|
||||
}
|
||||
else if ((palinfo >> 14) == 3)
|
||||
{
|
||||
u16 color0 = GPU::ReadVRAM_TexPal<u16>(texpal + paloffset);
|
||||
u16 color1 = GPU::ReadVRAM_TexPal<u16>(texpal + paloffset + 2);
|
||||
u16 color0 = ReadVRAM_TexPal<u16>(texpal + paloffset);
|
||||
u16 color1 = ReadVRAM_TexPal<u16>(texpal + paloffset + 2);
|
||||
|
||||
u32 r0 = color0 & 0x001F;
|
||||
u32 g0 = color0 & 0x03E0;
|
||||
@ -759,10 +771,10 @@ void TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha
|
||||
case 6: // A5I3
|
||||
{
|
||||
vramaddr += ((t * width) + s);
|
||||
u8 pixel = GPU::ReadVRAM_Texture<u8>(vramaddr);
|
||||
u8 pixel = ReadVRAM_Texture<u8>(vramaddr);
|
||||
|
||||
texpal <<= 4;
|
||||
*color = GPU::ReadVRAM_TexPal<u16>(texpal + ((pixel&0x7)<<1));
|
||||
*color = ReadVRAM_TexPal<u16>(texpal + ((pixel&0x7)<<1));
|
||||
*alpha = (pixel >> 3);
|
||||
}
|
||||
break;
|
||||
@ -770,7 +782,7 @@ void TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha
|
||||
case 7: // direct color
|
||||
{
|
||||
vramaddr += (((t * width) + s) << 1);
|
||||
*color = GPU::ReadVRAM_Texture<u16>(vramaddr);
|
||||
*color = ReadVRAM_Texture<u16>(vramaddr);
|
||||
*alpha = (*color & 0x8000) ? 31 : 0;
|
||||
}
|
||||
break;
|
||||
@ -2007,8 +2019,8 @@ void ClearBuffers()
|
||||
{
|
||||
for (int x = 0; x < 256; x++)
|
||||
{
|
||||
u16 val2 = GPU::ReadVRAM_Texture<u16>(0x40000 + (yoff << 9) + (xoff << 1));
|
||||
u16 val3 = GPU::ReadVRAM_Texture<u16>(0x60000 + (yoff << 9) + (xoff << 1));
|
||||
u16 val2 = ReadVRAM_Texture<u16>(0x40000 + (yoff << 9) + (xoff << 1));
|
||||
u16 val3 = ReadVRAM_Texture<u16>(0x60000 + (yoff << 9) + (xoff << 1));
|
||||
|
||||
// TODO: confirm color conversion
|
||||
u32 r = (val2 << 1) & 0x3E; if (r) r++;
|
||||
@ -2088,11 +2100,19 @@ void VCount144()
|
||||
|
||||
void RenderFrame()
|
||||
{
|
||||
auto textureDirty = GPU::VRAMDirty_Texture.DeriveState(GPU::VRAMMap_Texture);
|
||||
auto texPalDirty = GPU::VRAMDirty_TexPal.DeriveState(GPU::VRAMMap_TexPal);
|
||||
|
||||
bool textureChanged = GPU::MakeVRAMFlat_TextureCoherent(textureDirty);
|
||||
bool texPalChanged = GPU::MakeVRAMFlat_TexPalCoherent(texPalDirty);
|
||||
|
||||
FrameIdentical = !(textureChanged || texPalChanged) && RenderFrameIdentical;
|
||||
|
||||
if (RenderThreadRunning)
|
||||
{
|
||||
Platform::Semaphore_Post(Sema_RenderStart);
|
||||
}
|
||||
else
|
||||
else if (!FrameIdentical)
|
||||
{
|
||||
ClearBuffers();
|
||||
RenderPolygons(false, &RenderPolygonRAM[0], RenderNumPolygons);
|
||||
@ -2107,8 +2127,15 @@ void RenderThreadFunc()
|
||||
if (!RenderThreadRunning) return;
|
||||
|
||||
RenderThreadRendering = true;
|
||||
ClearBuffers();
|
||||
RenderPolygons(true, &RenderPolygonRAM[0], RenderNumPolygons);
|
||||
if (FrameIdentical)
|
||||
{
|
||||
Platform::Semaphore_Post(Sema_ScanlineCount, 192);
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearBuffers();
|
||||
RenderPolygons(true, &RenderPolygonRAM[0], RenderNumPolygons);
|
||||
}
|
||||
|
||||
Platform::Semaphore_Post(Sema_RenderDone);
|
||||
RenderThreadRendering = false;
|
||||
|
@ -36,6 +36,7 @@ int ScreenH, ScreenW;
|
||||
|
||||
GLuint CompShader[1][3];
|
||||
GLuint CompScaleLoc[1];
|
||||
GLuint Comp3DXPosLoc[1];
|
||||
|
||||
GLuint CompVertexBufferID;
|
||||
GLuint CompVertexArrayID;
|
||||
@ -64,6 +65,7 @@ bool Init()
|
||||
return false;
|
||||
|
||||
CompScaleLoc[i] = glGetUniformLocation(CompShader[i][2], "u3DScale");
|
||||
Comp3DXPosLoc[i] = glGetUniformLocation(CompShader[i][2], "u3DXPos");
|
||||
|
||||
glUseProgram(CompShader[i][2]);
|
||||
uni_id = glGetUniformLocation(CompShader[i][2], "ScreenTex");
|
||||
@ -180,6 +182,9 @@ void RenderFrame()
|
||||
OpenGL::UseShaderProgram(CompShader[0]);
|
||||
glUniform1ui(CompScaleLoc[0], Scale);
|
||||
|
||||
// TODO: support setting this midframe, if ever needed
|
||||
glUniform1i(Comp3DXPosLoc[0], ((int)GPU3D::RenderXPos << 23) >> 23);
|
||||
|
||||
int frontbuf = GPU::FrontBuffer;
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, CompScreenInputTex);
|
||||
|
@ -40,6 +40,7 @@ void main()
|
||||
const char* kCompositorFS_Nearest = R"(#version 140
|
||||
|
||||
uniform uint u3DScale;
|
||||
uniform int u3DXPos;
|
||||
|
||||
uniform usampler2D ScreenTex;
|
||||
uniform sampler2D _3DTex;
|
||||
@ -52,6 +53,8 @@ void main()
|
||||
{
|
||||
ivec4 pixel = ivec4(texelFetch(ScreenTex, ivec2(fTexcoord), 0));
|
||||
|
||||
float _3dxpos = float(u3DXPos);
|
||||
|
||||
ivec4 mbright = ivec4(texelFetch(ScreenTex, ivec2(256*3, int(fTexcoord.y)), 0));
|
||||
int dispmode = mbright.b & 0x3;
|
||||
|
||||
@ -68,7 +71,7 @@ void main()
|
||||
{
|
||||
// 3D on top, blending
|
||||
|
||||
float xpos = val3.r + fract(fTexcoord.x);
|
||||
float xpos = fTexcoord.x + _3dxpos;
|
||||
float ypos = mod(fTexcoord.y, 192);
|
||||
ivec4 _3dpix = ivec4(texelFetch(_3DTex, ivec2(vec2(xpos, ypos)*u3DScale), 0).bgra
|
||||
* vec4(63,63,63,31));
|
||||
@ -89,7 +92,7 @@ void main()
|
||||
{
|
||||
// 3D on bottom, blending
|
||||
|
||||
float xpos = val3.r + fract(fTexcoord.x);
|
||||
float xpos = fTexcoord.x + _3dxpos;
|
||||
float ypos = mod(fTexcoord.y, 192);
|
||||
ivec4 _3dpix = ivec4(texelFetch(_3DTex, ivec2(vec2(xpos, ypos)*u3DScale), 0).bgra
|
||||
* vec4(63,63,63,31));
|
||||
@ -109,7 +112,7 @@ void main()
|
||||
{
|
||||
// 3D on top, normal/fade
|
||||
|
||||
float xpos = val3.r + fract(fTexcoord.x);
|
||||
float xpos = fTexcoord.x + _3dxpos;
|
||||
float ypos = mod(fTexcoord.y, 192);
|
||||
ivec4 _3dpix = ivec4(texelFetch(_3DTex, ivec2(vec2(xpos, ypos)*u3DScale), 0).bgra
|
||||
* vec4(63,63,63,31));
|
||||
|
91
src/NDS.cpp
91
src/NDS.cpp
@ -210,13 +210,13 @@ bool Init()
|
||||
|
||||
void DeInit()
|
||||
{
|
||||
delete ARM9;
|
||||
delete ARM7;
|
||||
|
||||
#ifdef JIT_ENABLED
|
||||
ARMJIT::DeInit();
|
||||
#endif
|
||||
|
||||
delete ARM9;
|
||||
delete ARM7;
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
delete DMAs[i];
|
||||
|
||||
@ -908,7 +908,7 @@ void RunSystem(u64 timestamp)
|
||||
}
|
||||
}
|
||||
|
||||
template <bool EnableJIT>
|
||||
template <bool EnableJIT, int ConsoleType>
|
||||
u32 RunFrame()
|
||||
{
|
||||
FrameStartTimestamp = SysTimestamp;
|
||||
@ -934,10 +934,10 @@ u32 RunFrame()
|
||||
}
|
||||
else if (CPUStop & 0x0FFF)
|
||||
{
|
||||
DMAs[0]->Run();
|
||||
if (!(CPUStop & 0x80000000)) DMAs[1]->Run();
|
||||
if (!(CPUStop & 0x80000000)) DMAs[2]->Run();
|
||||
if (!(CPUStop & 0x80000000)) DMAs[3]->Run();
|
||||
DMAs[0]->Run<ConsoleType>();
|
||||
if (!(CPUStop & 0x80000000)) DMAs[1]->Run<ConsoleType>();
|
||||
if (!(CPUStop & 0x80000000)) DMAs[2]->Run<ConsoleType>();
|
||||
if (!(CPUStop & 0x80000000)) DMAs[3]->Run<ConsoleType>();
|
||||
if (ConsoleType == 1) DSi::RunNDMAs(0);
|
||||
}
|
||||
else
|
||||
@ -962,10 +962,10 @@ u32 RunFrame()
|
||||
|
||||
if (CPUStop & 0x0FFF0000)
|
||||
{
|
||||
DMAs[4]->Run();
|
||||
DMAs[5]->Run();
|
||||
DMAs[6]->Run();
|
||||
DMAs[7]->Run();
|
||||
DMAs[4]->Run<ConsoleType>();
|
||||
DMAs[5]->Run<ConsoleType>();
|
||||
DMAs[6]->Run<ConsoleType>();
|
||||
DMAs[7]->Run<ConsoleType>();
|
||||
if (ConsoleType == 1) DSi::RunNDMAs(1);
|
||||
}
|
||||
else
|
||||
@ -999,6 +999,9 @@ u32 RunFrame()
|
||||
ARM7Timestamp-SysTimestamp,
|
||||
GPU3D::Timestamp-SysTimestamp);
|
||||
#endif
|
||||
SPU::TransferOutput();
|
||||
|
||||
NDSCart::FlushSRAMFile();
|
||||
|
||||
NumFrames++;
|
||||
|
||||
@ -1009,10 +1012,14 @@ u32 RunFrame()
|
||||
{
|
||||
#ifdef JIT_ENABLED
|
||||
if (Config::JIT_Enable)
|
||||
return RunFrame<true>();
|
||||
return NDS::ConsoleType == 1
|
||||
? RunFrame<true, 1>()
|
||||
: RunFrame<true, 0>();
|
||||
else
|
||||
#endif
|
||||
return RunFrame<false>();
|
||||
return NDS::ConsoleType == 1
|
||||
? RunFrame<false, 1>()
|
||||
: RunFrame<false, 0>();
|
||||
}
|
||||
|
||||
void Reschedule(u64 target)
|
||||
@ -1470,7 +1477,7 @@ void HandleTimerOverflow(u32 tid)
|
||||
{
|
||||
Timer* timer = &Timers[tid];
|
||||
|
||||
timer->Counter += timer->Reload << 16;
|
||||
timer->Counter += (timer->Reload << 10);
|
||||
if (timer->Cnt & (1<<6))
|
||||
SetIRQ(tid >> 2, IRQ_Timer0 + (tid & 0x3));
|
||||
|
||||
@ -1486,11 +1493,11 @@ void HandleTimerOverflow(u32 tid)
|
||||
if ((timer->Cnt & 0x84) != 0x84)
|
||||
break;
|
||||
|
||||
timer->Counter += 0x10000;
|
||||
if (timer->Counter >> 16)
|
||||
timer->Counter += (1 << 10);
|
||||
if (!(timer->Counter >> 26))
|
||||
break;
|
||||
|
||||
timer->Counter = timer->Reload << 16;
|
||||
timer->Counter = timer->Reload << 10;
|
||||
if (timer->Cnt & (1<<6))
|
||||
SetIRQ(tid >> 2, IRQ_Timer0 + (tid & 0x3));
|
||||
|
||||
@ -1505,8 +1512,13 @@ void RunTimer(u32 tid, s32 cycles)
|
||||
|
||||
u32 oldcount = timer->Counter;
|
||||
timer->Counter += (cycles << timer->CycleShift);
|
||||
if (timer->Counter < oldcount)
|
||||
//if (timer->Counter < oldcount)
|
||||
// HandleTimerOverflow(tid);
|
||||
while (timer->Counter >> 26)
|
||||
{
|
||||
timer->Counter -= (1 << 26);
|
||||
HandleTimerOverflow(tid);
|
||||
}
|
||||
}
|
||||
|
||||
void RunTimers(u32 cpu)
|
||||
@ -1623,7 +1635,7 @@ u16 TimerGetCounter(u32 timer)
|
||||
RunTimers(timer>>2);
|
||||
u32 ret = Timers[timer].Counter;
|
||||
|
||||
return ret >> 16;
|
||||
return ret >> 10;
|
||||
}
|
||||
|
||||
void TimerStart(u32 id, u16 cnt)
|
||||
@ -1633,11 +1645,11 @@ void TimerStart(u32 id, u16 cnt)
|
||||
u16 newstart = cnt & (1<<7);
|
||||
|
||||
timer->Cnt = cnt;
|
||||
timer->CycleShift = 16 - TimerPrescaler[cnt & 0x03];
|
||||
timer->CycleShift = 10 - TimerPrescaler[cnt & 0x03];
|
||||
|
||||
if ((!curstart) && newstart)
|
||||
{
|
||||
timer->Counter = timer->Reload << 16;
|
||||
timer->Counter = timer->Reload << 10;
|
||||
|
||||
/*if ((cnt & 0x84) == 0x80)
|
||||
{
|
||||
@ -1824,14 +1836,14 @@ void debug(u32 param)
|
||||
fclose(shit);*/
|
||||
|
||||
FILE*
|
||||
shit = fopen("debug/picto9.bin", "wb");
|
||||
shit = fopen("debug/power9.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/picto7.bin", "wb");
|
||||
shit = fopen("debug/power7.bin", "wb");
|
||||
for (u32 i = 0x02000000; i < 0x04000000; i+=4)
|
||||
{
|
||||
u32 val = DSi::ARM7Read32(i);
|
||||
@ -3001,6 +3013,7 @@ u32 ARM9IORead32(u32 addr)
|
||||
case 0x04000130: return (KeyInput & 0xFFFF) | (KeyCnt << 16);
|
||||
|
||||
case 0x04000180: return IPCSync9;
|
||||
case 0x04000184: return ARM9IORead16(addr);
|
||||
|
||||
case 0x040001A0: return NDSCart::SPICnt | (NDSCart::ReadSPIData() << 16);
|
||||
case 0x040001A4: return NDSCart::ROMCnt;
|
||||
@ -3115,6 +3128,10 @@ void ARM9IOWrite8(u32 addr, u8 val)
|
||||
NDSCart::WriteSPIData(val);
|
||||
return;
|
||||
|
||||
case 0x04000188:
|
||||
ARM9IOWrite32(addr, val | (val << 8) | (val << 16) | (val << 24));
|
||||
return;
|
||||
|
||||
case 0x040001A8: NDSCart::ROMCommand[0] = val; return;
|
||||
case 0x040001A9: NDSCart::ROMCommand[1] = val; return;
|
||||
case 0x040001AA: NDSCart::ROMCommand[2] = val; return;
|
||||
@ -3228,7 +3245,11 @@ void ARM9IOWrite16(u32 addr, u16 val)
|
||||
SetIRQ(0, IRQ_IPCRecv);
|
||||
if (val & 0x4000)
|
||||
IPCFIFOCnt9 &= ~0x4000;
|
||||
IPCFIFOCnt9 = val & 0x8404;
|
||||
IPCFIFOCnt9 = (val & 0x8404) | (IPCFIFOCnt9 & 0x4000);
|
||||
return;
|
||||
|
||||
case 0x04000188:
|
||||
ARM9IOWrite32(addr, val | (val << 16));
|
||||
return;
|
||||
|
||||
case 0x040001A0:
|
||||
@ -3378,10 +3399,11 @@ void ARM9IOWrite32(u32 addr, u32 val)
|
||||
case 0x04000130:
|
||||
KeyCnt = val >> 16;
|
||||
return;
|
||||
|
||||
case 0x04000180:
|
||||
case 0x04000184:
|
||||
ARM9IOWrite16(addr, val);
|
||||
return;
|
||||
|
||||
case 0x04000188:
|
||||
if (IPCFIFOCnt9 & 0x8000)
|
||||
{
|
||||
@ -3640,6 +3662,7 @@ u32 ARM7IORead32(u32 addr)
|
||||
case 0x04000138: return RTC::Read();
|
||||
|
||||
case 0x04000180: return IPCSync7;
|
||||
case 0x04000184: return ARM7IORead16(addr);
|
||||
|
||||
case 0x040001A0: return NDSCart::SPICnt | (NDSCart::ReadSPIData() << 16);
|
||||
case 0x040001A4: return NDSCart::ROMCnt;
|
||||
@ -3716,6 +3739,10 @@ void ARM7IOWrite8(u32 addr, u8 val)
|
||||
|
||||
case 0x04000138: RTC::Write(val, true); return;
|
||||
|
||||
case 0x04000188:
|
||||
ARM7IOWrite32(addr, val | (val << 8) | (val << 16) | (val << 24));
|
||||
return;
|
||||
|
||||
case 0x040001A0:
|
||||
if (ExMemCnt[0] & (1<<11))
|
||||
{
|
||||
@ -3821,7 +3848,11 @@ void ARM7IOWrite16(u32 addr, u16 val)
|
||||
SetIRQ(1, IRQ_IPCRecv);
|
||||
if (val & 0x4000)
|
||||
IPCFIFOCnt7 &= ~0x4000;
|
||||
IPCFIFOCnt7 = val & 0x8404;
|
||||
IPCFIFOCnt7 = (val & 0x8404) | (IPCFIFOCnt7 & 0x4000);
|
||||
return;
|
||||
|
||||
case 0x04000188:
|
||||
ARM7IOWrite32(addr, val | (val << 16));
|
||||
return;
|
||||
|
||||
case 0x040001A0:
|
||||
@ -3940,6 +3971,7 @@ void ARM7IOWrite32(u32 addr, u32 val)
|
||||
case 0x04000138: RTC::Write(val & 0xFFFF, false); return;
|
||||
|
||||
case 0x04000180:
|
||||
case 0x04000184:
|
||||
ARM7IOWrite16(addr, val);
|
||||
return;
|
||||
case 0x04000188:
|
||||
@ -3984,6 +4016,11 @@ void ARM7IOWrite32(u32 addr, u32 val)
|
||||
case 0x040001B0: *(u32*)&ROMSeed0[8] = val; return;
|
||||
case 0x040001B4: *(u32*)&ROMSeed1[8] = val; return;
|
||||
|
||||
case 0x040001C0:
|
||||
SPI::WriteCnt(val & 0xFFFF);
|
||||
SPI::WriteData((val >> 16) & 0xFF);
|
||||
return;
|
||||
|
||||
case 0x04000208: IME[1] = val & 0x1; UpdateIRQ(1); return;
|
||||
case 0x04000210: IE[1] = val; UpdateIRQ(1); return;
|
||||
case 0x04000214: IF[1] &= ~val; UpdateIRQ(1); return;
|
||||
|
@ -46,6 +46,8 @@ enum
|
||||
Event_DSi_SDMMCTransfer,
|
||||
Event_DSi_SDIOTransfer,
|
||||
Event_DSi_NWifi,
|
||||
Event_DSi_CamIRQ,
|
||||
Event_DSi_CamTransfer,
|
||||
|
||||
Event_DSi_RAMSizeChange,
|
||||
|
||||
@ -82,7 +84,7 @@ enum
|
||||
IRQ_IPCSendDone,
|
||||
IRQ_IPCRecv,
|
||||
IRQ_CartSendDone, // TODO: less misleading name
|
||||
IRQ_CartIREQMC, // IRQ triggered by game cart (example: Pok<EFBFBD>mon Typing Adventure, BT controller)
|
||||
IRQ_CartIREQMC, // IRQ triggered by game cart (example: Pokémon Typing Adventure, BT controller)
|
||||
IRQ_GXFIFO,
|
||||
IRQ_LidOpen,
|
||||
IRQ_SPI,
|
||||
|
@ -37,6 +37,7 @@ u8* SRAM;
|
||||
u32 SRAMLength;
|
||||
|
||||
char SRAMPath[1024];
|
||||
bool SRAMFileDirty;
|
||||
|
||||
void (*WriteFunc)(u8 val, bool islast);
|
||||
|
||||
@ -445,14 +446,21 @@ void Write(u8 val, u32 hold)
|
||||
break;
|
||||
}
|
||||
|
||||
if (islast && (CurCmd == 0x02 || CurCmd == 0x0A) && (SRAMLength > 0))
|
||||
SRAMFileDirty |= islast && (CurCmd == 0x02 || CurCmd == 0x0A) && (SRAMLength > 0);
|
||||
}
|
||||
|
||||
void FlushSRAMFile()
|
||||
{
|
||||
if (!SRAMFileDirty)
|
||||
return;
|
||||
|
||||
SRAMFileDirty = false;
|
||||
|
||||
FILE* f = Platform::OpenFile(SRAMPath, "wb");
|
||||
if (f)
|
||||
{
|
||||
FILE* f = Platform::OpenFile(SRAMPath, "wb");
|
||||
if (f)
|
||||
{
|
||||
fwrite(SRAM, SRAMLength, 1, f);
|
||||
fclose(f);
|
||||
}
|
||||
fwrite(SRAM, SRAMLength, 1, f);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1034,6 +1042,11 @@ void RelocateSave(const char* path, bool write)
|
||||
NDSCart_SRAM::RelocateSave(path, write);
|
||||
}
|
||||
|
||||
void FlushSRAMFile()
|
||||
{
|
||||
NDSCart_SRAM::FlushSRAMFile();
|
||||
}
|
||||
|
||||
int ImportSRAM(const u8* data, u32 length)
|
||||
{
|
||||
memcpy(NDSCart_SRAM::SRAM, data, std::min(length, NDSCart_SRAM::SRAMLength));
|
||||
|
@ -46,6 +46,9 @@ void DoSavestate(Savestate* file);
|
||||
|
||||
void DecryptSecureArea(u8* out);
|
||||
bool LoadROM(const char* path, const char* sram, bool direct);
|
||||
|
||||
void FlushSRAMFile();
|
||||
|
||||
void RelocateSave(const char* path, bool write);
|
||||
|
||||
int ImportSRAM(const u8* data, u32 length);
|
||||
|
149
src/NonStupidBitfield.h
Normal file
149
src/NonStupidBitfield.h
Normal file
@ -0,0 +1,149 @@
|
||||
#ifndef NONSTUPIDBITFIELD_H
|
||||
#define NONSTUPIDBITFIELD_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#include <memory.h>
|
||||
|
||||
#include <initializer_list>
|
||||
#include <algorithm>
|
||||
|
||||
// like std::bitset but less stupid and optimised for
|
||||
// our use case (keeping track of memory invalidations)
|
||||
|
||||
template <u32 Size>
|
||||
struct NonStupidBitField
|
||||
{
|
||||
static_assert((Size % 8) == 0, "bitfield size must be a multiple of 8");
|
||||
static const u32 DataLength = Size / 8;
|
||||
u8 Data[DataLength];
|
||||
|
||||
struct Ref
|
||||
{
|
||||
NonStupidBitField<Size>& BitField;
|
||||
u32 Idx;
|
||||
|
||||
operator bool()
|
||||
{
|
||||
return BitField.Data[Idx >> 3] & (1 << (Idx & 0x7));
|
||||
}
|
||||
|
||||
Ref& operator=(bool set)
|
||||
{
|
||||
BitField.Data[Idx >> 3] &= ~(1 << (Idx & 0x7));
|
||||
BitField.Data[Idx >> 3] |= ((u8)set << (Idx & 0x7));
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
struct Iterator
|
||||
{
|
||||
NonStupidBitField<Size>& BitField;
|
||||
u32 DataIdx;
|
||||
u32 BitIdx;
|
||||
u64 RemainingBits;
|
||||
|
||||
u32 operator*() { return DataIdx * 8 + BitIdx; }
|
||||
|
||||
bool operator==(const Iterator& other) { return other.DataIdx == DataIdx; }
|
||||
bool operator!=(const Iterator& other) { return other.DataIdx != DataIdx; }
|
||||
|
||||
template <typename T>
|
||||
void Next()
|
||||
{
|
||||
while (RemainingBits == 0 && DataIdx < DataLength)
|
||||
{
|
||||
DataIdx += sizeof(T);
|
||||
RemainingBits = *(T*)&BitField.Data[DataIdx];
|
||||
}
|
||||
|
||||
BitIdx = __builtin_ctzll(RemainingBits);
|
||||
RemainingBits &= ~(1ULL << BitIdx);
|
||||
}
|
||||
|
||||
Iterator operator++(int)
|
||||
{
|
||||
Iterator prev(*this);
|
||||
++*this;
|
||||
return prev;
|
||||
}
|
||||
|
||||
Iterator& operator++()
|
||||
{
|
||||
if ((DataLength % 8) == 0)
|
||||
Next<u64>();
|
||||
else if ((DataLength % 4) == 0)
|
||||
Next<u32>();
|
||||
else if ((DataLength % 2) == 0)
|
||||
Next<u16>();
|
||||
else
|
||||
Next<u8>();
|
||||
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
NonStupidBitField(u32 start, u32 size)
|
||||
{
|
||||
memset(Data, 0, sizeof(Data));
|
||||
|
||||
if (size == 0)
|
||||
return;
|
||||
|
||||
u32 roundedStartBit = (start + 7) & ~7;
|
||||
u32 roundedEndBit = (start + size) & ~7;
|
||||
if (roundedStartBit != roundedEndBit)
|
||||
memset(Data + roundedStartBit / 8, 0xFF, (roundedEndBit - roundedStartBit) / 8);
|
||||
|
||||
if (start & 0x7)
|
||||
Data[start >> 3] = 0xFF << (start & 0x7);
|
||||
if ((start + size) & 0x7)
|
||||
Data[(start + size) >> 3] = 0xFF >> ((start + size) & 0x7);
|
||||
}
|
||||
|
||||
NonStupidBitField()
|
||||
{
|
||||
memset(Data, 0, sizeof(Data));
|
||||
}
|
||||
|
||||
Iterator End()
|
||||
{
|
||||
return Iterator{*this, DataLength, 0, 0};
|
||||
}
|
||||
Iterator Begin()
|
||||
{
|
||||
if ((DataLength % 8) == 0)
|
||||
return ++Iterator{*this, 0, 0, *(u64*)Data};
|
||||
else if ((DataLength % 4) == 0)
|
||||
return ++Iterator{*this, 0, 0, *(u32*)Data};
|
||||
else if ((DataLength % 2) == 0)
|
||||
return ++Iterator{*this, 0, 0, *(u16*)Data};
|
||||
else
|
||||
return ++Iterator{*this, 0, 0, *Data};
|
||||
}
|
||||
|
||||
Ref operator[](u32 idx)
|
||||
{
|
||||
return Ref{*this, idx};
|
||||
}
|
||||
|
||||
NonStupidBitField& operator|=(const NonStupidBitField<Size>& other)
|
||||
{
|
||||
for (u32 i = 0; i < DataLength; i++)
|
||||
{
|
||||
Data[i] |= other.Data[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
NonStupidBitField& operator&=(const NonStupidBitField<Size>& other)
|
||||
{
|
||||
for (u32 i = 0; i < DataLength; i++)
|
||||
{
|
||||
Data[i] &= other.Data[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
@ -23,8 +23,13 @@
|
||||
#include <string.h>
|
||||
|
||||
// TODO: different includes for each platform
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
#ifdef __APPLE__
|
||||
#include <OpenGL/gl3.h>
|
||||
#include <OpenGL/gl3ext.h>
|
||||
#else
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
#endif
|
||||
|
||||
#include "Platform.h"
|
||||
|
||||
@ -61,6 +66,11 @@
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
#define DO_PROCLIST(func)
|
||||
|
||||
#else
|
||||
|
||||
#define DO_PROCLIST(func) \
|
||||
DO_PROCLIST_1_3(func) \
|
||||
@ -128,6 +138,7 @@
|
||||
\
|
||||
func(GLGETSTRINGI, glGetStringi); \
|
||||
|
||||
#endif
|
||||
|
||||
namespace OpenGL
|
||||
{
|
||||
|
@ -67,15 +67,24 @@ inline bool LocalFileExists(const char* name)
|
||||
return true;
|
||||
}
|
||||
|
||||
void* Thread_Create(void (*func)());
|
||||
void Thread_Free(void* thread);
|
||||
void Thread_Wait(void* thread);
|
||||
struct Thread;
|
||||
Thread* Thread_Create(void (*func)());
|
||||
void Thread_Free(Thread* thread);
|
||||
void Thread_Wait(Thread* thread);
|
||||
|
||||
void* Semaphore_Create();
|
||||
void Semaphore_Free(void* sema);
|
||||
void Semaphore_Reset(void* sema);
|
||||
void Semaphore_Wait(void* sema);
|
||||
void Semaphore_Post(void* sema);
|
||||
struct Semaphore;
|
||||
Semaphore* Semaphore_Create();
|
||||
void Semaphore_Free(Semaphore* sema);
|
||||
void Semaphore_Reset(Semaphore* sema);
|
||||
void Semaphore_Wait(Semaphore* sema);
|
||||
void Semaphore_Post(Semaphore* sema, int count = 1);
|
||||
|
||||
struct Mutex;
|
||||
Mutex* Mutex_Create();
|
||||
void Mutex_Free(Mutex* mutex);
|
||||
void Mutex_Lock(Mutex* mutex);
|
||||
void Mutex_Unlock(Mutex* mutex);
|
||||
bool Mutex_TryLock(Mutex* mutex);
|
||||
|
||||
void* GL_GetProcAddress(const char* proc);
|
||||
|
||||
|
@ -24,11 +24,11 @@ typedef struct
|
||||
u32 GameCode;
|
||||
u32 ROMSize;
|
||||
u32 SaveMemType;
|
||||
|
||||
|
||||
} ROMListEntry;
|
||||
|
||||
|
||||
ROMListEntry ROMList[] =
|
||||
ROMListEntry ROMList[] =
|
||||
{
|
||||
{0x41464141, 0x00800000, 0x00000004},
|
||||
{0x414D4155, 0x00800000, 0x00000008},
|
||||
@ -1143,7 +1143,7 @@ ROMListEntry ROMList[] =
|
||||
{0x454A4943, 0x00800000, 0x00000001},
|
||||
{0x454A4956, 0x04000000, 0x00000003},
|
||||
{0x454A4A42, 0x01000000, 0x00000001},
|
||||
{0x454A4A43, 0x00800000, 0x00000001},
|
||||
{0x454A4A43, 0x00800000, 0x00000002},
|
||||
{0x454A4C41, 0x01000000, 0x00000001},
|
||||
{0x454A4C42, 0x04000000, 0x00000001},
|
||||
{0x454A4C43, 0x08000000, 0x00000002},
|
||||
|
298
src/SPU.cpp
298
src/SPU.cpp
@ -18,6 +18,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "Platform.h"
|
||||
#include "NDS.h"
|
||||
#include "DSi.h"
|
||||
#include "SPU.h"
|
||||
@ -61,13 +62,15 @@ const s16 PSGTable[8][8] =
|
||||
{-0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF}
|
||||
};
|
||||
|
||||
const u32 kSamplesPerRun = 1;
|
||||
const u32 OutputBufferSize = 2*2048;
|
||||
s16 OutputBackbuffer[2 * OutputBufferSize];
|
||||
u32 OutputBackbufferWritePosition;
|
||||
|
||||
const u32 OutputBufferSize = 2*1024;
|
||||
s16 OutputBuffer[2 * OutputBufferSize];
|
||||
volatile u32 OutputReadOffset;
|
||||
volatile u32 OutputWriteOffset;
|
||||
s16 OutputFrontBuffer[2 * OutputBufferSize];
|
||||
u32 OutputFrontBufferWritePosition;
|
||||
u32 OutputFrontBufferReadPosition;
|
||||
|
||||
Platform::Mutex* AudioLock;
|
||||
|
||||
u16 Cnt;
|
||||
u8 MasterVolume;
|
||||
@ -85,6 +88,8 @@ bool Init()
|
||||
Capture[0] = new CaptureUnit(0);
|
||||
Capture[1] = new CaptureUnit(1);
|
||||
|
||||
AudioLock = Platform::Mutex_Create();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -95,6 +100,8 @@ void DeInit()
|
||||
|
||||
delete Capture[0];
|
||||
delete Capture[1];
|
||||
|
||||
Platform::Mutex_Free(AudioLock);
|
||||
}
|
||||
|
||||
void Reset()
|
||||
@ -111,15 +118,18 @@ void Reset()
|
||||
Capture[0]->Reset();
|
||||
Capture[1]->Reset();
|
||||
|
||||
NDS::ScheduleEvent(NDS::Event_SPU, true, 1024*kSamplesPerRun, Mix, kSamplesPerRun);
|
||||
NDS::ScheduleEvent(NDS::Event_SPU, true, 1024, Mix, 0);
|
||||
}
|
||||
|
||||
void Stop()
|
||||
{
|
||||
memset(OutputBuffer, 0, 2*OutputBufferSize*2);
|
||||
Platform::Mutex_Lock(AudioLock);
|
||||
memset(OutputFrontBuffer, 0, 2*OutputBufferSize*2);
|
||||
|
||||
OutputReadOffset = 0;
|
||||
OutputWriteOffset = 0;
|
||||
OutputBackbufferWritePosition = 0;
|
||||
OutputFrontBufferReadPosition = 0;
|
||||
OutputFrontBufferWritePosition = 0;
|
||||
Platform::Mutex_Unlock(AudioLock);
|
||||
}
|
||||
|
||||
void DoSavestate(Savestate* file)
|
||||
@ -416,11 +426,11 @@ void Channel::NextSample_Noise()
|
||||
}
|
||||
|
||||
template<u32 type>
|
||||
void Channel::Run(s32* buf, u32 samples)
|
||||
s32 Channel::Run()
|
||||
{
|
||||
if (!(Cnt & (1<<31))) return;
|
||||
if (!(Cnt & (1<<31))) return 0;
|
||||
|
||||
if ((type < 3) && ((Length+LoopPos) < 16)) return;
|
||||
if ((type < 3) && ((Length+LoopPos) < 16)) return 0;
|
||||
|
||||
if (KeyOn)
|
||||
{
|
||||
@ -428,45 +438,32 @@ void Channel::Run(s32* buf, u32 samples)
|
||||
KeyOn = false;
|
||||
}
|
||||
|
||||
for (u32 s = 0; s < samples; s++)
|
||||
Timer += 512; // 1 sample = 512 cycles at 16MHz
|
||||
|
||||
while (Timer >> 16)
|
||||
{
|
||||
Timer += 512; // 1 sample = 512 cycles at 16MHz
|
||||
Timer = TimerReload + (Timer - 0x10000);
|
||||
|
||||
while (Timer >> 16)
|
||||
switch (type)
|
||||
{
|
||||
Timer = TimerReload + (Timer - 0x10000);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case 0: NextSample_PCM8(); break;
|
||||
case 1: NextSample_PCM16(); break;
|
||||
case 2: NextSample_ADPCM(); break;
|
||||
case 3: NextSample_PSG(); break;
|
||||
case 4: NextSample_Noise(); break;
|
||||
}
|
||||
case 0: NextSample_PCM8(); break;
|
||||
case 1: NextSample_PCM16(); break;
|
||||
case 2: NextSample_ADPCM(); break;
|
||||
case 3: NextSample_PSG(); break;
|
||||
case 4: NextSample_Noise(); break;
|
||||
}
|
||||
|
||||
s32 val = (s32)CurSample;
|
||||
val <<= VolumeShift;
|
||||
val *= Volume;
|
||||
buf[s] = val;
|
||||
|
||||
if (!(Cnt & (1<<31))) break;
|
||||
}
|
||||
|
||||
s32 val = (s32)CurSample;
|
||||
val <<= VolumeShift;
|
||||
val *= Volume;
|
||||
return val;
|
||||
}
|
||||
|
||||
void Channel::PanOutput(s32* inbuf, u32 samples, s32* leftbuf, s32* rightbuf)
|
||||
void Channel::PanOutput(s32 in, s32& left, s32& right)
|
||||
{
|
||||
for (u32 s = 0; s < samples; s++)
|
||||
{
|
||||
s32 val = (s32)inbuf[s];
|
||||
|
||||
s32 l = ((s64)val * (128-Pan)) >> 10;
|
||||
s32 r = ((s64)val * Pan) >> 10;
|
||||
|
||||
leftbuf[s] += l;
|
||||
rightbuf[s] += r;
|
||||
}
|
||||
left += ((s64)in * (128-Pan)) >> 10;
|
||||
right += ((s64)in * Pan) >> 10;
|
||||
}
|
||||
|
||||
|
||||
@ -602,39 +599,31 @@ void CaptureUnit::Run(s32 sample)
|
||||
}
|
||||
|
||||
|
||||
void Mix(u32 samples)
|
||||
void Mix(u32 dummy)
|
||||
{
|
||||
s32 channelbuf[32];
|
||||
s32 leftbuf[32], rightbuf[32];
|
||||
s32 ch0buf[32], ch1buf[32], ch2buf[32], ch3buf[32];
|
||||
s32 leftoutput[32], rightoutput[32];
|
||||
|
||||
for (u32 s = 0; s < samples; s++)
|
||||
{
|
||||
leftbuf[s] = 0; rightbuf[s] = 0;
|
||||
leftoutput[s] = 0; rightoutput[s] = 0;
|
||||
}
|
||||
s32 left = 0, right = 0;
|
||||
s32 leftoutput = 0, rightoutput = 0;
|
||||
|
||||
if (Cnt & (1<<15))
|
||||
{
|
||||
Channels[0]->DoRun(ch0buf, samples);
|
||||
Channels[1]->DoRun(ch1buf, samples);
|
||||
Channels[2]->DoRun(ch2buf, samples);
|
||||
Channels[3]->DoRun(ch3buf, samples);
|
||||
s32 ch0 = Channels[0]->DoRun();
|
||||
s32 ch1 = Channels[1]->DoRun();
|
||||
s32 ch2 = Channels[2]->DoRun();
|
||||
s32 ch3 = Channels[3]->DoRun();
|
||||
|
||||
// TODO: addition from capture registers
|
||||
Channels[0]->PanOutput(ch0buf, samples, leftbuf, rightbuf);
|
||||
Channels[2]->PanOutput(ch2buf, samples, leftbuf, rightbuf);
|
||||
Channels[0]->PanOutput(ch0, left, right);
|
||||
Channels[2]->PanOutput(ch2, left, right);
|
||||
|
||||
if (!(Cnt & (1<<12))) Channels[1]->PanOutput(ch1buf, samples, leftbuf, rightbuf);
|
||||
if (!(Cnt & (1<<13))) Channels[3]->PanOutput(ch3buf, samples, leftbuf, rightbuf);
|
||||
if (!(Cnt & (1<<12))) Channels[1]->PanOutput(ch1, left, right);
|
||||
if (!(Cnt & (1<<13))) Channels[3]->PanOutput(ch3, left, right);
|
||||
|
||||
for (int i = 4; i < 16; i++)
|
||||
{
|
||||
Channel* chan = Channels[i];
|
||||
|
||||
chan->DoRun(channelbuf, samples);
|
||||
chan->PanOutput(channelbuf, samples, leftbuf, rightbuf);
|
||||
s32 channel = chan->DoRun();
|
||||
chan->PanOutput(channel, left, right);
|
||||
}
|
||||
|
||||
// sound capture
|
||||
@ -642,32 +631,24 @@ void Mix(u32 samples)
|
||||
|
||||
if (Capture[0]->Cnt & (1<<7))
|
||||
{
|
||||
for (u32 s = 0; s < samples; s++)
|
||||
{
|
||||
s32 val = leftbuf[s];
|
||||
s32 val = left;
|
||||
|
||||
val >>= 8;
|
||||
if (val < -0x8000) val = -0x8000;
|
||||
else if (val > 0x7FFF) val = 0x7FFF;
|
||||
val >>= 8;
|
||||
if (val < -0x8000) val = -0x8000;
|
||||
else if (val > 0x7FFF) val = 0x7FFF;
|
||||
|
||||
Capture[0]->Run(val);
|
||||
if (!(Capture[0]->Cnt & (1<<7))) break;
|
||||
}
|
||||
Capture[0]->Run(val);
|
||||
}
|
||||
|
||||
if (Capture[1]->Cnt & (1<<7))
|
||||
{
|
||||
for (u32 s = 0; s < samples; s++)
|
||||
{
|
||||
s32 val = rightbuf[s];
|
||||
s32 val = right;
|
||||
|
||||
val >>= 8;
|
||||
if (val < -0x8000) val = -0x8000;
|
||||
else if (val > 0x7FFF) val = 0x7FFF;
|
||||
val >>= 8;
|
||||
if (val < -0x8000) val = -0x8000;
|
||||
else if (val > 0x7FFF) val = 0x7FFF;
|
||||
|
||||
Capture[1]->Run(val);
|
||||
if (!(Capture[1]->Cnt & (1<<7))) break;
|
||||
}
|
||||
Capture[1]->Run(val);
|
||||
}
|
||||
|
||||
// final output
|
||||
@ -675,31 +656,25 @@ void Mix(u32 samples)
|
||||
switch (Cnt & 0x0300)
|
||||
{
|
||||
case 0x0000: // left mixer
|
||||
{
|
||||
for (u32 s = 0; s < samples; s++)
|
||||
leftoutput[s] = leftbuf[s];
|
||||
}
|
||||
leftoutput = left;
|
||||
break;
|
||||
case 0x0100: // channel 1
|
||||
{
|
||||
s32 pan = 128 - Channels[1]->Pan;
|
||||
for (u32 s = 0; s < samples; s++)
|
||||
leftoutput[s] = ((s64)ch1buf[s] * pan) >> 10;
|
||||
leftoutput = ((s64)ch1 * pan) >> 10;
|
||||
}
|
||||
break;
|
||||
case 0x0200: // channel 3
|
||||
{
|
||||
s32 pan = 128 - Channels[3]->Pan;
|
||||
for (u32 s = 0; s < samples; s++)
|
||||
leftoutput[s] = ((s64)ch3buf[s] * pan) >> 10;
|
||||
leftoutput = ((s64)ch3 * pan) >> 10;
|
||||
}
|
||||
break;
|
||||
case 0x0300: // channel 1+3
|
||||
{
|
||||
s32 pan1 = 128 - Channels[1]->Pan;
|
||||
s32 pan3 = 128 - Channels[3]->Pan;
|
||||
for (u32 s = 0; s < samples; s++)
|
||||
leftoutput[s] = (((s64)ch1buf[s] * pan1) >> 10) + (((s64)ch3buf[s] * pan3) >> 10);
|
||||
leftoutput = (((s64)ch1 * pan1) >> 10) + (((s64)ch3 * pan3) >> 10);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -707,105 +682,122 @@ void Mix(u32 samples)
|
||||
switch (Cnt & 0x0C00)
|
||||
{
|
||||
case 0x0000: // right mixer
|
||||
{
|
||||
for (u32 s = 0; s < samples; s++)
|
||||
rightoutput[s] = rightbuf[s];
|
||||
}
|
||||
rightoutput = right;
|
||||
break;
|
||||
case 0x0400: // channel 1
|
||||
{
|
||||
s32 pan = Channels[1]->Pan;
|
||||
for (u32 s = 0; s < samples; s++)
|
||||
rightoutput[s] = ((s64)ch1buf[s] * pan) >> 10;
|
||||
rightoutput = ((s64)ch1 * pan) >> 10;
|
||||
}
|
||||
break;
|
||||
case 0x0800: // channel 3
|
||||
{
|
||||
s32 pan = Channels[3]->Pan;
|
||||
for (u32 s = 0; s < samples; s++)
|
||||
rightoutput[s] = ((s64)ch3buf[s] * pan) >> 10;
|
||||
rightoutput = ((s64)ch3 * pan) >> 10;
|
||||
}
|
||||
break;
|
||||
case 0x0C00: // channel 1+3
|
||||
{
|
||||
s32 pan1 = Channels[1]->Pan;
|
||||
s32 pan3 = Channels[3]->Pan;
|
||||
for (u32 s = 0; s < samples; s++)
|
||||
rightoutput[s] = (((s64)ch1buf[s] * pan1) >> 10) + (((s64)ch3buf[s] * pan3) >> 10);
|
||||
rightoutput = (((s64)ch1 * pan1) >> 10) + (((s64)ch3 * pan3) >> 10);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (u32 s = 0; s < samples; s++)
|
||||
{
|
||||
s32 l = leftoutput[s];
|
||||
s32 r = rightoutput[s];
|
||||
leftoutput = ((s64)leftoutput * MasterVolume) >> 7;
|
||||
rightoutput = ((s64)rightoutput * MasterVolume) >> 7;
|
||||
|
||||
l = ((s64)l * MasterVolume) >> 7;
|
||||
r = ((s64)r * MasterVolume) >> 7;
|
||||
leftoutput >>= 8;
|
||||
if (leftoutput < -0x8000) leftoutput = -0x8000;
|
||||
else if (leftoutput > 0x7FFF) leftoutput = 0x7FFF;
|
||||
rightoutput >>= 8;
|
||||
if (rightoutput < -0x8000) rightoutput = -0x8000;
|
||||
else if (rightoutput > 0x7FFF) rightoutput = 0x7FFF;
|
||||
|
||||
l >>= 8;
|
||||
if (l < -0x8000) l = -0x8000;
|
||||
else if (l > 0x7FFF) l = 0x7FFF;
|
||||
r >>= 8;
|
||||
if (r < -0x8000) r = -0x8000;
|
||||
else if (r > 0x7FFF) r = 0x7FFF;
|
||||
// OutputBufferFrame can never get full because it's
|
||||
// transfered to OutputBuffer at the end of the frame
|
||||
OutputBackbuffer[OutputBackbufferWritePosition ] = leftoutput >> 1;
|
||||
OutputBackbuffer[OutputBackbufferWritePosition + 1] = rightoutput >> 1;
|
||||
OutputBackbufferWritePosition += 2;
|
||||
|
||||
OutputBuffer[OutputWriteOffset ] = l >> 1;
|
||||
OutputBuffer[OutputWriteOffset + 1] = r >> 1;
|
||||
OutputWriteOffset += 2;
|
||||
OutputWriteOffset &= ((2*OutputBufferSize)-1);
|
||||
if (OutputWriteOffset == OutputReadOffset)
|
||||
{
|
||||
//printf("!! SOUND FIFO OVERFLOW %d\n", OutputWriteOffset>>1);
|
||||
// advance the read position too, to avoid losing the entire FIFO
|
||||
OutputReadOffset += 2;
|
||||
OutputReadOffset &= ((2*OutputBufferSize)-1);
|
||||
}
|
||||
}
|
||||
|
||||
NDS::ScheduleEvent(NDS::Event_SPU, true, 1024*kSamplesPerRun, Mix, kSamplesPerRun);
|
||||
NDS::ScheduleEvent(NDS::Event_SPU, true, 1024, Mix, 0);
|
||||
}
|
||||
|
||||
void TransferOutput()
|
||||
{
|
||||
Platform::Mutex_Lock(AudioLock);
|
||||
for (u32 i = 0; i < OutputBackbufferWritePosition; i += 2)
|
||||
{
|
||||
OutputFrontBuffer[OutputFrontBufferWritePosition ] = OutputBackbuffer[i ];
|
||||
OutputFrontBuffer[OutputFrontBufferWritePosition + 1] = OutputBackbuffer[i + 1];
|
||||
|
||||
OutputFrontBufferWritePosition += 2;
|
||||
OutputFrontBufferWritePosition &= OutputBufferSize*2-1;
|
||||
if (OutputFrontBufferWritePosition == OutputFrontBufferReadPosition)
|
||||
{
|
||||
// advance the read position too, to avoid losing the entire FIFO
|
||||
OutputFrontBufferReadPosition += 2;
|
||||
OutputFrontBufferReadPosition &= OutputBufferSize*2-1;
|
||||
}
|
||||
}
|
||||
OutputBackbufferWritePosition = 0;
|
||||
Platform::Mutex_Unlock(AudioLock);
|
||||
}
|
||||
|
||||
void TrimOutput()
|
||||
{
|
||||
Platform::Mutex_Lock(AudioLock);
|
||||
const int halflimit = (OutputBufferSize / 2);
|
||||
|
||||
int readpos = OutputWriteOffset - (halflimit*2);
|
||||
int readpos = OutputFrontBufferWritePosition - (halflimit*2);
|
||||
if (readpos < 0) readpos += (OutputBufferSize*2);
|
||||
|
||||
OutputReadOffset = readpos;
|
||||
OutputFrontBufferReadPosition = readpos;
|
||||
Platform::Mutex_Unlock(AudioLock);
|
||||
}
|
||||
|
||||
void DrainOutput()
|
||||
{
|
||||
OutputReadOffset = 0;
|
||||
OutputWriteOffset = 0;
|
||||
Platform::Mutex_Lock(AudioLock);
|
||||
OutputFrontBufferWritePosition = 0;
|
||||
OutputFrontBufferReadPosition = 0;
|
||||
Platform::Mutex_Unlock(AudioLock);
|
||||
}
|
||||
|
||||
void InitOutput()
|
||||
{
|
||||
memset(OutputBuffer, 0, 2*OutputBufferSize*2);
|
||||
OutputReadOffset = 0;
|
||||
OutputWriteOffset = OutputBufferSize;
|
||||
Platform::Mutex_Lock(AudioLock);
|
||||
memset(OutputBackbuffer, 0, 2*OutputBufferSize*2);
|
||||
memset(OutputFrontBuffer, 0, 2*OutputBufferSize*2);
|
||||
OutputFrontBufferReadPosition = 0;
|
||||
OutputFrontBufferWritePosition = 0;
|
||||
Platform::Mutex_Unlock(AudioLock);
|
||||
}
|
||||
|
||||
int GetOutputSize()
|
||||
{
|
||||
Platform::Mutex_Lock(AudioLock);
|
||||
|
||||
int ret;
|
||||
if (OutputWriteOffset >= OutputReadOffset)
|
||||
ret = OutputWriteOffset - OutputReadOffset;
|
||||
if (OutputFrontBufferWritePosition >= OutputFrontBufferReadPosition)
|
||||
ret = OutputFrontBufferWritePosition - OutputFrontBufferReadPosition;
|
||||
else
|
||||
ret = (OutputBufferSize*2) - OutputReadOffset + OutputWriteOffset;
|
||||
ret = (OutputBufferSize*2) - OutputFrontBufferReadPosition + OutputFrontBufferWritePosition;
|
||||
|
||||
ret >>= 1;
|
||||
|
||||
Platform::Mutex_Unlock(AudioLock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Sync(bool wait)
|
||||
{
|
||||
// this function is currently not used anywhere
|
||||
// depending on the usage context the thread safety measures could be made
|
||||
// a lot faster
|
||||
|
||||
// sync to audio output in case the core is running too fast
|
||||
// * wait=true: wait until enough audio data has been played
|
||||
// * wait=false: merely skip some audio data to avoid a FIFO overflow
|
||||
@ -819,32 +811,42 @@ void Sync(bool wait)
|
||||
}
|
||||
else if (GetOutputSize() > halflimit)
|
||||
{
|
||||
int readpos = OutputWriteOffset - (halflimit*2);
|
||||
Platform::Mutex_Lock(AudioLock);
|
||||
|
||||
int readpos = OutputFrontBufferWritePosition - (halflimit*2);
|
||||
if (readpos < 0) readpos += (OutputBufferSize*2);
|
||||
|
||||
OutputReadOffset = readpos;
|
||||
OutputFrontBufferReadPosition = readpos;
|
||||
|
||||
Platform::Mutex_Unlock(AudioLock);
|
||||
}
|
||||
}
|
||||
|
||||
int ReadOutput(s16* data, int samples)
|
||||
{
|
||||
if (OutputReadOffset == OutputWriteOffset)
|
||||
Platform::Mutex_Lock(AudioLock);
|
||||
if (OutputFrontBufferReadPosition == OutputFrontBufferWritePosition)
|
||||
{
|
||||
Platform::Mutex_Unlock(AudioLock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < samples; i++)
|
||||
{
|
||||
*data++ = OutputBuffer[OutputReadOffset];
|
||||
*data++ = OutputBuffer[OutputReadOffset + 1];
|
||||
*data++ = OutputFrontBuffer[OutputFrontBufferReadPosition];
|
||||
*data++ = OutputFrontBuffer[OutputFrontBufferReadPosition + 1];
|
||||
|
||||
//if (OutputReadOffset != OutputWriteOffset)
|
||||
OutputFrontBufferReadPosition += 2;
|
||||
OutputFrontBufferReadPosition &= ((2*OutputBufferSize)-1);
|
||||
|
||||
if (OutputFrontBufferWritePosition == OutputFrontBufferReadPosition)
|
||||
{
|
||||
OutputReadOffset += 2;
|
||||
OutputReadOffset &= ((2*OutputBufferSize)-1);
|
||||
}
|
||||
if (OutputReadOffset == OutputWriteOffset)
|
||||
Platform::Mutex_Unlock(AudioLock);
|
||||
return i+1;
|
||||
}
|
||||
}
|
||||
|
||||
Platform::Mutex_Unlock(AudioLock);
|
||||
return samples;
|
||||
}
|
||||
|
||||
|
25
src/SPU.h
25
src/SPU.h
@ -33,7 +33,7 @@ void DoSavestate(Savestate* file);
|
||||
|
||||
void SetBias(u16 bias);
|
||||
|
||||
void Mix(u32 samples);
|
||||
void Mix(u32 dummy);
|
||||
|
||||
void TrimOutput();
|
||||
void DrainOutput();
|
||||
@ -41,6 +41,7 @@ void InitOutput();
|
||||
int GetOutputSize();
|
||||
void Sync(bool wait);
|
||||
int ReadOutput(s16* data, int samples);
|
||||
void TransferOutput();
|
||||
|
||||
u8 Read8(u32 addr);
|
||||
u16 Read16(u32 addr);
|
||||
@ -123,26 +124,24 @@ public:
|
||||
void NextSample_PSG();
|
||||
void NextSample_Noise();
|
||||
|
||||
template<u32 type> void Run(s32* buf, u32 samples);
|
||||
template<u32 type> s32 Run();
|
||||
|
||||
void DoRun(s32* buf, u32 samples)
|
||||
s32 DoRun()
|
||||
{
|
||||
for (u32 s = 0; s < samples; s++)
|
||||
buf[s] = 0;
|
||||
|
||||
switch ((Cnt >> 29) & 0x3)
|
||||
{
|
||||
case 0: Run<0>(buf, samples); break;
|
||||
case 1: Run<1>(buf, samples); break;
|
||||
case 2: Run<2>(buf, samples); break;
|
||||
case 0: return Run<0>(); break;
|
||||
case 1: return Run<1>(); break;
|
||||
case 2: return Run<2>(); break;
|
||||
case 3:
|
||||
if (Num >= 14) Run<4>(buf, samples);
|
||||
else if (Num >= 8) Run<3>(buf, samples);
|
||||
break;
|
||||
if (Num >= 14) return Run<4>();
|
||||
else if (Num >= 8) return Run<3>();
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void PanOutput(s32* inbuf, u32 samples, s32* leftbuf, s32* rightbuf);
|
||||
void PanOutput(s32 in, s32& left, s32& right);
|
||||
|
||||
private:
|
||||
u32 (*BusRead32)(u32 addr);
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include <stdio.h>
|
||||
#include "types.h"
|
||||
|
||||
#define SAVESTATE_MAJOR 6
|
||||
#define SAVESTATE_MAJOR 7
|
||||
#define SAVESTATE_MINOR 0
|
||||
|
||||
class Savestate
|
||||
|
13
src/frontend/SharedConfig.h
Normal file
13
src/frontend/SharedConfig.h
Normal file
@ -0,0 +1,13 @@
|
||||
#ifndef SHAREDCONFIG_H
|
||||
#define SHAREDCONFIG_H
|
||||
|
||||
namespace Config
|
||||
{
|
||||
|
||||
extern int ConsoleType;
|
||||
extern int DirectBoot;
|
||||
extern int SavestateRelocSRAM;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -21,7 +21,7 @@
|
||||
|
||||
#include "FrontendUtil.h"
|
||||
#include "Config.h"
|
||||
#include "qt_sdl/PlatformConfig.h" // FIXME!!!
|
||||
#include "SharedConfig.h"
|
||||
#include "Platform.h"
|
||||
|
||||
#include "NDS.h"
|
||||
|
@ -100,6 +100,19 @@ if (PORTABLE)
|
||||
add_definitions(-DPORTABLE)
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
set_target_properties(melonDS PROPERTIES
|
||||
MACOSX_BUNDLE true
|
||||
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/melonDS.plist
|
||||
OUTPUT_NAME melonDS
|
||||
)
|
||||
|
||||
# Copy icon into the bundle
|
||||
target_sources(melonDS PRIVATE "${CMAKE_SOURCE_DIR}/melonDS.icns")
|
||||
set_source_files_properties("${CMAKE_SOURCE_DIR}/melonDS.icns" PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||
|
||||
endif()
|
||||
|
||||
install(FILES ../../../net.kuribo64.melonDS.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications)
|
||||
install(FILES ../../../icon/melon_16x16.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/16x16/apps RENAME net.kuribo64.melonDS.png)
|
||||
install(FILES ../../../icon/melon_32x32.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/32x32/apps RENAME net.kuribo64.melonDS.png)
|
||||
@ -107,4 +120,4 @@ install(FILES ../../../icon/melon_48x48.png DESTINATION ${CMAKE_INSTALL_PREFIX}/
|
||||
install(FILES ../../../icon/melon_64x64.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/64x64/apps RENAME net.kuribo64.melonDS.png)
|
||||
install(FILES ../../../icon/melon_128x128.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/128x128/apps RENAME net.kuribo64.melonDS.png)
|
||||
install(FILES ../../../icon/melon_256x256.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/256x256/apps RENAME net.kuribo64.melonDS.png)
|
||||
install(TARGETS melonDS RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
|
||||
install(TARGETS melonDS BUNDLE DESTINATION ${CMAKE_BINARY_DIR} RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
|
||||
|
@ -65,6 +65,9 @@ EmuSettingsDialog::EmuSettingsDialog(QWidget* parent) : QDialog(parent), ui(new
|
||||
ui->chkJITBranchOptimisations->setChecked(Config::JIT_BranchOptimisations != 0);
|
||||
ui->chkJITLiteralOptimisations->setChecked(Config::JIT_LiteralOptimisations != 0);
|
||||
ui->chkJITFastMemory->setChecked(Config::JIT_FastMemory != 0);
|
||||
#ifdef __APPLE__
|
||||
ui->chkJITFastMemory->setDisabled(true);
|
||||
#endif
|
||||
ui->spnJITMaximumBlockSize->setValue(Config::JIT_MaxBlockSize);
|
||||
#else
|
||||
ui->chkEnableJIT->setDisabled(true);
|
||||
@ -329,6 +332,8 @@ void EmuSettingsDialog::on_chkEnableJIT_toggled()
|
||||
bool disabled = !ui->chkEnableJIT->isChecked();
|
||||
ui->chkJITBranchOptimisations->setDisabled(disabled);
|
||||
ui->chkJITLiteralOptimisations->setDisabled(disabled);
|
||||
ui->chkJITFastMemory->setDisabled(disabled);
|
||||
#ifndef __APPLE__
|
||||
ui->chkJITFastMemory->setDisabled(disabled);
|
||||
#endif
|
||||
ui->spnJITMaximumBlockSize->setDisabled(disabled);
|
||||
}
|
||||
|
@ -216,6 +216,7 @@ KeyMapButton::KeyMapButton(int* mapping, bool hotkey) : QPushButton()
|
||||
|
||||
setCheckable(true);
|
||||
setText(mappingText());
|
||||
setFocusPolicy(Qt::StrongFocus); //Fixes binding keys in macOS
|
||||
|
||||
connect(this, &KeyMapButton::clicked, this, &KeyMapButton::onClick);
|
||||
}
|
||||
|
@ -33,7 +33,11 @@
|
||||
#include <sys/types.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <netinet/in.h>
|
||||
#include <linux/if_packet.h>
|
||||
#ifdef __linux__
|
||||
#include <linux/if_packet.h>
|
||||
#else
|
||||
#include <net/if_dl.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
@ -66,6 +70,9 @@ const char* PCapLibNames[] =
|
||||
#ifdef __WIN32__
|
||||
// TODO: name for npcap in non-WinPCap mode
|
||||
"wpcap.dll",
|
||||
#elif defined(__APPLE__)
|
||||
"libpcap.A.dylib",
|
||||
"libpcap.dylib",
|
||||
#else
|
||||
// Linux lib names
|
||||
"libpcap.so.1",
|
||||
@ -276,6 +283,7 @@ bool Init(bool open_adapter)
|
||||
struct sockaddr_in* sa = (sockaddr_in*)curaddr->ifa_addr;
|
||||
memcpy(adata->IP_v4, &sa->sin_addr, 4);
|
||||
}
|
||||
#ifdef __linux__
|
||||
else if (af == AF_PACKET)
|
||||
{
|
||||
struct sockaddr_ll* sa = (sockaddr_ll*)curaddr->ifa_addr;
|
||||
@ -284,7 +292,16 @@ bool Init(bool open_adapter)
|
||||
else
|
||||
memcpy(adata->MAC, sa->sll_addr, 6);
|
||||
}
|
||||
|
||||
#else
|
||||
else if (af == AF_LINK)
|
||||
{
|
||||
struct sockaddr_dl* sa = (sockaddr_dl*)curaddr->ifa_addr;
|
||||
if (sa->sdl_alen != 6)
|
||||
printf("weird MAC length %d for %s\n", sa->sdl_alen, curaddr->ifa_name);
|
||||
else
|
||||
memcpy(adata->MAC, LLADDR(sa), 6);
|
||||
}
|
||||
#endif
|
||||
curaddr = curaddr->ifa_next;
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <QDir>
|
||||
#include <QThread>
|
||||
#include <QSemaphore>
|
||||
#include <QMutex>
|
||||
#include <QOpenGLContext>
|
||||
|
||||
#include "Platform.h"
|
||||
@ -187,53 +188,77 @@ FILE* OpenLocalFile(const char* path, const char* mode)
|
||||
return OpenFile(fullpath.toUtf8(), mode, mode[0] != 'w');
|
||||
}
|
||||
|
||||
void* Thread_Create(void (* func)())
|
||||
Thread* Thread_Create(void (* func)())
|
||||
{
|
||||
QThread* t = QThread::create(func);
|
||||
t->start();
|
||||
return (void*) t;
|
||||
return (Thread*) t;
|
||||
}
|
||||
|
||||
void Thread_Free(void* thread)
|
||||
void Thread_Free(Thread* thread)
|
||||
{
|
||||
QThread* t = (QThread*) thread;
|
||||
t->terminate();
|
||||
delete t;
|
||||
}
|
||||
|
||||
void Thread_Wait(void* thread)
|
||||
void Thread_Wait(Thread* thread)
|
||||
{
|
||||
((QThread*) thread)->wait();
|
||||
}
|
||||
|
||||
|
||||
void* Semaphore_Create()
|
||||
Semaphore* Semaphore_Create()
|
||||
{
|
||||
return new QSemaphore();
|
||||
return (Semaphore*)new QSemaphore();
|
||||
}
|
||||
|
||||
void Semaphore_Free(void* sema)
|
||||
void Semaphore_Free(Semaphore* sema)
|
||||
{
|
||||
delete (QSemaphore*) sema;
|
||||
}
|
||||
|
||||
void Semaphore_Reset(void* sema)
|
||||
void Semaphore_Reset(Semaphore* sema)
|
||||
{
|
||||
QSemaphore* s = (QSemaphore*) sema;
|
||||
|
||||
s->acquire(s->available());
|
||||
}
|
||||
|
||||
void Semaphore_Wait(void* sema)
|
||||
void Semaphore_Wait(Semaphore* sema)
|
||||
{
|
||||
((QSemaphore*) sema)->acquire();
|
||||
}
|
||||
|
||||
void Semaphore_Post(void* sema)
|
||||
void Semaphore_Post(Semaphore* sema, int count)
|
||||
{
|
||||
((QSemaphore*) sema)->release();
|
||||
((QSemaphore*) sema)->release(count);
|
||||
}
|
||||
|
||||
Mutex* Mutex_Create()
|
||||
{
|
||||
return (Mutex*)new QMutex();
|
||||
}
|
||||
|
||||
void Mutex_Free(Mutex* mutex)
|
||||
{
|
||||
delete (QMutex*) mutex;
|
||||
}
|
||||
|
||||
void Mutex_Lock(Mutex* mutex)
|
||||
{
|
||||
((QMutex*) mutex)->lock();
|
||||
}
|
||||
|
||||
void Mutex_Unlock(Mutex* mutex)
|
||||
{
|
||||
((QMutex*) mutex)->unlock();
|
||||
}
|
||||
|
||||
bool Mutex_TryLock(Mutex* mutex)
|
||||
{
|
||||
return ((QMutex*) mutex)->try_lock();
|
||||
}
|
||||
|
||||
void* GL_GetProcAddress(const char* proc)
|
||||
{
|
||||
|
@ -120,7 +120,7 @@ ConfigEntry PlatformConfigFile[] =
|
||||
{"HKJoy_Reset", 0, &HKJoyMapping[HK_Reset], -1, NULL, 0},
|
||||
{"HKJoy_FastForward", 0, &HKJoyMapping[HK_FastForward], -1, NULL, 0},
|
||||
{"HKJoy_FastForwardToggle", 0, &HKJoyMapping[HK_FastForwardToggle], -1, NULL, 0},
|
||||
{"HKJoy_FastForwardToggle", 0, &HKJoyMapping[HK_FullscreenToggle], -1, NULL, 0},
|
||||
{"HKJoy_FullscreenToggle", 0, &HKJoyMapping[HK_FullscreenToggle], -1, NULL, 0},
|
||||
{"HKJoy_SolarSensorDecrease", 0, &HKJoyMapping[HK_SolarSensorDecrease], -1, NULL, 0},
|
||||
{"HKJoy_SolarSensorIncrease", 0, &HKJoyMapping[HK_SolarSensorIncrease], -1, NULL, 0},
|
||||
|
||||
|
@ -54,7 +54,7 @@ WifiSettingsDialog::WifiSettingsDialog(QWidget* parent) : QDialog(parent), ui(ne
|
||||
LAN_Socket::Init();
|
||||
haspcap = LAN_PCap::Init(false);
|
||||
|
||||
ui->cbDirectMode->setText("Direct mode (requires " PCAP_NAME " and ethernet connection)");
|
||||
ui->rbDirectMode->setText("Direct mode (requires " PCAP_NAME " and ethernet connection)");
|
||||
|
||||
ui->cbBindAnyAddr->setChecked(Config::SocketBindAnyAddr != 0);
|
||||
ui->cbRandomizeMAC->setChecked(Config::RandomizeMAC != 0);
|
||||
@ -71,8 +71,9 @@ WifiSettingsDialog::WifiSettingsDialog(QWidget* parent) : QDialog(parent), ui(ne
|
||||
}
|
||||
ui->cbxDirectAdapter->setCurrentIndex(sel);
|
||||
|
||||
ui->cbDirectMode->setChecked(Config::DirectLAN != 0);
|
||||
if (!haspcap) ui->cbDirectMode->setEnabled(false);
|
||||
ui->rbDirectMode->setChecked(Config::DirectLAN != 0);
|
||||
ui->rbIndirectMode->setChecked(Config::DirectLAN == 0);
|
||||
if (!haspcap) ui->rbDirectMode->setEnabled(false);
|
||||
|
||||
updateAdapterControls();
|
||||
}
|
||||
@ -101,7 +102,7 @@ void WifiSettingsDialog::done(int r)
|
||||
|
||||
Config::SocketBindAnyAddr = ui->cbBindAnyAddr->isChecked() ? 1:0;
|
||||
Config::RandomizeMAC = randommac;
|
||||
Config::DirectLAN = ui->cbDirectMode->isChecked() ? 1:0;
|
||||
Config::DirectLAN = ui->rbDirectMode->isChecked() ? 1:0;
|
||||
|
||||
int sel = ui->cbxDirectAdapter->currentIndex();
|
||||
if (sel < 0 || sel >= LAN_PCap::NumAdapters) sel = 0;
|
||||
@ -125,11 +126,14 @@ void WifiSettingsDialog::done(int r)
|
||||
closeDlg();
|
||||
}
|
||||
|
||||
void WifiSettingsDialog::on_cbDirectMode_stateChanged(int state)
|
||||
void WifiSettingsDialog::on_rbDirectMode_clicked()
|
||||
{
|
||||
updateAdapterControls();
|
||||
}
|
||||
void WifiSettingsDialog::on_rbIndirectMode_clicked()
|
||||
{
|
||||
updateAdapterControls();
|
||||
}
|
||||
|
||||
void WifiSettingsDialog::on_cbxDirectAdapter_currentIndexChanged(int sel)
|
||||
{
|
||||
if (!haspcap) return;
|
||||
@ -153,7 +157,7 @@ void WifiSettingsDialog::on_cbxDirectAdapter_currentIndexChanged(int sel)
|
||||
|
||||
void WifiSettingsDialog::updateAdapterControls()
|
||||
{
|
||||
bool enable = haspcap && ui->cbDirectMode->isChecked();
|
||||
bool enable = haspcap && ui->rbDirectMode->isChecked();
|
||||
|
||||
ui->cbxDirectAdapter->setEnabled(enable);
|
||||
ui->lblAdapterMAC->setEnabled(enable);
|
||||
|
@ -55,7 +55,8 @@ public:
|
||||
private slots:
|
||||
void done(int r);
|
||||
|
||||
void on_cbDirectMode_stateChanged(int state);
|
||||
void on_rbDirectMode_clicked();
|
||||
void on_rbIndirectMode_clicked();
|
||||
void on_cbxDirectAdapter_currentIndexChanged(int sel);
|
||||
|
||||
private:
|
||||
|
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>479</width>
|
||||
<height>240</height>
|
||||
<width>572</width>
|
||||
<height>296</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@ -58,15 +58,81 @@
|
||||
<string>Online</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<item row="3" column="0" rowspan="3" colspan="2">
|
||||
<widget class="QGroupBox" name="groupBox_3">
|
||||
<property name="title">
|
||||
<string>Direct Mode Settings</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Network adapter:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="cbxDirectAdapter">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>300</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string><html><head/><body><p>Selects the network adapter through which to route network traffic under direct mode.</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>MAC address:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="lblAdapterMAC">
|
||||
<property name="text">
|
||||
<string>[PLACEHOLDER]</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>IP address:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="lblAdapterIP">
|
||||
<property name="text">
|
||||
<string>[PLACEHOLDER]</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QRadioButton" name="rbIndirectMode">
|
||||
<property name="whatsThis">
|
||||
<string><html><head/><body><p>Indirect mode uses libslirp. It requires no extra setup and is easy to use.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>MAC address:</string>
|
||||
<string>Indirect Mode (uses libslirp, recommended)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="cbDirectMode">
|
||||
<item row="2" column="0">
|
||||
<widget class="QRadioButton" name="rbDirectMode">
|
||||
<property name="whatsThis">
|
||||
<string><html><head/><body><p>Direct mode directly routes network traffic to the host network. It is the most reliable, but requires an ethernet connection.</p><p><br/></p><p>Non-direct mode uses a layer of emulation to get around this, but is more prone to problems.</p></body></html></string>
|
||||
</property>
|
||||
@ -75,53 +141,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="cbxDirectAdapter">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>350</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string><html><head/><body><p>Selects the network adapter through which to route network traffic under direct mode.</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Network adapter:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>IP address:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="lblAdapterMAC">
|
||||
<property name="text">
|
||||
<string>[PLACEHOLDER]</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="lblAdapterIP">
|
||||
<property name="text">
|
||||
<string>[PLACEHOLDER]</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -274,6 +274,7 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent)
|
||||
connect(this, SIGNAL(windowEmuStop()), mainWindow, SLOT(onEmuStop()));
|
||||
connect(this, SIGNAL(windowEmuPause()), mainWindow->actPause, SLOT(trigger()));
|
||||
connect(this, SIGNAL(windowEmuReset()), mainWindow->actReset, SLOT(trigger()));
|
||||
connect(this, SIGNAL(windowLimitFPSChange()), mainWindow->actLimitFramerate, SLOT(trigger()));
|
||||
connect(this, SIGNAL(screenLayoutChange()), mainWindow->panel, SLOT(onScreenLayoutChanged()));
|
||||
connect(this, SIGNAL(windowFullscreenToggle()), mainWindow, SLOT(onFullscreenToggled()));
|
||||
|
||||
@ -363,10 +364,10 @@ void EmuThread::run()
|
||||
Input::Init();
|
||||
|
||||
u32 nframes = 0;
|
||||
u32 starttick = SDL_GetTicks();
|
||||
u32 lasttick = starttick;
|
||||
u32 lastmeasuretick = lasttick;
|
||||
u32 fpslimitcount = 0;
|
||||
double perfCountsSec = 1.0 / SDL_GetPerformanceFrequency();
|
||||
double lastTime = SDL_GetPerformanceCounter() * perfCountsSec;
|
||||
double frameLimitError = 0.0;
|
||||
double lastMeasureTime = lastTime;
|
||||
|
||||
char melontitle[100];
|
||||
|
||||
@ -378,7 +379,7 @@ void EmuThread::run()
|
||||
|
||||
if (Input::HotkeyPressed(HK_Pause)) emit windowEmuPause();
|
||||
if (Input::HotkeyPressed(HK_Reset)) emit windowEmuReset();
|
||||
|
||||
|
||||
if (Input::HotkeyPressed(HK_FullscreenToggle)) emit windowFullscreenToggle();
|
||||
|
||||
if (GBACart::CartInserted && GBACart::HasSolarSensor)
|
||||
@ -500,49 +501,43 @@ void EmuThread::run()
|
||||
SDL_UnlockMutex(audioSyncLock);
|
||||
}
|
||||
|
||||
float framerate = (1000.0f * nlines) / (60.0f * 263.0f);
|
||||
double frametimeStep = nlines / (60.0 * 263.0);
|
||||
|
||||
{
|
||||
u32 curtick = SDL_GetTicks();
|
||||
u32 delay = curtick - lasttick;
|
||||
|
||||
bool limitfps = Config::LimitFPS && !fastforward;
|
||||
if (limitfps)
|
||||
{
|
||||
float wantedtickF = starttick + (framerate * (fpslimitcount+1));
|
||||
u32 wantedtick = (u32)ceil(wantedtickF);
|
||||
if (curtick < wantedtick) SDL_Delay(wantedtick - curtick);
|
||||
|
||||
lasttick = SDL_GetTicks();
|
||||
fpslimitcount++;
|
||||
if ((abs(wantedtickF - (float)wantedtick) < 0.001312) || (fpslimitcount > 60))
|
||||
{
|
||||
fpslimitcount = 0;
|
||||
starttick = lasttick;
|
||||
}
|
||||
}
|
||||
else
|
||||
double practicalFramelimit = limitfps ? frametimeStep : 1.0 / 1000.0;
|
||||
|
||||
double curtime = SDL_GetPerformanceCounter() * perfCountsSec;
|
||||
|
||||
frameLimitError += practicalFramelimit - (curtime - lastTime);
|
||||
if (frameLimitError < -practicalFramelimit)
|
||||
frameLimitError = -practicalFramelimit;
|
||||
if (frameLimitError > practicalFramelimit)
|
||||
frameLimitError = practicalFramelimit;
|
||||
|
||||
if (round(frameLimitError * 1000.0) > 0.0)
|
||||
{
|
||||
if (delay < 1) SDL_Delay(1);
|
||||
lasttick = SDL_GetTicks();
|
||||
SDL_Delay(round(frameLimitError * 1000.0));
|
||||
double timeBeforeSleep = curtime;
|
||||
curtime = SDL_GetPerformanceCounter() * perfCountsSec;
|
||||
frameLimitError -= curtime - timeBeforeSleep;
|
||||
}
|
||||
|
||||
lastTime = curtime;
|
||||
}
|
||||
|
||||
nframes++;
|
||||
if (nframes >= 30)
|
||||
{
|
||||
u32 tick = SDL_GetTicks();
|
||||
u32 diff = tick - lastmeasuretick;
|
||||
lastmeasuretick = tick;
|
||||
double time = SDL_GetPerformanceCounter() * perfCountsSec;
|
||||
double dt = time - lastMeasureTime;
|
||||
lastMeasureTime = time;
|
||||
|
||||
u32 fps;
|
||||
if (diff < 1) fps = 77777;
|
||||
else fps = (nframes * 1000) / diff;
|
||||
u32 fps = round(nframes / dt);
|
||||
nframes = 0;
|
||||
|
||||
float fpstarget;
|
||||
if (framerate < 1) fpstarget = 999;
|
||||
else fpstarget = 1000.0f/framerate;
|
||||
float fpstarget = 1.0/frametimeStep;
|
||||
|
||||
sprintf(melontitle, "[%d/%.0f] melonDS " MELONDS_VERSION, fps, fpstarget);
|
||||
changeWindowTitle(melontitle);
|
||||
@ -552,10 +547,8 @@ void EmuThread::run()
|
||||
{
|
||||
// paused
|
||||
nframes = 0;
|
||||
lasttick = SDL_GetTicks();
|
||||
starttick = lasttick;
|
||||
lastmeasuretick = lasttick;
|
||||
fpslimitcount = 0;
|
||||
lastTime = SDL_GetPerformanceCounter() * perfCountsSec;
|
||||
lastMeasureTime = lastTime;
|
||||
|
||||
emit windowUpdate();
|
||||
|
||||
@ -1339,6 +1332,7 @@ void MainWindow::keyPressEvent(QKeyEvent* event)
|
||||
{
|
||||
if (event->isAutoRepeat()) return;
|
||||
|
||||
// TODO!! REMOVE ME IN RELEASE BUILDS!!
|
||||
if (event->key() == Qt::Key_F11) NDS::debug(0);
|
||||
|
||||
Input::KeyPress(event);
|
||||
@ -1362,7 +1356,7 @@ void MainWindow::dragEnterEvent(QDragEnterEvent* event)
|
||||
QString filename = urls.at(0).toLocalFile();
|
||||
QString ext = filename.right(3);
|
||||
|
||||
if (ext == "nds" || ext == "srl" || ext == "dsi" || (ext == "gba" && RunningSomething))
|
||||
if (ext == "nds" || ext == "srl" || ext == "dsi" || ext == "gba")
|
||||
event->acceptProposedAction();
|
||||
}
|
||||
|
||||
@ -1986,9 +1980,9 @@ void MainWindow::onTitleUpdate(QString title)
|
||||
|
||||
void MainWindow::onFullscreenToggled()
|
||||
{
|
||||
if (!mainWindow->isFullScreen())
|
||||
if (!mainWindow->isFullScreen())
|
||||
{
|
||||
mainWindow->showFullScreen();
|
||||
mainWindow->showFullScreen();
|
||||
mainWindow->menuBar()->hide();
|
||||
}
|
||||
else
|
||||
|
Loading…
Reference in New Issue
Block a user