More libultra function implementations, euc-jp decoding for print output, improved build times for output project
This commit is contained in:
parent
c6de2b6189
commit
d2603ce07c
4
recomp.h
4
recomp.h
|
@ -182,10 +182,10 @@ void do_break(uint32_t vram);
|
|||
|
||||
typedef void (recomp_func_t)(uint8_t* restrict rdram, recomp_context* restrict ctx);
|
||||
|
||||
recomp_func_t* get_function(uint32_t vram);
|
||||
recomp_func_t* get_function(int32_t vram);
|
||||
|
||||
#define LOOKUP_FUNC(val) \
|
||||
get_function(val)
|
||||
get_function((int32_t)(val))
|
||||
|
||||
// For the Mario Party games (not working)
|
||||
//// This has to be in this file so it can be inlined
|
||||
|
|
|
@ -2,11 +2,12 @@
|
|||
#define __SECTIONS_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include "recomp.h"
|
||||
|
||||
#define ARRLEN(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
typedef struct {
|
||||
void* func;
|
||||
recomp_func_t* func;
|
||||
uint32_t offset;
|
||||
} FuncEntry;
|
||||
|
||||
|
|
130
src/main.cpp
130
src/main.cpp
|
@ -17,12 +17,14 @@ std::unordered_set<std::string> reimplemented_funcs{
|
|||
// OS initialize functions
|
||||
"__osInitialize_common",
|
||||
"osInitialize",
|
||||
"osGetMemSize",
|
||||
// Audio interface functions
|
||||
"osAiGetLength",
|
||||
"osAiGetStatus",
|
||||
"osAiSetFrequency",
|
||||
"osAiSetNextBuffer",
|
||||
// Video interface functions
|
||||
"osViSetXScale",
|
||||
"osViSetYScale",
|
||||
"osCreateViManager",
|
||||
"osViBlack",
|
||||
|
@ -44,6 +46,8 @@ std::unordered_set<std::string> reimplemented_funcs{
|
|||
"osContInit",
|
||||
"osContStartReadData",
|
||||
"osContGetReadData",
|
||||
"osContStartQuery",
|
||||
"osContGetQuery",
|
||||
"osContSetCh",
|
||||
// EEPROM functions
|
||||
"osEepromProbe",
|
||||
|
@ -56,6 +60,14 @@ std::unordered_set<std::string> reimplemented_funcs{
|
|||
"osMotorInit",
|
||||
"osMotorStart",
|
||||
"osMotorStop",
|
||||
// PFS functions
|
||||
"osPfsInitPak",
|
||||
"osPfsFreeBlocks",
|
||||
"osPfsAllocateFile",
|
||||
"osPfsDeleteFile",
|
||||
"osPfsFileState",
|
||||
"osPfsFindFile",
|
||||
"osPfsReadWriteFile",
|
||||
// Parallel interface (cartridge, DMA, etc.) functions
|
||||
"osCartRomInit",
|
||||
"osCreatePiManager",
|
||||
|
@ -63,6 +75,7 @@ std::unordered_set<std::string> reimplemented_funcs{
|
|||
"osEPiStartDma",
|
||||
"osPiGetStatus",
|
||||
"osEPiRawStartDma",
|
||||
"osEPiReadIo",
|
||||
// Threading functions
|
||||
"osCreateThread",
|
||||
"osStartThread",
|
||||
|
@ -79,6 +92,8 @@ std::unordered_set<std::string> reimplemented_funcs{
|
|||
"osSetEventMesg",
|
||||
// Timer functions
|
||||
"osGetTime",
|
||||
"osSetTimer",
|
||||
"osStopTimer",
|
||||
// interrupt functions
|
||||
"osSetIntMask",
|
||||
"__osDisableInt",
|
||||
|
@ -94,6 +109,7 @@ std::unordered_set<std::string> reimplemented_funcs{
|
|||
"osWritebackDCache",
|
||||
"osWritebackDCacheAll",
|
||||
// Debug functions
|
||||
"is_proutSyncPrintf",
|
||||
"__checkHardware_msp",
|
||||
"__checkHardware_kmc",
|
||||
"__checkHardware_isv",
|
||||
|
@ -119,6 +135,7 @@ std::unordered_set<std::string> ignored_funcs {
|
|||
"__createSpeedParam",
|
||||
"__osInitialize_common",
|
||||
"osInitialize",
|
||||
"osGetMemSize",
|
||||
// Audio interface functions
|
||||
"osAiGetLength",
|
||||
"osAiGetStatus",
|
||||
|
@ -615,26 +632,74 @@ bool read_symbols(RecompPort::Context& context, const ELFIO::elfio& elf_file, EL
|
|||
return found_entrypoint_func;
|
||||
}
|
||||
|
||||
struct SegmentEntry {
|
||||
ELFIO::Elf64_Off data_offset;
|
||||
ELFIO::Elf64_Addr physical_address;
|
||||
ELFIO::Elf_Xword memory_size;
|
||||
};
|
||||
|
||||
ELFIO::section* read_sections(RecompPort::Context& context, const ELFIO::elfio& elf_file) {
|
||||
ELFIO::section* symtab_section = nullptr;
|
||||
std::vector<SegmentEntry> segments{};
|
||||
segments.resize(elf_file.segments.size());
|
||||
|
||||
// Copy the data for each segment into the segment entry list
|
||||
for (size_t segment_index = 0; segment_index < elf_file.segments.size(); segment_index++) {
|
||||
const auto& segment = *elf_file.segments[segment_index];
|
||||
segments[segment_index].data_offset = segment.get_offset();
|
||||
segments[segment_index].physical_address = segment.get_physical_address();
|
||||
segments[segment_index].memory_size = segment.get_memory_size();
|
||||
}
|
||||
|
||||
// Sort the segments by physical address
|
||||
std::sort(segments.begin(), segments.end(),
|
||||
[](const SegmentEntry& lhs, const SegmentEntry& rhs) {
|
||||
return lhs.data_offset < rhs.data_offset;
|
||||
}
|
||||
);
|
||||
|
||||
// Iterate over every section to record rom addresses and find the symbol table
|
||||
fmt::print("Sections\n");
|
||||
for (const std::unique_ptr<ELFIO::section>& section : elf_file.sections) {
|
||||
for (const auto& section : elf_file.sections) {
|
||||
auto& section_out = context.sections[section->get_index()];
|
||||
//fmt::print(" {}: {} @ 0x{:08X}, 0x{:08X}\n", section->get_index(), section->get_name(), section->get_address(), context.rom.size());
|
||||
// Set the rom address of this section to the current accumulated ROM size
|
||||
section_out.rom_addr = context.rom.size();
|
||||
section_out.ram_addr = section->get_address();
|
||||
section_out.size = section->get_size();
|
||||
// If this section isn't bss (SHT_NOBITS) and ends up in the rom (SHF_ALLOC), copy this section into the rom
|
||||
|
||||
// If this section isn't bss (SHT_NOBITS) and ends up in the rom (SHF_ALLOC),
|
||||
// find this section's rom address and copy it into the rom
|
||||
if (section->get_type() != ELFIO::SHT_NOBITS && section->get_flags() & ELFIO::SHF_ALLOC) {
|
||||
size_t cur_rom_size = context.rom.size();
|
||||
context.rom.resize(context.rom.size() + section->get_size());
|
||||
std::copy(section->get_data(), section->get_data() + section->get_size(), &context.rom[cur_rom_size]);
|
||||
// Find the segment this section is in to determine the physical (rom) address of the section
|
||||
auto segment_it = std::upper_bound(segments.begin(), segments.end(), section->get_offset(),
|
||||
[](ELFIO::Elf64_Off section_offset, const SegmentEntry& segment) {
|
||||
return section_offset < segment.data_offset;
|
||||
}
|
||||
// Check if this section is the symbol table and record it if so
|
||||
if (section->get_type() == ELFIO::SHT_SYMTAB) {
|
||||
symtab_section = section.get();
|
||||
);
|
||||
if (segment_it == segments.begin()) {
|
||||
fmt::print(stderr, "Could not find segment that section {} belongs to!\n", section->get_name().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
// Upper bound returns the iterator after the element we're looking for, so rewind by one
|
||||
// This is safe because we checked if segment_it was segments.begin() already, which is the minimum value it could be
|
||||
const SegmentEntry& segment = *(segment_it - 1);
|
||||
// Check to be sure that the section is actually in this segment
|
||||
if (section->get_offset() >= segment.data_offset + segment.memory_size) {
|
||||
fmt::print(stderr, "Section {} out of range of segment at offset 0x{:08X}\n", section->get_name().c_str(), segment.data_offset);
|
||||
return nullptr;
|
||||
}
|
||||
// Calculate the rom address based on this section's offset into the segment and the segment's rom address
|
||||
section_out.rom_addr = segment.physical_address + (section->get_offset() - segment.data_offset);
|
||||
// Resize the output rom if needed to fit this section
|
||||
size_t required_rom_size = section_out.rom_addr + section_out.size;
|
||||
if (required_rom_size > context.rom.size()) {
|
||||
context.rom.resize(required_rom_size);
|
||||
}
|
||||
// Copy this section's data into the rom
|
||||
std::copy(section->get_data(), section->get_data() + section->get_size(), &context.rom[section_out.rom_addr]);
|
||||
} else {
|
||||
// Otherwise mark this section as having an invalid rom address
|
||||
section_out.rom_addr = (ELFIO::Elf_Xword)-1;
|
||||
}
|
||||
// Check if this section is marked as executable, which means it has code in it
|
||||
if (section->get_flags() & ELFIO::SHF_EXECINSTR) {
|
||||
|
@ -643,6 +708,13 @@ ELFIO::section* read_sections(RecompPort::Context& context, const ELFIO::elfio&
|
|||
}
|
||||
section_out.name = section->get_name();
|
||||
}
|
||||
// Find the symbol table
|
||||
for (const auto& section : elf_file.sections) {
|
||||
// Check if this section is the symbol table and record it if so
|
||||
if (section->get_type() == ELFIO::SHT_SYMTAB) {
|
||||
symtab_section = section.get();
|
||||
}
|
||||
}
|
||||
return symtab_section;
|
||||
}
|
||||
|
||||
|
@ -735,15 +807,15 @@ int main(int argc, char** argv) {
|
|||
|
||||
fmt::print("Function count: {}\n", context.functions.size());
|
||||
|
||||
std::ofstream func_lookup_file{ "test/funcs/lookup.cpp" };
|
||||
std::ofstream lookup_file{ "test/funcs/lookup.cpp" };
|
||||
std::ofstream func_header_file{ "test/funcs/funcs.h" };
|
||||
|
||||
fmt::print(func_lookup_file,
|
||||
"#include <utility>\n"
|
||||
fmt::print(lookup_file,
|
||||
//"#include <utility>\n"
|
||||
"#include \"recomp.h\"\n"
|
||||
"#include \"funcs.h\"\n"
|
||||
//"#include \"funcs.h\"\n"
|
||||
"\n"
|
||||
"std::pair<uint32_t, recomp_func_t*> funcs[] {{\n"
|
||||
//"std::pair<uint32_t, recomp_func_t*> funcs[] {{\n"
|
||||
);
|
||||
|
||||
fmt::print(func_header_file,
|
||||
|
@ -766,18 +838,18 @@ int main(int argc, char** argv) {
|
|||
if (!func.ignored && func.words.size() != 0) {
|
||||
fmt::print(func_header_file,
|
||||
"void {}(uint8_t* restrict rdram, recomp_context* restrict ctx);\n", func.name);
|
||||
fmt::print(func_lookup_file,
|
||||
" {{ 0x{:08X}u, {} }},\n", func.vram, func.name);
|
||||
//fmt::print(lookup_file,
|
||||
// " {{ 0x{:08X}u, {} }},\n", func.vram, func.name);
|
||||
if (RecompPort::recompile_function(context, func, output_dir + "ignore.txt"/*func.name + ".c"*/, static_funcs_by_section) == false) {
|
||||
func_lookup_file.clear();
|
||||
//lookup_file.clear();
|
||||
fmt::print(stderr, "Error recompiling {}\n", func.name);
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
} else if (func.reimplemented) {
|
||||
fmt::print(func_header_file,
|
||||
"void {}(uint8_t* restrict rdram, recomp_context* restrict ctx);\n", func.name);
|
||||
fmt::print(func_lookup_file,
|
||||
" {{ 0x{:08X}u, {} }},\n", func.vram, func.name);
|
||||
//fmt::print(lookup_file,
|
||||
// " {{ 0x{:08X}u, {} }},\n", func.vram, func.name);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -832,20 +904,20 @@ int main(int argc, char** argv) {
|
|||
|
||||
fmt::print(func_header_file,
|
||||
"void {}(uint8_t* restrict rdram, recomp_context* restrict ctx);\n", func.name);
|
||||
fmt::print(func_lookup_file,
|
||||
" {{ 0x{:08X}u, {} }},\n", func.vram, func.name);
|
||||
//fmt::print(lookup_file,
|
||||
// " {{ 0x{:08X}u, {} }},\n", func.vram, func.name);
|
||||
if (RecompPort::recompile_function(context, func, output_dir + func.name + ".c", static_funcs_by_section) == false) {
|
||||
func_lookup_file.clear();
|
||||
//lookup_file.clear();
|
||||
fmt::print(stderr, "Error recompiling {}\n", func.name);
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt::print(func_lookup_file,
|
||||
"}};\n"
|
||||
"extern const size_t num_funcs = sizeof(funcs) / sizeof(funcs[0]);\n"
|
||||
"\n"
|
||||
fmt::print(lookup_file,
|
||||
//"}};\n"
|
||||
//"extern const size_t num_funcs = sizeof(funcs) / sizeof(funcs[0]);\n"
|
||||
//"\n"
|
||||
"gpr get_entrypoint_address() {{ return (gpr)(int32_t)0x{:08X}u; }}\n"
|
||||
"\n"
|
||||
"const char* get_rom_name() {{ return \"{}\"; }}\n"
|
||||
|
@ -862,8 +934,8 @@ int main(int argc, char** argv) {
|
|||
);
|
||||
|
||||
{
|
||||
std::ofstream overlay_file(output_dir + "recomp_overlays.c");
|
||||
std::string section_load_table = "SectionTableEntry sections[] = {\n";
|
||||
std::ofstream overlay_file(output_dir + "recomp_overlays.inl");
|
||||
std::string section_load_table = "static SectionTableEntry section_table[] = {\n";
|
||||
|
||||
fmt::print(overlay_file,
|
||||
"#include \"recomp.h\"\n"
|
||||
|
@ -888,7 +960,7 @@ int main(int argc, char** argv) {
|
|||
section_load_table += fmt::format(" {{ .rom_addr = 0x{0:08X}, .ram_addr = 0x{1:08X}, .size = 0x{2:08X}, .funcs = {3}, .num_funcs = ARRLEN({3}) }},\n",
|
||||
section.rom_addr, section.ram_addr, section.size, section_funcs_array_name);
|
||||
|
||||
fmt::print(overlay_file, "FuncEntry {}[] = {{\n", section_funcs_array_name);
|
||||
fmt::print(overlay_file, "static FuncEntry {}[] = {{\n", section_funcs_array_name);
|
||||
|
||||
for (size_t func_index : section_funcs) {
|
||||
const auto& func = context.functions[func_index];
|
||||
|
|
178
test/RecompFuncs/RecompFuncs.vcxproj
Normal file
178
test/RecompFuncs/RecompFuncs.vcxproj
Normal file
|
@ -0,0 +1,178 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{d080900b-5c12-45fc-941b-515a5fea7594}</ProjectGuid>
|
||||
<RootNamespace>RecompFuncs</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level1</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>
|
||||
</PrecompiledHeaderFile>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)..</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>
|
||||
</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level1</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>
|
||||
</PrecompiledHeaderFile>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)..</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>
|
||||
</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level1</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)..</AdditionalIncludeDirectories>
|
||||
<PrecompiledHeaderFile />
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>
|
||||
</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level1</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)..</AdditionalIncludeDirectories>
|
||||
<PrecompiledHeaderFile />
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>
|
||||
</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\recomp.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="test.cpp" />
|
||||
<_WildCardClCompile Include="..\funcs\*.c" />
|
||||
<ClCompile Include="@(_WildCardClCompile)" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
13235
test/RecompFuncs/RecompFuncs.vcxproj.filters
Normal file
13235
test/RecompFuncs/RecompFuncs.vcxproj.filters
Normal file
File diff suppressed because it is too large
Load diff
|
@ -4,6 +4,11 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
|||
VisualStudioVersion = 16.0.32929.386
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RecompTest", "RecompTest.vcxproj", "{73819ED8-8A5B-4554-B3F3-60257A43F296}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{D080900B-5C12-45FC-941B-515A5FEA7594} = {D080900B-5C12-45FC-941B-515A5FEA7594}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RecompFuncs", "RecompFuncs\RecompFuncs.vcxproj", "{D080900B-5C12-45FC-941B-515A5FEA7594}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
@ -21,6 +26,14 @@ Global
|
|||
{73819ED8-8A5B-4554-B3F3-60257A43F296}.Release|x64.Build.0 = Release|x64
|
||||
{73819ED8-8A5B-4554-B3F3-60257A43F296}.Release|x86.ActiveCfg = Release|Win32
|
||||
{73819ED8-8A5B-4554-B3F3-60257A43F296}.Release|x86.Build.0 = Release|Win32
|
||||
{D080900B-5C12-45FC-941B-515A5FEA7594}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{D080900B-5C12-45FC-941B-515A5FEA7594}.Debug|x64.Build.0 = Debug|x64
|
||||
{D080900B-5C12-45FC-941B-515A5FEA7594}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{D080900B-5C12-45FC-941B-515A5FEA7594}.Debug|x86.Build.0 = Debug|Win32
|
||||
{D080900B-5C12-45FC-941B-515A5FEA7594}.Release|x64.ActiveCfg = Release|x64
|
||||
{D080900B-5C12-45FC-941B-515A5FEA7594}.Release|x64.Build.0 = Release|x64
|
||||
{D080900B-5C12-45FC-941B-515A5FEA7594}.Release|x86.ActiveCfg = Release|Win32
|
||||
{D080900B-5C12-45FC-941B-515A5FEA7594}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
|
@ -29,21 +29,25 @@
|
|||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>ClangCL</PlatformToolset>
|
||||
<CharacterSet>NotSet</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>ClangCL</PlatformToolset>
|
||||
<CharacterSet>NotSet</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>NotSet</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>NotSet</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
|
@ -71,6 +75,7 @@
|
|||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<CopyCppRuntimeToOutputDir>true</CopyCppRuntimeToOutputDir>
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<CopyCppRuntimeToOutputDir>true</CopyCppRuntimeToOutputDir>
|
||||
|
@ -79,11 +84,12 @@
|
|||
<ClCompile>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<WarningLevel>Level1</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir)..;$(ProjectDir)thirdparty;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||
<AdditionalOptions>/utf-8</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
|
@ -95,10 +101,11 @@
|
|||
<ClCompile>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<WarningLevel>Level1</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir)..;$(ProjectDir)thirdparty;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||
<AdditionalOptions>/utf-8</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
|
@ -112,12 +119,12 @@
|
|||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir)..;$(ProjectDir)thirdparty;$(ProjectDir)Lib\SDL2-2.24.0\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
<AdditionalOptions>/utf-8</AdditionalOptions>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WarningLevel>Level1</WarningLevel>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalDependencies>$(ProjectDir)RT64\$(Configuration)\RT64.lib;$(ProjectDir)Lib\SDL2-2.24.0\lib\$(Platform)\SDL2.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<EntryPointSymbol>mainCRTStartup</EntryPointSymbol>
|
||||
</Link>
|
||||
|
@ -130,12 +137,12 @@ XCOPY "$(ProjectDir)Lib\SDL2-2.24.0\lib\$(Platform)\SDL2.dll" "$(TargetDir)" /S
|
|||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir)..;$(ProjectDir)thirdparty;$(ProjectDir)Lib\SDL2-2.24.0\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
<AdditionalOptions>/utf-8</AdditionalOptions>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WarningLevel>Level1</WarningLevel>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalDependencies>$(ProjectDir)RT64\$(Configuration)\RT64.lib;$(ProjectDir)Lib\SDL2-2.24.0\lib\$(Platform)\SDL2.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<EntryPointSymbol>mainCRTStartup</EntryPointSymbol>
|
||||
</Link>
|
||||
|
@ -145,10 +152,9 @@ XCOPY "$(ProjectDir)Lib\SDL2-2.24.0\lib\$(Platform)\SDL2.dll" "$(TargetDir)" /S
|
|||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<_WildCardClCompile Include="funcs\*.c" />
|
||||
<ClCompile Include="@(_WildCardClCompile)" />
|
||||
<ClCompile Include="funcs\lookup.cpp" />
|
||||
<ClCompile Include="portultra\events.cpp" />
|
||||
<ClCompile Include="portultra\timer.cpp" />
|
||||
<ClCompile Include="portultra\ultrainit.cpp" />
|
||||
<ClCompile Include="portultra\port_main.c" />
|
||||
<ClCompile Include="portultra\mesgqueue.cpp" />
|
||||
|
@ -162,9 +168,13 @@ XCOPY "$(ProjectDir)Lib\SDL2-2.24.0\lib\$(Platform)\SDL2.dll" "$(TargetDir)" /S
|
|||
<ClCompile Include="src\dp.cpp" />
|
||||
<ClCompile Include="src\eep.cpp" />
|
||||
<ClCompile Include="portultra\misc_ultra.cpp" />
|
||||
<ClCompile Include="src\euc-jp.cpp" />
|
||||
<ClCompile Include="src\math_routines.cpp" />
|
||||
<ClCompile Include="src\overlays.cpp" />
|
||||
<ClCompile Include="src\pak.cpp" />
|
||||
<ClCompile Include="src\pi.cpp" />
|
||||
<ClCompile Include="src\portultra_translation.cpp" />
|
||||
<ClCompile Include="src\print.cpp" />
|
||||
<ClCompile Include="src\recomp.cpp" />
|
||||
<ClCompile Include="src\sp.cpp" />
|
||||
<ClCompile Include="src\vi.cpp" />
|
||||
|
@ -176,10 +186,16 @@ XCOPY "$(ProjectDir)Lib\SDL2-2.24.0\lib\$(Platform)\SDL2.dll" "$(TargetDir)" /S
|
|||
<ClInclude Include="portultra\platform_specific.h" />
|
||||
<ClInclude Include="RT64\rt64_layer.h" />
|
||||
<ClInclude Include="portultra\ultra64.h" />
|
||||
<ClInclude Include="src\euc-jp.h" />
|
||||
<ClInclude Include="thirdparty\blockingconcurrentqueue.h" />
|
||||
<ClInclude Include="thirdparty\concurrentqueue.h" />
|
||||
<ClInclude Include="thirdparty\lightweightsemaphore.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="RecompFuncs\RecompFuncs.vcxproj">
|
||||
<Project>{d080900b-5c12-45fc-941b-515a5fea7594}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -6,6 +6,7 @@
|
|||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
|
||||
#include <Windows.h>
|
||||
#include "SDL.h"
|
||||
|
@ -29,6 +30,8 @@ static struct {
|
|||
struct {
|
||||
std::thread thread;
|
||||
PTR(OSMesgQueue) mq = NULLPTR;
|
||||
PTR(void) current_buffer = NULLPTR;
|
||||
PTR(void) next_buffer = NULLPTR;
|
||||
OSMesg msg = (OSMesg)0;
|
||||
int retrace_count = 1;
|
||||
} vi;
|
||||
|
@ -55,7 +58,6 @@ static struct {
|
|||
// The same message queue may be used for multiple events, so share a mutex for all of them
|
||||
std::mutex message_mutex;
|
||||
uint8_t* rdram;
|
||||
std::chrono::system_clock::time_point start;
|
||||
moodycamel::BlockingConcurrentQueue<Action> action_queue{};
|
||||
} events_context{};
|
||||
|
||||
|
@ -89,43 +91,15 @@ extern "C" void osViSetEvent(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, u32 ret
|
|||
events_context.vi.retrace_count = retrace_count;
|
||||
}
|
||||
|
||||
constexpr uint32_t speed_multiplier = 1;
|
||||
|
||||
// N64 CPU counter ticks per millisecond
|
||||
constexpr uint32_t counter_per_ms = 46'875 * speed_multiplier;
|
||||
|
||||
uint64_t duration_to_count(std::chrono::system_clock::duration duration) {
|
||||
uint64_t delta_micros = std::chrono::duration_cast<std::chrono::microseconds>(duration).count();
|
||||
// More accurate than using a floating point timer, will only overflow after running for 12.47 years
|
||||
// Units: (micros * (counts/millis)) / (micros/millis) = counts
|
||||
uint64_t total_count = (delta_micros * counter_per_ms) / 1000;
|
||||
|
||||
return total_count;
|
||||
}
|
||||
|
||||
extern "C" u32 osGetCount() {
|
||||
uint64_t total_count = duration_to_count(std::chrono::system_clock::now() - events_context.start);
|
||||
|
||||
// Allow for overflows, which is how osGetCount behaves
|
||||
return (uint32_t)total_count;
|
||||
}
|
||||
|
||||
extern "C" OSTime osGetTime() {
|
||||
uint64_t total_count = duration_to_count(std::chrono::system_clock::now() - events_context.start);
|
||||
|
||||
return total_count;
|
||||
}
|
||||
|
||||
void vi_thread_func() {
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
events_context.start = std::chrono::system_clock::now();
|
||||
uint64_t total_vis = 0;
|
||||
int remaining_retraces = events_context.vi.retrace_count;
|
||||
|
||||
while (true) {
|
||||
// Determine the next VI time (more accurate than adding 16ms each VI interrupt)
|
||||
auto next = events_context.start + (total_vis * 1000000us) / (60 * speed_multiplier);
|
||||
auto next = Multilibultra::get_start() + (total_vis * 1000000us) / (60 * Multilibultra::get_speed_multiplier());
|
||||
//if (next > std::chrono::system_clock::now()) {
|
||||
// printf("Sleeping for %" PRIu64 " us to get from %" PRIu64 " us to %" PRIu64 " us \n",
|
||||
// (next - std::chrono::system_clock::now()) / 1us,
|
||||
|
@ -136,7 +110,7 @@ void vi_thread_func() {
|
|||
//}
|
||||
std::this_thread::sleep_until(next);
|
||||
// Calculate how many VIs have passed
|
||||
uint64_t new_total_vis = ((std::chrono::system_clock::now() - events_context.start) * (60 * speed_multiplier) / 1000ms) + 1;
|
||||
uint64_t new_total_vis = (Multilibultra::time_since_start() * (60 * Multilibultra::get_speed_multiplier()) / 1000ms) + 1;
|
||||
if (new_total_vis > total_vis + 1) {
|
||||
//printf("Skipped % " PRId64 " frames in VI interupt thread!\n", new_total_vis - total_vis - 1);
|
||||
}
|
||||
|
@ -223,7 +197,7 @@ int sdl_event_filter(void* userdata, SDL_Event* event) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
void gfx_thread_func(uint8_t* rdram, uint8_t* rom) {
|
||||
void event_thread_func(uint8_t* rdram, uint8_t* rom) {
|
||||
using namespace std::chrono_literals;
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
|
||||
fprintf(stderr, "Failed to initialize SDL2: %s\n", SDL_GetError());
|
||||
|
@ -234,7 +208,7 @@ void gfx_thread_func(uint8_t* rdram, uint8_t* rom) {
|
|||
// TODO set this window title in RT64, create the window here and send it to RT64, or something else entirely
|
||||
// as the current window name visibly changes as RT64 is initialized
|
||||
SDL_SetWindowTitle(window, "Recomp");
|
||||
SDL_SetEventFilter(sdl_event_filter, nullptr);
|
||||
//SDL_SetEventFilter(sdl_event_filter, nullptr);
|
||||
|
||||
while (true) {
|
||||
// Try to pull an action from the queue
|
||||
|
@ -254,19 +228,35 @@ void gfx_thread_func(uint8_t* rdram, uint8_t* rom) {
|
|||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
} else if (const auto* swap_action = std::get_if<SwapBuffersAction>(&action)) {
|
||||
events_context.vi.current_buffer = events_context.vi.next_buffer;
|
||||
RT64UpdateScreen(swap_action->origin);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle events
|
||||
SDL_PumpEvents();
|
||||
constexpr int max_events_per_frame = 16;
|
||||
SDL_Event cur_event;
|
||||
int i = 0;
|
||||
while (i++ < max_events_per_frame && SDL_PollEvent(&cur_event)) {
|
||||
sdl_event_filter(nullptr, &cur_event);
|
||||
}
|
||||
//SDL_PumpEvents();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void osViSwapBuffer(RDRAM_ARG PTR(void) frameBufPtr) {
|
||||
events_context.vi.next_buffer = frameBufPtr;
|
||||
events_context.action_queue.enqueue(SwapBuffersAction{ osVirtualToPhysical(frameBufPtr) + 640 });
|
||||
}
|
||||
|
||||
extern "C" PTR(void) osViGetNextFramebuffer() {
|
||||
return events_context.vi.next_buffer;
|
||||
}
|
||||
|
||||
extern "C" PTR(void) osViGetCurrentFramebuffer() {
|
||||
return events_context.vi.current_buffer;
|
||||
}
|
||||
|
||||
void Multilibultra::submit_rsp_task(RDRAM_ARG PTR(OSTask) task_) {
|
||||
OSTask* task = TO_PTR(OSTask, task_);
|
||||
events_context.action_queue.enqueue(SpTaskAction{ *task });
|
||||
|
@ -280,5 +270,5 @@ void Multilibultra::send_si_message() {
|
|||
void Multilibultra::init_events(uint8_t* rdram, uint8_t* rom) {
|
||||
events_context.rdram = rdram;
|
||||
events_context.vi.thread = std::thread{ vi_thread_func };
|
||||
events_context.sp.thread = std::thread{ gfx_thread_func, rdram, rom };
|
||||
events_context.sp.thread = std::thread{ event_thread_func, rdram, rom };
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ void preinit(uint8_t* rdram, uint8_t* rom);
|
|||
void native_init();
|
||||
void init_scheduler();
|
||||
void init_events(uint8_t* rdram, uint8_t* rom);
|
||||
void init_timers(RDRAM_ARG1);
|
||||
void native_thread_init(OSThread *t);
|
||||
void set_self_paused(RDRAM_ARG1);
|
||||
void wait_for_resumed(RDRAM_ARG1);
|
||||
|
@ -42,6 +43,9 @@ void set_main_thread();
|
|||
bool is_game_thread();
|
||||
void submit_rsp_task(RDRAM_ARG PTR(OSTask) task);
|
||||
void send_si_message();
|
||||
uint32_t get_speed_multiplier();
|
||||
std::chrono::system_clock::time_point get_start();
|
||||
std::chrono::system_clock::duration time_since_start();
|
||||
|
||||
class preemption_guard {
|
||||
public:
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
#include <cstdio>
|
||||
#include <thread>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
|
||||
#include "ultra64.h"
|
||||
#include "multilibultra.hpp"
|
||||
|
||||
// Native APIs only used to set thread names for easier debugging
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
extern "C" void bootproc();
|
||||
|
||||
thread_local bool is_main_thread = false;
|
||||
|
@ -41,6 +47,16 @@ static void _thread_func(RDRAM_ARG PTR(OSThread) self_, PTR(thread_func_t) entry
|
|||
thread_self = self_;
|
||||
is_game_thread = true;
|
||||
|
||||
// Set the thread name
|
||||
#ifdef _WIN32
|
||||
std::wstring thread_name = L"Game Thread " + std::to_wstring(self->id);
|
||||
HRESULT r;
|
||||
r = SetThreadDescription(
|
||||
GetCurrentThread(),
|
||||
thread_name.c_str()
|
||||
);
|
||||
#endif
|
||||
|
||||
// Perform any necessary native thread initialization.
|
||||
Multilibultra::native_thread_init(self);
|
||||
|
||||
|
@ -93,7 +109,7 @@ extern "C" void osCreateThread(RDRAM_ARG PTR(OSThread) t_, OSId id, PTR(thread_f
|
|||
t->priority = pri;
|
||||
t->id = id;
|
||||
t->state = OSThreadState::PAUSED;
|
||||
t->sp = sp;
|
||||
t->sp = sp - 0x10; // Set up the first stack frame
|
||||
|
||||
// Spawn a new thread, which will immediately pause itself and wait until it's been started.
|
||||
t->context = new UltraThreadContext{};
|
||||
|
|
187
test/portultra/timer.cpp
Normal file
187
test/portultra/timer.cpp
Normal file
|
@ -0,0 +1,187 @@
|
|||
#include <thread>
|
||||
#include <variant>
|
||||
#include <set>
|
||||
#include "blockingconcurrentqueue.h"
|
||||
|
||||
#include "ultra64.h"
|
||||
#include "multilibultra.hpp"
|
||||
#include "recomp.h"
|
||||
|
||||
// Start time for the program
|
||||
static std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
|
||||
// Game speed multiplier (1 means no speedup)
|
||||
constexpr uint32_t speed_multiplier = 1;
|
||||
// N64 CPU counter ticks per millisecond
|
||||
constexpr uint32_t counter_per_ms = 46'875 * speed_multiplier;
|
||||
|
||||
struct OSTimer {
|
||||
PTR(OSTimer) unused1;
|
||||
PTR(OSTimer) unused2;
|
||||
OSTime interval;
|
||||
OSTime timestamp;
|
||||
PTR(OSMesgQueue) mq;
|
||||
OSMesg msg;
|
||||
};
|
||||
|
||||
struct AddTimerAction {
|
||||
PTR(OSTask) timer;
|
||||
};
|
||||
|
||||
struct RemoveTimerAction {
|
||||
PTR(OSTimer) timer;
|
||||
};
|
||||
|
||||
using Action = std::variant<AddTimerAction, RemoveTimerAction>;
|
||||
|
||||
struct {
|
||||
std::thread thread;
|
||||
moodycamel::BlockingConcurrentQueue<Action> action_queue{};
|
||||
} timer_context;
|
||||
|
||||
uint64_t duration_to_ticks(std::chrono::system_clock::duration duration) {
|
||||
uint64_t delta_micros = std::chrono::duration_cast<std::chrono::microseconds>(duration).count();
|
||||
// More accurate than using a floating point timer, will only overflow after running for 12.47 years
|
||||
// Units: (micros * (counts/millis)) / (micros/millis) = counts
|
||||
uint64_t total_count = (delta_micros * counter_per_ms) / 1000;
|
||||
|
||||
return total_count;
|
||||
}
|
||||
|
||||
std::chrono::microseconds ticks_to_duration(uint64_t ticks) {
|
||||
using namespace std::chrono_literals;
|
||||
return ticks * 1000us / counter_per_ms;
|
||||
}
|
||||
|
||||
std::chrono::system_clock::time_point ticks_to_timepoint(uint64_t ticks) {
|
||||
return start + ticks_to_duration(ticks);
|
||||
}
|
||||
|
||||
uint64_t time_now() {
|
||||
return duration_to_ticks(std::chrono::system_clock::now() - start);
|
||||
}
|
||||
|
||||
void timer_thread(RDRAM_ARG1) {
|
||||
// Lambda comparator function to keep the set ordered
|
||||
auto timer_sort = [PASS_RDRAM1](PTR(OSTimer) a_, PTR(OSTimer) b_) {
|
||||
OSTimer* a = TO_PTR(OSTimer, a_);
|
||||
OSTimer* b = TO_PTR(OSTimer, b_);
|
||||
|
||||
// Order by timestamp if the timers have different timestamps
|
||||
if (a->timestamp != b->timestamp) {
|
||||
return a->timestamp < b->timestamp;
|
||||
}
|
||||
|
||||
// If they have the exact same timestamp then order by address instead
|
||||
return a < b;
|
||||
};
|
||||
|
||||
// Ordered set of timers that are currently active
|
||||
std::set<PTR(OSTimer), decltype(timer_sort)> active_timers{timer_sort};
|
||||
|
||||
// Lambda to process a timer action to handle adding and removing timers
|
||||
auto process_timer_action = [&](const Action& action) {
|
||||
// Determine the action type and act on it
|
||||
if (const auto* add_action = std::get_if<AddTimerAction>(&action)) {
|
||||
active_timers.insert(add_action->timer);
|
||||
} else if (const auto* remove_action = std::get_if<RemoveTimerAction>(&action)) {
|
||||
active_timers.erase(remove_action->timer);
|
||||
}
|
||||
};
|
||||
|
||||
while (true) {
|
||||
// Empty the action queue
|
||||
Action cur_action;
|
||||
while (timer_context.action_queue.try_dequeue(cur_action)) {
|
||||
process_timer_action(cur_action);
|
||||
}
|
||||
|
||||
// If there's no timer to act on, wait for one to come in from the action queue
|
||||
while (active_timers.empty()) {
|
||||
timer_context.action_queue.wait_dequeue(cur_action);
|
||||
process_timer_action(cur_action);
|
||||
}
|
||||
|
||||
// Get the timer that's closest to running out
|
||||
PTR(OSTimer) cur_timer_ = *active_timers.begin();
|
||||
OSTimer* cur_timer = TO_PTR(OSTimer, cur_timer_);
|
||||
|
||||
// Remove the timer from the queue (it may get readded if waiting is interrupted)
|
||||
active_timers.erase(cur_timer_);
|
||||
|
||||
// Determine how long to wait to reach the timer's timestamp
|
||||
auto wait_duration = ticks_to_timepoint(cur_timer->timestamp) - std::chrono::system_clock::now();
|
||||
auto wait_us = std::chrono::duration_cast<std::chrono::microseconds>(wait_duration);
|
||||
|
||||
// Wait for either the duration to complete or a new action to come through
|
||||
if (timer_context.action_queue.wait_dequeue_timed(cur_action, wait_duration)) {
|
||||
// Timer was interrupted by a new action
|
||||
// Add the current timer back to the queue (done first in case the action is to remove this timer)
|
||||
active_timers.insert(cur_timer_);
|
||||
// Process the new action
|
||||
process_timer_action(cur_action);
|
||||
} else {
|
||||
// Waiting for the timer completed, so send the timer's message to its message queue
|
||||
osSendMesg(PASS_RDRAM cur_timer->mq, cur_timer->msg, OS_MESG_NOBLOCK);
|
||||
// If the timer has a specified interval then reload it with that value
|
||||
if (cur_timer->interval != 0) {
|
||||
cur_timer->timestamp = cur_timer->interval + time_now();
|
||||
active_timers.insert(cur_timer_);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Multilibultra::init_timers(RDRAM_ARG1) {
|
||||
timer_context.thread = std::thread{ timer_thread, PASS_RDRAM1 };
|
||||
}
|
||||
|
||||
uint32_t Multilibultra::get_speed_multiplier() {
|
||||
return speed_multiplier;
|
||||
}
|
||||
|
||||
std::chrono::system_clock::time_point Multilibultra::get_start() {
|
||||
return start;
|
||||
}
|
||||
|
||||
std::chrono::system_clock::duration Multilibultra::time_since_start() {
|
||||
return std::chrono::system_clock::now() - start;
|
||||
}
|
||||
|
||||
extern "C" u32 osGetCount() {
|
||||
uint64_t total_count = time_now();
|
||||
|
||||
// Allow for overflows, which is how osGetCount behaves
|
||||
return (uint32_t)total_count;
|
||||
}
|
||||
|
||||
extern "C" OSTime osGetTime() {
|
||||
uint64_t total_count = time_now();
|
||||
|
||||
return total_count;
|
||||
}
|
||||
|
||||
extern "C" int osSetTimer(RDRAM_ARG PTR(OSTimer) t_, OSTime countdown, OSTime interval, PTR(OSMesgQueue) mq, OSMesg msg) {
|
||||
OSTimer* t = TO_PTR(OSTimer, t_);
|
||||
|
||||
// Determine the time when this timer will trigger off
|
||||
if (countdown == 0) {
|
||||
// Set the timestamp based on the interval
|
||||
t->timestamp = interval + time_now();
|
||||
} else {
|
||||
t->timestamp = countdown + time_now();
|
||||
}
|
||||
t->interval = interval;
|
||||
t->mq = mq;
|
||||
t->msg = msg;
|
||||
|
||||
timer_context.action_queue.enqueue(AddTimerAction{ t_ });
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int osStopTimer(RDRAM_ARG PTR(OSTimer) t_) {
|
||||
timer_context.action_queue.enqueue(RemoveTimerAction{ t_ });
|
||||
|
||||
// TODO don't blindly return 0 here; requires some response from the timer thread to know what the returned value was
|
||||
return 0;
|
||||
}
|
|
@ -166,8 +166,12 @@ s32 osRecvMesg(RDRAM_ARG PTR(OSMesgQueue), PTR(OSMesg), s32);
|
|||
void osSetEventMesg(RDRAM_ARG OSEvent, PTR(OSMesgQueue), OSMesg);
|
||||
void osViSetEvent(RDRAM_ARG PTR(OSMesgQueue), OSMesg, u32);
|
||||
void osViSwapBuffer(RDRAM_ARG PTR(void) frameBufPtr);
|
||||
PTR(void) osViGetNextFramebuffer();
|
||||
PTR(void) osViGetCurrentFramebuffer();
|
||||
u32 osGetCount();
|
||||
OSTime osGetTime();
|
||||
int osSetTimer(RDRAM_ARG PTR(OSTimer) timer, OSTime countdown, OSTime interval, PTR(OSMesgQueue) mq, OSMesg msg);
|
||||
int osStopTimer(RDRAM_ARG PTR(OSTimer) timer);
|
||||
u32 osVirtualToPhysical(PTR(void) addr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
void Multilibultra::preinit(uint8_t* rdram, uint8_t* rom) {
|
||||
Multilibultra::set_main_thread();
|
||||
Multilibultra::init_events(rdram, rom);
|
||||
Multilibultra::init_timers(rdram);
|
||||
}
|
||||
|
||||
extern "C" void osInitialize() {
|
||||
|
|
|
@ -1,18 +1,26 @@
|
|||
#include "../portultra/multilibultra.hpp"
|
||||
#include "recomp.h"
|
||||
|
||||
static int max_controllers = 0;
|
||||
|
||||
extern "C" void osContInit_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
||||
gpr bitpattern = ctx->r5;
|
||||
gpr status = ctx->r6;
|
||||
|
||||
// Set bit 0 to indicate that controller 0 is present
|
||||
MEM_B(0, bitpattern) = 0x01;
|
||||
|
||||
MEM_H(0, status) = 0x0005; // CONT_TYPE_NORMAL
|
||||
MEM_B(2, status) = 0; // controller status
|
||||
MEM_B(3, status) = 0; // controller errno
|
||||
// Mark controller 0 as present
|
||||
MEM_H(0, status) = 0x0005; // type: CONT_TYPE_NORMAL (from joybus)
|
||||
MEM_B(2, status) = 0x00; // status: 0 (from joybus)
|
||||
MEM_B(3, status) = 0x00; // errno: 0 (from libultra)
|
||||
|
||||
// Write CHNL_ERR_NORESP for the other controllers
|
||||
for (size_t controller = 1; controller < 4; controller++) {
|
||||
MEM_B(4 * controller + 3, status) = 0x80;
|
||||
max_controllers = 4;
|
||||
|
||||
// Mark controllers 1-3 as not connected
|
||||
for (size_t controller = 1; controller < max_controllers; controller++) {
|
||||
// Libultra doesn't write status or type for absent controllers
|
||||
MEM_B(4 * controller + 3, status) = 0x80 >> 4; // errno: CONT_NO_RESPONSE_ERROR >> 4
|
||||
}
|
||||
|
||||
ctx->r2 = 0;
|
||||
|
@ -42,8 +50,9 @@ void release_button(int button) {
|
|||
}
|
||||
|
||||
extern "C" void osContGetReadData_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
||||
int32_t pad = (uint32_t)ctx->r4;
|
||||
int32_t pad = (int32_t)ctx->r4;
|
||||
|
||||
if (max_controllers > 0) {
|
||||
// button
|
||||
MEM_H(0, pad) = button;
|
||||
// stick_x
|
||||
|
@ -53,8 +62,32 @@ extern "C" void osContGetReadData_recomp(uint8_t* restrict rdram, recomp_context
|
|||
// errno
|
||||
MEM_B(4, pad) = 0;
|
||||
}
|
||||
for (int controller = 1; controller < max_controllers; controller++) {
|
||||
MEM_B(6 * controller + 4, pad) = 0x80 >> 4; // errno: CONT_NO_RESPONSE_ERROR >> 4
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void osContStartQuery_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
Multilibultra::send_si_message();
|
||||
}
|
||||
|
||||
extern "C" void osContGetQuery_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
gpr status = ctx->r4;
|
||||
|
||||
// Mark controller 0 as present
|
||||
MEM_H(0, status) = 0x0005; // type: CONT_TYPE_NORMAL (from joybus)
|
||||
MEM_B(2, status) = 0x00; // status: 0 (from joybus)
|
||||
MEM_B(3, status) = 0x00; // errno: 0 (from libultra)
|
||||
|
||||
// Mark controllers 1-3 as not connected
|
||||
for (size_t controller = 1; controller < max_controllers; controller++) {
|
||||
// Libultra doesn't write status or type for absent controllers
|
||||
MEM_B(4 * controller + 3, status) = 0x80 >> 4; // errno: CONT_NO_RESPONSE_ERROR >> 4
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void osContSetCh_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
||||
max_controllers = std::min((unsigned int)ctx->r4, 4u);
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
|
|
2587
test/src/euc-jp.cpp
Normal file
2587
test/src/euc-jp.cpp
Normal file
File diff suppressed because it is too large
Load diff
11
test/src/euc-jp.h
Normal file
11
test/src/euc-jp.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#ifndef __EUC_JP_H__
|
||||
#define __EUC_JP_H__
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace Encoding {
|
||||
std::string decode_eucjp(std::string_view src);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
|
||||
extern "C" void __udivdi3_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
uint64_t a = (ctx->r4 << 32) | (ctx->r5 << 0);
|
||||
uint64_t b = (ctx->r6 << 32) | (ctx->r7 << 0);
|
||||
uint64_t a = (ctx->r4 << 32) | ((ctx->r5 << 0) & 0xFFFFFFFFu);
|
||||
uint64_t b = (ctx->r6 << 32) | ((ctx->r7 << 0) & 0xFFFFFFFFu);
|
||||
uint64_t ret = a / b;
|
||||
|
||||
ctx->r2 = (int32_t)(ret >> 32);
|
||||
|
@ -12,8 +12,8 @@ extern "C" void __udivdi3_recomp(uint8_t * restrict rdram, recomp_context * rest
|
|||
}
|
||||
|
||||
extern "C" void __divdi3_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
int64_t a = (ctx->r4 << 32) | (ctx->r5 << 0);
|
||||
int64_t b = (ctx->r6 << 32) | (ctx->r7 << 0);
|
||||
int64_t a = (ctx->r4 << 32) | ((ctx->r5 << 0) & 0xFFFFFFFFu);
|
||||
int64_t b = (ctx->r6 << 32) | ((ctx->r7 << 0) & 0xFFFFFFFFu);
|
||||
int64_t ret = a / b;
|
||||
|
||||
ctx->r2 = (int32_t)(ret >> 32);
|
||||
|
@ -21,8 +21,8 @@ extern "C" void __divdi3_recomp(uint8_t * restrict rdram, recomp_context * restr
|
|||
}
|
||||
|
||||
extern "C" void __umoddi3_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
uint64_t a = (ctx->r4 << 32) | (ctx->r5 << 0);
|
||||
uint64_t b = (ctx->r6 << 32) | (ctx->r7 << 0);
|
||||
uint64_t a = (ctx->r4 << 32) | ((ctx->r5 << 0) & 0xFFFFFFFFu);
|
||||
uint64_t b = (ctx->r6 << 32) | ((ctx->r7 << 0) & 0xFFFFFFFFu);
|
||||
uint64_t ret = a % b;
|
||||
|
||||
ctx->r2 = (int32_t)(ret >> 32);
|
||||
|
@ -30,8 +30,8 @@ extern "C" void __umoddi3_recomp(uint8_t * restrict rdram, recomp_context * rest
|
|||
}
|
||||
|
||||
extern "C" void __ull_div_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
uint64_t a = (ctx->r4 << 32) | (ctx->r5 << 0);
|
||||
uint64_t b = (ctx->r6 << 32) | (ctx->r7 << 0);
|
||||
uint64_t a = (ctx->r4 << 32) | ((ctx->r5 << 0) & 0xFFFFFFFFu);
|
||||
uint64_t b = (ctx->r6 << 32) | ((ctx->r7 << 0) & 0xFFFFFFFFu);
|
||||
uint64_t ret = a / b;
|
||||
|
||||
ctx->r2 = (int32_t)(ret >> 32);
|
||||
|
@ -39,8 +39,8 @@ extern "C" void __ull_div_recomp(uint8_t * restrict rdram, recomp_context * rest
|
|||
}
|
||||
|
||||
extern "C" void __ll_div_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
int64_t a = (ctx->r4 << 32) | (ctx->r5 << 0);
|
||||
int64_t b = (ctx->r6 << 32) | (ctx->r7 << 0);
|
||||
int64_t a = (ctx->r4 << 32) | ((ctx->r5 << 0) & 0xFFFFFFFFu);
|
||||
int64_t b = (ctx->r6 << 32) | ((ctx->r7 << 0) & 0xFFFFFFFFu);
|
||||
int64_t ret = a / b;
|
||||
|
||||
ctx->r2 = (int32_t)(ret >> 32);
|
||||
|
@ -48,8 +48,8 @@ extern "C" void __ll_div_recomp(uint8_t * restrict rdram, recomp_context * restr
|
|||
}
|
||||
|
||||
extern "C" void __ll_mul_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
uint64_t a = (ctx->r4 << 32) | (ctx->r5 << 0);
|
||||
uint64_t b = (ctx->r6 << 32) | (ctx->r7 << 0);
|
||||
uint64_t a = (ctx->r4 << 32) | ((ctx->r5 << 0) & 0xFFFFFFFFu);
|
||||
uint64_t b = (ctx->r6 << 32) | ((ctx->r7 << 0) & 0xFFFFFFFFu);
|
||||
uint64_t ret = a * b;
|
||||
|
||||
ctx->r2 = (int32_t)(ret >> 32);
|
||||
|
@ -57,8 +57,8 @@ extern "C" void __ll_mul_recomp(uint8_t * restrict rdram, recomp_context * restr
|
|||
}
|
||||
|
||||
extern "C" void __ull_rem_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
uint64_t a = (ctx->r4 << 32) | (ctx->r5 << 0);
|
||||
uint64_t b = (ctx->r6 << 32) | (ctx->r7 << 0);
|
||||
uint64_t a = (ctx->r4 << 32) | ((ctx->r5 << 0) & 0xFFFFFFFFu);
|
||||
uint64_t b = (ctx->r6 << 32) | ((ctx->r7 << 0) & 0xFFFFFFFFu);
|
||||
uint64_t ret = a % b;
|
||||
|
||||
ctx->r2 = (int32_t)(ret >> 32);
|
||||
|
@ -66,14 +66,14 @@ extern "C" void __ull_rem_recomp(uint8_t * restrict rdram, recomp_context * rest
|
|||
}
|
||||
|
||||
extern "C" void __ull_to_d_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
uint64_t a = (ctx->r4 << 32) | (ctx->r5 << 0);
|
||||
uint64_t a = (ctx->r4 << 32) | ((ctx->r5 << 0) & 0xFFFFFFFFu);
|
||||
double ret = (double)a;
|
||||
|
||||
ctx->f0.d = ret;
|
||||
}
|
||||
|
||||
extern "C" void __ull_to_f_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
uint64_t a = (ctx->r4 << 32) | (ctx->r5 << 0);
|
||||
uint64_t a = (ctx->r4 << 32) | ((ctx->r5 << 0) & 0xFFFFFFFFu);
|
||||
float ret = (float)a;
|
||||
|
||||
ctx->f0.fl = ret;
|
||||
|
|
98
test/src/overlays.cpp
Normal file
98
test/src/overlays.cpp
Normal file
|
@ -0,0 +1,98 @@
|
|||
#include <unordered_map>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include "recomp.h"
|
||||
#include "../funcs/recomp_overlays.inl"
|
||||
|
||||
constexpr size_t num_sections = ARRLEN(section_table);
|
||||
|
||||
// SectionTableEntry sections[] defined in recomp_overlays.inl
|
||||
|
||||
struct LoadedSection {
|
||||
int32_t loaded_ram_addr;
|
||||
size_t section_table_index;
|
||||
|
||||
bool operator<(const LoadedSection& rhs) {
|
||||
return loaded_ram_addr < rhs.loaded_ram_addr;
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<LoadedSection> loaded_sections{};
|
||||
std::unordered_map<int32_t, recomp_func_t*> func_map{};
|
||||
|
||||
void load_overlay(size_t section_table_index, int32_t ram) {
|
||||
const SectionTableEntry& section = section_table[section_table_index];
|
||||
for (size_t function_index = 0; function_index < section.num_funcs; function_index++) {
|
||||
const FuncEntry& func = section.funcs[function_index];
|
||||
func_map[ram + func.offset] = func.func;
|
||||
}
|
||||
loaded_sections.emplace_back(ram, section_table_index);
|
||||
}
|
||||
|
||||
extern "C" void load_overlays(uint32_t rom, int32_t ram_addr, uint32_t size) {
|
||||
// Search for the first section that's included in the loaded rom range
|
||||
// Sections were sorted by `init_overlays` so we can use the bounds functions
|
||||
auto lower = std::lower_bound(§ion_table[0], §ion_table[num_sections], rom,
|
||||
[](const SectionTableEntry& entry, uint32_t addr) {
|
||||
return entry.rom_addr < addr;
|
||||
}
|
||||
);
|
||||
auto upper = std::upper_bound(§ion_table[0], §ion_table[num_sections], (uint32_t)(rom + size),
|
||||
[](uint32_t addr, const SectionTableEntry& entry) {
|
||||
return addr < entry.size + entry.rom_addr;
|
||||
}
|
||||
);
|
||||
// Load the overlays that were found
|
||||
for (auto it = lower; it != upper; ++it) {
|
||||
load_overlay(std::distance(§ion_table[0], it), it->rom_addr - rom + ram_addr);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void unload_overlays(int32_t ram_addr, uint32_t size) {
|
||||
for (auto it = loaded_sections.begin(); it != loaded_sections.end();) {
|
||||
const auto& section = section_table[it->section_table_index];
|
||||
|
||||
// Check if the unloaded region overlaps with the loaded section
|
||||
if (ram_addr < (it->loaded_ram_addr + section.size) && (ram_addr + size) >= it->loaded_ram_addr) {
|
||||
// Check if the section isn't entirely in the loaded region
|
||||
if (ram_addr > it->loaded_ram_addr || (ram_addr + size) < (it->loaded_ram_addr + section.size)) {
|
||||
fprintf(stderr,
|
||||
"Cannot partially unload section\n"
|
||||
" rom: 0x%08X size: 0x%08X loaded_addr: 0x%08X\n"
|
||||
" unloaded_ram: 0x%08X unloaded_size : 0x%08X\n",
|
||||
section.rom_addr, section.size, it->loaded_ram_addr, ram_addr, size);
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
// Determine where each function was loaded to and remove that entry from the function map
|
||||
for (size_t func_index = 0; func_index < section.num_funcs; func_index++) {
|
||||
const auto& func = section.funcs[func_index];
|
||||
uint32_t func_address = func.offset + it->loaded_ram_addr;
|
||||
func_map.erase(func_address);
|
||||
}
|
||||
// Remove the section from the loaded section map
|
||||
it = loaded_sections.erase(it);
|
||||
// Skip incrementing the iterator
|
||||
continue;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void init_overlays() {
|
||||
// Sort the executable sections by rom address
|
||||
std::sort(§ion_table[0], §ion_table[num_sections],
|
||||
[](const SectionTableEntry& a, const SectionTableEntry& b) {
|
||||
return a.rom_addr < b.rom_addr;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
extern "C" recomp_func_t * get_function(int32_t addr) {
|
||||
auto func_find = func_map.find(addr);
|
||||
if (func_find == func_map.end()) {
|
||||
fprintf(stderr, "Failed to find function at 0x%08X\n", addr);
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
return func_find->second;
|
||||
}
|
||||
|
31
test/src/pak.cpp
Normal file
31
test/src/pak.cpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
#include "recomp.h"
|
||||
#include "../portultra/ultra64.h"
|
||||
#include "../portultra/multilibultra.hpp"
|
||||
|
||||
extern "C" void osPfsInitPak_recomp(uint8_t * restrict rdram, recomp_context* restrict ctx) {
|
||||
ctx->r2 = 1; // PFS_ERR_NOPACK
|
||||
}
|
||||
|
||||
extern "C" void osPfsFreeBlocks_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
ctx->r2 = 1; // PFS_ERR_NOPACK
|
||||
}
|
||||
|
||||
extern "C" void osPfsAllocateFile_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
ctx->r2 = 1; // PFS_ERR_NOPACK
|
||||
}
|
||||
|
||||
extern "C" void osPfsDeleteFile_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
ctx->r2 = 1; // PFS_ERR_NOPACK
|
||||
}
|
||||
|
||||
extern "C" void osPfsFileState_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
ctx->r2 = 1; // PFS_ERR_NOPACK
|
||||
}
|
||||
|
||||
extern "C" void osPfsFindFile_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
ctx->r2 = 1; // PFS_ERR_NOPACK
|
||||
}
|
||||
|
||||
extern "C" void osPfsReadWriteFile_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
ctx->r2 = 1; // PFS_ERR_NOPACK
|
||||
}
|
193
test/src/pi.cpp
193
test/src/pi.cpp
|
@ -3,49 +3,6 @@
|
|||
#include "../portultra/ultra64.h"
|
||||
#include "../portultra/multilibultra.hpp"
|
||||
|
||||
extern std::unique_ptr<uint8_t[]> rom;
|
||||
extern size_t rom_size;
|
||||
|
||||
extern "C" void osCartRomInit_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
||||
;
|
||||
}
|
||||
|
||||
extern "C" void osCreatePiManager_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
||||
;
|
||||
}
|
||||
|
||||
constexpr uint32_t rom_base = 0xB0000000;
|
||||
|
||||
void do_rom_read(uint8_t* rdram, gpr ram_address, uint32_t dev_address, size_t num_bytes) {
|
||||
// TODO use word copies when possible
|
||||
uint8_t* rom_addr = rom.get() + (dev_address | rom_base) - rom_base;
|
||||
for (size_t i = 0; i < num_bytes; i++) {
|
||||
MEM_B(i, ram_address) = *rom_addr;
|
||||
rom_addr++;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void osPiStartDma_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
||||
uint32_t mb = ctx->r4;
|
||||
uint32_t pri = ctx->r5;
|
||||
uint32_t direction = ctx->r6;
|
||||
uint32_t devAddr = ctx->r7;
|
||||
gpr dramAddr = MEM_W(0x10, ctx->r29);
|
||||
uint32_t size = MEM_W(0x14, ctx->r29);
|
||||
PTR(OSMesgQueue) mq = MEM_W(0x18, ctx->r29);
|
||||
|
||||
debug_printf("[pi] DMA from 0x%08X into 0x%08X of size 0x%08X\n", devAddr, dramAddr, size);
|
||||
|
||||
// TODO asynchronous transfer (will require preemption in the scheduler)
|
||||
// TODO this won't handle unaligned DMA
|
||||
do_rom_read(rdram, dramAddr, devAddr, size);
|
||||
|
||||
//memcpy(rdram + (dramAddr & 0x3FFFFFF), rom.get() + (devAddr | rom_base) - rom_base, num_bytes);
|
||||
|
||||
// Send a message to the mq to indicate that the transfer completed
|
||||
osSendMesg(rdram, mq, 0, OS_MESG_NOBLOCK);
|
||||
}
|
||||
|
||||
struct OSIoMesgHdr {
|
||||
// These 3 reversed due to endianness
|
||||
u8 status; /* Return status */
|
||||
|
@ -62,24 +19,148 @@ struct OSIoMesg {
|
|||
u32 piHandle; /* PI device handle */
|
||||
};
|
||||
|
||||
extern "C" void osEPiStartDma_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
||||
OSIoMesg* mb = TO_PTR(OSIoMesg, ctx->r5);
|
||||
uint32_t direction = ctx->r6;
|
||||
uint32_t devAddr = mb->devAddr;
|
||||
gpr dramAddr = mb->dramAddr;
|
||||
uint32_t size = mb->size;
|
||||
PTR(OSMesgQueue) mq = mb->hdr.retQueue;
|
||||
struct OSPiHandle {
|
||||
PTR(OSPiHandle_s) unused; /* point to next handle on the table */
|
||||
// These four members reversed due to endianness
|
||||
u8 relDuration; /* domain release duration */
|
||||
u8 pageSize; /* domain page size */
|
||||
u8 latency; /* domain latency */
|
||||
u8 type; /* DEVICE_TYPE_BULK for disk */
|
||||
// These three members reversed due to endianness
|
||||
u16 padding; /* struct alignment padding */
|
||||
u8 domain; /* which domain */
|
||||
u8 pulse; /* domain pulse width */
|
||||
u32 baseAddress; /* Domain address */
|
||||
u32 speed; /* for roms only */
|
||||
/* The following are "private" elements" */
|
||||
u32 transferInfo[18]; /* for disk only */
|
||||
};
|
||||
|
||||
debug_printf("[pi] DMA from 0x%08X into 0x%08X of size 0x%08X\n", devAddr, dramAddr, size);
|
||||
// Flashram occupies the same physical address as sram, but that issue is avoided because libultra exposes
|
||||
// a high-level interface for flashram. Because that high-level interface is reimplemented, low level accesses
|
||||
// that involve physical addresses don't need to be handled for flashram.
|
||||
constexpr uint32_t sram_base = 0x08000000;
|
||||
constexpr uint32_t rom_base = 0x10000000;
|
||||
|
||||
// TODO asynchronous transfer (will require preemption in the scheduler)
|
||||
// TODO this won't handle unaligned DMA
|
||||
do_rom_read(rdram, dramAddr, devAddr, size);
|
||||
constexpr uint32_t k1_to_phys(uint32_t addr) {
|
||||
return addr & 0x1FFFFFFF;
|
||||
}
|
||||
|
||||
//memcpy(rdram + (dramAddr & 0x3FFFFFF), rom.get() + (devAddr | rom_base) - rom_base, num_bytes);
|
||||
constexpr uint32_t phys_to_k1(uint32_t addr) {
|
||||
return addr | 0xA0000000;
|
||||
}
|
||||
|
||||
// We need a place in rdram to hold the cart handle, so pick an address in extended rdram
|
||||
constexpr int32_t cart_handle = 0x80800000;
|
||||
|
||||
extern std::unique_ptr<uint8_t[]> rom;
|
||||
extern size_t rom_size;
|
||||
|
||||
extern "C" void osCartRomInit_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
||||
OSPiHandle* handle = TO_PTR(OSPiHandle, cart_handle);
|
||||
handle->type = 0; // cart
|
||||
handle->baseAddress = phys_to_k1(rom_base);
|
||||
handle->domain = 0;
|
||||
|
||||
ctx->r2 = (gpr)cart_handle;
|
||||
}
|
||||
|
||||
extern "C" void osCreatePiManager_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
||||
;
|
||||
}
|
||||
|
||||
void do_rom_read(uint8_t* rdram, gpr ram_address, uint32_t physical_addr, size_t num_bytes) {
|
||||
// TODO use word copies when possible
|
||||
uint8_t* rom_addr = rom.get() + physical_addr - rom_base;
|
||||
for (size_t i = 0; i < num_bytes; i++) {
|
||||
MEM_B(i, ram_address) = *rom_addr;
|
||||
rom_addr++;
|
||||
}
|
||||
}
|
||||
|
||||
void do_dma(uint8_t* restrict rdram, PTR(OSMesgQueue) mq, gpr rdram_address, uint32_t physical_addr, uint32_t size, uint32_t direction) {
|
||||
// TODO asynchronous transfer
|
||||
// TODO implement unaligned DMA correctly
|
||||
if (direction == 0) {
|
||||
if (physical_addr > rom_base) {
|
||||
// read cart rom
|
||||
do_rom_read(rdram, rdram_address, physical_addr, size);
|
||||
|
||||
// Send a message to the mq to indicate that the transfer completed
|
||||
osSendMesg(rdram, mq, 0, OS_MESG_NOBLOCK);
|
||||
} else {
|
||||
// read sram
|
||||
printf("[WARN] SRAM read unimplemented, returning zeroes\n");
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
MEM_B(i, rdram_address) = 0;
|
||||
}
|
||||
|
||||
// Send a message to the mq to indicate that the transfer completed
|
||||
osSendMesg(rdram, mq, 0, OS_MESG_NOBLOCK);
|
||||
}
|
||||
} else {
|
||||
if (physical_addr > rom_base) {
|
||||
// write cart rom
|
||||
throw std::runtime_error("ROM DMA write unimplemented");
|
||||
} else {
|
||||
// write sram
|
||||
printf("[WARN] SRAM write unimplemented, ignoring data\n");
|
||||
|
||||
// Send a message to the mq to indicate that the transfer completed
|
||||
osSendMesg(rdram, mq, 0, OS_MESG_NOBLOCK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void osPiStartDma_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
||||
uint32_t mb = ctx->r4;
|
||||
uint32_t pri = ctx->r5;
|
||||
uint32_t direction = ctx->r6;
|
||||
uint32_t devAddr = ctx->r7;
|
||||
gpr dramAddr = MEM_W(0x10, ctx->r29);
|
||||
uint32_t size = MEM_W(0x14, ctx->r29);
|
||||
PTR(OSMesgQueue) mq = MEM_W(0x18, ctx->r29);
|
||||
uint32_t physical_addr = k1_to_phys(devAddr);
|
||||
|
||||
debug_printf("[pi] DMA from 0x%08X into 0x%08X of size 0x%08X\n", devAddr, dramAddr, size);
|
||||
|
||||
do_dma(rdram, mq, dramAddr, physical_addr, size, direction);
|
||||
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void osEPiStartDma_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
||||
OSPiHandle* handle = TO_PTR(OSPiHandle, ctx->r4);
|
||||
OSIoMesg* mb = TO_PTR(OSIoMesg, ctx->r5);
|
||||
uint32_t direction = ctx->r6;
|
||||
uint32_t devAddr = handle->baseAddress | mb->devAddr;
|
||||
gpr dramAddr = mb->dramAddr;
|
||||
uint32_t size = mb->size;
|
||||
PTR(OSMesgQueue) mq = mb->hdr.retQueue;
|
||||
uint32_t physical_addr = k1_to_phys(devAddr);
|
||||
|
||||
debug_printf("[pi] DMA from 0x%08X into 0x%08X of size 0x%08X\n", devAddr, dramAddr, size);
|
||||
|
||||
do_dma(rdram, mq, dramAddr, physical_addr, size, direction);
|
||||
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void osEPiReadIo_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
OSPiHandle* handle = TO_PTR(OSPiHandle, ctx->r4);
|
||||
uint32_t devAddr = handle->baseAddress | ctx->r5;
|
||||
gpr dramAddr = ctx->r6;
|
||||
uint32_t physical_addr = k1_to_phys(devAddr);
|
||||
|
||||
if (physical_addr > rom_base) {
|
||||
// cart rom
|
||||
do_rom_read(rdram, dramAddr, physical_addr, sizeof(uint32_t));
|
||||
} else {
|
||||
// sram
|
||||
assert(false && "SRAM ReadIo unimplemented");
|
||||
}
|
||||
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void osPiGetStatus_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
|
@ -87,5 +168,5 @@ extern "C" void osPiGetStatus_recomp(uint8_t * restrict rdram, recomp_context *
|
|||
}
|
||||
|
||||
extern "C" void osPiRawStartDma_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
;
|
||||
ctx->r2 = 0;
|
||||
}
|
|
@ -70,10 +70,20 @@ extern "C" void osGetCount_recomp(uint8_t * restrict rdram, recomp_context * res
|
|||
|
||||
extern "C" void osGetTime_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
uint64_t total_count = osGetTime();
|
||||
ctx->r2 = (uint32_t)(total_count >> 32);
|
||||
ctx->r2 = (int32_t)(total_count >> 32);
|
||||
ctx->r3 = (int32_t)(total_count >> 0);
|
||||
}
|
||||
|
||||
extern "C" void osSetTimer_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
uint64_t countdown = ((uint64_t)(ctx->r6) << 32) | ((ctx->r7) & 0xFFFFFFFFu);
|
||||
uint64_t interval = load_doubleword(rdram, ctx->r29, 0x10);
|
||||
ctx->r2 = osSetTimer(rdram, (int32_t)ctx->r4, countdown, interval, (int32_t)MEM_W(0x18, ctx->r29), (OSMesg)MEM_W(0x1C, ctx->r29));
|
||||
}
|
||||
|
||||
extern "C" void osStopTimer_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
ctx->r2 = osStopTimer(rdram, (int32_t)ctx->r4);
|
||||
}
|
||||
|
||||
extern "C" void osVirtualToPhysical_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
ctx->r2 = osVirtualToPhysical((int32_t)ctx->r2);
|
||||
}
|
||||
|
@ -110,43 +120,6 @@ extern "C" void __osSetFpcCsr_recomp(uint8_t * restrict rdram, recomp_context *
|
|||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void __checkHardware_msp_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void __checkHardware_kmc_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void __checkHardware_isv_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void __osInitialize_msp_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
}
|
||||
|
||||
extern "C" void __osInitialize_kmc_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
}
|
||||
|
||||
extern "C" void __osInitialize_isv_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
}
|
||||
|
||||
extern "C" void __osRdbSend_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
gpr buf = ctx->r4;
|
||||
size_t size = ctx->r5;
|
||||
u32 type = (u32)ctx->r6;
|
||||
std::unique_ptr<char[]> to_print = std::make_unique<char[]>(size);
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
to_print[i] = MEM_B(i, buf);
|
||||
}
|
||||
to_print[size] = '\x00';
|
||||
|
||||
fwrite(to_print.get(), 1, size, stdout);
|
||||
|
||||
ctx->r2 = size;
|
||||
}
|
||||
|
||||
// For the Mario Party games (not working)
|
||||
//extern "C" void longjmp_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
// RecompJmpBuf* buf = TO_PTR(RecompJmpBuf, ctx->r4);
|
||||
|
|
70
test/src/print.cpp
Normal file
70
test/src/print.cpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
#include "../portultra/ultra64.h"
|
||||
#include "../portultra/multilibultra.hpp"
|
||||
#include "recomp.h"
|
||||
#include "euc-jp.h"
|
||||
|
||||
extern "C" void __checkHardware_msp_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void __checkHardware_kmc_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void __checkHardware_isv_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void __osInitialize_msp_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
}
|
||||
|
||||
extern "C" void __osInitialize_kmc_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
}
|
||||
|
||||
extern "C" void __osInitialize_isv_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
}
|
||||
|
||||
extern "C" void isPrintfInit_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
}
|
||||
|
||||
extern "C" void __osRdbSend_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
gpr buf = ctx->r4;
|
||||
size_t size = ctx->r5;
|
||||
u32 type = (u32)ctx->r6;
|
||||
std::unique_ptr<char[]> to_print = std::make_unique<char[]>(size + 1);
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
to_print[i] = MEM_B(i, buf);
|
||||
}
|
||||
to_print[size] = '\x00';
|
||||
|
||||
fwrite(to_print.get(), 1, size, stdout);
|
||||
|
||||
ctx->r2 = size;
|
||||
}
|
||||
|
||||
extern "C" void is_proutSyncPrintf_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
// Buffering to speed up print performance
|
||||
static std::vector<char> print_buffer;
|
||||
|
||||
gpr buf = ctx->r5;
|
||||
size_t size = ctx->r6;
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
// Add the new character to the buffer
|
||||
char cur_char = MEM_B(i, buf);
|
||||
|
||||
// If the new character is a newline, flush the buffer
|
||||
if (cur_char == '\n') {
|
||||
std::string utf8_str = Encoding::decode_eucjp(std::string_view{ print_buffer.data(), print_buffer.size() });
|
||||
puts(utf8_str.c_str());
|
||||
print_buffer.clear();
|
||||
} else {
|
||||
print_buffer.push_back(cur_char);
|
||||
}
|
||||
}
|
||||
|
||||
//fwrite(to_print.get(), size, 1, stdout);
|
||||
|
||||
ctx->r2 = 1;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
#ifdef _WIN32
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
#include <cstdio>
|
||||
|
@ -7,6 +7,7 @@
|
|||
#include <cmath>
|
||||
#include <unordered_map>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include "recomp.h"
|
||||
#include "../portultra/multilibultra.hpp"
|
||||
|
||||
|
@ -20,20 +21,6 @@ constexpr uint32_t byteswap(uint32_t val) {
|
|||
}
|
||||
#endif
|
||||
|
||||
extern std::pair<uint32_t, recomp_func_t*> funcs[];
|
||||
extern const size_t num_funcs;
|
||||
|
||||
std::unordered_map<uint32_t, recomp_func_t*> func_map{};
|
||||
|
||||
extern "C" recomp_func_t* get_function(uint32_t addr) {
|
||||
auto func_find = func_map.find(addr);
|
||||
if (func_find == func_map.end()) {
|
||||
fprintf(stderr, "Failed to find function at 0x%08X\n", addr);
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
return func_find->second;
|
||||
}
|
||||
|
||||
extern "C" void _bzero(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
||||
gpr start_addr = ctx->r4;
|
||||
gpr size = ctx->r5;
|
||||
|
@ -43,6 +30,10 @@ extern "C" void _bzero(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
|||
}
|
||||
}
|
||||
|
||||
extern "C" void osGetMemSize_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
|
||||
ctx->r2 = 8 * 1024 * 1024;
|
||||
}
|
||||
|
||||
extern "C" void switch_error(const char* func, uint32_t vram, uint32_t jtbl) {
|
||||
printf("Switch-case out of bounds in %s at 0x%08X for jump table at 0x%08X\n", func, vram, jtbl);
|
||||
exit(EXIT_FAILURE);
|
||||
|
@ -70,6 +61,13 @@ size_t rom_size;
|
|||
extern "C" void recomp_entrypoint(uint8_t * restrict rdram, recomp_context * restrict ctx);
|
||||
gpr get_entrypoint_address();
|
||||
const char* get_rom_name();
|
||||
void init_overlays();
|
||||
extern "C" void load_overlays(uint32_t rom, int32_t ram_addr, uint32_t size);
|
||||
extern "C" void unload_overlays(int32_t ram_addr, uint32_t size);
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
//if (argc != 2) {
|
||||
|
@ -77,6 +75,24 @@ int main(int argc, char **argv) {
|
|||
// exit(EXIT_SUCCESS);
|
||||
//}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Set up console output to accept UTF-8 on windows
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
|
||||
// Change to a font that supports Japanese characters
|
||||
CONSOLE_FONT_INFOEX cfi;
|
||||
cfi.cbSize = sizeof cfi;
|
||||
cfi.nFont = 0;
|
||||
cfi.dwFontSize.X = 0;
|
||||
cfi.dwFontSize.Y = 16;
|
||||
cfi.FontFamily = FF_DONTCARE;
|
||||
cfi.FontWeight = FW_NORMAL;
|
||||
wcscpy_s(cfi.FaceName, L"NSimSun");
|
||||
SetCurrentConsoleFontEx(GetStdHandle(STD_OUTPUT_HANDLE), FALSE, &cfi);
|
||||
#else
|
||||
std::setlocale(LC_ALL, "en_US.UTF-8");
|
||||
#endif
|
||||
|
||||
{
|
||||
std::basic_ifstream<uint8_t> rom_file{ get_rom_name(), std::ios::binary };
|
||||
|
||||
|
@ -96,24 +112,28 @@ int main(int argc, char **argv) {
|
|||
rom = std::make_unique<uint8_t[]>(rom_size);
|
||||
|
||||
rom_file.read(rom.get(), rom_size);
|
||||
|
||||
// TODO remove this
|
||||
// Modify the name in the rom header so RT64 doesn't find it
|
||||
rom[0x2F] = 'O';
|
||||
}
|
||||
|
||||
// Initialize the overlays
|
||||
init_overlays();
|
||||
|
||||
// Get entrypoint from recomp function
|
||||
gpr entrypoint = get_entrypoint_address();
|
||||
|
||||
// Allocate rdram_buffer
|
||||
std::unique_ptr<uint8_t[]> rdram_buffer = std::make_unique<uint8_t[]>(8 * 1024 * 1024);
|
||||
// Load overlays in the first 1MB
|
||||
load_overlays(0x1000, (int32_t)entrypoint, 1024 * 1024);
|
||||
|
||||
// Allocate rdram_buffer (16MB to give room for any extra addressable data used by recomp)
|
||||
std::unique_ptr<uint8_t[]> rdram_buffer = std::make_unique<uint8_t[]>(16 * 1024 * 1024);
|
||||
std::memset(rdram_buffer.get(), 0, 8 * 1024 * 1024);
|
||||
recomp_context context{};
|
||||
|
||||
// Initial 1MB DMA
|
||||
do_rom_read(rdram_buffer.get(), entrypoint, 0x1000, 0x100000);
|
||||
//std::copy_n(rom.get() + 0x1000, 0x100000, rdram_buffer.get() + entrypoint - 0x80000000);
|
||||
|
||||
// Initialize function address map
|
||||
for (size_t i = 0; i < num_funcs; i++) {
|
||||
func_map[funcs[i].first] = funcs[i].second;
|
||||
}
|
||||
// Initial 1MB DMA (rom address 0x1000 = physical address 0x10001000)
|
||||
do_rom_read(rdram_buffer.get(), entrypoint, 0x10001000, 0x100000);
|
||||
|
||||
// Set up stack pointer
|
||||
context.r29 = 0xFFFFFFFF803FFFF0u;
|
||||
|
|
|
@ -15,7 +15,7 @@ extern "C" void osSpTaskStartGo_recomp(uint8_t* restrict rdram, recomp_context*
|
|||
if (task->t.type == M_GFXTASK) {
|
||||
//printf("[sp] Gfx task: %08X\n", (uint32_t)ctx->r4);
|
||||
} else if (task->t.type == M_AUDTASK) {
|
||||
printf("[sp] Audio task: %08X\n", (uint32_t)ctx->r4);
|
||||
//printf("[sp] Audio task: %08X\n", (uint32_t)ctx->r4);
|
||||
}
|
||||
// For debugging
|
||||
if (dump_frame) {
|
||||
|
|
|
@ -5,6 +5,10 @@ extern "C" void osViSetYScale_recomp(uint8_t * restrict rdram, recomp_context *
|
|||
;
|
||||
}
|
||||
|
||||
extern "C" void osViSetXScale_recomp(uint8_t* restrict rdram, recomp_context * restrict ctx) {
|
||||
;
|
||||
}
|
||||
|
||||
extern "C" void osCreateViManager_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
||||
;
|
||||
}
|
||||
|
@ -18,11 +22,11 @@ extern "C" void osViSetSpecialFeatures_recomp(uint8_t* restrict rdram, recomp_co
|
|||
}
|
||||
|
||||
extern "C" void osViGetCurrentFramebuffer_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
||||
;
|
||||
ctx->r2 = (gpr)(int32_t)osViGetCurrentFramebuffer();
|
||||
}
|
||||
|
||||
extern "C" void osViGetNextFramebuffer_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
||||
;
|
||||
ctx->r2 = (gpr)(int32_t)osViGetNextFramebuffer();
|
||||
}
|
||||
|
||||
extern "C" void osViSwapBuffer_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
||||
|
|
Loading…
Reference in a new issue