mirror of
https://github.com/Zelda64Recomp/Zelda64Recomp
synced 2026-09-03 02:33:43 -04:00
Added recomp runtime library and portultra, MM initial boot
This commit is contained in:
+29
@@ -0,0 +1,29 @@
|
||||
#include "recomp.h"
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include "../portultra/ultra64.h"
|
||||
#include "../portultra/multilibultra.hpp"
|
||||
|
||||
#define VI_NTSC_CLOCK 48681812
|
||||
|
||||
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;
|
||||
ctx->r2 = freq;
|
||||
Multilibultra::set_audio_frequency(freq);
|
||||
}
|
||||
|
||||
extern "C" void osAiSetNextBuffer_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
Multilibultra::queue_audio_buffer(rdram, ctx->r4, ctx->r5);
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void osAiGetLength_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
ctx->r2 = Multilibultra::get_remaining_audio_bytes();
|
||||
}
|
||||
|
||||
extern "C" void osAiGetStatus_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
ctx->r2 = 0x00000000; // Pretend the audio DMAs finish instantly
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
#include "../portultra/multilibultra.hpp"
|
||||
#include "recomp.h"
|
||||
|
||||
static int max_controllers = 0;
|
||||
|
||||
extern "C" void osContInit_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
gpr bitpattern = ctx->r5;
|
||||
gpr status = ctx->r6;
|
||||
|
||||
// Set bit 0 to indicate that controller 0 is present
|
||||
MEM_B(0, bitpattern) = 0x01;
|
||||
|
||||
// Mark controller 0 as present
|
||||
MEM_H(0, status) = 0x0005; // type: CONT_TYPE_NORMAL (from joybus)
|
||||
MEM_B(2, status) = 0x00; // status: 0 (from joybus)
|
||||
MEM_B(3, status) = 0x00; // errno: 0 (from libultra)
|
||||
|
||||
max_controllers = 4;
|
||||
|
||||
// Mark controllers 1-3 as not connected
|
||||
for (size_t controller = 1; controller < max_controllers; controller++) {
|
||||
// Libultra doesn't write status or type for absent controllers
|
||||
MEM_B(4 * controller + 3, status) = 0x80 >> 4; // errno: CONT_NO_RESPONSE_ERROR >> 4
|
||||
}
|
||||
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void osContStartReadData_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
Multilibultra::send_si_message();
|
||||
}
|
||||
|
||||
struct OSContPad {
|
||||
u16 button;
|
||||
s8 stick_x; /* -80 <= stick_x <= 80 */
|
||||
s8 stick_y; /* -80 <= stick_y <= 80 */
|
||||
u8 errno_;
|
||||
};
|
||||
|
||||
int button = 0;
|
||||
int stick_x = 0;
|
||||
int stick_y = 0;
|
||||
|
||||
void press_button(int button) {
|
||||
|
||||
}
|
||||
|
||||
void release_button(int button) {
|
||||
|
||||
}
|
||||
|
||||
extern "C" void osContGetReadData_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
int32_t pad = (int32_t)ctx->r4;
|
||||
|
||||
if (max_controllers > 0) {
|
||||
// button
|
||||
MEM_H(0, pad) = button;
|
||||
// stick_x
|
||||
MEM_B(2, pad) = stick_x;
|
||||
// stick_y
|
||||
MEM_B(3, pad) = stick_y;
|
||||
// errno
|
||||
MEM_B(4, pad) = 0;
|
||||
}
|
||||
for (int controller = 1; controller < max_controllers; controller++) {
|
||||
MEM_B(6 * controller + 4, pad) = 0x80 >> 4; // errno: CONT_NO_RESPONSE_ERROR >> 4
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void osContStartQuery_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
Multilibultra::send_si_message();
|
||||
}
|
||||
|
||||
extern "C" void osContGetQuery_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
gpr status = ctx->r4;
|
||||
|
||||
// Mark controller 0 as present
|
||||
MEM_H(0, status) = 0x0005; // type: CONT_TYPE_NORMAL (from joybus)
|
||||
MEM_B(2, status) = 0x00; // status: 0 (from joybus)
|
||||
MEM_B(3, status) = 0x00; // errno: 0 (from libultra)
|
||||
|
||||
// Mark controllers 1-3 as not connected
|
||||
for (size_t controller = 1; controller < max_controllers; controller++) {
|
||||
// Libultra doesn't write status or type for absent controllers
|
||||
MEM_B(4 * controller + 3, status) = 0x80 >> 4; // errno: CONT_NO_RESPONSE_ERROR >> 4
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void osContSetCh_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
max_controllers = std::min((unsigned int)ctx->r4, 4u);
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void __osMotorAccess_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
|
||||
}
|
||||
|
||||
extern "C" void osMotorInit_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
;
|
||||
}
|
||||
|
||||
extern "C" void osMotorStart_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
;
|
||||
}
|
||||
|
||||
extern "C" void osMotorStop_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#include "recomp.h"
|
||||
|
||||
extern "C" void osDpSetNextBuffer_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
;
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
#include "recomp.h"
|
||||
|
||||
extern "C" void osEepromProbe_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
;
|
||||
}
|
||||
|
||||
extern "C" void osEepromWrite_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
;
|
||||
}
|
||||
|
||||
extern "C" void osEepromLongWrite_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
;
|
||||
}
|
||||
|
||||
extern "C" void osEepromRead_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
;
|
||||
}
|
||||
|
||||
extern "C" void osEepromLongRead_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
;
|
||||
}
|
||||
+2587
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
#ifndef __EUC_JP_H__
|
||||
#define __EUC_JP_H__
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace Encoding {
|
||||
std::string decode_eucjp(std::string_view src);
|
||||
}
|
||||
|
||||
#endif
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include "../portultra/ultra64.h"
|
||||
#include "../portultra/multilibultra.hpp"
|
||||
#include "recomp.h"
|
||||
|
||||
constexpr uint32_t flash_size = 1024 * 1024 / 8; // 1Mbit
|
||||
constexpr uint32_t page_size = 128;
|
||||
constexpr uint32_t pages_per_sector = 128;
|
||||
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(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);
|
||||
void save_clear(uint32_t start, uint32_t size);
|
||||
|
||||
std::array<char, page_size> write_buffer;
|
||||
|
||||
extern "C" void osFlashInit_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
ctx->r2 = Multilibultra::flash_handle;
|
||||
}
|
||||
|
||||
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) {
|
||||
PTR(u32) flash_type = ctx->r4;
|
||||
PTR(u32) flash_maker = ctx->r5;
|
||||
|
||||
MEM_B(0, flash_type) = 0;
|
||||
MEM_B(0, flash_maker) = 0;
|
||||
}
|
||||
|
||||
extern "C" void osFlashClearStatus_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
|
||||
}
|
||||
|
||||
extern "C" void osFlashAllErase_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
save_clear(0, Multilibultra::save_size);
|
||||
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void osFlashAllEraseThrough_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
save_clear(0, Multilibultra::save_size);
|
||||
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void osFlashSectorErase_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
uint32_t page_num = (uint32_t)ctx->r4;
|
||||
uint32_t sector_num = page_num / sector_size;
|
||||
|
||||
// Prevent out of bounds erase
|
||||
if (sector_num >= sector_size) {
|
||||
ctx->r2 = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
save_clear(sector_num * sector_size, sector_size);
|
||||
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void osFlashSectorEraseThrough_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
uint32_t page_num = (uint32_t)ctx->r4;
|
||||
uint32_t sector_num = page_num / sector_size;
|
||||
|
||||
// Prevent out of bounds erase
|
||||
if (sector_num >= sector_size) {
|
||||
ctx->r2 = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
save_clear(sector_num * sector_size, sector_size);
|
||||
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
// Send the message indicating write completion
|
||||
osSendMesg(PASS_RDRAM mq, 0, OS_MESG_NOBLOCK);
|
||||
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
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
|
||||
save_write_ptr(write_buffer.data(), page_num * page_size, page_size);
|
||||
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
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;
|
||||
uint32_t n_pages = MEM_W(0x10, ctx->r29);
|
||||
PTR(OSMesgQueue) mq = MEM_W(0x14, ctx->r29);
|
||||
|
||||
uint32_t offset = page_num * page_size;
|
||||
uint32_t count = n_pages * page_size;
|
||||
|
||||
// Read from the save file into the provided buffer
|
||||
save_read(PASS_RDRAM dramAddr, offset, count);
|
||||
|
||||
// Send the message indicating read completion
|
||||
osSendMesg(PASS_RDRAM mq, 0, OS_MESG_NOBLOCK);
|
||||
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void osFlashChange_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
assert(false);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
#include "../portultra/multilibultra.hpp"
|
||||
#include "recomp.h"
|
||||
|
||||
|
||||
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;
|
||||
|
||||
ctx->r2 = (int32_t)(ret >> 32);
|
||||
ctx->r3 = (int32_t)(ret >> 0);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
ctx->r2 = (int32_t)(ret >> 32);
|
||||
ctx->r3 = (int32_t)(ret >> 0);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
ctx->r2 = (int32_t)(ret >> 32);
|
||||
ctx->r3 = (int32_t)(ret >> 0);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
ctx->r2 = (int32_t)(ret >> 32);
|
||||
ctx->r3 = (int32_t)(ret >> 0);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
ctx->r2 = (int32_t)(ret >> 32);
|
||||
ctx->r3 = (int32_t)(ret >> 0);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
ctx->r2 = (int32_t)(ret >> 32);
|
||||
ctx->r3 = (int32_t)(ret >> 0);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
ctx->r2 = (int32_t)(ret >> 32);
|
||||
ctx->r3 = (int32_t)(ret >> 0);
|
||||
}
|
||||
|
||||
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) {
|
||||
uint64_t a = (ctx->r4 << 32) | ((ctx->r5 << 0) & 0xFFFFFFFFu);
|
||||
float ret = (float)a;
|
||||
|
||||
ctx->f0.fl = ret;
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
#include <unordered_map>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include "recomp.h"
|
||||
#include "../RecompiledFuncs/recomp_overlays.inl"
|
||||
|
||||
constexpr size_t num_code_sections = ARRLEN(section_table);
|
||||
|
||||
// SectionTableEntry sections[] defined in recomp_overlays.inl
|
||||
|
||||
struct LoadedSection {
|
||||
int32_t loaded_ram_addr;
|
||||
size_t section_table_index;
|
||||
|
||||
bool operator<(const LoadedSection& rhs) {
|
||||
return loaded_ram_addr < rhs.loaded_ram_addr;
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<LoadedSection> loaded_sections{};
|
||||
std::unordered_map<int32_t, recomp_func_t*> func_map{};
|
||||
|
||||
void load_overlay(size_t section_table_index, int32_t ram) {
|
||||
const SectionTableEntry& section = section_table[section_table_index];
|
||||
for (size_t function_index = 0; function_index < section.num_funcs; function_index++) {
|
||||
const FuncEntry& func = section.funcs[function_index];
|
||||
func_map[ram + func.offset] = func.func;
|
||||
}
|
||||
loaded_sections.emplace_back(ram, section_table_index);
|
||||
section_addresses[section.index] = ram;
|
||||
}
|
||||
|
||||
|
||||
extern "C" {
|
||||
int32_t section_addresses[num_sections];
|
||||
}
|
||||
|
||||
extern "C" void load_overlays(uint32_t rom, int32_t ram_addr, uint32_t size) {
|
||||
// Search for the first section that's included in the loaded rom range
|
||||
// Sections were sorted by `init_overlays` so we can use the bounds functions
|
||||
auto lower = std::lower_bound(§ion_table[0], §ion_table[num_code_sections], rom,
|
||||
[](const SectionTableEntry& entry, uint32_t addr) {
|
||||
return entry.rom_addr < addr;
|
||||
}
|
||||
);
|
||||
auto upper = std::upper_bound(§ion_table[0], §ion_table[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(§ion_table[0], it), it->rom_addr - rom + ram_addr);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void unload_overlays(int32_t ram_addr, uint32_t size) {
|
||||
for (auto it = loaded_sections.begin(); it != loaded_sections.end();) {
|
||||
const auto& section = section_table[it->section_table_index];
|
||||
|
||||
// Check if the unloaded region overlaps with the loaded section
|
||||
if (ram_addr < (it->loaded_ram_addr + section.size) && (ram_addr + size) >= it->loaded_ram_addr) {
|
||||
// Check if the section isn't entirely in the loaded region
|
||||
if (ram_addr > it->loaded_ram_addr || (ram_addr + size) < (it->loaded_ram_addr + section.size)) {
|
||||
fprintf(stderr,
|
||||
"Cannot partially unload section\n"
|
||||
" rom: 0x%08X size: 0x%08X loaded_addr: 0x%08X\n"
|
||||
" unloaded_ram: 0x%08X unloaded_size : 0x%08X\n",
|
||||
section.rom_addr, section.size, it->loaded_ram_addr, ram_addr, size);
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
// Determine where each function was loaded to and remove that entry from the function map
|
||||
for (size_t func_index = 0; func_index < section.num_funcs; func_index++) {
|
||||
const auto& func = section.funcs[func_index];
|
||||
uint32_t func_address = func.offset + it->loaded_ram_addr;
|
||||
func_map.erase(func_address);
|
||||
}
|
||||
// Reset the section's address in the address table
|
||||
section_addresses[section.index] = 0;
|
||||
// Remove the section from the loaded section map
|
||||
it = loaded_sections.erase(it);
|
||||
// Skip incrementing the iterator
|
||||
continue;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void init_overlays() {
|
||||
for (size_t section_index = 0; section_index < num_code_sections; section_index++) {
|
||||
section_addresses[section_table[section_index].index] = section_table[section_index].ram_addr;
|
||||
}
|
||||
|
||||
// Sort the executable sections by rom address
|
||||
std::sort(§ion_table[0], §ion_table[num_code_sections],
|
||||
[](const SectionTableEntry& a, const SectionTableEntry& b) {
|
||||
return a.rom_addr < b.rom_addr;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
extern "C" recomp_func_t * get_function(int32_t addr) {
|
||||
auto func_find = func_map.find(addr);
|
||||
if (func_find == func_map.end()) {
|
||||
fprintf(stderr, "Failed to find function at 0x%08X\n", addr);
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
return func_find->second;
|
||||
}
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
#include "recomp.h"
|
||||
#include "../portultra/ultra64.h"
|
||||
#include "../portultra/multilibultra.hpp"
|
||||
|
||||
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) {
|
||||
ctx->r2 = 1; // PFS_ERR_NOPACK
|
||||
}
|
||||
|
||||
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) {
|
||||
ctx->r2 = 1; // PFS_ERR_NOPACK
|
||||
}
|
||||
|
||||
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) {
|
||||
ctx->r2 = 1; // PFS_ERR_NOPACK
|
||||
}
|
||||
|
||||
extern "C" void osPfsReadWriteFile_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
ctx->r2 = 1; // PFS_ERR_NOPACK
|
||||
}
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
#include <memory>
|
||||
#include <fstream>
|
||||
#include <array>
|
||||
#include "recomp.h"
|
||||
#include "../portultra/ultra64.h"
|
||||
#include "../portultra/multilibultra.hpp"
|
||||
|
||||
// Flashram occupies the same physical address as sram, but that issue is avoided because libultra exposes
|
||||
// a high-level interface for flashram. Because that high-level interface is reimplemented, low level accesses
|
||||
// that involve physical addresses don't need to be handled for flashram.
|
||||
constexpr uint32_t sram_base = 0x08000000;
|
||||
constexpr uint32_t rom_base = 0x10000000;
|
||||
|
||||
constexpr uint32_t k1_to_phys(uint32_t addr) {
|
||||
return addr & 0x1FFFFFFF;
|
||||
}
|
||||
|
||||
constexpr uint32_t phys_to_k1(uint32_t addr) {
|
||||
return addr | 0xA0000000;
|
||||
}
|
||||
|
||||
extern std::unique_ptr<uint8_t[]> rom;
|
||||
extern size_t rom_size;
|
||||
|
||||
extern "C" void osCartRomInit_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
OSPiHandle* handle = TO_PTR(OSPiHandle, Multilibultra::cart_handle);
|
||||
handle->type = 0; // cart
|
||||
handle->baseAddress = phys_to_k1(rom_base);
|
||||
handle->domain = 0;
|
||||
|
||||
ctx->r2 = (gpr)Multilibultra::cart_handle;
|
||||
}
|
||||
|
||||
extern "C" void osCreatePiManager_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
;
|
||||
}
|
||||
|
||||
void do_rom_read(uint8_t* rdram, gpr ram_address, uint32_t physical_addr, size_t num_bytes) {
|
||||
// TODO use word copies when possible
|
||||
uint8_t* rom_addr = rom.get() + physical_addr - rom_base;
|
||||
for (size_t i = 0; i < num_bytes; i++) {
|
||||
MEM_B(i, ram_address) = *rom_addr;
|
||||
rom_addr++;
|
||||
}
|
||||
}
|
||||
|
||||
std::array<char, 0x20000> save_buffer;
|
||||
const char save_filename[] = "save.bin";
|
||||
|
||||
void update_save_file() {
|
||||
std::ofstream save_file{ save_filename, std::ios_base::binary };
|
||||
|
||||
if (save_file.good()) {
|
||||
save_file.write(save_buffer.data(), save_buffer.size());
|
||||
} else {
|
||||
fprintf(stderr, "Failed to save!\n");
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void save_write_ptr(const void* in, uint32_t offset, uint32_t count) {
|
||||
memcpy(&save_buffer[offset], in, count);
|
||||
update_save_file();
|
||||
}
|
||||
|
||||
void save_write(uint8_t* rdram, gpr rdram_address, uint32_t offset, uint32_t count) {
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
save_buffer[offset + i] = MEM_B(i, rdram_address);
|
||||
}
|
||||
update_save_file();
|
||||
}
|
||||
|
||||
void save_read(uint8_t* rdram, gpr rdram_address, uint32_t offset, uint32_t count) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
MEM_B(i, rdram_address) = save_buffer[offset + i];
|
||||
}
|
||||
}
|
||||
|
||||
void save_clear(uint32_t start, uint32_t size) {
|
||||
std::fill_n(save_buffer.begin() + start, size, 0);
|
||||
std::ofstream save_file{ save_filename, std::ios_base::binary };
|
||||
|
||||
if (save_file.good()) {
|
||||
save_file.write(save_buffer.data(), save_buffer.size());
|
||||
} else {
|
||||
fprintf(stderr, "Failed to save!\n");
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void Multilibultra::save_init() {
|
||||
std::ifstream save_file{ save_filename, std::ios_base::binary };
|
||||
|
||||
if (save_file.good()) {
|
||||
save_file.read(save_buffer.data(), save_buffer.size());
|
||||
} else {
|
||||
save_buffer.fill(0);
|
||||
}
|
||||
}
|
||||
|
||||
void do_dma(uint8_t* rdram, PTR(OSMesgQueue) mq, gpr rdram_address, uint32_t physical_addr, uint32_t size, uint32_t direction) {
|
||||
// TODO asynchronous transfer
|
||||
// TODO implement unaligned DMA correctly
|
||||
if (direction == 0) {
|
||||
if (physical_addr >= rom_base) {
|
||||
// read cart rom
|
||||
do_rom_read(rdram, rdram_address, physical_addr, size);
|
||||
|
||||
// Send a message to the mq to indicate that the transfer completed
|
||||
osSendMesg(rdram, mq, 0, OS_MESG_NOBLOCK);
|
||||
} else 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 {
|
||||
fprintf(stderr, "[WARN] PI DMA read from unknown region, phys address 0x%08X\n", physical_addr);
|
||||
}
|
||||
} else {
|
||||
if (physical_addr >= rom_base) {
|
||||
// write cart rom
|
||||
throw std::runtime_error("ROM DMA write unimplemented");
|
||||
} 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 {
|
||||
fprintf(stderr, "[WARN] PI DMA write to unknown region, phys address 0x%08X\n", physical_addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
gpr dramAddr = MEM_W(0x10, ctx->r29);
|
||||
uint32_t size = MEM_W(0x14, ctx->r29);
|
||||
PTR(OSMesgQueue) mq = MEM_W(0x18, ctx->r29);
|
||||
uint32_t physical_addr = k1_to_phys(devAddr);
|
||||
|
||||
debug_printf("[pi] DMA from 0x%08X into 0x%08X of size 0x%08X\n", devAddr, dramAddr, size);
|
||||
|
||||
do_dma(rdram, mq, dramAddr, physical_addr, size, direction);
|
||||
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void osEPiStartDma_recomp(uint8_t* rdram, 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;
|
||||
uint32_t size = mb->size;
|
||||
PTR(OSMesgQueue) mq = mb->hdr.retQueue;
|
||||
uint32_t physical_addr = k1_to_phys(devAddr);
|
||||
|
||||
debug_printf("[pi] DMA from 0x%08X into 0x%08X of size 0x%08X\n", devAddr, dramAddr, size);
|
||||
|
||||
do_dma(rdram, mq, dramAddr, physical_addr, size, direction);
|
||||
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void osEPiReadIo_recomp(uint8_t * rdram, 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);
|
||||
|
||||
if (physical_addr > rom_base) {
|
||||
// cart rom
|
||||
do_rom_read(rdram, dramAddr, physical_addr, sizeof(uint32_t));
|
||||
} else {
|
||||
// sram
|
||||
assert(false && "SRAM ReadIo unimplemented");
|
||||
}
|
||||
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void osPiGetStatus_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void osPiRawStartDma_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#include "../portultra/ultra64.h"
|
||||
#include "../portultra/multilibultra.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) {
|
||||
// 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) {
|
||||
ctx->r2 = 11; // CONT_ERR_DEVICE
|
||||
}
|
||||
|
||||
extern "C" void osVoiceSetWord_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
extern "C" void osVoiceCheckWord_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
extern "C" void osVoiceStopReadData_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
extern "C" void osVoiceMaskDictionary_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
extern "C" void osVoiceStartReadData_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
extern "C" void osVoiceControlGain_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
extern "C" void osVoiceGetReadData_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
extern "C" void osVoiceClearDictionary_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
#include <memory>
|
||||
#include "../portultra/ultra64.h"
|
||||
#include "../portultra/multilibultra.hpp"
|
||||
#include "recomp.h"
|
||||
|
||||
extern "C" void osInitialize_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
osInitialize();
|
||||
}
|
||||
|
||||
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 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) {
|
||||
osStopThread(rdram, (int32_t)ctx->r4);
|
||||
}
|
||||
|
||||
extern "C" void osDestroyThread_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
osDestroyThread(rdram, (int32_t)ctx->r4);
|
||||
}
|
||||
|
||||
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) {
|
||||
ctx->r2 = osGetThreadPri(rdram, (int32_t)ctx->r4);
|
||||
}
|
||||
|
||||
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) {
|
||||
osCreateMesgQueue(rdram, (int32_t)ctx->r4, (int32_t)ctx->r5, (s32)ctx->r6);
|
||||
}
|
||||
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
osSetEventMesg(rdram, (OSEvent)ctx->r4, (int32_t)ctx->r5, (OSMesg)ctx->r6);
|
||||
}
|
||||
|
||||
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) {
|
||||
ctx->r2 = osGetCount();
|
||||
}
|
||||
|
||||
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) {
|
||||
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) {
|
||||
ctx->r2 = osStopTimer(rdram, (int32_t)ctx->r4);
|
||||
}
|
||||
|
||||
extern "C" void osVirtualToPhysical_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
ctx->r2 = osVirtualToPhysical((int32_t)ctx->r2);
|
||||
}
|
||||
|
||||
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 osWritebackDCache_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 __osDisableInt_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) {
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
// For the Mario Party games (not working)
|
||||
//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
|
||||
// if (buf->magic == SETJMP_MAGIC) {
|
||||
// // If so, longjmp to it
|
||||
// // Setjmp/longjmp does not work across threads, so verify that this buffer was made by this thread
|
||||
// assert(buf->owner == Multilibultra::this_thread());
|
||||
// longjmp(buf->storage->buffer, ctx->r5);
|
||||
// } else {
|
||||
// // Otherwise, check if it was one built manually by the game with $ra pointing to a function
|
||||
// gpr sp = MEM_W(0, ctx->r4);
|
||||
// gpr ra = MEM_W(4, ctx->r4);
|
||||
// ctx->r29 = sp;
|
||||
// recomp_func_t* target = LOOKUP_FUNC(ra);
|
||||
// if (target == nullptr) {
|
||||
// fprintf(stderr, "Failed to find function for manual longjmp\n");
|
||||
// std::quick_exit(EXIT_FAILURE);
|
||||
// }
|
||||
// target(rdram, ctx);
|
||||
//
|
||||
// // TODO kill this thread if the target function returns
|
||||
// assert(false);
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//#undef setjmp_recomp
|
||||
//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) {
|
||||
// return Multilibultra::this_thread();
|
||||
//}
|
||||
@@ -0,0 +1,70 @@
|
||||
#include "../portultra/ultra64.h"
|
||||
#include "../portultra/multilibultra.hpp"
|
||||
#include "recomp.h"
|
||||
#include "euc-jp.h"
|
||||
|
||||
extern "C" void __checkHardware_msp_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
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) {
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
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_isv_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) {
|
||||
gpr buf = ctx->r4;
|
||||
size_t size = ctx->r5;
|
||||
u32 type = (u32)ctx->r6;
|
||||
std::unique_ptr<char[]> to_print = std::make_unique<char[]>(size + 1);
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
to_print[i] = MEM_B(i, buf);
|
||||
}
|
||||
to_print[size] = '\x00';
|
||||
|
||||
fwrite(to_print.get(), 1, size, stdout);
|
||||
|
||||
ctx->r2 = size;
|
||||
}
|
||||
|
||||
extern "C" void is_proutSyncPrintf_recomp(uint8_t * 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);
|
||||
|
||||
// // If the new character is a newline, flush the buffer
|
||||
// if (cur_char == '\n') {
|
||||
// std::string utf8_str = Encoding::decode_eucjp(std::string_view{ print_buffer.data(), print_buffer.size() });
|
||||
// puts(utf8_str.c_str());
|
||||
// print_buffer.clear();
|
||||
// } else {
|
||||
// print_buffer.push_back(cur_char);
|
||||
// }
|
||||
//}
|
||||
|
||||
//fwrite(to_print.get(), size, 1, stdout);
|
||||
|
||||
ctx->r2 = 1;
|
||||
}
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <cmath>
|
||||
#include <unordered_map>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include "recomp.h"
|
||||
#include "../portultra/multilibultra.hpp"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
inline uint32_t byteswap(uint32_t val) {
|
||||
return _byteswap_ulong(val);
|
||||
}
|
||||
#else
|
||||
constexpr uint32_t byteswap(uint32_t val) {
|
||||
return __builtin_bswap32(val);
|
||||
}
|
||||
#endif
|
||||
|
||||
extern "C" void _bzero(uint8_t* rdram, recomp_context* ctx) {
|
||||
gpr start_addr = ctx->r4;
|
||||
gpr size = ctx->r5;
|
||||
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
MEM_B(start_addr, i) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void osGetMemSize_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
ctx->r2 = 8 * 1024 * 1024;
|
||||
}
|
||||
|
||||
extern "C" void switch_error(const char* func, uint32_t vram, uint32_t jtbl) {
|
||||
printf("Switch-case out of bounds in %s at 0x%08X for jump table at 0x%08X\n", func, vram, jtbl);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
extern "C" void do_break(uint32_t vram) {
|
||||
printf("Encountered break at original vram 0x%08X\n", vram);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
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;
|
||||
recomp_func_t* func = get_function(addr);
|
||||
func(rdram, &ctx);
|
||||
}
|
||||
|
||||
void do_rom_read(uint8_t* rdram, gpr ram_address, uint32_t dev_address, size_t num_bytes);
|
||||
|
||||
std::unique_ptr<uint8_t[]> rom;
|
||||
size_t rom_size;
|
||||
|
||||
// Recomp generation functions
|
||||
extern "C" void recomp_entrypoint(uint8_t * rdram, recomp_context * ctx);
|
||||
gpr get_entrypoint_address();
|
||||
const char* get_rom_name();
|
||||
void init_overlays();
|
||||
extern "C" void load_overlays(uint32_t rom, int32_t ram_addr, uint32_t size);
|
||||
extern "C" void unload_overlays(int32_t ram_addr, uint32_t size);
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
//if (argc != 2) {
|
||||
// printf("Usage: %s [baserom]\n", argv[0]);
|
||||
// exit(EXIT_SUCCESS);
|
||||
//}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Set up console output to accept UTF-8 on windows
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
|
||||
// Change to a font that supports Japanese characters
|
||||
CONSOLE_FONT_INFOEX cfi;
|
||||
cfi.cbSize = sizeof cfi;
|
||||
cfi.nFont = 0;
|
||||
cfi.dwFontSize.X = 0;
|
||||
cfi.dwFontSize.Y = 16;
|
||||
cfi.FontFamily = FF_DONTCARE;
|
||||
cfi.FontWeight = FW_NORMAL;
|
||||
wcscpy_s(cfi.FaceName, L"NSimSun");
|
||||
SetCurrentConsoleFontEx(GetStdHandle(STD_OUTPUT_HANDLE), FALSE, &cfi);
|
||||
#else
|
||||
std::setlocale(LC_ALL, "en_US.UTF-8");
|
||||
#endif
|
||||
|
||||
{
|
||||
std::basic_ifstream<uint8_t> rom_file{ get_rom_name(), std::ios::binary };
|
||||
|
||||
size_t iobuf_size = 0x100000;
|
||||
std::unique_ptr<uint8_t[]> iobuf = std::make_unique<uint8_t[]>(iobuf_size);
|
||||
rom_file.rdbuf()->pubsetbuf(iobuf.get(), iobuf_size);
|
||||
|
||||
if (!rom_file) {
|
||||
fprintf(stderr, "Failed to open rom: %s\n", get_rom_name());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
rom_file.seekg(0, std::ios::end);
|
||||
rom_size = rom_file.tellg();
|
||||
rom_file.seekg(0, std::ios::beg);
|
||||
|
||||
rom = std::make_unique<uint8_t[]>(rom_size);
|
||||
|
||||
rom_file.read(rom.get(), rom_size);
|
||||
|
||||
// TODO remove this
|
||||
// Modify the name in the rom header so RT64 doesn't find it
|
||||
rom[0x2F] = 'O';
|
||||
}
|
||||
|
||||
// Initialize the overlays
|
||||
init_overlays();
|
||||
|
||||
// Get entrypoint from recomp function
|
||||
gpr entrypoint = get_entrypoint_address();
|
||||
|
||||
// Load overlays in the first 1MB
|
||||
load_overlays(0x1000, (int32_t)entrypoint, 1024 * 1024);
|
||||
|
||||
// Allocate rdram_buffer (16MB to give room for any extra addressable data used by recomp)
|
||||
std::unique_ptr<uint8_t[]> rdram_buffer = std::make_unique<uint8_t[]>(16 * 1024 * 1024);
|
||||
std::memset(rdram_buffer.get(), 0, 8 * 1024 * 1024);
|
||||
recomp_context context{};
|
||||
|
||||
// Initial 1MB DMA (rom address 0x1000 = physical address 0x10001000)
|
||||
do_rom_read(rdram_buffer.get(), entrypoint, 0x10001000, 0x100000);
|
||||
|
||||
// Set up stack pointer
|
||||
context.r29 = 0xFFFFFFFF803FFFF0u;
|
||||
|
||||
// Initialize variables normally set by IPL3
|
||||
constexpr int32_t osTvType = 0x80000300;
|
||||
constexpr int32_t osRomType = 0x80000304;
|
||||
constexpr int32_t osRomBase = 0x80000308;
|
||||
constexpr int32_t osResetType = 0x8000030c;
|
||||
constexpr int32_t osCicId = 0x80000310;
|
||||
constexpr int32_t osVersion = 0x80000314;
|
||||
constexpr int32_t osMemSize = 0x80000318;
|
||||
constexpr int32_t osAppNMIBuffer = 0x8000031c;
|
||||
uint8_t *rdram = rdram_buffer.get();
|
||||
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
|
||||
|
||||
debug_printf("[Recomp] Starting\n");
|
||||
|
||||
Multilibultra::preinit(rdram_buffer.get(), rom.get());
|
||||
|
||||
recomp_entrypoint(rdram_buffer.get(), &context);
|
||||
|
||||
debug_printf("[Recomp] Quitting\n");
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
#include <memory>
|
||||
#include <Windows.h>
|
||||
|
||||
#include "../portultra/multilibultra.hpp"
|
||||
#include "rt64_layer.h"
|
||||
#include "SDL.h"
|
||||
|
||||
static uint8_t DMEM[0x1000];
|
||||
static uint8_t IMEM[0x1000];
|
||||
|
||||
unsigned int MI_INTR_REG = 0;
|
||||
|
||||
unsigned int DPC_START_REG = 0;
|
||||
unsigned int DPC_END_REG = 0;
|
||||
unsigned int DPC_CURRENT_REG = 0;
|
||||
unsigned int DPC_STATUS_REG = 0;
|
||||
unsigned int DPC_CLOCK_REG = 0;
|
||||
unsigned int DPC_BUFBUSY_REG = 0;
|
||||
unsigned int DPC_PIPEBUSY_REG = 0;
|
||||
unsigned int DPC_TMEM_REG = 0;
|
||||
|
||||
unsigned int VI_STATUS_REG = 0;
|
||||
unsigned int VI_ORIGIN_REG = 0;
|
||||
unsigned int VI_WIDTH_REG = 0;
|
||||
unsigned int VI_INTR_REG = 0;
|
||||
unsigned int VI_V_CURRENT_LINE_REG = 0;
|
||||
unsigned int VI_TIMING_REG = 0;
|
||||
unsigned int VI_V_SYNC_REG = 0;
|
||||
unsigned int VI_H_SYNC_REG = 0;
|
||||
unsigned int VI_LEAP_REG = 0;
|
||||
unsigned int VI_H_START_REG = 0;
|
||||
unsigned int VI_V_START_REG = 0;
|
||||
unsigned int VI_V_BURST_REG = 0;
|
||||
unsigned int VI_X_SCALE_REG = 0;
|
||||
unsigned int VI_Y_SCALE_REG = 0;
|
||||
|
||||
unsigned int SP_STATUS_REG = 0;
|
||||
unsigned int RDRAM_SIZE = 0x800000;
|
||||
|
||||
#define GET_FUNC(lib, name) \
|
||||
name = (decltype(name))GetProcAddress(lib, #name)
|
||||
|
||||
void dummy_check_interrupts() {
|
||||
|
||||
}
|
||||
|
||||
void RT64Init(uint8_t* rom, uint8_t* rdram) {
|
||||
// Dynamic loading
|
||||
//auto RT64 = LoadLibrary("RT64.dll");
|
||||
//if (RT64 == 0) {
|
||||
// fprintf(stdout, "Failed to load RT64\n");
|
||||
// std::exit(EXIT_FAILURE);
|
||||
//}
|
||||
//GET_FUNC(RT64, InitiateGFX);
|
||||
//GET_FUNC(RT64, ProcessRDPList);
|
||||
//GET_FUNC(RT64, ProcessDList);
|
||||
//GET_FUNC(RT64, UpdateScreen);
|
||||
|
||||
GFX_INFO gfx_info{};
|
||||
gfx_info.HEADER = rom;
|
||||
gfx_info.RDRAM = rdram;
|
||||
gfx_info.DMEM = DMEM;
|
||||
gfx_info.IMEM = IMEM;
|
||||
|
||||
gfx_info.MI_INTR_REG = &MI_INTR_REG;
|
||||
|
||||
gfx_info.DPC_START_REG = &DPC_START_REG;
|
||||
gfx_info.DPC_END_REG = &DPC_END_REG;
|
||||
gfx_info.DPC_CURRENT_REG = &DPC_CURRENT_REG;
|
||||
gfx_info.DPC_STATUS_REG = &DPC_STATUS_REG;
|
||||
gfx_info.DPC_CLOCK_REG = &DPC_CLOCK_REG;
|
||||
gfx_info.DPC_BUFBUSY_REG = &DPC_BUFBUSY_REG;
|
||||
gfx_info.DPC_PIPEBUSY_REG = &DPC_PIPEBUSY_REG;
|
||||
gfx_info.DPC_TMEM_REG = &DPC_TMEM_REG;
|
||||
|
||||
gfx_info.VI_STATUS_REG = &VI_STATUS_REG;
|
||||
gfx_info.VI_ORIGIN_REG = &VI_ORIGIN_REG;
|
||||
gfx_info.VI_WIDTH_REG = &VI_WIDTH_REG;
|
||||
gfx_info.VI_INTR_REG = &VI_INTR_REG;
|
||||
gfx_info.VI_V_CURRENT_LINE_REG = &VI_V_CURRENT_LINE_REG;
|
||||
gfx_info.VI_TIMING_REG = &VI_TIMING_REG;
|
||||
gfx_info.VI_V_SYNC_REG = &VI_V_SYNC_REG;
|
||||
gfx_info.VI_H_SYNC_REG = &VI_H_SYNC_REG;
|
||||
gfx_info.VI_LEAP_REG = &VI_LEAP_REG;
|
||||
gfx_info.VI_H_START_REG = &VI_H_START_REG;
|
||||
gfx_info.VI_V_START_REG = &VI_V_START_REG;
|
||||
gfx_info.VI_V_BURST_REG = &VI_V_BURST_REG;
|
||||
gfx_info.VI_X_SCALE_REG = &VI_X_SCALE_REG;
|
||||
gfx_info.VI_Y_SCALE_REG = &VI_Y_SCALE_REG;
|
||||
|
||||
gfx_info.CheckInterrupts = dummy_check_interrupts;
|
||||
gfx_info.version = 2;
|
||||
gfx_info.SP_STATUS_REG = &SP_STATUS_REG;
|
||||
gfx_info.RDRAM_SIZE = &RDRAM_SIZE;
|
||||
|
||||
InitiateGFX(gfx_info);
|
||||
}
|
||||
|
||||
void RT64SendDL(uint8_t* rdram, const OSTask* task) {
|
||||
OSTask task_copy = *task;
|
||||
task_copy.t.data_ptr &= 0x3FFFFFF;
|
||||
task_copy.t.ucode &= 0x3FFFFFF;
|
||||
task_copy.t.ucode_data &= 0x3FFFFFF;
|
||||
|
||||
memcpy(DMEM + 0xFC0, &task_copy, 0x40);
|
||||
|
||||
ProcessDList();
|
||||
}
|
||||
|
||||
void RT64UpdateScreen(uint32_t vi_origin) {
|
||||
VI_ORIGIN_REG = vi_origin;
|
||||
|
||||
UpdateScreen();
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include "../portultra/multilibultra.hpp"
|
||||
#include "recomp.h"
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
// For debugging
|
||||
if (dump_frame) {
|
||||
char addr_str[32];
|
||||
constexpr size_t ram_size = 0x800000;
|
||||
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};
|
||||
|
||||
for (size_t i = 0; i < ram_size; i++) {
|
||||
ram_unswapped[i] = rdram[i ^ 3];
|
||||
}
|
||||
|
||||
dump_file.write(ram_unswapped.get(), ram_size);
|
||||
dump_frame = false;
|
||||
}
|
||||
Multilibultra::submit_rsp_task(rdram, ctx->r4);
|
||||
}
|
||||
|
||||
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) {
|
||||
// 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) {
|
||||
assert(false);
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
#include "../portultra/multilibultra.hpp"
|
||||
#include "recomp.h"
|
||||
|
||||
extern "C" void osViSetYScale_recomp(uint8_t* rdram, recomp_context * ctx) {
|
||||
;
|
||||
}
|
||||
|
||||
extern "C" void osViSetXScale_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 osViSetSpecialFeatures_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) {
|
||||
ctx->r2 = (gpr)(int32_t)osViGetNextFramebuffer();
|
||||
}
|
||||
|
||||
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) {
|
||||
;
|
||||
}
|
||||
Reference in New Issue
Block a user