More libultra function implementations, euc-jp decoding for print output, improved build times for output project

This commit is contained in:
Mr-Wiseguy 2023-01-16 23:01:21 -05:00
parent c6de2b6189
commit d2603ce07c
26 changed files with 30090 additions and 238 deletions

View file

@ -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

View file

@ -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;

View file

@ -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];

View 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>

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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

View file

@ -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 };
}

View file

@ -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:

View file

@ -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
View 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;
}

View file

@ -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

View file

@ -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() {

View file

@ -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
@ -52,9 +61,33 @@ extern "C" void osContGetReadData_recomp(uint8_t* restrict rdram, recomp_context
MEM_B(3, pad) = stick_y;
// 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

File diff suppressed because it is too large Load diff

11
test/src/euc-jp.h Normal file
View 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

View file

@ -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
View 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(&section_table[0], &section_table[num_sections], rom,
[](const SectionTableEntry& entry, uint32_t addr) {
return entry.rom_addr < addr;
}
);
auto upper = std::upper_bound(&section_table[0], &section_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(&section_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(&section_table[0], &section_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
View 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
}

View file

@ -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;
}

View file

@ -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
View 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;
}

View file

@ -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;

View file

@ -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) {

View file

@ -1,7 +1,11 @@
#include "../portultra/multilibultra.hpp"
#include "recomp.h"
extern "C" void osViSetYScale_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
extern "C" void osViSetYScale_recomp(uint8_t* restrict rdram, recomp_context * restrict ctx) {
;
}
extern "C" void osViSetXScale_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) {