Monday, December 23, 2024

Wasm3 + TinyGo on PSP

Programming LanguageWasm3 + TinyGo on PSP


I will attach the GitHub repository first: wasm3-tinygo-psp.

Main Topic

For some time now, I have wanted to run Golang on the PSP.

However, native compilation to the PSP is impossible (probably).

Certainly, Golang does support GOARCH=mipsle, but it assumes the presence of an operating system. If you attempt to achieve this, you would likely have to navigate a very difficult and thorny path.

So, I chose another way. In other words, the approach is to convert it into WASM using TinyGo.

Now, let’s take a look at the actual code.

package main

import "unsafe"

//go:wasmimport debug println
func println(ptr unsafe.Pointer, len int32)

//export start
func start() {
    main()
}

func main() {
    message := "Hello, WebAssembly, from Golang!"
    println(unsafe.Pointer(unsafe.StringData(message)), int32(len(message)))
}
Enter fullscreen mode

Exit fullscreen mode

Essentially, we are simply calling the functions provided by the runtime. It’s just a Hello World example.

About runtime

There was also an option to use Rust and wasmi for the runtime, but although it worked on emulators like PPSSPP, errors occurred on the actual hardware (as shown in the screenshot below).

I suspect it may be related to kernel mode, but since I ultimately couldn’t resolve it, I took a different approach.

Ultimately, the combination of C and Wasm3 worked successfully.

Image of a PSP in Operation

Regarding println, which was mentioned in the earlier code, it is defined on the runtime side as follows.

#define printf pspDebugScreenPrintf

static const void* host_debug_println(IM3Runtime runtime, IM3ImportContext ctx, uint64_t *stack, void *mem)
{
    uint32_t ptr = (uint32_t) stack[0];
    uint32_t length = (uint32_t) stack[1];

    uint8_t* bytes = (uint8_t*)mem + ptr;

    char buffer[256];
    if (length >= sizeof(buffer)) {
        length = sizeof(buffer)-1;
    }
    memcpy(buffer, bytes, length);
    buffer[length] = '\0';

    printf("%s\n", buffer);

    return NULL;
}
Enter fullscreen mode

Exit fullscreen mode

Since this is just a demo, this is all for now. However, if you also wrap other functions and make them callable from WASM, you should be able to develop full-fledged applications.

Challenges Faced

Cross-compiling Wasm3

It was necessary to compile Wasm3 using the PSP toolchain.

I created a forked repository to make it easy for everyone to set up the environment, so please take a look for reference: wasm3-for-psp.

TinyGo build options

In the end, I ended up with the command tinygo build -o hello.wasm -target=wasm -no-debug main.go, but it took me quite a bit of time just to arrive at such a simple command. I still have a lot to learn.

Export main function

If you are using -target=wasi, TinyGo exports the main function as _start. But in this case, I had to define and export a separate start function.

And then…

This time, I used TinyGo to compile Golang code into WASM, but if other languages can also be compiled into WASM, I believe they can be executed using a similar method. I am passionate about Golang, so I would be happy if someone could give it a try.

That’s all. Thank you for reading.

Check out our other content

Check out other tags:

Most Popular Articles