Skip to content

Commit 3650c2c

Browse files
racerxdlaykevl
authored andcommitted
nintendoswitch: Add experimental Nintendo Switch support without CRT
Bare minimal nintendo switch support using LLD
1 parent d4e04e4 commit 3650c2c

19 files changed

+401
-4
lines changed

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,8 @@ endif
342342
@$(MD5SUM) test.hex
343343
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=1 examples/blinky1
344344
@$(MD5SUM) test.hex
345+
$(TINYGO) build -o test.elf -target=nintendoswitch examples/serial
346+
@$(MD5SUM) test.elf
345347

346348
wasmtest:
347349
$(GO) test ./tests/wasm

compileopts/config.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,16 @@ func (c *Config) CodeModel() string {
284284
return "default"
285285
}
286286

287+
// RelocationModel returns the relocation model in use on this platform. Valid
288+
// values are "static", "pic", "dynamicnopic".
289+
func (c *Config) RelocationModel() string {
290+
if c.Target.RelocationModel != "" {
291+
return c.Target.RelocationModel
292+
}
293+
294+
return "static"
295+
}
296+
287297
type TestConfig struct {
288298
CompileTestBinary bool
289299
// TODO: Filter the test functions to run, include verbose flag, etc

compileopts/target.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ type TargetSpec struct {
5050
OpenOCDTransport string `json:"openocd-transport"`
5151
JLinkDevice string `json:"jlink-device"`
5252
CodeModel string `json:"code-model"`
53+
RelocationModel string `json:"relocation-model"`
5354
}
5455

5556
// copyProperties copies all properties that are set in spec2 into itself.
@@ -134,6 +135,10 @@ func (spec *TargetSpec) copyProperties(spec2 *TargetSpec) {
134135
if spec2.CodeModel != "" {
135136
spec.CodeModel = spec2.CodeModel
136137
}
138+
139+
if spec2.RelocationModel != "" {
140+
spec.RelocationModel = spec2.RelocationModel
141+
}
137142
}
138143

139144
// load reads a target specification from the JSON in the given io.Reader. It

compiler/compiler.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) {
9393
features := strings.Join(config.Features(), ",")
9494

9595
var codeModel llvm.CodeModel
96+
var relocationModel llvm.RelocMode
9697

9798
switch config.CodeModel() {
9899
case "default":
@@ -109,7 +110,16 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) {
109110
codeModel = llvm.CodeModelLarge
110111
}
111112

112-
machine := target.CreateTargetMachine(config.Triple(), config.CPU(), features, llvm.CodeGenLevelDefault, llvm.RelocStatic, codeModel)
113+
switch config.RelocationModel() {
114+
case "static":
115+
relocationModel = llvm.RelocStatic
116+
case "pic":
117+
relocationModel = llvm.RelocPIC
118+
case "dynamicnopic":
119+
relocationModel = llvm.RelocDynamicNoPic
120+
}
121+
122+
machine := target.CreateTargetMachine(config.Triple(), config.CPU(), features, llvm.CodeGenLevelDefault, relocationModel, codeModel)
113123
return machine, nil
114124
}
115125

loader/goroot.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ func mergeDirectory(goroot, tinygoroot, tmpgoroot, importPath string, overrides
165165
// with the TinyGo version. This is the case on some targets.
166166
func needsSyscallPackage(buildTags []string) bool {
167167
for _, tag := range buildTags {
168-
if tag == "baremetal" || tag == "darwin" {
168+
if tag == "baremetal" || tag == "darwin" || tag == "nintendoswitch" {
169169
return true
170170
}
171171
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// +build nintendoswitch
2+
3+
package runtime
4+
5+
type timeUnit int64
6+
7+
const asyncScheduler = false
8+
9+
func postinit() {}
10+
11+
// Entry point for Go. Initialize all packages and call main.main().
12+
//export main
13+
func main() int {
14+
preinit()
15+
run()
16+
17+
// Call exit to correctly finish the program
18+
// Without this, the application crashes at start, not sure why
19+
return exit(0)
20+
}
21+
22+
// sleepTicks sleeps for the specified system ticks
23+
func sleepTicks(d timeUnit) {
24+
sleepThread(uint64(ticksToNanoseconds(d)))
25+
}
26+
27+
// armTicksToNs converts cpu ticks to nanoseconds
28+
// Nintendo Switch CPU ticks has a fixed rate at 19200000
29+
// It is basically 52 ns per tick
30+
// The formula 625 / 12 is equivalent to 1e9 / 19200000
31+
func ticksToNanoseconds(tick timeUnit) int64 {
32+
return int64(tick * 625 / 12)
33+
}
34+
35+
func nanosecondsToTicks(ns int64) timeUnit {
36+
return timeUnit(12 * ns / 625)
37+
}
38+
39+
func ticks() timeUnit {
40+
return timeUnit(ticksToNanoseconds(timeUnit(getArmSystemTick())))
41+
}
42+
43+
var stdoutBuffer = make([]byte, 0, 120)
44+
45+
func putchar(c byte) {
46+
if c == '\n' || len(stdoutBuffer)+1 >= 120 {
47+
NxOutputString(string(stdoutBuffer))
48+
stdoutBuffer = stdoutBuffer[:0]
49+
return
50+
}
51+
52+
stdoutBuffer = append(stdoutBuffer, c)
53+
}
54+
55+
func usleep(usec uint) int {
56+
sleepThread(uint64(usec) * 1000)
57+
return 0
58+
}
59+
60+
func abort() {
61+
for {
62+
exit(1)
63+
}
64+
}
65+
66+
//export sleepThread
67+
func sleepThread(nanos uint64)
68+
69+
//export exit
70+
func exit(code int) int
71+
72+
//export armGetSystemTick
73+
func getArmSystemTick() int64
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
.section .text.armGetSystemTick, "ax", %progbits
2+
.global armGetSystemTick
3+
.type armGetSystemTick, %function
4+
.align 2
5+
armGetSystemTick:
6+
mrs x0, cntpct_el0
7+
ret
8+
9+
.section .text.nxOutputString, "ax", %progbits
10+
.global nxOutputString
11+
.type nxOutputString, %function
12+
.align 2
13+
.cfi_startproc
14+
nxOutputString:
15+
svc 0x27
16+
ret
17+
.cfi_endproc
18+
19+
.section .text.exit, "ax", %progbits
20+
.global exit
21+
.type exit, %function
22+
.align 2
23+
exit:
24+
svc 0x7
25+
ret
26+
27+
.section .text.setHeapSize, "ax", %progbits
28+
.global setHeapSize
29+
.type setHeapSize, %function
30+
.align 2
31+
setHeapSize:
32+
str x0, [sp, #-16]!
33+
svc 0x1
34+
ldr x2, [sp], #16
35+
str x1, [x2]
36+
ret
37+
38+
39+
.section .text.sleepThread, "ax", %progbits
40+
.global sleepThread
41+
.type sleepThread, %function
42+
.align 2
43+
sleepThread:
44+
svc 0xB
45+
ret
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// +build nintendoswitch
2+
3+
// +build gc.conservative gc.leaking
4+
5+
package runtime
6+
7+
import "unsafe"
8+
9+
const heapSize = 0x2000000 * 16 // Default by libnx
10+
11+
//go:extern _stack_top
12+
var stackTopSymbol [0]byte
13+
14+
var (
15+
heapStart = uintptr(0)
16+
heapEnd = uintptr(0)
17+
stackTop = uintptr(unsafe.Pointer(&stackTopSymbol))
18+
)
19+
20+
//export setHeapSize
21+
func setHeapSize(addr *uintptr, length uint64) uint64
22+
23+
func preinit() {
24+
setHeapSize(&heapStart, heapSize)
25+
26+
if heapStart == 0 {
27+
panic("failed to allocate heap")
28+
}
29+
30+
heapEnd = heapStart + heapSize
31+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// +build nintendoswitch
2+
3+
// +build gc.none gc.extalloc
4+
5+
package runtime
6+
7+
func preinit() {}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// +build nintendoswitch
2+
3+
package runtime
4+
5+
import (
6+
"unsafe"
7+
)
8+
9+
// Result nxOutputString(const char *str, u64 size)
10+
//export nxOutputString
11+
func nxOutputString(str *uint8, size uint64) uint64
12+
13+
func NxOutputString(str string) uint64 {
14+
strData := (*_string)(unsafe.Pointer(&str))
15+
return nxOutputString((*uint8)(unsafe.Pointer(strData.ptr)), uint64(strData.length))
16+
}
17+
18+
//export malloc
19+
func extalloc(size uintptr) unsafe.Pointer
20+
21+
//export free
22+
func extfree(ptr unsafe.Pointer)

0 commit comments

Comments
 (0)