mirror of
https://github.com/N64Recomp/N64ModernRuntime.git
synced 2026-06-14 03:52:53 +00:00
Merge 0bec4adafc into 5f350b858e
This commit is contained in:
commit
596f0ab9b3
47 changed files with 903 additions and 825 deletions
60
.clang-format
Normal file
60
.clang-format
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
IndentWidth: 4
|
||||
Language: Cpp
|
||||
UseTab: Never
|
||||
ColumnLimit: 140
|
||||
ReferenceAlignment: Left
|
||||
PointerAlignment: Right
|
||||
SpaceAfterCStyleCast: false
|
||||
Cpp11BracedListStyle: false
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
|
||||
# Cases inside switches are indented one level
|
||||
IndentCaseLabels: true
|
||||
|
||||
# Do not indent access modifiers (public, protected, private)
|
||||
AccessModifierOffset: -4
|
||||
|
||||
AlignAfterOpenBracket: AlwaysBreak
|
||||
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortEnumsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: false
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLambdasOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
|
||||
# Otherwise it would align escaped newlines to the column limit
|
||||
AlignEscapedNewlines: Left
|
||||
|
||||
AlignTrailingComments: true
|
||||
SortIncludes: true
|
||||
|
||||
BreakBeforeBraces: Custom
|
||||
BraceWrapping:
|
||||
AfterControlStatement: Never
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
AfterExternBlock: false
|
||||
BeforeElse: true
|
||||
|
||||
# Adds a comment at the end of the namespace with its name
|
||||
FixNamespaceComments: true
|
||||
# Indent namespace's content
|
||||
NamespaceIndentation: All
|
||||
|
||||
# Aligns preprocessor directives
|
||||
IndentPPDirectives: AfterHash
|
||||
|
||||
# No space between template and <
|
||||
SpaceAfterTemplateKeyword: false
|
||||
# Newline after template
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
|
||||
# Make the indentation of lambdas nicer
|
||||
LambdaBodyIndentation: Signature
|
||||
|
||||
# Tags these macros as types
|
||||
TypenameMacros: ["PTR"]
|
||||
|
|
@ -9,6 +9,6 @@ namespace recomp {
|
|||
std::ifstream open_input_backup_file(const std::filesystem::path& filepath, std::ios_base::openmode mode = std::ios_base::in);
|
||||
std::ofstream open_output_file_with_backup(const std::filesystem::path& filepath, std::ios_base::openmode mode = std::ios_base::out);
|
||||
bool finalize_output_file_with_backup(const std::filesystem::path& filepath);
|
||||
};
|
||||
}; // namespace recomp
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
#ifndef __RECOMP_GAME__
|
||||
#define __RECOMP_GAME__
|
||||
|
||||
#include <vector>
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
|
||||
#include "recomp.h"
|
||||
#include "rsp.hpp"
|
||||
|
|
@ -17,50 +17,46 @@ namespace recomp {
|
|||
bool is_enabled;
|
||||
|
||||
gpr entrypoint_address;
|
||||
void (*entrypoint)(uint8_t* rdram, recomp_context* context);
|
||||
void (*entrypoint)(uint8_t *rdram, recomp_context *context);
|
||||
|
||||
std::u8string stored_filename() const;
|
||||
};
|
||||
enum class RomValidationError {
|
||||
Good,
|
||||
FailedToOpen,
|
||||
NotARom,
|
||||
IncorrectRom,
|
||||
NotYet,
|
||||
IncorrectVersion,
|
||||
OtherError
|
||||
};
|
||||
void register_config_path(std::filesystem::path path);
|
||||
bool register_game(const recomp::GameEntry& entry);
|
||||
void check_all_stored_roms();
|
||||
bool load_stored_rom(std::u8string& game_id);
|
||||
RomValidationError select_rom(const std::filesystem::path& rom_path, std::u8string& game_id);
|
||||
bool is_rom_valid(std::u8string& game_id);
|
||||
bool is_rom_loaded();
|
||||
void set_rom_contents(std::vector<uint8_t>&& new_rom);
|
||||
void do_rom_read(uint8_t* rdram, gpr ram_address, uint32_t physical_addr, size_t num_bytes);
|
||||
void do_rom_pio(uint8_t* rdram, gpr ram_address, uint32_t physical_addr);
|
||||
enum class RomValidationError {
|
||||
Good,
|
||||
FailedToOpen,
|
||||
NotARom,
|
||||
IncorrectRom,
|
||||
NotYet,
|
||||
IncorrectVersion,
|
||||
OtherError
|
||||
};
|
||||
void register_config_path(std::filesystem::path path);
|
||||
bool register_game(const recomp::GameEntry& entry);
|
||||
void check_all_stored_roms();
|
||||
bool load_stored_rom(std::u8string& game_id);
|
||||
RomValidationError select_rom(const std::filesystem::path& rom_path, std::u8string& game_id);
|
||||
bool is_rom_valid(std::u8string& game_id);
|
||||
bool is_rom_loaded();
|
||||
void set_rom_contents(std::vector<uint8_t>&& new_rom);
|
||||
void do_rom_read(uint8_t *rdram, gpr ram_address, uint32_t physical_addr, size_t num_bytes);
|
||||
void do_rom_pio(uint8_t *rdram, gpr ram_address, uint32_t physical_addr);
|
||||
|
||||
/**
|
||||
* The following arguments contain mandatory callbacks that need to be registered (i.e., can't be `nullptr`):
|
||||
* - `rsp_callbacks`
|
||||
* - `renderer_callbacks`
|
||||
*
|
||||
* It must be called only once and it must be called before `ultramodern::preinit`.
|
||||
*/
|
||||
/**
|
||||
* The following arguments contain mandatory callbacks that need to be registered (i.e., can't be `nullptr`):
|
||||
* - `rsp_callbacks`
|
||||
* - `renderer_callbacks`
|
||||
*
|
||||
* It must be called only once and it must be called before `ultramodern::preinit`.
|
||||
*/
|
||||
void start(
|
||||
ultramodern::renderer::WindowHandle window_handle,
|
||||
const recomp::rsp::callbacks_t& rsp_callbacks,
|
||||
const ultramodern::renderer::callbacks_t& renderer_callbacks,
|
||||
const ultramodern::audio_callbacks_t& audio_callbacks,
|
||||
const ultramodern::input::callbacks_t& input_callbacks,
|
||||
const ultramodern::gfx_callbacks_t& gfx_callbacks,
|
||||
ultramodern::renderer::WindowHandle window_handle, const recomp::rsp::callbacks_t& rsp_callbacks,
|
||||
const ultramodern::renderer::callbacks_t& renderer_callbacks, const ultramodern::audio_callbacks_t& audio_callbacks,
|
||||
const ultramodern::input::callbacks_t& input_callbacks, const ultramodern::gfx_callbacks_t& gfx_callbacks,
|
||||
const ultramodern::events::callbacks_t& events_callbacks,
|
||||
const ultramodern::error_handling::callbacks_t& error_handling_callbacks_
|
||||
);
|
||||
const ultramodern::error_handling::callbacks_t& error_handling_callbacks_);
|
||||
|
||||
void start_game(const std::u8string& game_id);
|
||||
std::u8string current_game_id();
|
||||
}
|
||||
void start_game(const std::u8string& game_id);
|
||||
std::u8string current_game_id();
|
||||
} // namespace recomp
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
#include <ultramodern/ultra64.h>
|
||||
|
||||
template<int index, typename T>
|
||||
T _arg(uint8_t* rdram, recomp_context* ctx) {
|
||||
T _arg(uint8_t *rdram, recomp_context *ctx) {
|
||||
static_assert(index < 4, "Only args 0 through 3 supported");
|
||||
gpr raw_arg = (&ctx->r4)[index];
|
||||
if constexpr (std::is_same_v<T, float>) {
|
||||
|
|
@ -15,13 +15,14 @@ T _arg(uint8_t* rdram, recomp_context* ctx) {
|
|||
}
|
||||
else {
|
||||
// static_assert in else workaround
|
||||
[] <bool flag = false>() {
|
||||
[]<bool flag = false>() {
|
||||
static_assert(flag, "Floats in a2/a3 not supported");
|
||||
}();
|
||||
}
|
||||
();
|
||||
}
|
||||
}
|
||||
else if constexpr (std::is_pointer_v<T>) {
|
||||
static_assert (!std::is_pointer_v<std::remove_pointer_t<T>>, "Double pointers not supported");
|
||||
static_assert(!std::is_pointer_v<std::remove_pointer_t<T>>, "Double pointers not supported");
|
||||
return TO_PTR(std::remove_pointer_t<T>, raw_arg);
|
||||
}
|
||||
else if constexpr (std::is_integral_v<T>) {
|
||||
|
|
@ -30,14 +31,15 @@ T _arg(uint8_t* rdram, recomp_context* ctx) {
|
|||
}
|
||||
else {
|
||||
// static_assert in else workaround
|
||||
[] <bool flag = false>() {
|
||||
[]<bool flag = false>() {
|
||||
static_assert(flag, "Unsupported type");
|
||||
}();
|
||||
}
|
||||
();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void _return(recomp_context* ctx, T val) {
|
||||
template<typename T>
|
||||
void _return(recomp_context *ctx, T val) {
|
||||
static_assert(sizeof(T) <= 4 && "Only 32-bit value returns supported currently");
|
||||
if constexpr (std::is_same_v<T, float>) {
|
||||
ctx->f0.fl = val;
|
||||
|
|
@ -47,9 +49,10 @@ void _return(recomp_context* ctx, T val) {
|
|||
}
|
||||
else {
|
||||
// static_assert in else workaround
|
||||
[] <bool flag = false>() {
|
||||
[]<bool flag = false>() {
|
||||
static_assert(flag, "Unsupported type");
|
||||
}();
|
||||
}
|
||||
();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,30 +1,30 @@
|
|||
#ifndef __RECOMP_OVERLAYS_H__
|
||||
#define __RECOMP_OVERLAYS_H__
|
||||
|
||||
#include <cstdint>
|
||||
#include "sections.h"
|
||||
#include <cstdint>
|
||||
|
||||
namespace recomp {
|
||||
namespace overlays {
|
||||
struct overlay_section_table_data_t {
|
||||
SectionTableEntry* code_sections;
|
||||
SectionTableEntry *code_sections;
|
||||
size_t num_code_sections;
|
||||
size_t total_num_sections;
|
||||
};
|
||||
|
||||
struct overlays_by_index_t {
|
||||
int* table;
|
||||
int *table;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
void register_overlays(const overlay_section_table_data_t& sections, const overlays_by_index_t& overlays);
|
||||
|
||||
void register_patches(const char* patch_data, std::size_t patch_size, SectionTableEntry* code_sections);
|
||||
void read_patch_data(uint8_t* rdram, gpr patch_data_address);
|
||||
void register_patches(const char *patch_data, std::size_t patch_size, SectionTableEntry *code_sections);
|
||||
void read_patch_data(uint8_t *rdram, gpr patch_data_address);
|
||||
|
||||
void init_overlays();
|
||||
}
|
||||
};
|
||||
} // namespace overlays
|
||||
}; // namespace recomp
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
#include <cstdio>
|
||||
|
||||
#include "rsp_vu.hpp"
|
||||
#include "recomp.h"
|
||||
#include "rsp_vu.hpp"
|
||||
#include "ultramodern/ultra64.h"
|
||||
|
||||
// TODO: Move these to recomp namespace?
|
||||
|
|
@ -17,36 +17,34 @@ enum class RspExitReason {
|
|||
Unsupported
|
||||
};
|
||||
|
||||
using RspUcodeFunc = RspExitReason(uint8_t* rdram);
|
||||
using RspUcodeFunc = RspExitReason(uint8_t *rdram);
|
||||
|
||||
extern uint8_t dmem[];
|
||||
extern uint16_t rspReciprocals[512];
|
||||
extern uint16_t rspInverseSquareRoots[512];
|
||||
|
||||
#define RSP_MEM_B(offset, addr) \
|
||||
(*reinterpret_cast<int8_t*>(dmem + (0xFFF & (((offset) + (addr)) ^ 3))))
|
||||
#define RSP_MEM_B(offset, addr) (*reinterpret_cast<int8_t *>(dmem + (0xFFF & (((offset) + (addr)) ^ 3))))
|
||||
|
||||
#define RSP_MEM_BU(offset, addr) \
|
||||
(*reinterpret_cast<uint8_t*>(dmem + (0xFFF & (((offset) + (addr)) ^ 3))))
|
||||
#define RSP_MEM_BU(offset, addr) (*reinterpret_cast<uint8_t *>(dmem + (0xFFF & (((offset) + (addr)) ^ 3))))
|
||||
|
||||
static inline uint32_t RSP_MEM_W_LOAD(uint32_t offset, uint32_t addr) {
|
||||
uint32_t out;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
reinterpret_cast<uint8_t*>(&out)[i ^ 3] = RSP_MEM_BU(offset + i, addr);
|
||||
reinterpret_cast<uint8_t *>(&out)[i ^ 3] = RSP_MEM_BU(offset + i, addr);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static inline void RSP_MEM_W_STORE(uint32_t offset, uint32_t addr, uint32_t val) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
RSP_MEM_BU(offset + i, addr) = reinterpret_cast<uint8_t*>(&val)[i ^ 3];
|
||||
RSP_MEM_BU(offset + i, addr) = reinterpret_cast<uint8_t *>(&val)[i ^ 3];
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32_t RSP_MEM_HU_LOAD(uint32_t offset, uint32_t addr) {
|
||||
uint16_t out;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
reinterpret_cast<uint8_t*>(&out)[(i + 2) ^ 3] = RSP_MEM_BU(offset + i, addr);
|
||||
reinterpret_cast<uint8_t *>(&out)[(i + 2) ^ 3] = RSP_MEM_BU(offset + i, addr);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
|
@ -54,32 +52,29 @@ static inline uint32_t RSP_MEM_HU_LOAD(uint32_t offset, uint32_t addr) {
|
|||
static inline uint32_t RSP_MEM_H_LOAD(uint32_t offset, uint32_t addr) {
|
||||
int16_t out;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
reinterpret_cast<uint8_t*>(&out)[(i + 2) ^ 3] = RSP_MEM_BU(offset + i, addr);
|
||||
reinterpret_cast<uint8_t *>(&out)[(i + 2) ^ 3] = RSP_MEM_BU(offset + i, addr);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static inline void RSP_MEM_H_STORE(uint32_t offset, uint32_t addr, uint32_t val) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
RSP_MEM_BU(offset + i, addr) = reinterpret_cast<uint8_t*>(&val)[(i + 2) ^ 3];
|
||||
RSP_MEM_BU(offset + i, addr) = reinterpret_cast<uint8_t *>(&val)[(i + 2) ^ 3];
|
||||
}
|
||||
}
|
||||
|
||||
#define RSP_ADD32(a, b) \
|
||||
((int32_t)((a) + (b)))
|
||||
#define RSP_ADD32(a, b) ((int32_t)((a) + (b)))
|
||||
|
||||
#define RSP_SUB32(a, b) \
|
||||
((int32_t)((a) - (b)))
|
||||
#define RSP_SUB32(a, b) ((int32_t)((a) - (b)))
|
||||
|
||||
#define RSP_SIGNED(val) \
|
||||
((int32_t)(val))
|
||||
#define RSP_SIGNED(val) ((int32_t)(val))
|
||||
|
||||
#define SET_DMA_DMEM(dmem_addr) dma_dmem_address = (dmem_addr)
|
||||
#define SET_DMA_DRAM(dram_addr) dma_dram_address = (dram_addr)
|
||||
#define DO_DMA_READ(rd_len) dma_rdram_to_dmem(rdram, dma_dmem_address, dma_dram_address, (rd_len))
|
||||
#define DO_DMA_WRITE(wr_len) dma_dmem_to_rdram(rdram, dma_dmem_address, dma_dram_address, (wr_len))
|
||||
|
||||
static inline void dma_rdram_to_dmem(uint8_t* rdram, uint32_t dmem_addr, uint32_t dram_addr, uint32_t rd_len) {
|
||||
static inline void dma_rdram_to_dmem(uint8_t *rdram, uint32_t dmem_addr, uint32_t dram_addr, uint32_t rd_len) {
|
||||
rd_len += 1; // Read length is inclusive
|
||||
dram_addr &= 0xFFFFF8;
|
||||
assert(dmem_addr + rd_len <= 0x1000);
|
||||
|
|
@ -88,7 +83,7 @@ static inline void dma_rdram_to_dmem(uint8_t* rdram, uint32_t dmem_addr, uint32_
|
|||
}
|
||||
}
|
||||
|
||||
static inline void dma_dmem_to_rdram(uint8_t* rdram, uint32_t dmem_addr, uint32_t dram_addr, uint32_t wr_len) {
|
||||
static inline void dma_dmem_to_rdram(uint8_t *rdram, uint32_t dmem_addr, uint32_t dram_addr, uint32_t wr_len) {
|
||||
wr_len += 1; // Write length is inclusive
|
||||
dram_addr &= 0xFFFFF8;
|
||||
assert(dmem_addr + wr_len <= 0x1000);
|
||||
|
|
@ -100,24 +95,26 @@ static inline void dma_dmem_to_rdram(uint8_t* rdram, uint32_t dmem_addr, uint32_
|
|||
namespace recomp {
|
||||
namespace rsp {
|
||||
struct callbacks_t {
|
||||
using get_rsp_microcode_t = RspUcodeFunc*(const OSTask* task);
|
||||
using get_rsp_microcode_t = RspUcodeFunc *(const OSTask *task);
|
||||
|
||||
/**
|
||||
* Return a function pointer to the corresponding RSP microcode function for the given `task_type`.
|
||||
*
|
||||
* The full OSTask (`task` parameter) is passed in case the `task_type` number is not enough information to distinguish out the exact microcode function.
|
||||
* The full OSTask (`task` parameter) is passed in case the `task_type` number is not enough information to distinguish out the
|
||||
* exact microcode function.
|
||||
*
|
||||
* This function is allowed to return `nullptr` if no microcode matches the specified task. In this case a message will be printed to stderr and the program will exit.
|
||||
* This function is allowed to return `nullptr` if no microcode matches the specified task. In this case a message will be
|
||||
* printed to stderr and the program will exit.
|
||||
*/
|
||||
get_rsp_microcode_t* get_rsp_microcode;
|
||||
get_rsp_microcode_t *get_rsp_microcode;
|
||||
};
|
||||
|
||||
void set_callbacks(const callbacks_t& callbacks);
|
||||
|
||||
void constants_init();
|
||||
|
||||
bool run_task(uint8_t* rdram, const OSTask* task);
|
||||
}
|
||||
}
|
||||
bool run_task(uint8_t *rdram, const OSTask *task);
|
||||
} // namespace rsp
|
||||
} // namespace recomp
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -2,12 +2,13 @@
|
|||
#define __SECTIONS_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "recomp.h"
|
||||
|
||||
#define ARRLEN(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
typedef struct {
|
||||
recomp_func_t* func;
|
||||
recomp_func_t *func;
|
||||
uint32_t offset;
|
||||
} FuncEntry;
|
||||
|
||||
|
|
|
|||
|
|
@ -6,24 +6,24 @@
|
|||
|
||||
#define VI_NTSC_CLOCK 48681812
|
||||
|
||||
extern "C" void osAiSetFrequency_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osAiSetFrequency_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
uint32_t freq = ctx->r4;
|
||||
// This makes actual audio frequency more accurate to console, but may not be desirable
|
||||
//uint32_t dacRate = (uint32_t)(((float)VI_NTSC_CLOCK / freq) + 0.5f);
|
||||
//freq = VI_NTSC_CLOCK / dacRate;
|
||||
// uint32_t dacRate = (uint32_t)(((float)VI_NTSC_CLOCK / freq) + 0.5f);
|
||||
// freq = VI_NTSC_CLOCK / dacRate;
|
||||
ctx->r2 = freq;
|
||||
ultramodern::set_audio_frequency(freq);
|
||||
}
|
||||
|
||||
extern "C" void osAiSetNextBuffer_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osAiSetNextBuffer_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
ultramodern::queue_audio_buffer(rdram, ctx->r4, ctx->r5);
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void osAiGetLength_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osAiGetLength_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
ctx->r2 = ultramodern::get_remaining_audio_bytes();
|
||||
}
|
||||
|
||||
extern "C" void osAiGetStatus_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osAiGetStatus_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
ctx->r2 = 0x00000000; // Pretend the audio DMAs finish instantly
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,15 +4,15 @@
|
|||
|
||||
#define MAXCONTROLLERS 4
|
||||
|
||||
extern "C" void recomp_set_current_frame_poll_id(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void recomp_set_current_frame_poll_id(uint8_t *rdram, recomp_context *ctx) {
|
||||
// TODO reimplement the system for tagging polls with IDs to handle games with multithreaded input polling.
|
||||
}
|
||||
|
||||
extern "C" void recomp_measure_latency(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void recomp_measure_latency(uint8_t *rdram, recomp_context *ctx) {
|
||||
ultramodern::measure_input_latency();
|
||||
}
|
||||
|
||||
extern "C" void osContInit_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osContInit_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
PTR(OSMesgQueue) mq = _arg<0, PTR(OSMesgQueue)>(rdram, ctx);
|
||||
PTR(u8) bitpattern = _arg<1, PTR(u8)>(rdram, ctx);
|
||||
PTR(OSContStatus) data = _arg<2, PTR(OSContStatus)>(rdram, ctx);
|
||||
|
|
@ -22,7 +22,7 @@ extern "C" void osContInit_recomp(uint8_t* rdram, recomp_context* ctx) {
|
|||
_return<s32>(ctx, ret);
|
||||
}
|
||||
|
||||
extern "C" void osContReset_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osContReset_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
PTR(OSMesgQueue) mq = _arg<0, PTR(OSMesgQueue)>(rdram, ctx);
|
||||
PTR(OSContStatus) data = _arg<1, PTR(OSContStatus)>(rdram, ctx);
|
||||
|
||||
|
|
@ -31,7 +31,7 @@ extern "C" void osContReset_recomp(uint8_t* rdram, recomp_context* ctx) {
|
|||
_return<s32>(ctx, ret);
|
||||
}
|
||||
|
||||
extern "C" void osContStartReadData_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osContStartReadData_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
PTR(OSMesgQueue) mq = _arg<0, PTR(OSMesgQueue)>(rdram, ctx);
|
||||
|
||||
s32 ret = osContStartReadData(PASS_RDRAM mq);
|
||||
|
|
@ -39,7 +39,7 @@ extern "C" void osContStartReadData_recomp(uint8_t* rdram, recomp_context* ctx)
|
|||
_return<s32>(ctx, ret);
|
||||
}
|
||||
|
||||
extern "C" void osContGetReadData_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osContGetReadData_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
PTR(OSContPad) data = _arg<0, PTR(OSContPad)>(rdram, ctx);
|
||||
|
||||
OSContPad dummy_data[MAXCONTROLLERS];
|
||||
|
|
@ -54,7 +54,7 @@ extern "C" void osContGetReadData_recomp(uint8_t* rdram, recomp_context* ctx) {
|
|||
}
|
||||
}
|
||||
|
||||
extern "C" void osContStartQuery_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osContStartQuery_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
PTR(OSMesgQueue) mq = _arg<0, PTR(OSMesgQueue)>(rdram, ctx);
|
||||
|
||||
s32 ret = osContStartQuery(PASS_RDRAM mq);
|
||||
|
|
@ -62,13 +62,13 @@ extern "C" void osContStartQuery_recomp(uint8_t * rdram, recomp_context * ctx) {
|
|||
_return<s32>(ctx, ret);
|
||||
}
|
||||
|
||||
extern "C" void osContGetQuery_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osContGetQuery_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
PTR(OSContStatus) data = _arg<0, PTR(OSContStatus)>(rdram, ctx);
|
||||
|
||||
osContGetQuery(PASS_RDRAM data);
|
||||
}
|
||||
|
||||
extern "C" void osContSetCh_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osContSetCh_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
u8 ch = _arg<0, u8>(rdram, ctx);
|
||||
|
||||
s32 ret = osContSetCh(PASS_RDRAM ch);
|
||||
|
|
@ -76,7 +76,7 @@ extern "C" void osContSetCh_recomp(uint8_t* rdram, recomp_context* ctx) {
|
|||
_return<s32>(ctx, ret);
|
||||
}
|
||||
|
||||
extern "C" void __osMotorAccess_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void __osMotorAccess_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
PTR(OSPfs) pfs = _arg<0, PTR(OSPfs)>(rdram, ctx);
|
||||
s32 flag = _arg<1, s32>(rdram, ctx);
|
||||
|
||||
|
|
@ -85,7 +85,7 @@ extern "C" void __osMotorAccess_recomp(uint8_t* rdram, recomp_context* ctx) {
|
|||
_return<s32>(ctx, ret);
|
||||
}
|
||||
|
||||
extern "C" void osMotorInit_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osMotorInit_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
PTR(OSMesgQueue) mq = _arg<0, PTR(OSMesgQueue)>(rdram, ctx);
|
||||
PTR(OSPfs) pfs = _arg<1, PTR(OSPfs)>(rdram, ctx);
|
||||
int channel = _arg<2, s32>(rdram, ctx);
|
||||
|
|
@ -95,7 +95,7 @@ extern "C" void osMotorInit_recomp(uint8_t* rdram, recomp_context* ctx) {
|
|||
_return<s32>(ctx, ret);
|
||||
}
|
||||
|
||||
extern "C" void osMotorStart_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osMotorStart_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
PTR(OSPfs) pfs = _arg<0, PTR(OSPfs)>(rdram, ctx);
|
||||
|
||||
s32 ret = osMotorStart(PASS_RDRAM pfs);
|
||||
|
|
@ -103,7 +103,7 @@ extern "C" void osMotorStart_recomp(uint8_t* rdram, recomp_context* ctx) {
|
|||
_return<s32>(ctx, ret);
|
||||
}
|
||||
|
||||
extern "C" void osMotorStop_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osMotorStop_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
PTR(OSPfs) pfs = _arg<0, PTR(OSPfs)>(rdram, ctx);
|
||||
|
||||
s32 ret = osMotorStop(PASS_RDRAM pfs);
|
||||
|
|
|
|||
|
|
@ -29,15 +29,15 @@ constexpr void update_bit(uint32_t& state, uint32_t flags, RDPStatusBit bit) {
|
|||
|
||||
uint32_t rdp_state = 1 << (int)RDPStatusBit::BufferReady;
|
||||
|
||||
extern "C" void osDpSetNextBuffer_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osDpSetNextBuffer_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
extern "C" void osDpGetStatus_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osDpGetStatus_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
ctx->r2 = rdp_state;
|
||||
}
|
||||
|
||||
extern "C" void osDpSetStatus_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osDpSetStatus_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
update_bit(rdp_state, ctx->r4, RDPStatusBit::XbusDmem);
|
||||
update_bit(rdp_state, ctx->r4, RDPStatusBit::Freeze);
|
||||
update_bit(rdp_state, ctx->r4, RDPStatusBit::Flush);
|
||||
|
|
|
|||
|
|
@ -11,15 +11,15 @@ constexpr int eep4_block_count = eep4_size / eeprom_block_size;
|
|||
constexpr int eep16_size = 16384;
|
||||
constexpr int eep16_block_count = eep16_size / eeprom_block_size;
|
||||
|
||||
extern "C" void osEepromProbe_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osEepromProbe_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
ctx->r2 = 0x02; // EEP16K
|
||||
}
|
||||
|
||||
extern "C" void osEepromWrite_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
assert(false);// ctx->r2 = 8; // CONT_NO_RESPONSE_ERROR
|
||||
extern "C" void osEepromWrite_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
assert(false); // ctx->r2 = 8; // CONT_NO_RESPONSE_ERROR
|
||||
}
|
||||
|
||||
extern "C" void osEepromLongWrite_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osEepromLongWrite_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
uint8_t eep_address = ctx->r5;
|
||||
gpr buffer = ctx->r6;
|
||||
int32_t nbytes = ctx->r7;
|
||||
|
|
@ -32,11 +32,11 @@ extern "C" void osEepromLongWrite_recomp(uint8_t* rdram, recomp_context* ctx) {
|
|||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void osEepromRead_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
assert(false);// ctx->r2 = 8; // CONT_NO_RESPONSE_ERROR
|
||||
extern "C" void osEepromRead_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
assert(false); // ctx->r2 = 8; // CONT_NO_RESPONSE_ERROR
|
||||
}
|
||||
|
||||
extern "C" void osEepromLongRead_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osEepromLongRead_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
uint8_t eep_address = ctx->r5;
|
||||
gpr buffer = ctx->r6;
|
||||
int32_t nbytes = ctx->r7;
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@ constexpr std::u8string_view backup_suffix = u8".bak";
|
|||
constexpr std::u8string_view temp_suffix = u8".temp";
|
||||
|
||||
std::ifstream recomp::open_input_backup_file(const std::filesystem::path& filepath, std::ios_base::openmode mode) {
|
||||
std::filesystem::path backup_path{filepath};
|
||||
std::filesystem::path backup_path{ filepath };
|
||||
backup_path += backup_suffix;
|
||||
return std::ifstream{backup_path, mode};
|
||||
return std::ifstream{ backup_path, mode };
|
||||
}
|
||||
|
||||
std::ifstream recomp::open_input_file_with_backup(const std::filesystem::path& filepath, std::ios_base::openmode mode) {
|
||||
std::ifstream ret{filepath, mode};
|
||||
std::ifstream ret{ filepath, mode };
|
||||
|
||||
// Check if the file failed to open and open the corresponding backup file instead if so.
|
||||
if (!ret.good()) {
|
||||
|
|
@ -21,7 +21,7 @@ std::ifstream recomp::open_input_file_with_backup(const std::filesystem::path& f
|
|||
}
|
||||
|
||||
std::ofstream recomp::open_output_file_with_backup(const std::filesystem::path& filepath, std::ios_base::openmode mode) {
|
||||
std::filesystem::path temp_path{filepath};
|
||||
std::filesystem::path temp_path{ filepath };
|
||||
temp_path += temp_suffix;
|
||||
std::ofstream temp_file_out{ temp_path, mode };
|
||||
|
||||
|
|
@ -29,10 +29,10 @@ std::ofstream recomp::open_output_file_with_backup(const std::filesystem::path&
|
|||
}
|
||||
|
||||
bool recomp::finalize_output_file_with_backup(const std::filesystem::path& filepath) {
|
||||
std::filesystem::path backup_path{filepath};
|
||||
std::filesystem::path backup_path{ filepath };
|
||||
backup_path += backup_suffix;
|
||||
|
||||
std::filesystem::path temp_path{filepath};
|
||||
std::filesystem::path temp_path{ filepath };
|
||||
temp_path += temp_suffix;
|
||||
|
||||
std::error_code ec;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
#include <array>
|
||||
#include <cassert>
|
||||
|
||||
#include <ultramodern/ultra64.h>
|
||||
#include <ultramodern/ultramodern.hpp>
|
||||
|
||||
#include "recomp.h"
|
||||
|
||||
// TODO move this out into ultramodern code
|
||||
|
|
@ -13,24 +15,24 @@ constexpr uint32_t page_count = flash_size / page_size;
|
|||
constexpr uint32_t sector_size = page_size * pages_per_sector;
|
||||
constexpr uint32_t sector_count = flash_size / sector_size;
|
||||
|
||||
void save_write_ptr(const void* in, uint32_t offset, uint32_t count);
|
||||
void save_write_ptr(const void *in, uint32_t offset, uint32_t count);
|
||||
void save_write(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t count);
|
||||
void save_read(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t count);
|
||||
void save_clear(uint32_t start, uint32_t size, char value);
|
||||
|
||||
std::array<char, page_size> write_buffer;
|
||||
|
||||
extern "C" void osFlashInit_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osFlashInit_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
ctx->r2 = ultramodern::flash_handle;
|
||||
}
|
||||
|
||||
extern "C" void osFlashReadStatus_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osFlashReadStatus_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
PTR(u8) flash_status = ctx->r4;
|
||||
|
||||
MEM_B(0, flash_status) = 0;
|
||||
}
|
||||
|
||||
extern "C" void osFlashReadId_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osFlashReadId_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
PTR(u32) flash_type = ctx->r4;
|
||||
PTR(u32) flash_maker = ctx->r5;
|
||||
|
||||
|
|
@ -39,24 +41,23 @@ extern "C" void osFlashReadId_recomp(uint8_t * rdram, recomp_context * ctx) {
|
|||
MEM_W(0, flash_maker) = 0x00C2001E;
|
||||
}
|
||||
|
||||
extern "C" void osFlashClearStatus_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
|
||||
extern "C" void osFlashClearStatus_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
}
|
||||
|
||||
extern "C" void osFlashAllErase_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osFlashAllErase_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
save_clear(0, ultramodern::save_size, 0xFF);
|
||||
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void osFlashAllEraseThrough_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osFlashAllEraseThrough_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
save_clear(0, ultramodern::save_size, 0xFF);
|
||||
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
// This function is named sector but really means page.
|
||||
extern "C" void osFlashSectorErase_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osFlashSectorErase_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
uint32_t page_num = (uint32_t)ctx->r4;
|
||||
|
||||
// Prevent out of bounds erase
|
||||
|
|
@ -71,7 +72,7 @@ extern "C" void osFlashSectorErase_recomp(uint8_t * rdram, recomp_context * ctx)
|
|||
}
|
||||
|
||||
// Same naming issue as above.
|
||||
extern "C" void osFlashSectorEraseThrough_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osFlashSectorEraseThrough_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
uint32_t page_num = (uint32_t)ctx->r4;
|
||||
|
||||
// Prevent out of bounds erase
|
||||
|
|
@ -85,17 +86,17 @@ extern "C" void osFlashSectorEraseThrough_recomp(uint8_t * rdram, recomp_context
|
|||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void osFlashCheckEraseEnd_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osFlashCheckEraseEnd_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
// All erases are blocking in this implementation, so this should always return OK.
|
||||
ctx->r2 = 0; // FLASH_STATUS_ERASE_OK
|
||||
}
|
||||
|
||||
extern "C" void osFlashWriteBuffer_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
OSIoMesg* mb = TO_PTR(OSIoMesg, ctx->r4);
|
||||
extern "C" void osFlashWriteBuffer_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
OSIoMesg *mb = TO_PTR(OSIoMesg, ctx->r4);
|
||||
int32_t pri = ctx->r5;
|
||||
PTR(void) dramAddr = ctx->r6;
|
||||
PTR(OSMesgQueue) mq = ctx->r7;
|
||||
|
||||
|
||||
// Copy the input data into the write buffer
|
||||
for (size_t i = 0; i < page_size; i++) {
|
||||
write_buffer[i] = MEM_B(i, dramAddr);
|
||||
|
|
@ -107,7 +108,7 @@ extern "C" void osFlashWriteBuffer_recomp(uint8_t * rdram, recomp_context * ctx)
|
|||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void osFlashWriteArray_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osFlashWriteArray_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
uint32_t page_num = ctx->r4;
|
||||
|
||||
// Copy the write buffer into the save file
|
||||
|
|
@ -116,8 +117,8 @@ extern "C" void osFlashWriteArray_recomp(uint8_t * rdram, recomp_context * ctx)
|
|||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void osFlashReadArray_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
OSIoMesg* mb = TO_PTR(OSIoMesg, ctx->r4);
|
||||
extern "C" void osFlashReadArray_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
OSIoMesg *mb = TO_PTR(OSIoMesg, ctx->r4);
|
||||
int32_t pri = ctx->r5;
|
||||
uint32_t page_num = ctx->r6;
|
||||
PTR(void) dramAddr = ctx->r7;
|
||||
|
|
@ -136,6 +137,6 @@ extern "C" void osFlashReadArray_recomp(uint8_t * rdram, recomp_context * ctx) {
|
|||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void osFlashChange_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osFlashChange_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
assert(false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
#include <ultramodern/ultramodern.hpp>
|
||||
|
||||
#include "recomp.h"
|
||||
|
||||
// TODO remove these by implementing the necessary instructions and control flow handling in the recompiler.
|
||||
// This has already been partially completed.
|
||||
|
||||
extern "C" void __udivdi3_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void __udivdi3_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
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;
|
||||
|
|
@ -13,7 +14,7 @@ extern "C" void __udivdi3_recomp(uint8_t * rdram, recomp_context * ctx) {
|
|||
ctx->r3 = (int32_t)(ret >> 0);
|
||||
}
|
||||
|
||||
extern "C" void __divdi3_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void __divdi3_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
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;
|
||||
|
|
@ -22,7 +23,7 @@ extern "C" void __divdi3_recomp(uint8_t * rdram, recomp_context * ctx) {
|
|||
ctx->r3 = (int32_t)(ret >> 0);
|
||||
}
|
||||
|
||||
extern "C" void __umoddi3_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void __umoddi3_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
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;
|
||||
|
|
@ -31,7 +32,7 @@ extern "C" void __umoddi3_recomp(uint8_t * rdram, recomp_context * ctx) {
|
|||
ctx->r3 = (int32_t)(ret >> 0);
|
||||
}
|
||||
|
||||
extern "C" void __ull_div_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void __ull_div_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
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;
|
||||
|
|
@ -40,7 +41,7 @@ extern "C" void __ull_div_recomp(uint8_t * rdram, recomp_context * ctx) {
|
|||
ctx->r3 = (int32_t)(ret >> 0);
|
||||
}
|
||||
|
||||
extern "C" void __ll_div_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void __ll_div_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
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;
|
||||
|
|
@ -49,7 +50,7 @@ extern "C" void __ll_div_recomp(uint8_t * rdram, recomp_context * ctx) {
|
|||
ctx->r3 = (int32_t)(ret >> 0);
|
||||
}
|
||||
|
||||
extern "C" void __ll_mul_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void __ll_mul_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
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;
|
||||
|
|
@ -58,7 +59,7 @@ extern "C" void __ll_mul_recomp(uint8_t * rdram, recomp_context * ctx) {
|
|||
ctx->r3 = (int32_t)(ret >> 0);
|
||||
}
|
||||
|
||||
extern "C" void __ull_rem_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void __ull_rem_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
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;
|
||||
|
|
@ -67,14 +68,14 @@ extern "C" void __ull_rem_recomp(uint8_t * rdram, recomp_context * ctx) {
|
|||
ctx->r3 = (int32_t)(ret >> 0);
|
||||
}
|
||||
|
||||
extern "C" void __ull_to_d_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void __ull_to_d_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
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 * rdram, recomp_context * ctx) {
|
||||
extern "C" void __ull_to_f_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
uint64_t a = (ctx->r4 << 32) | ((ctx->r5 << 0) & 0xFFFFFFFFu);
|
||||
float ret = (float)a;
|
||||
|
||||
|
|
|
|||
|
|
@ -7,14 +7,14 @@
|
|||
|
||||
#include "ultramodern/ultramodern.hpp"
|
||||
|
||||
#include "recomp.h"
|
||||
#include "overlays.hpp"
|
||||
#include "recomp.h"
|
||||
#include "sections.h"
|
||||
|
||||
static recomp::overlays::overlay_section_table_data_t sections_info {};
|
||||
static recomp::overlays::overlays_by_index_t overlays_info {};
|
||||
static recomp::overlays::overlay_section_table_data_t sections_info{};
|
||||
static recomp::overlays::overlays_by_index_t overlays_info{};
|
||||
|
||||
static SectionTableEntry* patch_code_sections = nullptr;
|
||||
static SectionTableEntry *patch_code_sections = nullptr;
|
||||
static std::vector<char> patch_data;
|
||||
|
||||
void recomp::overlays::register_overlays(const overlay_section_table_data_t& sections, const overlays_by_index_t& overlays) {
|
||||
|
|
@ -22,7 +22,7 @@ void recomp::overlays::register_overlays(const overlay_section_table_data_t& sec
|
|||
overlays_info = overlays;
|
||||
}
|
||||
|
||||
void recomp::overlays::register_patches(const char* patch, std::size_t size, SectionTableEntry* sections) {
|
||||
void recomp::overlays::register_patches(const char *patch, std::size_t size, SectionTableEntry *sections) {
|
||||
patch_code_sections = sections;
|
||||
|
||||
patch_data.resize(size);
|
||||
|
|
@ -44,7 +44,7 @@ struct LoadedSection {
|
|||
};
|
||||
|
||||
std::vector<LoadedSection> loaded_sections{};
|
||||
std::unordered_map<int32_t, recomp_func_t*> func_map{};
|
||||
std::unordered_map<int32_t, recomp_func_t *> func_map{};
|
||||
|
||||
void load_overlay(size_t section_table_index, int32_t ram) {
|
||||
const SectionTableEntry& section = sections_info.code_sections[section_table_index];
|
||||
|
|
@ -73,29 +73,29 @@ static void load_patch_functions() {
|
|||
load_special_overlay(patch_code_sections[0], patch_code_sections[0].ram_addr);
|
||||
}
|
||||
|
||||
void recomp::overlays::read_patch_data(uint8_t* rdram, gpr patch_data_address) {
|
||||
void recomp::overlays::read_patch_data(uint8_t *rdram, gpr patch_data_address) {
|
||||
for (size_t i = 0; i < patch_data.size(); i++) {
|
||||
MEM_B(i, patch_data_address) = patch_data[i];
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
int32_t* section_addresses = nullptr;
|
||||
int32_t *section_addresses = nullptr;
|
||||
}
|
||||
|
||||
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(§ions_info.code_sections[0], §ions_info.code_sections[sections_info.num_code_sections], rom,
|
||||
auto lower = std::lower_bound(
|
||||
§ions_info.code_sections[0], §ions_info.code_sections[sections_info.num_code_sections], rom,
|
||||
[](const SectionTableEntry& entry, uint32_t addr) {
|
||||
return entry.rom_addr < addr;
|
||||
}
|
||||
);
|
||||
auto upper = std::upper_bound(§ions_info.code_sections[0], §ions_info.code_sections[sections_info.num_code_sections], (uint32_t)(rom + size),
|
||||
});
|
||||
auto upper = std::upper_bound(
|
||||
§ions_info.code_sections[0], §ions_info.code_sections[sections_info.num_code_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(§ions_info.code_sections[0], it), it->rom_addr - rom + ram_addr);
|
||||
|
|
@ -106,7 +106,9 @@ extern "C" void unload_overlay_by_id(uint32_t id) {
|
|||
uint32_t section_table_index = overlays_info.table[id];
|
||||
const SectionTableEntry& section = sections_info.code_sections[section_table_index];
|
||||
|
||||
auto find_it = std::find_if(loaded_sections.begin(), loaded_sections.end(), [section_table_index](const LoadedSection& s) { return s.section_table_index == section_table_index; });
|
||||
auto find_it = std::find_if(loaded_sections.begin(), loaded_sections.end(), [section_table_index](const LoadedSection& s) {
|
||||
return s.section_table_index == section_table_index;
|
||||
});
|
||||
|
||||
if (find_it != loaded_sections.end()) {
|
||||
// Determine where each function was loaded to and remove that entry from the function map
|
||||
|
|
@ -144,11 +146,12 @@ extern "C" void unload_overlays(int32_t ram_addr, uint32_t size) {
|
|||
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,
|
||||
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);
|
||||
section.rom_addr, section.size, it->loaded_ram_addr, ram_addr, size);
|
||||
assert(false);
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
|
@ -177,16 +180,16 @@ void recomp::overlays::init_overlays() {
|
|||
}
|
||||
|
||||
// Sort the executable sections by rom address
|
||||
std::sort(§ions_info.code_sections[0], §ions_info.code_sections[sections_info.num_code_sections],
|
||||
std::sort(
|
||||
§ions_info.code_sections[0], §ions_info.code_sections[sections_info.num_code_sections],
|
||||
[](const SectionTableEntry& a, const SectionTableEntry& b) {
|
||||
return a.rom_addr < b.rom_addr;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
load_patch_functions();
|
||||
}
|
||||
|
||||
extern "C" recomp_func_t * get_function(int32_t 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);
|
||||
|
|
@ -195,4 +198,3 @@ extern "C" recomp_func_t * get_function(int32_t addr) {
|
|||
}
|
||||
return func_find->second;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,34 +2,34 @@
|
|||
#include <ultramodern/ultra64.h>
|
||||
#include <ultramodern/ultramodern.hpp>
|
||||
|
||||
extern "C" void osPfsInitPak_recomp(uint8_t * rdram, recomp_context* ctx) {
|
||||
extern "C" void osPfsInitPak_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
ctx->r2 = 1; // PFS_ERR_NOPACK
|
||||
}
|
||||
|
||||
extern "C" void osPfsFreeBlocks_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osPfsFreeBlocks_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
ctx->r2 = 1; // PFS_ERR_NOPACK
|
||||
}
|
||||
|
||||
extern "C" void osPfsAllocateFile_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osPfsAllocateFile_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
ctx->r2 = 1; // PFS_ERR_NOPACK
|
||||
}
|
||||
|
||||
extern "C" void osPfsDeleteFile_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osPfsDeleteFile_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
ctx->r2 = 1; // PFS_ERR_NOPACK
|
||||
}
|
||||
|
||||
extern "C" void osPfsFileState_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osPfsFileState_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
ctx->r2 = 1; // PFS_ERR_NOPACK
|
||||
}
|
||||
|
||||
extern "C" void osPfsFindFile_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osPfsFindFile_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
ctx->r2 = 1; // PFS_ERR_NOPACK
|
||||
}
|
||||
|
||||
extern "C" void osPfsReadWriteFile_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osPfsReadWriteFile_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
ctx->r2 = 1; // PFS_ERR_NOPACK
|
||||
}
|
||||
|
||||
extern "C" void osPfsChecker_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osPfsChecker_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
ctx->r2 = 1; // PFS_ERR_NOPACK
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,17 @@
|
|||
#include <memory>
|
||||
#include <fstream>
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include "recomp.h"
|
||||
#include "game.hpp"
|
||||
#include "files.hpp"
|
||||
#include <string>
|
||||
|
||||
#include <ultramodern/ultra64.h>
|
||||
#include <ultramodern/ultramodern.hpp>
|
||||
|
||||
#include "files.hpp"
|
||||
#include "game.hpp"
|
||||
#include "recomp.h"
|
||||
|
||||
static std::vector<uint8_t> rom;
|
||||
|
||||
bool recomp::is_rom_loaded() {
|
||||
|
|
@ -35,14 +37,14 @@ constexpr uint32_t phys_to_k1(uint32_t addr) {
|
|||
return addr | 0xA0000000;
|
||||
}
|
||||
|
||||
extern "C" void __osPiGetAccess_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void __osPiGetAccess_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
}
|
||||
|
||||
extern "C" void __osPiRelAccess_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void __osPiRelAccess_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
}
|
||||
|
||||
extern "C" void osCartRomInit_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
OSPiHandle* handle = TO_PTR(OSPiHandle, ultramodern::cart_handle);
|
||||
extern "C" void osCartRomInit_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
OSPiHandle *handle = TO_PTR(OSPiHandle, ultramodern::cart_handle);
|
||||
handle->type = 0; // cart
|
||||
handle->baseAddress = phys_to_k1(rom_base);
|
||||
handle->domain = 0;
|
||||
|
|
@ -50,8 +52,8 @@ extern "C" void osCartRomInit_recomp(uint8_t* rdram, recomp_context* ctx) {
|
|||
ctx->r2 = (gpr)ultramodern::cart_handle;
|
||||
}
|
||||
|
||||
extern "C" void osDriveRomInit_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
OSPiHandle* handle = TO_PTR(OSPiHandle, ultramodern::drive_handle);
|
||||
extern "C" void osDriveRomInit_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
OSPiHandle *handle = TO_PTR(OSPiHandle, ultramodern::drive_handle);
|
||||
handle->type = 1; // bulk
|
||||
handle->baseAddress = phys_to_k1(drive_base);
|
||||
handle->domain = 0;
|
||||
|
|
@ -59,28 +61,28 @@ extern "C" void osDriveRomInit_recomp(uint8_t * rdram, recomp_context * ctx) {
|
|||
ctx->r2 = (gpr)ultramodern::drive_handle;
|
||||
}
|
||||
|
||||
extern "C" void osCreatePiManager_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osCreatePiManager_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
;
|
||||
}
|
||||
|
||||
void recomp::do_rom_read(uint8_t* rdram, gpr ram_address, uint32_t physical_addr, size_t num_bytes) {
|
||||
void recomp::do_rom_read(uint8_t *rdram, gpr ram_address, uint32_t physical_addr, size_t num_bytes) {
|
||||
// TODO use word copies when possible
|
||||
|
||||
// TODO handle misaligned DMA
|
||||
assert((physical_addr & 0x1) == 0 && "Only PI DMA from aligned ROM addresses is currently supported");
|
||||
assert((ram_address & 0x7) == 0 && "Only PI DMA to aligned RDRAM addresses is currently supported");
|
||||
assert((num_bytes & 0x1) == 0 && "Only PI DMA with aligned sizes is currently supported");
|
||||
uint8_t* rom_addr = rom.data() + physical_addr - rom_base;
|
||||
uint8_t *rom_addr = rom.data() + physical_addr - rom_base;
|
||||
for (size_t i = 0; i < num_bytes; i++) {
|
||||
MEM_B(i, ram_address) = *rom_addr;
|
||||
rom_addr++;
|
||||
}
|
||||
}
|
||||
|
||||
void recomp::do_rom_pio(uint8_t* rdram, gpr ram_address, uint32_t physical_addr) {
|
||||
void recomp::do_rom_pio(uint8_t *rdram, gpr ram_address, uint32_t physical_addr) {
|
||||
assert((physical_addr & 0x3) == 0 && "PIO not 4-byte aligned in device, currently unsupported");
|
||||
assert((ram_address & 0x3) == 0 && "PIO not 4-byte aligned in RDRAM, currently unsupported");
|
||||
uint8_t* rom_addr = rom.data() + physical_addr - rom_base;
|
||||
uint8_t *rom_addr = rom.data() + physical_addr - rom_base;
|
||||
MEM_B(0, ram_address) = *rom_addr++;
|
||||
MEM_B(1, ram_address) = *rom_addr++;
|
||||
MEM_B(2, ram_address) = *rom_addr++;
|
||||
|
|
@ -99,7 +101,7 @@ const std::u8string save_folder = u8"saves";
|
|||
extern std::filesystem::path config_path;
|
||||
|
||||
std::filesystem::path get_save_file_path() {
|
||||
return config_path / save_folder / (std::u8string{recomp::current_game_id()} + u8".bin");
|
||||
return config_path / save_folder / (std::u8string{ recomp::current_game_id() } + u8".bin");
|
||||
}
|
||||
|
||||
void update_save_file() {
|
||||
|
|
@ -119,7 +121,8 @@ void update_save_file() {
|
|||
saving_failed = !recomp::finalize_output_file_with_backup(get_save_file_path());
|
||||
}
|
||||
if (saving_failed) {
|
||||
ultramodern::error_handling::message_box("Failed to write to the save file. Check your file permissions and whether the save folder has been moved to Dropbox or similar, as this can cause issues.");
|
||||
ultramodern::error_handling::message_box("Failed to write to the save file. Check your file permissions and whether the save "
|
||||
"folder has been moved to Dropbox or similar, as this can cause issues.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -148,18 +151,18 @@ void saving_thread_func(RDRAM_ARG1) {
|
|||
}
|
||||
}
|
||||
|
||||
void save_write_ptr(const void* in, uint32_t offset, uint32_t count) {
|
||||
void save_write_ptr(const void *in, uint32_t offset, uint32_t count) {
|
||||
{
|
||||
std::lock_guard lock { save_context.save_buffer_mutex };
|
||||
std::lock_guard lock{ save_context.save_buffer_mutex };
|
||||
memcpy(&save_context.save_buffer[offset], in, count);
|
||||
}
|
||||
|
||||
|
||||
save_context.write_sempahore.signal();
|
||||
}
|
||||
|
||||
void save_write(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t count) {
|
||||
{
|
||||
std::lock_guard lock { save_context.save_buffer_mutex };
|
||||
std::lock_guard lock{ save_context.save_buffer_mutex };
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
save_context.save_buffer[offset + i] = MEM_B(i, rdram_address);
|
||||
}
|
||||
|
|
@ -169,7 +172,7 @@ void save_write(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t cou
|
|||
}
|
||||
|
||||
void save_read(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t count) {
|
||||
std::lock_guard lock { save_context.save_buffer_mutex };
|
||||
std::lock_guard lock{ save_context.save_buffer_mutex };
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
MEM_B(i, rdram_address) = save_context.save_buffer[offset + i];
|
||||
}
|
||||
|
|
@ -177,7 +180,7 @@ void save_read(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t coun
|
|||
|
||||
void save_clear(uint32_t start, uint32_t size, char value) {
|
||||
{
|
||||
std::lock_guard lock { save_context.save_buffer_mutex };
|
||||
std::lock_guard lock{ save_context.save_buffer_mutex };
|
||||
std::fill_n(save_context.save_buffer.begin() + start, size, value);
|
||||
}
|
||||
|
||||
|
|
@ -200,7 +203,7 @@ void ultramodern::init_saving(RDRAM_ARG1) {
|
|||
save_context.save_buffer.fill(0);
|
||||
}
|
||||
|
||||
save_context.saving_thread = std::thread{saving_thread_func, PASS_RDRAM};
|
||||
save_context.saving_thread = std::thread{ saving_thread_func, PASS_RDRAM };
|
||||
}
|
||||
|
||||
void ultramodern::join_saving_thread() {
|
||||
|
|
@ -219,32 +222,37 @@ void do_dma(RDRAM_ARG PTR(OSMesgQueue) mq, gpr rdram_address, uint32_t physical_
|
|||
|
||||
// Send a message to the mq to indicate that the transfer completed
|
||||
osSendMesg(rdram, mq, 0, OS_MESG_NOBLOCK);
|
||||
} else if (physical_addr >= sram_base) {
|
||||
}
|
||||
else if (physical_addr >= sram_base) {
|
||||
// read sram
|
||||
save_read(rdram, rdram_address, physical_addr - sram_base, size);
|
||||
|
||||
// Send a message to the mq to indicate that the transfer completed
|
||||
osSendMesg(rdram, mq, 0, OS_MESG_NOBLOCK);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "[WARN] PI DMA read from unknown region, phys address 0x%08X\n", physical_addr);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
if (physical_addr >= rom_base) {
|
||||
// write cart rom
|
||||
throw std::runtime_error("ROM DMA write unimplemented");
|
||||
} else if (physical_addr >= sram_base) {
|
||||
}
|
||||
else if (physical_addr >= sram_base) {
|
||||
// write sram
|
||||
save_write(rdram, rdram_address, physical_addr - sram_base, size);
|
||||
|
||||
// Send a message to the mq to indicate that the transfer completed
|
||||
osSendMesg(rdram, mq, 0, OS_MESG_NOBLOCK);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "[WARN] PI DMA write to unknown region, phys address 0x%08X\n", physical_addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void osPiStartDma_recomp(RDRAM_ARG recomp_context* ctx) {
|
||||
extern "C" void osPiStartDma_recomp(RDRAM_ARG recomp_context *ctx) {
|
||||
uint32_t mb = ctx->r4;
|
||||
uint32_t pri = ctx->r5;
|
||||
uint32_t direction = ctx->r6;
|
||||
|
|
@ -261,9 +269,9 @@ extern "C" void osPiStartDma_recomp(RDRAM_ARG recomp_context* ctx) {
|
|||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void osEPiStartDma_recomp(RDRAM_ARG recomp_context* ctx) {
|
||||
OSPiHandle* handle = TO_PTR(OSPiHandle, ctx->r4);
|
||||
OSIoMesg* mb = TO_PTR(OSIoMesg, ctx->r5);
|
||||
extern "C" void osEPiStartDma_recomp(RDRAM_ARG recomp_context *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;
|
||||
|
|
@ -278,8 +286,8 @@ extern "C" void osEPiStartDma_recomp(RDRAM_ARG recomp_context* ctx) {
|
|||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void osEPiReadIo_recomp(RDRAM_ARG recomp_context * ctx) {
|
||||
OSPiHandle* handle = TO_PTR(OSPiHandle, ctx->r4);
|
||||
extern "C" void osEPiReadIo_recomp(RDRAM_ARG recomp_context *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);
|
||||
|
|
@ -287,7 +295,8 @@ extern "C" void osEPiReadIo_recomp(RDRAM_ARG recomp_context * ctx) {
|
|||
if (physical_addr > rom_base) {
|
||||
// cart rom
|
||||
recomp::do_rom_pio(PASS_RDRAM dramAddr, physical_addr);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// sram
|
||||
assert(false && "SRAM ReadIo unimplemented");
|
||||
}
|
||||
|
|
@ -295,10 +304,10 @@ extern "C" void osEPiReadIo_recomp(RDRAM_ARG recomp_context * ctx) {
|
|||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void osPiGetStatus_recomp(RDRAM_ARG recomp_context * ctx) {
|
||||
extern "C" void osPiGetStatus_recomp(RDRAM_ARG recomp_context *ctx) {
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void osPiRawStartDma_recomp(RDRAM_ARG recomp_context * ctx) {
|
||||
extern "C" void osPiRawStartDma_recomp(RDRAM_ARG recomp_context *ctx) {
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,34 +2,35 @@
|
|||
|
||||
#include <ultramodern/ultra64.h>
|
||||
#include <ultramodern/ultramodern.hpp>
|
||||
#include "recomp.h"
|
||||
|
||||
#include "euc-jp.hpp"
|
||||
#include "recomp.h"
|
||||
|
||||
extern "C" void __checkHardware_msp_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void __checkHardware_msp_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void __checkHardware_kmc_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void __checkHardware_kmc_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void __checkHardware_isv_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void __checkHardware_isv_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void __osInitialize_msp_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void __osInitialize_msp_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
}
|
||||
|
||||
extern "C" void __osInitialize_kmc_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void __osInitialize_kmc_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
}
|
||||
|
||||
extern "C" void __osInitialize_isv_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void __osInitialize_isv_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
}
|
||||
|
||||
extern "C" void isPrintfInit_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void isPrintfInit_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
}
|
||||
|
||||
extern "C" void __osRdbSend_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void __osRdbSend_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
gpr buf = ctx->r4;
|
||||
size_t size = ctx->r5;
|
||||
u32 type = (u32)ctx->r6;
|
||||
|
|
@ -45,16 +46,16 @@ extern "C" void __osRdbSend_recomp(uint8_t * rdram, recomp_context * ctx) {
|
|||
ctx->r2 = size;
|
||||
}
|
||||
|
||||
extern "C" void is_proutSyncPrintf_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void is_proutSyncPrintf_recomp(uint8_t *rdram, recomp_context *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);
|
||||
// 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') {
|
||||
|
|
@ -66,7 +67,7 @@ extern "C" void is_proutSyncPrintf_recomp(uint8_t * rdram, recomp_context * ctx)
|
|||
// }
|
||||
//}
|
||||
|
||||
//fwrite(to_print.get(), size, 1, stdout);
|
||||
// fwrite(to_print.get(), size, 1, stdout);
|
||||
|
||||
ctx->r2 = 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,24 @@
|
|||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <cmath>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "recomp.h"
|
||||
#include "overlays.hpp"
|
||||
#include "game.hpp"
|
||||
#include "xxHash/xxh3.h"
|
||||
#include "ultramodern/ultramodern.hpp"
|
||||
|
||||
#include "ultramodern/error_handling.hpp"
|
||||
#include "ultramodern/ultramodern.hpp"
|
||||
|
||||
#include "game.hpp"
|
||||
#include "overlays.hpp"
|
||||
#include "recomp.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
inline uint32_t byteswap(uint32_t val) {
|
||||
|
|
@ -40,7 +42,7 @@ std::mutex current_game_mutex;
|
|||
|
||||
// Global variables
|
||||
std::filesystem::path config_path;
|
||||
std::unordered_map<std::u8string, recomp::GameEntry> game_roms {};
|
||||
std::unordered_map<std::u8string, recomp::GameEntry> game_roms{};
|
||||
|
||||
std::u8string recomp::GameEntry::stored_filename() const {
|
||||
return game_id + u8".z64";
|
||||
|
|
@ -64,14 +66,14 @@ bool check_hash(const std::vector<uint8_t>& rom_data, uint64_t expected_hash) {
|
|||
static std::vector<uint8_t> read_file(const std::filesystem::path& path) {
|
||||
std::vector<uint8_t> ret;
|
||||
|
||||
std::ifstream file{ path, std::ios::binary};
|
||||
std::ifstream file{ path, std::ios::binary };
|
||||
|
||||
if (file.good()) {
|
||||
file.seekg(0, std::ios::end);
|
||||
ret.resize(file.tellg());
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
file.read(reinterpret_cast<char*>(ret.data()), ret.size());
|
||||
file.read(reinterpret_cast<char *>(ret.data()), ret.size());
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
@ -84,7 +86,7 @@ bool write_file(const std::filesystem::path& path, const std::vector<uint8_t>& d
|
|||
return false;
|
||||
}
|
||||
|
||||
out_file.write(reinterpret_cast<const char*>(data.data()), data.size());
|
||||
out_file.write(reinterpret_cast<const char *>(data.data()), data.size());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -108,7 +110,7 @@ bool recomp::is_rom_valid(std::u8string& game_id) {
|
|||
}
|
||||
|
||||
void recomp::check_all_stored_roms() {
|
||||
for (const auto& cur_rom_entry: game_roms) {
|
||||
for (const auto& cur_rom_entry : game_roms) {
|
||||
if (check_stored_rom(cur_rom_entry.second)) {
|
||||
valid_game_roms.insert(cur_rom_entry.first);
|
||||
}
|
||||
|
|
@ -121,7 +123,7 @@ bool recomp::load_stored_rom(std::u8string& game_id) {
|
|||
if (find_it == game_roms.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
std::vector<uint8_t> stored_rom_data = read_file(config_path / find_it->second.stored_filename());
|
||||
|
||||
if (!check_hash(stored_rom_data, find_it->second.rom_hash)) {
|
||||
|
|
@ -134,7 +136,7 @@ bool recomp::load_stored_rom(std::u8string& game_id) {
|
|||
return true;
|
||||
}
|
||||
|
||||
const std::array<uint8_t, 4> first_rom_bytes { 0x80, 0x37, 0x12, 0x40 };
|
||||
const std::array<uint8_t, 4> first_rom_bytes{ 0x80, 0x37, 0x12, 0x40 };
|
||||
|
||||
enum class ByteswapType {
|
||||
NotByteswapped,
|
||||
|
|
@ -149,25 +151,22 @@ ByteswapType check_rom_start(const std::vector<uint8_t>& rom_data) {
|
|||
}
|
||||
|
||||
auto check_match = [&](uint8_t index0, uint8_t index1, uint8_t index2, uint8_t index3) {
|
||||
return
|
||||
rom_data[0] == first_rom_bytes[index0] &&
|
||||
rom_data[1] == first_rom_bytes[index1] &&
|
||||
rom_data[2] == first_rom_bytes[index2] &&
|
||||
rom_data[3] == first_rom_bytes[index3];
|
||||
return rom_data[0] == first_rom_bytes[index0] && rom_data[1] == first_rom_bytes[index1] && rom_data[2] == first_rom_bytes[index2] &&
|
||||
rom_data[3] == first_rom_bytes[index3];
|
||||
};
|
||||
|
||||
// Check if the ROM is already in the correct byte order.
|
||||
if (check_match(0,1,2,3)) {
|
||||
if (check_match(0, 1, 2, 3)) {
|
||||
return ByteswapType::NotByteswapped;
|
||||
}
|
||||
|
||||
// Check if the ROM has been byteswapped in groups of 4 bytes.
|
||||
if (check_match(3,2,1,0)) {
|
||||
if (check_match(3, 2, 1, 0)) {
|
||||
return ByteswapType::Byteswapped4;
|
||||
}
|
||||
|
||||
// Check if the ROM has been byteswapped in groups of 2 bytes.
|
||||
if (check_match(1,0,3,2)) {
|
||||
if (check_match(1, 0, 3, 2)) {
|
||||
return ByteswapType::Byteswapped2;
|
||||
}
|
||||
|
||||
|
|
@ -223,12 +222,13 @@ recomp::RomValidationError recomp::select_rom(const std::filesystem::path& rom_p
|
|||
}
|
||||
|
||||
if (!check_hash(rom_data, game_entry.rom_hash)) {
|
||||
const std::string_view name{ reinterpret_cast<const char*>(rom_data.data()) + 0x20, game_entry.internal_name.size()};
|
||||
const std::string_view name{ reinterpret_cast<const char *>(rom_data.data()) + 0x20, game_entry.internal_name.size() };
|
||||
if (name == game_entry.internal_name) {
|
||||
return recomp::RomValidationError::IncorrectVersion;
|
||||
}
|
||||
else {
|
||||
if (game_entry.is_enabled && std::string_view{ reinterpret_cast<const char*>(rom_data.data()) + 0x20, 19 } == game_entry.internal_name) {
|
||||
if (game_entry.is_enabled &&
|
||||
std::string_view{ reinterpret_cast<const char *>(rom_data.data()) + 0x20, 19 } == game_entry.internal_name) {
|
||||
return recomp::RomValidationError::NotYet;
|
||||
}
|
||||
else {
|
||||
|
|
@ -238,11 +238,11 @@ recomp::RomValidationError recomp::select_rom(const std::filesystem::path& rom_p
|
|||
}
|
||||
|
||||
write_file(config_path / game_entry.stored_filename(), rom_data);
|
||||
|
||||
|
||||
return recomp::RomValidationError::Good;
|
||||
}
|
||||
|
||||
extern "C" void osGetMemSize_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osGetMemSize_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
ctx->r2 = 8 * 1024 * 1024;
|
||||
}
|
||||
|
||||
|
|
@ -250,7 +250,7 @@ enum class StatusReg {
|
|||
FR = 0x04000000,
|
||||
};
|
||||
|
||||
extern "C" void cop0_status_write(recomp_context* ctx, gpr value) {
|
||||
extern "C" void cop0_status_write(recomp_context *ctx, gpr value) {
|
||||
uint32_t old_sr = ctx->status_reg;
|
||||
uint32_t new_sr = (uint32_t)value;
|
||||
uint32_t changed = old_sr ^ new_sr;
|
||||
|
|
@ -280,16 +280,16 @@ extern "C" void cop0_status_write(recomp_context* ctx, gpr value) {
|
|||
assert(false);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
// Update the status register in the context
|
||||
ctx->status_reg = new_sr;
|
||||
}
|
||||
|
||||
extern "C" gpr cop0_status_read(recomp_context* ctx) {
|
||||
extern "C" gpr cop0_status_read(recomp_context *ctx) {
|
||||
return (gpr)(int32_t)ctx->status_reg;
|
||||
}
|
||||
|
||||
extern "C" void switch_error(const char* func, uint32_t vram, uint32_t jtbl) {
|
||||
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);
|
||||
assert(false);
|
||||
exit(EXIT_FAILURE);
|
||||
|
|
@ -301,17 +301,17 @@ extern "C" void do_break(uint32_t vram) {
|
|||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
void run_thread_function(uint8_t* rdram, uint64_t addr, uint64_t sp, uint64_t arg) {
|
||||
void run_thread_function(uint8_t *rdram, uint64_t addr, uint64_t sp, uint64_t arg) {
|
||||
recomp_context ctx{};
|
||||
ctx.r29 = sp;
|
||||
ctx.r4 = arg;
|
||||
ctx.mips3_float_mode = 0;
|
||||
ctx.f_odd = &ctx.f0.u32h;
|
||||
recomp_func_t* func = get_function(addr);
|
||||
recomp_func_t *func = get_function(addr);
|
||||
func(rdram, &ctx);
|
||||
}
|
||||
|
||||
void init(uint8_t* rdram, recomp_context* ctx, gpr entrypoint) {
|
||||
void init(uint8_t *rdram, recomp_context *ctx, gpr entrypoint) {
|
||||
// Initialize the overlays
|
||||
recomp::overlays::init_overlays();
|
||||
|
||||
|
|
@ -337,9 +337,9 @@ void init(uint8_t* rdram, recomp_context* ctx, gpr entrypoint) {
|
|||
constexpr int32_t osVersion = 0x80000314;
|
||||
constexpr int32_t osMemSize = 0x80000318;
|
||||
constexpr int32_t osAppNMIBuffer = 0x8000031c;
|
||||
MEM_W(osTvType, 0) = 1; // NTSC
|
||||
MEM_W(osRomBase, 0) = 0xB0000000u; // standard rom base
|
||||
MEM_W(osResetType, 0) = 0; // cold reset
|
||||
MEM_W(osTvType, 0) = 1; // NTSC
|
||||
MEM_W(osRomBase, 0) = 0xB0000000u; // standard rom base
|
||||
MEM_W(osResetType, 0) = 0; // cold reset
|
||||
MEM_W(osMemSize, 0) = 8 * 1024 * 1024; // 8MB
|
||||
}
|
||||
|
||||
|
|
@ -374,25 +374,22 @@ void ultramodern::quit() {
|
|||
}
|
||||
|
||||
void recomp::start(
|
||||
ultramodern::renderer::WindowHandle window_handle,
|
||||
const recomp::rsp::callbacks_t& rsp_callbacks,
|
||||
const ultramodern::renderer::callbacks_t& renderer_callbacks,
|
||||
const ultramodern::audio_callbacks_t& audio_callbacks,
|
||||
const ultramodern::input::callbacks_t& input_callbacks,
|
||||
const ultramodern::gfx_callbacks_t& gfx_callbacks_,
|
||||
const ultramodern::events::callbacks_t& events_callbacks,
|
||||
const ultramodern::error_handling::callbacks_t& error_handling_callbacks_
|
||||
) {
|
||||
ultramodern::renderer::WindowHandle window_handle, const recomp::rsp::callbacks_t& rsp_callbacks,
|
||||
const ultramodern::renderer::callbacks_t& renderer_callbacks, const ultramodern::audio_callbacks_t& audio_callbacks,
|
||||
const ultramodern::input::callbacks_t& input_callbacks, const ultramodern::gfx_callbacks_t& gfx_callbacks_,
|
||||
const ultramodern::events::callbacks_t& events_callbacks, const ultramodern::error_handling::callbacks_t& error_handling_callbacks_) {
|
||||
recomp::check_all_stored_roms();
|
||||
|
||||
recomp::rsp::set_callbacks(rsp_callbacks);
|
||||
|
||||
static const ultramodern::rsp::callbacks_t ultramodern_rsp_callbacks {
|
||||
static const ultramodern::rsp::callbacks_t ultramodern_rsp_callbacks{
|
||||
.init = recomp::rsp::constants_init,
|
||||
.run_task = recomp::rsp::run_task,
|
||||
};
|
||||
|
||||
ultramodern::set_callbacks(ultramodern_rsp_callbacks, renderer_callbacks, audio_callbacks, input_callbacks, gfx_callbacks_, events_callbacks, error_handling_callbacks_);
|
||||
ultramodern::set_callbacks(
|
||||
ultramodern_rsp_callbacks, renderer_callbacks, audio_callbacks, input_callbacks, gfx_callbacks_, events_callbacks,
|
||||
error_handling_callbacks_);
|
||||
|
||||
ultramodern::gfx_callbacks_t gfx_callbacks = gfx_callbacks_;
|
||||
|
||||
|
|
@ -415,20 +412,20 @@ void recomp::start(
|
|||
std::unique_ptr<uint8_t[]> rdram_buffer = std::make_unique<uint8_t[]>(ultramodern::rdram_size);
|
||||
std::memset(rdram_buffer.get(), 0, ultramodern::rdram_size);
|
||||
|
||||
std::thread game_thread{[](ultramodern::renderer::WindowHandle window_handle, uint8_t* rdram) {
|
||||
debug_printf("[Recomp] Starting\n");
|
||||
std::thread game_thread{
|
||||
[](ultramodern::renderer::WindowHandle window_handle, uint8_t *rdram) {
|
||||
debug_printf("[Recomp] Starting\n");
|
||||
|
||||
ultramodern::set_native_thread_name("Game Start Thread");
|
||||
ultramodern::set_native_thread_name("Game Start Thread");
|
||||
|
||||
ultramodern::preinit(rdram, window_handle);
|
||||
ultramodern::preinit(rdram, window_handle);
|
||||
|
||||
game_status.wait(GameStatus::None);
|
||||
recomp_context context{};
|
||||
game_status.wait(GameStatus::None);
|
||||
recomp_context context{};
|
||||
|
||||
switch (game_status.load()) {
|
||||
// TODO refactor this to allow a project to specify what entrypoint function to run for a give game.
|
||||
case GameStatus::Running:
|
||||
{
|
||||
switch (game_status.load()) {
|
||||
// TODO refactor this to allow a project to specify what entrypoint function to run for a give game.
|
||||
case GameStatus::Running: {
|
||||
if (!recomp::load_stored_rom(current_game.value())) {
|
||||
ultramodern::error_handling::message_box("Error opening stored ROM! Please restart this program.");
|
||||
}
|
||||
|
|
@ -443,20 +440,21 @@ void recomp::start(
|
|||
try {
|
||||
game_entry.entrypoint(rdram, &context);
|
||||
} catch (ultramodern::thread_terminated& terminated) {
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
} break;
|
||||
|
||||
case GameStatus::Quit:
|
||||
break;
|
||||
case GameStatus::Quit:
|
||||
break;
|
||||
|
||||
case GameStatus::None:
|
||||
break;
|
||||
}
|
||||
case GameStatus::None:
|
||||
break;
|
||||
}
|
||||
|
||||
debug_printf("[Recomp] Quitting\n");
|
||||
}, window_handle, rdram_buffer.get()};
|
||||
debug_printf("[Recomp] Quitting\n");
|
||||
},
|
||||
window_handle,
|
||||
rdram_buffer.get(),
|
||||
};
|
||||
|
||||
while (!exited) {
|
||||
ultramodern::sleep_milliseconds(1);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
|
||||
#include "rsp.hpp"
|
||||
|
||||
static recomp::rsp::callbacks_t rsp_callbacks {};
|
||||
static recomp::rsp::callbacks_t rsp_callbacks{};
|
||||
|
||||
void recomp::rsp::set_callbacks(const callbacks_t& callbacks) {
|
||||
rsp_callbacks = callbacks;
|
||||
|
|
@ -26,16 +26,17 @@ void recomp::rsp::constants_init() {
|
|||
for (u16 index = 0; index < 512; index++) {
|
||||
u64 a = (index + 512) >> ((index % 2 == 1) ? 1 : 0);
|
||||
u64 b = 1 << 17;
|
||||
//find the largest b where b < 1.0 / sqrt(a)
|
||||
while (a * (b + 1) * (b + 1) < (u64(1) << 44)) b++;
|
||||
// find the largest b where b < 1.0 / sqrt(a)
|
||||
while (a * (b + 1) * (b + 1) < (u64(1) << 44))
|
||||
b++;
|
||||
rspInverseSquareRoots[index] = u16(b >> 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Runs a recompiled RSP microcode
|
||||
bool recomp::rsp::run_task(uint8_t* rdram, const OSTask* task) {
|
||||
bool recomp::rsp::run_task(uint8_t *rdram, const OSTask *task) {
|
||||
assert(rsp_callbacks.get_rsp_microcode != nullptr);
|
||||
RspUcodeFunc* ucode_func = rsp_callbacks.get_rsp_microcode(task);
|
||||
RspUcodeFunc *ucode_func = rsp_callbacks.get_rsp_microcode(task);
|
||||
|
||||
if (ucode_func == nullptr) {
|
||||
fprintf(stderr, "No registered RSP ucode for %" PRIu32 " (returned `nullptr`)\n", task->t.type);
|
||||
|
|
|
|||
|
|
@ -1,21 +1,24 @@
|
|||
#include <cstdio>
|
||||
#include <fstream>
|
||||
|
||||
#include <ultramodern/ultramodern.hpp>
|
||||
|
||||
#include "recomp.h"
|
||||
|
||||
extern "C" void osSpTaskLoad_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osSpTaskLoad_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
// Nothing to do here
|
||||
}
|
||||
|
||||
bool dump_frame = false;
|
||||
|
||||
extern "C" void osSpTaskStartGo_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
//printf("[sp] osSpTaskStartGo(0x%08X)\n", (uint32_t)ctx->r4);
|
||||
OSTask* task = TO_PTR(OSTask, ctx->r4);
|
||||
extern "C" void osSpTaskStartGo_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
// printf("[sp] osSpTaskStartGo(0x%08X)\n", (uint32_t)ctx->r4);
|
||||
OSTask *task = TO_PTR(OSTask, ctx->r4);
|
||||
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] 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);
|
||||
}
|
||||
// For debugging
|
||||
if (dump_frame) {
|
||||
|
|
@ -24,7 +27,7 @@ extern "C" void osSpTaskStartGo_recomp(uint8_t* rdram, recomp_context* ctx) {
|
|||
std::unique_ptr<char[]> ram_unswapped = std::make_unique<char[]>(ram_size);
|
||||
snprintf(addr_str, sizeof(addr_str) - 1, "%08X", task->t.data_ptr);
|
||||
addr_str[sizeof(addr_str) - 1] = '\0';
|
||||
std::ofstream dump_file{ "ramdump" + std::string{ addr_str } + ".bin", std::ios::binary};
|
||||
std::ofstream dump_file{ "ramdump" + std::string{ addr_str } + ".bin", std::ios::binary };
|
||||
|
||||
for (size_t i = 0; i < ram_size; i++) {
|
||||
ram_unswapped[i] = rdram[i ^ 3];
|
||||
|
|
@ -36,15 +39,15 @@ extern "C" void osSpTaskStartGo_recomp(uint8_t* rdram, recomp_context* ctx) {
|
|||
ultramodern::submit_rsp_task(rdram, ctx->r4);
|
||||
}
|
||||
|
||||
extern "C" void osSpTaskYield_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osSpTaskYield_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
// Ignore yield requests (acts as if the task completed before it received the yield request)
|
||||
}
|
||||
|
||||
extern "C" void osSpTaskYielded_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osSpTaskYielded_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
// Task yield requests are ignored, so always return 0 as tasks will never be yielded
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void __osSpSetPc_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void __osSpSetPc_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
assert(false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,45 +1,45 @@
|
|||
#include <ultramodern/ultra64.h>
|
||||
#include <ultramodern/ultramodern.hpp>
|
||||
|
||||
#include "recomp.h"
|
||||
|
||||
// None of these functions need to be reimplemented, so stub them out
|
||||
extern "C" void osUnmapTLBAll_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osUnmapTLBAll_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
// TODO this will need to be implemented in the future for any games that actually use the TLB
|
||||
}
|
||||
|
||||
extern "C" void osVoiceInit_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osVoiceInit_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
ctx->r2 = 11; // CONT_ERR_DEVICE
|
||||
}
|
||||
|
||||
extern "C" void osVoiceSetWord_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osVoiceSetWord_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
extern "C" void osVoiceCheckWord_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osVoiceCheckWord_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
extern "C" void osVoiceStopReadData_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osVoiceStopReadData_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
extern "C" void osVoiceMaskDictionary_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osVoiceMaskDictionary_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
extern "C" void osVoiceStartReadData_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osVoiceStartReadData_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
extern "C" void osVoiceControlGain_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osVoiceControlGain_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
extern "C" void osVoiceGetReadData_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osVoiceGetReadData_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
extern "C" void osVoiceClearDictionary_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osVoiceClearDictionary_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,132 +1,135 @@
|
|||
#include <memory>
|
||||
|
||||
#include <ultramodern/ultra64.h>
|
||||
#include <ultramodern/ultramodern.hpp>
|
||||
|
||||
#include "recomp.h"
|
||||
|
||||
extern "C" void osInitialize_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osInitialize_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
osInitialize();
|
||||
}
|
||||
|
||||
extern "C" void __osInitialize_common_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void __osInitialize_common_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
osInitialize();
|
||||
}
|
||||
|
||||
extern "C" void osCreateThread_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
osCreateThread(rdram, (int32_t)ctx->r4, (OSId)ctx->r5, (int32_t)ctx->r6, (int32_t)ctx->r7,
|
||||
(int32_t)MEM_W(0x10, ctx->r29), (OSPri)MEM_W(0x14, ctx->r29));
|
||||
extern "C" void osCreateThread_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
osCreateThread(
|
||||
rdram, (int32_t)ctx->r4, (OSId)ctx->r5, (int32_t)ctx->r6, (int32_t)ctx->r7, (int32_t)MEM_W(0x10, ctx->r29),
|
||||
(OSPri)MEM_W(0x14, ctx->r29));
|
||||
}
|
||||
|
||||
extern "C" void osStartThread_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osStartThread_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
osStartThread(rdram, (int32_t)ctx->r4);
|
||||
}
|
||||
|
||||
extern "C" void osStopThread_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osStopThread_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
osStopThread(rdram, (int32_t)ctx->r4);
|
||||
}
|
||||
|
||||
extern "C" void osDestroyThread_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osDestroyThread_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
osDestroyThread(rdram, (int32_t)ctx->r4);
|
||||
}
|
||||
|
||||
extern "C" void osYieldThread_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osYieldThread_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
assert(false);
|
||||
// osYieldThread(rdram);
|
||||
}
|
||||
|
||||
extern "C" void osSetThreadPri_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osSetThreadPri_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
osSetThreadPri(rdram, (int32_t)ctx->r4, (OSPri)ctx->r5);
|
||||
}
|
||||
|
||||
extern "C" void osGetThreadPri_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osGetThreadPri_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
ctx->r2 = osGetThreadPri(rdram, (int32_t)ctx->r4);
|
||||
}
|
||||
|
||||
extern "C" void osGetThreadId_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osGetThreadId_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
ctx->r2 = osGetThreadId(rdram, (int32_t)ctx->r4);
|
||||
}
|
||||
|
||||
extern "C" void osCreateMesgQueue_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osCreateMesgQueue_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
osCreateMesgQueue(rdram, (int32_t)ctx->r4, (int32_t)ctx->r5, (s32)ctx->r6);
|
||||
}
|
||||
|
||||
extern "C" void osRecvMesg_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osRecvMesg_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
ctx->r2 = osRecvMesg(rdram, (int32_t)ctx->r4, (int32_t)ctx->r5, (s32)ctx->r6);
|
||||
}
|
||||
|
||||
extern "C" void osSendMesg_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osSendMesg_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
ctx->r2 = osSendMesg(rdram, (int32_t)ctx->r4, (OSMesg)ctx->r5, (s32)ctx->r6);
|
||||
}
|
||||
|
||||
extern "C" void osJamMesg_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osJamMesg_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
ctx->r2 = osJamMesg(rdram, (int32_t)ctx->r4, (OSMesg)ctx->r5, (s32)ctx->r6);
|
||||
}
|
||||
|
||||
extern "C" void osSetEventMesg_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osSetEventMesg_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
osSetEventMesg(rdram, (OSEvent)ctx->r4, (int32_t)ctx->r5, (OSMesg)ctx->r6);
|
||||
}
|
||||
|
||||
extern "C" void osViSetEvent_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osViSetEvent_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
osViSetEvent(rdram, (int32_t)ctx->r4, (OSMesg)ctx->r5, (u32)ctx->r6);
|
||||
}
|
||||
|
||||
extern "C" void osGetCount_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osGetCount_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
ctx->r2 = osGetCount();
|
||||
}
|
||||
|
||||
extern "C" void osGetTime_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osGetTime_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
uint64_t total_count = osGetTime();
|
||||
ctx->r2 = (int32_t)(total_count >> 32);
|
||||
ctx->r3 = (int32_t)(total_count >> 0);
|
||||
}
|
||||
|
||||
extern "C" void osSetTimer_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osSetTimer_recomp(uint8_t *rdram, recomp_context *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 * rdram, recomp_context * ctx) {
|
||||
extern "C" void osStopTimer_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
ctx->r2 = osStopTimer(rdram, (int32_t)ctx->r4);
|
||||
}
|
||||
|
||||
extern "C" void osVirtualToPhysical_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osVirtualToPhysical_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
ctx->r2 = osVirtualToPhysical((int32_t)ctx->r4);
|
||||
}
|
||||
|
||||
extern "C" void osInvalDCache_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osInvalDCache_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
;
|
||||
}
|
||||
|
||||
extern "C" void osInvalICache_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osInvalICache_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
;
|
||||
}
|
||||
|
||||
extern "C" void osWritebackDCache_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osWritebackDCache_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
;
|
||||
}
|
||||
|
||||
extern "C" void osWritebackDCacheAll_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osWritebackDCacheAll_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
;
|
||||
}
|
||||
|
||||
extern "C" void osSetIntMask_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void osSetIntMask_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
;
|
||||
}
|
||||
|
||||
extern "C" void __osDisableInt_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void __osDisableInt_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
;
|
||||
}
|
||||
|
||||
extern "C" void __osRestoreInt_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void __osRestoreInt_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
;
|
||||
}
|
||||
|
||||
extern "C" void __osSetFpcCsr_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
extern "C" void __osSetFpcCsr_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
// For the Mario Party games (not working)
|
||||
//extern "C" void longjmp_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
// extern "C" void longjmp_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
// RecompJmpBuf* buf = TO_PTR(RecompJmpBuf, ctx->r4);
|
||||
//
|
||||
// // Check if this is a buffer that was set up with setjmp
|
||||
|
|
@ -153,11 +156,11 @@ extern "C" void __osSetFpcCsr_recomp(uint8_t * rdram, recomp_context * ctx) {
|
|||
//}
|
||||
//
|
||||
//#undef setjmp_recomp
|
||||
//extern "C" void setjmp_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
// extern "C" void setjmp_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
// fprintf(stderr, "Program called setjmp_recomp\n");
|
||||
// std::quick_exit(EXIT_FAILURE);
|
||||
//}
|
||||
//
|
||||
//extern "C" int32_t osGetThreadEx(void) {
|
||||
// extern "C" int32_t osGetThreadEx(void) {
|
||||
// return ultramodern::this_thread();
|
||||
//}
|
||||
|
|
|
|||
|
|
@ -1,45 +1,46 @@
|
|||
#include <ultramodern/ultramodern.hpp>
|
||||
|
||||
#include "recomp.h"
|
||||
|
||||
extern "C" void osViSetYScale_recomp(uint8_t* rdram, recomp_context * ctx) {
|
||||
extern "C" void osViSetYScale_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
osViSetYScale(ctx->f12.fl);
|
||||
}
|
||||
|
||||
extern "C" void osViSetXScale_recomp(uint8_t* rdram, recomp_context * ctx) {
|
||||
extern "C" void osViSetXScale_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
osViSetXScale(ctx->f12.fl);
|
||||
}
|
||||
|
||||
extern "C" void osCreateViManager_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osCreateViManager_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
;
|
||||
}
|
||||
|
||||
extern "C" void osViBlack_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osViBlack_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
osViBlack((uint32_t)ctx->r4);
|
||||
}
|
||||
|
||||
extern "C" void osViSetSpecialFeatures_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osViSetSpecialFeatures_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
osViSetSpecialFeatures((uint32_t)ctx->r4);
|
||||
}
|
||||
|
||||
extern "C" void osViGetCurrentFramebuffer_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osViGetCurrentFramebuffer_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
ctx->r2 = (gpr)(int32_t)osViGetCurrentFramebuffer();
|
||||
}
|
||||
|
||||
extern "C" void osViGetNextFramebuffer_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osViGetNextFramebuffer_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
ctx->r2 = (gpr)(int32_t)osViGetNextFramebuffer();
|
||||
}
|
||||
|
||||
extern "C" void osViSwapBuffer_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osViSwapBuffer_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
osViSwapBuffer(rdram, (int32_t)ctx->r4);
|
||||
}
|
||||
|
||||
extern "C" void osViSetMode_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osViSetMode_recomp(uint8_t *rdram, recomp_context *ctx) {
|
||||
osViSetMode(rdram, (int32_t)ctx->r4);
|
||||
}
|
||||
|
||||
extern uint64_t total_vis;
|
||||
|
||||
extern "C" void wait_one_frame(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void wait_one_frame(uint8_t *rdram, recomp_context *ctx) {
|
||||
uint64_t cur_vis = total_vis;
|
||||
while (cur_vis == total_vis) {
|
||||
std::this_thread::yield();
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
#ifndef __CONFIG_HPP__
|
||||
#define __CONFIG_HPP__
|
||||
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "json/json.hpp"
|
||||
|
||||
|
|
@ -81,6 +81,7 @@ namespace ultramodern {
|
|||
const GraphicsConfig& get_graphics_config();
|
||||
void set_graphics_config(const GraphicsConfig& new_config);
|
||||
|
||||
// clang-format off
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(ultramodern::renderer::Resolution, {
|
||||
{ultramodern::renderer::Resolution::Original, "Original"},
|
||||
{ultramodern::renderer::Resolution::Original2x, "Original2x"},
|
||||
|
|
@ -128,7 +129,8 @@ namespace ultramodern {
|
|||
{ultramodern::renderer::HighPrecisionFramebuffer::On, "On"},
|
||||
{ultramodern::renderer::HighPrecisionFramebuffer::Off, "Off"},
|
||||
});
|
||||
}
|
||||
}
|
||||
// clang-format on
|
||||
} // namespace renderer
|
||||
} // namespace ultramodern
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
namespace ultramodern {
|
||||
namespace error_handling {
|
||||
struct callbacks_t {
|
||||
using message_box_t = void(const char* msg);
|
||||
using message_box_t = void(const char *msg);
|
||||
|
||||
/**
|
||||
* Show an OS dialog with the given `msg`.
|
||||
|
|
@ -20,10 +20,10 @@ namespace ultramodern {
|
|||
|
||||
void set_callbacks(const callbacks_t& callbacks);
|
||||
|
||||
void message_box(const char* msg);
|
||||
void message_box(const char *msg);
|
||||
|
||||
[[noreturn]] void quick_exit(const char* filename, int line, const char *func, int exit_status = EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
[[noreturn]] void quick_exit(const char *filename, int line, const char *func, int exit_status = EXIT_FAILURE);
|
||||
} // namespace error_handling
|
||||
} // namespace ultramodern
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -10,16 +10,16 @@ namespace ultramodern {
|
|||
/**
|
||||
* Called in each VI.
|
||||
*/
|
||||
vi_callback_t* vi_callback;
|
||||
vi_callback_t *vi_callback;
|
||||
|
||||
/**
|
||||
* Called before entering the gfx main loop.
|
||||
*/
|
||||
gfx_init_callback_t* gfx_init_callback;
|
||||
gfx_init_callback_t *gfx_init_callback;
|
||||
};
|
||||
|
||||
void set_callbacks(const callbacks_t& callbacks);
|
||||
}
|
||||
}
|
||||
} // namespace events
|
||||
} // namespace ultramodern
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -26,11 +26,11 @@ namespace ultramodern {
|
|||
|
||||
struct callbacks_t {
|
||||
using poll_input_t = void(void);
|
||||
using get_input_t = bool(int controller_num, uint16_t* buttons, float* x, float* y);
|
||||
using get_input_t = bool(int controller_num, uint16_t *buttons, float *x, float *y);
|
||||
using set_rumble_t = void(int controller_num, bool rumble);
|
||||
using get_connected_device_info_t = connected_device_info_t(int controller_num);
|
||||
|
||||
poll_input_t* poll_input;
|
||||
poll_input_t *poll_input;
|
||||
|
||||
/**
|
||||
* Requests the state of the pressed buttons and the analog stick for the given `controller_num`.
|
||||
|
|
@ -39,25 +39,25 @@ namespace ultramodern {
|
|||
*
|
||||
* Returns `true` if was able to fetch the specified data, `false` otherwise and the parameter arguments are left untouched.
|
||||
*/
|
||||
get_input_t* get_input;
|
||||
get_input_t *get_input;
|
||||
|
||||
/**
|
||||
* Turns on or off rumbling for the specified controller.
|
||||
*
|
||||
* `controller_num` is zero-indexed, meaning 0 corresponds to the first controller.
|
||||
*/
|
||||
set_rumble_t* set_rumble;
|
||||
set_rumble_t *set_rumble;
|
||||
|
||||
/**
|
||||
* Returns the connected device info for the given `controller_num` (as in, the controller port of the console).
|
||||
*
|
||||
* `controller_num` is zero-indexed, meaning 0 corresponds to the first controller.
|
||||
*/
|
||||
get_connected_device_info_t* get_connected_device_info;
|
||||
get_connected_device_info_t *get_connected_device_info;
|
||||
};
|
||||
|
||||
void set_callbacks(const callbacks_t& callbacks);
|
||||
}
|
||||
}
|
||||
} // namespace input
|
||||
} // namespace ultramodern
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -7,21 +7,21 @@
|
|||
#include <span>
|
||||
|
||||
#if defined(_WIN32)
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# include <Windows.h>
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# include <Windows.h>
|
||||
#elif defined(__ANDROID__)
|
||||
# include "android/native_window.h"
|
||||
# include "android/native_window.h"
|
||||
#elif defined(__linux__)
|
||||
# include "X11/Xlib.h"
|
||||
# undef None
|
||||
# undef Status
|
||||
# undef LockMask
|
||||
# undef Always
|
||||
# undef Success
|
||||
# include "X11/Xlib.h"
|
||||
# undef None
|
||||
# undef Status
|
||||
# undef LockMask
|
||||
# undef Always
|
||||
# undef Success
|
||||
#endif
|
||||
|
||||
#include "ultra64.h"
|
||||
#include "config.hpp"
|
||||
#include "ultra64.h"
|
||||
|
||||
namespace ultramodern {
|
||||
namespace renderer {
|
||||
|
|
@ -34,17 +34,17 @@ namespace ultramodern {
|
|||
auto operator<=>(const WindowHandle&) const = default;
|
||||
};
|
||||
#elif defined(__ANDROID__)
|
||||
using WindowHandle = ANativeWindow*;
|
||||
using WindowHandle = ANativeWindow *;
|
||||
#elif defined(__linux__)
|
||||
struct WindowHandle {
|
||||
Display* display;
|
||||
Display *display;
|
||||
Window window;
|
||||
auto operator<=>(const WindowHandle&) const = default;
|
||||
};
|
||||
#elif defined(__APPLE__)
|
||||
struct WindowHandle {
|
||||
void* window;
|
||||
void* view;
|
||||
void *window;
|
||||
void *view;
|
||||
auto operator<=>(const WindowHandle&) const = default;
|
||||
};
|
||||
#endif
|
||||
|
|
@ -58,28 +58,31 @@ namespace ultramodern {
|
|||
};
|
||||
|
||||
class RendererContext {
|
||||
public:
|
||||
virtual ~RendererContext() = default;
|
||||
public:
|
||||
virtual ~RendererContext() = default;
|
||||
|
||||
virtual bool valid() = 0;
|
||||
virtual SetupResult get_setup_result() const { return setup_result; }
|
||||
virtual bool valid() = 0;
|
||||
virtual SetupResult get_setup_result() const {
|
||||
return setup_result;
|
||||
}
|
||||
|
||||
virtual bool update_config(const GraphicsConfig& old_config, const GraphicsConfig& new_config) = 0;
|
||||
virtual bool update_config(const GraphicsConfig& old_config, const GraphicsConfig& new_config) = 0;
|
||||
|
||||
virtual void enable_instant_present() = 0;
|
||||
virtual void send_dl(const OSTask* task) = 0;
|
||||
virtual void update_screen(uint32_t vi_origin) = 0;
|
||||
virtual void shutdown() = 0;
|
||||
virtual uint32_t get_display_framerate() const = 0;
|
||||
virtual float get_resolution_scale() const = 0;
|
||||
virtual void load_shader_cache(std::span<const char> cache_binary) = 0;
|
||||
virtual void enable_instant_present() = 0;
|
||||
virtual void send_dl(const OSTask *task) = 0;
|
||||
virtual void update_screen(uint32_t vi_origin) = 0;
|
||||
virtual void shutdown() = 0;
|
||||
virtual uint32_t get_display_framerate() const = 0;
|
||||
virtual float get_resolution_scale() const = 0;
|
||||
virtual void load_shader_cache(std::span<const char> cache_binary) = 0;
|
||||
|
||||
protected:
|
||||
SetupResult setup_result;
|
||||
protected:
|
||||
SetupResult setup_result;
|
||||
};
|
||||
|
||||
struct callbacks_t {
|
||||
using create_render_context_t = std::unique_ptr<RendererContext>(uint8_t* rdram, WindowHandle window_handle, bool developer_mode);
|
||||
using create_render_context_t =
|
||||
std::unique_ptr<RendererContext>(uint8_t *rdram, WindowHandle window_handle, bool developer_mode);
|
||||
using get_graphics_api_name_t = std::string(const GraphicsConfig& config);
|
||||
|
||||
/**
|
||||
|
|
@ -97,10 +100,10 @@ namespace ultramodern {
|
|||
|
||||
void set_callbacks(const callbacks_t& callbacks);
|
||||
|
||||
std::unique_ptr<RendererContext> create_render_context(uint8_t* rdram, WindowHandle window_handle, bool developer_mode);
|
||||
std::unique_ptr<RendererContext> create_render_context(uint8_t *rdram, WindowHandle window_handle, bool developer_mode);
|
||||
|
||||
std::string get_graphics_api_name(const GraphicsConfig& config);
|
||||
}
|
||||
}
|
||||
} // namespace renderer
|
||||
} // namespace ultramodern
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -13,23 +13,23 @@ namespace ultramodern {
|
|||
namespace rsp {
|
||||
struct callbacks_t {
|
||||
using init_t = void();
|
||||
using run_microcode_t = bool(RDRAM_ARG const OSTask* task);
|
||||
using run_microcode_t = bool(RDRAM_ARG const OSTask *task);
|
||||
|
||||
init_t* init;
|
||||
init_t *init;
|
||||
|
||||
/**
|
||||
* Executes the given RSP task.
|
||||
*
|
||||
* Returns true if task was executed successfully.
|
||||
*/
|
||||
run_microcode_t* run_task;
|
||||
run_microcode_t *run_task;
|
||||
};
|
||||
|
||||
void set_callbacks(const callbacks_t& callbacks);
|
||||
|
||||
void init();
|
||||
bool run_task(RDRAM_ARG const OSTask* task);
|
||||
};
|
||||
bool run_task(RDRAM_ARG const OSTask *task);
|
||||
}; // namespace rsp
|
||||
} // namespace ultramodern
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define UNUSED __attribute__((unused))
|
||||
#define ALIGNED(x) __attribute__((aligned(x)))
|
||||
# define UNUSED __attribute__((unused))
|
||||
# define ALIGNED(x) __attribute__((aligned(x)))
|
||||
#else
|
||||
#define UNUSED
|
||||
#define ALIGNED(x)
|
||||
# define UNUSED
|
||||
# define ALIGNED(x)
|
||||
#endif
|
||||
|
||||
typedef int64_t s64;
|
||||
|
|
@ -23,60 +23,60 @@ typedef uint8_t u8;
|
|||
// TODO allow a compile-time flag to be set to switch between recomp mode and
|
||||
// fully native mode.
|
||||
#if 0 // For native compilation
|
||||
# define PTR(x) x*
|
||||
# define RDRAM_ARG
|
||||
# define RDRAM_ARG1
|
||||
# define PASS_RDRAM
|
||||
# define PASS_RDRAM1
|
||||
# define TO_PTR(type, var) var
|
||||
# define GET_MEMBER(type, addr, member) (&addr->member)
|
||||
# ifdef __cplusplus
|
||||
# define NULLPTR nullptr
|
||||
# endif
|
||||
# define PTR(x) x *
|
||||
# define RDRAM_ARG
|
||||
# define RDRAM_ARG1
|
||||
# define PASS_RDRAM
|
||||
# define PASS_RDRAM1
|
||||
# define TO_PTR(type, var) var
|
||||
# define GET_MEMBER(type, addr, member) (&addr->member)
|
||||
# ifdef __cplusplus
|
||||
# define NULLPTR nullptr
|
||||
# endif
|
||||
#else
|
||||
# define PTR(x) int32_t
|
||||
# define RDRAM_ARG uint8_t *rdram,
|
||||
# define RDRAM_ARG1 uint8_t *rdram
|
||||
# define PASS_RDRAM rdram,
|
||||
# define PASS_RDRAM1 rdram
|
||||
# define TO_PTR(type, var) ((type*)(&rdram[(uint64_t)var - 0xFFFFFFFF80000000]))
|
||||
# define GET_MEMBER(type, addr, member) (addr + (intptr_t)&(((type*)nullptr)->member))
|
||||
# ifdef __cplusplus
|
||||
# define NULLPTR (PTR(void))0
|
||||
# endif
|
||||
# define PTR(x) int32_t
|
||||
# define RDRAM_ARG uint8_t *rdram,
|
||||
# define RDRAM_ARG1 uint8_t *rdram
|
||||
# define PASS_RDRAM rdram,
|
||||
# define PASS_RDRAM1 rdram
|
||||
# define TO_PTR(type, var) ((type *)(&rdram[(uint64_t)var - 0xFFFFFFFF80000000]))
|
||||
# define GET_MEMBER(type, addr, member) (addr + (intptr_t) & (((type *)nullptr)->member))
|
||||
# ifdef __cplusplus
|
||||
# define NULLPTR (PTR(void))0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL (PTR(void) 0)
|
||||
# define NULL (PTR(void) 0)
|
||||
#endif
|
||||
|
||||
#define OS_MESG_NOBLOCK 0
|
||||
#define OS_MESG_BLOCK 1
|
||||
#define OS_MESG_NOBLOCK 0
|
||||
#define OS_MESG_BLOCK 1
|
||||
|
||||
typedef s32 OSPri;
|
||||
typedef s32 OSId;
|
||||
|
||||
typedef u64 OSTime;
|
||||
typedef u64 OSTime;
|
||||
|
||||
#define OS_EVENT_SW1 0 /* CPU SW1 interrupt */
|
||||
#define OS_EVENT_SW2 1 /* CPU SW2 interrupt */
|
||||
#define OS_EVENT_CART 2 /* Cartridge interrupt: used by rmon */
|
||||
#define OS_EVENT_COUNTER 3 /* Counter int: used by VI/Timer Mgr */
|
||||
#define OS_EVENT_SP 4 /* SP task done interrupt */
|
||||
#define OS_EVENT_SI 5 /* SI (controller) interrupt */
|
||||
#define OS_EVENT_AI 6 /* AI interrupt */
|
||||
#define OS_EVENT_VI 7 /* VI interrupt: used by VI/Timer Mgr */
|
||||
#define OS_EVENT_PI 8 /* PI interrupt: used by PI Manager */
|
||||
#define OS_EVENT_DP 9 /* DP full sync interrupt */
|
||||
#define OS_EVENT_CPU_BREAK 10 /* CPU breakpoint: used by rmon */
|
||||
#define OS_EVENT_SP_BREAK 11 /* SP breakpoint: used by rmon */
|
||||
#define OS_EVENT_FAULT 12 /* CPU fault event: used by rmon */
|
||||
#define OS_EVENT_THREADSTATUS 13 /* CPU thread status: used by rmon */
|
||||
#define OS_EVENT_PRENMI 14 /* Pre NMI interrupt */
|
||||
#define OS_EVENT_SW1 0 /* CPU SW1 interrupt */
|
||||
#define OS_EVENT_SW2 1 /* CPU SW2 interrupt */
|
||||
#define OS_EVENT_CART 2 /* Cartridge interrupt: used by rmon */
|
||||
#define OS_EVENT_COUNTER 3 /* Counter int: used by VI/Timer Mgr */
|
||||
#define OS_EVENT_SP 4 /* SP task done interrupt */
|
||||
#define OS_EVENT_SI 5 /* SI (controller) interrupt */
|
||||
#define OS_EVENT_AI 6 /* AI interrupt */
|
||||
#define OS_EVENT_VI 7 /* VI interrupt: used by VI/Timer Mgr */
|
||||
#define OS_EVENT_PI 8 /* PI interrupt: used by PI Manager */
|
||||
#define OS_EVENT_DP 9 /* DP full sync interrupt */
|
||||
#define OS_EVENT_CPU_BREAK 10 /* CPU breakpoint: used by rmon */
|
||||
#define OS_EVENT_SP_BREAK 11 /* SP breakpoint: used by rmon */
|
||||
#define OS_EVENT_FAULT 12 /* CPU fault event: used by rmon */
|
||||
#define OS_EVENT_THREADSTATUS 13 /* CPU thread status: used by rmon */
|
||||
#define OS_EVENT_PRENMI 14 /* Pre NMI interrupt */
|
||||
|
||||
#define M_GFXTASK 1
|
||||
#define M_AUDTASK 2
|
||||
#define M_VIDTASK 3
|
||||
#define M_GFXTASK 1
|
||||
#define M_AUDTASK 2
|
||||
#define M_VIDTASK 3
|
||||
#define M_NJPEGTASK 4
|
||||
|
||||
/////////////
|
||||
|
|
@ -103,7 +103,7 @@ typedef struct OSThread_t {
|
|||
uint16_t state;
|
||||
OSId id;
|
||||
int32_t pad3;
|
||||
UltraThreadContext* context; // An actual pointer regardless of platform
|
||||
UltraThreadContext *context; // An actual pointer regardless of platform
|
||||
int32_t sp;
|
||||
} OSThread;
|
||||
|
||||
|
|
@ -112,7 +112,7 @@ typedef PTR(void) OSMesg;
|
|||
|
||||
typedef struct OSMesgQueue {
|
||||
PTR(OSThread) blocked_on_recv; /* Linked list of threads blocked on receiving from this queue */
|
||||
PTR(OSThread) blocked_on_send; /* Linked list of threads blocked on sending to this queue */
|
||||
PTR(OSThread) blocked_on_send; /* Linked list of threads blocked on sending to this queue */
|
||||
s32 validCount; /* Number of messages in the queue */
|
||||
s32 first; /* Index of the first message in the ring buffer */
|
||||
s32 msgCount; /* Size of message buffer */
|
||||
|
|
@ -122,29 +122,29 @@ typedef struct OSMesgQueue {
|
|||
// RSP
|
||||
|
||||
typedef struct {
|
||||
u32 type;
|
||||
u32 flags;
|
||||
u32 type;
|
||||
u32 flags;
|
||||
|
||||
PTR(u64) ucode_boot;
|
||||
u32 ucode_boot_size;
|
||||
u32 ucode_boot_size;
|
||||
|
||||
PTR(u64) ucode;
|
||||
u32 ucode_size;
|
||||
u32 ucode_size;
|
||||
|
||||
PTR(u64) ucode_data;
|
||||
u32 ucode_data_size;
|
||||
u32 ucode_data_size;
|
||||
|
||||
PTR(u64) dram_stack;
|
||||
u32 dram_stack_size;
|
||||
u32 dram_stack_size;
|
||||
|
||||
PTR(u64) output_buff;
|
||||
PTR(u64) output_buff_size;
|
||||
|
||||
PTR(u64) data_ptr;
|
||||
u32 data_size;
|
||||
u32 data_size;
|
||||
|
||||
PTR(u64) yield_data_ptr;
|
||||
u32 yield_data_size;
|
||||
u32 yield_data_size;
|
||||
} OSTask_s;
|
||||
|
||||
typedef union {
|
||||
|
|
@ -163,55 +163,55 @@ struct OSIoMesgHdr {
|
|||
};
|
||||
|
||||
struct OSIoMesg {
|
||||
OSIoMesgHdr hdr; /* Message header */
|
||||
PTR(void) dramAddr; /* RDRAM buffer address (DMA) */
|
||||
u32 devAddr; /* Device buffer address (DMA) */
|
||||
u32 size; /* DMA transfer size in bytes */
|
||||
u32 piHandle; /* PI device handle */
|
||||
OSIoMesgHdr hdr; /* Message header */
|
||||
PTR(void) dramAddr; /* RDRAM buffer address (DMA) */
|
||||
u32 devAddr; /* Device buffer address (DMA) */
|
||||
u32 size; /* DMA transfer size in bytes */
|
||||
u32 piHandle; /* PI device handle */
|
||||
};
|
||||
|
||||
struct OSPiHandle {
|
||||
PTR(OSPiHandle_s) unused; /* point to next handle on the table */
|
||||
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 */
|
||||
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 */
|
||||
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 */
|
||||
u32 transferInfo[18]; /* for disk only */
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
u32 ctrl;
|
||||
u32 width;
|
||||
u32 burst;
|
||||
u32 vSync;
|
||||
u32 hSync;
|
||||
u32 leap;
|
||||
u32 hStart;
|
||||
u32 xScale;
|
||||
u32 vCurrent;
|
||||
u32 ctrl;
|
||||
u32 width;
|
||||
u32 burst;
|
||||
u32 vSync;
|
||||
u32 hSync;
|
||||
u32 leap;
|
||||
u32 hStart;
|
||||
u32 xScale;
|
||||
u32 vCurrent;
|
||||
} OSViCommonRegs;
|
||||
|
||||
typedef struct {
|
||||
u32 origin;
|
||||
u32 yScale;
|
||||
u32 vStart;
|
||||
u32 vBurst;
|
||||
u32 vIntr;
|
||||
u32 origin;
|
||||
u32 yScale;
|
||||
u32 vStart;
|
||||
u32 vBurst;
|
||||
u32 vIntr;
|
||||
} OSViFieldRegs;
|
||||
|
||||
typedef struct {
|
||||
u8 padding[3];
|
||||
u8 type;
|
||||
OSViCommonRegs comRegs;
|
||||
OSViFieldRegs fldRegs[2];
|
||||
OSViFieldRegs fldRegs[2];
|
||||
} OSViMode;
|
||||
|
||||
/*
|
||||
|
|
@ -221,13 +221,13 @@ typedef struct {
|
|||
int status;
|
||||
PTR(OSMesgQueue) queue;
|
||||
int channel;
|
||||
u8 id[32]; // TODO: funky endianness here
|
||||
u8 id[32]; // TODO: funky endianness here
|
||||
u8 label[32]; // TODO: funky endianness here
|
||||
int version;
|
||||
int dir_size;
|
||||
int inode_table; /* block location */
|
||||
int minode_table; /* mirrioring inode_table */
|
||||
int dir_table; /* block location */
|
||||
int inode_table; /* block location */
|
||||
int minode_table; /* mirrioring inode_table */
|
||||
int dir_table; /* block location */
|
||||
int inode_start_page; /* page # */
|
||||
// Padding and reversed members due to endianness
|
||||
u8 padding[2];
|
||||
|
|
@ -235,14 +235,13 @@ typedef struct {
|
|||
u8 banks;
|
||||
} OSPfs;
|
||||
|
||||
|
||||
// Controller
|
||||
|
||||
typedef struct {
|
||||
// These three members reversed due to endianness
|
||||
u8 err_no;
|
||||
u8 status; /* Controller status */
|
||||
u16 type; /* Controller Type */
|
||||
u8 status; /* Controller status */
|
||||
u16 type; /* Controller Type */
|
||||
} OSContStatus;
|
||||
|
||||
typedef struct {
|
||||
|
|
@ -252,7 +251,6 @@ typedef struct {
|
|||
u8 err_no;
|
||||
} OSContPad;
|
||||
|
||||
|
||||
///////////////
|
||||
// Functions //
|
||||
///////////////
|
||||
|
|
@ -263,7 +261,7 @@ extern "C" {
|
|||
|
||||
void osInitialize(void);
|
||||
|
||||
typedef void (thread_func_t)(PTR(void));
|
||||
typedef void(thread_func_t)(PTR(void));
|
||||
|
||||
void osCreateThread(RDRAM_ARG PTR(OSThread) t, OSId id, PTR(thread_func_t) entry, PTR(void) arg, PTR(void) sp, OSPri p);
|
||||
void osStartThread(RDRAM_ARG PTR(OSThread) t);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
#ifndef __ultramodern_HPP__
|
||||
#define __ultramodern_HPP__
|
||||
|
||||
#include <thread>
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
#include <span>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
|
||||
#undef MOODYCAMEL_DELETE_FUNCTION
|
||||
#define MOODYCAMEL_DELETE_FUNCTION = delete
|
||||
|
|
@ -14,8 +14,8 @@
|
|||
#include "ultramodern/error_handling.hpp"
|
||||
#include "ultramodern/events.hpp"
|
||||
#include "ultramodern/input.hpp"
|
||||
#include "ultramodern/rsp.hpp"
|
||||
#include "ultramodern/renderer_context.hpp"
|
||||
#include "ultramodern/rsp.hpp"
|
||||
|
||||
struct UltraThreadContext {
|
||||
std::thread host_thread;
|
||||
|
|
@ -25,127 +25,122 @@ struct UltraThreadContext {
|
|||
|
||||
namespace ultramodern {
|
||||
|
||||
// We need a place in rdram to hold the PI handles, so pick an address in extended rdram
|
||||
constexpr uint32_t rdram_size = 1024 * 1024 * 16; // 16MB to give extra room for anything custom
|
||||
constexpr int32_t cart_handle = 0x80800000;
|
||||
constexpr int32_t drive_handle = (int32_t)(cart_handle + sizeof(OSPiHandle));
|
||||
constexpr int32_t flash_handle = (int32_t)(drive_handle + sizeof(OSPiHandle));
|
||||
constexpr uint32_t save_size = 1024 * 1024 / 8; // Maximum save size, 1Mbit for flash
|
||||
// We need a place in rdram to hold the PI handles, so pick an address in extended rdram
|
||||
constexpr uint32_t rdram_size = 1024 * 1024 * 16; // 16MB to give extra room for anything custom
|
||||
constexpr int32_t cart_handle = 0x80800000;
|
||||
constexpr int32_t drive_handle = (int32_t)(cart_handle + sizeof(OSPiHandle));
|
||||
constexpr int32_t flash_handle = (int32_t)(drive_handle + sizeof(OSPiHandle));
|
||||
constexpr uint32_t save_size = 1024 * 1024 / 8; // Maximum save size, 1Mbit for flash
|
||||
|
||||
// Initialization.
|
||||
void preinit(RDRAM_ARG renderer::WindowHandle window_handle);
|
||||
void init_saving(RDRAM_ARG1);
|
||||
void init_events(RDRAM_ARG renderer::WindowHandle window_handle);
|
||||
void init_timers(RDRAM_ARG1);
|
||||
void init_thread_cleanup();
|
||||
// Initialization.
|
||||
void preinit(RDRAM_ARG renderer::WindowHandle window_handle);
|
||||
void init_saving(RDRAM_ARG1);
|
||||
void init_events(RDRAM_ARG renderer::WindowHandle window_handle);
|
||||
void init_timers(RDRAM_ARG1);
|
||||
void init_thread_cleanup();
|
||||
|
||||
// Thread queues.
|
||||
constexpr PTR(PTR(OSThread)) running_queue = (PTR(PTR(OSThread)))-1;
|
||||
// Thread queues.
|
||||
constexpr PTR(PTR(OSThread)) running_queue = (PTR(PTR(OSThread)))-1;
|
||||
|
||||
void thread_queue_insert(RDRAM_ARG PTR(PTR(OSThread)) queue, PTR(OSThread) toadd);
|
||||
PTR(OSThread) thread_queue_pop(RDRAM_ARG PTR(PTR(OSThread)) queue);
|
||||
bool thread_queue_remove(RDRAM_ARG PTR(PTR(OSThread)) queue_, PTR(OSThread) t_);
|
||||
bool thread_queue_empty(RDRAM_ARG PTR(PTR(OSThread)) queue);
|
||||
PTR(OSThread) thread_queue_peek(RDRAM_ARG PTR(PTR(OSThread)) queue);
|
||||
void thread_queue_insert(RDRAM_ARG PTR(PTR(OSThread)) queue, PTR(OSThread) toadd);
|
||||
PTR(OSThread) thread_queue_pop(RDRAM_ARG PTR(PTR(OSThread)) queue);
|
||||
bool thread_queue_remove(RDRAM_ARG PTR(PTR(OSThread)) queue_, PTR(OSThread) t_);
|
||||
bool thread_queue_empty(RDRAM_ARG PTR(PTR(OSThread)) queue);
|
||||
PTR(OSThread) thread_queue_peek(RDRAM_ARG PTR(PTR(OSThread)) queue);
|
||||
|
||||
// Message queues.
|
||||
void wait_for_external_message(RDRAM_ARG1);
|
||||
void wait_for_external_message_timed(RDRAM_ARG1, u32 millis);
|
||||
// Message queues.
|
||||
void wait_for_external_message(RDRAM_ARG1);
|
||||
void wait_for_external_message_timed(RDRAM_ARG1, u32 millis);
|
||||
|
||||
// Thread scheduling.
|
||||
void check_running_queue(RDRAM_ARG1);
|
||||
void run_next_thread_and_wait(RDRAM_ARG1);
|
||||
void resume_thread_and_wait(RDRAM_ARG OSThread* t);
|
||||
void schedule_running_thread(RDRAM_ARG PTR(OSThread) t);
|
||||
void cleanup_thread(UltraThreadContext* thread_context);
|
||||
uint32_t permanent_thread_count();
|
||||
uint32_t temporary_thread_count();
|
||||
struct thread_terminated : std::exception {};
|
||||
// Thread scheduling.
|
||||
void check_running_queue(RDRAM_ARG1);
|
||||
void run_next_thread_and_wait(RDRAM_ARG1);
|
||||
void resume_thread_and_wait(RDRAM_ARG OSThread *t);
|
||||
void schedule_running_thread(RDRAM_ARG PTR(OSThread) t);
|
||||
void cleanup_thread(UltraThreadContext *thread_context);
|
||||
uint32_t permanent_thread_count();
|
||||
uint32_t temporary_thread_count();
|
||||
struct thread_terminated : std::exception {};
|
||||
|
||||
enum class ThreadPriority {
|
||||
Low,
|
||||
Normal,
|
||||
High,
|
||||
VeryHigh,
|
||||
Critical
|
||||
};
|
||||
enum class ThreadPriority {
|
||||
Low,
|
||||
Normal,
|
||||
High,
|
||||
VeryHigh,
|
||||
Critical
|
||||
};
|
||||
|
||||
void set_native_thread_name(const std::string& name);
|
||||
void set_native_thread_priority(ThreadPriority pri);
|
||||
PTR(OSThread) this_thread();
|
||||
void set_main_thread();
|
||||
bool is_game_thread();
|
||||
void submit_rsp_task(RDRAM_ARG PTR(OSTask) task);
|
||||
void send_si_message(RDRAM_ARG1);
|
||||
uint32_t get_speed_multiplier();
|
||||
void set_native_thread_name(const std::string& name);
|
||||
void set_native_thread_priority(ThreadPriority pri);
|
||||
PTR(OSThread) this_thread();
|
||||
void set_main_thread();
|
||||
bool is_game_thread();
|
||||
void submit_rsp_task(RDRAM_ARG PTR(OSTask) task);
|
||||
void send_si_message(RDRAM_ARG1);
|
||||
uint32_t get_speed_multiplier();
|
||||
|
||||
// Time
|
||||
std::chrono::high_resolution_clock::time_point get_start();
|
||||
std::chrono::high_resolution_clock::duration time_since_start();
|
||||
void measure_input_latency();
|
||||
void sleep_milliseconds(uint32_t millis);
|
||||
void sleep_until(const std::chrono::high_resolution_clock::time_point& time_point);
|
||||
// Time
|
||||
std::chrono::high_resolution_clock::time_point get_start();
|
||||
std::chrono::high_resolution_clock::duration time_since_start();
|
||||
void measure_input_latency();
|
||||
void sleep_milliseconds(uint32_t millis);
|
||||
void sleep_until(const std::chrono::high_resolution_clock::time_point& time_point);
|
||||
|
||||
// Graphics
|
||||
uint32_t get_target_framerate(uint32_t original);
|
||||
uint32_t get_display_refresh_rate();
|
||||
float get_resolution_scale();
|
||||
void load_shader_cache(std::span<const char> cache_data);
|
||||
void trigger_config_action();
|
||||
// Graphics
|
||||
uint32_t get_target_framerate(uint32_t original);
|
||||
uint32_t get_display_refresh_rate();
|
||||
float get_resolution_scale();
|
||||
void load_shader_cache(std::span<const char> cache_data);
|
||||
void trigger_config_action();
|
||||
|
||||
// Audio
|
||||
void init_audio();
|
||||
void set_audio_frequency(uint32_t freq);
|
||||
void queue_audio_buffer(RDRAM_ARG PTR(s16) audio_data, uint32_t byte_count);
|
||||
uint32_t get_remaining_audio_bytes();
|
||||
// Audio
|
||||
void init_audio();
|
||||
void set_audio_frequency(uint32_t freq);
|
||||
void queue_audio_buffer(RDRAM_ARG PTR(s16) audio_data, uint32_t byte_count);
|
||||
uint32_t get_remaining_audio_bytes();
|
||||
|
||||
struct audio_callbacks_t {
|
||||
using queue_samples_t = void(int16_t*, size_t);
|
||||
using get_samples_remaining_t = size_t();
|
||||
using set_frequency_t = void(uint32_t);
|
||||
queue_samples_t* queue_samples;
|
||||
get_samples_remaining_t* get_frames_remaining;
|
||||
set_frequency_t* set_frequency;
|
||||
};
|
||||
struct audio_callbacks_t {
|
||||
using queue_samples_t = void(int16_t *, size_t);
|
||||
using get_samples_remaining_t = size_t();
|
||||
using set_frequency_t = void(uint32_t);
|
||||
queue_samples_t *queue_samples;
|
||||
get_samples_remaining_t *get_frames_remaining;
|
||||
set_frequency_t *set_frequency;
|
||||
};
|
||||
|
||||
// TODO: Most of the members of this struct are not used by ultramodern. Should we move them to librecomp instead?
|
||||
struct gfx_callbacks_t {
|
||||
using gfx_data_t = void*;
|
||||
using create_gfx_t = gfx_data_t();
|
||||
using create_window_t = renderer::WindowHandle(gfx_data_t);
|
||||
using update_gfx_t = void(gfx_data_t);
|
||||
// TODO: Most of the members of this struct are not used by ultramodern. Should we move them to librecomp instead?
|
||||
struct gfx_callbacks_t {
|
||||
using gfx_data_t = void *;
|
||||
using create_gfx_t = gfx_data_t();
|
||||
using create_window_t = renderer::WindowHandle(gfx_data_t);
|
||||
using update_gfx_t = void(gfx_data_t);
|
||||
|
||||
create_gfx_t* create_gfx;
|
||||
create_window_t* create_window;
|
||||
update_gfx_t* update_gfx;
|
||||
};
|
||||
create_gfx_t *create_gfx;
|
||||
create_window_t *create_window;
|
||||
update_gfx_t *update_gfx;
|
||||
};
|
||||
|
||||
bool is_game_started();
|
||||
void quit();
|
||||
void join_event_threads();
|
||||
void join_thread_cleaner_thread();
|
||||
void join_saving_thread();
|
||||
bool is_game_started();
|
||||
void quit();
|
||||
void join_event_threads();
|
||||
void join_thread_cleaner_thread();
|
||||
void join_saving_thread();
|
||||
|
||||
void set_audio_callbacks(const audio_callbacks_t& callbacks);
|
||||
void set_audio_callbacks(const audio_callbacks_t& callbacks);
|
||||
|
||||
/**
|
||||
* Register all the callbacks used by `ultramodern`, most of them being optional.
|
||||
*
|
||||
* The following arguments contain mandatory callbacks that need to be registered (i.e., can't be `nullptr`):
|
||||
* - `rsp_callbacks`
|
||||
* - `renderer_callbacks`
|
||||
*
|
||||
* It must be called only once and it must be called before `ultramodern::preinit`.
|
||||
*/
|
||||
void set_callbacks(
|
||||
const rsp::callbacks_t& rsp_callbacks,
|
||||
const renderer::callbacks_t& renderer_callbacks,
|
||||
const audio_callbacks_t& audio_callbacks,
|
||||
const input::callbacks_t& input_callbacks,
|
||||
const gfx_callbacks_t& gfx_callbacks,
|
||||
const events::callbacks_t& events_callbacks,
|
||||
const error_handling::callbacks_t& error_handling_callbacks
|
||||
);
|
||||
/**
|
||||
* Register all the callbacks used by `ultramodern`, most of them being optional.
|
||||
*
|
||||
* The following arguments contain mandatory callbacks that need to be registered (i.e., can't be `nullptr`):
|
||||
* - `rsp_callbacks`
|
||||
* - `renderer_callbacks`
|
||||
*
|
||||
* It must be called only once and it must be called before `ultramodern::preinit`.
|
||||
*/
|
||||
void set_callbacks(
|
||||
const rsp::callbacks_t& rsp_callbacks, const renderer::callbacks_t& renderer_callbacks, const audio_callbacks_t& audio_callbacks,
|
||||
const input::callbacks_t& input_callbacks, const gfx_callbacks_t& gfx_callbacks, const events::callbacks_t& events_callbacks,
|
||||
const error_handling::callbacks_t& error_handling_callbacks);
|
||||
} // namespace ultramodern
|
||||
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ void ultramodern::queue_audio_buffer(RDRAM_ARG PTR(int16_t) audio_data_, uint32_
|
|||
}
|
||||
|
||||
// For SDL2
|
||||
//uint32_t buffer_offset_frames = 1;
|
||||
// uint32_t buffer_offset_frames = 1;
|
||||
// For Godot
|
||||
float buffer_offset_frames = 0.5f;
|
||||
|
||||
|
|
@ -52,16 +52,16 @@ uint32_t ultramodern::get_remaining_audio_bytes() {
|
|||
else {
|
||||
buffered_byte_count = 100;
|
||||
}
|
||||
// Adjust the reported count to be some number of refreshes in the future, which helps ensure that
|
||||
// there are enough samples even if the audio thread experiences a small amount of lag. This prevents
|
||||
// audio popping on games that use the buffered audio byte count to determine how many samples
|
||||
// to generate.
|
||||
uint32_t samples_per_vi = (sample_rate / 60);
|
||||
if (buffered_byte_count > static_cast<uint32_t>(buffer_offset_frames * sizeof(int16_t) * samples_per_vi)) {
|
||||
buffered_byte_count -= static_cast<uint32_t>(buffer_offset_frames * sizeof(int16_t) * samples_per_vi);
|
||||
}
|
||||
else {
|
||||
buffered_byte_count = 0;
|
||||
}
|
||||
return buffered_byte_count;
|
||||
// Adjust the reported count to be some number of refreshes in the future, which helps ensure that
|
||||
// there are enough samples even if the audio thread experiences a small amount of lag. This prevents
|
||||
// audio popping on games that use the buffered audio byte count to determine how many samples
|
||||
// to generate.
|
||||
uint32_t samples_per_vi = (sample_rate / 60);
|
||||
if (buffered_byte_count > static_cast<uint32_t>(buffer_offset_frames * sizeof(int16_t) * samples_per_vi)) {
|
||||
buffered_byte_count -= static_cast<uint32_t>(buffer_offset_frames * sizeof(int16_t) * samples_per_vi);
|
||||
}
|
||||
else {
|
||||
buffered_byte_count = 0;
|
||||
}
|
||||
return buffered_byte_count;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ void ultramodern::error_handling::set_callbacks(const ultramodern::error_handlin
|
|||
error_handling_callbacks = callbacks;
|
||||
}
|
||||
|
||||
void ultramodern::error_handling::message_box(const char* msg) {
|
||||
void ultramodern::error_handling::message_box(const char *msg) {
|
||||
// We print the message to stderr since the user may not have provided a message_box callback
|
||||
|
||||
fprintf(stderr, "%s\n", msg);
|
||||
|
|
@ -18,7 +18,7 @@ void ultramodern::error_handling::message_box(const char* msg) {
|
|||
}
|
||||
}
|
||||
|
||||
void ultramodern::error_handling::quick_exit(const char* filename, int line, const char *func, int exit_status) {
|
||||
void ultramodern::error_handling::quick_exit(const char *filename, int line, const char *func, int exit_status) {
|
||||
fprintf(stderr, "Exiting with exit status '%i'. Function %s, at file %s:%i, ", exit_status, func, filename, line);
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cinttypes>
|
||||
#include <variant>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <cstring>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
#include "blockingconcurrentqueue.h"
|
||||
|
||||
#include "ultramodern/ultra64.h"
|
||||
#include "ultramodern/ultramodern.hpp"
|
||||
|
||||
#include "ultramodern/rsp.hpp"
|
||||
#include "ultramodern/renderer_context.hpp"
|
||||
#include "ultramodern/rsp.hpp"
|
||||
|
||||
static ultramodern::events::callbacks_t events_callbacks{};
|
||||
|
||||
|
|
@ -31,8 +31,7 @@ struct SwapBuffersAction {
|
|||
uint32_t origin;
|
||||
};
|
||||
|
||||
struct UpdateConfigAction {
|
||||
};
|
||||
struct UpdateConfigAction {};
|
||||
|
||||
struct LoadShaderCacheAction {
|
||||
std::span<const char> data;
|
||||
|
|
@ -69,14 +68,14 @@ static struct {
|
|||
} si;
|
||||
// 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;
|
||||
uint8_t *rdram;
|
||||
moodycamel::BlockingConcurrentQueue<Action> action_queue{};
|
||||
moodycamel::BlockingConcurrentQueue<OSTask*> sp_task_queue{};
|
||||
moodycamel::ConcurrentQueue<OSThread*> deleted_threads{};
|
||||
moodycamel::BlockingConcurrentQueue<OSTask *> sp_task_queue{};
|
||||
moodycamel::ConcurrentQueue<OSThread *> deleted_threads{};
|
||||
} events_context{};
|
||||
|
||||
extern "C" void osSetEventMesg(RDRAM_ARG OSEvent event_id, PTR(OSMesgQueue) mq_, OSMesg msg) {
|
||||
OSMesgQueue* mq = TO_PTR(OSMesgQueue, mq_);
|
||||
OSMesgQueue *mq = TO_PTR(OSMesgQueue, mq_);
|
||||
std::lock_guard lock{ events_context.message_mutex };
|
||||
|
||||
switch (event_id) {
|
||||
|
|
@ -107,7 +106,6 @@ extern "C" void osViSetEvent(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, u32 ret
|
|||
|
||||
uint64_t total_vis = 0;
|
||||
|
||||
|
||||
extern std::atomic_bool exited;
|
||||
|
||||
void set_dummy_vi();
|
||||
|
|
@ -124,15 +122,16 @@ void vi_thread_func() {
|
|||
while (!exited) {
|
||||
// Determine the next VI time (more accurate than adding 16ms each VI interrupt)
|
||||
auto next = ultramodern::get_start() + (total_vis * 1000000us) / (60 * ultramodern::get_speed_multiplier());
|
||||
//if (next > std::chrono::high_resolution_clock::now()) {
|
||||
// printf("Sleeping for %" PRIu64 " us to get from %" PRIu64 " us to %" PRIu64 " us \n",
|
||||
// (next - std::chrono::high_resolution_clock::now()) / 1us,
|
||||
// (std::chrono::high_resolution_clock::now() - events_context.start) / 1us,
|
||||
// (next - events_context.start) / 1us);
|
||||
//} else {
|
||||
// printf("No need to sleep\n");
|
||||
//}
|
||||
// Detect if there's more than a second to wait and wait a fixed amount instead for the next VI if so, as that usually means the system clock went back in time.
|
||||
// if (next > std::chrono::high_resolution_clock::now()) {
|
||||
// printf("Sleeping for %" PRIu64 " us to get from %" PRIu64 " us to %" PRIu64 " us \n",
|
||||
// (next - std::chrono::high_resolution_clock::now()) / 1us,
|
||||
// (std::chrono::high_resolution_clock::now() - events_context.start) / 1us,
|
||||
// (next - events_context.start) / 1us);
|
||||
// } else {
|
||||
// printf("No need to sleep\n");
|
||||
// }
|
||||
// Detect if there's more than a second to wait and wait a fixed amount instead for the next VI if so, as that usually means the
|
||||
// system clock went back in time.
|
||||
if (std::chrono::floor<std::chrono::seconds>(next - std::chrono::high_resolution_clock::now()) > 1s) {
|
||||
// printf("Skipping the next VI wait\n");
|
||||
next = std::chrono::high_resolution_clock::now();
|
||||
|
|
@ -141,7 +140,7 @@ void vi_thread_func() {
|
|||
// Calculate how many VIs have passed
|
||||
uint64_t new_total_vis = (ultramodern::time_since_start() * (60 * ultramodern::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);
|
||||
// printf("Skipped % " PRId64 " frames in VI interupt thread!\n", new_total_vis - total_vis - 1);
|
||||
}
|
||||
total_vis = new_total_vis;
|
||||
|
||||
|
|
@ -149,14 +148,14 @@ void vi_thread_func() {
|
|||
|
||||
{
|
||||
std::lock_guard lock{ events_context.message_mutex };
|
||||
uint8_t* rdram = events_context.rdram;
|
||||
uint8_t *rdram = events_context.rdram;
|
||||
if (remaining_retraces == 0) {
|
||||
remaining_retraces = events_context.vi.retrace_count;
|
||||
|
||||
if (ultramodern::is_game_started()) {
|
||||
if (events_context.vi.mq != NULLPTR) {
|
||||
if (osSendMesg(PASS_RDRAM events_context.vi.mq, events_context.vi.msg, OS_MESG_NOBLOCK) == -1) {
|
||||
//printf("Game skipped a VI frame!\n");
|
||||
// printf("Game skipped a VI frame!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -174,7 +173,7 @@ void vi_thread_func() {
|
|||
}
|
||||
if (events_context.ai.mq != NULLPTR) {
|
||||
if (osSendMesg(PASS_RDRAM events_context.ai.mq, events_context.ai.msg, OS_MESG_NOBLOCK) == -1) {
|
||||
//printf("Game skipped a AI frame!\n");
|
||||
// printf("Game skipped a AI frame!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -186,18 +185,18 @@ void vi_thread_func() {
|
|||
}
|
||||
|
||||
void sp_complete() {
|
||||
uint8_t* rdram = events_context.rdram;
|
||||
uint8_t *rdram = events_context.rdram;
|
||||
std::lock_guard lock{ events_context.message_mutex };
|
||||
osSendMesg(PASS_RDRAM events_context.sp.mq, events_context.sp.msg, OS_MESG_NOBLOCK);
|
||||
}
|
||||
|
||||
void dp_complete() {
|
||||
uint8_t* rdram = events_context.rdram;
|
||||
uint8_t *rdram = events_context.rdram;
|
||||
std::lock_guard lock{ events_context.message_mutex };
|
||||
osSendMesg(PASS_RDRAM events_context.dp.mq, events_context.dp.msg, OS_MESG_NOBLOCK);
|
||||
}
|
||||
|
||||
void task_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_ready) {
|
||||
void task_thread_func(uint8_t *rdram, moodycamel::LightweightSemaphore *thread_ready) {
|
||||
ultramodern::set_native_thread_name("SP Task Thread");
|
||||
ultramodern::set_native_thread_priority(ultramodern::ThreadPriority::Normal);
|
||||
|
||||
|
|
@ -206,7 +205,7 @@ void task_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_r
|
|||
|
||||
while (true) {
|
||||
// Wait until an RSP task has been sent
|
||||
OSTask* task;
|
||||
OSTask *task;
|
||||
events_context.sp_task_queue.wait_dequeue(task);
|
||||
|
||||
if (task == nullptr) {
|
||||
|
|
@ -249,7 +248,7 @@ float ultramodern::get_resolution_scale() {
|
|||
}
|
||||
|
||||
void ultramodern::load_shader_cache(std::span<const char> cache_data) {
|
||||
events_context.action_queue.enqueue(LoadShaderCacheAction{cache_data});
|
||||
events_context.action_queue.enqueue(LoadShaderCacheAction{ cache_data });
|
||||
}
|
||||
|
||||
void ultramodern::trigger_config_action() {
|
||||
|
|
@ -258,7 +257,7 @@ void ultramodern::trigger_config_action() {
|
|||
|
||||
std::atomic<ultramodern::renderer::SetupResult> renderer_setup_result = ultramodern::renderer::SetupResult::Success;
|
||||
|
||||
void gfx_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_ready, ultramodern::renderer::WindowHandle window_handle) {
|
||||
void gfx_thread_func(uint8_t *rdram, moodycamel::LightweightSemaphore *thread_ready, ultramodern::renderer::WindowHandle window_handle) {
|
||||
bool enabled_instant_present = false;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
|
|
@ -267,7 +266,8 @@ void gfx_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_re
|
|||
|
||||
auto old_config = ultramodern::renderer::get_graphics_config();
|
||||
|
||||
auto renderer_context = ultramodern::renderer::create_render_context(rdram, window_handle, ultramodern::renderer::get_graphics_config().developer_mode);
|
||||
auto renderer_context =
|
||||
ultramodern::renderer::create_render_context(rdram, window_handle, ultramodern::renderer::get_graphics_config().developer_mode);
|
||||
|
||||
if (!renderer_context->valid()) {
|
||||
renderer_setup_result.store(renderer_context->get_setup_result());
|
||||
|
|
@ -290,7 +290,7 @@ void gfx_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_re
|
|||
Action action;
|
||||
if (events_context.action_queue.wait_dequeue_timed(action, 1ms)) {
|
||||
// Determine the action type and act on it
|
||||
if (const auto* task_action = std::get_if<SpTaskAction>(&action)) {
|
||||
if (const auto *task_action = std::get_if<SpTaskAction>(&action)) {
|
||||
// Turn on instant present if the game has been started and it hasn't been turned on yet.
|
||||
if (ultramodern::is_game_started() && !enabled_instant_present) {
|
||||
renderer_context->enable_instant_present();
|
||||
|
|
@ -307,21 +307,22 @@ void gfx_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_re
|
|||
renderer_context->send_dl(&task_action->task);
|
||||
auto renderer_end = std::chrono::high_resolution_clock::now();
|
||||
dp_complete();
|
||||
// printf("Renderer ProcessDList time: %d us\n", static_cast<u32>(std::chrono::duration_cast<std::chrono::microseconds>(renderer_end - renderer_start).count()));
|
||||
// printf("Renderer ProcessDList time: %d us\n",
|
||||
// static_cast<u32>(std::chrono::duration_cast<std::chrono::microseconds>(renderer_end - renderer_start).count()));
|
||||
}
|
||||
else if (const auto* swap_action = std::get_if<SwapBuffersAction>(&action)) {
|
||||
else if (const auto *swap_action = std::get_if<SwapBuffersAction>(&action)) {
|
||||
events_context.vi.current_buffer = events_context.vi.next_buffer;
|
||||
renderer_context->update_screen(swap_action->origin);
|
||||
display_refresh_rate = renderer_context->get_display_framerate();
|
||||
resolution_scale = renderer_context->get_resolution_scale();
|
||||
}
|
||||
else if (const auto* config_action = std::get_if<UpdateConfigAction>(&action)) {
|
||||
else if (const auto *config_action = std::get_if<UpdateConfigAction>(&action)) {
|
||||
auto new_config = ultramodern::renderer::get_graphics_config();
|
||||
if (renderer_context->update_config(old_config, new_config)) {
|
||||
old_config = new_config;
|
||||
}
|
||||
}
|
||||
else if (const auto* load_shader_cache_action = std::get_if<LoadShaderCacheAction>(&action)) {
|
||||
else if (const auto *load_shader_cache_action = std::get_if<LoadShaderCacheAction>(&action)) {
|
||||
renderer_context->load_shader_cache(load_shader_cache_action->data);
|
||||
}
|
||||
}
|
||||
|
|
@ -368,7 +369,8 @@ void set_dummy_vi() {
|
|||
extern "C" void osViSwapBuffer(RDRAM_ARG PTR(void) frameBufPtr) {
|
||||
if (vi_black) {
|
||||
VI_H_START_REG = 0;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
VI_H_START_REG = hstart;
|
||||
}
|
||||
events_context.vi.next_buffer = frameBufPtr;
|
||||
|
|
@ -376,7 +378,7 @@ extern "C" void osViSwapBuffer(RDRAM_ARG PTR(void) frameBufPtr) {
|
|||
}
|
||||
|
||||
extern "C" void osViSetMode(RDRAM_ARG PTR(OSViMode) mode_) {
|
||||
OSViMode* mode = TO_PTR(OSViMode, mode_);
|
||||
OSViMode *mode = TO_PTR(OSViMode, mode_);
|
||||
VI_STATUS_REG = mode->comRegs.ctrl;
|
||||
VI_WIDTH_REG = mode->comRegs.width;
|
||||
// burst
|
||||
|
|
@ -395,29 +397,29 @@ extern "C" void osViSetMode(RDRAM_ARG PTR(OSViMode) mode_) {
|
|||
VI_INTR_REG = mode->fldRegs[0].vIntr;
|
||||
}
|
||||
|
||||
#define VI_CTRL_TYPE_16 0x00002
|
||||
#define VI_CTRL_TYPE_32 0x00003
|
||||
#define VI_CTRL_GAMMA_DITHER_ON 0x00004
|
||||
#define VI_CTRL_GAMMA_ON 0x00008
|
||||
#define VI_CTRL_DIVOT_ON 0x00010
|
||||
#define VI_CTRL_SERRATE_ON 0x00040
|
||||
#define VI_CTRL_ANTIALIAS_MASK 0x00300
|
||||
#define VI_CTRL_ANTIALIAS_MODE_1 0x00100
|
||||
#define VI_CTRL_ANTIALIAS_MODE_2 0x00200
|
||||
#define VI_CTRL_ANTIALIAS_MODE_3 0x00300
|
||||
#define VI_CTRL_PIXEL_ADV_MASK 0x01000
|
||||
#define VI_CTRL_PIXEL_ADV_1 0x01000
|
||||
#define VI_CTRL_PIXEL_ADV_2 0x02000
|
||||
#define VI_CTRL_PIXEL_ADV_3 0x03000
|
||||
#define VI_CTRL_DITHER_FILTER_ON 0x10000
|
||||
#define VI_CTRL_TYPE_16 0x00002
|
||||
#define VI_CTRL_TYPE_32 0x00003
|
||||
#define VI_CTRL_GAMMA_DITHER_ON 0x00004
|
||||
#define VI_CTRL_GAMMA_ON 0x00008
|
||||
#define VI_CTRL_DIVOT_ON 0x00010
|
||||
#define VI_CTRL_SERRATE_ON 0x00040
|
||||
#define VI_CTRL_ANTIALIAS_MASK 0x00300
|
||||
#define VI_CTRL_ANTIALIAS_MODE_1 0x00100
|
||||
#define VI_CTRL_ANTIALIAS_MODE_2 0x00200
|
||||
#define VI_CTRL_ANTIALIAS_MODE_3 0x00300
|
||||
#define VI_CTRL_PIXEL_ADV_MASK 0x01000
|
||||
#define VI_CTRL_PIXEL_ADV_1 0x01000
|
||||
#define VI_CTRL_PIXEL_ADV_2 0x02000
|
||||
#define VI_CTRL_PIXEL_ADV_3 0x03000
|
||||
#define VI_CTRL_DITHER_FILTER_ON 0x10000
|
||||
|
||||
#define OS_VI_GAMMA_ON 0x0001
|
||||
#define OS_VI_GAMMA_OFF 0x0002
|
||||
#define OS_VI_GAMMA_DITHER_ON 0x0004
|
||||
#define OS_VI_GAMMA_DITHER_OFF 0x0008
|
||||
#define OS_VI_DIVOT_ON 0x0010
|
||||
#define OS_VI_DIVOT_OFF 0x0020
|
||||
#define OS_VI_DITHER_FILTER_ON 0x0040
|
||||
#define OS_VI_GAMMA_ON 0x0001
|
||||
#define OS_VI_GAMMA_OFF 0x0002
|
||||
#define OS_VI_GAMMA_DITHER_ON 0x0004
|
||||
#define OS_VI_GAMMA_DITHER_OFF 0x0008
|
||||
#define OS_VI_DIVOT_ON 0x0010
|
||||
#define OS_VI_DIVOT_OFF 0x0020
|
||||
#define OS_VI_DITHER_FILTER_ON 0x0040
|
||||
#define OS_VI_DITHER_FILTER_OFF 0x0080
|
||||
|
||||
extern "C" void osViSetSpecialFeatures(uint32_t func) {
|
||||
|
|
@ -452,7 +454,7 @@ extern "C" void osViSetSpecialFeatures(uint32_t func) {
|
|||
|
||||
if ((func & OS_VI_DITHER_FILTER_OFF) != 0) {
|
||||
VI_STATUS_REG &= ~VI_CTRL_DITHER_FILTER_ON;
|
||||
//VI_STATUS_REG |= __osViNext->modep->comRegs.ctrl & VI_CTRL_ANTIALIAS_MASK;
|
||||
// VI_STATUS_REG |= __osViNext->modep->comRegs.ctrl & VI_CTRL_ANTIALIAS_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -481,7 +483,7 @@ extern "C" PTR(void) osViGetCurrentFramebuffer() {
|
|||
}
|
||||
|
||||
void ultramodern::submit_rsp_task(RDRAM_ARG PTR(OSTask) task_) {
|
||||
OSTask* task = TO_PTR(OSTask, task_);
|
||||
OSTask *task = TO_PTR(OSTask, task_);
|
||||
|
||||
// Send gfx tasks to the graphics action queue
|
||||
if (task->t.type == M_GFXTASK) {
|
||||
|
|
@ -525,10 +527,14 @@ void ultramodern::init_events(RDRAM_ARG ultramodern::renderer::WindowHandle wind
|
|||
show_renderer_error("Failed to load dynamic libraries. Make sure the DLLs are next to the recomp executable.");
|
||||
break;
|
||||
case ultramodern::renderer::SetupResult::InvalidGraphicsAPI:
|
||||
show_renderer_error(ultramodern::renderer::get_graphics_api_name(ultramodern::renderer::get_graphics_config()) + " is not supported on this platform. Please select a different graphics API.");
|
||||
show_renderer_error(
|
||||
ultramodern::renderer::get_graphics_api_name(ultramodern::renderer::get_graphics_config()) +
|
||||
" is not supported on this platform. Please select a different graphics API.");
|
||||
break;
|
||||
case ultramodern::renderer::SetupResult::GraphicsAPINotFound:
|
||||
show_renderer_error("Unable to initialize " + ultramodern::renderer::get_graphics_api_name(ultramodern::renderer::get_graphics_config()) + "." + driver_os_suffix);
|
||||
show_renderer_error(
|
||||
"Unable to initialize " + ultramodern::renderer::get_graphics_api_name(ultramodern::renderer::get_graphics_config()) +
|
||||
"." + driver_os_suffix);
|
||||
break;
|
||||
case ultramodern::renderer::SetupResult::GraphicsDeviceNotFound:
|
||||
show_renderer_error("Unable to find compatible graphics device." + driver_os_suffix);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
#include "ultramodern/ultra64.h"
|
||||
#include "ultramodern/ultramodern.hpp"
|
||||
|
||||
static ultramodern::input::callbacks_t input_callbacks {};
|
||||
static ultramodern::input::callbacks_t input_callbacks{};
|
||||
|
||||
void ultramodern::input::set_callbacks(const callbacks_t& callbacks) {
|
||||
input_callbacks = callbacks;
|
||||
|
|
@ -27,8 +27,8 @@ void ultramodern::measure_input_latency() {
|
|||
#define CONT_NO_RESPONSE_ERROR 0x8
|
||||
|
||||
#define CONT_TYPE_NORMAL 0x0005
|
||||
#define CONT_TYPE_MOUSE 0x0002
|
||||
#define CONT_TYPE_VOICE 0x0100
|
||||
#define CONT_TYPE_MOUSE 0x0002
|
||||
#define CONT_TYPE_VOICE 0x0100
|
||||
|
||||
static int max_controllers = 0;
|
||||
|
||||
|
|
@ -36,11 +36,11 @@ static int max_controllers = 0;
|
|||
|
||||
static u16 get_controller_type(ultramodern::input::Device device_type) {
|
||||
switch (device_type) {
|
||||
case ultramodern::input::Device::None:
|
||||
return 0;
|
||||
case ultramodern::input::Device::None:
|
||||
return 0;
|
||||
|
||||
case ultramodern::input::Device::Controller:
|
||||
return CONT_TYPE_NORMAL;
|
||||
case ultramodern::input::Device::Controller:
|
||||
return CONT_TYPE_NORMAL;
|
||||
|
||||
#if 0
|
||||
case ultramodern::input::Device::Mouse:
|
||||
|
|
@ -54,7 +54,7 @@ static u16 get_controller_type(ultramodern::input::Device device_type) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void __osContGetInitData(u8* pattern, OSContStatus *data) {
|
||||
static void __osContGetInitData(u8 *pattern, OSContStatus *data) {
|
||||
*pattern = 0x00;
|
||||
|
||||
for (int controller = 0; controller < max_controllers; controller++) {
|
||||
|
|
@ -144,8 +144,9 @@ extern "C" void osContGetReadData(OSContPad *data) {
|
|||
data[controller].stick_x = (int8_t)(127 * x);
|
||||
data[controller].stick_y = (int8_t)(127 * y);
|
||||
data[controller].err_no = 0;
|
||||
} else {
|
||||
data[controller].err_no = CONT_NO_RESPONSE_ERROR; // CHNL_ERR_NORESP >> 4
|
||||
}
|
||||
else {
|
||||
data[controller].err_no = CONT_NO_RESPONSE_ERROR; // CHNL_ERR_NORESP >> 4
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@ struct QueuedMessage {
|
|||
bool jam;
|
||||
};
|
||||
|
||||
static moodycamel::BlockingConcurrentQueue<QueuedMessage> external_messages {};
|
||||
static moodycamel::BlockingConcurrentQueue<QueuedMessage> external_messages{};
|
||||
|
||||
void enqueue_external_message(PTR(OSMesgQueue) mq, OSMesg msg, bool jam) {
|
||||
external_messages.enqueue({mq, msg, jam});
|
||||
external_messages.enqueue({ mq, msg, jam });
|
||||
}
|
||||
|
||||
bool do_send(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, bool jam, bool block);
|
||||
|
|
@ -34,7 +34,7 @@ void ultramodern::wait_for_external_message(RDRAM_ARG1) {
|
|||
|
||||
void ultramodern::wait_for_external_message_timed(RDRAM_ARG1, u32 millis) {
|
||||
QueuedMessage to_send;
|
||||
if (external_messages.wait_dequeue_timed(to_send, std::chrono::milliseconds{millis})) {
|
||||
if (external_messages.wait_dequeue_timed(to_send, std::chrono::milliseconds{ millis })) {
|
||||
do_send(PASS_RDRAM to_send.mq, to_send.mesg, to_send.jam, false);
|
||||
}
|
||||
}
|
||||
|
|
@ -57,12 +57,12 @@ s32 MQ_IS_EMPTY(OSMesgQueue *mq) {
|
|||
return mq->validCount == 0;
|
||||
}
|
||||
|
||||
s32 MQ_IS_FULL(OSMesgQueue* mq) {
|
||||
s32 MQ_IS_FULL(OSMesgQueue *mq) {
|
||||
return MQ_GET_COUNT(mq) >= mq->msgCount;
|
||||
}
|
||||
|
||||
bool do_send(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, bool jam, bool block) {
|
||||
OSMesgQueue* mq = TO_PTR(OSMesgQueue, mq_);
|
||||
OSMesgQueue *mq = TO_PTR(OSMesgQueue, mq_);
|
||||
if (!block) {
|
||||
// If non-blocking, fail if the queue is full.
|
||||
if (MQ_IS_FULL(mq)) {
|
||||
|
|
@ -77,7 +77,7 @@ bool do_send(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, bool jam, bool block) {
|
|||
ultramodern::run_next_thread_and_wait(PASS_RDRAM1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (jam) {
|
||||
// Jams insert at the head of the message queue's buffer.
|
||||
mq->first = (mq->first + mq->msgCount - 1) % mq->msgCount;
|
||||
|
|
@ -96,18 +96,19 @@ bool do_send(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, bool jam, bool block) {
|
|||
if (!ultramodern::thread_queue_empty(PASS_RDRAM blocked_queue)) {
|
||||
ultramodern::schedule_running_thread(PASS_RDRAM ultramodern::thread_queue_pop(PASS_RDRAM blocked_queue));
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool do_recv(RDRAM_ARG PTR(OSMesgQueue) mq_, PTR(OSMesg) msg_, bool block) {
|
||||
OSMesgQueue* mq = TO_PTR(OSMesgQueue, mq_);
|
||||
OSMesgQueue *mq = TO_PTR(OSMesgQueue, mq_);
|
||||
if (!block) {
|
||||
// If non-blocking, fail if the queue is empty
|
||||
if (MQ_IS_EMPTY(mq)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// Otherwise, yield this thread in a loop until the queue is no longer full
|
||||
while (MQ_IS_EMPTY(mq)) {
|
||||
debug_printf("[Message Queue] Thread %d is blocked on receive\n", TO_PTR(OSThread, ultramodern::this_thread())->id);
|
||||
|
|
@ -119,7 +120,7 @@ bool do_recv(RDRAM_ARG PTR(OSMesgQueue) mq_, PTR(OSMesg) msg_, bool block) {
|
|||
if (msg_ != NULLPTR) {
|
||||
*TO_PTR(OSMesg, msg_) = TO_PTR(OSMesg, mq->msg)[mq->first];
|
||||
}
|
||||
|
||||
|
||||
mq->first = (mq->first + 1) % mq->msgCount;
|
||||
mq->validCount--;
|
||||
|
||||
|
|
@ -135,19 +136,19 @@ bool do_recv(RDRAM_ARG PTR(OSMesgQueue) mq_, PTR(OSMesg) msg_, bool block) {
|
|||
extern "C" s32 osSendMesg(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, s32 flags) {
|
||||
OSMesgQueue *mq = TO_PTR(OSMesgQueue, mq_);
|
||||
bool jam = false;
|
||||
|
||||
|
||||
// Don't directly send to the message queue if this isn't a game thread to avoid contention.
|
||||
if (!ultramodern::is_game_thread()) {
|
||||
enqueue_external_message(mq_, msg, jam);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Handle any messages that have been received from an external thread.
|
||||
dequeue_external_messages(PASS_RDRAM1);
|
||||
|
||||
// Try to send the message.
|
||||
bool sent = do_send(PASS_RDRAM mq_, msg, jam, flags == OS_MESG_BLOCK);
|
||||
|
||||
|
||||
// Check the queue to see if this thread should swap execution to another.
|
||||
ultramodern::check_running_queue(PASS_RDRAM1);
|
||||
|
||||
|
|
@ -157,19 +158,19 @@ extern "C" s32 osSendMesg(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, s32 flags)
|
|||
extern "C" s32 osJamMesg(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, s32 flags) {
|
||||
OSMesgQueue *mq = TO_PTR(OSMesgQueue, mq_);
|
||||
bool jam = true;
|
||||
|
||||
|
||||
// Don't directly send to the message queue if this isn't a game thread to avoid contention.
|
||||
if (!ultramodern::is_game_thread()) {
|
||||
enqueue_external_message(mq_, msg, jam);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Handle any messages that have been received from an external thread.
|
||||
dequeue_external_messages(PASS_RDRAM1);
|
||||
|
||||
// Try to send the message.
|
||||
bool sent = do_send(PASS_RDRAM mq_, msg, jam, flags == OS_MESG_BLOCK);
|
||||
|
||||
|
||||
// Check the queue to see if this thread should swap execution to another.
|
||||
ultramodern::check_running_queue(PASS_RDRAM1);
|
||||
|
||||
|
|
@ -178,15 +179,15 @@ extern "C" s32 osJamMesg(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, s32 flags)
|
|||
|
||||
extern "C" s32 osRecvMesg(RDRAM_ARG PTR(OSMesgQueue) mq_, PTR(OSMesg) msg_, s32 flags) {
|
||||
OSMesgQueue *mq = TO_PTR(OSMesgQueue, mq_);
|
||||
|
||||
|
||||
assert(ultramodern::is_game_thread() && "RecvMesg not allowed outside of game threads.");
|
||||
|
||||
|
||||
// Handle any messages that have been received from an external thread.
|
||||
dequeue_external_messages(PASS_RDRAM1);
|
||||
|
||||
// Try to receive a message.
|
||||
bool received = do_recv(PASS_RDRAM mq_, msg_, flags == OS_MESG_BLOCK);
|
||||
|
||||
|
||||
// Check the queue to see if this thread should swap execution to another.
|
||||
ultramodern::check_running_queue(PASS_RDRAM1);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
#include "ultramodern/ultra64.h"
|
||||
|
||||
#define K0BASE 0x80000000
|
||||
#define K1BASE 0xA0000000
|
||||
#define K2BASE 0xC0000000
|
||||
#define IS_KSEG0(x) ((u32)(x) >= K0BASE && (u32)(x) < K1BASE)
|
||||
#define IS_KSEG1(x) ((u32)(x) >= K1BASE && (u32)(x) < K2BASE)
|
||||
#define K0BASE 0x80000000
|
||||
#define K1BASE 0xA0000000
|
||||
#define K2BASE 0xC0000000
|
||||
#define IS_KSEG0(x) ((u32)(x) >= K0BASE && (u32)(x) < K1BASE)
|
||||
#define IS_KSEG1(x) ((u32)(x) >= K1BASE && (u32)(x) < K2BASE)
|
||||
#define K0_TO_PHYS(x) ((u32)(x)&0x1FFFFFFF)
|
||||
#define K1_TO_PHYS(x) ((u32)(x)&0x1FFFFFFF)
|
||||
|
||||
|
|
@ -12,11 +12,12 @@ u32 osVirtualToPhysical(PTR(void) addr) {
|
|||
uintptr_t addr_val = (uintptr_t)addr;
|
||||
if (IS_KSEG0(addr_val)) {
|
||||
return K0_TO_PHYS(addr_val);
|
||||
} else if (IS_KSEG1(addr_val)) {
|
||||
}
|
||||
else if (IS_KSEG1(addr_val)) {
|
||||
return K1_TO_PHYS(addr_val);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// TODO handle TLB mappings
|
||||
return (u32)addr_val;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
#if 0
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "ultra64.h"
|
||||
# include "ultra64.h"
|
||||
# include <stdio.h>
|
||||
# include <stdlib.h>
|
||||
|
||||
#define THREAD_STACK_SIZE 0x1000
|
||||
# define THREAD_STACK_SIZE 0x1000
|
||||
|
||||
u8 idle_stack[THREAD_STACK_SIZE] ALIGNED(16);
|
||||
u8 main_stack[THREAD_STACK_SIZE] ALIGNED(16);
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ void ultramodern::renderer::set_callbacks(const callbacks_t& callbacks) {
|
|||
render_callbacks = callbacks;
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<ultramodern::renderer::RendererContext> ultramodern::renderer::create_render_context(uint8_t* rdram, WindowHandle window_handle, bool developer_mode) {
|
||||
std::unique_ptr<ultramodern::renderer::RendererContext>
|
||||
ultramodern::renderer::create_render_context(uint8_t *rdram, WindowHandle window_handle, bool developer_mode) {
|
||||
if (render_callbacks.create_render_context == nullptr) {
|
||||
error_handling::message_box("[Error] The mandatory render callback `create_render_context` was not registered");
|
||||
ULTRAMODERN_QUICK_EXIT();
|
||||
|
|
@ -27,7 +27,6 @@ std::string ultramodern::renderer::get_graphics_api_name(const GraphicsConfig& c
|
|||
return config.get_graphics_api_name();
|
||||
}
|
||||
|
||||
|
||||
static ultramodern::renderer::GraphicsConfig graphic_config{};
|
||||
static std::mutex graphic_config_mutex;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include "ultramodern/rsp.hpp"
|
||||
|
||||
static ultramodern::rsp::callbacks_t rsp_callbacks {};
|
||||
static ultramodern::rsp::callbacks_t rsp_callbacks{};
|
||||
|
||||
void ultramodern::rsp::set_callbacks(const callbacks_t& callbacks) {
|
||||
rsp_callbacks = callbacks;
|
||||
|
|
@ -15,7 +15,7 @@ void ultramodern::rsp::init() {
|
|||
}
|
||||
}
|
||||
|
||||
bool ultramodern::rsp::run_task(RDRAM_ARG const OSTask* task) {
|
||||
bool ultramodern::rsp::run_task(RDRAM_ARG const OSTask *task) {
|
||||
assert(rsp_callbacks.run_task != nullptr);
|
||||
|
||||
return rsp_callbacks.run_task(PASS_RDRAM task);
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ void ultramodern::check_running_queue(RDRAM_ARG1) {
|
|||
// Check if there are any threads in the running queue.
|
||||
if (!thread_queue_empty(PASS_RDRAM running_queue)) {
|
||||
// Check if the highest priority thread in the queue is higher priority than the current thread.
|
||||
OSThread* next_thread = TO_PTR(OSThread, ultramodern::thread_queue_peek(PASS_RDRAM running_queue));
|
||||
OSThread* self = TO_PTR(OSThread, ultramodern::this_thread());
|
||||
OSThread *next_thread = TO_PTR(OSThread, ultramodern::thread_queue_peek(PASS_RDRAM running_queue));
|
||||
OSThread *self = TO_PTR(OSThread, ultramodern::this_thread());
|
||||
if (next_thread->priority > self->priority) {
|
||||
ultramodern::thread_queue_pop(PASS_RDRAM running_queue);
|
||||
// Swap to the higher priority thread.
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
#ifdef _WIN32
|
||||
|
||||
#include <Windows.h>
|
||||
# include <Windows.h>
|
||||
|
||||
#include "ultramodern/ultra64.h"
|
||||
#include "ultramodern/ultramodern.hpp"
|
||||
# include "ultramodern/ultra64.h"
|
||||
# include "ultramodern/ultramodern.hpp"
|
||||
|
||||
extern "C" unsigned int sleep(unsigned int seconds) {
|
||||
Sleep(seconds * 1000);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
static PTR(OSThread) running_queue_impl = NULLPTR;
|
||||
|
||||
static PTR(OSThread)* queue_to_ptr(RDRAM_ARG PTR(PTR(OSThread)) queue) {
|
||||
static PTR(OSThread) *queue_to_ptr(RDRAM_ARG PTR(PTR(OSThread)) queue) {
|
||||
if (queue == ultramodern::running_queue) {
|
||||
return &running_queue_impl;
|
||||
}
|
||||
|
|
@ -12,8 +12,8 @@ static PTR(OSThread)* queue_to_ptr(RDRAM_ARG PTR(PTR(OSThread)) queue) {
|
|||
}
|
||||
|
||||
void ultramodern::thread_queue_insert(RDRAM_ARG PTR(PTR(OSThread)) queue_, PTR(OSThread) toadd_) {
|
||||
PTR(OSThread)* cur = queue_to_ptr(PASS_RDRAM queue_);
|
||||
OSThread* toadd = TO_PTR(OSThread, toadd_);
|
||||
PTR(OSThread) *cur = queue_to_ptr(PASS_RDRAM queue_);
|
||||
OSThread *toadd = TO_PTR(OSThread, toadd_);
|
||||
debug_printf("[Thread Queue] Inserting thread %d into queue 0x%08X\n", toadd->id, (uintptr_t)queue_);
|
||||
while (*cur && TO_PTR(OSThread, *cur)->priority > toadd->priority) {
|
||||
cur = &TO_PTR(OSThread, *cur)->next;
|
||||
|
|
@ -32,7 +32,7 @@ void ultramodern::thread_queue_insert(RDRAM_ARG PTR(PTR(OSThread)) queue_, PTR(O
|
|||
}
|
||||
|
||||
PTR(OSThread) ultramodern::thread_queue_pop(RDRAM_ARG PTR(PTR(OSThread)) queue_) {
|
||||
PTR(OSThread)* queue = queue_to_ptr(PASS_RDRAM queue_);
|
||||
PTR(OSThread) *queue = queue_to_ptr(PASS_RDRAM queue_);
|
||||
PTR(OSThread) ret = *queue;
|
||||
*queue = TO_PTR(OSThread, ret)->next;
|
||||
TO_PTR(OSThread, ret)->queue = NULLPTR;
|
||||
|
|
@ -45,7 +45,7 @@ bool ultramodern::thread_queue_remove(RDRAM_ARG PTR(PTR(OSThread)) queue_, PTR(O
|
|||
|
||||
PTR(PTR(OSThread)) cur = queue_;
|
||||
while (cur != NULLPTR) {
|
||||
PTR(OSThread)* cur_ptr = queue_to_ptr(PASS_RDRAM queue_);
|
||||
PTR(OSThread) *cur_ptr = queue_to_ptr(PASS_RDRAM queue_);
|
||||
if (*cur_ptr == t_) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -56,11 +56,11 @@ bool ultramodern::thread_queue_remove(RDRAM_ARG PTR(PTR(OSThread)) queue_, PTR(O
|
|||
}
|
||||
|
||||
bool ultramodern::thread_queue_empty(RDRAM_ARG PTR(PTR(OSThread)) queue_) {
|
||||
PTR(OSThread)* queue = queue_to_ptr(PASS_RDRAM queue_);
|
||||
PTR(OSThread) *queue = queue_to_ptr(PASS_RDRAM queue_);
|
||||
return *queue == NULLPTR;
|
||||
}
|
||||
|
||||
PTR(OSThread) ultramodern::thread_queue_peek(RDRAM_ARG PTR(PTR(OSThread)) queue_) {
|
||||
PTR(OSThread)* queue = queue_to_ptr(PASS_RDRAM queue_);
|
||||
PTR(OSThread) *queue = queue_to_ptr(PASS_RDRAM queue_);
|
||||
return *queue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
#include <cstdio>
|
||||
#include <thread>
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "blockingconcurrentqueue.h"
|
||||
#include "ultramodern/ultra64.h"
|
||||
#include "ultramodern/ultramodern.hpp"
|
||||
#include "blockingconcurrentqueue.h"
|
||||
|
||||
// Native APIs only used to set thread names for easier debugging
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
# include <Windows.h>
|
||||
#endif
|
||||
|
||||
extern "C" void bootproc();
|
||||
|
|
@ -37,20 +37,17 @@ int main(int argc, char** argv) {
|
|||
#endif
|
||||
|
||||
#if 1
|
||||
void run_thread_function(uint8_t* rdram, uint64_t addr, uint64_t sp, uint64_t arg);
|
||||
void run_thread_function(uint8_t *rdram, uint64_t addr, uint64_t sp, uint64_t arg);
|
||||
#else
|
||||
#define run_thread_function(func, sp, arg) func(arg)
|
||||
# define run_thread_function(func, sp, arg) func(arg)
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
void ultramodern::set_native_thread_name(const std::string& name) {
|
||||
std::wstring wname{name.begin(), name.end()};
|
||||
std::wstring wname{ name.begin(), name.end() };
|
||||
|
||||
HRESULT r;
|
||||
r = SetThreadDescription(
|
||||
GetCurrentThread(),
|
||||
wname.c_str()
|
||||
);
|
||||
r = SetThreadDescription(GetCurrentThread(), wname.c_str());
|
||||
}
|
||||
|
||||
void ultramodern::set_native_thread_priority(ThreadPriority pri) {
|
||||
|
|
@ -116,13 +113,14 @@ void ultramodern::set_native_thread_name(const std::string& name) {
|
|||
pthread_setname_np(name.c_str());
|
||||
}
|
||||
|
||||
void ultramodern::set_native_thread_priority(ThreadPriority pri) {}
|
||||
void ultramodern::set_native_thread_priority(ThreadPriority pri) {
|
||||
}
|
||||
#endif
|
||||
|
||||
std::atomic_int temporary_threads = 0;
|
||||
std::atomic_int permanent_threads = 0;
|
||||
|
||||
void wait_for_resumed(RDRAM_ARG UltraThreadContext* thread_context) {
|
||||
void wait_for_resumed(RDRAM_ARG UltraThreadContext *thread_context) {
|
||||
TO_PTR(OSThread, ultramodern::this_thread())->context->running.wait();
|
||||
// If this thread's context was replaced by another thread or deleted, destroy it again from its own context.
|
||||
// This will trigger thread cleanup instead.
|
||||
|
|
@ -131,7 +129,7 @@ void wait_for_resumed(RDRAM_ARG UltraThreadContext* thread_context) {
|
|||
}
|
||||
}
|
||||
|
||||
void resume_thread(OSThread* t) {
|
||||
void resume_thread(OSThread *t) {
|
||||
debug_printf("[Thread] Resuming execution of thread %d\n", t->id);
|
||||
t->context->running.signal();
|
||||
}
|
||||
|
|
@ -141,24 +139,24 @@ void run_next_thread(RDRAM_ARG1) {
|
|||
throw std::runtime_error("No threads left to run!\n");
|
||||
}
|
||||
|
||||
OSThread* to_run = TO_PTR(OSThread, ultramodern::thread_queue_pop(PASS_RDRAM ultramodern::running_queue));
|
||||
OSThread *to_run = TO_PTR(OSThread, ultramodern::thread_queue_pop(PASS_RDRAM ultramodern::running_queue));
|
||||
debug_printf("[Scheduling] Resuming execution of thread %d\n", to_run->id);
|
||||
to_run->context->running.signal();
|
||||
}
|
||||
|
||||
void ultramodern::run_next_thread_and_wait(RDRAM_ARG1) {
|
||||
UltraThreadContext* cur_context = TO_PTR(OSThread, thread_self)->context;
|
||||
UltraThreadContext *cur_context = TO_PTR(OSThread, thread_self)->context;
|
||||
run_next_thread(PASS_RDRAM1);
|
||||
wait_for_resumed(PASS_RDRAM cur_context);
|
||||
}
|
||||
|
||||
void ultramodern::resume_thread_and_wait(RDRAM_ARG OSThread *t) {
|
||||
UltraThreadContext* cur_context = TO_PTR(OSThread, thread_self)->context;
|
||||
UltraThreadContext *cur_context = TO_PTR(OSThread, thread_self)->context;
|
||||
resume_thread(t);
|
||||
wait_for_resumed(PASS_RDRAM cur_context);
|
||||
}
|
||||
|
||||
static void _thread_func(RDRAM_ARG PTR(OSThread) self_, PTR(thread_func_t) entrypoint, PTR(void) arg, UltraThreadContext* thread_context) {
|
||||
static void _thread_func(RDRAM_ARG PTR(OSThread) self_, PTR(thread_func_t) entrypoint, PTR(void) arg, UltraThreadContext *thread_context) {
|
||||
OSThread *self = TO_PTR(OSThread, self_);
|
||||
debug_printf("[Thread] Thread created: %d\n", self->id);
|
||||
thread_self = self_;
|
||||
|
|
@ -183,7 +181,7 @@ static void _thread_func(RDRAM_ARG PTR(OSThread) self_, PTR(thread_func_t) entry
|
|||
|
||||
// Wait until the thread is marked as running.
|
||||
wait_for_resumed(PASS_RDRAM thread_context);
|
||||
|
||||
|
||||
// Make sure the thread wasn't replaced or destroyed before it was started.
|
||||
if (self->context == thread_context) {
|
||||
debug_printf("[Thread] Thread started: %d\n", self->id);
|
||||
|
|
@ -206,7 +204,7 @@ static void _thread_func(RDRAM_ARG PTR(OSThread) self_, PTR(thread_func_t) entry
|
|||
|
||||
// Dispose of this thread now that it's completed or terminated.
|
||||
ultramodern::cleanup_thread(thread_context);
|
||||
|
||||
|
||||
// TODO fix these being hardcoded (this is only used for quicksaving)
|
||||
if ((self->id == 2 && self->priority == 5) || self->id == 13) { // slowly, flashrom
|
||||
temporary_threads.fetch_sub(1);
|
||||
|
|
@ -222,7 +220,7 @@ uint32_t ultramodern::temporary_thread_count() {
|
|||
}
|
||||
|
||||
extern "C" void osStartThread(RDRAM_ARG PTR(OSThread) t_) {
|
||||
OSThread* t = TO_PTR(OSThread, t_);
|
||||
OSThread *t = TO_PTR(OSThread, t_);
|
||||
debug_printf("[os] Start Thread %d\n", t->id);
|
||||
|
||||
// Wait until the thread is initialized to indicate that it's ready to be started.
|
||||
|
|
@ -239,14 +237,14 @@ extern "C" void osStartThread(RDRAM_ARG PTR(OSThread) t_) {
|
|||
else {
|
||||
t->state = OSThreadState::QUEUED;
|
||||
resume_thread(t);
|
||||
//throw ultramodern::thread_terminated{};
|
||||
// throw ultramodern::thread_terminated{};
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void osCreateThread(RDRAM_ARG PTR(OSThread) t_, OSId id, PTR(thread_func_t) entrypoint, PTR(void) arg, PTR(void) sp, OSPri pri) {
|
||||
debug_printf("[os] Create Thread %d\n", id);
|
||||
OSThread *t = TO_PTR(OSThread, t_);
|
||||
|
||||
|
||||
t->next = NULLPTR;
|
||||
t->queue = NULLPTR;
|
||||
t->priority = pri;
|
||||
|
|
@ -257,7 +255,7 @@ extern "C" void osCreateThread(RDRAM_ARG PTR(OSThread) t_, OSId id, PTR(thread_f
|
|||
// Spawn a new thread, which will immediately pause itself and wait until it's been started.
|
||||
// Pass the context as an argument to the thread function to ensure that it can't get cleared before the thread captures its value.
|
||||
t->context = new UltraThreadContext{};
|
||||
t->context->host_thread = std::thread{_thread_func, PASS_RDRAM t_, entrypoint, arg, t->context};
|
||||
t->context->host_thread = std::thread{ _thread_func, PASS_RDRAM t_, entrypoint, arg, t->context };
|
||||
}
|
||||
|
||||
extern "C" void osStopThread(RDRAM_ARG PTR(OSThread) t_) {
|
||||
|
|
@ -268,17 +266,17 @@ extern "C" void osDestroyThread(RDRAM_ARG PTR(OSThread) t_) {
|
|||
if (t_ == NULLPTR) {
|
||||
t_ = thread_self;
|
||||
}
|
||||
OSThread* t = TO_PTR(OSThread, t_);
|
||||
OSThread *t = TO_PTR(OSThread, t_);
|
||||
// Check if the thread is destroying itself (arg is null or thread_self)
|
||||
if (t_ == thread_self) {
|
||||
throw ultramodern::thread_terminated{};
|
||||
}
|
||||
// Otherwise if the thread isn't stopped, remove it from its currrent queue.,
|
||||
// Otherwise if the thread isn't stopped, remove it from its currrent queue.,
|
||||
if (t->state != OSThreadState::STOPPED) {
|
||||
ultramodern::thread_queue_remove(PASS_RDRAM t->queue, t_);
|
||||
}
|
||||
// Check if the thread has already been destroyed to prevent destroying it again.
|
||||
UltraThreadContext* cur_context = t->context;
|
||||
UltraThreadContext *cur_context = t->context;
|
||||
if (cur_context != nullptr) {
|
||||
// Mark the target thread as destroyed and resume it. When it starts it'll check this and terminate itself instead of resuming.
|
||||
t->context = nullptr;
|
||||
|
|
@ -290,7 +288,7 @@ extern "C" void osSetThreadPri(RDRAM_ARG PTR(OSThread) t_, OSPri pri) {
|
|||
if (t_ == NULLPTR) {
|
||||
t_ = thread_self;
|
||||
}
|
||||
OSThread* t = TO_PTR(OSThread, t_);
|
||||
OSThread *t = TO_PTR(OSThread, t_);
|
||||
|
||||
if (t->priority != pri) {
|
||||
t->priority = pri;
|
||||
|
|
@ -323,13 +321,13 @@ PTR(OSThread) ultramodern::this_thread() {
|
|||
}
|
||||
|
||||
static std::thread thread_cleaner_thread;
|
||||
static moodycamel::BlockingConcurrentQueue<UltraThreadContext*> deleted_threads{};
|
||||
static moodycamel::BlockingConcurrentQueue<UltraThreadContext *> deleted_threads{};
|
||||
extern std::atomic_bool exited;
|
||||
|
||||
void thread_cleaner_func() {
|
||||
using namespace std::chrono_literals;
|
||||
while (!exited) {
|
||||
UltraThreadContext* to_delete;
|
||||
UltraThreadContext *to_delete;
|
||||
if (deleted_threads.wait_dequeue_timed(to_delete, 10ms)) {
|
||||
debug_printf("[Cleanup] Deleting thread context %p\n", to_delete);
|
||||
|
||||
|
|
@ -340,7 +338,7 @@ void thread_cleaner_func() {
|
|||
}
|
||||
|
||||
void ultramodern::init_thread_cleanup() {
|
||||
thread_cleaner_thread = std::thread{thread_cleaner_func};
|
||||
thread_cleaner_thread = std::thread{ thread_cleaner_func };
|
||||
}
|
||||
|
||||
void ultramodern::cleanup_thread(UltraThreadContext *cur_context) {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
#include "blockingconcurrentqueue.h"
|
||||
#include <set>
|
||||
#include <thread>
|
||||
#include <variant>
|
||||
#include <set>
|
||||
#include "blockingconcurrentqueue.h"
|
||||
|
||||
#include "ultramodern/ultra64.h"
|
||||
#include "ultramodern/ultramodern.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include "Windows.h"
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# include "Windows.h"
|
||||
#endif
|
||||
|
||||
// Start time for the program
|
||||
|
|
@ -70,8 +70,8 @@ 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_);
|
||||
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) {
|
||||
|
|
@ -83,14 +83,15 @@ void timer_thread(RDRAM_ARG1) {
|
|||
};
|
||||
|
||||
// Ordered set of timers that are currently active
|
||||
std::set<PTR(OSTimer), decltype(timer_sort)> active_timers{timer_sort};
|
||||
|
||||
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)) {
|
||||
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)) {
|
||||
}
|
||||
else if (const auto *remove_action = std::get_if<RemoveTimerAction>(&action)) {
|
||||
active_timers.erase(remove_action->timer);
|
||||
}
|
||||
};
|
||||
|
|
@ -110,7 +111,7 @@ void timer_thread(RDRAM_ARG1) {
|
|||
|
||||
// Get the timer that's closest to running out
|
||||
PTR(OSTimer) cur_timer_ = *active_timers.begin();
|
||||
OSTimer* cur_timer = TO_PTR(OSTimer, cur_timer_);
|
||||
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_);
|
||||
|
|
@ -120,7 +121,7 @@ void timer_thread(RDRAM_ARG1) {
|
|||
|
||||
// Wait for either the duration to complete or a new action to come through
|
||||
if (wait_duration.count() >= 0 && timer_context.action_queue.wait_dequeue_timed(cur_action, wait_duration)) {
|
||||
// Timer was interrupted by a new action
|
||||
// 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
|
||||
|
|
@ -169,13 +170,14 @@ extern "C" OSTime osGetTime() {
|
|||
}
|
||||
|
||||
extern "C" int osSetTimer(RDRAM_ARG PTR(OSTimer) t_, OSTime countdown, OSTime interval, PTR(OSMesgQueue) mq, OSMesg msg) {
|
||||
OSTimer* t = TO_PTR(OSTimer, t_);
|
||||
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 {
|
||||
}
|
||||
else {
|
||||
t->timestamp = countdown + time_now();
|
||||
}
|
||||
t->interval = interval;
|
||||
|
|
@ -214,7 +216,7 @@ void ultramodern::sleep_until(const std::chrono::high_resolution_clock::time_poi
|
|||
#else
|
||||
|
||||
void ultramodern::sleep_milliseconds(uint32_t millis) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds{millis});
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds{ millis });
|
||||
}
|
||||
|
||||
void ultramodern::sleep_until(const std::chrono::high_resolution_clock::time_point& time_point) {
|
||||
|
|
|
|||
|
|
@ -2,14 +2,9 @@
|
|||
#include "ultramodern/ultramodern.hpp"
|
||||
|
||||
void ultramodern::set_callbacks(
|
||||
const rsp::callbacks_t& rsp_callbacks,
|
||||
const renderer::callbacks_t& renderer_callbacks,
|
||||
const audio_callbacks_t& audio_callbacks,
|
||||
const input::callbacks_t& input_callbacks,
|
||||
const gfx_callbacks_t& gfx_callbacks,
|
||||
const events::callbacks_t& thread_callbacks,
|
||||
const error_handling::callbacks_t& error_handling_callbacks
|
||||
) {
|
||||
const rsp::callbacks_t& rsp_callbacks, const renderer::callbacks_t& renderer_callbacks, const audio_callbacks_t& audio_callbacks,
|
||||
const input::callbacks_t& input_callbacks, const gfx_callbacks_t& gfx_callbacks, const events::callbacks_t& thread_callbacks,
|
||||
const error_handling::callbacks_t& error_handling_callbacks) {
|
||||
ultramodern::rsp::set_callbacks(rsp_callbacks);
|
||||
ultramodern::renderer::set_callbacks(renderer_callbacks);
|
||||
ultramodern::set_audio_callbacks(audio_callbacks);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue