我决定尝试在 PSP 上使用 Golang,并探索了多种方法,包括移植 Clang、使用 TinyGo 以及使用 GopherJS 将 Golang 编译为 JavaScript。然后,我在 PSP 自制程序 Discord 上遇到了一位名为 aethiopicuschan 的用户,他正在使用 WebAssembly 尝试实现相同的目标。他的示例在 PPSSPP 上有效,但在实际硬件上无效。
无论如何,由于即将到来的考试,我不得不暂停我的项目。但是,几个月后,我发现一篇文章,aethiopicuschan 成功地将 Golang 编译为 WASM,并使用 WASM 解释器在 PSP 上运行它。
他的方法使用了一个名为 Wasm3 的 Wasm 解释器来运行代码,但我知道我可以做得更好。我知道像 Wasm2C 这样的项目,可以将 Wasm 二进制文件转换为 C。
我很兴奋,更深入地研究了这个主题,并发现了一篇有关将 WebAssembly 编译为可移植 C 代码的文章。他们使用了一个名为 w2c2 的编译器,我猜这是续集。
经过几个小时的修改 CMake,我成功地使用 TinyGo 并针对 WASI 创建了一个工作示例。我还封装了一个 raylib 函数 InitWindow(顺便说一下,psp 有一个 raylib 端口),目的是将 raylib-go 绑定移植到这个 WASM-to-C 平台。该示例成功将 C InitWindow 函数绑定到编译后的 WASM 代码。
如您所见,它看起来与任何其他 golang 代码
package main import "time" import rl "github.com/gen2brain/raylib-go/raylib" func main() { rl.InitWindow(480, 272, "Psp test") for { time.Sleep(time.Millisecond * 16) } }
但是在 rl 包中,我们导入了一个 C 函数,我们也给它一个函数签名。请记住。
package rl //go:wasmimport rl InitWindow func c_InitWindow(width int32, height int32, title string) // InitWindow - Initialize Window and OpenGL Graphics func InitWindow(width int32, height int32, title string) { c_InitWindow(width, height, title) }
让我们一步步分解这段代码。
#define __GLIBC_USE #include <raylib.h> #include <stdio.h> #include <time.h> #include <pspkernel.h> PSP_MODULE_INFO("WasiExample", 0, 1, 0); PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER); #include "pspdebug.h" #define printf pspDebugScreenPrintf
这里我们导入编译后的模块app.h
我们还导入了 w2c2 提供的 wasi“运行时”
// // the compiled wasm -> C code #include "app.h" // // wasi runtime #include "w2c2_base.h" #include "wasi.h" extern wasmMemory* e_memory; // the WASM memory. void trap(Trap trap) { fprintf(stderr, "TRAP: %s\n", trapDescription(trap)); abort(); } wasmMemory* wasiMemory(void* instance) { return app_memory((appInstance*)instance); } extern char** environ;
陷阱函数是样板,
wasiMemory 函数是我为另一个辅助函数制作的辅助函数
char* getCStringFromMemory(void* memoryptr, U32 offset, U32 length) { wasmMemory* memory = wasiMemory(memoryptr); char* str = (char*)(memory->data + offset); char* result = (char*)malloc( length + 1); // Allocate space for the string + null terminator if (result == NULL) { fprintf(stderr, "Memory allocation failed\n"); return NULL; } // Copy the string from WASI memory to local memory for (U32 i = 0; i < length; ++i) { result[i] = str[i]; } result[length] = '<pre class="brush:php;toolbar:false">void rl__InitWindow( void* memoryptr, U32 width, U32 height, U32 offset, U32 length) { char* title = getCStringFromMemory(memoryptr, offset, length); InitWindow(width, height, title); bool ready = IsWindowReady(); if (ready) { // this will print to the psp screen. printf("Window was created"); } }
这是我们从 golang 代码中调用的 C 函数,我必须自己定义它。
int main(int argc, char* argv[]) { pspDebugScreenInit(); appInstance i; appInstantiate(&i, NULL); if (!wasiInit(argc, argv, environ)) { fprintf(stderr, "failed to initialize WASI\n"); return 1; } app__start(&i); appFreeInstance(&i); return 0; }
我们正在读取转译的 wasm 传递的参数,并将它们传递给 raylib。
这是更多的样板文件,我们所做的就是运行 golang 代码的 main 函数,该函数导出为 app_start()
这是 psp 模拟器的屏幕截图。
但它也适用于原始硬件。
有任何问题请在评论区留言!
以上是PSP(和其他语言)上的 Golang的详细内容。更多信息请关注PHP中文网其他相关文章!