-
Notifications
You must be signed in to change notification settings - Fork 947
Nintendo Switch Basic Support #1108
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
base: dev
Are you sure you want to change the base?
Conversation
Wow! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did a first pass over the PR.
There is a bit of code in builder/build.go that I'm not so happy about, I wonder how that can best be fixed? Ideally no environment variable is needed (apart from making sure the compiler is included in $PATH
), but otherwise the best place to configure this would be compileopts, not builder.
The issue is that there still need to have the devkitpro env to get the libnx include path. It's always inside devkitpro folder, but you dont know where is installed. All devKitPro projects expect a DEVKITPRO environment to build. |
I was playing around with it and you're right, even the bundled compilers haven't been configured to include libnx by default. Which makes things a whole lot more complicated. I tried running examples/nintendoswitch in Yuzu but it doesn't work for me. That may also because of my Yuzu build (which I only barely managed to get to work). Does the example work in Yuzu? |
The example does work on Yuzu (the screenshot is from yuzu). But I don't remember if you need the basic Nintendo Switch dump to make it work. To avoid legal issues with Nintendo proprietary assets, to run commercial games on yuzu you need to dump keys from your console and some content from the NAND flash. That's a requirement for commercial games because they're signed and encrypted. The NRO files are neither, so I don't know what's the minimum requirements to run. I will check here (since I have everything) what's the minimum setup to get it working. |
Can you maybe share a working .nro file that I can test? To determine which is the cause (the build or Yuzu). |
Sure, this one works here: |
Well that version sadly doesn't run, so I'm afraid my Yuzu build still doesn't work.
(fish is my shell) |
Hmm thats odd. As I remember it gives a message box saying what you need to run, not crashing. Maybe its some issue with wayland? Here I'm using Xorg instead, I never tested on wayland. |
That's the thing, for commercial games I'm 100% sure you need everything. Homebrew need only a subset. In about an hour I can confirm what's needed. |
I have also managed to produce a test.nro that works in yuzu, so I have all the tools I need for local testing. |
Played around a bit with avoiding libnx, but all I got was a panic in linkle:
|
@racerxdl can you please rebase this branch against |
551c9c6
to
6245cd4
Compare
Done @deadprogram ! I also squashed all commits. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i never doubt about teske work so its approved
enjoy running golang on switch
We recently changed compiler/compiler.go in #941 so I'm afraid this will need another conflict resolution. |
76f294a
to
b97ea9a
Compare
@aykevl just changed the way the PIC stuff reaches the compiler. Now it goes on TargetSpec and comes from json spec. I modified the original |
Hello, everyone. I would love to help get this PR merged before the next release. Seems like there are again (or still?) some merge conflicts to resolve. After that, what else will be needed to get it into a mergeable state? Thank you. |
Done @deadprogram. Not sure what's next. |
It currently works by using Dev Kit Pro for Nintendo Switch in a env DEVKITPRO. See https://switchbrew.org/wiki/Setting_up_Development_Environment Basic Nintendo Switch to do a Hello World * Added Integration with libnx to be able to print a hello world Add printhello example Cleanup. using devkitpro as default for now Linking libnx with lld proved to be hard. Then for now we will default to devKitPro and print a message if we cannot find a DEVKITPRO environment pointing to DKP root Change default gc to extalloc move calls to gonx and redirect default panic Default printstring now outputs to SvcOutputDebugString which prints on Emulator Console Remove libnx from submodules Changed hello world by vt52-example Add buffered output to putchar
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If possible, please add a smoke test for this PR. It will make it obvious when there is a compile-time breaking change.
@@ -27,11 +28,16 @@ import ( | |||
// The error value may be of type *MultiError. Callers will likely want to check | |||
// for this case and print such errors individually. | |||
func Build(pkgName, outpath string, config *compileopts.Config, action func(string) error) error { | |||
var err error | |||
var machine llvm.TargetMachine |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like you didn't undo these changes, which are now just noise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there something needed still for this comment?
dkp := os.Getenv("DEVKITPRO") | ||
// If Nintendo Switch, add libnx | ||
if config.Target.Linker == "devKitPro" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still don't really agree with adding code here that is specific for a particular board. I was under the impression that the special linker and libnx wasn't necessary for initial Switch support?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is needed. The support is complete useless without being able to link libnx. I tried libtransistor (which is made using LLVM) but its basically abandoned and has some issues.
GDB: "gdb", | ||
PortReset: "false", | ||
FlashMethod: "native", | ||
RelocationModel: "static", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is unnecessary: it defaults to static
already.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is action required on this comment?
@@ -109,7 +110,18 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) { | |||
codeModel = llvm.CodeModelLarge | |||
} | |||
|
|||
machine := target.CreateTargetMachine(config.Triple(), config.CPU(), features, llvm.CodeGenLevelDefault, llvm.RelocStatic, codeModel) | |||
switch config.Target.RelocationModel { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
switch config.Target.RelocationModel { | |
switch config.RelocationModel() { |
case "dynamicnopic": | ||
relocationModel = llvm.RelocDynamicNoPic | ||
default: | ||
relocationModel = llvm.RelocStatic |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should return an error instead of silently defaulting to static with an unknown relocation model.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like something still required here?
const heapSize = 16 * 1024 * 1024 | ||
|
||
//go:extern _stack_top | ||
var stackTopSymbol [0]byte |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where is this defined?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's from devKitPro libc. I will put a comment there.
@@ -0,0 +1,17 @@ | |||
// +build nintendoswitch |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems to me that this could be in the runtime_nintendoswitch.go file, or is there a reason to keep it separate?
@@ -12,6 +12,7 @@ package syscall | |||
|
|||
// A Signal is a number describing a process signal. | |||
// It implements the os.Signal interface. | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks unintentional?
func Close(fd int) (err error) { | ||
return ENOSYS // TODO | ||
errN := libc_close(int32(fd)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are all these syscall changes for? Are they necessary for initial support?
It seems better to me to do it in a separate PR and do it properly (without all the TODOs here).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, for nintendo switch that is actually needed (it does have file I/O). Actually I'm not sure why they weren't implemented like that since the picolibc included on tinygo has that libc calls as stubs.
"goos": "linux", | ||
"goarch": "arm64", | ||
"compiler": "aarch64-none-elf-gcc", | ||
"linker": "devKitPro", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wasn't it possible to link using lld
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, if you can use aarch64-none-elf-gcc
, would clang
(with the right options) work? The less dependencies, the better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It wasn't. LLD can't link stuff pre-compiled with GCC with custom linker script :( - It gave several errors. Also it does need DevKitPro because of the libc required for all nintendo switch deps (the DevKitPro is a modified picolibc).
We might eventually want to port devKitPro picolibc to tinygo and replace the current one, because the picolibc from devKitPro is more generic than the one we have on tinygo today. I checked if it was easy to port it, but there are some things that require changes on other tinygo targets, so I think its better a separated PR for that eventually.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, now I understand the situation better.
The linker
field here really refers to an actual command. I think instead of repurposing the linker
field, you should do something like this:
"libc": "libnx",
That is actually the field intended for changing the libc, which is the main reason all these custom build steps are needed. You can then check for libnx (similar to picolibc) and if it is used, add the correct paths by querying the DEVKITPRO
environment variable. Not how I would hope it would work, but I think that is the cleanest way to do it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On second thought, you need to run a command in the $DEVKITPRO
directory. Which is not known statically. This requires some logic to determine the correct path.
This is somewhat of a mismatch between how I intended TinyGo to work and how DevKitPro works. I'm not sure how to fix that exactly. Maybe as it is right now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe as it is right now.
@aykevl do you mean as this PR has already implemented?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. I don't like it but I don't see a better way, so maybe just do it as the PR is now. It's unfortunate that devkitPro assumes the use of global variables, which isn't very compatible with TinyGo right now.
Yes, please, @racerxdl is this possible? Ideally something with no external dependencies. Does not have to do much, just shows that the core compiler itself is working. |
Hi @deadprogram / @aykevl - sorry I didn't had much time to look these days. This weekend I will try to get a look in all comments and do the nescessary fixes / comments. About the smoke test, yes I can put a very small program just to test that everything is compiling. The thing is that the only change for Nintendo Switch is basically the devKitpro call for libnx linking, otherwise is just a normal arm64. So If I remove the devKitPro dependency for a smoke test, it will just test the ARM64 compiler part. Is that what's intended? The last time I was working on that, I tried to make the libnx linkage externally to the tinygo (by doing this https://github.com/racerxdl/gonx/blob/dev/base.go#L6-L7 ) but that does seen to trigger a bug in CGO ( #1148 ) which @aykevl wasn't able to simulate. (I think because he is importing from a local folder and not a external library). If I manage to do a external linkage, I can put a very small CRT for nintendo switch inside tinygo so it works as a application inside the nintendo switch. Also I think in that case, the lld can link it (just not link when using libnx). |
Thanks @racerxdl that sounds great! |
Hi @deadprogram / @aykevl while I work on the external integration of libnx for this branch, I did another setup that does not require devKitPro or anything, but can only print to Nintendo Switch emulator debug console. But this will serve as a base for external linkage of libnx. I will change this PR (#1108) to draft and then when its ready again I switch to a PR again. |
I added basic Nintendo Switch support to TinyGo.
It still relies on devKitPro and libnx to interact with Horizon OS (The Nintendo Switch Operating System), I hope that changes soon.
Still, that does work. At first I added the libnx calls here, but I found out its easier to make an external library to that. So I created this:
https://github.com/racerxdl/gonx
Without
gonx
the application can run inside nintendo switch, but can't use the display or anything until we implement full golang calls for the Switch IPCs.I also created this: https://github.com/racerxdl/go-switch-examples
I will port each example from the switchbrew examples repo ( https://github.com/switchbrew/switch-examples/ ) and implement the nescessary calls on gonx as needed.
For compiling to nintendo switch its simple by using:
For running on real devices a
NRO
file needs to be generated, that can be done with elf2nro (which is inside devKitPro) or with linkle:Here is a video of working: https://twitter.com/lucasteske/status/1259650256897298432
And the vt52-example inside examples folder prints this:
