mirror of
https://github.com/Zelda64Recomp/Zelda64Recomp
synced 2026-07-10 15:14:20 -04:00
More WIP linux work, upgraded libultra to include changes from BT recomp
This commit is contained in:
+41
-2
@@ -1,5 +1,44 @@
|
||||
#include "recomp.h"
|
||||
|
||||
extern "C" void osDpSetNextBuffer_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
;
|
||||
enum class RDPStatusBit {
|
||||
XbusDmem = 0,
|
||||
Freeze = 1,
|
||||
Flush = 2,
|
||||
CommandBusy = 6,
|
||||
BufferReady = 7,
|
||||
DmaBusy = 8,
|
||||
EndValid = 9,
|
||||
StartValid = 10,
|
||||
};
|
||||
|
||||
constexpr void update_bit(uint32_t& state, uint32_t flags, RDPStatusBit bit) {
|
||||
int set_bit_pos = (int)bit * 2 + 0;
|
||||
int reset_bit_pos = (int)bit * 2 + 1;
|
||||
bool set = (flags & (1U << set_bit_pos)) != 0;
|
||||
bool reset = (flags & (1U << reset_bit_pos)) != 0;
|
||||
|
||||
if (set ^ reset) {
|
||||
if (set) {
|
||||
state |= (1U << (int)bit);
|
||||
}
|
||||
else {
|
||||
state &= ~(1U << (int)bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t rdp_state = 1 << (int)RDPStatusBit::BufferReady;
|
||||
|
||||
extern "C" void osDpSetNextBuffer_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
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) {
|
||||
update_bit(rdp_state, ctx->r4, RDPStatusBit::XbusDmem);
|
||||
update_bit(rdp_state, ctx->r4, RDPStatusBit::Freeze);
|
||||
update_bit(rdp_state, ctx->r4, RDPStatusBit::Flush);
|
||||
}
|
||||
|
||||
+33
-5
@@ -1,21 +1,49 @@
|
||||
#include "recomp.h"
|
||||
#include "../portultra/ultra64.h"
|
||||
|
||||
void save_write(uint8_t* rdram, gpr rdram_address, uint32_t offset, uint32_t count);
|
||||
void save_read(uint8_t* rdram, gpr rdram_address, uint32_t offset, uint32_t count);
|
||||
|
||||
constexpr int eeprom_block_size = 8;
|
||||
constexpr int eep4_size = 4096;
|
||||
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) {
|
||||
;
|
||||
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 osEepromLongWrite_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
;
|
||||
uint8_t eep_address = ctx->r5;
|
||||
gpr buffer = ctx->r6;
|
||||
int32_t nbytes = ctx->r7;
|
||||
|
||||
assert(!(nbytes & 7));
|
||||
assert(eep_address * eeprom_block_size + nbytes <= eep16_size);
|
||||
|
||||
save_write(rdram, buffer, eep_address * eeprom_block_size, nbytes);
|
||||
|
||||
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 osEepromLongRead_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
;
|
||||
uint8_t eep_address = ctx->r5;
|
||||
gpr buffer = ctx->r6;
|
||||
int32_t nbytes = ctx->r7;
|
||||
|
||||
assert(!(nbytes & 7));
|
||||
assert(eep_address * eeprom_block_size + nbytes <= eep16_size);
|
||||
|
||||
save_read(rdram, buffer, eep_address * eeprom_block_size, nbytes);
|
||||
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,366 @@
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "../../portultra/ultra64.h"
|
||||
#include "../../portultra/multilibultra.hpp"
|
||||
#define SDL_MAIN_HANDLED
|
||||
#ifdef _WIN32
|
||||
#include "SDL.h"
|
||||
#else
|
||||
#include "SDL2/SDL.h"
|
||||
#include "SDL2/SDL_syswm.h"
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#define NOMINMAX
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
#include "SDL_syswm.h"
|
||||
#endif
|
||||
|
||||
extern "C" void init();
|
||||
/*extern "C"*/ void start(Multilibultra::WindowHandle window_handle, const Multilibultra::audio_callbacks_t* audio_callbacks, const Multilibultra::input_callbacks_t* input_callbacks);
|
||||
|
||||
template<typename... Ts>
|
||||
void exit_error(const char* str, Ts ...args) {
|
||||
// TODO pop up an error
|
||||
((void)fprintf(stderr, str, args), ...);
|
||||
assert(false);
|
||||
std::quick_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::pair<SDL_Scancode, int>> keyboard_button_map{
|
||||
{ SDL_Scancode::SDL_SCANCODE_LEFT, 0x0002 }, // c left
|
||||
{ SDL_Scancode::SDL_SCANCODE_RIGHT, 0x0001 }, // c right
|
||||
{ SDL_Scancode::SDL_SCANCODE_UP, 0x0008 }, // c up
|
||||
{ SDL_Scancode::SDL_SCANCODE_DOWN, 0x0004 }, // c down
|
||||
{ SDL_Scancode::SDL_SCANCODE_RETURN, 0x1000 }, // start
|
||||
{ SDL_Scancode::SDL_SCANCODE_SPACE, 0x8000 }, // a
|
||||
{ SDL_Scancode::SDL_SCANCODE_LSHIFT, 0x4000 }, // b
|
||||
{ SDL_Scancode::SDL_SCANCODE_Q, 0x2000 }, // z
|
||||
{ SDL_Scancode::SDL_SCANCODE_E, 0x0020 }, // l
|
||||
{ SDL_Scancode::SDL_SCANCODE_R, 0x0010 }, // r
|
||||
{ SDL_Scancode::SDL_SCANCODE_J, 0x0200 }, // dpad left
|
||||
{ SDL_Scancode::SDL_SCANCODE_L, 0x0100 }, // dpad right
|
||||
{ SDL_Scancode::SDL_SCANCODE_I, 0x0800 }, // dpad up
|
||||
{ SDL_Scancode::SDL_SCANCODE_K, 0x0400 }, // dpad down
|
||||
};
|
||||
|
||||
struct GameControllerAxisMapping {
|
||||
SDL_GameControllerAxis axis;
|
||||
int threshold; // Positive or negative to indicate direction
|
||||
uint16_t output_mask;
|
||||
};
|
||||
|
||||
constexpr int controller_default_threshold = 20000;
|
||||
|
||||
std::vector<GameControllerAxisMapping> controller_axis_map{
|
||||
{ SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTX, -controller_default_threshold, 0x0002 }, // c left
|
||||
{ SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTX, controller_default_threshold, 0x0001 }, // c right
|
||||
{ SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTY, -controller_default_threshold, 0x0008 }, // c up
|
||||
{ SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTY, controller_default_threshold, 0x0004 }, // c down
|
||||
{ SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_TRIGGERLEFT, 10000, 0x2000 }, // z
|
||||
//{ SDL_Scancode::SDL_SCANCODE_RIGHT, 0x0001 }, // c right
|
||||
//{ SDL_Scancode::SDL_SCANCODE_UP, 0x0008 }, // c up
|
||||
//{ SDL_Scancode::SDL_SCANCODE_DOWN, 0x0004 }, // c down
|
||||
//{ SDL_Scancode::SDL_SCANCODE_RETURN, 0x1000 }, // start
|
||||
//{ SDL_Scancode::SDL_SCANCODE_SPACE, 0x8000 }, // a
|
||||
//{ SDL_Scancode::SDL_SCANCODE_LSHIFT, 0x4000 }, // b
|
||||
//{ SDL_Scancode::SDL_SCANCODE_Q, 0x2000 }, // z
|
||||
//{ SDL_Scancode::SDL_SCANCODE_E, 0x0020 }, // l
|
||||
//{ SDL_Scancode::SDL_SCANCODE_R, 0x0010 }, // r
|
||||
//{ SDL_Scancode::SDL_SCANCODE_J, 0x0200 }, // dpad left
|
||||
//{ SDL_Scancode::SDL_SCANCODE_L, 0x0100 }, // dpad right
|
||||
//{ SDL_Scancode::SDL_SCANCODE_I, 0x0800 }, // dpad up
|
||||
//{ SDL_Scancode::SDL_SCANCODE_K, 0x0400 }, // dpad down
|
||||
};
|
||||
|
||||
struct GameControllerButtonMapping {
|
||||
SDL_GameControllerButton button;
|
||||
uint16_t output_mask;
|
||||
};
|
||||
std::vector<GameControllerButtonMapping> controller_button_map{
|
||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_START, 0x1000 }, // start
|
||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_A, 0x8000 }, // a
|
||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_B, 0x4000 }, // b
|
||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_X, 0x4000 }, // b
|
||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_LEFTSHOULDER, 0x0020 }, // l
|
||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, 0x0010 }, // r
|
||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_LEFT, 0x0200 }, // dpad left
|
||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_RIGHT, 0x0100 }, // dpad right
|
||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_UP, 0x0800 }, // dpad up
|
||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_DOWN, 0x0400 }, // dpad down
|
||||
};
|
||||
|
||||
std::vector<SDL_JoystickID> controllers{};
|
||||
|
||||
int sdl_event_filter(void* userdata, SDL_Event* event) {
|
||||
switch (event->type) {
|
||||
//case SDL_EventType::SDL_KEYUP:
|
||||
//case SDL_EventType::SDL_KEYDOWN:
|
||||
// {
|
||||
// const Uint8* key_states = SDL_GetKeyboardState(nullptr);
|
||||
// int new_button = 0;
|
||||
|
||||
// for (const auto& mapping : keyboard_button_map) {
|
||||
// if (key_states[mapping.first]) {
|
||||
// new_button |= mapping.second;
|
||||
// }
|
||||
// }
|
||||
|
||||
// button = new_button;
|
||||
|
||||
// stick_x = (100.0f / 100.0f) * (key_states[SDL_Scancode::SDL_SCANCODE_D] - key_states[SDL_Scancode::SDL_SCANCODE_A]);
|
||||
// stick_y = (100.0f / 100.0f) * (key_states[SDL_Scancode::SDL_SCANCODE_W] - key_states[SDL_Scancode::SDL_SCANCODE_S]);
|
||||
// }
|
||||
// break;
|
||||
case SDL_EventType::SDL_CONTROLLERDEVICEADDED:
|
||||
{
|
||||
SDL_ControllerDeviceEvent* controller_event = (SDL_ControllerDeviceEvent*)event;
|
||||
SDL_GameController* controller = SDL_GameControllerOpen(controller_event->which);
|
||||
printf("Controller added: %d\n", controller_event->which);
|
||||
if (controller != nullptr) {
|
||||
printf(" Instance ID: %d\n", SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller)));
|
||||
controllers.push_back(SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller)));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SDL_EventType::SDL_CONTROLLERDEVICEREMOVED:
|
||||
{
|
||||
SDL_ControllerDeviceEvent* controller_event = (SDL_ControllerDeviceEvent*)event;
|
||||
printf("Controller removed: %d\n", controller_event->which);
|
||||
std::remove(controllers.begin(), controllers.end(), controller_event->which);
|
||||
}
|
||||
break;
|
||||
case SDL_EventType::SDL_QUIT:
|
||||
std::quick_exit(EXIT_SUCCESS);
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
Multilibultra::gfx_callbacks_t::gfx_data_t create_gfx() {
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) > 0) {
|
||||
exit_error("Failed to initialize SDL2: %s\n", SDL_GetError());
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Multilibultra::WindowHandle create_window(Multilibultra::gfx_callbacks_t::gfx_data_t) {
|
||||
SDL_Window* window = SDL_CreateWindow("Majora's Mask", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_RESIZABLE);
|
||||
|
||||
if (window == nullptr) {
|
||||
exit_error("Failed to create window: %s\n", SDL_GetError());
|
||||
}
|
||||
|
||||
SDL_SysWMinfo wmInfo;
|
||||
SDL_VERSION(&wmInfo.version);
|
||||
SDL_GetWindowWMInfo(window, &wmInfo);
|
||||
|
||||
#if defined(_WIN32)
|
||||
return wmInfo.info.win.window;
|
||||
#elif defined(__ANDROID__)
|
||||
static_assert(false && "Unimplemented");
|
||||
#elif defined(__linux__)
|
||||
return Multilibultra::WindowHandle{ wmInfo.info.x11.display, wmInfo.info.x11.window };
|
||||
#else
|
||||
static_assert(false && "Unimplemented");
|
||||
#endif
|
||||
}
|
||||
|
||||
void update_gfx(void*) {
|
||||
// Handle events
|
||||
constexpr int max_events_per_frame = 16;
|
||||
SDL_Event cur_event;
|
||||
int i = 0;
|
||||
while (i++ < max_events_per_frame && SDL_PollEvent(&cur_event)) {
|
||||
sdl_event_filter(nullptr, &cur_event);
|
||||
}
|
||||
}
|
||||
|
||||
void get_input(uint16_t* buttons_out, float* x_out, float* y_out) {
|
||||
uint16_t cur_buttons = 0;
|
||||
float cur_x = 0.0f;
|
||||
float cur_y = 0.0f;
|
||||
|
||||
const Uint8* key_states = SDL_GetKeyboardState(nullptr);
|
||||
int new_button = 0;
|
||||
|
||||
for (const auto& mapping : keyboard_button_map) {
|
||||
if (key_states[mapping.first]) {
|
||||
cur_buttons |= mapping.second;
|
||||
}
|
||||
}
|
||||
|
||||
cur_x += (100.0f / 100.0f) * (key_states[SDL_Scancode::SDL_SCANCODE_D] - key_states[SDL_Scancode::SDL_SCANCODE_A]);
|
||||
cur_y += (100.0f / 100.0f) * (key_states[SDL_Scancode::SDL_SCANCODE_W] - key_states[SDL_Scancode::SDL_SCANCODE_S]);
|
||||
|
||||
for (SDL_JoystickID controller_id : controllers) {
|
||||
SDL_GameController* controller = SDL_GameControllerFromInstanceID(controller_id);
|
||||
if (controller != nullptr) {
|
||||
cur_x += SDL_GameControllerGetAxis(controller, SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_LEFTX) * (1/32768.0f);
|
||||
cur_y -= SDL_GameControllerGetAxis(controller, SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_LEFTY) * (1/32768.0f);
|
||||
}
|
||||
|
||||
for (const auto& mapping : controller_axis_map) {
|
||||
int input_value = SDL_GameControllerGetAxis(controller, mapping.axis);
|
||||
if (mapping.threshold > 0) {
|
||||
if (input_value > mapping.threshold) {
|
||||
cur_buttons |= mapping.output_mask;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (input_value < mapping.threshold) {
|
||||
cur_buttons |= mapping.output_mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& mapping : controller_button_map) {
|
||||
int input_value = SDL_GameControllerGetButton(controller, mapping.button);
|
||||
if (input_value) {
|
||||
cur_buttons |= mapping.output_mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*buttons_out = cur_buttons;
|
||||
cur_x = std::clamp(cur_x, -1.0f, 1.0f);
|
||||
cur_y = std::clamp(cur_y, -1.0f, 1.0f);
|
||||
*x_out = cur_x;
|
||||
*y_out = cur_y;
|
||||
}
|
||||
|
||||
static SDL_AudioDeviceID audio_device = 0;
|
||||
static uint32_t sample_rate = 48000;
|
||||
|
||||
void queue_samples(int16_t* audio_data, size_t sample_count) {
|
||||
// Buffer for holding the output of swapping the audio channels. This is reused across
|
||||
// calls to reduce runtime allocations.
|
||||
static std::vector<float> swap_buffer;
|
||||
|
||||
// Make sure the swap buffer is large enough to hold all the incoming audio data.
|
||||
if (sample_count > swap_buffer.size()) {
|
||||
swap_buffer.resize(sample_count);
|
||||
}
|
||||
|
||||
// Convert the audio from 16-bit values to floats and swap the audio channels into the
|
||||
// swap buffer to correct for the address xor caused by endianness handling.
|
||||
for (size_t i = 0; i < sample_count; i += 2) {
|
||||
swap_buffer[i + 0] = audio_data[i + 1] * (0.5f / 32768.0f);
|
||||
swap_buffer[i + 1] = audio_data[i + 0] * (0.5f / 32768.0f);
|
||||
}
|
||||
|
||||
// Queue the swapped audio data.
|
||||
SDL_QueueAudio(audio_device, swap_buffer.data(), sample_count * sizeof(swap_buffer[0]));
|
||||
}
|
||||
|
||||
constexpr int channel_count = 2;
|
||||
constexpr int bytes_per_frame = channel_count * sizeof(float);
|
||||
|
||||
size_t get_frames_remaining() {
|
||||
constexpr float buffer_offset_frames = 1.0f;
|
||||
// Get the number of remaining buffered audio bytes.
|
||||
uint32_t buffered_byte_count = SDL_GetQueuedAudioSize(audio_device);
|
||||
|
||||
// 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 frames_per_vi = (sample_rate / 60);
|
||||
if (buffered_byte_count > (buffer_offset_frames * bytes_per_frame * frames_per_vi)) {
|
||||
buffered_byte_count -= (buffer_offset_frames * bytes_per_frame * frames_per_vi);
|
||||
}
|
||||
else {
|
||||
buffered_byte_count = 0;
|
||||
}
|
||||
// Convert from byte count to sample count.
|
||||
return buffered_byte_count / bytes_per_frame;
|
||||
}
|
||||
|
||||
void set_frequency(uint32_t freq) {
|
||||
if (audio_device != 0) {
|
||||
SDL_CloseAudioDevice(audio_device);
|
||||
}
|
||||
SDL_AudioSpec spec_desired{
|
||||
.freq = (int)freq,
|
||||
.format = AUDIO_F32,
|
||||
.channels = channel_count,
|
||||
.silence = 0, // calculated
|
||||
.samples = 0x100, // Fairly small sample count to reduce the latency of internal buffering
|
||||
.padding = 0, // unused
|
||||
.size = 0, // calculated
|
||||
.callback = nullptr,//feed_audio, // Use a callback as QueueAudio causes popping
|
||||
.userdata = nullptr
|
||||
};
|
||||
|
||||
audio_device = SDL_OpenAudioDevice(nullptr, false, &spec_desired, nullptr, 0);
|
||||
if (audio_device == 0) {
|
||||
exit_error("SDL error opening audio device: %s\n", SDL_GetError());
|
||||
}
|
||||
SDL_PauseAudioDevice(audio_device, 0);
|
||||
sample_rate = freq;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
#ifdef _WIN32
|
||||
// Set up console output to accept UTF-8 on windows
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
|
||||
// Change to a font that supports Japanese characters
|
||||
CONSOLE_FONT_INFOEX cfi;
|
||||
cfi.cbSize = sizeof cfi;
|
||||
cfi.nFont = 0;
|
||||
cfi.dwFontSize.X = 0;
|
||||
cfi.dwFontSize.Y = 16;
|
||||
cfi.FontFamily = FF_DONTCARE;
|
||||
cfi.FontWeight = FW_NORMAL;
|
||||
wcscpy_s(cfi.FaceName, L"NSimSun");
|
||||
SetCurrentConsoleFontEx(GetStdHandle(STD_OUTPUT_HANDLE), FALSE, &cfi);
|
||||
#else
|
||||
std::setlocale(LC_ALL, "en_US.UTF-8");
|
||||
#endif
|
||||
|
||||
// Initialize SDL audio.
|
||||
SDL_InitSubSystem(SDL_INIT_AUDIO);
|
||||
// Pick an initial dummy sample rate; this will be set by the game later to the true sample rate.
|
||||
set_frequency(sample_rate);
|
||||
|
||||
init();
|
||||
|
||||
Multilibultra::gfx_callbacks_t gfx_callbacks{
|
||||
.create_gfx = create_gfx,
|
||||
.create_window = create_window,
|
||||
.update_gfx = update_gfx,
|
||||
};
|
||||
|
||||
Multilibultra::audio_callbacks_t audio_callbacks{
|
||||
.queue_samples = queue_samples,
|
||||
.get_frames_remaining = get_frames_remaining,
|
||||
.set_frequency = set_frequency,
|
||||
};
|
||||
|
||||
Multilibultra::input_callbacks_t input_callbacks{
|
||||
.get_input = get_input,
|
||||
};
|
||||
|
||||
//create_gfx();
|
||||
//void* window_handle = create_window(nullptr);
|
||||
|
||||
Multilibultra::set_gfx_callbacks(&gfx_callbacks);
|
||||
start(Multilibultra::WindowHandle{}, &audio_callbacks, &input_callbacks);
|
||||
|
||||
// Do nothing forever
|
||||
while (1) {
|
||||
using namespace std::chrono_literals;
|
||||
std::this_thread::sleep_for(10ms);
|
||||
//update_gfx(nullptr);
|
||||
//std::this_thread::sleep_for(1ms);
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
+39
-1
@@ -59,6 +59,42 @@ 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);
|
||||
|
||||
extern "C" void unload_overlay_by_id(uint32_t id) {
|
||||
uint32_t section_table_index = overlay_sections_by_index[id];
|
||||
const SectionTableEntry& section = section_table[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
|
||||
for (size_t func_index = 0; func_index < section.num_funcs; func_index++) {
|
||||
const auto& func = section.funcs[func_index];
|
||||
uint32_t func_address = func.offset + find_it->loaded_ram_addr;
|
||||
func_map.erase(func_address);
|
||||
}
|
||||
// Reset the section's address in the address table
|
||||
section_addresses[section.index] = section.ram_addr;
|
||||
// Remove the section from the loaded section map
|
||||
loaded_sections.erase(find_it);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void load_overlay_by_id(uint32_t id, uint32_t ram_addr) {
|
||||
uint32_t section_table_index = overlay_sections_by_index[id];
|
||||
const SectionTableEntry& section = section_table[section_table_index];
|
||||
int32_t prev_address = section_addresses[section.index];
|
||||
if (/*ram_addr >= 0x80000000 && ram_addr < 0x81000000) {*/ prev_address == section.ram_addr) {
|
||||
load_overlay(section_table_index, ram_addr);
|
||||
}
|
||||
else {
|
||||
int32_t new_address = prev_address + ram_addr;
|
||||
unload_overlay_by_id(id);
|
||||
load_overlay(section_table_index, new_address);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void unload_overlays(int32_t ram_addr, uint32_t size) {
|
||||
for (auto it = loaded_sections.begin(); it != loaded_sections.end();) {
|
||||
const auto& section = section_table[it->section_table_index];
|
||||
@@ -72,6 +108,7 @@ extern "C" void unload_overlays(int32_t ram_addr, uint32_t size) {
|
||||
" 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);
|
||||
assert(false);
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
// Determine where each function was loaded to and remove that entry from the function map
|
||||
@@ -81,7 +118,7 @@ extern "C" void unload_overlays(int32_t ram_addr, uint32_t size) {
|
||||
func_map.erase(func_address);
|
||||
}
|
||||
// Reset the section's address in the address table
|
||||
section_addresses[section.index] = 0;
|
||||
section_addresses[section.index] = section.ram_addr;
|
||||
// Remove the section from the loaded section map
|
||||
it = loaded_sections.erase(it);
|
||||
// Skip incrementing the iterator
|
||||
@@ -108,6 +145,7 @@ 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);
|
||||
assert(false);
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
return func_find->second;
|
||||
|
||||
+1
-1
@@ -138,7 +138,7 @@ extern "C" void osPiStartDma_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
uint32_t mb = ctx->r4;
|
||||
uint32_t pri = ctx->r5;
|
||||
uint32_t direction = ctx->r6;
|
||||
uint32_t devAddr = ctx->r7;
|
||||
uint32_t devAddr = ctx->r7 | rom_base;
|
||||
gpr dramAddr = MEM_W(0x10, ctx->r29);
|
||||
uint32_t size = MEM_W(0x14, ctx->r29);
|
||||
PTR(OSMesgQueue) mq = MEM_W(0x18, ctx->r29);
|
||||
|
||||
@@ -28,6 +28,10 @@ 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) {
|
||||
osYieldThread(rdram);
|
||||
}
|
||||
|
||||
extern "C" void osSetThreadPri_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
osSetThreadPri(rdram, (int32_t)ctx->r4, (OSPri)ctx->r5);
|
||||
}
|
||||
@@ -85,7 +89,7 @@ extern "C" void osStopTimer_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
}
|
||||
|
||||
extern "C" void osVirtualToPhysical_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
ctx->r2 = osVirtualToPhysical((int32_t)ctx->r2);
|
||||
ctx->r2 = osVirtualToPhysical((int32_t)ctx->r4);
|
||||
}
|
||||
|
||||
extern "C" void osInvalDCache_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
|
||||
+81
-33
@@ -1,4 +1,4 @@
|
||||
#ifdef _WIN32
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
#include <cstdio>
|
||||
@@ -41,13 +41,58 @@ extern "C" void osGetMemSize_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
ctx->r2 = 8 * 1024 * 1024;
|
||||
}
|
||||
|
||||
enum class StatusReg {
|
||||
FR = 0x04000000,
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
// Check if the FR bit changed
|
||||
if (changed & (uint32_t)StatusReg::FR) {
|
||||
// Check if the FR bit was set
|
||||
if (new_sr & (uint32_t)StatusReg::FR) {
|
||||
// FR = 1, odd single floats point to their own registers
|
||||
ctx->f_odd = &ctx->f1.u32l;
|
||||
ctx->mips3_float_mode = true;
|
||||
}
|
||||
// Otherwise, it was cleared
|
||||
else {
|
||||
// FR = 0, odd single floats point to the upper half of the previous register
|
||||
ctx->f_odd = &ctx->f0.u32h;
|
||||
ctx->mips3_float_mode = false;
|
||||
}
|
||||
|
||||
// Remove the FR bit from the changed bits as it's been handled
|
||||
changed &= ~(uint32_t)StatusReg::FR;
|
||||
}
|
||||
|
||||
// If any other bits were changed, assert false as they're not handled currently
|
||||
if (changed) {
|
||||
printf("Unhandled status register bits changed: 0x%08X\n", changed);
|
||||
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) {
|
||||
return (gpr)(int32_t)ctx->status_reg;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
extern "C" void do_break(uint32_t vram) {
|
||||
printf("Encountered break at original vram 0x%08X\n", vram);
|
||||
assert(false);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@@ -55,6 +100,8 @@ void run_thread_function(uint8_t* rdram, uint64_t addr, uint64_t sp, uint64_t ar
|
||||
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);
|
||||
func(rdram, &ctx);
|
||||
}
|
||||
@@ -72,10 +119,6 @@ void init_overlays();
|
||||
extern "C" void load_overlays(uint32_t rom, int32_t ram_addr, uint32_t size);
|
||||
extern "C" void unload_overlays(int32_t ram_addr, uint32_t size);
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
std::unique_ptr<uint8_t[]> rdram_buffer;
|
||||
recomp_context context{};
|
||||
|
||||
@@ -124,6 +167,10 @@ EXPORT extern "C" void init() {
|
||||
// Set up stack pointer
|
||||
context.r29 = 0xFFFFFFFF803FFFF0u;
|
||||
|
||||
// Set up context floats
|
||||
context.f_odd = &context.f0.u32h;
|
||||
context.mips3_float_mode = false;
|
||||
|
||||
// Initialize variables normally set by IPL3
|
||||
constexpr int32_t osTvType = 0x80000300;
|
||||
constexpr int32_t osRomType = 0x80000304;
|
||||
@@ -140,10 +187,37 @@ EXPORT extern "C" void init() {
|
||||
MEM_W(osMemSize, 0) = 8 * 1024 * 1024; // 8MB
|
||||
}
|
||||
|
||||
EXPORT extern "C" void start(void* window_handle, const Multilibultra::audio_callbacks_t* audio_callbacks, const Multilibultra::input_callbacks_t* input_callbacks) {
|
||||
// LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
||||
// return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||||
// }
|
||||
|
||||
/*EXPORT extern "C"*/ void start(Multilibultra::WindowHandle window_handle, const Multilibultra::audio_callbacks_t* audio_callbacks, const Multilibultra::input_callbacks_t* input_callbacks) {
|
||||
Multilibultra::set_audio_callbacks(audio_callbacks);
|
||||
Multilibultra::set_input_callbacks(input_callbacks);
|
||||
std::thread game_thread{[](void* window_handle) {
|
||||
|
||||
//// Register window class.
|
||||
//WNDCLASS wc;
|
||||
//memset(&wc, 0, sizeof(WNDCLASS));
|
||||
//wc.lpfnWndProc = WindowProc;
|
||||
//wc.hInstance = GetModuleHandle(0);
|
||||
//wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND);
|
||||
//wc.lpszClassName = "RT64Sample";
|
||||
//RegisterClass(&wc);
|
||||
|
||||
//// Create window.
|
||||
//const int Width = 1280;
|
||||
//const int Height = 720;
|
||||
//RECT rect;
|
||||
//UINT dwStyle = WS_OVERLAPPEDWINDOW | WS_VISIBLE;
|
||||
//rect.left = (GetSystemMetrics(SM_CXSCREEN) - Width) / 2;
|
||||
//rect.top = (GetSystemMetrics(SM_CYSCREEN) - Height) / 2;
|
||||
//rect.right = rect.left + Width;
|
||||
//rect.bottom = rect.top + Height;
|
||||
//AdjustWindowRectEx(&rect, dwStyle, 0, 0);
|
||||
|
||||
//HWND hwnd = CreateWindow(wc.lpszClassName, "Recomp", dwStyle, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, 0, 0, wc.hInstance, NULL);
|
||||
|
||||
std::thread game_thread{[](Multilibultra::WindowHandle window_handle) {
|
||||
debug_printf("[Recomp] Starting\n");
|
||||
|
||||
Multilibultra::set_native_thread_name("Game Start Thread");
|
||||
@@ -157,29 +231,3 @@ EXPORT extern "C" void start(void* window_handle, const Multilibultra::audio_cal
|
||||
|
||||
game_thread.detach();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
#ifdef _WIN32
|
||||
// Set up console output to accept UTF-8 on windows
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
|
||||
// Change to a font that supports Japanese characters
|
||||
CONSOLE_FONT_INFOEX cfi;
|
||||
cfi.cbSize = sizeof cfi;
|
||||
cfi.nFont = 0;
|
||||
cfi.dwFontSize.X = 0;
|
||||
cfi.dwFontSize.Y = 16;
|
||||
cfi.FontFamily = FF_DONTCARE;
|
||||
cfi.FontWeight = FW_NORMAL;
|
||||
wcscpy_s(cfi.FaceName, L"NSimSun");
|
||||
SetCurrentConsoleFontEx(GetStdHandle(STD_OUTPUT_HANDLE), FALSE, &cfi);
|
||||
#else
|
||||
std::setlocale(LC_ALL, "en_US.UTF-8");
|
||||
#endif
|
||||
|
||||
init();
|
||||
start(nullptr, nullptr, nullptr);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
+8
-4
@@ -44,7 +44,7 @@ void dummy_check_interrupts() {
|
||||
|
||||
}
|
||||
|
||||
void RT64Init(uint8_t* rom, uint8_t* rdram, void* window_handle) {
|
||||
void RT64Init(uint8_t* rom, uint8_t* rdram, Multilibultra::WindowHandle window_handle) {
|
||||
// Dynamic loading
|
||||
//auto RT64 = LoadLibrary("RT64.dll");
|
||||
//if (RT64 == 0) {
|
||||
@@ -57,8 +57,8 @@ void RT64Init(uint8_t* rom, uint8_t* rdram, void* window_handle) {
|
||||
//GET_FUNC(RT64, UpdateScreen);
|
||||
|
||||
GFX_INFO gfx_info{};
|
||||
gfx_info.hWnd = window_handle;
|
||||
gfx_info.hStatusBar = nullptr;
|
||||
// gfx_info.hWnd = window_handle;
|
||||
// gfx_info.hStatusBar = nullptr;
|
||||
|
||||
gfx_info.HEADER = rom;
|
||||
gfx_info.RDRAM = rdram;
|
||||
@@ -93,7 +93,11 @@ void RT64Init(uint8_t* rom, uint8_t* rdram, void* window_handle) {
|
||||
|
||||
gfx_info.CheckInterrupts = dummy_check_interrupts;
|
||||
|
||||
InitiateGFX(gfx_info);
|
||||
gfx_info.version = 2;
|
||||
gfx_info.SP_STATUS_REG = &SP_STATUS_REG;
|
||||
gfx_info.RDRAM_SIZE = &RDRAM_SIZE;
|
||||
|
||||
InitiateGFXLinux(gfx_info, window_handle.window, window_handle.display);
|
||||
}
|
||||
|
||||
void RT64SendDL(uint8_t* rdram, const OSTask* task) {
|
||||
|
||||
@@ -36,3 +36,12 @@ extern "C" void osViSwapBuffer_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) {
|
||||
uint64_t cur_vis = total_vis;
|
||||
while (cur_vis == total_vis) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user