Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Misc fixes and implement osStopThread (partial), osEepromWrite, and osEepromRead #67

Merged
merged 5 commits into from
Oct 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions librecomp/include/librecomp/addresses.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
#include "librecomp/recomp.h"

namespace recomp {
// 2GB (Addressable upper half of rdram)
constexpr size_t mem_size = 2U * 1024U * 1024U * 1024U;
// 512GB (kseg0 size)
constexpr size_t mem_size = 512U * 1024U * 1024U;
// 2GB (Addressable upper half of the address space)
constexpr size_t allocation_size = 2048U * 1024U * 1024U;
// We need a place in rdram to hold the PI handles, so pick an address in extended rdram
constexpr int32_t cart_handle = 0x80800000;
constexpr int32_t drive_handle = (int32_t)(cart_handle + sizeof(OSPiHandle));
Expand Down
22 changes: 20 additions & 2 deletions librecomp/src/eep.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,16 @@ extern "C" void osEepromProbe_recomp(uint8_t* rdram, recomp_context* ctx) {
}

extern "C" void osEepromWrite_recomp(uint8_t* rdram, recomp_context* ctx) {
assert(false);// ctx->r2 = 8; // CONT_NO_RESPONSE_ERROR
uint8_t eep_address = ctx->r5;
gpr buffer = ctx->r6;
int32_t nbytes = eeprom_block_size;

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 osEepromLongWrite_recomp(uint8_t* rdram, recomp_context* ctx) {
Expand All @@ -33,7 +42,16 @@ extern "C" void osEepromLongWrite_recomp(uint8_t* rdram, recomp_context* ctx) {
}

extern "C" void osEepromRead_recomp(uint8_t* rdram, recomp_context* ctx) {
assert(false);// ctx->r2 = 8; // CONT_NO_RESPONSE_ERROR
uint8_t eep_address = ctx->r5;
gpr buffer = ctx->r6;
int32_t nbytes = eeprom_block_size;

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

extern "C" void osEepromLongRead_recomp(uint8_t* rdram, recomp_context* ctx) {
Expand Down
4 changes: 2 additions & 2 deletions librecomp/src/pi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ 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) {
{
std::lock_guard lock { save_context.save_buffer_mutex };
for (uint32_t i = 0; i < count; i++) {
for (gpr i = 0; i < count; i++) {
save_context.save_buffer[offset + i] = MEM_B(i, rdram_address);
}
}
Expand All @@ -164,7 +164,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 };
for (size_t i = 0; i < count; i++) {
for (gpr i = 0; i < count; i++) {
MEM_B(i, rdram_address) = save_context.save_buffer[offset + i];
}
}
Expand Down
23 changes: 19 additions & 4 deletions librecomp/src/recomp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -609,15 +609,30 @@ void recomp::start(
}

// Allocate rdram without comitting it. Use a platform-specific virtual allocation function
// that initializes to zero.
// that initializes to zero. Protect the region above the memory size to catch accesses to invalid addresses.
uint8_t* rdram;
bool alloc_failed;
#ifdef _WIN32
rdram = reinterpret_cast<uint8_t*>(VirtualAlloc(nullptr, mem_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE));
rdram = reinterpret_cast<uint8_t*>(VirtualAlloc(nullptr, allocation_size, MEM_COMMIT | MEM_RESERVE, PAGE_NOACCESS));
DWORD old_protect = 0;
alloc_failed = (rdram == nullptr);
if (!alloc_failed) {
// VirtualProtect returns 0 on failure.
alloc_failed = (VirtualProtect(rdram, mem_size, PAGE_READWRITE, &old_protect) == 0);
if (alloc_failed) {
VirtualFree(rdram, 0, MEM_RELEASE);
}
}
#else
rdram = (uint8_t*)mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
rdram = (uint8_t*)mmap(NULL, allocation_size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
alloc_failed = rdram == reinterpret_cast<uint8_t*>(MAP_FAILED);
if (!alloc_failed) {
// mprotect returns -1 on failure.
alloc_failed = (mprotect(rdram, mem_size, PROT_READ | PROT_WRITE) == -1);
if (alloc_failed) {
munmap(rdram, allocation_size);
}
}
#endif

if (alloc_failed) {
Expand Down Expand Up @@ -659,7 +674,7 @@ void recomp::start(
free_failed = (VirtualFree(rdram, 0, MEM_RELEASE) == 0);
#else
// munmap returns -1 on failure.
free_failed = (munmap(rdram, mem_size) == -1);
free_failed = (munmap(rdram, allocation_size) == -1);
#endif

if (free_failed) {
Expand Down
26 changes: 13 additions & 13 deletions ultramodern/src/audio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ void ultramodern::queue_audio_buffer(RDRAM_ARG PTR(int16_t) audio_data_, uint32_
uint32_t sample_count = byte_count / sizeof(int16_t);

// Queue the swapped audio data.
if (audio_callbacks.queue_samples) {
if (sample_count > 0 && audio_callbacks.queue_samples) {
audio_callbacks.queue_samples(TO_PTR(int16_t, audio_data_), sample_count);
}
}
Expand All @@ -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;
}
32 changes: 22 additions & 10 deletions ultramodern/src/threads.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ void ultramodern::set_native_thread_priority(ThreadPriority pri) {}
#endif

void wait_for_resumed(RDRAM_ARG UltraThreadContext* thread_context) {
TO_PTR(OSThread, ultramodern::this_thread())->context->running.wait();
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.
if (TO_PTR(OSThread, ultramodern::this_thread())->context != thread_context) {
Expand Down Expand Up @@ -198,7 +198,10 @@ static void _thread_func(RDRAM_ARG PTR(OSThread) self_, PTR(thread_func_t) entry
debug_printf("[Thread] Thread waiting to be started: %d\n", self->id);

// Wait until the thread is marked as running.
wait_for_resumed(PASS_RDRAM thread_context);
try {
wait_for_resumed(PASS_RDRAM thread_context);
} catch (ultramodern::thread_terminated& terminated) {
}

// Make sure the thread wasn't replaced or destroyed before it was started.
if (self->context == thread_context) {
Expand Down Expand Up @@ -228,11 +231,6 @@ extern "C" void osStartThread(RDRAM_ARG 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.
t->context->initialized.wait();

debug_printf("[os] Thread %d is ready to be started\n", t->id);

// If this is a game thread, insert the new thread into the running queue and then check the running queue.
if (thread_self) {
ultramodern::schedule_running_thread(PASS_RDRAM t_);
Expand All @@ -259,12 +257,26 @@ 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};
UltraThreadContext* context = new UltraThreadContext{};
t->context = context;
context->host_thread = std::thread{_thread_func, PASS_RDRAM t_, entrypoint, arg, t->context};

// Wait until the thread is initialized to indicate that it's ready to be started.
context->initialized.wait();
debug_printf("[os] Thread %d is ready to be started\n", t->id);
}

extern "C" void osStopThread(RDRAM_ARG PTR(OSThread) t_) {
assert(false);
if (t_ == NULLPTR) {
t_ = thread_self;
}
// Check if the thread is stopping itself (arg is null or thread_self).
if (t_ == thread_self) {
ultramodern::run_next_thread_and_wait(PASS_RDRAM1);
}
else {
assert(false);
}
}

extern "C" void osDestroyThread(RDRAM_ARG PTR(OSThread) t_) {
Expand Down